├── 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