├── .gitattributes ├── SRLInjector ├── RemoteControl.dll ├── Properties │ └── AssemblyInfo.cs ├── SRLInjector.csproj └── Program.cs ├── NPK3Tool ├── Properties │ └── launchSettings.json ├── NPK3Tool.csproj ├── NPK File Table.bt ├── VirtStream.cs ├── Extensions.cs ├── NPK.cs ├── Program.cs └── StructStream.cs ├── MwareHook ├── DiasmHelper.cs ├── CreateMutexInterceptor.cs ├── MwareHook.csproj ├── EntryPoint.cs ├── ScanHelper.cs ├── KeyInterceptor.cs └── MwareKeyFinder.cs ├── Readme.md ├── LICENSE ├── NUTEditor ├── Properties │ └── AssemblyInfo.cs ├── NUTEditor.csproj └── NUT.cs ├── How To Use.md ├── MwareStuff.sln └── .gitignore /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /SRLInjector/RemoteControl.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcussacana/MwareStuff/HEAD/SRLInjector/RemoteControl.dll -------------------------------------------------------------------------------- /NPK3Tool/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "NPK3Tool": { 4 | "commandName": "Project" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /NPK3Tool/NPK3Tool.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8 6 | 5.9.0 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /MwareHook/DiasmHelper.cs: -------------------------------------------------------------------------------- 1 | using Iced.Intel; 2 | using StringReloads.Hook.Base; 3 | using System.Linq; 4 | 5 | namespace MwareHook 6 | { 7 | unsafe class DiasmHelper 8 | { 9 | MemoryCodeReader Reader; 10 | Decoder Decoder; 11 | public DiasmHelper(void* Address) { 12 | Reader = new MemoryCodeReader(Address); 13 | Decoder = Decoder.Create(32, Reader); 14 | } 15 | 16 | public Instruction Diassembly() { 17 | return Decoder.DecodeAmount(1).Single(); 18 | } 19 | 20 | public void Reset() { 21 | Reader.Reset(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /MwareHook/CreateMutexInterceptor.cs: -------------------------------------------------------------------------------- 1 | using StringReloads.Hook.Base; 2 | using System; 3 | 4 | namespace MwareHook 5 | { 6 | unsafe class CreateMutexInterceptor : Intercept 7 | { 8 | Action OnCalled; 9 | public CreateMutexInterceptor(Action OnCalled) : base("kernel32.dll", "CreateMutexA") 10 | { 11 | this.OnCalled = OnCalled; 12 | } 13 | 14 | public override InterceptDelegate HookFunction => new InterceptDelegate(OnCreateMutexIntercepted); 15 | 16 | void OnCreateMutexIntercepted(void* ESP) 17 | { 18 | OnCalled(*(((uint*)ESP)+8)); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /MwareHook/MwareHook.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | preview 6 | 7 | 8 | 9 | true 10 | 11 | 12 | 13 | true 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /MwareHook/EntryPoint.cs: -------------------------------------------------------------------------------- 1 | using StringReloads.Engine; 2 | using StringReloads.Engine.Interface; 3 | using StringReloads.Hook.Base; 4 | using System; 5 | 6 | namespace MwareHook 7 | { 8 | public class EntryPoint : IPlugin 9 | { 10 | public string Name => "MwareHook"; 11 | 12 | public IAutoInstall[] GetAutoInstallers() => null; 13 | 14 | public IEncoding[] GetEncodings() => null; 15 | 16 | public Hook[] GetHooks() => null; 17 | 18 | public IMatch[] GetMatchs() => null; 19 | 20 | public IStringModifier[] GetModifiers() => null; 21 | 22 | public IMod[] GetMods() => new IMod[] { new MwareKeyFinder() }; 23 | 24 | public IReloader[] GetReloaders() => null; 25 | 26 | public void Initialize(SRL Engine) { } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | ## My Ko-fi 2 | Buy Me a Coffee at ko-fi.com 3 | 4 | # MwareStuff 5 | My tools to the Mware Engine 6 | 7 | ### NPK3Tool 8 | Can extract/repack the NPK3 Packages 9 | 10 | ### NUTEditor 11 | A brute tool that aim to translate NUT files 12 | 13 | #### Supported Games 14 | - You and Me and Her (Totono JastUSA and Steam) 15 | - Tokyo Necro 16 | - Tokyo Necro (Jast USA) 17 | - Minikui Mojika no Ko 18 | - Mojika: Truth Rears Its Ugly Head (Steam) 19 | - SoniComi (JastUSA) 20 | - The Song of Saya (Steam and Steam +18) 21 | - Kishin Houkou Demonbane 22 | - DRAMAtical Murder (Jast USA) 23 | - Full Metal Daemon Muramasa (Jast USA) 24 | - Slow Damage (Jast USA) 25 | - sweet pool (Jast) 26 | - Togainu no Chi (Jast USA) 27 | 28 | ### MwareHook 29 | StringReloads Plugin to try find the game key 30 | 31 | ### SRLInjector 32 | A User-Friendly Loader to the **MwareHook** 33 | 34 | 35 | ### Notes: 36 | Don't Request for a game support without test the **SRLInjector/MwareHook** before! 37 | -------------------------------------------------------------------------------- /MwareHook/ScanHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace MwareHook 6 | { 7 | unsafe static class ModuleInfo 8 | { 9 | public static CodeInfo GetCodeInfo(void* Address) 10 | { 11 | ulong PEStart = *(uint*)((byte*)Address + 0x3C) + (ulong)Address; 12 | ulong OptionalHeader = PEStart + 0x18; 13 | 14 | uint SizeOfCode = *(uint*)(OptionalHeader + 0x04); 15 | uint EntryPoint = *(uint*)(OptionalHeader + 0x10); 16 | uint BaseOfCode = *(uint*)(OptionalHeader + 0x14); 17 | 18 | return new CodeInfo() 19 | { 20 | CodeAddress = ((byte*)Address) + BaseOfCode, 21 | EntryPoint = ((byte*)Address) + EntryPoint, 22 | CodeSize = SizeOfCode 23 | }; 24 | } 25 | } 26 | public unsafe struct CodeInfo 27 | { 28 | public void* CodeAddress; 29 | public uint CodeSize; 30 | public void* EntryPoint; 31 | 32 | public void* EndCodeAddress => (void*)((ulong)CodeAddress + CodeSize); 33 | 34 | public bool AddressIsContained(void* Address) => Address >= CodeAddress && Address <= EndCodeAddress; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /MwareHook/KeyInterceptor.cs: -------------------------------------------------------------------------------- 1 | using Iced.Intel; 2 | using StringReloads.Hook.Base; 3 | using System; 4 | 5 | namespace MwareHook 6 | { 7 | unsafe class KeyInterceptor : Intercept 8 | { 9 | int Register; 10 | public KeyInterceptor(void* Address, Register Register) : base(Address) { 11 | this.Register = Register switch { 12 | Register.EDI => 0, 13 | Register.ESI => 1, 14 | Register.EBP => 2, 15 | Register.ESP => 3, 16 | Register.EBX => 4, 17 | Register.EDX => 5, 18 | Register.ECX => 6, 19 | Register.EAX => 7, 20 | _ => throw new NotSupportedException("Invalid Key Handler Register") 21 | }; 22 | } 23 | 24 | public override InterceptDelegate HookFunction => new InterceptDelegate(OnKeyExpanderBegin); 25 | 26 | public Action OnKeyIntercepted; 27 | 28 | void OnKeyExpanderBegin(void* ESP) { 29 | uint* Stack = (uint*)ESP; 30 | byte* KeyBuffer = (byte*)*(Stack + Register); 31 | byte[] Key = new byte[0x20]; 32 | for (int i = 0; i < Key.Length; i++) 33 | Key[i] = KeyBuffer[i]; 34 | OnKeyIntercepted?.Invoke(Key); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /NPK3Tool/NPK File Table.bt: -------------------------------------------------------------------------------- 1 | //------------------------------------------------ 2 | //--- 010 Editor v10.0.1 Binary Template 3 | // 4 | // File: NPK File Table 5 | // Authors: Marcussacana 6 | // Version: 1.0 7 | // Purpose: 8 | // Category: 9 | // File Mask: *.tbl 10 | // ID Bytes: 11 | // History: 12 | //------------------------------------------------ 13 | 14 | 15 | typedef struct { 16 | DisplayFormatHex(); 17 | SetForeColor( cRed ); 18 | uint64 Offset; 19 | DisplayFormatHex(); 20 | SetForeColor( cBlue ); 21 | uint AlignedSize; 22 | DisplayFormatHex(); 23 | SetForeColor( cGreen ); 24 | uint RealSize; 25 | DisplayFormatHex(); 26 | SetForeColor( cAqua ); 27 | uint DecompressedSize; 28 | } NPKSegmentInfo; 29 | 30 | typedef struct 31 | { 32 | DisplayFormatHex(); 33 | SetForeColor( cDkRed ); 34 | byte SegmentationMode; 35 | SetForeColor( cDkGreen ); 36 | DisplayFormatHex(); 37 | ushort FileNameSize; 38 | SetForeColor( cLtGreen ); 39 | char FilePath[FileNameSize]; 40 | SetForeColor( cDkBlue ); 41 | uint FileSize; 42 | DisplayFormatHex(); 43 | SetForeColor( cLtBlue ); 44 | byte SHA256[0x20]; 45 | SetForeColor( cPurple ); 46 | uint SegmentCount; 47 | SetBackColor( cDkBlue ); 48 | NPKSegmentInfo SegmentsInfo[SegmentCount]; 49 | } NPK3Entry; 50 | 51 | 52 | while( !FEof() ) { 53 | NPK3Entry Entry; 54 | } 55 | -------------------------------------------------------------------------------- /NUTEditor/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // As informações gerais sobre um assembly são controladas por 6 | // conjunto de atributos. Altere estes valores de atributo para modificar as informações 7 | // associada a um assembly. 8 | [assembly: AssemblyTitle("NUTEditor")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("NUTEditor")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2020")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Definir ComVisible como false torna os tipos neste assembly invisíveis 18 | // para componentes COM. Caso precise acessar um tipo neste assembly de 19 | // COM, defina o atributo ComVisible como true nesse tipo. 20 | [assembly: ComVisible(false)] 21 | 22 | // O GUID a seguir será destinado à ID de typelib se este projeto for exposto para COM 23 | [assembly: Guid("86456308-054e-4ff8-bdc3-5dff45009efe")] 24 | 25 | // As informações da versão de um assembly consistem nos quatro valores a seguir: 26 | // 27 | // Versão Principal 28 | // Versão Secundária 29 | // Número da Versão 30 | // Revisão 31 | // 32 | // É possível especificar todos os valores ou usar como padrão os Números de Build e da Revisão 33 | // usando o "*" como mostrado abaixo: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /SRLInjector/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // As informações gerais sobre um assembly são controladas por 6 | // conjunto de atributos. Altere estes valores de atributo para modificar as informações 7 | // associadas a um assembly. 8 | [assembly: AssemblyTitle("SRLInjector")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("SRLInjector")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2020")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Definir ComVisible como false torna os tipos neste assembly invisíveis 18 | // para componentes COM. Caso precise acessar um tipo neste assembly de 19 | // COM, defina o atributo ComVisible como true nesse tipo. 20 | [assembly: ComVisible(false)] 21 | 22 | // O GUID a seguir será destinado à ID de typelib se este projeto for exposto para COM 23 | [assembly: Guid("a7eb8124-bf10-4a89-8020-421ed5a8efff")] 24 | 25 | // As informações da versão de um assembly consistem nos quatro valores a seguir: 26 | // 27 | // Versão Principal 28 | // Versão Secundária 29 | // Número da Versão 30 | // Revisão 31 | // 32 | // É possível especificar todos os valores ou usar como padrão os Números de Build e da Revisão 33 | // usando o "*" como mostrado abaixo: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /How To Use.md: -------------------------------------------------------------------------------- 1 | # How To Unpack NPK Files 2 | 3 | ### 1. Download and install both MwareKeyFinder.rar and NPK3Tool.rar and extract them. 4 | If key is already included in the NPK3Tool, skip Steps 2-3 5 | ### 2. Navigate to your game's exe file and drag and drop it onto the MwareKeyFinder tool (SRLInjector.exe). 6 | ### 3. You will receive a message showing you the key for the game. 7 | Copy and paste the message into an empty text file. 8 | Remove the "0x" and commas manually. 9 | Save the remaining text and this will be the key to extract the game. 10 | ### 4. Getting the version of the engine the game is using(Optional). 11 | Only needed for repacking. 12 | Open the npk file in a hex editor. Check the decoded text for the first 4 characters in the file. 13 | The number after "NPK" (2 or 3) is the engine's version. 14 | ### 5. Drag the npk file you want to extract onto the NPK3Tool.exe file. 15 | Options will pop up so select the option corresponding to your game. If it is not there, type 11. 16 | ### 6. If asked for the key, copy/paste the key. 17 | ### 7. Select between SJIS and UTF-8 encoding. 18 | Most of the time UTF-8 should be selected. If text is not correct, select the other option. 19 | ### 8. The npk file's contents will be extracted into a new folder. 20 | Folder will end with a "~". 21 | 22 | # How To Repack NPK Files 23 | 24 | ### 1. If editing the files inside the new folder, replace those original files with the new ones. 25 | The following process is similar to the previous process(Steps 5-7). 26 | ### 2. Drag and drop the folder from Step 8 onto the NPK3Tool.exe file. 27 | Re-enter the key and the encoding. 28 | A new folder will be created with the folder's name but ending with "_new". 29 | -------------------------------------------------------------------------------- /NUTEditor/NUTEditor.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {86456308-054E-4FF8-BDC3-5DFF45009EFE} 8 | Library 9 | Properties 10 | NUTEditor 11 | NUTEditor 12 | v4.0 13 | 512 14 | true 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /SRLInjector/SRLInjector.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {A7EB8124-BF10-4A89-8020-421ED5A8EFFF} 8 | Exe 9 | SRLInjector 10 | SRLInjector 11 | v4.0 12 | 512 13 | true 14 | 15 | 16 | true 17 | bin\x86\Debug\ 18 | DEBUG;TRACE 19 | full 20 | x86 21 | 7.3 22 | prompt 23 | MinimumRecommendedRules.ruleset 24 | 25 | 26 | bin\x86\Release\ 27 | TRACE 28 | true 29 | pdbonly 30 | x86 31 | 7.3 32 | prompt 33 | MinimumRecommendedRules.ruleset 34 | 35 | 36 | Always 37 | 38 | 39 | 40 | .\RemoteControl.dll 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | if not exist "$(TargetDir)Plugins" ( mkdir "$(TargetDir)Plugins" ) 57 | copy /Y "$(SolutionDir)MwareHook\bin\$(ConfigurationName)\netstandard2.0\MwareHook.dll" "$(TargetDir)\Plugins" 58 | 59 | 60 | -------------------------------------------------------------------------------- /NPK3Tool/VirtStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | 5 | public class VirtStream : Stream 6 | { 7 | private Stream Packget; 8 | private long FilePos = 0; 9 | private long Len; 10 | 11 | internal VirtStream(Stream Packget, long Pos, long Len) 12 | { 13 | this.Packget = Packget; 14 | FilePos = Pos; 15 | this.Len = Len; 16 | } 17 | public override bool CanRead 18 | { 19 | get 20 | { 21 | return true; 22 | } 23 | } 24 | 25 | public override bool CanSeek 26 | { 27 | get 28 | { 29 | return false; 30 | } 31 | } 32 | 33 | public override bool CanWrite 34 | { 35 | get 36 | { 37 | return false; 38 | } 39 | } 40 | public override long Length 41 | { 42 | get 43 | { 44 | return Len; 45 | } 46 | } 47 | 48 | internal long Pos = 0; 49 | public override long Position 50 | { 51 | get 52 | { 53 | return Pos; 54 | } 55 | set 56 | { 57 | Seek(value, SeekOrigin.Begin); 58 | } 59 | } 60 | 61 | public override void Flush() 62 | { 63 | throw new NotImplementedException(); 64 | } 65 | 66 | public override int Read(byte[] buffer, int offset, int count) 67 | { 68 | long ReadPos = FilePos + Pos; 69 | if (ReadPos != Packget.Position) 70 | Packget.Position = ReadPos; 71 | 72 | if (Pos + count > Length) 73 | count = (int)(Length - Pos); 74 | 75 | int Readed = Packget.Read(buffer, offset, count); 76 | Pos += Readed; 77 | return Readed; 78 | } 79 | 80 | /// 81 | /// Seek the file another location 82 | /// 83 | /// Value to change the pointer location 84 | /// Change from 85 | /// New Position 86 | public override long Seek(long offset, SeekOrigin origin) 87 | { 88 | if (offset < 0 || offset > Length) 89 | throw new Exception("Invalid Position"); 90 | switch (origin) 91 | { 92 | case SeekOrigin.Begin: 93 | Packget.Position = FilePos + offset; 94 | this.Pos = offset; 95 | break; 96 | case SeekOrigin.Current: 97 | if (Position + offset > Length) 98 | throw new Exception("Out of Range"); 99 | Packget.Position += offset; 100 | this.Pos += offset; 101 | break; 102 | case SeekOrigin.End: 103 | long Pos = Length - offset; 104 | this.Pos = Pos; 105 | long FP = FilePos + Pos; 106 | if (Pos < 0) 107 | throw new Exception("Out of Range"); 108 | Packget.Position = FP; 109 | break; 110 | } 111 | return Pos; 112 | } 113 | 114 | public override void SetLength(long value) 115 | { 116 | throw new NotImplementedException(); 117 | } 118 | 119 | public override void Write(byte[] buffer, int offset, int count) 120 | { 121 | throw new NotImplementedException(); 122 | } 123 | } -------------------------------------------------------------------------------- /SRLInjector/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Text; 8 | using VNX; 9 | 10 | namespace SRLInjector 11 | { 12 | class Program 13 | { 14 | static void Main(string[] args) 15 | { 16 | Console.Title = "SRLInjector - By Marcussacana"; 17 | if (args == null || args.Length == 0 || !File.Exists(args.First())) 18 | { 19 | Console.WriteLine("Drag&Drop the Game Executable"); 20 | Console.ReadKey(); 21 | return; 22 | } 23 | 24 | var InjectorDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 25 | 26 | string[] SRLPaths = new string[] { "SRLWrapper.dll", "SRLx32.dll" }; 27 | string SRLPath = null; 28 | foreach (var SRL in SRLPaths) 29 | { 30 | var FPath = Path.Combine(InjectorDirectory, SRL); 31 | if (File.Exists(FPath)) 32 | { 33 | SRLPath = FPath; 34 | break; 35 | } 36 | } 37 | 38 | if (SRLPath == null) 39 | { 40 | Console.WriteLine("SRL Not Found in the Current Directory"); 41 | Console.ReadKey(); 42 | return; 43 | } 44 | 45 | if (!File.Exists(Path.Combine(InjectorDirectory, "SRL.ini"))) 46 | { 47 | Console.WriteLine("SRL.ini Not Found in the Current Directory"); 48 | Console.ReadKey(); 49 | return; 50 | } 51 | 52 | if (!File.Exists(Path.Combine(InjectorDirectory, "Plugins", "MwareHook.dll"))) 53 | { 54 | Console.WriteLine("MwareHook.dll Not Found in the \"Plugins\" Directory"); 55 | Console.ReadKey(); 56 | return; 57 | } 58 | 59 | string FullExePath = Path.GetFullPath(args.First()); 60 | string Dir = Path.GetDirectoryName(FullExePath); 61 | 62 | 63 | var Exe = File.ReadAllBytes(FullExePath); 64 | var SteamStub = new byte[] { 0x2E, 0x62, 0x69, 0x6E, 0x64 }; 65 | for (int i = 0; i < Exe.Length; i++) 66 | { 67 | bool Protected = EqualsAt(Exe, SteamStub, i); 68 | if (Protected) 69 | { 70 | var OriForeColor = Console.ForegroundColor; 71 | Console.ForegroundColor = ConsoleColor.Red; 72 | Console.WriteLine("This Game is protected with the Steam Stub DRM\nTo the Key Finder works you must crack it before."); 73 | Console.ForegroundColor = OriForeColor; 74 | Console.ReadKey(); 75 | return; 76 | } 77 | } 78 | 79 | 80 | if (!File.Exists(Path.Combine(Dir, "SRL.ini"))) 81 | File.Copy(Path.Combine(InjectorDirectory, "SRL.ini"), Path.Combine(Dir, "SRL.ini")); 82 | 83 | RemoteControl Control = new RemoteControl(args.First(), out Process Game, WorkingDirectory: Dir); 84 | Control.WaitInitialize(); 85 | Control.LockEntryPoint(); 86 | Control.Invoke(SRLPath, "Process", IntPtr.Zero); 87 | Control.UnlockEntryPoint(); 88 | } 89 | 90 | private static bool EqualsAt(byte[] ArrA, byte[] ArrB, int Index) 91 | { 92 | if (Index + ArrB.Length >= ArrA.Length) 93 | return false; 94 | 95 | for (int i = 0; i < ArrB.Length; i++) 96 | if (ArrA[i + Index] != ArrB[i]) 97 | return false; 98 | 99 | return true; 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /NUTEditor/NUT.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace NUTEditor 7 | { 8 | public class NUT 9 | { 10 | //public Encoding Encoding = Encoding.UTF8;//Totono (Steam) 11 | public Encoding Encoding = Encoding.GetEncoding(932);//JP Games 12 | 13 | readonly byte[] StringPrefix = new byte[] { 0x10, 0x00, 0x00, 0x08 }; 14 | List StringOffsets = new List(); 15 | byte[] Script; 16 | public NUT(byte[] Script) { 17 | this.Script = Script; 18 | } 19 | 20 | public string[] Import() { 21 | List Strings = new List(); 22 | StringOffsets = new List(); 23 | for (int i = 0; i < Script.Length; i++) { 24 | if (!EqualsAt(Script, StringPrefix, i)) 25 | continue; 26 | i += 4; 27 | if (!IsValidString(Script, i)) { 28 | i -= 4; 29 | continue; 30 | } 31 | Strings.Add(ReadStringAt(Script, i)); 32 | StringOffsets.Add(i); 33 | } 34 | 35 | return Strings.ToArray(); 36 | } 37 | 38 | public byte[] Export(string[] Lines) { 39 | byte[] Output = Script.Take(Script.Length).ToArray(); 40 | for (int i = Lines.Length - 1; i >= 0; i--) { 41 | Output = ReplaceStringAt(Output, StringOffsets[i], Lines[i]); 42 | } 43 | 44 | int Diff = Output.Length - Script.Length; 45 | Output = UpdateOffset(Output, 0x8, Diff); 46 | Output = UpdateOffset(Output, 0xC, Diff); 47 | 48 | return Output; 49 | } 50 | 51 | bool EqualsAt(byte[] Buffer, byte[] Pattern, int Index) { 52 | if (Buffer.Length <= Pattern.Length + Index) 53 | return false; 54 | 55 | for (int i = 0; i < Pattern.Length; i++) 56 | if (Buffer[Index + i] != Pattern[i]) 57 | return false; 58 | return true; 59 | } 60 | 61 | private bool IsValidString(byte[] Buffer, int Index) { 62 | var Size = ReadU32At(Buffer, Index); 63 | if (Size + Index >= Buffer.Length) 64 | return false; 65 | 66 | var Data = GetRange(Buffer, Index + sizeof(uint), (int)Size); 67 | foreach (var Byte in Data) 68 | if (Byte < 0x0A) 69 | return false; 70 | return true; 71 | } 72 | 73 | private byte[] GetRange(byte[] Buffer, int Index, int Length) { 74 | byte[] Output = new byte[Length]; 75 | for (int i = 0; i < Length; i++) 76 | Output[i] = Buffer[i + Index]; 77 | return Output; 78 | } 79 | 80 | private string ReadStringAt(byte[] Buffer, int Index) { 81 | uint StrSize = ReadU32At(Buffer, Index); 82 | byte[] String = GetRange(Buffer, Index + sizeof(uint), (int)StrSize); 83 | 84 | return Encoding.GetString(String); 85 | } 86 | 87 | private byte[] ReplaceStringAt(byte[] BufferArr, int Index, string Data) { 88 | var Buffer = new List(BufferArr); 89 | var OriLen = ReadU32At(BufferArr, Index); 90 | Buffer.RemoveRange(Index, sizeof(uint) + (int)OriLen); 91 | 92 | var NewContent = Encoding.GetBytes(Data); 93 | var NewStrData = BitConverter.GetBytes(NewContent.Length).Concat(NewContent).ToArray(); 94 | 95 | Buffer.InsertRange(Index, NewStrData); 96 | return Buffer.ToArray(); 97 | } 98 | 99 | private uint ReadU32At(byte[] Buffer, int Index) { 100 | byte[] tmp = GetRange(Buffer, Index, 4); 101 | return BitConverter.ToUInt32(tmp, 0); 102 | } 103 | 104 | byte[] UpdateOffset(byte[] Buffer, int Index, int Diff) { 105 | var OriVal = ReadU32At(Buffer, Index); 106 | var NewVal = BitConverter.GetBytes((int)(OriVal + Diff)); 107 | 108 | var Output = Buffer.Take(Buffer.Length).ToArray(); 109 | NewVal.CopyTo(Output, Index); 110 | return Output; 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /NPK3Tool/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.IO.Compression; 4 | using System.Security.Cryptography; 5 | using System.Text; 6 | using Zstandard.Net; 7 | 8 | namespace NPK3Tool 9 | { 10 | static class Extensions 11 | { 12 | 13 | public static byte[] ReadBytes(this Stream Stream, int Pos, int Count) 14 | { 15 | byte[] Buffer = new byte[Count]; 16 | Stream.Position = Pos; 17 | Stream.Read(Buffer, 0, Count); 18 | return Buffer; 19 | } 20 | 21 | public static void WriteBytes(this Stream Stream, byte[] Buffer) { 22 | Stream.Write(Buffer, 0, Buffer.Length); 23 | } 24 | 25 | public static uint ReadUInt32(this Stream Stream, int Pos) 26 | { 27 | return BitConverter.ToUInt32(Stream.ReadBytes(Pos, 4), 0); 28 | } 29 | 30 | public static void WriteUIn32(this Stream Stream, uint Value) { 31 | Stream.Write(BitConverter.GetBytes(Value), 0, 4); 32 | } 33 | 34 | public static Stream CreateStream(this Stream Stream, long Pos, uint Size) 35 | { 36 | return new VirtStream(Stream, Pos, Size); 37 | } 38 | 39 | public static Stream CreateDecryptor(this Stream Stream, byte[] Key, byte[] IV) 40 | { 41 | var aes = Aes.Create(); 42 | aes.Mode = CipherMode.CBC; 43 | aes.Padding = PaddingMode.PKCS7; 44 | aes.Key = Key; 45 | if (IV != null) 46 | aes.IV = IV; 47 | var decryptor = aes.CreateDecryptor(); 48 | return new CryptoStream(Stream, decryptor, CryptoStreamMode.Read); 49 | } 50 | 51 | public static Stream CreateEncryptor(this Stream Stream, byte[] Key, byte[] IV) 52 | { 53 | var aes = Aes.Create(); 54 | aes.Mode = CipherMode.CBC; 55 | aes.Padding = PaddingMode.PKCS7; 56 | aes.Key = Key; 57 | if (IV != null) 58 | aes.IV = IV; 59 | var encryptor = aes.CreateEncryptor(); 60 | return new CryptoStream(Stream, encryptor, CryptoStreamMode.Read); 61 | } 62 | 63 | public static Stream CreateDecompressor(this Stream Stream, int NpkVersion) 64 | { 65 | switch (NpkVersion) 66 | { 67 | case 3: 68 | return new ZstandardStream(Stream, CompressionMode.Decompress, true); 69 | case 2: 70 | return new DeflateStream(Stream, CompressionMode.Decompress, true); 71 | default: 72 | throw new NotSupportedException("NPK Version Not Supported"); 73 | } 74 | } 75 | public static Stream Compress(this Stream Stream, int NpkVersion) 76 | { 77 | MemoryStream Compressed = new MemoryStream(); 78 | switch (NpkVersion) { 79 | case 3: 80 | using (var Compressor = new ZstandardStream(Compressed, CompressionMode.Compress, true)) 81 | { 82 | Stream.CopyTo(Compressor); 83 | Compressor.Close(); 84 | Compressed.Position = 0; 85 | Stream.Position = 0; 86 | return Compressed; 87 | } 88 | case 2: 89 | using (var Compressor = new DeflateStream(Compressed, CompressionLevel.Optimal, true)) 90 | { 91 | Stream.CopyTo(Compressor); 92 | Compressor.Close(); 93 | Compressed.Position = 0; 94 | Stream.Position = 0; 95 | return Compressed; 96 | } 97 | default: 98 | throw new NotSupportedException("NPK Version Not Supported"); 99 | 100 | } 101 | } 102 | 103 | public static Stream ToMemory(this Stream Stream) 104 | { 105 | var NewStream = new MemoryStream(); 106 | Stream.CopyTo(NewStream); 107 | NewStream.Position = 0; 108 | return NewStream; 109 | } 110 | public static byte[] SHA256Checksum(this Stream Stream) 111 | { 112 | long OriPos = Stream.Position; 113 | using SHA256 SHA256 = SHA256.Create(); 114 | var Data = SHA256.ComputeHash(Stream); 115 | Stream.Position = OriPos; 116 | return Data; 117 | } 118 | public static Encoding ToEncoding(this string Value) 119 | { 120 | if (int.TryParse(Value, out int CP)) 121 | return Encoding.GetEncoding(CP); 122 | 123 | return Value.ToLowerInvariant() switch 124 | { 125 | "sjis" => Encoding.GetEncoding(932), 126 | "shiftjis" => Encoding.GetEncoding(932), 127 | "shift-jis" => Encoding.GetEncoding(932), 128 | "unicode" => Encoding.Unicode, 129 | "utf16" => Encoding.Unicode, 130 | "utf16be" => Encoding.BigEndianUnicode, 131 | "utf16wb" => new UnicodeEncoding(false, true), 132 | "utf16wbbe" => new UnicodeEncoding(true, true), 133 | "utf16bewb" => new UnicodeEncoding(true, true), 134 | "utf8" => Encoding.UTF8, 135 | "utf8wb" => new UTF8Encoding(true), 136 | "utf7" => Encoding.UTF7, 137 | _ => Encoding.GetEncoding(Value) 138 | }; 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /MwareStuff.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30001.183 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MwareHook", "MwareHook\MwareHook.csproj", "{5B8D4FA9-E2E4-4988-A98D-BB7108F0815F}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StringReloads", "..\StringReloads\StringReloads\StringReloads.csproj", "{2CDADAF6-73C5-475D-9827-45B847ECC148}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NPK3Tool", "NPK3Tool\NPK3Tool.csproj", "{25255408-D697-4C7D-94A0-D978701F7228}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NUTEditor", "NUTEditor\NUTEditor.csproj", "{86456308-054E-4FF8-BDC3-5DFF45009EFE}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SRLInjector", "SRLInjector\SRLInjector.csproj", "{A7EB8124-BF10-4A89-8020-421ED5A8EFFF}" 15 | ProjectSection(ProjectDependencies) = postProject 16 | {5B8D4FA9-E2E4-4988-A98D-BB7108F0815F} = {5B8D4FA9-E2E4-4988-A98D-BB7108F0815F} 17 | EndProjectSection 18 | EndProject 19 | Global 20 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 21 | Debug|Any CPU = Debug|Any CPU 22 | Debug|x64 = Debug|x64 23 | Debug|x86 = Debug|x86 24 | Release|Any CPU = Release|Any CPU 25 | Release|x64 = Release|x64 26 | Release|x86 = Release|x86 27 | EndGlobalSection 28 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 29 | {5B8D4FA9-E2E4-4988-A98D-BB7108F0815F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {5B8D4FA9-E2E4-4988-A98D-BB7108F0815F}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {5B8D4FA9-E2E4-4988-A98D-BB7108F0815F}.Debug|x64.ActiveCfg = Debug|Any CPU 32 | {5B8D4FA9-E2E4-4988-A98D-BB7108F0815F}.Debug|x64.Build.0 = Debug|Any CPU 33 | {5B8D4FA9-E2E4-4988-A98D-BB7108F0815F}.Debug|x86.ActiveCfg = Debug|Any CPU 34 | {5B8D4FA9-E2E4-4988-A98D-BB7108F0815F}.Debug|x86.Build.0 = Debug|Any CPU 35 | {5B8D4FA9-E2E4-4988-A98D-BB7108F0815F}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {5B8D4FA9-E2E4-4988-A98D-BB7108F0815F}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {5B8D4FA9-E2E4-4988-A98D-BB7108F0815F}.Release|x64.ActiveCfg = Release|Any CPU 38 | {5B8D4FA9-E2E4-4988-A98D-BB7108F0815F}.Release|x64.Build.0 = Release|Any CPU 39 | {5B8D4FA9-E2E4-4988-A98D-BB7108F0815F}.Release|x86.ActiveCfg = Release|Any CPU 40 | {5B8D4FA9-E2E4-4988-A98D-BB7108F0815F}.Release|x86.Build.0 = Release|Any CPU 41 | {2CDADAF6-73C5-475D-9827-45B847ECC148}.Debug|Any CPU.ActiveCfg = Debug|x86 42 | {2CDADAF6-73C5-475D-9827-45B847ECC148}.Debug|x64.ActiveCfg = Debug|x64 43 | {2CDADAF6-73C5-475D-9827-45B847ECC148}.Debug|x64.Build.0 = Debug|x64 44 | {2CDADAF6-73C5-475D-9827-45B847ECC148}.Debug|x86.ActiveCfg = Debug|x86 45 | {2CDADAF6-73C5-475D-9827-45B847ECC148}.Debug|x86.Build.0 = Debug|x86 46 | {2CDADAF6-73C5-475D-9827-45B847ECC148}.Release|Any CPU.ActiveCfg = Release|x86 47 | {2CDADAF6-73C5-475D-9827-45B847ECC148}.Release|x64.ActiveCfg = Release|x64 48 | {2CDADAF6-73C5-475D-9827-45B847ECC148}.Release|x64.Build.0 = Release|x64 49 | {2CDADAF6-73C5-475D-9827-45B847ECC148}.Release|x86.ActiveCfg = Release|x86 50 | {2CDADAF6-73C5-475D-9827-45B847ECC148}.Release|x86.Build.0 = Release|x86 51 | {25255408-D697-4C7D-94A0-D978701F7228}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 52 | {25255408-D697-4C7D-94A0-D978701F7228}.Debug|Any CPU.Build.0 = Debug|Any CPU 53 | {25255408-D697-4C7D-94A0-D978701F7228}.Debug|x64.ActiveCfg = Debug|Any CPU 54 | {25255408-D697-4C7D-94A0-D978701F7228}.Debug|x64.Build.0 = Debug|Any CPU 55 | {25255408-D697-4C7D-94A0-D978701F7228}.Debug|x86.ActiveCfg = Debug|Any CPU 56 | {25255408-D697-4C7D-94A0-D978701F7228}.Debug|x86.Build.0 = Debug|Any CPU 57 | {25255408-D697-4C7D-94A0-D978701F7228}.Release|Any CPU.ActiveCfg = Release|Any CPU 58 | {25255408-D697-4C7D-94A0-D978701F7228}.Release|Any CPU.Build.0 = Release|Any CPU 59 | {25255408-D697-4C7D-94A0-D978701F7228}.Release|x64.ActiveCfg = Release|Any CPU 60 | {25255408-D697-4C7D-94A0-D978701F7228}.Release|x64.Build.0 = Release|Any CPU 61 | {25255408-D697-4C7D-94A0-D978701F7228}.Release|x86.ActiveCfg = Release|Any CPU 62 | {25255408-D697-4C7D-94A0-D978701F7228}.Release|x86.Build.0 = Release|Any CPU 63 | {86456308-054E-4FF8-BDC3-5DFF45009EFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 64 | {86456308-054E-4FF8-BDC3-5DFF45009EFE}.Debug|Any CPU.Build.0 = Debug|Any CPU 65 | {86456308-054E-4FF8-BDC3-5DFF45009EFE}.Debug|x64.ActiveCfg = Debug|Any CPU 66 | {86456308-054E-4FF8-BDC3-5DFF45009EFE}.Debug|x64.Build.0 = Debug|Any CPU 67 | {86456308-054E-4FF8-BDC3-5DFF45009EFE}.Debug|x86.ActiveCfg = Debug|Any CPU 68 | {86456308-054E-4FF8-BDC3-5DFF45009EFE}.Debug|x86.Build.0 = Debug|Any CPU 69 | {86456308-054E-4FF8-BDC3-5DFF45009EFE}.Release|Any CPU.ActiveCfg = Release|Any CPU 70 | {86456308-054E-4FF8-BDC3-5DFF45009EFE}.Release|Any CPU.Build.0 = Release|Any CPU 71 | {86456308-054E-4FF8-BDC3-5DFF45009EFE}.Release|x64.ActiveCfg = Release|Any CPU 72 | {86456308-054E-4FF8-BDC3-5DFF45009EFE}.Release|x64.Build.0 = Release|Any CPU 73 | {86456308-054E-4FF8-BDC3-5DFF45009EFE}.Release|x86.ActiveCfg = Release|Any CPU 74 | {86456308-054E-4FF8-BDC3-5DFF45009EFE}.Release|x86.Build.0 = Release|Any CPU 75 | {A7EB8124-BF10-4A89-8020-421ED5A8EFFF}.Debug|Any CPU.ActiveCfg = Debug|x86 76 | {A7EB8124-BF10-4A89-8020-421ED5A8EFFF}.Debug|Any CPU.Build.0 = Debug|x86 77 | {A7EB8124-BF10-4A89-8020-421ED5A8EFFF}.Debug|x64.ActiveCfg = Debug|x86 78 | {A7EB8124-BF10-4A89-8020-421ED5A8EFFF}.Debug|x86.ActiveCfg = Debug|x86 79 | {A7EB8124-BF10-4A89-8020-421ED5A8EFFF}.Debug|x86.Build.0 = Debug|x86 80 | {A7EB8124-BF10-4A89-8020-421ED5A8EFFF}.Release|Any CPU.ActiveCfg = Release|x86 81 | {A7EB8124-BF10-4A89-8020-421ED5A8EFFF}.Release|x64.ActiveCfg = Release|x86 82 | {A7EB8124-BF10-4A89-8020-421ED5A8EFFF}.Release|x86.ActiveCfg = Release|x86 83 | {A7EB8124-BF10-4A89-8020-421ED5A8EFFF}.Release|x86.Build.0 = Release|x86 84 | EndGlobalSection 85 | GlobalSection(SolutionProperties) = preSolution 86 | HideSolutionNode = FALSE 87 | EndGlobalSection 88 | GlobalSection(ExtensibilityGlobals) = postSolution 89 | SolutionGuid = {38888819-3047-460B-9655-AAC5EB09B26F} 90 | EndGlobalSection 91 | EndGlobal 92 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # JustCode is a .NET coding add-in 131 | .JustCode 132 | 133 | # TeamCity is a build add-in 134 | _TeamCity* 135 | 136 | # DotCover is a Code Coverage Tool 137 | *.dotCover 138 | 139 | # AxoCover is a Code Coverage Tool 140 | .axoCover/* 141 | !.axoCover/settings.json 142 | 143 | # Visual Studio code coverage results 144 | *.coverage 145 | *.coveragexml 146 | 147 | # NCrunch 148 | _NCrunch_* 149 | .*crunch*.local.xml 150 | nCrunchTemp_* 151 | 152 | # MightyMoose 153 | *.mm.* 154 | AutoTest.Net/ 155 | 156 | # Web workbench (sass) 157 | .sass-cache/ 158 | 159 | # Installshield output folder 160 | [Ee]xpress/ 161 | 162 | # DocProject is a documentation generator add-in 163 | DocProject/buildhelp/ 164 | DocProject/Help/*.HxT 165 | DocProject/Help/*.HxC 166 | DocProject/Help/*.hhc 167 | DocProject/Help/*.hhk 168 | DocProject/Help/*.hhp 169 | DocProject/Help/Html2 170 | DocProject/Help/html 171 | 172 | # Click-Once directory 173 | publish/ 174 | 175 | # Publish Web Output 176 | *.[Pp]ublish.xml 177 | *.azurePubxml 178 | # Note: Comment the next line if you want to checkin your web deploy settings, 179 | # but database connection strings (with potential passwords) will be unencrypted 180 | *.pubxml 181 | *.publishproj 182 | 183 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 184 | # checkin your Azure Web App publish settings, but sensitive information contained 185 | # in these scripts will be unencrypted 186 | PublishScripts/ 187 | 188 | # NuGet Packages 189 | *.nupkg 190 | # NuGet Symbol Packages 191 | *.snupkg 192 | # The packages folder can be ignored because of Package Restore 193 | **/[Pp]ackages/* 194 | # except build/, which is used as an MSBuild target. 195 | !**/[Pp]ackages/build/ 196 | # Uncomment if necessary however generally it will be regenerated when needed 197 | #!**/[Pp]ackages/repositories.config 198 | # NuGet v3's project.json files produces more ignorable files 199 | *.nuget.props 200 | *.nuget.targets 201 | 202 | # Microsoft Azure Build Output 203 | csx/ 204 | *.build.csdef 205 | 206 | # Microsoft Azure Emulator 207 | ecf/ 208 | rcf/ 209 | 210 | # Windows Store app package directories and files 211 | AppPackages/ 212 | BundleArtifacts/ 213 | Package.StoreAssociation.xml 214 | _pkginfo.txt 215 | *.appx 216 | *.appxbundle 217 | *.appxupload 218 | 219 | # Visual Studio cache files 220 | # files ending in .cache can be ignored 221 | *.[Cc]ache 222 | # but keep track of directories ending in .cache 223 | !?*.[Cc]ache/ 224 | 225 | # Others 226 | ClientBin/ 227 | ~$* 228 | *~ 229 | *.dbmdl 230 | *.dbproj.schemaview 231 | *.jfm 232 | *.pfx 233 | *.publishsettings 234 | orleans.codegen.cs 235 | 236 | # Including strong name files can present a security risk 237 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 238 | #*.snk 239 | 240 | # Since there are multiple workflows, uncomment next line to ignore bower_components 241 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 242 | #bower_components/ 243 | 244 | # RIA/Silverlight projects 245 | Generated_Code/ 246 | 247 | # Backup & report files from converting an old project file 248 | # to a newer Visual Studio version. Backup files are not needed, 249 | # because we have git ;-) 250 | _UpgradeReport_Files/ 251 | Backup*/ 252 | UpgradeLog*.XML 253 | UpgradeLog*.htm 254 | ServiceFabricBackup/ 255 | *.rptproj.bak 256 | 257 | # SQL Server files 258 | *.mdf 259 | *.ldf 260 | *.ndf 261 | 262 | # Business Intelligence projects 263 | *.rdl.data 264 | *.bim.layout 265 | *.bim_*.settings 266 | *.rptproj.rsuser 267 | *- [Bb]ackup.rdl 268 | *- [Bb]ackup ([0-9]).rdl 269 | *- [Bb]ackup ([0-9][0-9]).rdl 270 | 271 | # Microsoft Fakes 272 | FakesAssemblies/ 273 | 274 | # GhostDoc plugin setting file 275 | *.GhostDoc.xml 276 | 277 | # Node.js Tools for Visual Studio 278 | .ntvs_analysis.dat 279 | node_modules/ 280 | 281 | # Visual Studio 6 build log 282 | *.plg 283 | 284 | # Visual Studio 6 workspace options file 285 | *.opt 286 | 287 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 288 | *.vbw 289 | 290 | # Visual Studio LightSwitch build output 291 | **/*.HTMLClient/GeneratedArtifacts 292 | **/*.DesktopClient/GeneratedArtifacts 293 | **/*.DesktopClient/ModelManifest.xml 294 | **/*.Server/GeneratedArtifacts 295 | **/*.Server/ModelManifest.xml 296 | _Pvt_Extensions 297 | 298 | # Paket dependency manager 299 | .paket/paket.exe 300 | paket-files/ 301 | 302 | # FAKE - F# Make 303 | .fake/ 304 | 305 | # CodeRush personal settings 306 | .cr/personal 307 | 308 | # Python Tools for Visual Studio (PTVS) 309 | __pycache__/ 310 | *.pyc 311 | 312 | # Cake - Uncomment if you are using it 313 | # tools/** 314 | # !tools/packages.config 315 | 316 | # Tabs Studio 317 | *.tss 318 | 319 | # Telerik's JustMock configuration file 320 | *.jmconfig 321 | 322 | # BizTalk build output 323 | *.btp.cs 324 | *.btm.cs 325 | *.odx.cs 326 | *.xsd.cs 327 | 328 | # OpenCover UI analysis results 329 | OpenCover/ 330 | 331 | # Azure Stream Analytics local run output 332 | ASALocalRun/ 333 | 334 | # MSBuild Binary and Structured Log 335 | *.binlog 336 | 337 | # NVidia Nsight GPU debugger configuration file 338 | *.nvuser 339 | 340 | # MFractors (Xamarin productivity tool) working folder 341 | .mfractor/ 342 | 343 | # Local History for Visual Studio 344 | .localhistory/ 345 | 346 | # BeatPulse healthcheck temp database 347 | healthchecksdb 348 | 349 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 350 | MigrationBackup/ 351 | 352 | # Ionide (cross platform F# VS Code tools) working folder 353 | .ionide/ 354 | -------------------------------------------------------------------------------- /MwareHook/MwareKeyFinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Runtime.ExceptionServices; 7 | using Iced.Intel; 8 | using StringReloads.Engine; 9 | using StringReloads.Engine.Interface; 10 | 11 | namespace MwareHook 12 | { 13 | unsafe class MwareKeyFinder : IMod 14 | { 15 | CreateMutexInterceptor MInterceptor; 16 | KeyInterceptor KInterceptor; 17 | DiasmHelper Helper; 18 | CodeInfo? Info; 19 | byte?[] Pattern = { 0x66, 0x0F, 0x3A, 0xDF, null, 0x01 }; 20 | byte?[] FunctionHeader = { 0xCC, 0xCC, 0xCC, 0xCC }; 21 | 22 | void* Address; 23 | 24 | public string Name => "MwareKeyFinder"; 25 | 26 | public void Install() 27 | { 28 | if (Info == null) 29 | { 30 | var MainModule = Process.GetCurrentProcess().MainModule; 31 | foreach (var Module in Process.GetCurrentProcess().Modules.Cast()) 32 | { 33 | if (Module.ModuleName.ToLower().Trim() == "main.bin") 34 | { 35 | MainModule = Module;//SoftDenchi OEP 36 | } 37 | } 38 | 39 | var Exe = File.ReadAllBytes(Config.Default.GameExePath); 40 | fixed (void* pExe = &Exe[0]) 41 | { 42 | var SteamStub = new byte?[] { 0x2E, 0x62, 0x69, 0x6E, 0x64 }; 43 | for (int i = 0; i < Exe.Length; i++) 44 | { 45 | bool Protected = CheckPattern((byte*)pExe + i, SteamStub); 46 | if (Protected) 47 | { 48 | User.ShowMessageBox("This Game is protected with the Steam Stub DRM\nTo the Key Finder works you must crack it before.", "MwareKeyFinder - By Marcussacana", User.MBButtons.Ok, User.MBIcon.Error); 49 | break; 50 | } 51 | } 52 | } 53 | 54 | Info = ModuleInfo.GetCodeInfo(MainModule.BaseAddress.ToPointer()); 55 | if (!Scan(out Address)) 56 | { 57 | Log.Warning("Failed to find the KeyExpander Function, Trying WinMain Method..."); 58 | MInterceptor = new CreateMutexInterceptor(OnCreateMutexCalled); 59 | } 60 | else 61 | { 62 | Helper = new DiasmHelper(Address); 63 | var Instruction = Helper.Diassembly(); 64 | var KeyRegister = Instruction.MemoryBase.GetFullRegister32(); 65 | KInterceptor = new KeyInterceptor(Address, KeyRegister); 66 | KInterceptor.OnKeyIntercepted = OnKeyIntercepted; 67 | Log.Debug("Mware Key Interceptor Ready"); 68 | } 69 | } 70 | 71 | if (KInterceptor != null) 72 | KInterceptor.Install(); 73 | 74 | if (MInterceptor != null) 75 | MInterceptor.Install(); 76 | } 77 | 78 | void OnKeyIntercepted(byte[] Key) 79 | { 80 | if (KInterceptor != null) 81 | KInterceptor.Uninstall(); 82 | 83 | string KeyStr = string.Empty; 84 | for (int i = 0; i < Key.Length; i++) 85 | { 86 | KeyStr += $"0x{Key[i]:X2}, "; 87 | } 88 | KeyStr = KeyStr.TrimEnd(' ', ','); 89 | User.ShowMessageBox("Encryption Key Found:\n" + KeyStr, "MwareKeyFinder - By Marcussacana", User.MBButtons.Ok, User.MBIcon.Information); 90 | } 91 | 92 | //Alternative (Less Stable) Key Find Method 93 | void OnCreateMutexCalled(uint Caller) 94 | { 95 | 96 | var pCaller = (void*)Caller; 97 | bool FromMainModule = Info.Value.AddressIsContained(pCaller); 98 | Log.Trace($"CreateMutex Called At: 0x{Caller:X8} ({(FromMainModule ? "Main Module" : "Secundary Module")})"); 99 | if (!FromMainModule) 100 | return; 101 | 102 | MInterceptor.Uninstall(); 103 | 104 | Helper = new DiasmHelper(pCaller); 105 | 106 | var List = new InstructionList(); 107 | 108 | bool InMissmatch = false; 109 | bool InEnd = false; 110 | int MovCount = 0; 111 | int Tries = 0; 112 | while (Tries <= 500) 113 | { 114 | Tries++; 115 | if (MovCount > 7) 116 | InEnd = true; 117 | 118 | var Instruction = Helper.Diassembly(); 119 | 120 | bool IsMov = Instruction.Code == Code.Mov_rm32_imm32; 121 | 122 | if (IsMov) 123 | Log.Trace($"{Instruction} at ({Instruction.IP:X8})"); 124 | 125 | if (IsMov) 126 | MovCount++; 127 | 128 | if (!IsMov) 129 | { 130 | if (InMissmatch) 131 | { 132 | MovCount = 0; 133 | InMissmatch = false; 134 | } 135 | else 136 | InMissmatch = true; 137 | } 138 | else 139 | InMissmatch = false; 140 | 141 | if (InEnd && !IsMov) 142 | break; 143 | 144 | if (!InEnd && IsMov) 145 | List.Add(Instruction); 146 | else if (!InEnd && !InMissmatch) 147 | List.Clear(); 148 | } 149 | 150 | if (Tries >= 500) 151 | return; 152 | 153 | byte[] KBuffer = new byte[0x20]; 154 | for (int i = 0; i < List.Count; i++) 155 | BitConverter.GetBytes(List[i].Immediate32).CopyTo(KBuffer, i * 4); 156 | 157 | OnKeyIntercepted(KBuffer); 158 | } 159 | 160 | private bool Scan(out void* Address) 161 | { 162 | Address = null; 163 | long CodeAdd = (long)Info.Value.CodeAddress; 164 | long CodeLen = Info.Value.CodeSize - Pattern.Length; 165 | for (int i = 0; i < CodeLen; i++) 166 | { 167 | byte* pBuffer = (byte*)(Info.Value.CodeAddress) + i; 168 | if (!CheckPattern(pBuffer, Pattern)) 169 | continue; 170 | Log.Debug($"Decryption Key Pattern Found At: 0x{(ulong)pBuffer:X8}"); 171 | for (long x = (long)pBuffer; x > CodeAdd; x--) 172 | { 173 | byte* pFunc = (byte*)x; 174 | if (!CheckPattern(pFunc, FunctionHeader)) 175 | continue; 176 | Address = pFunc + FunctionHeader.Length; 177 | return true; 178 | } 179 | } 180 | return false; 181 | } 182 | 183 | private bool CheckPattern(byte* Buffer, byte?[] Pattern) 184 | { 185 | for (int i = 0; i < Pattern.Length; i++) 186 | { 187 | if (Pattern[i] == null) 188 | continue; 189 | byte bPattern = Pattern[i].Value; 190 | if (bPattern != Buffer[i]) 191 | return false; 192 | } 193 | return true; 194 | } 195 | 196 | public bool IsCompatible() => File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Mware.dll")); 197 | 198 | 199 | public void Uninstall() 200 | { 201 | if (KInterceptor != null) 202 | KInterceptor.Uninstall(); 203 | 204 | if (MInterceptor != null) 205 | MInterceptor.Uninstall(); 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /NPK3Tool/NPK.cs: -------------------------------------------------------------------------------- 1 | using AdvancedBinary; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | using System.Linq; 6 | using System; 7 | using System.Diagnostics; 8 | 9 | namespace NPK3Tool 10 | { 11 | static class NPK 12 | { 13 | public static bool EnableCompression = true; 14 | public static bool EnableSegmentation = true; 15 | public static bool ForceSegmentation = true; 16 | public static int NPKVersion = 3; 17 | public static uint NPKMinorVersion = 1;//Not Sure 18 | public static uint MaxSectionSize = 0x10000; 19 | public static Encoding Encoding = Encoding.UTF8; 20 | public static string[] DontCompress = { "png", "ogg", "jpg", "mpg" }; 21 | 22 | public static byte[] CurrentKey; 23 | public static byte[] CurrentIV = new byte[] { 0x42, 0x79, 0x20, 0x4D, 0x61, 0x72, 0x63, 0x75, 0x73, 0x73, 0x61, 0x63, 0x61, 0x6E, 0x61, 0x00 }; 24 | 25 | public static void Repack(string InputDirectory, string OutNPK = null) { 26 | InputDirectory = Path.GetFullPath(InputDirectory); 27 | if (!InputDirectory.EndsWith(Path.DirectorySeparatorChar) && !InputDirectory.EndsWith(Path.AltDirectorySeparatorChar)) 28 | InputDirectory += Path.DirectorySeparatorChar; 29 | 30 | if (OutNPK == null) { 31 | OutNPK = InputDirectory.TrimEnd('\\', '/', '~'); 32 | OutNPK = Path.Combine(Path.GetDirectoryName(OutNPK), Path.GetFileNameWithoutExtension(OutNPK) + "_New.npk"); 33 | } 34 | 35 | string[] FilesPath = Directory.GetFiles(InputDirectory, "*.*", SearchOption.AllDirectories); 36 | string[] RelativeFiles = (from x in FilesPath select x.Substring(InputDirectory.Length)).ToArray(); 37 | Stream[] FilesData = (from x in FilesPath select File.Open(x, FileMode.Open)).ToArray(); 38 | 39 | using (Stream Output = File.Create(OutNPK)) { 40 | switch (NPKVersion) { 41 | case 3: 42 | Output.WriteUIn32(0x334B504Eu);//NPK3 43 | break; 44 | case 2: 45 | Output.WriteUIn32(0x324B504Eu);//NPK2 46 | break; 47 | default: 48 | throw new NotSupportedException("NPK Version Not Supported"); 49 | } 50 | Output.WriteUIn32(NPKMinorVersion); 51 | 52 | Output.WriteBytes(CurrentIV); 53 | Output.WriteUIn32((uint)FilesPath.Length); 54 | 55 | var Entries = CreateInitialEntries(RelativeFiles, FilesData); 56 | 57 | uint TableSize; 58 | using (Stream TBuilder = BuildEntries(Entries)) 59 | using (Stream TEncryptor = TBuilder.CreateEncryptor(CurrentKey, CurrentIV)) 60 | using (Stream TBuffer = TEncryptor.ToMemory()) 61 | TableSize = (uint)TBuffer.Length; 62 | 63 | Output.WriteUIn32(TableSize); 64 | 65 | long TablePos = Output.Position; 66 | 67 | Output.WriteBytes(new byte[TableSize]); 68 | 69 | for (int i = 0; i < FilesData.Length; i++) { 70 | Console.WriteLine($"Writing File: {RelativeFiles[i]}"); 71 | string Ext = Path.GetExtension(FilesPath[i]).ToLower().TrimStart('.'); 72 | bool Compress = EnableCompression && !DontCompress.Contains(Ext); 73 | 74 | long ReadPos = 0; 75 | for (int x = 0; x < Entries[i].SegmentsInfo.Length; x++) { 76 | var SegmentData = FilesData[i].CreateStream(ReadPos, Entries[i].SegmentsInfo[x].DecompressedSize); 77 | ReadPos += SegmentData.Length; 78 | 79 | //Compress only if the compressed data is smaller 80 | var Stream = Compress ? SegmentData.Compress(NPKVersion) : SegmentData; 81 | if (Stream.Length >= Entries[i].SegmentsInfo[x].DecompressedSize) 82 | Stream = SegmentData; 83 | 84 | using var Crypted = Stream.CreateEncryptor(CurrentKey, CurrentIV).ToMemory(); 85 | 86 | Entries[i].SegmentsInfo[x].RealSize = (uint)Stream.Length; 87 | Entries[i].SegmentsInfo[x].AlignedSize = (uint)Crypted.Length; 88 | Entries[i].SegmentsInfo[x].Offset = (uint)Output.Position; 89 | 90 | Crypted.CopyTo(Output); 91 | } 92 | } 93 | 94 | Output.Position = TablePos; 95 | 96 | Console.WriteLine("Writing File Index..."); 97 | 98 | using var TableData = BuildEntries(Entries); 99 | using var TableEncryptor = TableData.CreateEncryptor(CurrentKey, CurrentIV); 100 | using var OutTableData = TableEncryptor.ToMemory(); 101 | Debug.Assert(OutTableData.Length == TableSize); 102 | OutTableData.CopyTo(Output); 103 | } 104 | } 105 | 106 | public static void SetIV(string IVHex) { 107 | IVHex = IVHex.Trim().Replace(" ", ""); 108 | if (IVHex.Length != CurrentIV.Length * 2) { 109 | Console.WriteLine("Warning: Invalid IV"); 110 | return; 111 | } 112 | for (int x = 0; x < IVHex.Length; x += 2) { 113 | string HByte = IVHex.Substring(x, 2); 114 | CurrentIV[x / 2] = Convert.ToByte(HByte, 16); 115 | } 116 | } 117 | public static void SetKey(string KeyHex) { 118 | CurrentKey = new byte[0x20]; 119 | KeyHex = KeyHex.Trim().Replace(" ", ""); 120 | if (KeyHex.Length != CurrentKey.Length * 2) { 121 | Console.WriteLine("Warning: Invalid KEY"); 122 | return; 123 | } 124 | for (int x = 0; x < KeyHex.Length; x += 2) { 125 | string HByte = KeyHex.Substring(x, 2); 126 | CurrentKey[x / 2] = Convert.ToByte(HByte, 16); 127 | } 128 | } 129 | 130 | public static void SetEncoding(string Name) { 131 | Encoding = Name.ToEncoding(); 132 | } 133 | 134 | public static void SetMaxSectionSize(string MaxSize) { 135 | MaxSize = MaxSize.Trim(); 136 | if (MaxSize.ToLower().StartsWith("0x")) 137 | { 138 | MaxSize = MaxSize.Substring(2); 139 | MaxSectionSize = uint.Parse(MaxSize, System.Globalization.NumberStyles.HexNumber); 140 | } 141 | else 142 | MaxSectionSize = uint.Parse(MaxSize); 143 | } 144 | 145 | public static NPK3Entry[] CreateInitialEntries(string[] Files, Stream[] Streams) { 146 | Console.WriteLine("Loading Files..."); 147 | 148 | List Entries = new List(); 149 | for (int i = 0; i < Files.Length; i++) { 150 | NPK3Entry Entry = new NPK3Entry(); 151 | Entry.FilePath = Files[i].Replace("\\", "/"); 152 | Entry.FileSize = (uint)Streams[i].Length; 153 | Entry.SHA256 = Streams[i].SHA256Checksum(); 154 | 155 | long Reaming = Entry.FileSize; 156 | if (EnableSegmentation || Reaming > uint.MaxValue || ForceSegmentation) 157 | { 158 | if (!EnableSegmentation) { 159 | var OriColor = Console.ForegroundColor; 160 | Console.ForegroundColor = ConsoleColor.Yellow; 161 | Console.WriteLine($"Big File Detected: '{Entry.FilePath}', Enforcing Segmentation"); 162 | Console.ForegroundColor = OriColor; 163 | } 164 | 165 | Entry.SegmentsInfo = new NPKSegmentInfo[1 + (Entry.FileSize / MaxSectionSize)]; 166 | Entry.SegmentationMode = (byte)(Entry.SegmentsInfo.Length > 1 ? 0 : 1); 167 | 168 | if (ForceSegmentation) 169 | Entry.SegmentationMode = 0; 170 | 171 | for (int x = 0; x < Entry.SegmentsInfo.Length; x++) 172 | { 173 | uint MaxBytes = Reaming < MaxSectionSize ? (uint)Reaming : MaxSectionSize; 174 | Entry.SegmentsInfo[x] = new NPKSegmentInfo() 175 | { 176 | Offset = 0, 177 | DecompressedSize = MaxBytes, 178 | RealSize = MaxBytes, 179 | AlignedSize = MaxBytes + (0x10 - (MaxBytes % 0x10)) 180 | }; 181 | 182 | Reaming -= MaxBytes; 183 | } 184 | } 185 | else 186 | { 187 | Entry.SegmentationMode = 1; 188 | Entry.SegmentsInfo = new NPKSegmentInfo[] { 189 | new NPKSegmentInfo(){ 190 | Offset = 0, 191 | DecompressedSize = (uint)Reaming, 192 | RealSize = (uint)Reaming, 193 | AlignedSize = (uint)Reaming + (0x10 - ((uint)Reaming % 0x10)) 194 | } 195 | }; 196 | } 197 | 198 | Entries.Add(Entry); 199 | } 200 | 201 | return Entries.ToArray(); 202 | } 203 | 204 | public static Stream BuildEntries(NPK3Entry[] Entries) { 205 | Stream Output = new MemoryStream(); 206 | StructWriter Writer = new StructWriter(Output, Encoding: Encoding); 207 | for (int i = 0; i < Entries.Length; i++) { 208 | var Entry = Entries[i]; 209 | Writer.WriteStruct(ref Entry); 210 | } 211 | Output.Position = 0; 212 | return Output; 213 | } 214 | 215 | public static void Unpack(string Package, string OutDir = null) 216 | { 217 | if (OutDir == null) 218 | OutDir = Path.Combine(Path.GetDirectoryName(Package), Path.GetFileName(Package) + "~"); 219 | 220 | if (new FileInfo(Package).IsReadOnly) { 221 | throw new Exception("Can't Unpack Read-Only Files"); 222 | } 223 | 224 | using (Stream NPK = File.Open(Package, FileMode.Open)) 225 | { 226 | switch (NPK.ReadUInt32(0)) { 227 | case 0x334B504Eu: 228 | NPKVersion = 3; 229 | break; 230 | case 0x324B504Eu: 231 | NPKVersion = 2; 232 | break; 233 | default: 234 | throw new NotSupportedException("NPK Version Not Supported"); 235 | } 236 | 237 | CurrentIV = NPK.ReadBytes(8, 0x10); 238 | var Table = GetEntryTable(NPK); 239 | var Entries = GetEntries(Table); 240 | 241 | foreach (var Entry in Entries) 242 | { 243 | Console.WriteLine($"Extracting File: {Entry.FilePath}"); 244 | string OutPath = Path.Combine(OutDir, Entry.FilePath.Replace("/", Path.DirectorySeparatorChar.ToString())); 245 | 246 | if (!Directory.Exists(Path.GetDirectoryName(OutPath))) 247 | Directory.CreateDirectory(Path.GetDirectoryName(OutPath)); 248 | 249 | using (Stream Output = File.Create(OutPath)) 250 | { 251 | foreach (var Segment in Entry.SegmentsInfo) 252 | { 253 | long Offset = Segment.Offset; 254 | uint Size = Segment.AlignedSize; 255 | 256 | using (Stream Buffer = new MemoryStream()) 257 | { 258 | var Reader = NPK.CreateStream(Offset, Size).CreateDecryptor(CurrentKey, CurrentIV); 259 | Reader.CopyTo(Buffer); 260 | 261 | Buffer.Position = 0; 262 | if (Segment.IsCompressed) { 263 | var Decompressor = Buffer.CreateDecompressor(NPKVersion); 264 | Decompressor.CopyTo(Output); 265 | } 266 | else 267 | Buffer.CopyTo(Output); 268 | } 269 | } 270 | } 271 | } 272 | } 273 | } 274 | public static NPK3Entry[] GetEntries(Stream EntryTable) { 275 | List Entries = new List(); 276 | StructReader Reader = new StructReader(EntryTable, Encoding: Encoding); 277 | while (Reader.BaseStream.Position + 1 < Reader.BaseStream.Length) { 278 | var Entry = new NPK3Entry(); 279 | Reader.ReadStruct(ref Entry); 280 | Entries.Add(Entry); 281 | } 282 | return Entries.ToArray(); 283 | } 284 | 285 | public static Stream GetEntryTable(Stream Package) { 286 | uint TableSize = Package.ReadUInt32(0x1C); 287 | var CryptedTable = Package.CreateStream(0x20, TableSize); 288 | return CryptedTable.CreateDecryptor(CurrentKey, CurrentIV).ToMemory(); 289 | } 290 | } 291 | #pragma warning disable 0219, 0649 292 | struct NPK3Entry 293 | { 294 | public byte SegmentationMode;//0 = With Segmentation, 1 = Without Segmentation 295 | 296 | [PString(PrefixType = Const.UINT16)] 297 | public string FilePath; 298 | 299 | public uint FileSize; 300 | 301 | [FArray(Length = 0x20)] 302 | public byte[] SHA256; 303 | 304 | [PArray(PrefixType = Const.UINT32), StructField] 305 | public NPKSegmentInfo[] SegmentsInfo; 306 | } 307 | 308 | struct NPKSegmentInfo { 309 | public long Offset; 310 | public uint AlignedSize; 311 | public uint RealSize; 312 | public uint DecompressedSize; 313 | 314 | [Ignore] 315 | public bool IsCompressed => RealSize < DecompressedSize; 316 | } 317 | #pragma warning restore 0219, 0649 318 | } 319 | -------------------------------------------------------------------------------- /NPK3Tool/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | 7 | namespace NPK3Tool 8 | { 9 | static class Program 10 | { 11 | //The "MinorVersion" is just something that I suposed, and i'm not sure, maybe that is just a flag 12 | static string CurrentExe => Path.GetFileName(Assembly.GetExecutingAssembly().Location); 13 | static void Main(string[] args) 14 | { 15 | Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); 16 | 17 | //System.Diagnostics.Debugger.Launch(); 18 | 19 | Console.OutputEncoding = Encoding.Unicode; 20 | Console.Title = "NPK3Tool - By Marcussacana"; 21 | 22 | if (args == null || args.Length == 0) 23 | args = new[] { "-h" }; 24 | 25 | for (int i = 0; i < args.Length; i++) 26 | { 27 | var flag = args[i].ToLower().TrimStart('-', '/', '\\'); 28 | switch (flag) 29 | { 30 | case "h": 31 | case "?": 32 | case "help": 33 | Console.WriteLine("Usage:"); 34 | Console.WriteLine($"{CurrentExe} [Options] -u Input.npk OutDir"); 35 | Console.WriteLine($"{CurrentExe} [Options] -r InputDir Output.npk"); 36 | Console.WriteLine(); 37 | Console.WriteLine("Options:"); 38 | Console.WriteLine("-IV [128bit hex]\t\t\tSet the repack IV"); 39 | Console.WriteLine("-MS 0x20000\t\t\t\tSet the NPK section Size"); 40 | Console.WriteLine("-EC UTF8\t\t\t\tSet the NPK filename encoding"); 41 | Console.WriteLine("-KY [256bit hex]\t\t\tSet a custom encyption key"); 42 | Console.WriteLine("-VS [3/2]\t\t\t\tSet the NPK repack version"); 43 | Console.WriteLine("-MV [1/2]\t\t\t\tSet the NPK repack minor version"); 44 | Console.WriteLine("-SG [1/0]\t\t\t\tEnable/Disable NPK Segmentation (Auto)"); 45 | Console.WriteLine("-FG [1/0]\t\t\t\tEnable/Disable NPK Segmentation (Forced)"); 46 | Console.WriteLine("-CP [1/0]\t\t\t\tEnable/Disable NPK Compression"); 47 | Console.WriteLine("-GM 0\t\t\t\t\tSet the NPK Game ID"); 48 | Console.WriteLine(); 49 | Console.WriteLine("Valid Game IDs:"); 50 | for (int x = 0; x < Games.Length; x++) 51 | { 52 | Console.WriteLine($"{x}: {Games[x].Game}"); 53 | } 54 | 55 | Console.WriteLine(); 56 | var Color = Console.ForegroundColor; 57 | Console.ForegroundColor = ConsoleColor.Yellow; 58 | Console.WriteLine("It's hard?"); 59 | Console.WriteLine("... then just Drag&Drop"); 60 | Console.WriteLine(); 61 | Console.ForegroundColor = Color; 62 | Console.WriteLine("Debug:"); 63 | Console.WriteLine("-DumpTable Input.npk\t\t\tDump File Table from the NPK"); 64 | Console.ReadKey(); 65 | break; 66 | case "u": 67 | EnsureGameSelection(); 68 | NPK.Unpack(args[++i], args[++i]); 69 | break; 70 | case "r": 71 | EnsureGameSelection(); 72 | NPK.Repack(args[++i], args[++i]); 73 | break; 74 | case "iv": 75 | NPK.SetIV(args[++i]); 76 | break; 77 | case "ms": 78 | NPK.SetMaxSectionSize(args[++i]); 79 | break; 80 | case "ec": 81 | NPK.SetEncoding(args[++i]); 82 | break; 83 | case "ky": 84 | NPK.SetKey(args[++i]); 85 | break; 86 | case "vs": 87 | if (int.TryParse(args[++i], out int VS)) 88 | { 89 | if (VS != 2 && VS != 3) 90 | { 91 | Console.WriteLine("Unsupported NPK Version"); 92 | continue; 93 | } 94 | NPK.NPKVersion = VS; 95 | } 96 | break; 97 | case "mv": 98 | if (uint.TryParse(args[++i], out uint MV)) 99 | NPK.NPKMinorVersion = MV; 100 | break; 101 | case "sg": 102 | string SG = args[++i].Trim().ToLower(); 103 | NPK.EnableSegmentation = SG == "1" || SG == "true" || SG == "yes" || SG == "y"; 104 | break; 105 | case "fg": 106 | string FG = args[++i].Trim().ToLower(); 107 | NPK.ForceSegmentation = FG == "1" || FG == "true" || FG == "yes" || FG == "y"; 108 | break; 109 | case "cp": 110 | string CP = args[++i].Trim().ToLower(); 111 | NPK.EnableCompression = CP == "1" || CP == "true" || CP == "yes" || CP == "y"; 112 | break; 113 | case "gm": 114 | if (int.TryParse(args[++i], out int GM)) 115 | { 116 | NPK.CurrentKey = Games[GM].Item2; 117 | NPK.Encoding = Games[GM].Item3; 118 | NPK.NPKVersion = Games[GM].Item4; 119 | NPK.EnableSegmentation = Games[GM].Item5; 120 | NPK.NPKMinorVersion = Games[GM].Item6; 121 | } 122 | break; 123 | case "dumptable": 124 | EnsureGameSelection(); 125 | using (var Input = File.Open(args[++i], FileMode.Open)) 126 | using (var Output = File.Create(args[i] + ".tbl")) 127 | { 128 | NPK.CurrentIV = Input.ReadBytes(8, 0x10); 129 | var Table = NPK.GetEntryTable(Input); 130 | Table.Position = 0; 131 | Table.CopyTo(Output); 132 | } 133 | break; 134 | default: 135 | if (File.Exists(args[i])) 136 | { 137 | EnsureGameSelection(); 138 | NPK.Unpack(args[i]); 139 | } 140 | else if (Directory.Exists(args[i])) 141 | { 142 | EnsureGameSelection(); 143 | NPK.Repack(args[i]); 144 | } 145 | break; 146 | } 147 | } 148 | } 149 | 150 | static void EnsureGameSelection() 151 | { 152 | if (NPK.CurrentKey != null) 153 | return; 154 | 155 | int Custom = 0; 156 | int Current = 0; 157 | if (Games.Length > 1) 158 | { 159 | foreach (var Game in Games) 160 | { 161 | Console.WriteLine($"Type {Current++} to \"{Game.Game}\""); 162 | } 163 | 164 | Console.WriteLine($"Type {Custom = Current} to manually specify a encryption key."); 165 | 166 | while (!int.TryParse(Console.ReadLine(), out Current)) 167 | continue; 168 | } 169 | 170 | if (Current == Custom) 171 | { 172 | Console.WriteLine("Type the 256bits key hex:"); 173 | NPK.SetKey(Console.ReadLine()); 174 | Console.WriteLine("Use what encoding to read the NPK? (UTF8/SJIS)"); 175 | NPK.SetEncoding(Console.ReadLine()); 176 | return; 177 | } 178 | 179 | Console.WriteLine($"Game \"{Games[Current].Game}\" Selected"); 180 | NPK.CurrentKey = Games[Current].Key; 181 | NPK.Encoding = Games[Current].Encoding; 182 | NPK.NPKVersion = Games[Current].NPKVersion; 183 | NPK.EnableSegmentation = Games[Current].NPKSegmentation; 184 | NPK.NPKMinorVersion = Games[Current].NPKMinorVersion; 185 | } 186 | 187 | //Name, Key, Encoding, NPKVersion, Segmentation, MinorVersion 188 | readonly static (string Game, byte[] Key, Encoding Encoding, int NPKVersion, bool NPKSegmentation, uint NPKMinorVersion)[] Games = new (string Game, byte[] Key, Encoding Encoding, int NPKVersion, bool NPKSegmentation, uint NPKMinorVersion)[] { 189 | ("You and Me and Her (Jast USA)", new byte[] { 190 | 0xE7, 0xE8, 0xA5, 0xF9, 0x9B, 0xAF, 0x7C, 0x73, 0xAE, 0x6B, 0xDF, 0x3D, 0x8C, 0x90, 0x26, 0x2F, 191 | 0xF2, 0x50, 0x25, 0xA1, 0x2D, 0xB5, 0x39, 0xF9, 0xCF, 0xD6, 0xE8, 0xE5, 0x79, 0x75, 0xB7, 0x98 192 | }, Encoding.UTF8 , 3, true , 1u), 193 | ("You and Me and Her (Steam)", new byte[] { 194 | 0xF8, 0x37, 0x0F, 0x24, 0xCA, 0x4E, 0x84, 0x4C, 0x6E, 0xEB, 0xF8, 0xB8, 0x60, 0x19, 0x5B, 0x6D, 195 | 0x72, 0x26, 0xB0, 0x7D, 0x20, 0x1F, 0x40, 0x31, 0x9C, 0xBC, 0x11, 0x1D, 0x96, 0xE1, 0xC1, 0x31 196 | }, Encoding.UTF8 , 3, true , 1u), 197 | ("Tokyo Necro", new byte[] { 198 | 0x96, 0x2C, 0x5F, 0x3A, 0x78, 0x9C, 0x84, 0x37, 0xB7, 0x12, 0x12, 0xA1, 0x15, 0xD6, 0xCA, 0x9F, 199 | 0x9A, 0xE3, 0xFD, 0x21, 0x0F, 0xF6, 0xAF, 0x70, 0xA8, 0xA8, 0xF8, 0xBB, 0xFE, 0x5E, 0x8A, 0xF5 200 | }, Encoding.GetEncoding(932), 2, false, 1u), 201 | ("Minikui Mojika no Ko", new byte[] { 202 | 0xAA, 0x45, 0x60, 0xF7, 0x83, 0xF7, 0x8A, 0x90, 0x20, 0x5D, 0xC1, 0x4E, 0x54, 0x09, 0x67, 0x04, 203 | 0x09, 0xBC, 0x00, 0x46, 0x39, 0x17, 0x5A, 0xD9, 0xC0, 0xB3, 0xD2, 0x97, 0xDA, 0x2F, 0x38, 0x68 204 | }, Encoding.UTF8 , 2, false, 2u), 205 | ("SoniComi (JastUSA)", new byte[] { 206 | 0x65, 0xAB, 0xB4, 0xA8, 0xCD, 0xE0, 0xC8, 0x10, 0xBB, 0x4A, 0x26, 0x72, 0x37, 0x54, 0xC3, 0xA7, 207 | 0xE4, 0x3D, 0xE9, 0xEA, 0x7F, 0x5B, 0xB8, 0x43, 0x50, 0x1D, 0x05, 0xAB, 0xCF, 0x08, 0xD9, 0xC1 208 | }, Encoding.GetEncoding(932), 2, false, 1u), 209 | ("The Song of Saya (Steam)", new byte[] { 210 | 0x76, 0x3A, 0x14, 0x33, 0x8B, 0x0D, 0xAC, 0x04, 0x0A, 0xCC, 0xFC, 0x13, 0x85, 0x1C, 0xFA, 0xCB, 211 | 0xB2, 0x0B, 0x00, 0x0B, 0x01, 0xF8, 0x68, 0x48, 0x6B, 0x46, 0x0F, 0x8C, 0x34, 0xD4, 0x2A, 0x96 212 | }, Encoding.GetEncoding(932), 2, false, 2u), 213 | ("The Song of Saya (Steam) [+18]", new byte[] { 214 | 0xD0, 0xB7, 0x1F, 0x3C, 0x4E, 0x24, 0xCE, 0xCF, 0xDD, 0xEE, 0xA9, 0x1D, 0x24, 0xB0, 0x40, 0x32, 215 | 0x29, 0xA3, 0xE5, 0x33, 0x0D, 0x29, 0x51, 0x82, 0x60, 0x51, 0xD6, 0xC9, 0x4A, 0xF5, 0xAF, 0x54 216 | }, Encoding.GetEncoding(932), 2, false, 2u), 217 | ("Kishin Houkou Demonbane", new byte[] { 218 | 0xBE, 0x28, 0x02, 0xAD, 0x5E, 0x91, 0xDD, 0x8E, 0x26, 0xEA, 0xD6, 0xB1, 0x61, 0xFE, 0xDB, 0x8A, 219 | 0x17, 0xE2, 0x36, 0x2F, 0x53, 0x33, 0x6D, 0x1B, 0x17, 0xD8, 0x0A, 0xE9, 0x55, 0xC0, 0x5A, 0xED 220 | }, Encoding.UTF8 , 2, false, 2u), 221 | ("DRAMAtical Murder (Jast USA)", new byte[] { 222 | 0x0F, 0x82, 0x31, 0x9A, 0x9C, 0xF6, 0xFB, 0x30, 0x36, 0xAA, 0x9F, 0x7E, 0x60, 0x29, 0xF3, 0x31, 223 | 0xA0, 0xC0, 0xE1, 0x5B, 0x05, 0xDB, 0xC9, 0xC4, 0xB6, 0x10, 0x3B, 0xB9, 0xA2, 0x5F, 0xDC, 0x9C 224 | }, Encoding.UTF8 , 3, true, 1u), 225 | ("DRAMAtical Murder (Steam)", new byte[] { 226 | 0x75, 0x93, 0xFC, 0x9B, 0xA5, 0xA4, 0x83, 0x19, 0x03, 0x18, 0x92, 0xBC, 0x1A, 0xB1, 0x72, 0x37, 227 | 0x05, 0x6A, 0xAA, 0x63, 0xBA, 0xD7, 0x9C, 0xD4, 0x46, 0xB1, 0xF0, 0x41, 0x55, 0xF8, 0x70, 0xEB 228 | }, Encoding.UTF8 , 3, true, 1u), 229 | ("Full Metal Daemon Muramasa (Jast USA)", new byte[] { 230 | 0x51, 0x83, 0x1A, 0x9E, 0x69, 0xEF, 0xA8, 0x5E, 0xE6, 0xC5, 0x51, 0xC2, 0x08, 0xDB, 0x18, 0x04, 231 | 0xB4, 0x37, 0x50, 0x23, 0x56, 0xAE, 0x1F, 0x5E, 0x29, 0xB1, 0x28, 0x88, 0x40, 0x3E, 0x78, 0xA8 232 | }, Encoding.UTF8 , 3, false, 1u), 233 | ("Slow Damage (Jast USA)", new byte[] { 234 | 0xFD, 0x78, 0x2F, 0xE4, 0xA9, 0x8F, 0xD6, 0xB1, 0x44, 0x8E, 0x29, 0xF5, 0xB2, 0xEA, 0x44, 0xE9, 235 | 0x6B, 0x12, 0x66, 0xE5, 0x06, 0x96, 0x69, 0x8B, 0x8F, 0x6F, 0xEC, 0xA8, 0x2D, 0x7F, 0xD2, 0xDC 236 | }, Encoding.UTF8 , 3, false, 1u), 237 | ("Tokyo Necro (Jast USA)", new byte[] { 238 | 0x92, 0x0A, 0x2C, 0xBD, 0x4A, 0xF0, 0x19, 0xC9, 0x5F, 0x4E, 0x94, 0x2D, 0x05, 0xF9, 0x06, 0xC7, 239 | 0xA6, 0x81, 0x26, 0xCD, 0x85, 0x84, 0x6E, 0x5A, 0x66, 0x92, 0xC7, 0xCA, 0x04, 0x83, 0xD1, 0x85 240 | }, Encoding.UTF8 , 2, false, 2u), 241 | ("sweet pool (Jast USA)", new byte[] { 242 | 0x08, 0xAC, 0xDE, 0xE7, 0x6D, 0x0F, 0xCB, 0x6A, 0x85, 0xAA, 0x92, 0xD2, 0xAC, 0x73, 0x91, 0x1F, 243 | 0xCA, 0x8E, 0x60, 0x64, 0x54, 0xF9, 0x18, 0x7D, 0x43, 0xAA, 0x95, 0xF2, 0x7A, 0x9E, 0xBF, 0xB8 244 | }, Encoding.UTF8 , 2, true, 2u), 245 | ("Togainu no Chi (Jast USA)", new byte[] { 246 | 0x6A, 0x93, 0x56, 0x69, 0x9D, 0x65, 0x1E, 0xEA, 0x2C, 0x8C, 0x82, 0xD6, 0xFD, 0xCC, 0x8E, 0x35, 247 | 0xF0, 0x81, 0x0A, 0xF8, 0x32, 0x4A, 0x6C, 0x49, 0x22, 0x25, 0xCA, 0x95, 0x7A, 0x8A, 0xE2, 0x2F 248 | }, Encoding.UTF8 , 2, true, 2u), 249 | ("Mojika: Truth Rears Its Ugly Head (Steam)", new byte[] { 250 | 0x0D, 0x34, 0xB8, 0x43, 0xD5, 0x80, 0x37, 0xF5, 0xC5, 0xDD, 0xA5, 0x21, 0x57, 0x3C, 0x8F, 0xF0, 251 | 0xFF, 0xBB, 0xE4, 0x05, 0x0C, 0x2A, 0x6C, 0xF1, 0x2F, 0x16, 0x38, 0x65, 0xDD, 0x12, 0x43, 0xB1 252 | }, Encoding.UTF8 , 3, true, 1u), 253 | ("Jingai Makyou - Windows 10 Support Edition", new byte[] { 254 | 0x58, 0xAC, 0xA2, 0x11, 0xE8, 0xC4, 0xEE, 0xBA, 0x76, 0x5E, 0x19, 0xAF, 0x37, 0x1E, 0x34, 0xBB, 255 | 0x9D, 0x5B, 0x93, 0x96, 0x1E, 0x80, 0x48, 0x8D, 0x02, 0xFD, 0x56, 0x83, 0x70, 0x9B, 0x24, 0xE7 256 | }, Encoding.UTF8 , 2, true, 2u), 257 | }; 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /NPK3Tool/StructStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.IO; 5 | using System.Reflection; 6 | using System.Collections; 7 | 8 | /// 9 | /// Advanced Binary Tools - By Marcussacana 10 | /// 11 | namespace AdvancedBinary { 12 | 13 | public enum StringStyle { 14 | /// 15 | /// C-Style String (null terminated) 16 | /// 17 | CString, 18 | /// 19 | /// Unicode C-Style String (null terminated 2x) 20 | /// 21 | UCString, 22 | /// 23 | /// Pascal-Style String (Prefixed with the Length) 24 | /// 25 | PString 26 | } 27 | 28 | 29 | /// 30 | /// InvokeMethod While Reading 31 | /// 32 | /// Stream Instance 33 | /// Determine if the method is invoked from the StructReader or StructWriter 34 | /// Determine if the Stream Struct is in the BigEndian Mode 35 | /// Struct instance reference 36 | /// New Struct Instance 37 | public delegate dynamic FieldInvoke(Stream Stream, bool FromReader, bool BigEndian, dynamic StructInstance); 38 | 39 | /// 40 | /// Ignore Struct Field 41 | /// 42 | public class Ignore : Attribute { } 43 | 44 | /// 45 | /// C-Style String (null terminated) 46 | /// 47 | public class CString : Attribute { } 48 | 49 | /// 50 | /// Unicode C-Style String (null terminated 2x) 51 | /// 52 | public class UCString : Attribute { } 53 | /// 54 | /// Pascal-Style String (int32 Length Prefix) 55 | /// 56 | public class PString : Attribute { 57 | public string PrefixType = Const.UINT32; 58 | public bool UnicodeLength; 59 | } 60 | 61 | /// 62 | /// Pre-Defined String Length 63 | /// 64 | public class FString : Attribute { 65 | public long Length; 66 | public bool TrimNull; 67 | } 68 | 69 | 70 | /// 71 | /// Fixed Length Array 72 | /// 73 | public class FArray : Attribute { 74 | public long Length; 75 | } 76 | 77 | /// 78 | /// Prefixed Length Array 79 | /// 80 | public class PArray : Attribute { 81 | public string PrefixType = Const.UINT32; 82 | } 83 | 84 | /// 85 | /// Struct Field Type (required only to sub structs) 86 | /// 87 | public class StructField : Attribute { } 88 | public class Const { 89 | //Types 90 | public const string INT8 = "System.SByte"; 91 | public const string UINT8 = "System.Byte"; 92 | public const string INT16 = "System.Int16"; 93 | public const string UINT16 = "System.UInt16"; 94 | public const string INT32 = "System.Int32"; 95 | public const string UINT32 = "System.UInt32"; 96 | public const string DOUBLE = "System.Double"; 97 | public const string FLOAT = "System.Single"; 98 | public const string INT64 = "System.Int64"; 99 | public const string UINT64 = "System.UInt64"; 100 | public const string STRING = "System.String"; 101 | public const string DELEGATE = "System.MulticastDelegate"; 102 | public const string CHAR = "System.Char"; 103 | 104 | //Attributes 105 | public const string PSTRING = "PString"; 106 | public const string CSTRING = "CString"; 107 | public const string UCSTRING = "UCString"; 108 | public const string FSTRING = "FString"; 109 | public const string STRUCT = "StructField"; 110 | public const string IGNORE = "Ignore"; 111 | public const string FARRAY = "FArray"; 112 | public const string PARRAY = "PArray"; 113 | } 114 | 115 | static class Tools { 116 | /* 117 | public static dynamic GetAttributePropertyValue(T Struct, string FieldName, string AttributeName, string PropertyName) { 118 | Type t = Struct.GetType(); 119 | FieldInfo[] Fields = t.GetFields(BindingFlags.NonPublic | BindingFlags.Public); 120 | foreach (FieldInfo Fld in Fields) { 121 | if (Fld.Name != FieldName) 122 | continue; 123 | foreach (Attribute tmp in Fld.GetCustomAttributes(true)) { 124 | Type Attrib = tmp.GetType(); 125 | if (Attrib.Name != AttributeName) 126 | continue; 127 | foreach (FieldInfo Field in Attrib.GetFields()) { 128 | if (Field.Name != PropertyName) 129 | continue; 130 | return Field.GetValue(tmp); 131 | } 132 | throw new Exception("Property Not Found"); 133 | } 134 | throw new Exception("Attribute Not Found"); 135 | } 136 | throw new Exception("Field Not Found"); 137 | }*/ 138 | 139 | public static void CopyStream(Stream input, Stream output) { 140 | int Readed = 0; 141 | byte[] Buffer = new byte[1024 * 1024]; 142 | do { 143 | Readed = input.Read(Buffer, 0, Buffer.Length); 144 | output.Write(Buffer, 0, Readed); 145 | } while (Readed > 00); 146 | } 147 | 148 | public static dynamic Reverse(dynamic Data) { 149 | byte[] Arr = BitConverter.GetBytes(Data); 150 | Array.Reverse(Arr, 0, Arr.Length); 151 | string type = Data.GetType().FullName; 152 | switch (type) { 153 | case Const.INT8: 154 | case Const.UINT8: 155 | return Data; 156 | case Const.INT16: 157 | return BitConverter.ToInt16(Arr, 0); 158 | case Const.UINT16: 159 | return BitConverter.ToUInt16(Arr, 0); 160 | case Const.INT32: 161 | return BitConverter.ToInt32(Arr, 0); 162 | case Const.UINT32: 163 | return BitConverter.ToUInt32(Arr, 0); 164 | case Const.INT64: 165 | return BitConverter.ToInt64(Arr, 0); 166 | case Const.UINT64: 167 | return BitConverter.ToUInt64(Arr, 0); 168 | case Const.DOUBLE: 169 | return BitConverter.ToDouble(Arr, 0); 170 | case Const.FLOAT: 171 | return BitConverter.ToSingle(Arr, 0); 172 | default: 173 | throw new Exception("Unk Data Type."); 174 | } 175 | } 176 | 177 | public static dynamic GetAttributePropertyValue(FieldInfo Fld, string AttributeName, string PropertyName) { 178 | foreach (Attribute tmp in Fld.GetCustomAttributes(true)) { 179 | Type Attrib = tmp.GetType(); 180 | if (Attrib.Name != AttributeName) 181 | continue; 182 | foreach (FieldInfo Field in Attrib.GetFields()) { 183 | if (Field.Name != PropertyName) 184 | continue; 185 | return Field.GetValue(tmp); 186 | } 187 | throw new Exception("Property Not Found"); 188 | } 189 | throw new Exception("Attribute Not Found"); 190 | } 191 | 192 | public static long GetStructLength(T Struct) { 193 | Type type = Struct.GetType(); 194 | FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); 195 | long Length = 0; 196 | foreach (FieldInfo field in fields) { 197 | if (HasAttribute(field, Const.IGNORE)) 198 | continue; 199 | switch (field.FieldType.ToString()) { 200 | case Const.INT8: 201 | case Const.UINT8: 202 | Length += 1; 203 | break; 204 | case Const.INT32: 205 | case Const.FLOAT: 206 | case Const.UINT32: 207 | Length += 4; 208 | break; 209 | case Const.UINT64: 210 | case Const.INT64: 211 | case Const.DOUBLE: 212 | Length += 8; 213 | break; 214 | case Const.STRING: 215 | if (!HasAttribute(field, Const.FSTRING)) 216 | throw new Exception("You can't calculate struct length with strings"); 217 | else 218 | Length += GetAttributePropertyValue(field, Const.FSTRING, "Length"); 219 | break; 220 | default: 221 | if (field.FieldType.BaseType.ToString() == Const.DELEGATE) 222 | break; 223 | if (HasAttribute(field, Const.IGNORE)) 224 | break; 225 | throw new Exception("Unk Struct Field: " + field.FieldType.ToString()); 226 | } 227 | } 228 | return Length; 229 | } 230 | 231 | internal static bool HasAttribute(FieldInfo Field, string Attrib) { 232 | foreach (Attribute attrib in Field.GetCustomAttributes(true)) 233 | if (attrib.GetType().Name == Attrib) 234 | return true; 235 | return false; 236 | } 237 | public static void ReadStruct(byte[] Array, ref T Struct, bool IsBigEnddian = false, Encoding Encoding = null, long BaseOffset = 0) { 238 | MemoryStream Stream = new MemoryStream(Array); 239 | StructReader Reader = new StructReader(Stream, IsBigEnddian, Encoding); 240 | Reader.Seek(BaseOffset, SeekOrigin.Begin); 241 | Reader.ReadStruct(ref Struct); 242 | Reader.Close(); 243 | Stream?.Close(); 244 | } 245 | 246 | public static byte[] BuildStruct(ref T Struct, bool BigEndian = false, Encoding Encoding = null) { 247 | MemoryStream Stream = new MemoryStream(); 248 | StructWriter Writer = new StructWriter(Stream, BigEndian, Encoding); 249 | Writer.WriteStruct(ref Struct); 250 | byte[] Result = Stream.ToArray(); 251 | Writer.Close(); 252 | Stream?.Close(); 253 | return Result; 254 | } 255 | 256 | internal static void CopyStruct(T Input, ref T Output) { 257 | Type type = Input.GetType(); 258 | object tmp = Output; 259 | FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); 260 | for (int i = 0; i < fields.Length; i++) { 261 | object value = fields[i].GetValue(Input); 262 | fields[i].SetValue(tmp, value); 263 | } 264 | Output = (T)tmp; 265 | } 266 | } 267 | 268 | public class StructWriter : BinaryWriter { 269 | 270 | public bool BigEndian { get; private set; } = false; 271 | internal Encoding Encoding; 272 | 273 | public StructWriter(Stream Output, bool BigEndian = false, Encoding Encoding = null) : base(Output) { 274 | if (Encoding == null) 275 | Encoding = Encoding.UTF8; 276 | this.BigEndian = BigEndian; 277 | this.Encoding = Encoding; 278 | } 279 | public StructWriter(string OutputFile, bool BigEndian = false, Encoding Encoding = null) : base(new StreamWriter(OutputFile).BaseStream) { 280 | if (Encoding == null) 281 | Encoding = Encoding.UTF8; 282 | this.BigEndian = BigEndian; 283 | this.Encoding = Encoding; 284 | } 285 | 286 | public void WriteStruct(ref T Struct) { 287 | Type type = Struct.GetType(); 288 | object tmp = Struct; 289 | WriteStruct(type, ref tmp); 290 | Struct = (T)tmp; 291 | } 292 | 293 | internal void WriteStruct(Type type, ref object Instance) { 294 | FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); 295 | foreach (FieldInfo field in fields) { 296 | if (HasAttribute(field, Const.IGNORE)) 297 | continue; 298 | dynamic Value = field.GetValue(Instance); 299 | string Type = field.FieldType.ToString(); 300 | if (!Type.EndsWith("[]")) { 301 | WriteField(Value, Type, field, ref Instance); 302 | continue; 303 | } 304 | if (HasAttribute(field, Const.FARRAY)) { 305 | long BufferLen = Tools.GetAttributePropertyValue(field, Const.FARRAY, "Length"); 306 | 307 | if (Value.Length > BufferLen) 308 | throw new Exception("Wrong Array Buffer Length"); 309 | 310 | byte[] Arr = new byte[BufferLen]; 311 | Value.CopyTo(Arr, 0); 312 | 313 | Write(Arr); 314 | continue; 315 | } 316 | 317 | if (!HasAttribute(field, Const.PARRAY)) 318 | throw new Exception("Bad Struct Array Configuration."); 319 | 320 | string PType = Tools.GetAttributePropertyValue(field, Const.PARRAY, "PrefixType"); 321 | dynamic Length = ParseType(Value.LongLength, PType); 322 | 323 | if (BigEndian) 324 | Length = Tools.Reverse(Length); 325 | Write(Length); 326 | 327 | System.Collections.IEnumerator Enum = Value.GetEnumerator(); 328 | for (long i = 0; i < Length; i++) { 329 | Enum.MoveNext(); 330 | WriteField(Enum.Current, Type.Substring(0, Type.Length-2), field, ref Instance); 331 | } 332 | } 333 | } 334 | 335 | internal dynamic ParseType(dynamic Value, string Type) { 336 | switch (Type) { 337 | case Const.INT8: 338 | return (sbyte)Value; 339 | case Const.UINT8: 340 | return (byte)Value; 341 | case Const.INT16: 342 | return (short)Value; 343 | case Const.UINT16: 344 | return (ushort)Value; 345 | case Const.INT32: 346 | return (int)Value; 347 | case Const.UINT32: 348 | return (uint)Value; 349 | case Const.INT64: 350 | return (long)Value; 351 | case Const.UINT64: 352 | return (ulong)Value; 353 | case Const.DOUBLE: 354 | return (double)Value; 355 | case Const.FLOAT: 356 | return (float)Value; 357 | default: 358 | throw new Exception("Bad Struct Configuration"); 359 | } 360 | } 361 | 362 | public void WriteRawType(string Type, dynamic Value) { 363 | object obj = new object(); 364 | WriteField(Value, Type, Type.GetType().GetFields()[0], ref obj); 365 | } 366 | 367 | internal void WriteField(dynamic Value, string Type, FieldInfo field, ref object Instance) { 368 | switch (Type) { 369 | case Const.STRING: 370 | if (HasAttribute(field, Const.CSTRING)) { 371 | Write(Value, StringStyle.CString); 372 | break; 373 | } 374 | if (HasAttribute(field, Const.UCSTRING)) { 375 | Write(Value, StringStyle.UCString); 376 | break; 377 | } 378 | if (HasAttribute(field, Const.PSTRING)) { 379 | Write(field, Value, StringStyle.PString); 380 | break; 381 | } 382 | if (HasAttribute(field, Const.FSTRING)) { 383 | byte[] Buffer = new byte[Tools.GetAttributePropertyValue(field, Const.FSTRING, "Length")]; 384 | Encoding.GetBytes((string)Value).CopyTo(Buffer, 0); 385 | Write(Buffer); 386 | break; 387 | } 388 | throw new Exception("String Attribute Not Specified."); 389 | default: 390 | if (HasAttribute(field, Const.STRUCT)) { 391 | WriteStruct(System.Type.GetType(Type), ref Value); 392 | } else { 393 | if (field.FieldType.BaseType.ToString() == Const.DELEGATE) { 394 | FieldInvoke Invoker = ((FieldInvoke)Value); 395 | if (Invoker == null) 396 | break; 397 | Instance = Invoker.Invoke(BaseStream, false, BigEndian, Instance); 398 | field.SetValue(Instance, Invoker); 399 | } else if (BigEndian) 400 | Write(Tools.Reverse(Value)); 401 | else 402 | Write(Value); 403 | } 404 | break; 405 | } 406 | } 407 | 408 | public void Write(string String, StringStyle Style) { 409 | switch (Style) { 410 | case StringStyle.UCString: 411 | case StringStyle.CString: 412 | List Buffer = new List(Encoding.GetBytes(String + "\x0")); 413 | base.Write(Buffer.ToArray(), 0, Buffer.Count); 414 | break; 415 | default: 416 | base.Write(String); 417 | break; 418 | } 419 | } 420 | public void Write(FieldInfo Field, dynamic Value, StringStyle Style) { 421 | switch (Style) { 422 | case StringStyle.UCString: 423 | case StringStyle.CString: 424 | List Buffer = new List(Encoding.GetBytes(Value + "\x0")); 425 | base.Write(Buffer.ToArray(), 0, Buffer.Count); 426 | break; 427 | case StringStyle.PString: 428 | WritePString(Field, Value); 429 | break; 430 | } 431 | } 432 | 433 | internal void WritePString(FieldInfo Field, dynamic Value) { 434 | byte[] Arr = Encoding.GetBytes(Value); 435 | 436 | string Prefix = Tools.GetAttributePropertyValue(Field, Const.PSTRING, "PrefixType"); 437 | bool UnicodeLength = Tools.GetAttributePropertyValue(Field, Const.PSTRING, "UnicodeLength"); 438 | 439 | 440 | long Length = Arr.LongLength; 441 | if (UnicodeLength) 442 | Length /= 2; 443 | 444 | switch (Prefix) { 445 | case Const.INT16: 446 | if (BigEndian) 447 | base.Write((short)Tools.Reverse((short)Length)); 448 | else 449 | base.Write((short)Length); 450 | break; 451 | case Const.UINT16: 452 | if (BigEndian) 453 | base.Write((ushort)Tools.Reverse((ushort)Length)); 454 | else 455 | base.Write((ushort)Length); 456 | break; 457 | case Const.UINT8: 458 | if (BigEndian) 459 | base.Write((byte)Tools.Reverse((byte)Length)); 460 | else 461 | base.Write((byte)Length); 462 | break; 463 | case Const.INT8: 464 | if (BigEndian) 465 | base.Write((sbyte)Tools.Reverse((sbyte)Length)); 466 | else 467 | base.Write((sbyte)Length); 468 | break; 469 | case Const.INT32: 470 | if (BigEndian) 471 | base.Write((int)Tools.Reverse((int)Length)); 472 | else 473 | base.Write((int)Length); 474 | break; 475 | case Const.UINT32: 476 | if (BigEndian) 477 | base.Write((uint)Tools.Reverse((uint)Length)); 478 | else 479 | base.Write((uint)Length); 480 | break; 481 | case Const.INT64: 482 | if (BigEndian) 483 | base.Write((long)Tools.Reverse(Length)); 484 | else 485 | base.Write(Length); 486 | break; 487 | default: 488 | throw new Exception("Invalid Data Type"); 489 | } 490 | base.Write(Arr); 491 | } 492 | 493 | internal bool HasAttribute(FieldInfo Field, string Attrib) { 494 | foreach (Attribute attrib in Field.GetCustomAttributes(true)) 495 | if (attrib.GetType().Name == Attrib) 496 | return true; 497 | return false; 498 | } 499 | 500 | internal void Seek(long Index, SeekOrigin Origin) { 501 | base.BaseStream.Seek(Index, Origin); 502 | base.BaseStream.Flush(); 503 | } 504 | } 505 | 506 | class StructReader : BinaryReader { 507 | 508 | public bool BigEndian { get; private set; } = false; 509 | internal Encoding Encoding; 510 | public StructReader(Stream Input, bool BigEndian = false, Encoding Encoding = null) : base(Input) { 511 | if (Encoding == null) 512 | Encoding = Encoding.UTF8; 513 | this.BigEndian = BigEndian; 514 | this.Encoding = Encoding; 515 | } 516 | public StructReader(string Input, bool BigEndian = false, Encoding Encoding = null) : base(new StreamReader(Input).BaseStream) { 517 | if (Encoding == null) 518 | Encoding = Encoding.UTF8; 519 | this.BigEndian = BigEndian; 520 | this.Encoding = Encoding; 521 | } 522 | 523 | public void ReadStruct(ref T Struct) { 524 | Type type = Struct.GetType(); 525 | object tmp = Struct; 526 | ReadStruct(type, ref tmp); 527 | Struct = (T)tmp; 528 | } 529 | 530 | internal void ReadStruct(Type type, ref object Instance) { 531 | FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); 532 | for (int i = 0; i < fields.Length; i++) { 533 | FieldInfo field = fields[i]; 534 | if (Tools.HasAttribute(field, Const.IGNORE)) 535 | continue; 536 | 537 | string FType = field.FieldType.ToString(); 538 | dynamic Value = null; 539 | 540 | if (!FType.EndsWith("[]")) { 541 | Value = ReadField(FType, field, ref Instance); 542 | 543 | } else { 544 | if (Tools.HasAttribute(field, Const.FARRAY)) { 545 | byte[] Buffer = new byte[Tools.GetAttributePropertyValue(field, Const.FARRAY, "Length")]; 546 | 547 | if (Read(Buffer, 0, Buffer.Length) != Buffer.Length) 548 | throw new Exception("Failed to Read"); 549 | 550 | Value = Buffer; 551 | 552 | } else if (Tools.HasAttribute(field, Const.PARRAY)) { 553 | FType = FType.Substring(0, FType.Length - 2); 554 | 555 | string PType = Tools.GetAttributePropertyValue(field, Const.PARRAY, "PrefixType"); 556 | dynamic Count = ReadField(PType, field, ref Instance); 557 | 558 | 559 | object[] Content = new object[Count]; 560 | for (long x = 0; x < Count; x++) { 561 | Content[x] = ReadField(FType, field, ref Instance); 562 | } 563 | 564 | 565 | Value = Array.CreateInstance(Type.GetType(FType), Count); 566 | Content.CopyTo(Value, 0); 567 | 568 | } else 569 | throw new Exception("Bad Struct Configuration"); 570 | } 571 | 572 | field.SetValue(Instance, Value); 573 | } 574 | } 575 | 576 | internal dynamic CreateArrayInstance(string TypeName, dynamic InitialLength) {/* 577 | switch (TypeName) { 578 | case Const.INT8: 579 | return new sbyte[InitialLength]; 580 | case Const.UINT8: 581 | return new byte[InitialLength]; 582 | case Const.INT16: 583 | return new short[InitialLength]; 584 | case Const.UINT16: 585 | return new ushort[InitialLength]; 586 | case Const.INT32: 587 | return new int[InitialLength]; 588 | case Const.UINT32: 589 | return new uint[InitialLength]; 590 | case Const.INT64: 591 | return new long[InitialLength]; 592 | case Const.UINT64: 593 | return new ulong[InitialLength]; 594 | case Const.DOUBLE: 595 | return new double[InitialLength]; 596 | case Const.FLOAT: 597 | return new float[InitialLength]; 598 | case Const.STRING: 599 | return new string[InitialLength]; 600 | default: 601 | throw new Exception("Unk Variable Type"); 602 | }*/ 603 | Type ArrType = Type.GetType(TypeName); 604 | return Array.CreateInstance(ArrType, InitialLength); 605 | /* 606 | Type stringArrayType = Type.GetType(TypeName).MakeArrayType(); 607 | return Activator.CreateInstance(stringArrayType, new object[] { InitialLength });*/ 608 | } 609 | 610 | internal dynamic ReadRawType(string Type) { 611 | object obj = new object(); 612 | return ReadField(Type, Type.GetType().GetFields()[0], ref obj); 613 | } 614 | internal dynamic ReadField(string Type, FieldInfo field, ref object Instance) { 615 | bool IsNumber = true; 616 | dynamic Value = null; 617 | switch (Type) { 618 | case Const.INT8: 619 | Value = base.ReadSByte(); 620 | break; 621 | case Const.INT16: 622 | Value = base.ReadInt16(); 623 | break; 624 | case Const.CHAR: 625 | Value = base.ReadChar(); 626 | break; 627 | case Const.UINT16: 628 | Value = base.ReadUInt16(); 629 | break; 630 | case Const.UINT8: 631 | Value = base.ReadByte(); 632 | break; 633 | case Const.INT32: 634 | Value = base.ReadInt32(); 635 | break; 636 | case Const.UINT32: 637 | Value = base.ReadUInt32(); 638 | break; 639 | case Const.DOUBLE: 640 | Value = base.ReadDouble(); 641 | break; 642 | case Const.FLOAT: 643 | Value = base.ReadSingle(); 644 | break; 645 | case Const.INT64: 646 | Value = base.ReadInt64(); 647 | break; 648 | case Const.UINT64: 649 | Value = base.ReadUInt64(); 650 | break; 651 | case Const.STRING: 652 | IsNumber = false; 653 | if (Tools.HasAttribute(field, Const.CSTRING) && Tools.HasAttribute(field, Const.PSTRING)) 654 | throw new Exception("You can't use CString and PString Attribute into the same field."); 655 | if (Tools.HasAttribute(field, Const.CSTRING)) { 656 | Value = ReadString(StringStyle.CString); 657 | break; 658 | } 659 | if (Tools.HasAttribute(field, Const.UCSTRING)) { 660 | Value = ReadString(StringStyle.UCString); 661 | break; 662 | } 663 | if (Tools.HasAttribute(field, Const.PSTRING)) { 664 | Value = ReadString(StringStyle.PString, field); 665 | break; 666 | } 667 | if (Tools.HasAttribute(field, Const.FSTRING)) { 668 | byte[] Bffr = new byte[Tools.GetAttributePropertyValue(field, Const.FSTRING, "Length")]; 669 | if (Read(Bffr, 0, Bffr.Length) != Bffr.Length) 670 | throw new Exception("Failed to Read a String"); 671 | bool TrimEnd = Tools.GetAttributePropertyValue(field, Const.FSTRING, "TrimNull"); 672 | Value = TrimEnd ? Encoding.GetString(Bffr).TrimEnd('\x0') : Encoding.GetString(Bffr); 673 | break; 674 | } 675 | throw new Exception("String Attribute Not Specified."); 676 | default: 677 | IsNumber = false; 678 | if (Tools.HasAttribute(field, Const.STRUCT)) { 679 | Type FieldType = System.Type.GetType(Type); 680 | Value = Activator.CreateInstance(FieldType); 681 | ReadStruct(FieldType, ref Value); 682 | } else { 683 | if (field.FieldType.BaseType.ToString() == Const.DELEGATE) { 684 | FieldInvoke Invoker = (FieldInvoke)field.GetValue(Instance); 685 | Value = Invoker; 686 | if (Invoker == null) 687 | break; 688 | Instance = Invoker.Invoke(BaseStream, true, BigEndian, Instance); 689 | break; 690 | } 691 | throw new Exception("Unk Struct Field: " + field.FieldType.ToString()); 692 | } 693 | break; 694 | } 695 | if (IsNumber && BigEndian) { 696 | Value = Tools.Reverse(Value); 697 | } 698 | return Value; 699 | } 700 | 701 | public string ReadString(StringStyle Style, FieldInfo Info = null) { 702 | List Buffer = new List(); 703 | switch (Style) { 704 | case StringStyle.CString: 705 | while (true) { 706 | byte Byte = base.ReadByte(); 707 | if (Byte < 1) 708 | break; 709 | Buffer.Add(Byte); 710 | } 711 | return Encoding.GetString(Buffer.ToArray()); 712 | case StringStyle.UCString: 713 | while (true) { 714 | byte Byte1 = base.ReadByte(); 715 | byte Byte2 = base.ReadByte(); 716 | if (Byte1 == 0x00 && Byte2 == 0x00) 717 | break; 718 | Buffer.Add(Byte1); 719 | Buffer.Add(Byte2); 720 | } 721 | return Encoding.GetString(Buffer.ToArray()); 722 | case StringStyle.PString: 723 | if (Info != null) { 724 | long Len; 725 | string Prefix = Tools.GetAttributePropertyValue(Info, Const.PSTRING, "PrefixType"); 726 | bool UnicodeLength = Tools.GetAttributePropertyValue(Info, Const.PSTRING, "UnicodeLength"); 727 | switch (Prefix) { 728 | case Const.INT16: 729 | if (BigEndian) 730 | Len = Tools.Reverse(ReadInt16()); 731 | else 732 | Len = ReadInt16(); 733 | break; 734 | case Const.UINT16: 735 | if (BigEndian) 736 | Len = Tools.Reverse(ReadUInt16()); 737 | else 738 | Len = ReadUInt16(); 739 | break; 740 | case Const.UINT8: 741 | if (BigEndian) 742 | Len = Tools.Reverse(ReadByte()); 743 | else 744 | Len = ReadByte(); 745 | break; 746 | case Const.INT8: 747 | if (BigEndian) 748 | Len = Tools.Reverse(ReadSByte()); 749 | else 750 | Len = ReadSByte(); 751 | break; 752 | case Const.INT32: 753 | if (BigEndian) 754 | Len = Tools.Reverse(ReadInt32()); 755 | else 756 | Len = ReadInt32(); 757 | break; 758 | case Const.UINT32: 759 | if (BigEndian) 760 | Len = Tools.Reverse(ReadUInt32()); 761 | else 762 | Len = ReadUInt32(); 763 | break; 764 | case Const.INT64: 765 | if (BigEndian) 766 | Len = Tools.Reverse(ReadInt64()); 767 | else 768 | Len = ReadInt64(); 769 | break; 770 | default: 771 | throw new Exception("Invalid Data Type"); 772 | } 773 | if (UnicodeLength) 774 | Len *= 2; 775 | if (Len > BaseStream.Length - BaseStream.Position) 776 | throw new Exception("Invalid Length"); 777 | byte[] Buff = new byte[Len]; 778 | while (Len > 0) 779 | Len -= BaseStream.Read(Buff, 0, Len > int.MaxValue ? int.MaxValue : (int)Len); 780 | return Encoding.GetString(Buff); 781 | } else 782 | return ReadString(); 783 | default: 784 | throw new Exception("Unk Value Type"); 785 | } 786 | } 787 | 788 | internal void Seek(long Index, SeekOrigin Origin) { 789 | base.BaseStream.Seek(Index, Origin); 790 | base.BaseStream.Flush(); 791 | } 792 | 793 | internal int Peek() { 794 | int b = BaseStream.ReadByte(); 795 | BaseStream.Position--; 796 | return b; 797 | } 798 | 799 | internal int PeekInt() { 800 | byte[] Buff = new byte[4]; 801 | int i = BaseStream.Read(Buff, 0, Buff.Length); 802 | BaseStream.Position -= i; 803 | return BitConverter.ToInt32(Buff, 0); 804 | } 805 | } 806 | } 807 | --------------------------------------------------------------------------------