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);
170178static uint64_t (*HashAndGetSize)(void *p1, void *p2, void *p3);
171179static void (*DoSignatureCheck)(void *ths, byte bWasCanceled, void *Request, int Index);
172180static 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
175186static byte* Base;
176187size_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;
207217uint64_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;
297304void 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+
316342std::vector<size_t > found_rva;
317343
318344byte* 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\n Updating 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
422606BOOL 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