Skip to content

Commit ae6e2a7

Browse files
committed
Added ability for song loader to create + load a customSongsUnlocked_P file to automatically unlock custom songs.
Switched function hooking to always scan for byte patterns.
1 parent 5887422 commit ae6e2a7

File tree

10 files changed

+7113
-38
lines changed

10 files changed

+7113
-38
lines changed

FuserModDoorstop/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
88

99
add_library(FuserModDoorstop SHARED
1010
${CMAKE_CURRENT_SOURCE_DIR}/src/dllmain.cpp
11+
${CMAKE_CURRENT_SOURCE_DIR}/src/sha1.cpp
12+
${CMAKE_CURRENT_SOURCE_DIR}/src/uasset.cpp
1113
)
1214

1315
include(GenerateExportHeader)

FuserModDoorstop/src/DT_UnlocksSongsData.h

Lines changed: 4584 additions & 0 deletions
Large diffs are not rendered by default.

FuserModDoorstop/src/core_types.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#pragma once
2+
#include <cstdint>
3+
#include <vector>
4+
#include <string>
5+
#include <ostream>
6+
#include <istream>
7+
#include <fstream>
8+
#include <variant>
9+
10+
using u8 = uint8_t;
11+
using i8 = int8_t;
12+
using u16 = uint16_t;
13+
using i16 = int16_t;
14+
using u32 = uint32_t;
15+
using i32 = int32_t;
16+
using u64 = uint64_t;
17+
using i64 = int64_t;
18+
19+

FuserModDoorstop/src/crc.h

Lines changed: 208 additions & 0 deletions
Large diffs are not rendered by default.

FuserModDoorstop/src/dllmain.cpp

Lines changed: 222 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
#include "DT_UnlocksSongsData.h"
2+
#include "uasset.h"
3+
4+
#include <unordered_set>
5+
#include <filesystem>
6+
namespace fs = std::filesystem;
7+
8+
#define NOMINMAX
19
#define WIN32_LEAN_AND_MEAN
210
#include <Windows.h>
311
#include <Psapi.h>
@@ -170,7 +178,10 @@ static void*(*GetPakSignatureFile)(void *ths, TCHAR* InFilename);
170178
static uint64_t(*HashAndGetSize)(void*p1, void*p2, void*p3);
171179
static void(*DoSignatureCheck)(void *ths, byte bWasCanceled, void *Request, int Index);
172180
static bool(*CheckSignature)(void *ths, byte *ChunkInfo);
173-
181+
static void(*MountPakFile)(void *ths, const TCHAR* InPakFilename, u32 PakOrder, const TCHAR* InPath /*= NULL*/, bool bLoadIndex /*= true*/);
182+
static void*(*FindPlatformFile)(void *ths, TCHAR *name);
183+
static void(*DoSignatureCheck_ContinueFn)(void *ths, byte bWasCanceled, void *Request, int Index);
184+
static void(*The_Second_Copy_UnencryptFn)(byte*, uint64_t, uint32_t);
174185

175186
static byte* Base;
176187
size_t ModuleSize = 0;
@@ -203,7 +214,6 @@ uint64_t Second_Inner_Hook(byte* param1, byte* param2, byte* param3) {
203214
return ret;
204215
}
205216

206-
size_t The_Second_Copy_UnencryptFnRVA;
207217
uint64_t The_Second_Copy_Hook(byte* param) {
208218
auto ret = The_Second_Copy(param);
209219

@@ -213,9 +223,7 @@ uint64_t The_Second_Copy_Hook(byte* param) {
213223
auto &&data = DOING_UNENCRYPTION[std::this_thread::get_id()];
214224
data.doing_unencryption = true;
215225

216-
217-
auto fn = (void(*)(byte*, uint64_t, uint32_t))(Base + The_Second_Copy_UnencryptFnRVA);
218-
fn(*(byte **)(param + 0x40), *offset<uint32_t>(*(byte**)offset(param,0x30), 0x4e8) , 0);
226+
The_Second_Copy_UnencryptFn(*(byte **)(param + 0x40), *offset<uint32_t>(*(byte**)offset(param,0x30), 0x4e8) , 0);
219227

220228
*reinterpret_cast<bool*>(param + 0x1ba) = false;
221229
return 1;
@@ -293,11 +301,23 @@ void GetMasterHash_Hook(void *p1, void *p2, void* outHash) {
293301
}
294302
}
295303

296-
size_t DoSignatureCheck_ContinueFnRVA;
297304
void DoSignatureCheck_Hook(void *ths, byte bWasCanceled, void *Request, int Index) {
298305
//Just skip the whole signature check
299-
auto continueFn = (void(*)(void *ths, byte bWasCanceled, void *Request, int Index))(Base + DoSignatureCheck_ContinueFnRVA);
300-
continueFn(ths, bWasCanceled, Request, Index);
306+
DoSignatureCheck_ContinueFn(ths, bWasCanceled, Request, Index);
307+
}
308+
309+
std::wstring needFileMounted;
310+
311+
void* FindPlatformFile_Hook(void *ths, TCHAR *name) {
312+
auto ret = FindPlatformFile(ths, name);
313+
314+
if (!needFileMounted.empty()) {
315+
auto pakfile = FindPlatformFile(ths, L"PakFile");
316+
MountPakFile(pakfile, needFileMounted.c_str(), 3, nullptr, true);
317+
needFileMounted.clear();
318+
}
319+
320+
return ret;
301321
}
302322

303323
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -313,16 +333,22 @@ std::vector<byte> HexToBytes(const std::string& hex) {
313333
return bytes;
314334
}
315335

336+
#if 0
337+
#define DBG_PRINT(...) printf(__VA_ARGS__)
338+
#else
339+
#define DBG_PRINT(...)
340+
#endif
341+
316342
std::vector<size_t> found_rva;
317343

318344
byte* find_function(const char *name, const char *byteSequence) {
319-
printf("Searching for %s.\n", name);
345+
DBG_PRINT("Searching for %s.\n", name);
320346
auto data = HexToBytes(byteSequence);
321347

322-
printf(" Byte Sequence: ");
348+
DBG_PRINT(" Byte Sequence: ");
323349
for (int i = 0; i < data.size(); i++)
324-
printf("%02x ", reinterpret_cast<byte*>(data.data())[i]);
325-
printf("\n");
350+
DBG_PRINT("%02x ", reinterpret_cast<byte*>(data.data())[i]);
351+
DBG_PRINT("\n");
326352

327353
for (size_t i = 0; i < ModuleSize; ++i) {
328354
bool done = true;
@@ -334,13 +360,13 @@ byte* find_function(const char *name, const char *byteSequence) {
334360
}
335361

336362
if (done) {
337-
printf(" Found! RVA: 0x%x\n", (unsigned int)i);
363+
DBG_PRINT(" Found! RVA: 0x%x\n", (unsigned int)i);
338364
found_rva.emplace_back(i);
339365
return Base + i;
340366
}
341367
}
342368

343-
printf(" No function found with that pattern!\n");
369+
DBG_PRINT(" No function found with that pattern!\n");
344370
found_rva.emplace_back(0);
345371
return nullptr;
346372
}
@@ -363,7 +389,7 @@ void FUSER_HOOK() {
363389
}
364390

365391
// Change to 0 to re-lookup all of these functions.
366-
#if 1
392+
#if 0
367393
The_Second_Copy = (decltype(The_Second_Copy))(Base + 0x2253aa0);
368394
Decrypt_Fn = (decltype(Decrypt_Fn))(Base + 0x2253e70);
369395
Second_Inner = (decltype(Second_Inner))(Base + 0x2252db0);
@@ -374,6 +400,8 @@ void FUSER_HOOK() {
374400
HashAndGetSize = (decltype(HashAndGetSize))(Base + 0x178ca80);
375401
DoSignatureCheck = (decltype(DoSignatureCheck))(Base + 0x179a540);
376402

403+
MountPakFile = (decltype(MountPakFile))(Base + 0x017a2d00);
404+
FindPlatformFile = (decltype(FindPlatformFile))(Base + 0x008ee7a0);
377405

378406
DoSignatureCheck_ContinueFnRVA = 0x17a39d0;
379407
The_Second_Copy_UnencryptFnRVA = 0x17ca410;
@@ -385,28 +413,31 @@ void FUSER_HOOK() {
385413

386414
printf("Functions are unknown. Searching %zd bytes in process...\n\n", ModuleSize);
387415

388-
find_function("The_Second_Copy", "405556488d6c24b14881ecd8000000488bf1488b89c00100004885c9740de85dc61300ff86c8010000eb0a");
389-
find_function("Decrypt_Fn", "405641564157b820800000e880235c00482be0488b4130498bf04c8bfa4c8bf1837824ff0f84040100004889bc2450800000");
390-
find_function("Second_Inner", "48895c2408574883ec204c894140488bd966c741480000498bf848895130c6421801c6413800488b49304883c128");
391-
find_function("FPakFile_Ctor", "48895c2410555657415641574883ec304533ff410fb6e94c8939498bf04c8979084c8bf2488bd94d85c0743f66453938");
392-
find_function("OnCrashFunc", "48895c24185556574154415541564157488dac2450faffff4881ecb0060000488b05????????4833c4488985a0050000");
393-
find_function("GetMasterHash", "4c8bdc534881ec00010000488b05????????4833c448898424f0000000498d4398498bd8498943d84c8bc233c0c744242001234567");
394-
find_function("GetPakSignatureFile", "405556574154488dac2478ffffff4881ec88010000488b05????????4833c4488945784c8be1488bf2488d0d");
395-
find_function("HashAndGetSize", "48895c24084889742410574883ec30498bd8488bfa488bf1e8530000004885c0742d0f10064c8b10488d5424204c8bcb4c8bc7488bc80f29442420");
396-
find_function("DoSignatureCheck", "4055535657415541564157488d6c24f04881ec10010000488b05????????4833c44889450033c04963f1488d590848894c2438");
397-
398-
find_function("DoSignatureCheck_ContinueFnRVA", "48895c240848896c2410488974241857415641574883ec20488bf14963f94883c108498be8440fb6f2");
399-
find_function("The_Second_Copy_UnencryptFnRVA", "488b0148ff6068cccccccccccccccccc");
400-
401-
printf("RVA list:\n");
402-
for (auto &&rva : found_rva) {
403-
printf("0x%x\n", (unsigned int)rva);
404-
}
405-
found_rva.clear();
406-
407-
printf("Searches done! Copy the addresses and apply them to the cpp file!\n");
408-
system("pause");
409-
_Exit(0);
416+
The_Second_Copy = (decltype(The_Second_Copy)) find_function("The_Second_Copy", "405556488d6c24b14881ecd8000000488bf1488b89c00100004885c9740de85dc61300ff86c8010000eb0a");
417+
Decrypt_Fn = (decltype(Decrypt_Fn)) find_function("Decrypt_Fn", "405641564157b820800000e880235c00482be0488b4130498bf04c8bfa4c8bf1837824ff0f84040100004889bc2450800000");
418+
Second_Inner = (decltype(Second_Inner)) find_function("Second_Inner", "48895c2408574883ec204c894140488bd966c741480000498bf848895130c6421801c6413800488b49304883c128");
419+
FPakFile_Ctor = (decltype(FPakFile_Ctor)) find_function("FPakFile_Ctor", "48895c2410555657415641574883ec304533ff410fb6e94c8939498bf04c8979084c8bf2488bd94d85c0743f66453938");
420+
OnCrashFunc = (decltype(OnCrashFunc)) find_function("OnCrashFunc", "48895c24185556574154415541564157488dac2450faffff4881ecb0060000488b05????????4833c4488985a0050000");
421+
GetMasterHash = (decltype(GetMasterHash)) find_function("GetMasterHash", "4c8bdc534881ec00010000488b05????????4833c448898424f0000000498d4398498bd8498943d84c8bc233c0c744242001234567");
422+
GetPakSignatureFile = (decltype(GetPakSignatureFile)) find_function("GetPakSignatureFile", "405556574154488dac2478ffffff4881ec88010000488b05????????4833c4488945784c8be1488bf2488d0d");
423+
HashAndGetSize = (decltype(HashAndGetSize)) find_function("HashAndGetSize", "48895c24084889742410574883ec30498bd8488bfa488bf1e8530000004885c0742d0f10064c8b10488d5424204c8bcb4c8bc7488bc80f29442420");
424+
DoSignatureCheck = (decltype(DoSignatureCheck)) find_function("DoSignatureCheck", "4055535657415541564157488d6c24f04881ec10010000488b05????????4833c44889450033c04963f1488d590848894c2438");
425+
426+
MountPakFile = (decltype(MountPakFile)) find_function("MountPakFile", "488bc4448940184889480855534157488d68a94881ecd000000048897010");
427+
FindPlatformFile = (decltype(FindPlatformFile)) find_function("FindPlatformFile", "48895c2408574883ec20488b19488bfa4885db7429488b03488bcbff5068488bc8488bd7");
428+
429+
DoSignatureCheck_ContinueFn = (decltype(DoSignatureCheck_ContinueFn)) find_function("DoSignatureCheck_Continue", "48895c240848896c2410488974241857415641574883ec20488bf14963f94883c108498be8440fb6f2");
430+
The_Second_Copy_UnencryptFn = (decltype(The_Second_Copy_UnencryptFn)) find_function("The_Second_Copy_UnencryptFn", "488b0148ff6068cccccccccccccccccc");
431+
432+
//printf("RVA list:\n");
433+
//for (auto &&rva : found_rva) {
434+
// printf("0x%x\n", (unsigned int)rva);
435+
//}
436+
//found_rva.clear();
437+
//
438+
//printf("Searches done! Copy the addresses and apply them to the cpp file!\n");
439+
//system("pause");
440+
//_Exit(0);
410441
#endif
411442

412443
setup_hook((void**)&The_Second_Copy, The_Second_Copy_Hook);
@@ -417,6 +448,159 @@ void FUSER_HOOK() {
417448
setup_hook((void**)&GetPakSignatureFile, GetPakSignatureFile_Hook);
418449
setup_hook((void**)&HashAndGetSize, HashAndGetSize_Hook);
419450
setup_hook((void**)&DoSignatureCheck, DoSignatureCheck_Hook);
451+
setup_hook((void**)&FindPlatformFile, FindPlatformFile_Hook);
452+
}
453+
454+
void CreateUnlockPak() {
455+
//Now, create a pak file for all songs that need unlocking
456+
printf("\n\nUpdating unlock file for custom songs...");
457+
std::error_code ec;
458+
459+
auto paksPath = fs::current_path().parent_path().parent_path() / "Content" / "Paks";
460+
auto custom_songs = paksPath / "custom_songs";
461+
auto customSongUnlockPak = paksPath / "customSongsUnlocked_P.pak";
462+
463+
printf("Paks Path: %ls\n", paksPath.c_str());
464+
printf("Custom Songs Path: %ls\n", custom_songs.c_str());
465+
printf("Unlock Pak Path: %ls\n", customSongUnlockPak.c_str());
466+
467+
auto unlockWriteTime = fs::last_write_time(customSongUnlockPak, ec);
468+
if (fs::exists(custom_songs)) {
469+
std::unordered_set<std::string> songNames;
470+
bool needRewrite = false;
471+
472+
for (auto& dirEntry : fs::recursive_directory_iterator(custom_songs)) {
473+
if (dirEntry.is_regular_file()) {
474+
auto pakName = dirEntry.path().stem().string();
475+
if (pakName.find("_P") != std::string::npos) {
476+
songNames.emplace(pakName.substr(0, pakName.size() - 2));
477+
478+
if (fs::last_write_time(dirEntry, ec) > unlockWriteTime) {
479+
needRewrite = true;
480+
}
481+
}
482+
}
483+
}
484+
485+
if (needRewrite) {
486+
printf("Found new custom songs! Creating customSongsUnlocked_P.pak to unlock them...\n");
487+
488+
DataBuffer pakBuffer;
489+
pakBuffer.loading = true;
490+
pakBuffer.buffer = DT_UnlocksSongsPak;
491+
pakBuffer.size = sizeof(DT_UnlocksSongsPak);
492+
493+
PakFile DT_UnlocksSongs;
494+
DT_UnlocksSongs.serialize(pakBuffer);
495+
496+
size_t expFileIdx = 0;
497+
for (auto &&e : DT_UnlocksSongs.entries) {
498+
if (e.name == "DT_UnlocksSongs.uexp") {
499+
break;
500+
}
501+
++expFileIdx;
502+
}
503+
504+
//Add songs to DT_UnlocksSongs.uexp
505+
auto &&data = std::get<PakFile::PakEntry::PakAssetData>(DT_UnlocksSongs.entries[expFileIdx].data);
506+
if (auto cat = std::get_if<DataTableCategory>(&data.data.catagoryValues[0].value)) {
507+
508+
//Find and clone the row for "bornthisway"
509+
std::vector<u8> cloneData;
510+
{
511+
AssetCtx ctx;
512+
ctx.header = data.header;
513+
ctx.parseHeader = false;
514+
ctx.length = 0;
515+
516+
DataBuffer entryCloneBuffer;
517+
entryCloneBuffer.ctx_ = &ctx;
518+
entryCloneBuffer.loading = false;
519+
entryCloneBuffer.setupVector(cloneData);
520+
521+
size_t bornthiswayEntryId = 0;
522+
for (auto &&e : cat->entries) {
523+
if (e.rowName.getString(data.header) == "bornthisway") {
524+
break;
525+
}
526+
bornthiswayEntryId++;
527+
}
528+
529+
cat->entries[bornthiswayEntryId].serialize(entryCloneBuffer);
530+
}
531+
532+
//For each custom song, use the cloned row to add an entry that unlocks the song at no cost
533+
for (auto &&songName : songNames) {
534+
DataTableCategory::Entry e;
535+
e.value.type.ref = cat->dataType.ref;
536+
{
537+
AssetCtx ctx;
538+
ctx.header = data.header;
539+
ctx.parseHeader = false;
540+
ctx.length = 0;
541+
542+
DataBuffer entryCloneBuffer;
543+
entryCloneBuffer.ctx_ = &ctx;
544+
entryCloneBuffer.setupVector(cloneData);
545+
546+
e.serialize(entryCloneBuffer);
547+
}
548+
549+
printf("Creating entry for %s\n", songName.c_str());
550+
e.rowName = data.header->findOrCreateName(songName);
551+
std::get<NameProperty>(std::get<IPropertyDataList*>(e.value.values[0]->v)->get(data.header->findName("unlockName"))->value).name = e.rowName;
552+
std::get<PrimitiveProperty<i32>>(std::get<IPropertyDataList*>(e.value.values[0]->v)->get(data.header->findName("audioCreditsCost"))->value).data = 0;
553+
554+
cat->entries.emplace_back(std::move(e));
555+
}
556+
}
557+
558+
559+
//Write out pak file
560+
std::vector<u8> outData;
561+
DataBuffer outBuf;
562+
outBuf.setupVector(outData);
563+
outBuf.loading = false;
564+
DT_UnlocksSongs.serialize(outBuf);
565+
outBuf.finalize();
566+
567+
std::ofstream outPak(customSongUnlockPak, std::ios_base::binary);
568+
outPak.write((char*)outBuf.buffer, outBuf.size);
569+
outPak.close();
570+
571+
//Write out sig file
572+
auto customSongUnlockSig = paksPath / "customSongsUnlocked_P.sig";
573+
PakSigFile sigFile;
574+
sigFile.encrypted_total_hash.resize(512);
575+
576+
const u32 chunkSize = 64 * 1024;
577+
for (size_t start = 0; start < outBuf.size; start += chunkSize) {
578+
size_t end = start + chunkSize;
579+
if (end > outBuf.size) {
580+
end = outBuf.size;
581+
}
582+
583+
sigFile.chunks.emplace_back(CRC::MemCrc32(outBuf.buffer + start, end - start));
584+
}
585+
586+
std::vector<u8> sigOutData;
587+
DataBuffer sigOutBuf;
588+
sigOutBuf.setupVector(sigOutData);
589+
sigOutBuf.loading = false;
590+
sigFile.serialize(sigOutBuf);
591+
sigOutBuf.finalize();
592+
593+
std::ofstream outSig(customSongUnlockSig, std::ios_base::binary);
594+
outSig.write((char*)sigOutBuf.buffer, sigOutBuf.size);
595+
outSig.close();
596+
597+
needFileMounted = std::wstring(L"../../../Fuser/Content/Paks/customSongsUnlocked_P.pak");
598+
}
599+
else {
600+
printf("Unlock pak file is up to date!\n");
601+
}
602+
}
603+
printf("\n\n");
420604
}
421605

422606
BOOL WINAPI DllMain(HMODULE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
@@ -451,7 +635,7 @@ BOOL WINAPI DllMain(HMODULE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
451635
freopen_s(&fp, "CONOUT$", "w", stderr);
452636

453637
#endif
454-
638+
CreateUnlockPak();
455639
FUSER_HOOK();
456640
}
457641

0 commit comments

Comments
 (0)