├── Common ├── Common.h ├── Common.vcxitems ├── Directory.cpp ├── Directory.h ├── FileStream.cpp ├── FileStream.h ├── MemoryUtil.cpp ├── MemoryUtil.h ├── PageUnprotector.cpp ├── PageUnprotector.h ├── Path.cpp ├── Path.h ├── Stream.cpp ├── Stream.h ├── StringUtil.cpp └── StringUtil.h ├── Detours ├── Detours.vcxproj ├── Detours.vcxproj.filters ├── Detours.vcxproj.user ├── creatwth.cpp ├── detours.cpp ├── detours.h ├── detver.h ├── disasm.cpp ├── disolarm.cpp ├── disolarm64.cpp ├── disolia64.cpp ├── disolx64.cpp ├── disolx86.cpp ├── image.cpp ├── modules.cpp └── uimports.cpp ├── KirikiriDescrambler ├── Adler32.cs ├── App.config ├── Descrambler.cs ├── KirikiriDescrambler.csproj ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── Scrambler.cs └── ZlibStream.cs ├── KirikiriTools.sln ├── KirikiriUnencryptedArchive ├── CompilerSpecific │ ├── CallingConvention │ │ ├── BorlandRegToCdeclAdapter.h │ │ ├── CdeclToBorlandRegAdapter.h │ │ ├── CdeclToThiscallAdapter.h │ │ ├── ThiscallToBorlandRegAdapter.h │ │ └── ThiscallToCdeclAdapter.h │ ├── CompilerHelper.cpp │ ├── CompilerHelper.h │ ├── CompilerSpecificVector.h │ ├── Enumerations.h │ └── Rtti │ │ ├── BorlandTypeDescriptor.h │ │ └── MsvcRttiCompleteObjectLocator.h ├── CustomTVPXP3ArchiveStream.cpp ├── CustomTVPXP3ArchiveStream.h ├── CxdecHelper.cpp ├── CxdecHelper.h ├── Debugger.cpp ├── Debugger.h ├── ImportHooker.cpp ├── ImportHooker.h ├── Kirikiri │ ├── Kirikiri.cpp │ ├── Kirikiri.h │ ├── ProxyFunctionExporter.cpp │ ├── ProxyFunctionExporter.h │ ├── iTJSDispatch2.h │ ├── iTJSNativeInstance.h │ ├── iTVPFunctionExporter.h │ ├── iTVPStorageMedia.h │ ├── tTJSBinaryStream.h │ ├── tTJSHashTable.h │ ├── tTJSString.cpp │ ├── tTJSString.h │ ├── tTJSVariant.cpp │ ├── tTJSVariant.h │ ├── tTJSVariantClosure.h │ ├── tTJSVariantOctet.h │ ├── tTJSVariantString.h │ ├── tTVPArchive.h │ └── tTVPXP3Archive.h ├── KirikiriUnencryptedArchive.vcxproj ├── KirikiriUnencryptedArchive.vcxproj.filters ├── KirikiriUnencryptedArchive.vcxproj.user ├── PE │ ├── PE.cpp │ └── PE.h ├── Patcher.cpp ├── Patcher.h ├── Proxy.cpp ├── Proxy.h ├── exports.def ├── main.cpp ├── stdafx.cpp └── stdafx.h ├── LICENSE ├── README.md ├── TJS functions.txt └── Xp3Pack ├── Adler32.cs ├── Program.cs ├── Properties └── AssemblyInfo.cs ├── Xp3ArchiveWriter.cs ├── Xp3IndexBuilder.cs ├── Xp3Pack.csproj ├── ZlibStream.cs ├── app.config └── packages.config /Common/Common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "MemoryUtil.h" 4 | #include "PageUnprotector.h" 5 | #include "StringUtil.h" 6 | #include "Stream.h" 7 | #include "FileStream.h" 8 | #include "Path.h" 9 | #include "Directory.h" 10 | -------------------------------------------------------------------------------- /Common/Common.vcxitems: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | true 6 | {bf6dd8ef-c0ab-449c-b58e-a66bf73e300c} 7 | 8 | 9 | 10 | %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Common/Directory.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "Directory.h" 3 | 4 | using namespace std; 5 | 6 | void Directory::Create(const wstring& folderPath) 7 | { 8 | filesystem::create_directories(folderPath); 9 | } 10 | 11 | vector Directory::GetFiles(const wstring& folderPath, const wstring& pattern) 12 | { 13 | vector files; 14 | 15 | WIN32_FIND_DATA findData; 16 | HANDLE hFind = FindFirstFile(Path::Combine(folderPath, pattern).c_str(), &findData); 17 | if (hFind == INVALID_HANDLE_VALUE) 18 | return files; 19 | 20 | do 21 | { 22 | files.push_back(Path::Combine(folderPath, findData.cFileName)); 23 | } while (FindNextFile(hFind, &findData)); 24 | FindClose(hFind); 25 | return files; 26 | } 27 | 28 | wstring Directory::GetTempDirectory() 29 | { 30 | DWORD pathLength = GetTempPath(0, nullptr); 31 | if (pathLength == 0) 32 | return L""; 33 | 34 | wstring path; 35 | path.resize(pathLength); 36 | GetTempPath(path.size(), path.data()); 37 | 38 | path.resize(path.size() - 1); 39 | return path; 40 | } 41 | -------------------------------------------------------------------------------- /Common/Directory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class Directory 4 | { 5 | public: 6 | static void Create (const std::wstring& folderPath); 7 | static std::vector GetFiles (const std::wstring& folderPath, const std::wstring& pattern); 8 | static std::wstring GetTempDirectory (); 9 | }; 10 | -------------------------------------------------------------------------------- /Common/FileStream.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | using namespace std; 4 | 5 | FileStream::FileStream(const wstring& filePath, const wchar_t* pMode) 6 | { 7 | _pFile = _wfopen(filePath.c_str(), pMode); 8 | if (_pFile == nullptr) 9 | throw exception("Failed to open file"); 10 | } 11 | 12 | FileStream::~FileStream() 13 | { 14 | fclose(_pFile); 15 | _pFile = nullptr; 16 | } 17 | 18 | __int64 FileStream::GetPosition() const 19 | { 20 | return _ftelli64(_pFile); 21 | } 22 | 23 | void FileStream::SetPosition(__int64 position) 24 | { 25 | _fseeki64(_pFile, position, SEEK_SET); 26 | } 27 | 28 | void FileStream::Seek(int offset) 29 | { 30 | _fseeki64(_pFile, offset, SEEK_CUR); 31 | } 32 | 33 | __int64 FileStream::Size() const 34 | { 35 | __int64 position = _ftelli64(_pFile); 36 | _fseeki64(_pFile, 0, SEEK_END); 37 | __int64 size = _ftelli64(_pFile); 38 | _fseeki64(_pFile, position, SEEK_SET); 39 | return size; 40 | } 41 | 42 | void FileStream::SkipBits(int count) 43 | { 44 | throw exception("Not implemented"); 45 | } 46 | 47 | void FileStream::AlignByte() 48 | { 49 | throw exception("Not implemented"); 50 | } 51 | 52 | BYTE FileStream::PeekByte() const 53 | { 54 | assert(GetPosition() < Size()); 55 | 56 | __int64 position = _ftelli64(_pFile); 57 | BYTE b; 58 | fread(&b, 1, 1, _pFile); 59 | _fseeki64(_pFile, position, SEEK_SET); 60 | return b; 61 | } 62 | 63 | void FileStream::ReadBytes(void* pBuffer, int size) 64 | { 65 | assert(GetPosition() + size <= Size()); 66 | fread(pBuffer, 1, size, _pFile); 67 | } 68 | 69 | DWORD FileStream::ReadBits(int count) 70 | { 71 | throw exception("Not implemented"); 72 | } 73 | 74 | void FileStream::Write(const void* pData, int size) 75 | { 76 | fwrite(pData, 1, size, _pFile); 77 | } 78 | -------------------------------------------------------------------------------- /Common/FileStream.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class FileStream : public Stream 4 | { 5 | private: 6 | FILE* _pFile = nullptr; 7 | 8 | public: 9 | FileStream (const std::wstring& filePath, const wchar_t* pMode); 10 | virtual ~FileStream (); 11 | 12 | virtual long long GetPosition () const override; 13 | virtual void SetPosition (long long position) override; 14 | 15 | virtual void Seek (int offset) override; 16 | 17 | virtual long long Size () const override; 18 | 19 | virtual void SkipBits (int count) override; 20 | virtual void AlignByte () override; 21 | 22 | virtual BYTE PeekByte () const override; 23 | virtual void ReadBytes (void* pBuffer, int size) override; 24 | virtual DWORD ReadBits (int count) override; 25 | 26 | virtual void Write (const void* pData, int size) override; 27 | using Stream::Write; 28 | }; 29 | -------------------------------------------------------------------------------- /Common/MemoryUtil.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | using namespace std; 4 | 5 | void* MemoryUtil::FindByte(const void* pStart, int length, BYTE value) 6 | { 7 | BYTE* pByte = (BYTE*)pStart; 8 | BYTE* pEnd = pByte + length; 9 | for (; pByte < pEnd; pByte++) 10 | { 11 | if (*pByte == value) 12 | return pByte; 13 | } 14 | return nullptr; 15 | } 16 | 17 | void** MemoryUtil::FindAlignedPointer(const void* pStart, int length, void* value) 18 | { 19 | void** pPointer = (void **)pStart; 20 | void** pEnd = pPointer + length / sizeof(void*); 21 | for (; pPointer < pEnd; pPointer++) 22 | { 23 | if (*pPointer == value) 24 | return pPointer; 25 | } 26 | return nullptr; 27 | } 28 | 29 | void* MemoryUtil::FindData(const void* pHaystack, int haystackLength, const void* pNeedle, int needleLength) 30 | { 31 | BYTE* pTest = (BYTE*)pHaystack; 32 | BYTE* pEnd = pTest + haystackLength - needleLength; 33 | for (; pTest <= pEnd; pTest++) 34 | { 35 | if (memcmp(pTest, pNeedle, needleLength) == 0) 36 | return pTest; 37 | } 38 | return nullptr; 39 | } 40 | 41 | void* MemoryUtil::FindData(const void* pHaystack, int haystackLength, const void* pNeedle, const void* pNeedleMask, int needleLength) 42 | { 43 | BYTE* pTest = (BYTE*)pHaystack; 44 | BYTE* pEnd = pTest + haystackLength - needleLength; 45 | for (; pTest <= pEnd; pTest++) 46 | { 47 | int offset = 0; 48 | bool failed = false; 49 | for (; offset < (needleLength & ~3); offset += 4) 50 | { 51 | DWORD test = *(DWORD*)(pTest + offset); 52 | DWORD needle = *(DWORD*)((BYTE*)pNeedle + offset); 53 | DWORD mask = *(DWORD*)((BYTE*)pNeedleMask + offset); 54 | if ((test & mask) != needle) 55 | { 56 | failed = true; 57 | break; 58 | } 59 | } 60 | if (failed) 61 | continue; 62 | 63 | for (; offset < needleLength; offset++) 64 | { 65 | BYTE test = pTest[offset]; 66 | BYTE needle = *((BYTE*)pNeedle + offset); 67 | BYTE mask = *((BYTE*)pNeedleMask + offset); 68 | if ((test & mask) != needle) 69 | { 70 | failed = true; 71 | break; 72 | } 73 | } 74 | if (failed) 75 | continue; 76 | 77 | return pTest; 78 | } 79 | return nullptr; 80 | } 81 | 82 | void MemoryUtil::WritePointer(void** ptr, void* value) 83 | { 84 | PageUnprotector unprotector(ptr, sizeof(value)); 85 | *ptr = value; 86 | } 87 | -------------------------------------------------------------------------------- /Common/MemoryUtil.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class MemoryUtil 4 | { 5 | public: 6 | static void* FindByte (const void* pStart, int length, BYTE value); 7 | static void** FindAlignedPointer (const void* pStart, int length, void* value); 8 | static void* FindData (const void* pHaystack, int haystackLength, const void* pNeedle, int needleLength); 9 | static void* FindData (const void* pHaystack, int haystackLength, const void* pNeedle, const void* pNeedleMask, int needleLength); 10 | static void WritePointer (void** ptr, void* value); 11 | }; 12 | -------------------------------------------------------------------------------- /Common/PageUnprotector.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | PageUnprotector::PageUnprotector(void* base, int size) 4 | { 5 | _base = base; 6 | _size = size; 7 | VirtualProtect(_base, _size, PAGE_READWRITE, &_originalProtect); 8 | } 9 | 10 | PageUnprotector::~PageUnprotector() 11 | { 12 | DWORD dummy; 13 | VirtualProtect(_base, _size, _originalProtect, &dummy); 14 | } 15 | -------------------------------------------------------------------------------- /Common/PageUnprotector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class PageUnprotector 4 | { 5 | public: 6 | PageUnprotector(void* base, int size); 7 | ~PageUnprotector(); 8 | 9 | private: 10 | void* _base; 11 | int _size; 12 | DWORD _originalProtect; 13 | }; 14 | -------------------------------------------------------------------------------- /Common/Path.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "Path.h" 3 | 4 | using namespace std; 5 | 6 | wstring Path::Combine(const wstring& path1, const wstring& path2) 7 | { 8 | wstring result = path1; 9 | if (!result.empty() && result[result.size() - 1] != L'\\') 10 | result += L'\\'; 11 | 12 | result += path2; 13 | return result; 14 | } 15 | 16 | wstring Path::GetDirectoryName(const wstring& filePath) 17 | { 18 | int lastSlashPos = filePath.find_last_of(L'\\'); 19 | if (lastSlashPos < 0) 20 | return L""; 21 | 22 | return filePath.substr(0, lastSlashPos); 23 | } 24 | 25 | wstring Path::GetFileName(const wstring& filePath) 26 | { 27 | int lastSlashPos = filePath.find_last_of(L'\\'); 28 | if (lastSlashPos < 0) 29 | return filePath; 30 | 31 | return filePath.substr(lastSlashPos + 1); 32 | } 33 | 34 | wstring Path::GetFileNameWithoutExtension(const wstring& filePath) 35 | { 36 | wstring fileName = GetFileName(filePath); 37 | int dotPos = fileName.find_last_of(L'.'); 38 | if (dotPos >= 0) 39 | fileName.erase(dotPos, fileName.size() - dotPos); 40 | 41 | return fileName; 42 | } 43 | 44 | wstring Path::GetExtension(const wstring& filePath) 45 | { 46 | int extensionPos = GetExtensionIndex(filePath); 47 | if (extensionPos < 0) 48 | return L""; 49 | 50 | return filePath.substr(extensionPos); 51 | } 52 | 53 | wstring Path::ChangeExtension(const wstring& filePath, const wstring& extension) 54 | { 55 | int extensionPos = GetExtensionIndex(filePath); 56 | if (extensionPos == wstring::npos) 57 | return filePath; 58 | 59 | return filePath.substr(0, extensionPos) + extension; 60 | } 61 | 62 | wstring Path::GetModuleFilePath(HMODULE hModule) 63 | { 64 | wchar_t filePath[MAX_PATH]; 65 | GetModuleFileName(hModule, filePath, MAX_PATH); 66 | return filePath; 67 | } 68 | 69 | wstring Path::GetModuleFolderPath(HMODULE hModule) 70 | { 71 | return GetDirectoryName(GetModuleFilePath(hModule)); 72 | } 73 | 74 | wstring Path::GetFullPath(const wstring& path) 75 | { 76 | DWORD length = GetFullPathName(path.c_str(), 0, nullptr, nullptr); 77 | wstring fullPath; 78 | fullPath.resize(length); 79 | GetFullPathName(path.c_str(), fullPath.size(), fullPath.data(), nullptr); 80 | fullPath.resize(fullPath.size() - 1); 81 | return fullPath; 82 | } 83 | 84 | int Path::GetExtensionIndex(const wstring& filePath) 85 | { 86 | int lastSlashPos = filePath.find_last_of(L'\\'); 87 | int dotPos = filePath.find_last_of(L'.'); 88 | if (dotPos < 0 || (lastSlashPos >= 0 && dotPos < lastSlashPos)) 89 | return wstring::npos; 90 | 91 | return dotPos + 1; 92 | } 93 | -------------------------------------------------------------------------------- /Common/Path.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class Path 4 | { 5 | public: 6 | static std::wstring Combine (const std::wstring& path1, const std::wstring& path2); 7 | 8 | static std::wstring GetDirectoryName (const std::wstring& filePath); 9 | static std::wstring GetFileName (const std::wstring& filePath); 10 | static std::wstring GetFileNameWithoutExtension (const std::wstring& filePath); 11 | static std::wstring GetExtension (const std::wstring& filePath); 12 | static std::wstring ChangeExtension (const std::wstring& filePath, const std::wstring& extension); 13 | 14 | static std::wstring GetModuleFilePath (HMODULE hModule); 15 | static std::wstring GetModuleFolderPath (HMODULE hModule); 16 | 17 | static std::wstring GetFullPath (const std::wstring& path); 18 | 19 | private: 20 | static int GetExtensionIndex (const std::wstring& filePath); 21 | }; 22 | -------------------------------------------------------------------------------- /Common/Stream.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | using namespace std; 4 | 5 | Stream::Stream() 6 | { 7 | _readOnly = false; 8 | } 9 | 10 | Stream::~Stream() 11 | { 12 | } 13 | 14 | bool Stream::IsAtEnd() const 15 | { 16 | return GetPosition() >= Size(); 17 | } 18 | 19 | bool Stream::IsReadOnly() const 20 | { 21 | return _readOnly; 22 | } 23 | 24 | vector Stream::ReadBytes(int size) 25 | { 26 | vector result(size); 27 | ReadBytes(result.data(), size); 28 | return result; 29 | } 30 | 31 | char Stream::ReadChar() 32 | { 33 | char c; 34 | ReadStruct(c); 35 | return c; 36 | } 37 | 38 | wchar_t Stream::ReadWChar() 39 | { 40 | wchar_t c; 41 | ReadStruct(c); 42 | return c; 43 | } 44 | 45 | BYTE Stream::ReadByte() 46 | { 47 | BYTE uc; 48 | ReadStruct(uc); 49 | return uc; 50 | } 51 | 52 | short Stream::ReadInt16() 53 | { 54 | short s; 55 | ReadStruct(s); 56 | return s; 57 | } 58 | 59 | unsigned short Stream::ReadUInt16() 60 | { 61 | unsigned short us; 62 | ReadStruct(us); 63 | return us; 64 | } 65 | 66 | int Stream::ReadInt32() 67 | { 68 | int i; 69 | ReadStruct(i); 70 | return i; 71 | } 72 | 73 | unsigned int Stream::ReadUInt32() 74 | { 75 | unsigned int ui; 76 | ReadStruct(ui); 77 | return ui; 78 | } 79 | 80 | int Stream::ReadVarInt32() 81 | { 82 | int result = ReadByte(); 83 | if ((result & 0x80) == 0) 84 | return result; 85 | 86 | result = ((result & 0x7F) << 8) | ReadByte(); 87 | if ((result & 0x4000) == 0) 88 | return result; 89 | 90 | BYTE uc1 = ReadByte(); 91 | BYTE uc0 = ReadByte(); 92 | result = ((result & 0x3FFF) << 16) | (uc1 << 8) | uc0; 93 | return result; 94 | } 95 | 96 | __int64 Stream::ReadInt64() 97 | { 98 | __int64 i; 99 | ReadStruct(i); 100 | return i; 101 | } 102 | 103 | unsigned __int64 Stream::ReadUInt64() 104 | { 105 | unsigned __int64 ui; 106 | ReadStruct(ui); 107 | return ui; 108 | } 109 | 110 | float Stream::ReadSingle() 111 | { 112 | float f; 113 | ReadStruct(f); 114 | return f; 115 | } 116 | 117 | double Stream::ReadDouble() 118 | { 119 | double d; 120 | ReadStruct(d); 121 | return d; 122 | } 123 | 124 | wstring Stream::ReadUtf8StringZ() 125 | { 126 | string str; 127 | char c; 128 | while ((c = ReadChar()) != '\0') 129 | { 130 | str += c; 131 | } 132 | 133 | return StringUtil::ToUTF16(str); 134 | } 135 | 136 | wstring Stream::ReadUtf8String(int length) 137 | { 138 | string str; 139 | str.resize(length); 140 | ReadBytes(const_cast(str.data()), length); 141 | return StringUtil::ToUTF16(str); 142 | } 143 | 144 | wstring Stream::ReadVarUtf8String() 145 | { 146 | return ReadUtf8String(ReadVarInt32()); 147 | } 148 | 149 | wstring Stream::ReadUtf16StringZ() 150 | { 151 | wstring wstr; 152 | wchar_t c; 153 | while ((c = ReadWChar()) != L'\0') 154 | { 155 | wstr += c; 156 | } 157 | return wstr; 158 | } 159 | 160 | wstring Stream::ReadUtf16String(int length) 161 | { 162 | wstring wstr; 163 | wstr.resize(length); 164 | ReadBytes(const_cast(wstr.data()), length * sizeof(wchar_t)); 165 | return wstr; 166 | } 167 | 168 | wstring Stream::ReadVarUtf16String() 169 | { 170 | return ReadUtf16String(ReadVarInt32()); 171 | } 172 | 173 | void Stream::Write(const vector& data) 174 | { 175 | Write(data.data(), data.size()); 176 | } 177 | 178 | void Stream::Write(char value) 179 | { 180 | WriteStruct(value); 181 | } 182 | 183 | void Stream::Write(wchar_t value) 184 | { 185 | WriteStruct(value); 186 | } 187 | 188 | void Stream::Write(BYTE value) 189 | { 190 | WriteStruct(value); 191 | } 192 | 193 | void Stream::Write(short value) 194 | { 195 | WriteStruct(value); 196 | } 197 | 198 | void Stream::Write(unsigned short value) 199 | { 200 | WriteStruct(value); 201 | } 202 | 203 | void Stream::Write(int value) 204 | { 205 | WriteStruct(value); 206 | } 207 | 208 | void Stream::Write(unsigned int value) 209 | { 210 | WriteStruct(value); 211 | } 212 | 213 | void Stream::WriteVar(int value) 214 | { 215 | if (value <= 0x7F) 216 | { 217 | Write((BYTE)value); 218 | } 219 | else if (value <= 0x3FFF) 220 | { 221 | Write((BYTE)(0x80 | (value >> 8))); 222 | Write((BYTE)value); 223 | } 224 | else 225 | { 226 | Write((BYTE)(0xC0 | (value >> 24))); 227 | Write((BYTE)(value >> 16)); 228 | Write((BYTE)(value >> 8)); 229 | Write((BYTE)value); 230 | } 231 | } 232 | 233 | void Stream::Write(__int64 value) 234 | { 235 | WriteStruct(value); 236 | } 237 | 238 | void Stream::Write(unsigned __int64 value) 239 | { 240 | WriteStruct(value); 241 | } 242 | 243 | void Stream::Write(float value) 244 | { 245 | WriteStruct(value); 246 | } 247 | 248 | void Stream::Write(double value) 249 | { 250 | WriteStruct(value); 251 | } 252 | 253 | void Stream::WriteUtf8StringZ(const wstring& wstr) 254 | { 255 | string str = StringUtil::ToUTF8(wstr); 256 | Write(str.data(), str.size()); 257 | Write('\0'); 258 | } 259 | 260 | void Stream::WriteVarUtf8String(const wstring& wstr) 261 | { 262 | string str = StringUtil::ToUTF8(wstr); 263 | WriteVar(str.size()); 264 | Write(str.data(), str.size()); 265 | } 266 | 267 | void Stream::WriteUtf16StringZ(const wstring& wstr) 268 | { 269 | Write(wstr.data(), wstr.size() * sizeof(wchar_t)); 270 | Write(L'\0'); 271 | } 272 | 273 | void Stream::WriteVarUtf16String(const wstring& wstr) 274 | { 275 | WriteVar(wstr.size()); 276 | Write(wstr.data(), wstr.size () * sizeof(wchar_t)); 277 | } 278 | 279 | void Stream::Write(Stream& stream) 280 | { 281 | int bytesLeft = stream.Size() - stream.GetPosition(); 282 | BYTE buffer[0x1000]; 283 | while (bytesLeft > 0) 284 | { 285 | int chunkSize = min(bytesLeft, (int)sizeof(buffer)); 286 | stream.ReadBytes(buffer, chunkSize); 287 | Write(buffer, chunkSize); 288 | bytesLeft -= chunkSize; 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /Common/Stream.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class Stream 4 | { 5 | protected: 6 | bool _readOnly; 7 | 8 | public: 9 | Stream (); 10 | virtual ~Stream (); 11 | 12 | virtual __int64 GetPosition () const = 0; 13 | virtual void SetPosition (__int64 position) = 0; 14 | virtual void Seek (int offset) = 0; 15 | bool IsAtEnd () const; 16 | 17 | virtual __int64 Size() const = 0; 18 | 19 | bool IsReadOnly () const; 20 | 21 | virtual void SkipBits (int count) = 0; 22 | virtual void AlignByte () = 0; 23 | 24 | virtual BYTE PeekByte () const = 0; 25 | 26 | virtual void ReadBytes (void* pBuffer, int size) = 0; 27 | std::vector ReadBytes (int size); 28 | virtual DWORD ReadBits (int count) = 0; 29 | char ReadChar (); 30 | wchar_t ReadWChar (); 31 | BYTE ReadByte (); 32 | short ReadInt16 (); 33 | unsigned short ReadUInt16 (); 34 | int ReadInt32 (); 35 | unsigned int ReadUInt32 (); 36 | int ReadVarInt32 (); 37 | __int64 ReadInt64 (); 38 | unsigned __int64 ReadUInt64 (); 39 | float ReadSingle (); 40 | double ReadDouble (); 41 | std::wstring ReadUtf8StringZ (); 42 | std::wstring ReadUtf8String (int length); 43 | std::wstring ReadVarUtf8String (); 44 | std::wstring ReadUtf16StringZ (); 45 | std::wstring ReadUtf16String (int length); 46 | std::wstring ReadVarUtf16String (); 47 | 48 | template 49 | T ReadStruct () 50 | { 51 | T result; 52 | ReadStruct(result); 53 | return result; 54 | } 55 | 56 | template 57 | void ReadStruct (T& data) 58 | { 59 | ReadBytes(&data, sizeof(data)); 60 | } 61 | 62 | virtual void Write (const void* pData, int size) = 0; 63 | void Write (const std::vector& data); 64 | void Write (char value); 65 | void Write (wchar_t value); 66 | void Write (BYTE value); 67 | void Write (short value); 68 | void Write (unsigned short value); 69 | void Write (int value); 70 | void Write (unsigned int value); 71 | void WriteVar (int value); 72 | void Write (__int64 value); 73 | void Write (unsigned __int64 value); 74 | void Write (float value); 75 | void Write (double value); 76 | void WriteUtf8StringZ (const std::wstring& wstr); 77 | void WriteVarUtf8String (const std::wstring& wstr); 78 | void WriteUtf16StringZ (const std::wstring& wstr); 79 | void WriteVarUtf16String (const std::wstring& wstr); 80 | void Write (Stream& stream); 81 | 82 | template 83 | void WriteStruct (const T data) 84 | { 85 | Write(&data, sizeof(data)); 86 | } 87 | }; 88 | -------------------------------------------------------------------------------- /Common/StringUtil.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | using namespace std; 4 | 5 | wstring StringUtil::ToUTF16(const string& str) 6 | { 7 | std::wstring wstr; 8 | wstr.resize(MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.size(), nullptr, 0)); 9 | MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.size(), const_cast(wstr.c_str()), wstr.size()); 10 | return wstr; 11 | } 12 | 13 | string StringUtil::ToUTF8(const wstring& wstr) 14 | { 15 | std::string str; 16 | str.resize(WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), wstr.size(), nullptr, 0, nullptr, nullptr)); 17 | WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), wstr.size(), const_cast(str.c_str()), str.size(), nullptr, nullptr); 18 | return str; 19 | } 20 | -------------------------------------------------------------------------------- /Common/StringUtil.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class StringUtil 4 | { 5 | private: 6 | struct CFunctions 7 | { 8 | template 9 | static TChar ToLower(TChar c); 10 | 11 | template<> 12 | static char ToLower(char c) 13 | { 14 | return (char)tolower(c); 15 | } 16 | 17 | template<> 18 | static wchar_t ToLower(wchar_t c) 19 | { 20 | return (wchar_t)towlower(c); 21 | } 22 | 23 | template 24 | static TChar ToUpper(TChar c); 25 | 26 | template<> 27 | static char ToUpper(char c) 28 | { 29 | return (char)toupper(c); 30 | } 31 | 32 | template<> 33 | static wchar_t ToUpper(wchar_t c) 34 | { 35 | return (wchar_t)towupper(c); 36 | } 37 | 38 | static int StringPrintf(char* pBuffer, int bufferSize, const char* pFormat, va_list args) 39 | { 40 | return vsnprintf(pBuffer, bufferSize, pFormat, args); 41 | } 42 | 43 | static int StringPrintf(wchar_t* pBuffer, int bufferSize, const wchar_t* pFormat, va_list args) 44 | { 45 | return _vsnwprintf(pBuffer, bufferSize, pFormat, args); 46 | } 47 | }; 48 | 49 | public: 50 | static std::wstring ToUTF16 (const std::string& str); 51 | static std::string ToUTF8 (const std::wstring& wstr); 52 | 53 | template 54 | static std::basic_string Format(const TChar* pFormat, va_list args) 55 | { 56 | va_list argsForLength = args; 57 | int length = CFunctions::StringPrintf(nullptr, 0, pFormat, argsForLength); 58 | 59 | va_list argsForText = args; 60 | std::basic_string result; 61 | result.resize(length); 62 | CFunctions::StringPrintf(result.data(), result.size(), pFormat, argsForText); 63 | 64 | return result; 65 | } 66 | 67 | template 68 | static std::basic_string Format(const TChar* pFormat, ...) 69 | { 70 | va_list args; 71 | va_start(args, pFormat); 72 | return Format(pFormat, args); 73 | } 74 | 75 | template 76 | static std::basic_string ToLower(const std::basic_string& str) 77 | { 78 | std::basic_string result; 79 | result.resize(str.size()); 80 | std::ranges::transform(str, result.begin(), CFunctions::ToLower); 81 | return result; 82 | } 83 | 84 | template 85 | static std::basic_string ToUpper(const std::basic_string& str) 86 | { 87 | std::basic_string result; 88 | result.resize(str.size()); 89 | std::ranges::transform(str, result.begin(), CFunctions::ToUpper); 90 | return result; 91 | } 92 | 93 | template 94 | static std::vector> Split(const std::basic_string& str, const std::basic_string& delimiter) 95 | { 96 | std::vector> result; 97 | int start = 0; 98 | while (start < str.size()) 99 | { 100 | int end = str.find(delimiter.c_str(), start); 101 | if (end < 0) 102 | end = str.size(); 103 | 104 | result.push_back(str.substr(start, end - start)); 105 | start = end + delimiter.size(); 106 | } 107 | return result; 108 | } 109 | 110 | template 111 | static std::basic_string Join(const std::vector>& elements, const std::basic_string& delimiter) 112 | { 113 | std::basic_string result; 114 | for (int i = 0; i < elements.size(); i++) 115 | { 116 | if (i > 0) 117 | result += delimiter; 118 | 119 | result += elements[i]; 120 | } 121 | return result; 122 | } 123 | 124 | template 125 | static std::basic_string Replace(const std::basic_string& str, TChar oldChar, TChar newChar) 126 | { 127 | std::basic_string result(str); 128 | std::ranges::replace(result, oldChar, newChar); 129 | return result; 130 | } 131 | }; 132 | -------------------------------------------------------------------------------- /Detours/Detours.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | 15.0 15 | {9D3F6C0A-9EC1-420C-AFFE-645A574DD9A0} 16 | Detours 17 | 10.0 18 | 19 | 20 | 21 | StaticLibrary 22 | true 23 | v142 24 | Unicode 25 | 26 | 27 | StaticLibrary 28 | false 29 | v142 30 | true 31 | Unicode 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | Level3 49 | Disabled 50 | true 51 | 52 | 53 | 54 | 55 | Level3 56 | MaxSpeed 57 | true 58 | true 59 | true 60 | 61 | 62 | true 63 | true 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /Detours/Detours.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | ソース ファイル 20 | 21 | 22 | ソース ファイル 23 | 24 | 25 | ソース ファイル 26 | 27 | 28 | ソース ファイル 29 | 30 | 31 | ソース ファイル 32 | 33 | 34 | ソース ファイル 35 | 36 | 37 | ソース ファイル 38 | 39 | 40 | ソース ファイル 41 | 42 | 43 | ソース ファイル 44 | 45 | 46 | ソース ファイル 47 | 48 | 49 | 50 | 51 | ヘッダー ファイル 52 | 53 | 54 | ヘッダー ファイル 55 | 56 | 57 | -------------------------------------------------------------------------------- /Detours/Detours.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | true 5 | 6 | -------------------------------------------------------------------------------- /Detours/detver.h: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Common version parameters. 4 | // 5 | // Microsoft Research Detours Package, Version 3.0 Build_343. 6 | // 7 | // Copyright (c) Microsoft Corporation. All rights reserved. 8 | // 9 | 10 | #define _USING_V110_SDK71_ 1 11 | #include "winver.h" 12 | #if 0 13 | #include 14 | #include 15 | #else 16 | #ifndef DETOURS_STRINGIFY 17 | #define DETOURS_STRINGIFY(x) DETOURS_STRINGIFY_(x) 18 | #define DETOURS_STRINGIFY_(x) #x 19 | #endif 20 | 21 | #define VER_FILEFLAGSMASK 0x3fL 22 | #define VER_FILEFLAGS 0x0L 23 | #define VER_FILEOS 0x00040004L 24 | #define VER_FILETYPE 0x00000002L 25 | #define VER_FILESUBTYPE 0x00000000L 26 | #endif 27 | #define VER_DETOURS_BITS DETOUR_STRINGIFY(DETOURS_BITS) 28 | -------------------------------------------------------------------------------- /Detours/disolarm.cpp: -------------------------------------------------------------------------------- 1 | #define DETOURS_ARM_OFFLINE_LIBRARY 2 | #include "disasm.cpp" 3 | -------------------------------------------------------------------------------- /Detours/disolarm64.cpp: -------------------------------------------------------------------------------- 1 | #define DETOURS_ARM64_OFFLINE_LIBRARY 2 | #include "disasm.cpp" 3 | -------------------------------------------------------------------------------- /Detours/disolia64.cpp: -------------------------------------------------------------------------------- 1 | #define DETOURS_IA64_OFFLINE_LIBRARY 2 | #include "disasm.cpp" 3 | -------------------------------------------------------------------------------- /Detours/disolx64.cpp: -------------------------------------------------------------------------------- 1 | #define DETOURS_X64_OFFLINE_LIBRARY 2 | #include "disasm.cpp" 3 | -------------------------------------------------------------------------------- /Detours/disolx86.cpp: -------------------------------------------------------------------------------- 1 | #define DETOURS_X86_OFFLINE_LIBRARY 2 | #include "disasm.cpp" 3 | -------------------------------------------------------------------------------- /Detours/uimports.cpp: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////////// 2 | // 3 | // Add DLLs to a module import table (uimports.cpp of detours.lib) 4 | // 5 | // Microsoft Research Detours Package, Version 3.0 Build_343. 6 | // 7 | // Copyright (c) Microsoft Corporation. All rights reserved. 8 | // 9 | // Note that this file is included into creatwth.cpp one or more times 10 | // (once for each supported module format). 11 | // 12 | 13 | #if DETOURS_VERSION != 30001 14 | #error detours.h version mismatch 15 | #endif 16 | 17 | // UpdateImports32 aka UpdateImports64 18 | static BOOL UPDATE_IMPORTS_XX(HANDLE hProcess, 19 | HMODULE hModule, 20 | __in_ecount(nDlls) LPCSTR *plpDlls, 21 | DWORD nDlls) 22 | { 23 | BOOL fSucceeded = FALSE; 24 | DWORD cbNew = 0; 25 | 26 | BYTE * pbNew = NULL; 27 | DWORD i; 28 | SIZE_T cbRead; 29 | DWORD n; 30 | 31 | PBYTE pbModule = (PBYTE)hModule; 32 | 33 | IMAGE_DOS_HEADER idh; 34 | ZeroMemory(&idh, sizeof(idh)); 35 | if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), &cbRead) 36 | || cbRead < sizeof(idh)) { 37 | 38 | DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %d\n", 39 | pbModule, pbModule + sizeof(idh), GetLastError())); 40 | 41 | finish: 42 | if (pbNew != NULL) { 43 | delete[] pbNew; 44 | pbNew = NULL; 45 | } 46 | return fSucceeded; 47 | } 48 | 49 | IMAGE_NT_HEADERS_XX inh; 50 | ZeroMemory(&inh, sizeof(inh)); 51 | 52 | if (!ReadProcessMemory(hProcess, pbModule + idh.e_lfanew, &inh, sizeof(inh), &cbRead) 53 | || cbRead < sizeof(inh)) { 54 | DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %d\n", 55 | pbModule + idh.e_lfanew, 56 | pbModule + idh.e_lfanew + sizeof(inh), 57 | GetLastError())); 58 | goto finish; 59 | } 60 | 61 | if (inh.OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC_XX) { 62 | DETOUR_TRACE(("Wrong size image (%04x != %04x).\n", 63 | inh.OptionalHeader.Magic, IMAGE_NT_OPTIONAL_HDR_MAGIC_XX)); 64 | SetLastError(ERROR_INVALID_BLOCK); 65 | goto finish; 66 | } 67 | 68 | // Zero out the bound table so loader doesn't use it instead of our new table. 69 | inh.BOUND_DIRECTORY.VirtualAddress = 0; 70 | inh.BOUND_DIRECTORY.Size = 0; 71 | 72 | // Find the size of the mapped file. 73 | DWORD dwSec = idh.e_lfanew + 74 | FIELD_OFFSET(IMAGE_NT_HEADERS_XX, OptionalHeader) + 75 | inh.FileHeader.SizeOfOptionalHeader; 76 | 77 | for (i = 0; i < inh.FileHeader.NumberOfSections; i++) { 78 | IMAGE_SECTION_HEADER ish; 79 | ZeroMemory(&ish, sizeof(ish)); 80 | 81 | if (!ReadProcessMemory(hProcess, pbModule + dwSec + sizeof(ish) * i, &ish, 82 | sizeof(ish), &cbRead) 83 | || cbRead < sizeof(ish)) { 84 | 85 | DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p) failed: %d\n", 86 | pbModule + dwSec + sizeof(ish) * i, 87 | pbModule + dwSec + sizeof(ish) * (i + 1), 88 | GetLastError())); 89 | goto finish; 90 | } 91 | 92 | DETOUR_TRACE(("ish[%d] : va=%08x sr=%d\n", i, ish.VirtualAddress, ish.SizeOfRawData)); 93 | 94 | // If the file didn't have an IAT_DIRECTORY, we assign it... 95 | if (inh.IAT_DIRECTORY.VirtualAddress == 0 && 96 | inh.IMPORT_DIRECTORY.VirtualAddress >= ish.VirtualAddress && 97 | inh.IMPORT_DIRECTORY.VirtualAddress < ish.VirtualAddress + ish.SizeOfRawData) { 98 | 99 | inh.IAT_DIRECTORY.VirtualAddress = ish.VirtualAddress; 100 | inh.IAT_DIRECTORY.Size = ish.SizeOfRawData; 101 | } 102 | } 103 | 104 | DETOUR_TRACE((" Imports: %p..%p\n", 105 | (DWORD_PTR)pbModule + inh.IMPORT_DIRECTORY.VirtualAddress, 106 | (DWORD_PTR)pbModule + inh.IMPORT_DIRECTORY.VirtualAddress + 107 | inh.IMPORT_DIRECTORY.Size)); 108 | 109 | DWORD nOldDlls = inh.IMPORT_DIRECTORY.Size / sizeof(IMAGE_IMPORT_DESCRIPTOR); 110 | DWORD obRem = sizeof(IMAGE_IMPORT_DESCRIPTOR) * nDlls; 111 | DWORD obOld = obRem + sizeof(IMAGE_IMPORT_DESCRIPTOR) * nOldDlls; 112 | DWORD obTab = PadToDwordPtr(obOld); 113 | DWORD obDll = obTab + sizeof(DWORD_XX) * 4 * nDlls; 114 | DWORD obStr = obDll; 115 | cbNew = obStr; 116 | for (n = 0; n < nDlls; n++) { 117 | cbNew += PadToDword((DWORD)strlen(plpDlls[n]) + 1); 118 | } 119 | 120 | _Analysis_assume_(cbNew > 121 | sizeof(IMAGE_IMPORT_DESCRIPTOR) * (nDlls + nOldDlls) 122 | + sizeof(DWORD_XX) * 4 * nDlls); 123 | pbNew = new BYTE [cbNew]; 124 | if (pbNew == NULL) { 125 | DETOUR_TRACE(("new BYTE [cbNew] failed.\n")); 126 | goto finish; 127 | } 128 | ZeroMemory(pbNew, cbNew); 129 | 130 | PBYTE pbBase = pbModule; 131 | PBYTE pbNext = pbBase 132 | + inh.OptionalHeader.BaseOfCode 133 | + inh.OptionalHeader.SizeOfCode 134 | + inh.OptionalHeader.SizeOfInitializedData 135 | + inh.OptionalHeader.SizeOfUninitializedData; 136 | if (pbBase < pbNext) { 137 | pbBase = pbNext; 138 | } 139 | DETOUR_TRACE(("pbBase = %p\n", pbBase)); 140 | 141 | PBYTE pbNewIid = FindAndAllocateNearBase(hProcess, pbBase, cbNew); 142 | if (pbNewIid == NULL) { 143 | DETOUR_TRACE(("FindAndAllocateNearBase failed.\n")); 144 | goto finish; 145 | } 146 | 147 | PIMAGE_IMPORT_DESCRIPTOR piid = (PIMAGE_IMPORT_DESCRIPTOR)pbNew; 148 | DWORD_XX *pt; 149 | 150 | DWORD obBase = (DWORD)(pbNewIid - pbModule); 151 | DWORD dwProtect = 0; 152 | 153 | if (inh.IMPORT_DIRECTORY.VirtualAddress != 0) { 154 | // Read the old import directory if it exists. 155 | DETOUR_TRACE(("IMPORT_DIRECTORY perms=%x\n", dwProtect)); 156 | 157 | if (!ReadProcessMemory(hProcess, 158 | pbModule + inh.IMPORT_DIRECTORY.VirtualAddress, 159 | &piid[nDlls], 160 | nOldDlls * sizeof(IMAGE_IMPORT_DESCRIPTOR), &cbRead) 161 | || cbRead < nOldDlls * sizeof(IMAGE_IMPORT_DESCRIPTOR)) { 162 | 163 | DETOUR_TRACE(("ReadProcessMemory(imports) failed: %d\n", GetLastError())); 164 | goto finish; 165 | } 166 | } 167 | 168 | for (n = 0; n < nDlls; n++) { 169 | HRESULT hrRet = StringCchCopyA((char*)pbNew + obStr, cbNew - obStr, plpDlls[n]); 170 | if (FAILED(hrRet)) { 171 | DETOUR_TRACE(("StringCchCopyA failed: %d\n", GetLastError())); 172 | goto finish; 173 | } 174 | 175 | // After copying the string, we patch up the size "??" bits if any. 176 | hrRet = ReplaceOptionalSizeA((char*)pbNew + obStr, 177 | cbNew - obStr, 178 | DETOURS_STRINGIFY(DETOURS_BITS_XX)); 179 | if (FAILED(hrRet)) { 180 | DETOUR_TRACE(("ReplaceOptionalSizeA failed: %d\n", GetLastError())); 181 | goto finish; 182 | } 183 | 184 | DWORD nOffset = obTab + (sizeof(DWORD_XX) * (4 * n)); 185 | piid[n].OriginalFirstThunk = obBase + nOffset; 186 | pt = ((DWORD_XX*)(pbNew + nOffset)); 187 | pt[0] = IMAGE_ORDINAL_FLAG_XX + 1; 188 | pt[1] = 0; 189 | 190 | nOffset = obTab + (sizeof(DWORD_XX) * ((4 * n) + 2)); 191 | piid[n].FirstThunk = obBase + nOffset; 192 | pt = ((DWORD_XX*)(pbNew + nOffset)); 193 | pt[0] = IMAGE_ORDINAL_FLAG_XX + 1; 194 | pt[1] = 0; 195 | piid[n].TimeDateStamp = 0; 196 | piid[n].ForwarderChain = 0; 197 | piid[n].Name = obBase + obStr; 198 | 199 | obStr += PadToDword((DWORD)strlen(plpDlls[n]) + 1); 200 | } 201 | _Analysis_assume_(obStr <= cbNew); 202 | 203 | #if 0 204 | for (i = 0; i < nDlls + nOldDlls; i++) { 205 | DETOUR_TRACE(("%8d. Look=%08x Time=%08x Fore=%08x Name=%08x Addr=%08x\n", 206 | i, 207 | piid[i].OriginalFirstThunk, 208 | piid[i].TimeDateStamp, 209 | piid[i].ForwarderChain, 210 | piid[i].Name, 211 | piid[i].FirstThunk)); 212 | if (piid[i].OriginalFirstThunk == 0 && piid[i].FirstThunk == 0) { 213 | break; 214 | } 215 | } 216 | #endif 217 | 218 | if (!WriteProcessMemory(hProcess, pbNewIid, pbNew, obStr, NULL)) { 219 | DETOUR_TRACE(("WriteProcessMemory(iid) failed: %d\n", GetLastError())); 220 | goto finish; 221 | } 222 | 223 | DETOUR_TRACE(("obBaseBef = %08x..%08x\n", 224 | inh.IMPORT_DIRECTORY.VirtualAddress, 225 | inh.IMPORT_DIRECTORY.VirtualAddress + inh.IMPORT_DIRECTORY.Size)); 226 | DETOUR_TRACE(("obBaseAft = %08x..%08x\n", obBase, obBase + obStr)); 227 | 228 | // If the file doesn't have an IAT_DIRECTORY, we create it... 229 | if (inh.IAT_DIRECTORY.VirtualAddress == 0) { 230 | inh.IAT_DIRECTORY.VirtualAddress = obBase; 231 | inh.IAT_DIRECTORY.Size = cbNew; 232 | } 233 | 234 | inh.IMPORT_DIRECTORY.VirtualAddress = obBase; 235 | inh.IMPORT_DIRECTORY.Size = cbNew; 236 | 237 | /////////////////////// Update the NT header for the new import directory. 238 | // 239 | if (!DetourVirtualProtectSameExecuteEx(hProcess, pbModule, inh.OptionalHeader.SizeOfHeaders, 240 | PAGE_EXECUTE_READWRITE, &dwProtect)) { 241 | DETOUR_TRACE(("VirtualProtectEx(inh) write failed: %d\n", GetLastError())); 242 | goto finish; 243 | } 244 | 245 | inh.OptionalHeader.CheckSum = 0; 246 | 247 | if (!WriteProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) { 248 | DETOUR_TRACE(("WriteProcessMemory(idh) failed: %d\n", GetLastError())); 249 | goto finish; 250 | } 251 | DETOUR_TRACE(("WriteProcessMemory(idh:%p..%p)\n", pbModule, pbModule + sizeof(idh))); 252 | 253 | if (!WriteProcessMemory(hProcess, pbModule + idh.e_lfanew, &inh, sizeof(inh), NULL)) { 254 | DETOUR_TRACE(("WriteProcessMemory(inh) failed: %d\n", GetLastError())); 255 | goto finish; 256 | } 257 | DETOUR_TRACE(("WriteProcessMemory(inh:%p..%p)\n", 258 | pbModule + idh.e_lfanew, 259 | pbModule + idh.e_lfanew + sizeof(inh))); 260 | 261 | if (!VirtualProtectEx(hProcess, pbModule, inh.OptionalHeader.SizeOfHeaders, 262 | dwProtect, &dwProtect)) { 263 | DETOUR_TRACE(("VirtualProtectEx(idh) restore failed: %d\n", GetLastError())); 264 | goto finish; 265 | } 266 | 267 | fSucceeded = TRUE; 268 | goto finish; 269 | } 270 | -------------------------------------------------------------------------------- /KirikiriDescrambler/Adler32.cs: -------------------------------------------------------------------------------- 1 | namespace Arc.Ddsi.KirikiriDescrambler 2 | { 3 | internal class Adler32 4 | { 5 | private int _a = 1; 6 | private int _b = 0; 7 | 8 | public int Checksum 9 | { 10 | get 11 | { 12 | return _b * 65536 + _a; 13 | } 14 | } 15 | 16 | private const int Modulus = 65521; 17 | 18 | public void Update(byte[] data, int offset, int length) 19 | { 20 | for (int counter = 0; counter < length; ++counter) 21 | { 22 | _a = (_a + (data[offset + counter])) % Modulus; 23 | _b = (_b + _a) % Modulus; 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /KirikiriDescrambler/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /KirikiriDescrambler/Descrambler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.IO.Compression; 4 | using System.Text; 5 | 6 | namespace Arc.Ddsi.KirikiriDescrambler 7 | { 8 | internal static class Descrambler 9 | { 10 | public static string Descramble(string filePath) 11 | { 12 | using (Stream stream = File.Open(filePath, FileMode.Open, FileAccess.Read)) 13 | { 14 | return Descramble(stream); 15 | } 16 | } 17 | 18 | public static string Descramble(Stream stream) 19 | { 20 | BinaryReader reader = new BinaryReader(stream); 21 | byte[] magic = reader.ReadBytes(2); 22 | if (magic[0] != 0xFE || magic[1] != 0xFE) 23 | return null; 24 | 25 | byte mode = reader.ReadByte(); 26 | byte[] bom = reader.ReadBytes(2); 27 | if (bom[0] != 0xFF || bom[1] != 0xFE) 28 | return null; 29 | 30 | byte[] utf16; 31 | switch (mode) 32 | { 33 | case 0: 34 | utf16 = DescrambleMode0(reader); 35 | break; 36 | 37 | case 1: 38 | utf16 = DescrambleMode1(reader); 39 | break; 40 | 41 | case 2: 42 | utf16 = Decompress(reader); 43 | break; 44 | 45 | default: 46 | throw new NotSupportedException($"File uses unsupported scrambling mode {mode}"); 47 | } 48 | return Encoding.Unicode.GetString(utf16); 49 | } 50 | 51 | private static byte[] DescrambleMode0(BinaryReader reader) 52 | { 53 | byte[] data = reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position)); 54 | for (int i = 0; i < data.Length; i += 2) 55 | { 56 | if (data[i + 1] == 0 && data[i] < 0x20) 57 | continue; 58 | 59 | data[i + 1] ^= (byte)(data[i] & 0xFE); 60 | data[i] ^= 1; 61 | } 62 | return data; 63 | } 64 | 65 | private static byte[] DescrambleMode1(BinaryReader reader) 66 | { 67 | byte[] data = reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position)); 68 | for (int i = 0; i < data.Length; i += 2) 69 | { 70 | char c = (char)(data[i] | (data[i + 1] << 8)); 71 | c = (char)(((c & 0xAAAA) >> 1) | ((c & 0x5555) << 1)); 72 | data[i] = (byte)c; 73 | data[i + 1] = (byte)(c >> 8); 74 | } 75 | return data; 76 | } 77 | 78 | private static byte[] Decompress(BinaryReader reader) 79 | { 80 | int compressedLength = (int)reader.ReadInt64(); 81 | int uncompressedLength = (int)reader.ReadInt64(); 82 | short zlibHeader = reader.ReadInt16(); 83 | 84 | byte[] uncompressedData = new byte[uncompressedLength]; 85 | using (DeflateStream stream = new DeflateStream(reader.BaseStream, CompressionMode.Decompress, true)) 86 | { 87 | stream.Read(uncompressedData, 0, uncompressedLength); 88 | } 89 | return uncompressedData; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /KirikiriDescrambler/KirikiriDescrambler.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {FAEAFEB2-29FF-4229-8E84-9C6FA7422F0F} 8 | Exe 9 | Arc.Ddsi.KirikiriDescrambler 10 | KirikiriDescrambler 11 | v4.8 12 | 512 13 | true 14 | true 15 | 16 | 17 | 18 | AnyCPU 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | Arc.Ddsi.KirikiriDescrambler.Program 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /KirikiriDescrambler/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | 7 | namespace Arc.Ddsi.KirikiriDescrambler 8 | { 9 | public static class Program 10 | { 11 | public static void Main(string[] args) 12 | { 13 | if (args.Length != 1) 14 | { 15 | Console.WriteLine($"Usage: {Assembly.GetEntryAssembly().GetName().Name} "); 16 | return; 17 | } 18 | 19 | string path = args[0]; 20 | if (Directory.Exists(path)) 21 | DescrambleFolder(path); 22 | else if (File.Exists(path)) 23 | DescrambleFile(path); 24 | else 25 | Console.WriteLine("Specified file or folder does not exist."); 26 | } 27 | 28 | private static void DescrambleFolder(string folderPath) 29 | { 30 | string[] extensions = { ".ks", ".tjs", ".txt", ".csv", ".ini" }; 31 | foreach (string filePath in Directory.EnumerateFiles(folderPath, "*", SearchOption.AllDirectories)) 32 | { 33 | if (!extensions.Contains(Path.GetExtension(filePath)?.ToLower())) 34 | continue; 35 | 36 | try 37 | { 38 | string content = Descrambler.Descramble(filePath); 39 | if (content == null) 40 | continue; 41 | 42 | File.WriteAllText(filePath, content, Encoding.UTF8); 43 | Console.WriteLine($"Descrambled {filePath}"); 44 | } 45 | catch (Exception ex) 46 | { 47 | Console.WriteLine($"Failed to descramble {filePath}: {ex.Message}"); 48 | } 49 | } 50 | } 51 | 52 | private static void DescrambleFile(string filePath) 53 | { 54 | try 55 | { 56 | string content = Descrambler.Descramble(filePath); 57 | if (content == null) 58 | { 59 | Console.WriteLine("File is not scrambled."); 60 | return; 61 | } 62 | 63 | File.WriteAllText(filePath, content, Encoding.UTF8); 64 | Console.WriteLine("File descrambled."); 65 | } 66 | catch (Exception ex) 67 | { 68 | Console.WriteLine($"Failed to descramble file: {ex.Message}"); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /KirikiriDescrambler/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // アセンブリに関する一般情報は以下の属性セットをとおして制御されます。 6 | // アセンブリに関連付けられている情報を変更するには、 7 | // これらの属性値を変更してください。 8 | [assembly: AssemblyTitle("TextDescramble")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TextDescramble")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // ComVisible を false に設定すると、このアセンブリ内の型は COM コンポーネントから 18 | // 参照できなくなります。COM からこのアセンブリ内の型にアクセスする必要がある場合は、 19 | // その型の ComVisible 属性を true に設定してください。 20 | [assembly: ComVisible(false)] 21 | 22 | // このプロジェクトが COM に公開される場合、次の GUID が typelib の ID になります 23 | [assembly: Guid("faeafeb2-29ff-4229-8e84-9c6fa7422f0f")] 24 | 25 | // アセンブリのバージョン情報は次の 4 つの値で構成されています: 26 | // 27 | // メジャー バージョン 28 | // マイナー バージョン 29 | // ビルド番号 30 | // Revision 31 | // 32 | // すべての値を指定するか、次を使用してビルド番号とリビジョン番号を既定に設定できます 33 | // 既定値にすることができます: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /KirikiriDescrambler/Scrambler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | 5 | namespace Arc.Ddsi.KirikiriDescrambler 6 | { 7 | internal static class Scrambler 8 | { 9 | public static byte[] Scramble(string filePath, byte mode) 10 | { 11 | using (Stream stream = File.Open(filePath, FileMode.Open, FileAccess.Read)) 12 | { 13 | return Scramble(stream, mode); 14 | } 15 | } 16 | 17 | public static byte[] Scramble(Stream stream, byte mode) 18 | { 19 | string text; 20 | using (StreamReader reader = new StreamReader(stream)) 21 | { 22 | text = reader.ReadToEnd(); 23 | } 24 | 25 | byte[] utf16 = Encoding.Unicode.GetBytes(text); 26 | MemoryStream resultStream = new MemoryStream(); 27 | BinaryWriter writer = new BinaryWriter(resultStream); 28 | writer.Write((ushort)0xFEFE); 29 | writer.Write(mode); 30 | writer.Write((ushort)0xFEFF); 31 | switch (mode) 32 | { 33 | case 0: 34 | ScrambleMode0(utf16, writer); 35 | break; 36 | 37 | case 1: 38 | ScrambleMode1(utf16, writer); 39 | break; 40 | 41 | case 2: 42 | Compress(utf16, writer); 43 | break; 44 | 45 | default: 46 | throw new ArgumentException("Invalid mode"); 47 | } 48 | 49 | byte[] resultData = new byte[resultStream.Length]; 50 | resultStream.Position = 0; 51 | resultStream.Read(resultData, 0, resultData.Length); 52 | return resultData; 53 | } 54 | 55 | private static void ScrambleMode0(byte[] data, BinaryWriter writer) 56 | { 57 | for (int i = 0; i < data.Length; i += 2) 58 | { 59 | if (data[i + 1] == 0 && data[i] < 0x20) 60 | continue; 61 | 62 | data[i] ^= 1; 63 | data[i + 1] ^= (byte)(data[i] & 0xFE); 64 | } 65 | writer.Write(data); 66 | } 67 | 68 | private static void ScrambleMode1(byte[] data, BinaryWriter writer) 69 | { 70 | for (int i = 0; i < data.Length; i += 2) 71 | { 72 | char c = (char)(data[i] | (data[i + 1] << 8)); 73 | c = (char)(((c & 0xAAAA) >> 1) | ((c & 0x5555) << 1)); 74 | data[i] = (byte)c; 75 | data[i + 1] = (byte)(c >> 8); 76 | } 77 | writer.Write(data); 78 | } 79 | 80 | private static void Compress(byte[] data, BinaryWriter writer) 81 | { 82 | long startPos = writer.BaseStream.Position; 83 | 84 | writer.Write(0L); 85 | writer.Write((long)data.Length); 86 | using (ZlibStream stream = new ZlibStream(writer.BaseStream)) 87 | { 88 | stream.Write(data, 0, data.Length); 89 | } 90 | 91 | long endPos = writer.BaseStream.Position; 92 | writer.BaseStream.Position = startPos; 93 | writer.Write(endPos - startPos - 16); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /KirikiriDescrambler/ZlibStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.IO.Compression; 4 | 5 | namespace Arc.Ddsi.KirikiriDescrambler 6 | { 7 | internal class ZlibStream : DeflateStream 8 | { 9 | private readonly Adler32 _adler32 = new Adler32(); 10 | 11 | public ZlibStream(Stream innerStream) 12 | : base(innerStream, CompressionMode.Compress, true) 13 | { 14 | byte[] header = { 0x78, 0x9C }; 15 | BaseStream.Write(header, 0, 2); 16 | } 17 | 18 | public override void Write(byte[] array, int offset, int count) 19 | { 20 | base.Write(array, offset, count); 21 | _adler32.Update(array, offset, count); 22 | } 23 | 24 | protected override void Dispose(bool disposing) 25 | { 26 | Stream baseStream = BaseStream; 27 | base.Dispose(disposing); 28 | 29 | byte[] checksum = BitConverter.GetBytes(_adler32.Checksum); 30 | for (int i = 3; i >= 0; i--) 31 | { 32 | baseStream.WriteByte(checksum[i]); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /KirikiriTools.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30804.86 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KirikiriUnencryptedArchive", "KirikiriUnencryptedArchive\KirikiriUnencryptedArchive.vcxproj", "{68FD9BD1-B4E9-4C34-BBB4-45ED49EE6315}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Detours", "Detours\Detours.vcxproj", "{9D3F6C0A-9EC1-420C-AFFE-645A574DD9A0}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xp3Pack", "Xp3Pack\Xp3Pack.csproj", "{45DCBC40-F6EB-46CF-9D07-C8FFC57728F7}" 11 | EndProject 12 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "Common\Common.vcxitems", "{BF6DD8EF-C0AB-449C-B58E-A66BF73E300C}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KirikiriDescrambler", "KirikiriDescrambler\KirikiriDescrambler.csproj", "{FAEAFEB2-29FF-4229-8E84-9C6FA7422F0F}" 15 | EndProject 16 | Global 17 | GlobalSection(SharedMSBuildProjectFiles) = preSolution 18 | Common\Common.vcxitems*{68fd9bd1-b4e9-4c34-bbb4-45ed49ee6315}*SharedItemsImports = 4 19 | Common\Common.vcxitems*{bf6dd8ef-c0ab-449c-b58e-a66bf73e300c}*SharedItemsImports = 9 20 | EndGlobalSection 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Release|Any CPU = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {68FD9BD1-B4E9-4C34-BBB4-45ED49EE6315}.Debug|Any CPU.ActiveCfg = Debug|Win32 27 | {68FD9BD1-B4E9-4C34-BBB4-45ED49EE6315}.Debug|Any CPU.Build.0 = Debug|Win32 28 | {68FD9BD1-B4E9-4C34-BBB4-45ED49EE6315}.Release|Any CPU.ActiveCfg = Release|Win32 29 | {68FD9BD1-B4E9-4C34-BBB4-45ED49EE6315}.Release|Any CPU.Build.0 = Release|Win32 30 | {9D3F6C0A-9EC1-420C-AFFE-645A574DD9A0}.Debug|Any CPU.ActiveCfg = Debug|Win32 31 | {9D3F6C0A-9EC1-420C-AFFE-645A574DD9A0}.Debug|Any CPU.Build.0 = Debug|Win32 32 | {9D3F6C0A-9EC1-420C-AFFE-645A574DD9A0}.Release|Any CPU.ActiveCfg = Release|Win32 33 | {9D3F6C0A-9EC1-420C-AFFE-645A574DD9A0}.Release|Any CPU.Build.0 = Release|Win32 34 | {45DCBC40-F6EB-46CF-9D07-C8FFC57728F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {45DCBC40-F6EB-46CF-9D07-C8FFC57728F7}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {45DCBC40-F6EB-46CF-9D07-C8FFC57728F7}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {45DCBC40-F6EB-46CF-9D07-C8FFC57728F7}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {FAEAFEB2-29FF-4229-8E84-9C6FA7422F0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {FAEAFEB2-29FF-4229-8E84-9C6FA7422F0F}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {FAEAFEB2-29FF-4229-8E84-9C6FA7422F0F}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {FAEAFEB2-29FF-4229-8E84-9C6FA7422F0F}.Release|Any CPU.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | GlobalSection(ExtensibilityGlobals) = postSolution 47 | SolutionGuid = {72E61BE1-4106-4C1F-99B4-E2E4EA1360F1} 48 | EndGlobalSection 49 | EndGlobal 50 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/CompilerSpecific/CallingConvention/BorlandRegToCdeclAdapter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | template 4 | class BorlandRegToCdeclAdapterBase 5 | { 6 | public: 7 | static TResult Call(TArgs...); 8 | 9 | private: 10 | static constexpr void** FuncPtrPtr = TFuncPtrPtr + TFuncPtrIndex; 11 | 12 | static constexpr int NumCdeclStackArgs = sizeof...(TArgs); 13 | static constexpr int CdeclStackArgsSize = NumCdeclStackArgs * 4; 14 | 15 | static constexpr int NumBorlandStackArgs = sizeof...(TArgs) > 3 ? sizeof...(TArgs) - 3 : 0; 16 | static constexpr int BorlandStackArgsSize = NumBorlandStackArgs * 4; 17 | }; 18 | 19 | template 20 | __declspec(naked) TResult BorlandRegToCdeclAdapterBase::Call(TArgs...) 21 | { 22 | __asm 23 | { 24 | push esi 25 | push edi 26 | } 27 | if constexpr (sizeof...(TArgs) > 0) 28 | { 29 | __asm mov eax, [esp+8+4] 30 | } 31 | if constexpr (sizeof...(TArgs) > 1) 32 | { 33 | __asm mov edx, [esp+8+8] 34 | } 35 | if constexpr (sizeof...(TArgs) > 2) 36 | { 37 | __asm mov ecx, [esp+8+0xC] 38 | } 39 | if constexpr (sizeof...(TArgs) > 3) 40 | { 41 | __asm 42 | { 43 | lea esi, [esp+8+0x10] 44 | mov edi, NumBorlandStackArgs 45 | 46 | nextStackArg: 47 | push dword ptr [esi] 48 | add esi, 4 49 | dec edi 50 | jnz nextStackArg 51 | } 52 | } 53 | __asm 54 | { 55 | mov edi, FuncPtrPtr 56 | call dword ptr [edi] 57 | pop edi 58 | pop esi 59 | ret 60 | } 61 | } 62 | 63 | template 64 | class BorlandRegToCdeclAdapter; 65 | 66 | template 67 | class BorlandRegToCdeclAdapter : public BorlandRegToCdeclAdapterBase 68 | { 69 | }; 70 | 71 | template 72 | class BorlandRegToCdeclAdapter : public BorlandRegToCdeclAdapterBase 73 | { 74 | }; 75 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/CompilerSpecific/CallingConvention/CdeclToBorlandRegAdapter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | template 4 | class CdeclToBorlandRegAdapterBase 5 | { 6 | public: 7 | static void Call(); 8 | 9 | private: 10 | static constexpr auto FuncPtrPtr = TFuncPtrPtr + TFuncPtrIndex; 11 | 12 | static constexpr int NumCdeclStackArgs = sizeof...(TArgs); 13 | static constexpr int CdeclStackArgsSize = NumCdeclStackArgs * 4; 14 | 15 | static constexpr int NumBorlandStackArgs = sizeof...(TArgs) > 3 ? sizeof...(TArgs) - 3 : 0; 16 | static constexpr int BorlandStackArgsSize = NumBorlandStackArgs * 4; 17 | }; 18 | 19 | template 20 | __declspec(naked) void CdeclToBorlandRegAdapterBase::Call() 21 | { 22 | __asm 23 | { 24 | push esi 25 | push edi 26 | } 27 | if constexpr (sizeof...(TArgs) > 3) 28 | { 29 | __asm 30 | { 31 | lea esi, [esp+8+4] 32 | mov edi, NumBorlandStackArgs 33 | 34 | nextStackArg: 35 | push dword ptr [esi] 36 | add esi, 4 37 | dec edi 38 | jnz nextStackArg 39 | } 40 | } 41 | if constexpr (sizeof...(TArgs) > 2) 42 | { 43 | __asm push ecx 44 | } 45 | if constexpr (sizeof...(TArgs) > 1) 46 | { 47 | __asm push edx 48 | } 49 | if constexpr (sizeof...(TArgs) > 0) 50 | { 51 | __asm push eax 52 | } 53 | __asm 54 | { 55 | mov edi, FuncPtrPtr 56 | call dword ptr [edi] 57 | } 58 | if constexpr (sizeof...(TArgs) > 0) 59 | { 60 | __asm add esp, CdeclStackArgsSize 61 | } 62 | __asm 63 | { 64 | pop edi 65 | pop esi 66 | } 67 | if constexpr (sizeof...(TArgs) > 3) 68 | { 69 | __asm 70 | { 71 | pop edx 72 | add esp, BorlandStackArgsSize 73 | jmp edx 74 | } 75 | } 76 | else 77 | { 78 | __asm 79 | { 80 | ret 81 | } 82 | } 83 | } 84 | 85 | template 86 | class CdeclToBorlandRegAdapter; 87 | 88 | template 89 | class CdeclToBorlandRegAdapter : public CdeclToBorlandRegAdapterBase 90 | { 91 | }; 92 | 93 | template 94 | class CdeclToBorlandRegAdapter : public CdeclToBorlandRegAdapterBase 95 | { 96 | }; 97 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/CompilerSpecific/CallingConvention/CdeclToThiscallAdapter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | template 4 | class CdeclToThiscallAdapterBase 5 | { 6 | static_assert(sizeof...(TArgs) > 0); 7 | 8 | public: 9 | static void Call(); 10 | 11 | private: 12 | static constexpr void** FuncPtrPtr = TFuncPtrPtr + TFuncPtrIndex; 13 | 14 | static constexpr int NumThiscallStackArgs = sizeof...(TArgs) - 1; 15 | static constexpr int ThiscallStackArgsSize = NumThiscallStackArgs * 4; 16 | 17 | static constexpr int NumCdeclStackArgs = sizeof...(TArgs); 18 | static constexpr int CdeclStackArgsSize = NumCdeclStackArgs * 4; 19 | }; 20 | 21 | template 22 | __declspec(naked) void CdeclToThiscallAdapterBase::Call() 23 | { 24 | if constexpr (sizeof...(TArgs) > 1) 25 | { 26 | __asm 27 | { 28 | mov eax, NumThiscallStackArgs 29 | lea edx, [esp+4+4*eax] 30 | 31 | nextStackArg: 32 | sub edx, 4 33 | push dword ptr [edx] 34 | dec eax 35 | jnz nextStackArg 36 | } 37 | } 38 | __asm 39 | { 40 | push ecx 41 | mov eax, FuncPtrPtr 42 | call dword ptr [eax] 43 | add esp, CdeclStackArgsSize 44 | } 45 | 46 | if constexpr (sizeof...(TArgs) > 1) 47 | { 48 | __asm 49 | { 50 | pop edx 51 | add esp, ThiscallStackArgsSize 52 | jmp edx 53 | } 54 | } 55 | else 56 | { 57 | __asm 58 | { 59 | ret 60 | } 61 | } 62 | } 63 | 64 | template 65 | class CdeclToThiscallAdapter; 66 | 67 | template 68 | class CdeclToThiscallAdapter : public CdeclToThiscallAdapterBase 69 | { 70 | }; 71 | 72 | template 73 | class CdeclToThiscallAdapter : public CdeclToThiscallAdapterBase 74 | { 75 | }; 76 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/CompilerSpecific/CallingConvention/ThiscallToBorlandRegAdapter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #pragma once 4 | 5 | template 6 | class ThiscallToBorlandRegAdapterBase 7 | { 8 | static_assert(sizeof...(TArgs) > 0); 9 | 10 | public: 11 | static void Call(); 12 | 13 | private: 14 | static constexpr auto FuncPtrPtr = TFuncPtrPtr + TFuncPtrIndex; 15 | 16 | static constexpr int NumThiscallStackArgs = sizeof...(TArgs) - 1; 17 | static constexpr int ThiscallStackArgsSize = NumThiscallStackArgs * 4; 18 | 19 | static constexpr int NumBorlandStackArgs = sizeof...(TArgs) > 3 ? sizeof...(TArgs) - 3 : 0; 20 | static constexpr int BorlandStackArgsSize = NumBorlandStackArgs * 4; 21 | }; 22 | 23 | template 24 | __declspec(naked) void ThiscallToBorlandRegAdapterBase::Call() 25 | { 26 | __asm 27 | { 28 | push esi 29 | push edi 30 | } 31 | if constexpr (sizeof...(TArgs) > 3) 32 | { 33 | __asm 34 | { 35 | lea esi, [esp+8+4] 36 | mov edi, NumBorlandStackArgs 37 | 38 | nextStackArg: 39 | push dword ptr [esi] 40 | add esi, 4 41 | dec edi 42 | jnz nextStackArg 43 | } 44 | } 45 | if constexpr (sizeof...(TArgs) > 2) 46 | { 47 | __asm push ecx 48 | } 49 | if constexpr (sizeof...(TArgs) > 1) 50 | { 51 | __asm push edx 52 | } 53 | __asm 54 | { 55 | mov ecx, eax 56 | mov edi, FuncPtrPtr 57 | call dword ptr [edi] 58 | pop edi 59 | pop esi 60 | } 61 | if constexpr (sizeof...(TArgs) > 3) 62 | { 63 | __asm 64 | { 65 | pop edx 66 | add esp, BorlandStackArgsSize 67 | jmp edx 68 | } 69 | } 70 | else 71 | { 72 | __asm 73 | { 74 | ret 75 | } 76 | } 77 | } 78 | 79 | template 80 | class ThiscallToBorlandRegAdapter; 81 | 82 | template 83 | class ThiscallToBorlandRegAdapter : public ThiscallToBorlandRegAdapterBase 84 | { 85 | }; 86 | 87 | template 88 | class ThiscallToBorlandRegAdapter : public ThiscallToBorlandRegAdapterBase 89 | { 90 | }; 91 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/CompilerSpecific/CallingConvention/ThiscallToCdeclAdapter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | template 4 | class ThiscallToCdeclAdapterBase 5 | { 6 | static_assert(sizeof...(TArgs) > 0); 7 | 8 | public: 9 | static TResult Call(TArgs...); 10 | 11 | private: 12 | static constexpr void* FuncPtrPtr = TFuncPtrPtr + TFuncPtrIndex; 13 | 14 | static constexpr int NumThiscallStackArgs = sizeof...(TArgs) - 1; 15 | static constexpr int ThiscallStackArgsSize = NumThiscallStackArgs * 4; 16 | 17 | static constexpr int NumCdeclStackArgs = sizeof...(TArgs); 18 | static constexpr int CdeclStackArgsSize = NumCdeclStackArgs * 4; 19 | }; 20 | 21 | template 22 | __declspec(naked) TResult ThiscallToCdeclAdapterBase::Call(TArgs...) 23 | { 24 | __asm 25 | { 26 | mov ecx, [esp+4] 27 | } 28 | if constexpr (sizeof...(TArgs) > 1) 29 | { 30 | __asm 31 | { 32 | mov eax, NumThiscallStackArgs 33 | lea edx, [esp+8+4*eax] 34 | 35 | nextStackArg: 36 | sub edx, 4 37 | push dword ptr [edx] 38 | dec eax 39 | jnz nextStackArg 40 | } 41 | } 42 | __asm 43 | { 44 | mov eax, FuncPtrPtr 45 | call dword ptr [eax] 46 | ret 47 | } 48 | } 49 | 50 | template 51 | class ThiscallToCdeclAdapter; 52 | 53 | template 54 | class ThiscallToCdeclAdapter : public ThiscallToCdeclAdapterBase 55 | { 56 | }; 57 | 58 | template 59 | class ThiscallToCdeclAdapter : public ThiscallToCdeclAdapterBase 60 | { 61 | }; 62 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/CompilerSpecific/CompilerHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | using namespace std; 4 | 5 | void CompilerHelper::Init() 6 | { 7 | HMODULE hGame = GetModuleHandle(nullptr); 8 | vector gameSections = PE::GetSections(hGame); 9 | 10 | const PE::Section& textSection = gameSections[0]; 11 | if (MemoryUtil::FindData(textSection.Start, textSection.Size, "Borland", 7) != nullptr) 12 | CompilerType = CompilerType::Borland; 13 | else 14 | CompilerType = CompilerType::Msvc; 15 | } 16 | 17 | void** CompilerHelper::FindVTable(const string& className) 18 | { 19 | return FindVTable(GetModuleHandle(nullptr), CompilerType, className); 20 | } 21 | 22 | void** CompilerHelper::FindVTable(HMODULE hModule, ::CompilerType compilerType, const std::string& className) 23 | { 24 | void* pModuleStart = (void*)hModule; 25 | void* pModuleEnd = (BYTE*)pModuleStart + DetourGetModuleSize(hModule); 26 | 27 | vector sections = PE::GetSections(hModule); 28 | const PE::Section& textSection = sections[0]; 29 | void* pCodeStart = textSection.Start; 30 | void* pCodeEnd = (BYTE*)textSection.Start + textSection.Size; 31 | 32 | string typeDescriptorClassName; 33 | switch (compilerType) 34 | { 35 | case CompilerType::Borland: 36 | typeDescriptorClassName = className; 37 | break; 38 | 39 | case CompilerType::Msvc: 40 | vector parts = StringUtil::Split(className, "::"); 41 | std::ranges::reverse(parts); 42 | typeDescriptorClassName = StringUtil::Join(parts, "@"); 43 | break; 44 | } 45 | 46 | for (int i = 1; i < sections.size(); i++) 47 | { 48 | const PE::Section& section = sections[i]; 49 | void* pSectionStart = section.Start; 50 | void* pSectionEnd = (BYTE*)section.Start + section.Size; 51 | for (void** ppFunc = (void**)pSectionStart + 3; ppFunc < pSectionEnd; ppFunc++) 52 | { 53 | if (*ppFunc < pCodeStart || *ppFunc >= pCodeEnd) 54 | continue; 55 | 56 | if (compilerType == CompilerType::Borland && HasBorlandTypeDescriptor(ppFunc, typeDescriptorClassName, pModuleStart, pModuleEnd)) 57 | return ppFunc; 58 | 59 | if (compilerType == CompilerType::Msvc && HasMsvcTypeDescriptor(ppFunc, typeDescriptorClassName, pModuleStart, pModuleEnd)) 60 | return ppFunc; 61 | } 62 | } 63 | return nullptr; 64 | } 65 | 66 | bool CompilerHelper::HasBorlandTypeDescriptor(void** pVTable, const string& className, void* pModuleStart, void* pModuleEnd) 67 | { 68 | BorlandTypeDescriptor* pTypeDescriptor = (BorlandTypeDescriptor*)pVTable[-3]; 69 | if (pTypeDescriptor < pModuleStart || pTypeDescriptor >= pModuleEnd || pTypeDescriptor + 1 + className.size() > pModuleEnd) 70 | return false; 71 | 72 | return memcmp(pTypeDescriptor->Name, className.c_str(), className.size() + 1) == 0; 73 | } 74 | 75 | bool CompilerHelper::HasMsvcTypeDescriptor(void** pVTable, const string& className, void* pModuleStart, void* pModuleEnd) 76 | { 77 | MsvcRttiCompleteObjectLocator* pLocator = (MsvcRttiCompleteObjectLocator*)pVTable[-1]; 78 | if (pLocator < pModuleStart || pLocator >= pModuleEnd || pLocator + 1 > pModuleEnd || 79 | pLocator->Signature != 0 || 80 | pLocator->pTypeDescriptor < pModuleStart || pLocator->pTypeDescriptor >= pModuleEnd || pLocator->pTypeDescriptor + 1 > pModuleEnd) 81 | { 82 | return false; 83 | } 84 | 85 | const char* pRttiClassName = pLocator->pTypeDescriptor->raw_name(); 86 | if (pRttiClassName < pModuleStart || pRttiClassName >= pModuleEnd || pRttiClassName + className.size() + 7 > pModuleEnd) 87 | return false; 88 | 89 | return memcmp(pRttiClassName, ".?A", 3) == 0 && 90 | memcmp(pRttiClassName + 4, className.c_str(), className.size()) == 0 && 91 | memcmp(pRttiClassName + 4 + className.size(), "@@\0", 3) == 0; 92 | } 93 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/CompilerSpecific/CompilerHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class CompilerHelper 4 | { 5 | public: 6 | static void Init (); 7 | 8 | static void** FindVTable (const std::string& className); 9 | static void** FindVTable (HMODULE hModule, CompilerType compilerType, const std::string& className); 10 | 11 | static inline CompilerType CompilerType{}; 12 | 13 | template 14 | static TResult CallStaticMethod (TArgs... args) 15 | { 16 | switch (CompilerType) 17 | { 18 | case CompilerType::Borland: 19 | return BorlandRegToCdeclAdapter::Call(args...); 20 | 21 | case CompilerType::Msvc: 22 | return static_cast(*TFuncPtrPtr)(args...); 23 | 24 | default: 25 | throw std::exception("Unsupported compiler type"); 26 | } 27 | } 28 | 29 | template 30 | static TResult CallInstanceMethod (TArgs... args) 31 | { 32 | switch (CompilerType) 33 | { 34 | case CompilerType::Borland: 35 | return BorlandRegToCdeclAdapter::Call(args...); 36 | 37 | case CompilerType::Msvc: 38 | return ThiscallToCdeclAdapter::Call(args...); 39 | 40 | default: 41 | throw std::exception("Unsupported compiler type"); 42 | } 43 | } 44 | 45 | template 46 | static void* WrapAsStaticMethod () 47 | { 48 | static void* FuncPtr = TFuncPtr; 49 | switch (CompilerType) 50 | { 51 | case CompilerType::Borland: 52 | return &CdeclToBorlandRegAdapter::Call; 53 | 54 | case CompilerType::Msvc: 55 | return TFuncPtr; 56 | 57 | default: 58 | throw std::exception("Unsupported compiler type"); 59 | } 60 | } 61 | 62 | template typename TCallbackClass> 63 | static void* WrapAsStaticMethod () 64 | { 65 | switch (CompilerType) 66 | { 67 | case CompilerType::Borland: 68 | return WrapAsStaticMethod<&TCallbackClass::Call>(); 69 | 70 | case CompilerType::Msvc: 71 | return WrapAsStaticMethod<&TCallbackClass::Call>(); 72 | 73 | default: 74 | throw std::exception("Unsupported compiler type"); 75 | } 76 | } 77 | 78 | template 79 | static void* WrapAsInstanceMethod () 80 | { 81 | static void* FuncPtr = TFuncPtr; 82 | switch (CompilerType) 83 | { 84 | case CompilerType::Borland: 85 | return &CdeclToBorlandRegAdapter::Call; 86 | 87 | case CompilerType::Msvc: 88 | return &CdeclToThiscallAdapter::Call; 89 | 90 | default: 91 | throw std::exception("Unsupported compiler type"); 92 | } 93 | } 94 | 95 | template typename TCallbackClass> 96 | static void* WrapAsInstanceMethod () 97 | { 98 | switch (CompilerType) 99 | { 100 | case CompilerType::Borland: 101 | return WrapAsInstanceMethod<&TCallbackClass::Call>(); 102 | 103 | case CompilerType::Msvc: 104 | return WrapAsInstanceMethod<&TCallbackClass::Call>(); 105 | 106 | default: 107 | throw std::exception("Unsupported compiler type"); 108 | } 109 | } 110 | 111 | template 112 | static void ApplyWrappedVTable (void* pObj) 113 | { 114 | *(void**)pObj = WrapVTable(*(void**)pObj); 115 | } 116 | 117 | template 118 | static void* WrapVTable (void* pVTable) 119 | { 120 | switch (CompilerType) 121 | { 122 | case CompilerType::Borland: 123 | return VTableAdapter::AdaptToBorlandReg(pVTable); 124 | 125 | case CompilerType::Msvc: 126 | return pVTable; 127 | 128 | default: 129 | throw std::exception("Unsupported compiler type"); 130 | } 131 | } 132 | 133 | struct SpecialVirtualFunction 134 | { 135 | }; 136 | 137 | static inline SpecialVirtualFunction NoChange{}; 138 | static inline SpecialVirtualFunction VirtualDestructor{}; 139 | 140 | private: 141 | template 142 | class VTableAdapter 143 | { 144 | public: 145 | static void* AdaptToBorlandReg (void *pOrigVTable) 146 | { 147 | return AdaptThiscallToBorlandReg(pOrigVTable, std::make_index_sequence()); 148 | } 149 | 150 | private: 151 | template 152 | static void* AdaptThiscallToBorlandReg (void* pOrigVTable, std::index_sequence indexes) 153 | { 154 | static void* origVTable[sizeof...(TFuncPtrs)]; 155 | if (origVTable[0] == nullptr) 156 | memcpy(origVTable, pOrigVTable, sizeof(origVTable)); 157 | 158 | static void* newVTable[] = { CallingConventionAdapter::AdaptThiscallToBorlandReg(TFuncPtrs)... }; 159 | return &newVTable; 160 | } 161 | }; 162 | 163 | template 164 | class CallingConventionAdapter 165 | { 166 | public: 167 | template 168 | static constexpr void* AdaptThiscallToBorlandReg (TResult (TClass::*pFunc)(TArgs...)) 169 | { 170 | return &ThiscallToBorlandRegAdapter::Call; 171 | } 172 | 173 | static constexpr void* AdaptThiscallToBorlandReg (SpecialVirtualFunction* pFunc) 174 | { 175 | if (pFunc == &NoChange) 176 | { 177 | void** ppFunc = TFuncPtrPtr + TFuncPtrIndex; 178 | return *ppFunc; 179 | } 180 | 181 | if (pFunc == &VirtualDestructor) 182 | return &ThiscallToBorlandRegAdapter::Call; 183 | 184 | throw std::exception("Unsupported special member function"); 185 | } 186 | }; 187 | 188 | static bool HasBorlandTypeDescriptor (void** pVTable, const std::string& className, void* pModuleStart, void* pModuleEnd); 189 | static bool HasMsvcTypeDescriptor (void** pVTable, const std::string& className, void* pModuleStart, void* pModuleEnd); 190 | }; 191 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/CompilerSpecific/CompilerSpecificVector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | template 4 | struct CompilerSpecificVector; 5 | 6 | template 7 | struct CompilerSpecificVector 8 | { 9 | public: 10 | T* begin() 11 | { 12 | return _start; 13 | } 14 | 15 | T* end() 16 | { 17 | return _finish; 18 | } 19 | 20 | private: 21 | T* _start; 22 | T* _finish; 23 | T* _end_of_storage; 24 | }; 25 | 26 | template 27 | struct CompilerSpecificVector 28 | { 29 | public: 30 | T* begin() 31 | { 32 | return _start; 33 | } 34 | 35 | T* end() 36 | { 37 | return _finish; 38 | } 39 | 40 | private: 41 | int _buffer_size; 42 | T* _start; 43 | T* _finish; 44 | int paddingC[3]; 45 | T* _end_of_storage; 46 | int padding1C; 47 | }; 48 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/CompilerSpecific/Enumerations.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum class CompilerType 4 | { 5 | Unknown, 6 | Borland, 7 | Msvc 8 | }; 9 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/CompilerSpecific/Rtti/BorlandTypeDescriptor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct BorlandTypeDescriptor 4 | { 5 | int tpDtt; 6 | short tpMask; 7 | short tpName; 8 | int bParent; 9 | int tpcFlags; 10 | short Size; 11 | short ExpDim; 12 | int mfnDel; 13 | short mfnMask; 14 | short mfnMaskArr; 15 | int mfnDelArr; 16 | int DtorCount; 17 | int DtorAltCount; 18 | void* DtorAddr; 19 | short DtorMask; 20 | short DtorMemberOff; 21 | char Name[1]; 22 | }; 23 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/CompilerSpecific/Rtti/MsvcRttiCompleteObjectLocator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct MsvcRttiCompleteObjectLocator 4 | { 5 | int Signature; 6 | int Offset; 7 | int ConstructorDisplacementOffset; 8 | type_info* pTypeDescriptor; 9 | void* pHierarchyDescriptor; 10 | }; 11 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/CustomTVPXP3ArchiveStream.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | using namespace std; 4 | 5 | CustomTVPXP3ArchiveStream::CustomTVPXP3ArchiveStream(const ttstr& archiveUrl, tjs_uint64 offset, tjs_uint64 originalSize, tjs_uint64 archiveSize, bool compressed) 6 | { 7 | _data.resize(originalSize); 8 | _position = 0; 9 | 10 | ttstr archivePath = archiveUrl; 11 | archivePath.Replace(L"file://./", L"", true); 12 | archivePath.Replace(L"/", L"\\", true); 13 | 14 | wstring archivePathStr(archivePath.c_str()); 15 | archivePathStr.insert(1, L":"); 16 | 17 | FileStream stream(archivePathStr, L"rb"); 18 | stream.SetPosition(offset); 19 | if (compressed) 20 | { 21 | vector compressedData; 22 | compressedData.resize(archiveSize); 23 | stream.ReadBytes(compressedData.data(), compressedData.size()); 24 | 25 | int uncompressedSize = _data.size(); 26 | Kirikiri::ZLIB_uncompress(_data.data(), &uncompressedSize, compressedData.data(), compressedData.size()); 27 | } 28 | else 29 | { 30 | stream.ReadBytes(_data.data(), _data.size()); 31 | } 32 | } 33 | 34 | CustomTVPXP3ArchiveStream::~CustomTVPXP3ArchiveStream() 35 | { 36 | } 37 | 38 | tjs_uint64 CustomTVPXP3ArchiveStream::Seek(tjs_int64 offset, tjs_int whence) 39 | { 40 | switch (whence) 41 | { 42 | case SEEK_SET: 43 | _position = offset; 44 | break; 45 | 46 | case SEEK_CUR: 47 | _position += offset; 48 | break; 49 | 50 | case SEEK_END: 51 | _position = _data.size() + offset; 52 | break; 53 | } 54 | 55 | return _position; 56 | } 57 | 58 | tjs_uint CustomTVPXP3ArchiveStream::Read(void* buffer, tjs_uint read_size) 59 | { 60 | if (read_size > _data.size() - _position) 61 | read_size = _data.size() - _position; 62 | 63 | memcpy(buffer, _data.data() + _position, read_size); 64 | _position += read_size; 65 | return read_size; 66 | } 67 | 68 | tjs_uint64 CustomTVPXP3ArchiveStream::GetSize() 69 | { 70 | return _data.size(); 71 | } 72 | 73 | tjs_uint CustomTVPXP3ArchiveStream::Write(const void* buffer, tjs_uint write_size) 74 | { 75 | throw exception("Not implemented"); 76 | } 77 | 78 | void CustomTVPXP3ArchiveStream::SetEndOfStorage() 79 | { 80 | throw exception("Not implemented"); 81 | } 82 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/CustomTVPXP3ArchiveStream.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class CustomTVPXP3ArchiveStream : public tTJSBinaryStream 4 | { 5 | public: 6 | CustomTVPXP3ArchiveStream (const ttstr& archiveUrl, tjs_uint64 offset, tjs_uint64 originalSize, tjs_uint64 archiveSize, bool compressed); 7 | ~CustomTVPXP3ArchiveStream () override; 8 | 9 | tjs_uint64 TJS_INTF_METHOD Seek (tjs_int64 offset, tjs_int whence) override; 10 | tjs_uint TJS_INTF_METHOD Read (void* buffer, tjs_uint read_size) override; 11 | tjs_uint TJS_INTF_METHOD Write (const void* buffer, tjs_uint write_size) override; 12 | tjs_uint64 TJS_INTF_METHOD GetSize () override; 13 | void TJS_INTF_METHOD SetEndOfStorage () override; 14 | 15 | std::vector _data; 16 | tjs_uint64 _position; 17 | }; 18 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/CxdecHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | using namespace std; 4 | 5 | #define TVP_XP3_INDEX_ENCODE_METHOD_MASK 0x07 6 | #define TVP_XP3_INDEX_ENCODE_RAW 0 7 | #define TVP_XP3_INDEX_ENCODE_ZLIB 1 8 | 9 | #define TVP_XP3_INDEX_CONTINUE 0x80 10 | 11 | bool CxdecHelper::IsCxdecUrl(const ttstr& url) 12 | { 13 | return url.StartsWith(L"archive://"); 14 | } 15 | 16 | ttstr CxdecHelper::CxdecUrlToXp3FilePath(const ttstr& url) 17 | { 18 | wstring folderPath = Path::GetModuleFolderPath(nullptr); 19 | 20 | const wchar_t* pPathStart = url.c_str() + strlen("archive://"); 21 | const wchar_t* pPathEnd = wcschr(pPathStart, L'/'); 22 | wstring fileName; 23 | if (pPathEnd != nullptr) 24 | fileName.assign(pPathStart, pPathEnd - pPathStart); 25 | else 26 | fileName.assign(pPathStart); 27 | 28 | return Path::Combine(folderPath, fileName).c_str(); 29 | } 30 | 31 | bool CxdecHelper::IsCxdecArchive(const ttstr& path) 32 | { 33 | FileStream stream(path.c_str(), L"rb"); 34 | 35 | static BYTE expectedHeader[] = { 0x58, 0x50, 0x33, 0x0D, 0x0A, 0x20, 0x0A, 0x1A, 0x8B, 0x67, 0x01 }; 36 | BYTE header[11]; 37 | stream.ReadBytes(header, sizeof(header)); 38 | if (memcmp(header, expectedHeader, sizeof(header)) != 0) 39 | return false; 40 | 41 | while (true) 42 | { 43 | __int64 indexOffset = stream.ReadInt64(); 44 | stream.SetPosition(indexOffset); 45 | 46 | BYTE indexFlags = stream.ReadByte(); 47 | int indexSize = (int)stream.ReadInt64(); 48 | 49 | if ((indexFlags & TVP_XP3_INDEX_CONTINUE) != 0) 50 | { 51 | assert((indexFlags & TVP_XP3_INDEX_ENCODE_METHOD_MASK) == TVP_XP3_INDEX_ENCODE_RAW); 52 | assert(indexSize == 0); 53 | continue; 54 | } 55 | 56 | vector indexData; 57 | if ((indexFlags & TVP_XP3_INDEX_ENCODE_METHOD_MASK) == TVP_XP3_INDEX_ENCODE_ZLIB) 58 | { 59 | int compressedIndexSize = indexSize; 60 | indexSize = (int)stream.ReadInt64(); 61 | 62 | indexData.resize(indexSize); 63 | vector compressedIndexData(compressedIndexSize); 64 | stream.ReadBytes(compressedIndexData.data(), compressedIndexSize); 65 | Kirikiri::ZLIB_uncompress(indexData.data(), &indexSize, compressedIndexData.data(), compressedIndexSize); 66 | } 67 | else if ((indexFlags & TVP_XP3_INDEX_ENCODE_METHOD_MASK) == TVP_XP3_INDEX_ENCODE_RAW) 68 | { 69 | indexData.resize(indexSize); 70 | stream.ReadBytes(indexData.data(), indexSize); 71 | } 72 | else 73 | { 74 | throw exception("Unknown XP3 index encoding"); 75 | } 76 | return indexSize >= 4 && *((int *)indexData.data()) != 0x656C6946; 77 | } 78 | } 79 | 80 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/CxdecHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class CxdecHelper 4 | { 5 | public: 6 | static bool IsCxdecUrl (const ttstr& url); 7 | static ttstr CxdecUrlToXp3FilePath (const ttstr& url); 8 | static bool IsCxdecArchive (const ttstr& path); 9 | }; 10 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Debugger.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | using namespace std; 4 | 5 | void Debugger::AddMemoryBreakpoint(void* address, int size) 6 | { 7 | Log(L"Setting memory breakpoint at %08X with size %X", address, size); 8 | 9 | if (((DWORD)address & 0xFFF) != 0) 10 | throw exception("Memory breakpoint base address must be page-aligned"); 11 | 12 | for (const MemoryBreakpoint& breakpoint : MemoryBreakpoints) 13 | { 14 | if (address < (BYTE*)breakpoint.GetAddress() + breakpoint.GetSize() && (BYTE*)address + size > breakpoint.GetAddress()) 15 | throw exception("Memory breakpoint overlaps with an existing one"); 16 | } 17 | 18 | MemoryBreakpoints.emplace_back(address, size); 19 | EnsureExceptionHandler(); 20 | } 21 | 22 | void Debugger::RemoveMemoryBreakpoint(void* address) 23 | { 24 | for (auto it = MemoryBreakpoints.begin(); it != MemoryBreakpoints.end(); ++it) 25 | { 26 | if (it->GetAddress() == address) 27 | { 28 | MemoryBreakpoints.erase(it); 29 | break; 30 | } 31 | } 32 | CleanExceptionHandler(); 33 | } 34 | 35 | void Debugger::ClearMemoryBreakpoints() 36 | { 37 | MemoryBreakpoints.clear(); 38 | CleanExceptionHandler(); 39 | } 40 | 41 | void Debugger::SetMemoryBreakpointHandler(const function& handler) 42 | { 43 | MemoryBreakpointHandler = handler; 44 | } 45 | 46 | void Debugger::AddHardwareBreakpoint(void* address, HWBreakpointSize size, HWBreakpointMode mode, const function& handler) 47 | { 48 | Log(L"Setting hardware breakpoint at %08X", address); 49 | 50 | vector availableIndexes = { 0, 1, 2, 3 }; 51 | for (const HardwareBreakpoint& breakpoint : HardwareBreakpoints) 52 | { 53 | if (breakpoint.GetAddress() == address) 54 | throw exception("A hardware breakpoint is already active at the specified address."); 55 | 56 | availableIndexes.erase(find(availableIndexes.begin(), availableIndexes.end(), breakpoint.GetIndex())); 57 | } 58 | 59 | if (availableIndexes.empty()) 60 | throw exception("The maximum amount of hardware breakpoints has already been placed."); 61 | 62 | HardwareBreakpoints.emplace_back(availableIndexes[0], address, size, mode, handler); 63 | EnsureExceptionHandler(); 64 | } 65 | 66 | void Debugger::RemoveHardwareBreakpoint(void* address) 67 | { 68 | for (auto it = HardwareBreakpoints.begin(); it != HardwareBreakpoints.end(); ++it) 69 | { 70 | if (it->GetAddress() == address) 71 | { 72 | HardwareBreakpoints.erase(it); 73 | break; 74 | } 75 | } 76 | 77 | CleanExceptionHandler(); 78 | } 79 | 80 | void Debugger::ClearHardwareBreakpoints() 81 | { 82 | HardwareBreakpoints.clear(); 83 | CleanExceptionHandler(); 84 | } 85 | 86 | void Debugger::RegisterDllLoadHandler(const function& handler) 87 | { 88 | DllLoadHandlers.push_back(handler); 89 | 90 | if (OriginalLoadLibraryW == nullptr) 91 | { 92 | OriginalVirtualProtect = VirtualProtect; 93 | OriginalLoadLibraryA = LoadLibraryA; 94 | OriginalLoadLibraryW = LoadLibraryW; 95 | OriginalLoadLibraryExA = LoadLibraryExA; 96 | OriginalLoadLibraryExW = LoadLibraryExW; 97 | 98 | DetourTransactionBegin(); 99 | DetourAttach((void**)&OriginalVirtualProtect, VirtualProtectHook); 100 | DetourAttach((void**)&OriginalLoadLibraryA, LoadLibraryAHook); 101 | DetourAttach((void**)&OriginalLoadLibraryW, LoadLibraryWHook); 102 | DetourAttach((void**)&OriginalLoadLibraryExA, LoadLibraryExAHook); 103 | DetourAttach((void**)&OriginalLoadLibraryExW, LoadLibraryExWHook); 104 | DetourTransactionCommit(); 105 | } 106 | } 107 | 108 | void* Debugger::FindExport(HMODULE hModule, const char* pszName) 109 | { 110 | FindExportContext context{}; 111 | context.pszName = pszName; 112 | DetourEnumerateExports(hModule, &context, CheckExport); 113 | return context.pFunction; 114 | } 115 | 116 | BOOL Debugger::CheckExport(PVOID pContext, ULONG nOrdinal, LPCSTR pszName, PVOID pCode) 117 | { 118 | FindExportContext* pFindContext = (FindExportContext*)pContext; 119 | if (pszName != nullptr && strcmp(pszName, pFindContext->pszName) == 0) 120 | { 121 | pFindContext->pFunction = pCode; 122 | return false; 123 | } 124 | return true; 125 | } 126 | 127 | void Debugger::Log(const wchar_t* pMessage, ...) 128 | { 129 | va_list args; 130 | va_start(args, pMessage); 131 | wstring message = StringUtil::Format(pMessage, args); 132 | OutputDebugString(message.c_str()); 133 | } 134 | 135 | Debugger::MemoryBreakpoint::MemoryBreakpoint(void* address, int size) 136 | { 137 | _address = address; 138 | _size = size; 139 | Reapply(); 140 | } 141 | 142 | Debugger::MemoryBreakpoint::~MemoryBreakpoint() 143 | { 144 | BYTE* pFrom = (BYTE*)_address; 145 | BYTE* pTo = pFrom + _size; 146 | 147 | for (BYTE* pPage = pFrom; pPage < pTo; pPage += 0x1000) 148 | { 149 | MEMORY_BASIC_INFORMATION info; 150 | VirtualQuery(pPage, &info, sizeof(info)); 151 | VirtualProtect(pPage, 0x1000, info.Protect & ~PAGE_GUARD, &info.Protect); 152 | } 153 | } 154 | 155 | void Debugger::MemoryBreakpoint::Reapply() const 156 | { 157 | BYTE* pFrom = (BYTE*)_address; 158 | BYTE* pTo = pFrom + _size; 159 | 160 | for (BYTE* pPage = pFrom; pPage < pTo; pPage += 0x1000) 161 | { 162 | MEMORY_BASIC_INFORMATION info; 163 | VirtualQuery(pPage, &info, sizeof(info)); 164 | VirtualProtect(pPage, 0x1000, info.Protect | PAGE_GUARD, &info.Protect); 165 | } 166 | } 167 | 168 | Debugger::HardwareBreakpoint::HardwareBreakpoint(int index, void* address, HWBreakpointSize size, HWBreakpointMode mode, const function& handler) 169 | { 170 | _index = index; 171 | _address = address; 172 | _handler = handler; 173 | 174 | CONTEXT context; 175 | context.ContextFlags = CONTEXT_DEBUG_REGISTERS; 176 | GetThreadContext(GetCurrentThread(), &context); 177 | 178 | (&context.Dr0)[index] = (DWORD)address; 179 | context.Dr7 |= 1 << (2 * index); 180 | context.Dr7 |= (DWORD)mode << (16 + 4 * index); 181 | context.Dr7 |= (DWORD)size << (18 + 4 * index); 182 | 183 | SetThreadContext(GetCurrentThread(), &context); 184 | } 185 | 186 | Debugger::HardwareBreakpoint::~HardwareBreakpoint() 187 | { 188 | CONTEXT context; 189 | context.ContextFlags = CONTEXT_DEBUG_REGISTERS; 190 | GetThreadContext(GetCurrentThread(), &context); 191 | 192 | (&context.Dr0)[_index] = 0; 193 | context.Dr7 &= ~(1 << (2 * _index)); 194 | context.Dr7 &= ~(0xF << (16 + 4 * _index)); 195 | 196 | SetThreadContext(GetCurrentThread(), &context); 197 | } 198 | 199 | void Debugger::ApplyMemoryBreakpoints() 200 | { 201 | for (const MemoryBreakpoint& breakpoint : MemoryBreakpoints) 202 | { 203 | breakpoint.Reapply(); 204 | } 205 | } 206 | 207 | void Debugger::EnsureExceptionHandler() 208 | { 209 | if (ExceptionHandlerHandle == nullptr) 210 | ExceptionHandlerHandle = AddVectoredExceptionHandler(true, HandleException); 211 | } 212 | 213 | void Debugger::CleanExceptionHandler() 214 | { 215 | if (ExceptionHandlerHandle == nullptr || !MemoryBreakpoints.empty() || !HardwareBreakpoints.empty()) 216 | return; 217 | 218 | RemoveVectoredExceptionHandler(ExceptionHandlerHandle); 219 | ExceptionHandlerHandle = nullptr; 220 | } 221 | 222 | long Debugger::HandleException(EXCEPTION_POINTERS* pException) 223 | { 224 | bool handled = false; 225 | switch (pException->ExceptionRecord->ExceptionCode) 226 | { 227 | case STATUS_GUARD_PAGE_VIOLATION: 228 | handled = HandleMemoryBreakpoint(pException->ContextRecord); 229 | break; 230 | 231 | case STATUS_SINGLE_STEP: 232 | handled = HandleHardwareBreakpoint(pException->ContextRecord); 233 | break; 234 | } 235 | 236 | return handled ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_CONTINUE_SEARCH; 237 | } 238 | 239 | bool Debugger::HandleMemoryBreakpoint(CONTEXT* pContext) 240 | { 241 | ScheduleBreakpointHandler(MemoryBreakpointHandler, pContext); 242 | return true; 243 | } 244 | 245 | bool Debugger::HandleHardwareBreakpoint(CONTEXT* pContext) 246 | { 247 | int index = -1; 248 | for (int i = 0; i < 4; i++) 249 | { 250 | if (pContext->Dr6 & (1 << i)) 251 | { 252 | index = i; 253 | break; 254 | } 255 | } 256 | 257 | if (index < 0) 258 | return false; 259 | 260 | const HardwareBreakpoint* pBreakpoint = nullptr; 261 | for (const HardwareBreakpoint& breakpoint : HardwareBreakpoints) 262 | { 263 | if (breakpoint.GetIndex() == index) 264 | { 265 | pBreakpoint = &breakpoint; 266 | break; 267 | } 268 | } 269 | 270 | if (pBreakpoint == nullptr) 271 | return false; 272 | 273 | pContext->Dr6 = 0; 274 | ScheduleBreakpointHandler(pBreakpoint->GetHandler(), pContext); 275 | return true; 276 | } 277 | 278 | void Debugger::ScheduleBreakpointHandler(const function& handler, CONTEXT* pContext) 279 | { 280 | CurrentBreakpointHandler = handler; 281 | CurrentBreakpointContext = *pContext; 282 | 283 | pContext->Esp -= 4; 284 | *(DWORD*)pContext->Esp = pContext->Eip; 285 | pContext->Eip = (DWORD)&ExecuteBreakpointHandlerWrap; 286 | } 287 | 288 | void __declspec(naked) Debugger::ExecuteBreakpointHandlerWrap() 289 | { 290 | __asm 291 | { 292 | pushfd 293 | pushad 294 | cld 295 | call ExecuteBreakpointHandler 296 | popad 297 | popfd 298 | ret 299 | } 300 | } 301 | 302 | void Debugger::ExecuteBreakpointHandler() 303 | { 304 | CurrentBreakpointHandler(&CurrentBreakpointContext); 305 | CurrentBreakpointHandler = nullptr; 306 | 307 | ApplyMemoryBreakpoints(); 308 | } 309 | 310 | BOOL Debugger::VirtualProtectHook(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect) 311 | { 312 | if (!OriginalVirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect)) 313 | return false; 314 | 315 | if (flNewProtect != PAGE_EXECUTE_READ) 316 | return true; 317 | 318 | BYTE* pModule = (BYTE*)lpAddress - 0x1000; 319 | MEMORY_BASIC_INFORMATION memInfo; 320 | if (VirtualQuery(pModule, &memInfo, sizeof(memInfo)) == 0) 321 | return true; 322 | 323 | if (memInfo.AllocationBase != pModule || 324 | memInfo.RegionSize != 0x1000 || 325 | memInfo.State != MEM_COMMIT || 326 | memInfo.Type != MEM_PRIVATE || 327 | pModule[0] != 'M' || 328 | pModule[1] != 'Z') 329 | { 330 | return true; 331 | } 332 | 333 | for (auto handler : DllLoadHandlers) 334 | { 335 | handler(nullptr, (HMODULE)pModule); 336 | } 337 | 338 | return true; 339 | } 340 | 341 | HMODULE Debugger::LoadLibraryAHook(LPCSTR lpLibFileName) 342 | { 343 | HMODULE hModule = OriginalLoadLibraryA(lpLibFileName); 344 | if (hModule == nullptr) 345 | return nullptr; 346 | 347 | wstring filePath = StringUtil::ToUTF16(lpLibFileName); 348 | for (auto handler : DllLoadHandlers) 349 | { 350 | handler(filePath.c_str(), hModule); 351 | } 352 | return hModule; 353 | } 354 | 355 | HMODULE Debugger::LoadLibraryWHook(LPCWSTR lpLibFileName) 356 | { 357 | HMODULE hModule = OriginalLoadLibraryW(lpLibFileName); 358 | if (hModule == nullptr) 359 | return nullptr; 360 | 361 | for (auto handler : DllLoadHandlers) 362 | { 363 | handler(lpLibFileName, hModule); 364 | } 365 | return hModule; 366 | } 367 | 368 | HMODULE Debugger::LoadLibraryExAHook(LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) 369 | { 370 | HMODULE hModule = OriginalLoadLibraryExA(lpLibFileName, hFile, dwFlags); 371 | if (hModule == nullptr) 372 | return nullptr; 373 | 374 | wstring filePath = StringUtil::ToUTF16(lpLibFileName); 375 | for (auto handler : DllLoadHandlers) 376 | { 377 | handler(filePath.c_str(), hModule); 378 | } 379 | return hModule; 380 | } 381 | 382 | HMODULE Debugger::LoadLibraryExWHook(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) 383 | { 384 | HMODULE hModule = OriginalLoadLibraryExW(lpLibFileName, hFile, dwFlags); 385 | if (hModule == nullptr) 386 | return nullptr; 387 | 388 | for (auto handler : DllLoadHandlers) 389 | { 390 | handler(lpLibFileName, hModule); 391 | } 392 | return hModule; 393 | } 394 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Debugger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum class HWBreakpointMode 4 | { 5 | Execute = 0b00, 6 | Write = 0b01, 7 | ReadWrite = 0b11 8 | }; 9 | 10 | enum class HWBreakpointSize 11 | { 12 | Byte = 0b00, 13 | Word = 0b01, 14 | Dword = 0b11, 15 | Qword = 0b10 16 | }; 17 | 18 | class Debugger 19 | { 20 | public: 21 | static void AddMemoryBreakpoint (void* address, int size); 22 | static void RemoveMemoryBreakpoint (void* address); 23 | static void ClearMemoryBreakpoints (); 24 | static void SetMemoryBreakpointHandler (const std::function& handler); 25 | 26 | static void AddHardwareBreakpoint (void* address, HWBreakpointSize size, HWBreakpointMode mode, const std::function& handler); 27 | static void RemoveHardwareBreakpoint (void* address); 28 | static void ClearHardwareBreakpoints (); 29 | 30 | static void RegisterDllLoadHandler (const std::function& handler); 31 | 32 | static void* FindExport (HMODULE hModule, const char* pszName); 33 | 34 | static void Log (const wchar_t* pMessage, ...); 35 | 36 | private: 37 | class MemoryBreakpoint 38 | { 39 | public: 40 | MemoryBreakpoint (void* address, int size); 41 | ~MemoryBreakpoint (); 42 | 43 | void* GetAddress () const { return _address; } 44 | int GetSize () const { return _size; } 45 | 46 | void Reapply () const; 47 | 48 | private: 49 | void* _address; 50 | int _size; 51 | }; 52 | 53 | class HardwareBreakpoint 54 | { 55 | public: 56 | HardwareBreakpoint (int index, void* address, HWBreakpointSize size, HWBreakpointMode mode, const std::function& handler); 57 | ~HardwareBreakpoint (); 58 | 59 | int GetIndex () const { return _index; } 60 | void* GetAddress () const { return _address; } 61 | const std::function& GetHandler () const { return _handler; } 62 | 63 | private: 64 | int _index; 65 | void* _address; 66 | std::function _handler; 67 | }; 68 | 69 | struct FindExportContext 70 | { 71 | const char* pszName; 72 | void* pFunction; 73 | }; 74 | 75 | static BOOL __stdcall CheckExport (PVOID pContext, ULONG nOrdinal, LPCSTR pszName, PVOID pCode); 76 | 77 | static void ApplyMemoryBreakpoints (); 78 | 79 | static void EnsureExceptionHandler (); 80 | static void CleanExceptionHandler (); 81 | 82 | static long __stdcall HandleException (EXCEPTION_POINTERS* pException); 83 | static bool HandleMemoryBreakpoint (CONTEXT* pContext); 84 | static bool HandleHardwareBreakpoint (CONTEXT* pContext); 85 | 86 | static void ScheduleBreakpointHandler (const std::function& handler, CONTEXT* pContext); 87 | static void ExecuteBreakpointHandlerWrap (); 88 | static void ExecuteBreakpointHandler (); 89 | 90 | static BOOL WINAPI VirtualProtectHook (LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); 91 | 92 | static HMODULE WINAPI LoadLibraryAHook (LPCSTR lpLibFileName); 93 | static HMODULE WINAPI LoadLibraryWHook (LPCWSTR lpLibFileName); 94 | static HMODULE WINAPI LoadLibraryExAHook (LPCSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); 95 | static HMODULE WINAPI LoadLibraryExWHook (LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); 96 | 97 | static inline void* ExceptionHandlerHandle{}; 98 | static inline std::vector MemoryBreakpoints{}; 99 | static inline std::function MemoryBreakpointHandler{}; 100 | static inline std::vector HardwareBreakpoints{}; 101 | 102 | static inline std::function CurrentBreakpointHandler{}; 103 | static inline CONTEXT CurrentBreakpointContext{}; 104 | 105 | static inline std::vector> DllLoadHandlers{}; 106 | static inline decltype(VirtualProtect)* OriginalVirtualProtect{}; 107 | static inline decltype(LoadLibraryA)* OriginalLoadLibraryA{}; 108 | static inline decltype(LoadLibraryW)* OriginalLoadLibraryW{}; 109 | static inline decltype(LoadLibraryExA)* OriginalLoadLibraryExA{}; 110 | static inline decltype(LoadLibraryExW)* OriginalLoadLibraryExW{}; 111 | }; 112 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/ImportHooker.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | using namespace std; 4 | 5 | bool ImportHooker::Hook(HMODULE hModule, const char* pName, void* pNewFunc) 6 | { 7 | Context context 8 | { 9 | .Name = pName, 10 | .NewFunc = pNewFunc, 11 | .Patched = false 12 | }; 13 | DetourEnumerateImportsEx(hModule, (void*)&context, nullptr, PatchGameImport); 14 | return context.Patched; 15 | } 16 | 17 | BOOL ImportHooker::PatchGameImport(void* pContext, DWORD nOrdinal, LPCSTR pszFunc, void** ppvFunc) 18 | { 19 | if (pszFunc == nullptr || ppvFunc == nullptr) 20 | return true; 21 | 22 | Context* pPatchContext = (Context*)pContext; 23 | if (strcmp(pszFunc, pPatchContext->Name) == 0) 24 | { 25 | PageUnprotector unprotect(ppvFunc, 4); 26 | *ppvFunc = pPatchContext->NewFunc; 27 | pPatchContext->Patched = true; 28 | } 29 | 30 | return true; 31 | } 32 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/ImportHooker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class ImportHooker 4 | { 5 | public: 6 | static bool Hook(HMODULE hModule, const char* pName, void* pNewFunc); 7 | 8 | private: 9 | struct Context 10 | { 11 | const char* Name; 12 | void* NewFunc; 13 | bool Patched; 14 | }; 15 | 16 | static BOOL __stdcall PatchGameImport(void* pContext, DWORD nOrdinal, LPCSTR pszFunc, void** ppvFunc); 17 | }; 18 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Kirikiri/Kirikiri.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | using namespace std; 4 | 5 | void Kirikiri::Init(const function& callback) 6 | { 7 | if (!IsKirikiriExe()) 8 | { 9 | Debugger::Log(L"Not a Kirikiri .exe - initialization aborted"); 10 | return; 11 | } 12 | 13 | Debugger::RegisterDllLoadHandler(HandleDllLoaded); 14 | InitializationCallback = callback; 15 | } 16 | 17 | wstring Kirikiri::FilePathToUrl(const wstring& path) 18 | { 19 | wstring url = Path::GetFullPath(path); 20 | if (url.size() >= 2 && url[1] == L':') 21 | { 22 | url[0] = towlower(url[0]); 23 | url.erase(1, 1); 24 | } 25 | 26 | url = wstring(L"file://./") + StringUtil::Replace(url, L'\\', L'/'); 27 | return url; 28 | } 29 | 30 | bool Kirikiri::IsKirikiriExe() 31 | { 32 | wstring exePath = Path::GetModuleFilePath(nullptr); 33 | 34 | DWORD handle; 35 | DWORD versionInfoSize = Proxy::OriginalGetFileVersionInfoSizeW(exePath.c_str(), &handle); 36 | if (versionInfoSize == 0) 37 | return false; 38 | 39 | vector versionInfo(versionInfoSize); 40 | if (!Proxy::OriginalGetFileVersionInfoW(exePath.c_str(), 0, versionInfo.size(), versionInfo.data())) 41 | return false; 42 | 43 | void* pLanguages; 44 | UINT languagesSize; 45 | if (!Proxy::OriginalVerQueryValueW(versionInfo.data(), L"\\VarFileInfo\\Translation", &pLanguages, &languagesSize) || languagesSize == 0) 46 | return false; 47 | 48 | UINT language = ((USHORT*)pLanguages)[0]; 49 | UINT codepage = ((USHORT*)pLanguages)[1]; 50 | wstring subBlockName = StringUtil::Format(L"\\StringFileInfo\\%04x%04x\\LegalCopyright", language, codepage); 51 | void* pProductName; 52 | UINT productNameSize; 53 | if (!Proxy::OriginalVerQueryValueW(versionInfo.data(), subBlockName.c_str(), &pProductName, &productNameSize)) 54 | return false; 55 | 56 | wstring copyright((wchar_t*)pProductName, productNameSize); 57 | return copyright.find(L"KIRIKIRI", 0) != string::npos; 58 | } 59 | 60 | void Kirikiri::HandleDllLoaded(const wchar_t* pwszDllPath, HMODULE hDll) 61 | { 62 | void* pLink = Debugger::FindExport(hDll, "V2Link"); 63 | if (pLink == nullptr) 64 | return; 65 | 66 | OriginalV2Link = pLink; 67 | DetourTransactionBegin(); 68 | DetourAttach(&OriginalV2Link, V2LinkHook); 69 | DetourTransactionCommit(); 70 | } 71 | 72 | __declspec(naked) void Kirikiri::V2LinkHook() 73 | { 74 | __asm 75 | { 76 | lea eax, [esp+4] 77 | push eax 78 | call HandleV2Link 79 | add esp, 4 80 | 81 | jmp OriginalV2Link 82 | } 83 | } 84 | 85 | void Kirikiri::HandleV2Link(iTVPFunctionExporter** ppExporter) 86 | { 87 | DetourTransactionBegin(); 88 | DetourDetach(&OriginalV2Link, V2LinkHook); 89 | DetourTransactionCommit(); 90 | 91 | HMODULE hPlugin = DetourGetContainingModule(OriginalV2Link); 92 | 93 | if (ProxyFunctionExporter == nullptr) 94 | { 95 | RealFunctionExporter = *ppExporter; 96 | ProxyFunctionExporter = new ::ProxyFunctionExporter(RealFunctionExporter); 97 | 98 | tTJSString::Init(); 99 | tTJSVariant::Init(); 100 | 101 | ResolveScriptExport(L"void ::TVPExecuteExpression(const ttstr &,tTJSVariant *)", TVPExecuteExpression); 102 | ResolveScriptExport(L"int ::ZLIB_uncompress(unsigned char *,unsigned long *,const unsigned char *,unsigned long)", ZLIB_uncompress); 103 | ResolveScriptExport(L"bool ::TVPIsExistentStorageNoSearch(const ttstr &)", TVPIsExistentStorageNoSearch); 104 | ResolveScriptExport(L"bool ::TVPIsExistentStorageNoSearchNoNormalize(const ttstr &)", TVPIsExistentStorageNoSearchNoNormalize); 105 | ResolveScriptExport(L"IStream * ::TVPCreateIStream(const ttstr &,tjs_uint32)", TVPCreateIStream); 106 | ResolveScriptExport(L"tTJSBinaryStream * ::TVPCreateBinaryStreamAdapter(IStream *)", TVPCreateBinaryStreamAdapter); 107 | } 108 | 109 | if (InitializationCallback != nullptr) 110 | { 111 | if (ImportHooker::Hook(hPlugin, "ImageUnload", CustomImageUnload)) 112 | { 113 | Debugger::Log(L"Hooked ImageUnload() to wait for plugin to finish verifying the game's code"); 114 | } 115 | else 116 | { 117 | Debugger::Log(L"Running initialization"); 118 | InitializationCallback(); 119 | InitializationCallback = nullptr; 120 | } 121 | } 122 | 123 | ImportHooker::Hook(hPlugin, "GetProcAddress", CustomGetProcAddress); 124 | 125 | *ppExporter = ProxyFunctionExporter; 126 | } 127 | 128 | FARPROC Kirikiri::CustomGetProcAddress(HMODULE hModule, LPCSTR lpProcName) 129 | { 130 | if (strcmp(lpProcName, "GetSystemWow64DirectoryA") == 0) 131 | { 132 | // Some games have the following buggy check for a proxy kernel32.dll: 133 | // 134 | // GetModuleFileName(GetModuleHandle("kernel32")) == GetSystemWow64Directory() + "\\kernel32.dll" 135 | // 136 | // This of course doesn't work because GetModuleFileName() returns "C:\Windows\System32\kernel32.dll", 137 | // not "C:\Windows\SysWOW64\kernel32.dll" like the game expects. The result is that even the 138 | // unmodified game thinks it's being hacked and refuses to start. 139 | // 140 | // The fix is to act as though GetSystemWow64DirectoryA() doesn't exist, which should make the game 141 | // fall back to the correct GetSystemDirectory(). 142 | return nullptr; 143 | } 144 | 145 | return GetProcAddress(hModule, lpProcName); 146 | } 147 | 148 | BOOL Kirikiri::CustomImageUnload(PLOADED_IMAGE LoadedImage) 149 | { 150 | if (InitializationCallback != nullptr) 151 | { 152 | wstring imageName = Path::GetFileNameWithoutExtension(StringUtil::ToUTF16(LoadedImage->ModuleName)); 153 | wstring exeName = Path::GetFileNameWithoutExtension(Path::GetModuleFilePath(nullptr)); 154 | if (imageName == exeName) 155 | { 156 | Debugger::Log(L"ImageUnload() called - running initialization"); 157 | InitializationCallback(); 158 | InitializationCallback = nullptr; 159 | } 160 | } 161 | 162 | return ImageUnload(LoadedImage); 163 | } 164 | 165 | void* Kirikiri::GetTrampoline(void* pTarget) 166 | { 167 | // Certain Kirikiri plugins really don't like it if an exported function pointer 168 | // is not inside the engine. The solution is easy: write a jmp instruction at the end of 169 | // the engine's .text section (to make sure we're not clobbering any existing code) 170 | // and use that as the function pointer instead. 171 | 172 | static BYTE* pPrevJump = nullptr; 173 | 174 | if (pPrevJump == nullptr) 175 | { 176 | vector sections = PE::GetSections(GetModuleHandle(nullptr)); 177 | pPrevJump = sections[1].Start; 178 | } 179 | 180 | BYTE* pJump = pPrevJump - 5; 181 | pPrevJump = pJump; 182 | 183 | PageUnprotector unprotect(pJump, 5); 184 | *pJump = 0xE9; 185 | *(int*)(pJump + 1) = (BYTE*)pTarget - (pJump + 5); 186 | 187 | return pJump; 188 | } 189 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Kirikiri/Kirikiri.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef __int8 tjs_int8; 4 | typedef unsigned __int8 tjs_uint8; 5 | typedef __int16 tjs_int16; 6 | typedef unsigned __int16 tjs_uint16; 7 | typedef __int32 tjs_int32; 8 | typedef unsigned __int32 tjs_uint32; 9 | typedef __int64 tjs_int64; 10 | typedef unsigned __int64 tjs_uint64; 11 | typedef int tjs_int; 12 | typedef unsigned int tjs_uint; 13 | 14 | typedef wchar_t tjs_char; 15 | typedef char tjs_nchar; 16 | typedef double tjs_real; 17 | 18 | typedef tjs_int32 tjs_error; 19 | 20 | typedef tjs_int64 tTVInteger; 21 | typedef tjs_real tTVReal; 22 | 23 | #define TJS_INTF_METHOD __cdecl 24 | 25 | #pragma pack(push, 4) 26 | #include "tTJSVariantString.h" 27 | #include "tTJSVariantOctet.h" 28 | #include "iTJSNativeInstance.h" 29 | #include "iTJSDispatch2.h" 30 | #include "tTJSVariantClosure.h" 31 | #include "tTJSVariant.h" 32 | #include "tTJSString.h" 33 | #include "tTJSHashTable.h" 34 | #include "iTVPFunctionExporter.h" 35 | #include "tTJSBinaryStream.h" 36 | #include "tTVPArchive.h" 37 | #include "tTVPXP3Archive.h" 38 | #include "iTVPStorageMedia.h" 39 | 40 | #include "ProxyFunctionExporter.h" 41 | #pragma pack(pop) 42 | 43 | class Kirikiri 44 | { 45 | public: 46 | static void Init (const std::function& callback); 47 | 48 | static inline void (__stdcall *TVPExecuteExpression) (const ttstr& content, tTJSVariant* pResult){}; 49 | static inline int (__stdcall *ZLIB_uncompress) (BYTE* pTarget, int* pTargetLength, const BYTE* pSource, int sourceLength){}; 50 | static inline bool (__stdcall *TVPIsExistentStorageNoSearch) (const ttstr&){}; 51 | static inline bool (__stdcall *TVPIsExistentStorageNoSearchNoNormalize)(const ttstr&){}; 52 | static inline void* (__stdcall *TVPCreateIStream) (const ttstr& name, tjs_uint32 flags){}; 53 | static inline tTJSBinaryStream* (__stdcall *TVPCreateBinaryStreamAdapter) (void* pComStream){}; 54 | 55 | template 56 | static void ResolveScriptExport (const tjs_char* pszName, T*& pFunction) 57 | { 58 | if (!RealFunctionExporter->QueryFunctions(&pszName, (void**)&pFunction, 1)) 59 | { 60 | Debugger::Log(L"Failed to resolve %s", pszName); 61 | throw std::exception("Failed to resolve function"); 62 | } 63 | 64 | Debugger::Log(L"Resolved %s", pszName); 65 | } 66 | 67 | template 68 | static void HookScriptExport (const tjs_char* pszName, T** ppOldFunction, T* pNewFunction) 69 | { 70 | ProxyFunctionExporter->Hook(pszName, (void**)ppOldFunction, GetTrampoline(pNewFunction)); 71 | } 72 | 73 | static std::wstring FilePathToUrl (const std::wstring& path); 74 | 75 | private: 76 | static bool IsKirikiriExe (); 77 | static void HandleDllLoaded (const wchar_t* pwszDllPath, HMODULE hDll); 78 | static void V2LinkHook (); 79 | static void HandleV2Link (iTVPFunctionExporter** ppExporter); 80 | static FARPROC __stdcall CustomGetProcAddress (HMODULE hModule, LPCSTR lpProcName); 81 | static BOOL __stdcall CustomImageUnload (PLOADED_IMAGE LoadedImage); 82 | 83 | static void* GetTrampoline (void* pTarget); 84 | 85 | static inline std::function InitializationCallback{}; 86 | 87 | static inline iTVPFunctionExporter* RealFunctionExporter{}; 88 | static inline ProxyFunctionExporter* ProxyFunctionExporter{}; 89 | static inline void* OriginalV2Link{}; 90 | }; -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Kirikiri/ProxyFunctionExporter.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | using namespace std; 4 | 5 | ProxyFunctionExporter::ProxyFunctionExporter(iTVPFunctionExporter* pInnerExporter) 6 | { 7 | _pInnerExporter = pInnerExporter; 8 | } 9 | 10 | void ProxyFunctionExporter::Hook(const wchar_t* pwszName, void** ppOrigFunc, void* pNewFunc) 11 | { 12 | if (!_pInnerExporter->QueryFunctions(&pwszName, ppOrigFunc, 1)) 13 | { 14 | Debugger::Log(L"Failed to resolve %s", pwszName); 15 | throw std::exception("Failed to resolve function"); 16 | } 17 | 18 | _hooks[pwszName] = pNewFunc; 19 | } 20 | 21 | bool TJS_INTF_METHOD ProxyFunctionExporter::QueryFunctions(const tjs_char** name, void** function, tjs_uint count) 22 | { 23 | bool success = true; 24 | for (int i = 0; i < count; i++) 25 | { 26 | auto it = _hooks.find(name[i]); 27 | if (it != _hooks.end()) 28 | function[i] = it->second; 29 | else 30 | success &= _pInnerExporter->QueryFunctions(&name[i], &function[i], 1); 31 | } 32 | return success; 33 | } 34 | 35 | bool TJS_INTF_METHOD ProxyFunctionExporter::QueryFunctionsByNarrowString(const char** name, void** function, tjs_uint count) 36 | { 37 | bool success = true; 38 | for (int i = 0; i < count; i++) 39 | { 40 | auto it = _hooks.find(StringUtil::ToUTF16(name[i])); 41 | if (it != _hooks.end()) 42 | function[i] = it->second; 43 | else 44 | success &= _pInnerExporter->QueryFunctionsByNarrowString(&name[i], &function[i], 1); 45 | } 46 | return success; 47 | } 48 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Kirikiri/ProxyFunctionExporter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class ProxyFunctionExporter : public iTVPFunctionExporter 4 | { 5 | public: 6 | ProxyFunctionExporter (iTVPFunctionExporter* pInnerExporter); 7 | 8 | void Hook (const wchar_t* pwszName, void** ppOrigFunc, void* pNewFunc); 9 | 10 | virtual bool TJS_INTF_METHOD QueryFunctions (const tjs_char** name, void** function, tjs_uint count) override; 11 | virtual bool TJS_INTF_METHOD QueryFunctionsByNarrowString (const char** name, void** function, tjs_uint count) override; 12 | 13 | private: 14 | iTVPFunctionExporter* _pInnerExporter{}; 15 | std::map _hooks{}; 16 | }; 17 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Kirikiri/iTJSDispatch2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class tTJSVariant; 4 | class tTJSVariantClosure; 5 | 6 | class iTJSDispatch2 7 | { 8 | public: 9 | virtual tjs_uint TJS_INTF_METHOD AddRef() = 0; 10 | virtual tjs_uint TJS_INTF_METHOD Release() = 0; 11 | 12 | virtual tjs_error TJS_INTF_METHOD FuncCall(tjs_uint32 flag, const tjs_char* membername, tjs_uint32* hint, tTJSVariant* result, tjs_int numparams, tTJSVariant** param, iTJSDispatch2* objthis) = 0; 13 | virtual tjs_error TJS_INTF_METHOD FuncCallByNum(tjs_uint32 flag, tjs_int num, tTJSVariant* result, tjs_int numparams, tTJSVariant** param, iTJSDispatch2* objthis) = 0; 14 | 15 | virtual tjs_error TJS_INTF_METHOD PropGet(tjs_uint32 flag, const tjs_char* membername, tjs_uint32* hint, tTJSVariant* result, iTJSDispatch2* objthis) = 0; 16 | virtual tjs_error TJS_INTF_METHOD PropGetByNum(tjs_uint32 flag, tjs_int num, tTJSVariant* result, iTJSDispatch2* objthis) = 0; 17 | 18 | virtual tjs_error TJS_INTF_METHOD PropSet(tjs_uint32 flag, const tjs_char* membername, tjs_uint32* hint, const tTJSVariant* param, iTJSDispatch2* objthis) = 0; 19 | virtual tjs_error TJS_INTF_METHOD PropSetByNum(tjs_uint32 flag, tjs_int num, const tTJSVariant* param, iTJSDispatch2* objthis) = 0; 20 | 21 | virtual tjs_error TJS_INTF_METHOD GetCount(tjs_int* result, const tjs_char* membername, tjs_uint32* hint, iTJSDispatch2* objthis) = 0; 22 | virtual tjs_error TJS_INTF_METHOD GetCountByNum(tjs_int* result, tjs_int num, iTJSDispatch2* objthis) = 0; 23 | 24 | virtual tjs_error TJS_INTF_METHOD PropSetByVS(tjs_uint32 flag, tTJSVariantString* membername, const tTJSVariant* param, iTJSDispatch2* objthis) = 0; 25 | 26 | virtual tjs_error TJS_INTF_METHOD EnumMembers(tjs_uint32 flag, tTJSVariantClosure* callback, iTJSDispatch2* objthis) = 0; 27 | 28 | virtual tjs_error TJS_INTF_METHOD DeleteMember(tjs_uint32 flag, const tjs_char* membername, tjs_uint32* hint, iTJSDispatch2* objthis) = 0; 29 | virtual tjs_error TJS_INTF_METHOD DeleteMemberByNum(tjs_uint32 flag, tjs_int num, iTJSDispatch2* objthis) = 0; 30 | 31 | virtual tjs_error TJS_INTF_METHOD Invalidate(tjs_uint32 flag, const tjs_char* membername, tjs_uint32* hint, iTJSDispatch2* objthis) = 0; 32 | virtual tjs_error TJS_INTF_METHOD InvalidateByNum(tjs_uint32 flag, tjs_int num, iTJSDispatch2* objthis) = 0; 33 | 34 | virtual tjs_error TJS_INTF_METHOD IsValid(tjs_uint32 flag, const tjs_char* membername, tjs_uint32* hint, iTJSDispatch2* objthis) = 0; 35 | virtual tjs_error TJS_INTF_METHOD IsValidByNum(tjs_uint32 flag, tjs_int num, iTJSDispatch2* objthis) = 0; 36 | 37 | virtual tjs_error TJS_INTF_METHOD CreateNew(tjs_uint32 flag, const tjs_char* membername, tjs_uint32* hint, iTJSDispatch2** result, tjs_int numparams, tTJSVariant** param, iTJSDispatch2* objthis) = 0; 38 | 39 | virtual tjs_error TJS_INTF_METHOD CreateNewByNum(tjs_uint32 flag, tjs_int num, iTJSDispatch2** result, tjs_int numparams, tTJSVariant** param, iTJSDispatch2* objthis) = 0; 40 | 41 | virtual tjs_error TJS_INTF_METHOD Reserved1() = 0; 42 | 43 | virtual tjs_error TJS_INTF_METHOD IsInstanceOf(tjs_uint32 flag, const tjs_char* membername, tjs_uint32* hint, const tjs_char* classname, iTJSDispatch2* objthis) = 0; 44 | virtual tjs_error TJS_INTF_METHOD IsInstanceOfByNum(tjs_uint32 flag, tjs_int num, const tjs_char* classname, iTJSDispatch2* objthis) = 0; 45 | 46 | virtual tjs_error TJS_INTF_METHOD Operation(tjs_uint32 flag, const tjs_char* membername, tjs_uint32* hint, tTJSVariant* result, const tTJSVariant* param, iTJSDispatch2* objthis) = 0; 47 | virtual tjs_error TJS_INTF_METHOD OperationByNum(tjs_uint32 flag, tjs_int num, tTJSVariant* result, const tTJSVariant* param, iTJSDispatch2* objthis) = 0; 48 | 49 | virtual tjs_error TJS_INTF_METHOD NativeInstanceSupport(tjs_uint32 flag, tjs_int32 classid, iTJSNativeInstance **pointer) = 0; 50 | 51 | virtual tjs_error TJS_INTF_METHOD ClassInstanceInfo(tjs_uint32 flag, tjs_uint num, tTJSVariant* value) = 0; 52 | 53 | virtual tjs_error TJS_INTF_METHOD Reserved2() = 0; 54 | virtual tjs_error TJS_INTF_METHOD Reserved3() = 0; 55 | }; 56 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Kirikiri/iTJSNativeInstance.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class tTJSVariant; 4 | class iTJSDispatch2; 5 | 6 | class iTJSNativeInstance 7 | { 8 | public: 9 | virtual tjs_error TJS_INTF_METHOD Construct(tjs_int numparams, tTJSVariant** param, iTJSDispatch2* tjs_obj) = 0; 10 | virtual void TJS_INTF_METHOD Invalidate() = 0; 11 | virtual void TJS_INTF_METHOD Destruct() = 0; 12 | }; 13 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Kirikiri/iTVPFunctionExporter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct iTVPFunctionExporter 4 | { 5 | virtual bool TJS_INTF_METHOD QueryFunctions(const tjs_char** name, void** function, tjs_uint count) = 0; 6 | virtual bool TJS_INTF_METHOD QueryFunctionsByNarrowString(const char** name, void** function, tjs_uint count) = 0; 7 | }; 8 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Kirikiri/iTVPStorageMedia.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class iTVPStorageLister 4 | { 5 | public: 6 | virtual void TJS_INTF_METHOD Add(const ttstr& file) = 0; 7 | }; 8 | 9 | class iTVPStorageMedia 10 | { 11 | public: 12 | virtual void TJS_INTF_METHOD AddRef() = 0; 13 | virtual void TJS_INTF_METHOD Release() = 0; 14 | 15 | virtual void TJS_INTF_METHOD GetName(ttstr& name) = 0; 16 | 17 | virtual void TJS_INTF_METHOD NormalizeDomainName(ttstr& name) = 0; 18 | virtual void TJS_INTF_METHOD NormalizePathName(ttstr& name) = 0; 19 | 20 | virtual bool TJS_INTF_METHOD CheckExistentStorage(const ttstr& name) = 0; 21 | 22 | virtual tTJSBinaryStream* TJS_INTF_METHOD Open(const ttstr& name, tjs_uint32 flags) = 0; 23 | 24 | virtual void TJS_INTF_METHOD GetListAt(const ttstr& name, iTVPStorageLister* lister) = 0; 25 | virtual void TJS_INTF_METHOD GetLocallyAccessibleName(ttstr& name) = 0; 26 | }; 27 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Kirikiri/tTJSBinaryStream.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class tTJSBinaryStream 4 | { 5 | public: 6 | virtual tjs_uint64 TJS_INTF_METHOD Seek(tjs_int64 offset, tjs_int whence) = 0; 7 | virtual tjs_uint TJS_INTF_METHOD Read(void* buffer, tjs_uint read_size) = 0; 8 | virtual tjs_uint TJS_INTF_METHOD Write(const void* buffer, tjs_uint write_size) = 0; 9 | virtual void TJS_INTF_METHOD SetEndOfStorage() = 0; 10 | virtual tjs_uint64 TJS_INTF_METHOD GetSize() = 0; 11 | virtual ~tTJSBinaryStream() { } 12 | 13 | template 14 | static void ApplyWrappedVTable(T* pObj) 15 | { 16 | CompilerHelper::ApplyWrappedVTable< 17 | &CompilerHelper::NoChange, 18 | &CompilerHelper::NoChange, 19 | &CompilerHelper::NoChange, 20 | &CompilerHelper::NoChange, 21 | &CompilerHelper::NoChange, 22 | &CompilerHelper::VirtualDestructor 23 | >(pObj); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Kirikiri/tTJSHashTable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define TJS_HS_DEFAULT_HASH_SIZE 64 4 | #define TJS_HS_HASH_USING 0x1 5 | #define TJS_HS_HASH_LV1 0x2 6 | 7 | template 8 | class tTJSHashFunc 9 | { 10 | public: 11 | static tjs_uint32 Make(const T &val) 12 | { 13 | const char *p = (const char*)&val; 14 | const char *plim = (const char*)&val + sizeof(T); 15 | register tjs_uint32 ret = 0; 16 | while (p> 6); 21 | p++; 22 | } 23 | ret += (ret << 3); 24 | ret ^= (ret >> 11); 25 | ret += (ret << 15); 26 | if (!ret) ret = (tjs_uint32)-1; 27 | return ret; 28 | } 29 | }; 30 | 31 | template <> 32 | class tTJSHashFunc // a specialized template of tTJSHashFunc for ttstr 33 | { 34 | public: 35 | static tjs_uint32 Make(const ttstr &val) 36 | { 37 | if (val.IsEmpty()) return 0; 38 | const tjs_char *str = val.c_str(); 39 | tjs_uint32 ret = 0; 40 | while (*str) 41 | { 42 | ret += *str; 43 | ret += (ret << 10); 44 | ret ^= (ret >> 6); 45 | str++; 46 | } 47 | ret += (ret << 3); 48 | ret ^= (ret >> 11); 49 | ret += (ret << 15); 50 | if (!ret) ret = (tjs_uint32)-1; 51 | return ret; 52 | } 53 | }; 54 | 55 | template , tjs_int HashSize = TJS_HS_DEFAULT_HASH_SIZE> 56 | class tTJSHashTable 57 | { 58 | private: 59 | struct element 60 | { 61 | tjs_uint32 Hash; 62 | tjs_uint32 Flags; // management flag 63 | char Key[sizeof(KeyT)]; 64 | char Value[sizeof(ValueT)]; 65 | element *Prev; // previous chain item 66 | element *Next; // next chain item 67 | element *NPrev; // previous item in the additional order 68 | element *NNext; // next item in the additional order 69 | } Elms[HashSize]; 70 | 71 | tjs_uint Count; 72 | 73 | element *NFirst; // first item in the additional order 74 | element *NLast; // last item in the additional order 75 | 76 | public: 77 | 78 | class tIterator // this differs a bit from STL's iterator 79 | { 80 | element * elm; 81 | public: 82 | tIterator() { elm = NULL; } 83 | 84 | // tIterator(const tIterator &ref) 85 | // { elm = ref.elm; } 86 | 87 | tIterator(element *r_elm) 88 | { 89 | elm = r_elm; 90 | } 91 | 92 | tIterator operator ++() 93 | { 94 | elm = elm->NNext; return elm; 95 | } 96 | 97 | tIterator operator --() 98 | { 99 | elm = elm->NPrev; return elm; 100 | } 101 | 102 | tIterator operator ++(int dummy) 103 | { 104 | element *b_elm = elm; elm = elm->NNext; return b_elm; 105 | } 106 | 107 | tIterator operator --(int dummy) 108 | { 109 | element *b_elm = elm; elm = elm->NPrev; return b_elm; 110 | } 111 | 112 | void operator +(tjs_int n) 113 | { 114 | while (n--) elm = elm->NNext; 115 | } 116 | 117 | void operator -(tjs_int n) 118 | { 119 | while (n--) elm = elm->NPrev; 120 | } 121 | 122 | bool operator ==(const tIterator & ref) const 123 | { 124 | return elm == ref.elm; 125 | } 126 | 127 | bool operator !=(const tIterator & ref) const 128 | { 129 | return elm != ref.elm; 130 | } 131 | 132 | KeyT & GetKey() 133 | { 134 | return *(KeyT*)elm->Key; 135 | } 136 | 137 | ValueT & GetValue() 138 | { 139 | return *(ValueT*)elm->Value; 140 | } 141 | 142 | bool IsNull() const { return elm == NULL; } 143 | }; 144 | 145 | static tjs_uint32 MakeHash(const KeyT &key) 146 | { 147 | return HashFuncT::Make(key); 148 | } 149 | 150 | tTJSHashTable() 151 | { 152 | InternalInit(); 153 | } 154 | 155 | ~tTJSHashTable() 156 | { 157 | InternalClear(); 158 | } 159 | 160 | void Clear() 161 | { 162 | InternalClear(); 163 | } 164 | 165 | tIterator GetFirst() const 166 | { 167 | return tIterator(NFirst); 168 | } 169 | 170 | tIterator GetLast() const 171 | { 172 | return tIterator(NLast); 173 | } 174 | 175 | 176 | void Add(const KeyT &key, const ValueT &value) 177 | { 178 | // add Key and Value 179 | AddWithHash(key, HashFuncT::Make(key), value); 180 | } 181 | 182 | void AddWithHash(const KeyT &key, tjs_uint32 hash, const ValueT &value) 183 | { 184 | // add Key ( hash ) and Value 185 | #ifdef TJS_HS_DEBUG_CHAIN 186 | hash = 0; 187 | #endif 188 | element *lv1 = Elms + hash % HashSize; 189 | element *elm = lv1->Next; 190 | while (elm) 191 | { 192 | if (hash == elm->Hash) 193 | { 194 | // same ? 195 | if (key == *(KeyT*)elm->Key) 196 | { 197 | // do copying instead of inserting if these are same 198 | CheckUpdateElementOrder(elm); 199 | *(ValueT*)elm->Value = value; 200 | return; 201 | } 202 | } 203 | elm = elm->Next; 204 | } 205 | 206 | // lv1 used ? 207 | if (!(lv1->Flags & TJS_HS_HASH_USING)) 208 | { 209 | // lv1 is unused 210 | Construct(*lv1, key, value); 211 | lv1->Hash = hash; 212 | lv1->Prev = NULL; 213 | // not initialize lv1->Next here 214 | CheckAddingElementOrder(lv1); 215 | return; 216 | } 217 | 218 | // lv1 is used 219 | if (hash == lv1->Hash) 220 | { 221 | // same? 222 | if (key == *(KeyT*)lv1->Key) 223 | { 224 | // do copying instead of inserting if these are same 225 | CheckUpdateElementOrder(lv1); 226 | *(ValueT*)lv1->Value = value; 227 | return; 228 | } 229 | } 230 | 231 | // insert after lv1 232 | element *newelm = new element; 233 | newelm->Flags = 0; 234 | Construct(*newelm, key, value); 235 | newelm->Hash = hash; 236 | if (lv1->Next) lv1->Next->Prev = newelm; 237 | newelm->Next = lv1->Next; 238 | newelm->Prev = lv1; 239 | lv1->Next = newelm; 240 | CheckAddingElementOrder(newelm); 241 | } 242 | 243 | 244 | ValueT * Find(const KeyT &key) const 245 | { 246 | // find key 247 | // return NULL if not found 248 | const element * elm = InternalFindWithHash(key, HashFuncT::Make(key)); 249 | if (!elm) return NULL; 250 | return (ValueT*)elm->Value; 251 | } 252 | 253 | bool Find(const KeyT &key, const KeyT *& keyout, ValueT *& value) const 254 | { 255 | // find key 256 | // return false if not found 257 | return FindWithHash(key, HashFuncT::Make(key), keyout, value); 258 | } 259 | 260 | ValueT * FindWithHash(const KeyT &key, tjs_uint32 hash) const 261 | { 262 | // find key ( hash ) 263 | // return NULL if not found 264 | #ifdef TJS_HS_DEBUG_CHAIN 265 | hash = 0; 266 | #endif 267 | const element * elm = InternalFindWithHash(key, hash); 268 | if (!elm) return NULL; 269 | return (ValueT*)elm->Value; 270 | } 271 | 272 | bool FindWithHash(const KeyT &key, tjs_uint32 hash, const KeyT *& keyout, ValueT *& value) const 273 | { 274 | // find key 275 | // return false if not found 276 | #ifdef TJS_HS_DEBUG_CHAIN 277 | hash = 0; 278 | #endif 279 | const element * elm = InternalFindWithHash(key, hash); 280 | if (elm) 281 | { 282 | value = (ValueT*)elm->Value; 283 | keyout = (const KeyT*)elm->Key; 284 | return true; 285 | } 286 | return false; 287 | } 288 | 289 | ValueT * FindAndTouch(const KeyT &key) 290 | { 291 | // find key and move it first if found 292 | const element * elm = InternalFindWithHash(key, HashFuncT::Make(key)); 293 | if (!elm) return NULL; 294 | CheckUpdateElementOrder((element *)elm); 295 | return (ValueT*)elm->Value; 296 | } 297 | 298 | bool FindAndTouch(const KeyT &key, const KeyT *& keyout, ValueT *& value) 299 | { 300 | // find key ( hash ) 301 | // return false if not found 302 | return FindAndTouchWithHash(key, HashFuncT::Make(key), keyout, value); 303 | } 304 | 305 | 306 | ValueT * FindAndTouchWithHash(const KeyT &key, tjs_uint32 hash) 307 | { 308 | // find key ( hash ) and move it first if found 309 | #ifdef TJS_HS_DEBUG_CHAIN 310 | hash = 0; 311 | #endif 312 | const element * elm = InternalFindWithHash(key, hash); 313 | if (!elm) return NULL; 314 | CheckUpdateElementOrder((element *)elm); // force casting 315 | return (ValueT*)elm->Value; 316 | } 317 | 318 | bool FindAndTouchWithHash(const KeyT &key, tjs_uint32 hash, const KeyT *& keyout, ValueT *& value) 319 | { 320 | // find key 321 | // return false if not found 322 | const element * elm = InternalFindWithHash(key, hash); 323 | if (elm) 324 | { 325 | CheckUpdateElementOrder((element *)elm); 326 | value = (ValueT*)elm->Value; 327 | keyout = (const KeyT*)elm->Key; 328 | return true; 329 | } 330 | return false; 331 | } 332 | 333 | bool Delete(const KeyT &key) 334 | { 335 | // delete key and return true if successed 336 | return DeleteWithHash(key, HashFuncT::Make(key)); 337 | } 338 | 339 | bool DeleteWithHash(const KeyT &key, tjs_uint32 hash) 340 | { 341 | // delete key ( hash ) and return true if succeeded 342 | element *lv1 = Elms + hash % HashSize; 343 | if (lv1->Flags & TJS_HS_HASH_USING && hash == lv1->Hash) 344 | { 345 | if (key == *(KeyT*)lv1->Key) 346 | { 347 | // delete lv1 348 | CheckDeletingElementOrder(lv1); 349 | Destruct(*lv1); 350 | return true; 351 | } 352 | } 353 | 354 | element *prev = lv1; 355 | element *elm = lv1->Next; 356 | while (elm) 357 | { 358 | if (hash == elm->Hash) 359 | { 360 | if (key == *(KeyT*)elm->Key) 361 | { 362 | CheckDeletingElementOrder(elm); 363 | Destruct(*elm); 364 | prev->Next = elm->Next; // sever from the chain 365 | if (elm->Next) elm->Next->Prev = prev; 366 | delete elm; 367 | return true; 368 | } 369 | } 370 | prev = elm; 371 | elm = elm->Next; 372 | } 373 | return false; // not found 374 | } 375 | 376 | tjs_int ChopLast(tjs_int count) 377 | { 378 | // chop items from last of additional order 379 | tjs_int ret = 0; 380 | while (count--) 381 | { 382 | if (!NLast) break; 383 | DeleteByElement(NLast); 384 | ret++; 385 | } 386 | return ret; 387 | } 388 | 389 | tjs_uint GetCount() { return Count; } 390 | 391 | private: 392 | void InternalClear() 393 | { 394 | for (tjs_int i = 0; i < HashSize; i++) 395 | { 396 | // delete all items 397 | element *elm = Elms[i].Next; 398 | while (elm) 399 | { 400 | Destruct(*elm); 401 | element *elmnext = elm->Next; 402 | delete elm; 403 | elm = elmnext; 404 | } 405 | if (Elms[i].Flags & TJS_HS_HASH_USING) 406 | { 407 | Destruct(Elms[i]); 408 | } 409 | } 410 | InternalInit(); 411 | } 412 | 413 | void InternalInit() 414 | { 415 | Count = 0; 416 | NFirst = NULL; 417 | NLast = NULL; 418 | for (tjs_int i = 0; i < HashSize; i++) 419 | { 420 | Elms[i].Flags = TJS_HS_HASH_LV1; 421 | Elms[i].Prev = NULL; 422 | Elms[i].Next = NULL; 423 | } 424 | } 425 | 426 | 427 | void DeleteByElement(element *elm) 428 | { 429 | CheckDeletingElementOrder(elm); 430 | Destruct(*elm); 431 | if (elm->Flags & TJS_HS_HASH_LV1) 432 | { 433 | // lv1 element 434 | // nothing to do 435 | } 436 | else 437 | { 438 | // other elements 439 | if (elm->Prev) elm->Prev->Next = elm->Next; 440 | if (elm->Next) elm->Next->Prev = elm->Prev; 441 | } 442 | if (!(elm->Flags & TJS_HS_HASH_LV1)) delete elm; 443 | } 444 | 445 | const element * InternalFindWithHash(const KeyT &key, tjs_uint32 hash) const 446 | { 447 | // find key ( hash ) 448 | const element *lv1 = Elms + hash % HashSize; 449 | if (hash == lv1->Hash && lv1->Flags & TJS_HS_HASH_USING) 450 | { 451 | if (key == *(KeyT*)lv1->Key) return lv1; 452 | } 453 | 454 | element *elm = lv1->Next; 455 | while (elm) 456 | { 457 | if (hash == elm->Hash) 458 | { 459 | if (key == *(KeyT*)elm->Key) return elm; 460 | } 461 | elm = elm->Next; 462 | } 463 | return NULL; // not found 464 | } 465 | 466 | 467 | void CheckAddingElementOrder(element *elm) 468 | { 469 | if (Count == 0) 470 | { 471 | NLast = elm; // first addition 472 | elm->NNext = NULL; 473 | } 474 | else 475 | { 476 | NFirst->NPrev = elm; 477 | elm->NNext = NFirst; 478 | } 479 | NFirst = elm; 480 | elm->NPrev = NULL; 481 | Count++; 482 | } 483 | 484 | void CheckDeletingElementOrder(element *elm) 485 | { 486 | Count--; 487 | if (Count > 0) 488 | { 489 | if (elm == NFirst) 490 | { 491 | // deletion of first item 492 | NFirst = elm->NNext; 493 | NFirst->NPrev = NULL; 494 | } 495 | else if (elm == NLast) 496 | { 497 | // deletion of last item 498 | NLast = elm->NPrev; 499 | NLast->NNext = NULL; 500 | } 501 | else 502 | { 503 | // deletion of intermediate item 504 | elm->NPrev->NNext = elm->NNext; 505 | elm->NNext->NPrev = elm->NPrev; 506 | } 507 | } 508 | else 509 | { 510 | // when the count becomes zero... 511 | NFirst = NLast = NULL; 512 | } 513 | } 514 | 515 | void CheckUpdateElementOrder(element *elm) 516 | { 517 | // move elm to the front of addtional order 518 | if (elm != NFirst) 519 | { 520 | if (NLast == elm) NLast = elm->NPrev; 521 | elm->NPrev->NNext = elm->NNext; 522 | if (elm->NNext) elm->NNext->NPrev = elm->NPrev; 523 | elm->NNext = NFirst; 524 | elm->NPrev = NULL; 525 | NFirst->NPrev = elm; 526 | NFirst = elm; 527 | } 528 | } 529 | 530 | static void Construct(element &elm, const KeyT &key, const ValueT &value) 531 | { 532 | ::new (&elm.Key) KeyT(key); 533 | ::new (&elm.Value) ValueT(value); 534 | elm.Flags |= TJS_HS_HASH_USING; 535 | } 536 | 537 | static void Destruct(element &elm) 538 | { 539 | ((KeyT*)(&elm.Key)) -> ~KeyT(); 540 | ((ValueT*)(&elm.Value)) -> ~ValueT(); 541 | elm.Flags &= ~TJS_HS_HASH_USING; 542 | } 543 | }; 544 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Kirikiri/tTJSString.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | void (__stdcall *tTJSString::_CtorStr) (tTJSString* pThis, const tTJSString& str); 4 | void (__stdcall *tTJSString::_CtorPsz) (tTJSString* pThis, const tjs_char* psz); 5 | void (__stdcall *tTJSString::_Dtor) (tTJSString* pThis); 6 | tTJSString& (__stdcall *tTJSString::_AssignStr) (tTJSString* pThis, const tTJSString& str); 7 | tTJSString& (__stdcall *tTJSString::_AssignPsz) (tTJSString* pThis, const tjs_char* psz); 8 | void (__stdcall *tTJSString::_PlusAssignStr) (tTJSString* pThis, const tTJSString& str); 9 | void (__stdcall *tTJSString::_PlusAssignPsz) (tTJSString* pThis, const tjs_char* psz); 10 | int (__stdcall *tTJSString::_GetLen) (const tTJSString* pThis); 11 | bool (__stdcall *tTJSString::_StartsWithStr) (const tTJSString* pThis, const tTJSString& str); 12 | bool (__stdcall *tTJSString::_StartsWithPsz) (const tTJSString* pThis, const tjs_char* psz); 13 | tTJSString (__stdcall *tTJSString::_PlusStr) (const tTJSString* pThis, const tTJSString& str); 14 | tTJSString (__stdcall *tTJSString::_PlusPsz) (const tTJSString* pThis, const tjs_char* psz); 15 | void (__stdcall *tTJSString::_Replace) (tTJSString* pThis, const tTJSString& from, const tTJSString& to, bool forAll); 16 | 17 | tTJSVariantString tTJSString::PreInitVariantString; 18 | 19 | void tTJSString::Init() 20 | { 21 | Kirikiri::ResolveScriptExport(L"tTJSString::~ tTJSString()", _Dtor); 22 | Kirikiri::ResolveScriptExport(L"tTJSString::tTJSString(const tTJSString &)", _CtorStr); 23 | Kirikiri::ResolveScriptExport(L"tTJSString::tTJSString(const tjs_char *)", _CtorPsz); 24 | Kirikiri::ResolveScriptExport(L"tTJSString & tTJSString::operator =(const tTJSString &)", _AssignStr); 25 | Kirikiri::ResolveScriptExport(L"tTJSString & tTJSString::operator =(const tjs_char *)", _AssignPsz); 26 | Kirikiri::ResolveScriptExport(L"void tTJSString::operator +=(const tTJSString &)", _PlusAssignStr); 27 | Kirikiri::ResolveScriptExport(L"void tTJSString::operator +=(const tjs_char *)", _PlusAssignPsz); 28 | Kirikiri::ResolveScriptExport(L"tjs_int tTJSString::GetLen() const", _GetLen); 29 | Kirikiri::ResolveScriptExport(L"bool tTJSString::StartsWith(const tTJSString &) const", _StartsWithStr); 30 | Kirikiri::ResolveScriptExport(L"bool tTJSString::StartsWith(const tjs_char *) const", _StartsWithPsz); 31 | Kirikiri::ResolveScriptExport(L"tTJSString tTJSString::operator +(const tTJSString &) const", _PlusStr); 32 | Kirikiri::ResolveScriptExport(L"tTJSString tTJSString::operator +(const tjs_char *) const", _PlusPsz); 33 | Kirikiri::ResolveScriptExport(L"void tTJSString::Replace(const tTJSString &,const tTJSString &,bool)", _Replace); 34 | } 35 | 36 | tTJSString::tTJSString() 37 | { 38 | Ptr = nullptr; 39 | } 40 | 41 | tTJSString::tTJSString(const tTJSString& str) 42 | { 43 | _CtorStr(this, str); 44 | } 45 | 46 | tTJSString::tTJSString(const tjs_char* psz) 47 | { 48 | if (_CtorPsz) 49 | { 50 | _CtorPsz(this, psz); 51 | } 52 | else 53 | { 54 | if (PreInitVariantString.LongString != nullptr) 55 | throw "Only one pre-init tTJSString can be active at a time"; 56 | 57 | PreInitVariantString.LongString = const_cast(psz); 58 | PreInitVariantString.Length = wcslen(psz); 59 | Ptr = &PreInitVariantString; 60 | } 61 | } 62 | 63 | tTJSString::~tTJSString() 64 | { 65 | if (Ptr == &PreInitVariantString) 66 | { 67 | PreInitVariantString.LongString = nullptr; 68 | Ptr = nullptr; 69 | } 70 | else 71 | { 72 | _Dtor(this); 73 | } 74 | } 75 | 76 | tTJSString& tTJSString::operator=(const tTJSString& str) 77 | { 78 | return _AssignStr(this, str); 79 | } 80 | 81 | tTJSString& tTJSString::operator=(const tjs_char* psz) 82 | { 83 | return _AssignPsz(this, psz); 84 | } 85 | 86 | void tTJSString::operator+=(const tTJSString& str) 87 | { 88 | _PlusAssignStr(this, str); 89 | } 90 | 91 | void tTJSString::operator+=(const tjs_char* psz) 92 | { 93 | _PlusAssignPsz(this, psz); 94 | } 95 | 96 | int tTJSString::GetLen() const 97 | { 98 | return _GetLen(this); 99 | } 100 | 101 | bool tTJSString::IsEmpty() const 102 | { 103 | return Ptr == nullptr; 104 | } 105 | 106 | bool tTJSString::StartsWith(const tTJSString& str) const 107 | { 108 | return _StartsWithStr(this, str); 109 | } 110 | 111 | bool tTJSString::StartsWith(const tjs_char* psz) const 112 | { 113 | return _StartsWithPsz(this, psz); 114 | } 115 | 116 | const tjs_char* tTJSString::c_str() const 117 | { 118 | return Ptr ? Ptr->operator const tjs_char*() : L""; 119 | } 120 | 121 | bool tTJSString::operator==(const tTJSString& str) const 122 | { 123 | if (Ptr == str.Ptr) return true; // both empty or the same pointer 124 | if (!Ptr && str.Ptr) return false; 125 | if (Ptr && !str.Ptr) return false; 126 | if (Ptr->Length != str.Ptr->Length) return false; 127 | return !wcscmp(*Ptr, *str.Ptr); 128 | } 129 | 130 | bool tTJSString::operator!=(const tTJSString& str) const 131 | { 132 | return !this->operator==(str); 133 | } 134 | 135 | tTJSString tTJSString::operator+(const tTJSString& str) const 136 | { 137 | return _PlusStr(this, str); 138 | } 139 | 140 | void tTJSString::Replace(const tTJSString& from, const tTJSString& to, bool forAll) 141 | { 142 | _Replace(this, from, to, forAll); 143 | } 144 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Kirikiri/tTJSString.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct tTJSString 4 | { 5 | public: 6 | static void Init (); 7 | 8 | tTJSString (); 9 | tTJSString (const tTJSString& str); 10 | tTJSString (const tjs_char* psz); 11 | 12 | ~tTJSString (); 13 | 14 | tTJSString& operator= (const tTJSString& str); 15 | tTJSString& operator= (const tjs_char* psz); 16 | 17 | void operator+= (const tTJSString& str); 18 | void operator+= (const tjs_char* psz); 19 | 20 | int GetLen () const; 21 | bool IsEmpty () const; 22 | bool StartsWith (const tTJSString& str) const; 23 | bool StartsWith (const tjs_char* psz) const; 24 | 25 | const tjs_char* c_str () const; 26 | 27 | bool operator== (const tTJSString& str) const; 28 | bool operator!= (const tTJSString& str) const; 29 | 30 | tTJSString operator+ (const tTJSString& str) const; 31 | 32 | void Replace (const tTJSString& from, const tTJSString& to, bool forAll); 33 | 34 | private: 35 | tTJSVariantString* Ptr; 36 | 37 | static void (__stdcall *_CtorStr) (tTJSString* pThis, const tTJSString& str); 38 | static void (__stdcall *_CtorPsz) (tTJSString* pThis, const tjs_char* psz); 39 | static void (__stdcall *_Dtor) (tTJSString* pThis); 40 | static tTJSString& (__stdcall *_AssignStr) (tTJSString* pThis, const tTJSString& str); 41 | static tTJSString& (__stdcall *_AssignPsz) (tTJSString* pThis, const tjs_char* psz); 42 | static void (__stdcall *_PlusAssignStr) (tTJSString* pThis, const tTJSString& str); 43 | static void (__stdcall *_PlusAssignPsz) (tTJSString* pThis, const tjs_char* psz); 44 | static int (__stdcall *_GetLen) (const tTJSString* pThis); 45 | static bool (__stdcall *_StartsWithStr) (const tTJSString* pThis, const tTJSString& str); 46 | static bool (__stdcall *_StartsWithPsz) (const tTJSString* pThis, const tjs_char* psz); 47 | static tTJSString (__stdcall *_PlusStr) (const tTJSString* pThis, const tTJSString& str); 48 | static tTJSString (__stdcall *_PlusPsz) (const tTJSString* pThis, const tjs_char* psz); 49 | static void (__stdcall *_Replace) (tTJSString* pThis, const tTJSString& from, const tTJSString& to, bool forAll); 50 | 51 | static tTJSVariantString PreInitVariantString; 52 | }; 53 | 54 | typedef tTJSString ttstr; 55 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Kirikiri/tTJSVariant.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | void (__stdcall *tTJSVariant::Ctor)(tTJSVariant* pThis); 4 | void (__stdcall *tTJSVariant::Dtor)(tTJSVariant* pThis); 5 | 6 | void tTJSVariant::Init() 7 | { 8 | Kirikiri::ResolveScriptExport(L"tTJSVariant::tTJSVariant()", Ctor); 9 | Kirikiri::ResolveScriptExport(L"tTJSVariant::~ tTJSVariant()", Dtor); 10 | } 11 | 12 | tTJSVariant::tTJSVariant() 13 | { 14 | Ctor(this); 15 | } 16 | 17 | tTJSVariant::~tTJSVariant() 18 | { 19 | Dtor(this); 20 | } 21 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Kirikiri/tTJSVariant.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | enum tTJSVariantType 4 | { 5 | tvtVoid, 6 | tvtObject, 7 | tvtString, 8 | tvtOctet, 9 | tvtInteger, 10 | tvtReal 11 | }; 12 | 13 | struct tTJSVariant 14 | { 15 | public: 16 | static void Init (); 17 | 18 | tTJSVariant (); 19 | ~tTJSVariant (); 20 | 21 | private: 22 | union 23 | { 24 | tTJSVariantClosure Object; 25 | tTVInteger Integer; 26 | tTVReal Real; 27 | tTJSVariantString* String; 28 | tTJSVariantOctet* Octet; 29 | }; 30 | tTJSVariantType vt; 31 | 32 | static void (__stdcall *Ctor) (tTJSVariant* pThis); 33 | static void (__stdcall *Dtor) (tTJSVariant* pThis); 34 | }; 35 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Kirikiri/tTJSVariantClosure.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct tTJSVariantClosure 4 | { 5 | iTJSDispatch2* Object; 6 | iTJSDispatch2* ObjThis; 7 | }; 8 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Kirikiri/tTJSVariantOctet.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct tTJSVariantOctet 4 | { 5 | tjs_uint Length; 6 | tjs_int RefCount; 7 | tjs_uint8* Data; 8 | }; 9 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Kirikiri/tTJSVariantString.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define TJS_VS_SHORT_LEN 21 4 | 5 | struct tTJSVariantString 6 | { 7 | tjs_int RefCount; 8 | tjs_char* LongString; 9 | tjs_char ShortString[TJS_VS_SHORT_LEN + 1]; 10 | tjs_int Length; 11 | tjs_uint32 HeapFlag; 12 | tjs_uint32 Hint; 13 | 14 | operator const tjs_char*() const 15 | { 16 | return LongString ? LongString : ShortString; 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Kirikiri/tTVPArchive.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class tTVPArchive 4 | { 5 | public: 6 | virtual ~tTVPArchive() = 0; 7 | virtual tjs_uint GetCount() = 0; 8 | virtual ttstr GetName(tjs_uint idx) = 0; 9 | virtual tTJSBinaryStream* CreateStreamByIndex(tjs_uint idx) = 0; 10 | 11 | protected: 12 | tjs_uint RefCount; 13 | tTJSHashTable, 1024> Hash; 14 | bool Init; 15 | ttstr ArchiveName; 16 | }; 17 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Kirikiri/tTVPXP3Archive.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct tTVPXP3ArchiveSegment 4 | { 5 | tjs_uint64 Start; 6 | tjs_uint64 Offset; 7 | tjs_uint64 OrgSize; 8 | tjs_uint64 ArcSize; 9 | bool IsCompressed; 10 | }; 11 | 12 | template 13 | class tTVPXP3Archive : public tTVPArchive 14 | { 15 | public: 16 | ttstr Name; 17 | 18 | struct tArchiveItem 19 | { 20 | ttstr Name; 21 | tjs_uint32 FileHash; 22 | tjs_uint64 OrgSize; 23 | tjs_uint64 ArcSize; 24 | CompilerSpecificVector Segments; 25 | }; 26 | 27 | tjs_int Count; 28 | 29 | CompilerSpecificVector ItemVector; 30 | }; 31 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/KirikiriUnencryptedArchive.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | 15.0 15 | {68FD9BD1-B4E9-4C34-BBB4-45ED49EE6315} 16 | KirikiriUnencryptedArchive 17 | 10.0 18 | 19 | 20 | 21 | DynamicLibrary 22 | true 23 | v142 24 | Unicode 25 | 26 | 27 | DynamicLibrary 28 | false 29 | v142 30 | true 31 | Unicode 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | version 48 | 49 | 50 | version 51 | 52 | 53 | 54 | Level3 55 | Disabled 56 | true 57 | Use 58 | _CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) 59 | .;%(AdditionalIncludeDirectories) 60 | stdcpplatest 61 | 62 | 63 | exports.def 64 | Imagehlp.lib;%(AdditionalDependencies) 65 | 66 | 67 | 68 | 69 | Level3 70 | MaxSpeed 71 | true 72 | true 73 | true 74 | Use 75 | _CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) 76 | .;%(AdditionalIncludeDirectories) 77 | stdcpplatest 78 | 79 | 80 | true 81 | true 82 | exports.def 83 | Imagehlp.lib;%(AdditionalDependencies) 84 | 85 | 86 | 87 | 88 | {9d3f6c0a-9ec1-420c-affe-645a574dd9a0} 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | Create 106 | Create 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/KirikiriUnencryptedArchive.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | ソース ファイル 20 | 21 | 22 | ソース ファイル 23 | 24 | 25 | ソース ファイル 26 | 27 | 28 | ソース ファイル 29 | 30 | 31 | ソース ファイル 32 | 33 | 34 | ソース ファイル 35 | 36 | 37 | ソース ファイル 38 | 39 | 40 | ソース ファイル 41 | 42 | 43 | ソース ファイル 44 | 45 | 46 | ソース ファイル 47 | 48 | 49 | ソース ファイル 50 | 51 | 52 | ソース ファイル 53 | 54 | 55 | ソース ファイル 56 | 57 | 58 | ソース ファイル 59 | 60 | 61 | 62 | 63 | ヘッダー ファイル 64 | 65 | 66 | ヘッダー ファイル 67 | 68 | 69 | ヘッダー ファイル 70 | 71 | 72 | ヘッダー ファイル 73 | 74 | 75 | ヘッダー ファイル 76 | 77 | 78 | ヘッダー ファイル 79 | 80 | 81 | ヘッダー ファイル 82 | 83 | 84 | ヘッダー ファイル 85 | 86 | 87 | ヘッダー ファイル 88 | 89 | 90 | ヘッダー ファイル 91 | 92 | 93 | ヘッダー ファイル 94 | 95 | 96 | ヘッダー ファイル 97 | 98 | 99 | ヘッダー ファイル 100 | 101 | 102 | ヘッダー ファイル 103 | 104 | 105 | ヘッダー ファイル 106 | 107 | 108 | ヘッダー ファイル 109 | 110 | 111 | ヘッダー ファイル 112 | 113 | 114 | ヘッダー ファイル 115 | 116 | 117 | ヘッダー ファイル 118 | 119 | 120 | ヘッダー ファイル 121 | 122 | 123 | ヘッダー ファイル 124 | 125 | 126 | ヘッダー ファイル 127 | 128 | 129 | ヘッダー ファイル 130 | 131 | 132 | ヘッダー ファイル 133 | 134 | 135 | ヘッダー ファイル 136 | 137 | 138 | ヘッダー ファイル 139 | 140 | 141 | ヘッダー ファイル 142 | 143 | 144 | ヘッダー ファイル 145 | 146 | 147 | ヘッダー ファイル 148 | 149 | 150 | ヘッダー ファイル 151 | 152 | 153 | ヘッダー ファイル 154 | 155 | 156 | ヘッダー ファイル 157 | 158 | 159 | ヘッダー ファイル 160 | 161 | 162 | 163 | 164 | ソース ファイル 165 | 166 | 167 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/KirikiriUnencryptedArchive.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | true 5 | 6 | 7 | WindowsLocalDebugger 8 | 9 | 10 | WindowsLocalDebugger 11 | 12 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/PE/PE.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | using namespace std; 4 | 5 | vector PE::GetSections(HMODULE hModule) 6 | { 7 | IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)hModule; 8 | IMAGE_NT_HEADERS* pNtHeaders = (IMAGE_NT_HEADERS*)((BYTE*)hModule + pDosHeader->e_lfanew); 9 | IMAGE_SECTION_HEADER* pSectionHeaders = IMAGE_FIRST_SECTION(pNtHeaders); 10 | 11 | vector
sections; 12 | for (int i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++) 13 | { 14 | Section section 15 | { 16 | .Start = (BYTE*)hModule + pSectionHeaders[i].VirtualAddress, 17 | .Size = (int)pSectionHeaders[i].SizeOfRawData, 18 | .Characteristics = pSectionHeaders[i].Characteristics 19 | }; 20 | memcpy(section.Name, pSectionHeaders[i].Name, 8); 21 | sections.push_back(section); 22 | } 23 | return sections; 24 | } 25 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/PE/PE.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class PE 4 | { 5 | public: 6 | struct Section 7 | { 8 | char Name[8]; 9 | BYTE* Start; 10 | int Size; 11 | DWORD Characteristics; 12 | }; 13 | 14 | static std::vector
GetSections (HMODULE hModule); 15 | }; 16 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Patcher.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | using namespace std; 4 | 5 | bool Patcher::PatchSignatureCheck(HMODULE hModule) 6 | { 7 | void** pVerifierVTable = CompilerHelper::FindVTable(hModule, CompilerType::Msvc, "KrkrSign::VerifierImpl"); 8 | if (pVerifierVTable == nullptr) 9 | return false; 10 | 11 | Debugger::Log(L"Patching KrkrSign::VerifierImpl"); 12 | MemoryUtil::WritePointer(pVerifierVTable + 4, CustomGetSignatureVerificationResult); 13 | return true; 14 | } 15 | 16 | void Patcher::PatchXP3StreamCreation() 17 | { 18 | void** pXP3ArchiveVTable = CompilerHelper::FindVTable("tTVPXP3Archive"); 19 | if (pXP3ArchiveVTable == nullptr) 20 | { 21 | Debugger::Log(L"Failed to find tTVPXP3Archive vtable"); 22 | return; 23 | } 24 | 25 | Debugger::Log(L"Located tTVPXP3Archive vtable (%08X - %s)", pXP3ArchiveVTable, CompilerHelper::CompilerType == CompilerType::Borland ? L"Borland" : L"MSVC"); 26 | OriginalCreateStreamByIndex = pXP3ArchiveVTable[3]; 27 | 28 | MemoryUtil::WritePointer(pXP3ArchiveVTable + 3, CompilerHelper::WrapAsInstanceMethod()); 29 | } 30 | 31 | void Patcher::PatchAutoPathExports() 32 | { 33 | Kirikiri::HookScriptExport(L"void ::TVPAddAutoPath(const ttstr &)", &OriginalTVPAddAutoPath, CustomTVPAddAutoPath); 34 | Kirikiri::HookScriptExport(L"void ::TVPRemoveAutoPath(const ttstr &)", &OriginalTVPRemoveAutoPath, CustomTVPRemoveAutoPath); 35 | } 36 | 37 | void Patcher::PatchStorageMediaRegistration() 38 | { 39 | Kirikiri::HookScriptExport(L"void ::TVPRegisterStorageMedia(iTVPStorageMedia *)", &OriginalTVPRegisterStorageMedia, CustomTVPRegisterStorageMedia); 40 | Kirikiri::HookScriptExport(L"void ::TVPUnregisterStorageMedia(iTVPStorageMedia *)", &OriginalTVPUnregisterStorageMedia, CustomTVPUnregisterStorageMedia); 41 | } 42 | 43 | void Patcher::CustomTVPAddAutoPath(const ttstr& url) 44 | { 45 | if (&url == nullptr) 46 | return; 47 | 48 | if (!CxdecHelper::IsCxdecUrl(url)) 49 | { 50 | OriginalTVPAddAutoPath(url); 51 | return; 52 | } 53 | 54 | ttstr filePath = CxdecHelper::CxdecUrlToXp3FilePath(url); 55 | if (CxdecHelper::IsCxdecArchive(filePath)) 56 | { 57 | OriginalTVPAddAutoPath(url); 58 | } 59 | else 60 | { 61 | Debugger::Log(L"CustomTVPAddAutoPath(): Changing Cxdec URL %s to %s", url.c_str(), filePath.c_str()); 62 | OriginalTVPAddAutoPath(filePath + L">"); 63 | } 64 | } 65 | 66 | void Patcher::CustomTVPRemoveAutoPath(const ttstr& url) 67 | { 68 | if (&url == nullptr) 69 | return; 70 | 71 | if (!CxdecHelper::IsCxdecUrl(url)) 72 | { 73 | OriginalTVPRemoveAutoPath(url); 74 | return; 75 | } 76 | 77 | ttstr filePath = CxdecHelper::CxdecUrlToXp3FilePath(url); 78 | if (CxdecHelper::IsCxdecArchive(filePath)) 79 | { 80 | OriginalTVPRemoveAutoPath(url); 81 | } 82 | else 83 | { 84 | Debugger::Log(L"CustomTVPRemoveAutoPath(): Changing Cxdec URL %s to %s", url.c_str(), filePath.c_str()); 85 | OriginalTVPRemoveAutoPath(filePath + L">"); 86 | } 87 | } 88 | 89 | void Patcher::CustomTVPRegisterStorageMedia(iTVPStorageMedia* pMedia) 90 | { 91 | ttstr mediaName; 92 | pMedia->GetName(mediaName); 93 | 94 | if (mediaName != ttstr(L"steam")) { 95 | Debugger::Log(L"Hooking storage media \"%s\"", mediaName.c_str()); 96 | 97 | void** pVtable = *(void***)pMedia; 98 | OriginalStorageMediaOpen[pMedia] = (decltype(CustomStorageMediaOpen)*)pVtable[6]; 99 | MemoryUtil::WritePointer(&pVtable[6], CustomStorageMediaOpen); 100 | } 101 | 102 | OriginalTVPRegisterStorageMedia(pMedia); 103 | } 104 | 105 | void Patcher::CustomTVPUnregisterStorageMedia(iTVPStorageMedia* pMedia) 106 | { 107 | auto it = OriginalStorageMediaOpen.find(pMedia); 108 | if (it != OriginalStorageMediaOpen.end()) 109 | { 110 | void** pVtable = *(void***)pMedia; 111 | MemoryUtil::WritePointer(&pVtable[6], it->second); 112 | 113 | OriginalStorageMediaOpen.erase(it); 114 | } 115 | 116 | OriginalTVPUnregisterStorageMedia(pMedia); 117 | } 118 | 119 | tTJSBinaryStream* Patcher::CustomStorageMediaOpen(iTVPStorageMedia* pMedia, const ttstr& name, tjs_uint32 flags) 120 | { 121 | static wstring folderPath = Path::GetModuleFolderPath(nullptr); 122 | 123 | const wchar_t* pFilePath = wcschr(name.c_str(), L'/') + 1; 124 | wstring looseFilePath = Path::Combine(Path::Combine(folderPath, L"unencrypted"), StringUtil::Replace(pFilePath, L'/', L'\\')); 125 | wstring unencryptedXp3Path = Path::Combine(folderPath, L"unencrypted.xp3"); 126 | 127 | vector urls = { Kirikiri::FilePathToUrl(looseFilePath) }; 128 | if (GetFileAttributes(unencryptedXp3Path.c_str()) != INVALID_FILE_ATTRIBUTES) 129 | { 130 | wstring unencryptedXp3Url = Kirikiri::FilePathToUrl(unencryptedXp3Path); 131 | urls.push_back(unencryptedXp3Url + L">" + pFilePath); 132 | 133 | const wchar_t* pFileName = wcsrchr(name.c_str(), L'/') + 1; 134 | if (pFileName != pFilePath) 135 | urls.push_back(unencryptedXp3Url + L">" + pFileName); 136 | } 137 | 138 | for (wstring& url : urls) 139 | { 140 | if (Kirikiri::TVPIsExistentStorageNoSearchNoNormalize(url.c_str())) 141 | { 142 | ttstr mediaName; 143 | pMedia->GetName(mediaName); 144 | Debugger::Log(L"Redirecting %s://%s to %s", mediaName.c_str(), name.c_str(), url.c_str()); 145 | 146 | void* pComStream = Kirikiri::TVPCreateIStream(url.c_str(), flags); 147 | return Kirikiri::TVPCreateBinaryStreamAdapter(pComStream); 148 | } 149 | } 150 | 151 | tTJSBinaryStream* pStream = OriginalStorageMediaOpen[pMedia](pMedia, name, flags); 152 | 153 | static bool extractionRequested = GetFileAttributes(Path::Combine(folderPath, L"extract-unencrypted.txt").c_str()) != INVALID_FILE_ATTRIBUTES; 154 | if (pStream != nullptr && extractionRequested) 155 | { 156 | Debugger::Log(L"Extracting %s", pFilePath); 157 | WriteStreamToFile(pStream, looseFilePath); 158 | } 159 | 160 | return pStream; 161 | } 162 | 163 | void Patcher::WriteStreamToFile(tTJSBinaryStream* pStream, const std::wstring& filePath) 164 | { 165 | vector data; 166 | data.resize(pStream->GetSize()); 167 | pStream->Read(data.data(), data.size()); 168 | pStream->Seek(0, SEEK_SET); 169 | 170 | Directory::Create(Path::GetDirectoryName(filePath)); 171 | FileStream fileStream(filePath, L"wb+"); 172 | fileStream.Write(data); 173 | } 174 | 175 | bool Patcher::CustomGetSignatureVerificationResult() 176 | { 177 | return true; 178 | } 179 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Patcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class Patcher 4 | { 5 | friend CompilerHelper; 6 | 7 | public: 8 | static bool PatchSignatureCheck (HMODULE hModule); 9 | 10 | static void PatchXP3StreamCreation (); 11 | static void PatchAutoPathExports (); 12 | static void PatchStorageMediaRegistration (); 13 | 14 | private: 15 | static void __stdcall CustomTVPAddAutoPath (const ttstr& url); 16 | static void __stdcall CustomTVPRemoveAutoPath (const ttstr& url); 17 | 18 | static void __stdcall CustomTVPRegisterStorageMedia (iTVPStorageMedia* pMedia); 19 | static void __stdcall CustomTVPUnregisterStorageMedia (iTVPStorageMedia* pMedia); 20 | static tTJSBinaryStream* CustomStorageMediaOpen (iTVPStorageMedia* pMedia, const ttstr& name, tjs_uint32 flags); 21 | static void WriteStreamToFile (tTJSBinaryStream* pStream, const std::wstring& filePath); 22 | 23 | static bool CustomGetSignatureVerificationResult (); 24 | 25 | template 26 | class CustomCreateStreamByIndex 27 | { 28 | public: 29 | static tTJSBinaryStream* Call(tTVPXP3Archive* pArchive, tjs_uint idx) 30 | { 31 | int itemSize = ((BYTE*)pArchive->ItemVector.end() - (BYTE*)pArchive->ItemVector.begin()) / pArchive->Count; 32 | auto* pItem = (typename tTVPXP3Archive::tArchiveItem*)((BYTE*)pArchive->ItemVector.begin() + idx * itemSize); 33 | if (pItem->FileHash != 0 || !pArchive->Name.StartsWith(L"file://")) 34 | return CompilerHelper::CallInstanceMethod*, tjs_uint>(pArchive, idx); 35 | 36 | Debugger::Log(L"Creating unencrypted XP3 stream for %s", pItem->Name.c_str()); 37 | tTVPXP3ArchiveSegment* pSegment = pItem->Segments.begin(); 38 | auto* pStream = new CustomTVPXP3ArchiveStream(pArchive->Name, pSegment->Start, pSegment->OrgSize, pSegment->ArcSize, pSegment->IsCompressed); 39 | tTJSBinaryStream::ApplyWrappedVTable(pStream); 40 | return pStream; 41 | } 42 | }; 43 | 44 | static inline void* OriginalCreateStreamByIndex{}; 45 | 46 | static inline void (__stdcall* OriginalTVPAddAutoPath)(const ttstr& path){}; 47 | static inline void (__stdcall* OriginalTVPRemoveAutoPath)(const ttstr& path){}; 48 | static inline void (__stdcall* OriginalTVPRegisterStorageMedia)(iTVPStorageMedia* pMedia){}; 49 | static inline void (__stdcall* OriginalTVPUnregisterStorageMedia)(iTVPStorageMedia* pMedia){}; 50 | static inline std::map OriginalStorageMediaOpen{}; 51 | }; 52 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Proxy.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | void Proxy::Init() 4 | { 5 | wchar_t realDllPath[MAX_PATH]; 6 | GetSystemDirectory(realDllPath, MAX_PATH); 7 | wcscat_s(realDllPath, L"\\version.dll"); 8 | HMODULE hDll = LoadLibrary(realDllPath); 9 | if (hDll == nullptr) 10 | { 11 | MessageBox(nullptr, L"Cannot load original version.dll library", L"KirikiriUnencryptedArchive", MB_ICONERROR); 12 | ExitProcess(0); 13 | } 14 | 15 | #define RESOLVE(fn) Original##fn = reinterpret_cast(GetProcAddress(hDll, #fn)) 16 | RESOLVE(GetFileVersionInfoA); 17 | RESOLVE(GetFileVersionInfoByHandle); 18 | RESOLVE(GetFileVersionInfoExA); 19 | RESOLVE(GetFileVersionInfoExW); 20 | RESOLVE(GetFileVersionInfoSizeA); 21 | RESOLVE(GetFileVersionInfoSizeExA); 22 | RESOLVE(GetFileVersionInfoSizeExW); 23 | RESOLVE(GetFileVersionInfoSizeW); 24 | RESOLVE(GetFileVersionInfoW); 25 | RESOLVE(VerFindFileA); 26 | RESOLVE(VerFindFileW); 27 | RESOLVE(VerInstallFileA); 28 | RESOLVE(VerInstallFileW); 29 | RESOLVE(VerLanguageNameA); 30 | RESOLVE(VerLanguageNameW); 31 | RESOLVE(VerQueryValueA); 32 | RESOLVE(VerQueryValueW); 33 | #undef RESOLVE 34 | } 35 | 36 | __declspec(naked) void FakeGetFileVersionInfoA() { __asm { jmp [Proxy::OriginalGetFileVersionInfoA] } } 37 | __declspec(naked) void FakeGetFileVersionInfoByHandle() { __asm { jmp [Proxy::OriginalGetFileVersionInfoByHandle] } } 38 | __declspec(naked) void FakeGetFileVersionInfoExA() { __asm { jmp [Proxy::OriginalGetFileVersionInfoExA] } } 39 | __declspec(naked) void FakeGetFileVersionInfoExW() { __asm { jmp [Proxy::OriginalGetFileVersionInfoExW] } } 40 | __declspec(naked) void FakeGetFileVersionInfoSizeA() { __asm { jmp [Proxy::OriginalGetFileVersionInfoSizeA] } } 41 | __declspec(naked) void FakeGetFileVersionInfoSizeExA() { __asm { jmp [Proxy::OriginalGetFileVersionInfoSizeExA] } } 42 | __declspec(naked) void FakeGetFileVersionInfoSizeExW() { __asm { jmp [Proxy::OriginalGetFileVersionInfoSizeExW] } } 43 | __declspec(naked) void FakeGetFileVersionInfoSizeW() { __asm { jmp [Proxy::OriginalGetFileVersionInfoSizeW] } } 44 | __declspec(naked) void FakeGetFileVersionInfoW() { __asm { jmp [Proxy::OriginalGetFileVersionInfoW] } } 45 | __declspec(naked) void FakeVerFindFileA() { __asm { jmp [Proxy::OriginalVerFindFileA] } } 46 | __declspec(naked) void FakeVerFindFileW() { __asm { jmp [Proxy::OriginalVerFindFileW] } } 47 | __declspec(naked) void FakeVerInstallFileA() { __asm { jmp [Proxy::OriginalVerInstallFileA] } } 48 | __declspec(naked) void FakeVerInstallFileW() { __asm { jmp [Proxy::OriginalVerInstallFileW] } } 49 | __declspec(naked) void FakeVerLanguageNameA() { __asm { jmp [Proxy::OriginalVerLanguageNameA] } } 50 | __declspec(naked) void FakeVerLanguageNameW() { __asm { jmp [Proxy::OriginalVerLanguageNameW] } } 51 | __declspec(naked) void FakeVerQueryValueA() { __asm { jmp [Proxy::OriginalVerQueryValueA] } } 52 | __declspec(naked) void FakeVerQueryValueW() { __asm { jmp [Proxy::OriginalVerQueryValueW] } } 53 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/Proxy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class Proxy 4 | { 5 | public: 6 | static void Init(); 7 | 8 | static inline decltype(GetFileVersionInfoA)* OriginalGetFileVersionInfoA{}; 9 | static inline void* OriginalGetFileVersionInfoByHandle{}; 10 | static inline decltype(GetFileVersionInfoExA)* OriginalGetFileVersionInfoExA{}; 11 | static inline decltype(GetFileVersionInfoExW)* OriginalGetFileVersionInfoExW{}; 12 | static inline decltype(GetFileVersionInfoSizeA)* OriginalGetFileVersionInfoSizeA{}; 13 | static inline decltype(GetFileVersionInfoSizeExA)* OriginalGetFileVersionInfoSizeExA{}; 14 | static inline decltype(GetFileVersionInfoSizeExW)* OriginalGetFileVersionInfoSizeExW{}; 15 | static inline decltype(GetFileVersionInfoSizeW)* OriginalGetFileVersionInfoSizeW{}; 16 | static inline decltype(GetFileVersionInfoW)* OriginalGetFileVersionInfoW{}; 17 | static inline decltype(VerFindFileA)* OriginalVerFindFileA{}; 18 | static inline decltype(VerFindFileW)* OriginalVerFindFileW{}; 19 | static inline decltype(VerInstallFileA)* OriginalVerInstallFileA{}; 20 | static inline decltype(VerInstallFileW)* OriginalVerInstallFileW{}; 21 | static inline decltype(VerLanguageNameA)* OriginalVerLanguageNameA{}; 22 | static inline decltype(VerLanguageNameW)* OriginalVerLanguageNameW{}; 23 | static inline decltype(VerQueryValueA)* OriginalVerQueryValueA{}; 24 | static inline decltype(VerQueryValueW)* OriginalVerQueryValueW{}; 25 | }; 26 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/exports.def: -------------------------------------------------------------------------------- 1 | LIBRARY version 2 | EXPORTS 3 | GetFileVersionInfoA=FakeGetFileVersionInfoA @1 4 | GetFileVersionInfoByHandle=FakeGetFileVersionInfoByHandle @2 5 | GetFileVersionInfoExA=FakeGetFileVersionInfoExA @3 6 | GetFileVersionInfoExW=FakeGetFileVersionInfoExW @4 7 | GetFileVersionInfoSizeA=FakeGetFileVersionInfoSizeA @5 8 | GetFileVersionInfoSizeExA=FakeGetFileVersionInfoSizeExA @6 9 | GetFileVersionInfoSizeExW=FakeGetFileVersionInfoSizeExW @7 10 | GetFileVersionInfoSizeW=FakeGetFileVersionInfoSizeW @8 11 | GetFileVersionInfoW=FakeGetFileVersionInfoW @9 12 | VerFindFileA=FakeVerFindFileA @10 13 | VerFindFileW=FakeVerFindFileW @11 14 | VerInstallFileA=FakeVerInstallFileA @12 15 | VerInstallFileW=FakeVerInstallFileW @13 16 | VerLanguageNameA=FakeVerLanguageNameA @14 17 | VerLanguageNameW=FakeVerLanguageNameW @15 18 | VerQueryValueA=FakeVerQueryValueA @16 19 | VerQueryValueW=FakeVerQueryValueW @17 20 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/main.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD reason, LPVOID reserved) 4 | { 5 | if (reason == DLL_PROCESS_ATTACH) 6 | { 7 | Proxy::Init(); 8 | 9 | Debugger::RegisterDllLoadHandler( 10 | [](const wchar_t* pwszDllPath, HMODULE hDll) 11 | { 12 | if (Debugger::FindExport(hDll, "V2Link") != nullptr) 13 | Patcher::PatchSignatureCheck(hDll); 14 | } 15 | ); 16 | 17 | Kirikiri::Init( 18 | [] 19 | { 20 | CompilerHelper::Init(); 21 | Patcher::PatchXP3StreamCreation(); 22 | Patcher::PatchAutoPathExports(); 23 | Patcher::PatchStorageMediaRegistration(); 24 | } 25 | ); 26 | } 27 | 28 | return TRUE; 29 | } 30 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/stdafx.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | -------------------------------------------------------------------------------- /KirikiriUnencryptedArchive/stdafx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "../Detours/detours.h" 22 | 23 | #include "Proxy.h" 24 | #include "Common.h" 25 | 26 | #include "PE/PE.h" 27 | 28 | #include "CompilerSpecific/Enumerations.h" 29 | #include "CompilerSpecific/CompilerSpecificVector.h" 30 | #include "CompilerSpecific/Rtti/BorlandTypeDescriptor.h" 31 | #include "CompilerSpecific/Rtti/MsvcRttiCompleteObjectLocator.h" 32 | #include "CompilerSpecific/CallingConvention/BorlandRegToCdeclAdapter.h" 33 | #include "CompilerSpecific/CallingConvention/CdeclToBorlandRegAdapter.h" 34 | #include "CompilerSpecific/CallingConvention/CdeclToThiscallAdapter.h" 35 | #include "CompilerSpecific/CallingConvention/ThiscallToBorlandRegAdapter.h" 36 | #include "CompilerSpecific/CallingConvention/ThiscallToCdeclAdapter.h" 37 | #include "CompilerSpecific/CompilerHelper.h" 38 | 39 | #include "Debugger.h" 40 | #include "ImportHooker.h" 41 | #include "Kirikiri/Kirikiri.h" 42 | #include "CustomTVPXP3ArchiveStream.h" 43 | #include "CxdecHelper.h" 44 | #include "Patcher.h" 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 arcusmaximus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KirikiriTools 2 | Tools for the Kirikiri visual novel engine. [Download page](https://github.com/arcusmaximus/KirikiriTools/releases) 3 | 4 | If you're looking to translate a Kirikiri game, take a look at [VNTextPatch](https://github.com/arcusmaximus/VNTranslationTools). 5 | 6 | ## KirikiriDescrambler 7 | Some Kirikiri games have their plaintext scripts (.ks/.tjs) scrambled or compressed. Such files can be recognized by the following signatures at the start: 8 | ``` 9 | FE FE 00 FF FE 10 | FE FE 01 FF FE 11 | FE FE 02 FF FE 12 | ``` 13 | KirikiriDescrambler turns these into regular text files which can be placed right back in the game - no rescrambling needed. 14 | 15 | ## KirikiriUnencryptedArchive 16 | A DLL (named "version.dll") that makes games accept unencrypted .xp3 archives. By using this file, it's no longer necessary to identify and replicate the game's encryption when trying to add/replace .xp3 files; just create an unencrypted one with the Xp3Pack tool in this repository, throw the version.dll in the game's folder, and you're done. 17 | 18 | The DLL produces debug messages that can be seen with Microsoft's [DebugView](https://docs.microsoft.com/en-us/sysinternals/downloads/debugview) tool - this can be handy to confirm it's working. 19 | 20 | Some special features are available for helping with .xp3 archives that contain encrypted/hashed file names and aren't supported by other tools. Specifically, these features are available if DebugView shows a message such as "Hooking storage media 'arc'" when the game starts up (this doesn't apply to the media "psb"). 21 | * If there's a file called `extract-unencrypted.txt` in the game's folder, the DLL will extract any files that the game accesses into a subfolder called "unencrypted", with their original names. Note that the files are only extracted as the game reads them, so if you want to extract everything, you have to skip though the entire game. **If you can extract the archives with another tool, use that instead.** 22 | * If a file exists in the "unencrypted" folder, the game will use that one instead of the original in the encrypted .xp3 archives. 23 | * If a file exists in an archive called "unencrypted.xp3", the game will use that one instead of the original in the encrypted .xp3 archives. 24 | 25 | ## Xp3Pack 26 | Creates unencrypted .xp3 archives for use with the KirikiriUnencryptedArchive DLL. Unlike other packing tools, it sets all hashes in the file table to zero; this serves as a marker for the DLL to bypass the game's decryption for those files. 27 | 28 | Typical usage is to place Xp3Pack.exe in the game folder, create a "patch" subfolder containing the files you want to include, and run "Xp3Pack patch" from the command line. This will create a patch.xp3 in the game folder. If the game already has its own patch.xp3, name your folder "patch2" and run "Xp3Pack patch2" instead. If the game already has a patch2.xp3, name your folder "patch3", and so on. 29 | -------------------------------------------------------------------------------- /Xp3Pack/Adler32.cs: -------------------------------------------------------------------------------- 1 | namespace Arc.Ddsi.Xp3Pack 2 | { 3 | internal class Adler32 4 | { 5 | private int _a = 1; 6 | private int _b = 0; 7 | 8 | public int Checksum 9 | { 10 | get 11 | { 12 | return _b * 65536 + _a; 13 | } 14 | } 15 | 16 | private const int Modulus = 65521; 17 | 18 | public void Update(byte[] data, int offset, int length) 19 | { 20 | for (int counter = 0; counter < length; ++counter) 21 | { 22 | _a = (_a + (data[offset + counter])) % Modulus; 23 | _b = (_b + _a) % Modulus; 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Xp3Pack/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Arc.Ddsi.Xp3Pack 5 | { 6 | public static class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | if (args.Length != 1) 11 | { 12 | Console.WriteLine("Usage: Xp3Pack folder"); 13 | return; 14 | } 15 | 16 | string folderPath = args[0]; 17 | if (!Directory.Exists(folderPath)) 18 | { 19 | Console.WriteLine("Specified folder does not exist."); 20 | return; 21 | } 22 | 23 | string parentFolderPath = Path.GetDirectoryName(folderPath); 24 | if (parentFolderPath == null) 25 | { 26 | Console.WriteLine("Specified folder does not have a parent folder."); 27 | return; 28 | } 29 | 30 | string xp3FilePath = Path.Combine(parentFolderPath, Path.GetFileName(folderPath) + ".xp3"); 31 | Xp3ArchiveWriter.Write(folderPath, xp3FilePath); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Xp3Pack/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // アセンブリに関する一般情報は以下の属性セットをとおして制御されます。 6 | // アセンブリに関連付けられている情報を変更するには、 7 | // これらの属性値を変更してください。 8 | [assembly: AssemblyTitle("Xp3Pack")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Xp3Pack")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // ComVisible を false に設定すると、このアセンブリ内の型は COM コンポーネントから 18 | // 参照できなくなります。COM からこのアセンブリ内の型にアクセスする必要がある場合は、 19 | // その型の ComVisible 属性を true に設定してください。 20 | [assembly: ComVisible(false)] 21 | 22 | // このプロジェクトが COM に公開される場合、次の GUID が typelib の ID になります 23 | [assembly: Guid("45dcbc40-f6eb-46cf-9d07-c8ffc57728f7")] 24 | 25 | // アセンブリのバージョン情報は次の 4 つの値で構成されています: 26 | // 27 | // メジャー バージョン 28 | // マイナー バージョン 29 | // ビルド番号 30 | // Revision 31 | // 32 | // すべての値を指定するか、次を使用してビルド番号とリビジョン番号を既定に設定できます 33 | // 以下のように '*' を使用します: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Xp3Pack/Xp3ArchiveWriter.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Arc.Ddsi.Xp3Pack 4 | { 5 | internal class Xp3ArchiveWriter 6 | { 7 | private static readonly byte[] HeaderMagic = { 0x58, 0x50, 0x33, 0x0D, 0x0A, 0x20, 0x0A, 0x1A, 0x8B, 0x67, 0x01 }; 8 | 9 | public static void Write(string folderPath, string xp3FilePath) 10 | { 11 | if (!folderPath.EndsWith("\\") && !folderPath.EndsWith("/")) 12 | folderPath += "\\"; 13 | 14 | using (Stream xp3Stream = File.Open(xp3FilePath, FileMode.Create, FileAccess.Write)) 15 | using (BinaryWriter xp3Writer = new BinaryWriter(xp3Stream)) 16 | { 17 | xp3Writer.Write(HeaderMagic); 18 | xp3Writer.Write(0L); // Index offset 19 | 20 | Xp3IndexBuilder index = new Xp3IndexBuilder(); 21 | 22 | foreach (string filePath in Directory.EnumerateFiles(folderPath, "*", SearchOption.AllDirectories)) 23 | { 24 | string extension = Path.GetExtension(filePath); 25 | bool compressed = extension != ".mpg"; 26 | 27 | long offset = xp3Stream.Position; 28 | long originalSize; 29 | long compressedSize; 30 | AppendFile(xp3Stream, filePath, compressed, out originalSize, out compressedSize); 31 | 32 | string relativeFilePath = filePath.Substring(folderPath.Length); 33 | index.Add(relativeFilePath, offset, originalSize, compressedSize, compressed); 34 | } 35 | 36 | long indexOffset = xp3Stream.Length; 37 | xp3Writer.Write(index.Build()); 38 | 39 | xp3Stream.Seek(HeaderMagic.Length, SeekOrigin.Begin); 40 | xp3Writer.Write(indexOffset); 41 | } 42 | } 43 | 44 | private static void AppendFile(Stream xp3Stream, string filePath, bool compressed, out long originalSize, out long compressedSize) 45 | { 46 | using (Stream fileStream = File.OpenRead(filePath)) 47 | { 48 | originalSize = fileStream.Length; 49 | 50 | long startPos = xp3Stream.Position; 51 | if (compressed) 52 | { 53 | using (ZlibStream compressionStream = new ZlibStream(xp3Stream)) 54 | { 55 | fileStream.CopyTo(compressionStream); 56 | } 57 | } 58 | else 59 | { 60 | fileStream.CopyTo(xp3Stream); 61 | } 62 | 63 | compressedSize = xp3Stream.Position - startPos; 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Xp3Pack/Xp3IndexBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace Arc.Ddsi.Xp3Pack 7 | { 8 | internal class Xp3IndexBuilder 9 | { 10 | private static class ChunkType 11 | { 12 | public const uint File = 0x656C6946; 13 | public const uint Info = 0x6F666E69; 14 | public const uint Segment = 0x6D676573; 15 | public const uint Checksum = 0x726C6461; 16 | } 17 | 18 | private readonly Stream _index; 19 | private readonly BinaryWriter _writer; 20 | private readonly Stack _chunkOffsets; 21 | 22 | public Xp3IndexBuilder() 23 | { 24 | _index = new MemoryStream(); 25 | _writer = new BinaryWriter(_index); 26 | _chunkOffsets = new Stack(); 27 | 28 | _writer.Write((byte)0); // Index flag 29 | _writer.Write(0L); // Index size 30 | } 31 | 32 | public void Add(string fileName, long offset, long originalSize, long compressedSize, bool compressed) 33 | { 34 | WriteFileChunk(fileName, offset, originalSize, compressedSize, compressed); 35 | } 36 | 37 | public byte[] Build() 38 | { 39 | UpdateIndexSize(); 40 | 41 | _index.Seek(0, SeekOrigin.Begin); 42 | byte[] result = new byte[_index.Length]; 43 | _index.Read(result, 0, result.Length); 44 | return result; 45 | } 46 | 47 | private void WriteFileChunk(string fileName, long offset, long originalSize, long compressedSize, bool compressed) 48 | { 49 | BeginChunk(ChunkType.File); 50 | WriteInfoChunk(fileName, originalSize, compressedSize); 51 | WriteSegmentChunk(offset, originalSize, compressedSize, compressed); 52 | WriteChecksumChunk(); 53 | EndChunk(); 54 | } 55 | 56 | private void WriteInfoChunk(string fileName, long originalSize, long compressedSize) 57 | { 58 | BeginChunk(ChunkType.Info); 59 | _writer.Write(0); // Flags 60 | _writer.Write(originalSize); // Original size 61 | _writer.Write(compressedSize); // Compressed size 62 | _writer.Write((ushort)fileName.Length); 63 | _writer.Write(Encoding.Unicode.GetBytes(fileName)); 64 | EndChunk(); 65 | } 66 | 67 | private void WriteSegmentChunk(long offset, long originalSize, long compressedSize, bool compressed) 68 | { 69 | BeginChunk(ChunkType.Segment); 70 | _writer.Write(Convert.ToInt32(compressed)); // Flags (1 = compressed) 71 | _writer.Write(offset); // Offset in archive 72 | _writer.Write(originalSize); // Original size 73 | _writer.Write(compressedSize); // Compressed size 74 | EndChunk(); 75 | } 76 | 77 | private void WriteChecksumChunk() 78 | { 79 | BeginChunk(ChunkType.Checksum); 80 | _writer.Write(0); // Checksum 81 | EndChunk(); 82 | } 83 | 84 | private void BeginChunk(uint type) 85 | { 86 | _writer.Write(type); 87 | _writer.Write(0L); 88 | _chunkOffsets.Push(_index.Length); 89 | } 90 | 91 | private void EndChunk() 92 | { 93 | long startOffset = _chunkOffsets.Pop(); 94 | long length = _index.Length - startOffset; 95 | _index.Seek(startOffset - 8, SeekOrigin.Begin); 96 | _writer.Write(length); 97 | _index.Seek(0, SeekOrigin.End); 98 | } 99 | 100 | private void UpdateIndexSize() 101 | { 102 | _index.Seek(1, SeekOrigin.Begin); 103 | _writer.Write(_index.Length - 9); 104 | _index.Seek(0, SeekOrigin.End); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Xp3Pack/Xp3Pack.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {45DCBC40-F6EB-46CF-9D07-C8FFC57728F7} 8 | Exe 9 | Properties 10 | Arc.Ddsi.Xp3Pack 11 | Xp3Pack 12 | v4.8 13 | 512 14 | 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 | Arc.Ddsi.Xp3Pack.Program 35 | 36 | 37 | 38 | ..\packages\SharpZipLib.1.0.0-rc2\lib\net45\ICSharpCode.SharpZipLib.dll 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Xp3Pack/ZlibStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.IO.Compression; 4 | 5 | namespace Arc.Ddsi.Xp3Pack 6 | { 7 | internal class ZlibStream : DeflateStream 8 | { 9 | private readonly Adler32 _adler32 = new Adler32(); 10 | 11 | public ZlibStream(Stream innerStream) 12 | : base(innerStream, CompressionMode.Compress, true) 13 | { 14 | byte[] header = { 0x78, 0x9C }; 15 | BaseStream.Write(header, 0, 2); 16 | } 17 | 18 | public override void Write(byte[] array, int offset, int count) 19 | { 20 | base.Write(array, offset, count); 21 | _adler32.Update(array, offset, count); 22 | } 23 | 24 | protected override void Dispose(bool disposing) 25 | { 26 | Stream baseStream = BaseStream; 27 | base.Dispose(disposing); 28 | 29 | byte[] checksum = BitConverter.GetBytes(_adler32.Checksum); 30 | for (int i = 3; i >= 0; i--) 31 | { 32 | baseStream.WriteByte(checksum[i]); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Xp3Pack/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Xp3Pack/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | --------------------------------------------------------------------------------