#include "sd_function_replacements.h" #include "applicationendshook/applicationends_function_replacements.h" #include "globals.h" #include "logger.h" #include #include #include #include #include #include uint64_t NWFPatchTitleList[] = { 0x0005000010102900L, // Demaecan JPN 0x0005000010102A00L, // YNN JPN 0x0005000010102D00L, // Hulu USA 0x0005000010102E00L, // Amazon Instant Video USA 0x0005000010102F00L, // Amazon / LOVEFiLM EUR 0x0005000010104C00L, // Hulu JPN 0x0005000010105700L, // YouTube ALL 0x000500001012D300L, // Wii Street U powered by Google JPN 0x0005000010132000L, // Wii Street U powered by Google USA 0x0005000010132100L, // Wii Street U powered by Google EUR 0x0005000010136100L, // BANDAI CHANNEL JPN 0x0005000010149300L, // Mario vs. Donkey Kong Tipping Stars USA 0x0005000010154500L, // Togabito JPN 0x0005000010154800L, // BBC iPlayer EUR 0x0005000010157D00L, // GAIABREAKER JPN 0x000500001016E200L, // XType Plus EUR 0x000500001016E500L, // BLOK DROP U USA 0x000500001016E600L, // ZaciSa's Last Stand USA 0x0005000010170400L, // XType Plus USA 0x0005000010172A00L, // Crunchyroll USA 0x0005000010172B00L, // Crunchyroll EUR 0x0005000010173000L, // Nintendo Game Seminar 2013 Student Works JPN 0x0005000010173100L, // I've Got to Run! USA 0x0005000010175700L, // BLOK DROP U EUR 0x0005000010175F00L, // Death Match Love Comedy JPN 0x0005000010176400L, // Pixel Paint USA 0x0005000010178E00L, // Mario vs. Donkey Kong Tipping Stars EUR 0x0005000010179200L, // Mario vs. Donkey Kong Tipping Stars JPN 0x0005000010179300L, // Internal Invasion USA 0x0005000010179500L, // Plenty of Fishies USA 0x000500001017A800L, // GAIABREAKER USA 0x000500001017A900L, // SUPER ROBO MOUSE USA 0x000500001017AB00L, // GAIABREAKER EUR 0x000500001017DB00L, // Dolphin Up USA 0x000500001017E200L, // 3Souls EUR 0x000500001017E700L, // 3Souls USA 0x000500001017EB00L, // SDK Paint USA 0x000500001017EC00L, // Guac' a Mole EUR 0x0005000010182E00L, // GetClose USA 0x0005000010183100L, // Snake Den USA 0x0005000010183400L, // U Host USA 0x0005000010185000L, // ブタメダル JPN 0x0005000010185900L, // GolT Stadium EUR 0x000500001018A400L, // POKER DICE SOLITAIRE FUTURE USA 0x000500001018D700L, // SHUT THE BOX USA 0x000500001018EE00L, // Don't Touch Anything Red USA 0x000500001018F900L, // TOSS N GO USA 0x000500001018FE00L, // Sense by Play.me EUR 0x0005000010190400L, // Red Riding Hood USA 0x0005000010193900L, // Flapp & Zegeta USA 0x0005000010193D00L, // Tiny Galaxy USA 0x0005000010193E00L, // Twisted Fusion USA 0x0005000010195F00L, // Arrow Time U USA 0x0005000010198700L, // Watchup USA 0x000500001019A100L, // Shapes of Gray USA 0x000500001019A300L, // SPIKEY WALLS USA 0x000500001019A700L, // BLOK DROP X TWISTED FUSION USA 0x000500001019A800L, // Citadale The Legends Trilogy USA 0x000500001019AD00L, // Elliot Quest USA 0x000500001019B900L, // SUPER ROBO MOUSE EUR 0x000500001019BA00L, // BLOK DROP X TWISTED FUSION EUR 0x00050000101A2400L, // Tested with robots ! EUR 0x00050000101A2E00L, // XType+ JPN 0x00050000101A3E00L, // Defense Dome USA 0x00050000101A4400L, // Toon Tanks USA 0x00050000101A9100L, // Rakuten TV JPN 0x00050000101A9F00L, // Dot Arcade USA 0x00050000101AA300L, // SDK Spriter USA 0x00050000101AAC00L, // Red Riding Hood EUR 0x00050000101ABB00L, // COLOR BOMBS USA 0x00050000101ACB00L, // psyscrolr USA 0x00050000101AEB00L, // SDK Paint EUR 0x00050000101AEF00L, // Midnight USA 0x00050000101AF400L, // PixlCross USA 0x00050000101B4F00L, // Asteroid Quarry USA 0x00050000101B5000L, // Drop It: Block Paradise! USA 0x00050000101B5E00L, // SPLASHY DUCK EUR 0x00050000101B5F00L, // TABLETOP GALLERY EUR 0x00050000101B8400L, // Mortar Melon USA 0x00050000101B9600L, // Super Destronaut USA 0x00050000101B9700L, // SDK Spriter EUR 0x00050000101BA100L, // Best way to motivate kids ManabiGet! JPN 0x00050000101BA700L, // PENTAPUZZLE USA 0x00050000101BB300L, // ZaciSa: Defense of the Crayon Dimension! EUR 0x00050000101BB800L, // Elliot Quest EUR 0x00050000101BC000L, // ドットペイント JPN 0x00050000101BC400L, // Plenty of Fishies EUR 0x00050000101BC500L, // Mortar Melon EUR 0x00050000101BCA00L, // Napster EUR 0x00050000101BCB00L, // Rhapsody USA 0x00050000101BCF00L, // Word Search by POWGI USA 0x00050000101C3900L, // Word Search by POWGI EUR 0x00050000101C3C00L, // PictoParty EUR 0x00050000101C4A00L, // Word Party EUR 0x00050000101C5500L, // Word Party USA 0x00050000101C5900L, // サイコロの森 3-in-1L, // JPN 0x00050000101C6100L, // Mini Mario & Friends amiibo Challenge JPN 0x00050000101C6200L, // Mini Mario & Friends amiibo Challenge USA 0x00050000101C6300L, // Mini Mario & Friends amiibo Challenge EUR 0x00050000101C6900L, // TABLETOP GALLERY USA 0x00050000101C6B00L, // PEG SOLITAIRE USA 0x00050000101C7200L, // Placards USA 0x00050000101C7300L, // The Stonecutter USA 0x00050000101C8E00L, // Drop It: Block Paradise! EUR 0x00050000101CA300L, // Neon Battle USA 0x00050000101CF900L, // Word Puzzles by POWGI EUR 0x00050000101CFA00L, // Word Puzzles by POWGI USA 0x00050000101CFD00L, // Near Earth Objects USA 0x00050000101CFF00L, // Job the Leprechaun USA 0x00050000101D1700L, // Booty Diver USA 0x00050000101D1A00L, // Aenigma Os USA 0x00050000101D2800L, // DON'T CRASH USA 0x00050000101D2900L, // PIXEL SLIME U USA 0x00050000101D2A00L, // Land it Rocket USA 0x00050000101D6200L, // Test Your Mind USA 0x00050000101D6700L, // Star Sky EUR 0x00050000101D7300L, // Molly Maggot USA 0x00050000101D7700L, // Explody Bomb USA 0x00050000101D7800L, // Hold Your Fire USA 0x00050000101D8A00L, // Star Sky USA 0x00050000101D8B00L, // ヒラメキパズル テトグラム JPN 0x00050000101D8C00L, // ブルームーン JPN 0x00050000101D9C00L, // Funky Physics USA 0x00050000101D9F00L, // Job the Leprechaun EUR 0x00050000101DB100L, // PictoParty USA 0x00050000101DD500L, // Elliot Quest JPN 0x00050000101DEE00L, // AVOIDER USA 0x00050000101DFA00L, // Draw 2 Survive USA 0x00050000101DFD00L, // Snowball USA 0x00050000101E2E00L, // Christmas Adventure of Rocket P. USA 0x00050000101E3200L, // Jewel Quest USA 0x00050000101E3300L, // Olympia Rising EUR 0x00050000101E3400L, // Olympia Rising USA 0x00050000101E5500L, // Escape From Flare Industries USA 0x00050000101E7800L, // SKEASY USA 0x00050000101E7900L, // SPLASHY DUCK USA 0x00050000101E7C00L, // TAP TAP ARCADE EUR 0x00050000101E7D00L, // TAP TAP ARCADE USA 0x00050000101E7E00L, // TOUCH SELECTIONS EUR 0x00050000101E7F00L, // TOUCH SELECTIONS USA 0x00050000101E8800L, // COLOR BOMBS EUR 0x00050000101E9700L, // U Host EUR 0x00050000101EA900L, // Armored ACORNs Action Squirrel Squad USA 0x00050000101EC600L, // Defense Dome EUR 0x00050000101EF400L, // Demonic Karma Summoner USA 0x00050000101F2700L, // Star Sky 2 EUR 0x00050000101F3E00L, // Star Sky 2 USA 0x00050000101F5900L, // PENTAPUZZLE EUR 0x00050000101F5F00L, // Hot Rod Racer USA 0x00050000101F6200L, // Adventure Party Cats and Caverns USA 0x00050000101F6600L, // Gravity+ USA 0x00050000101F6A00L, // Jewel Quest EUR 0x00050000101F6B00L, // PIXEL SLIME U EUR 0x00050000101F7400L, // Hot Rod Racer EUR 0x00050000101F7500L, // Midnight 2 USA 0x00050000101F7C00L, // TAP TAP ARCADE 2 USA 0x00050000101F8100L, // Gravity+ EUR 0x00050000101F8400L, // JACKPOT 777 EUR 0x00050000101F8D00L, // TAP TAP ARCADE 2 EUR 0x00050000101F9000L, // Citadale USA 0x00050000101F9200L, // JACKPOT 777 USA 0x00050000101F9400L, // SD2: Go Duck Yourself USA 0x00050000101F9900L, // Aenigma Os EUR 0x00050000101F9A00L, // Citadale EUR 0x00050000101FAB00L, // Collateral Thinking USA 0x00050000101FAC00L, // INVANOID USA 0x00050000101FAD00L, // SHOOTY SPACE USA 0x00050000101FAF00L, // Midnight 2 EUR 0x00050000101FB000L, // SD2: Go Duck Yourself EUR 0x00050000101FB400L, // Shadow Archer USA 0x00050000101FC000L, // BLOC USA 0x00050000101FCE00L, // Word Logic by POWGI USA 0x00050000101FD200L, // SUPER ROBO MOUSE JPN 0x00050000101FD300L, // BLOK DROP U JPN 0x00050000101FE600L, // Amazon Video JPN 0x00050000101FEC00L, // Toon Tanks EUR 0x00050000101FF900L, // Test Your Mind EUR 0x00050000101FFA00L, // Word Logic by POWGI EUR 0x0005000010200200L, // Collateral Thinking EUR 0x0005000010200700L, // Twisted Fusion EUR 0x0005000010200E00L, // PANDA LOVE USA 0x0005000010200F00L, // PANDA LOVE EUR 0x0005000010201500L, // TITANS TOWER USA 0x0005000010201600L, // TITANS TOWER EUR 0x0005000010201800L, // SHOOTY SPACE EUR 0x0005000010202100L, // Overworld Defender Remix USA 0x0005000010202500L, // Alice in Wonderland USA 0x0005000010202600L, // Laser Blaster USA 0x0005000010202700L, // Rorrim USA 0x0005000010202900L, // Amazon Video BETA JPN 0x0005000010202A00L, // Amazon Video USA 0x0005000010202B00L, // Laser Blaster EUR 0x0005000010203600L, // Rorrim EUR 0x0005000010203900L, // Alice in Wonderland EUR 0x0005000010203D00L, // Space Hunted: The Lost Levels USA 0x0005000010207600L, // Space Hunted USA 0x0005000010207700L, // Y.A.S.G USA 0x0005000010207B00L, // Shadow Archer EUR 0x0005000010207C00L, // A.C.E. - Alien Cleanup Elite USA 0x0005000010208E00L, // A.C.E. EUR 0x0005000010209D00L, // Sudoku Party EUR 0x0005000010209E00L, // Sudoku Party USA 0x000500001020A100L, // Star Sky 2 JPN 0x000500001020B800L, // Scribble EUR 0x000500001020BB00L, // Scribble USA 0x000500001020C700L, // BLOC EUR 0x000500001020D100L, // Pic-a-Pix Colour EUR 0x000500001020D700L, // Pic-a-Pix Color USA 0x000500001020F500L, // Absolutely Unstoppable MineRun EUR 0x000500001020FD00L, // Absolutely Unstoppable MineRun USA 0x0005000010210300L, // Citadale The Legends Trilogy EUR 0x0005000010210400L, // Space Hunted EUR 0x0005000010210500L, // Y.A.S.G EUR 0x0005000010210700L, // The Stonecutter EUR 0x0005000010211000L, // Coqui The Game USA 0x0005000010211400L, // Daikon Set USA 0x0005000010211E00L, // A Drawing's Journey EUR 0x0005000010212500L, // Armored ACORNs Action Squirrel Squad EUR 0x0005000010213500L, // Super Ultra Star Shooter EUR 0x0005000010213B00L, // Cubeshift USA 0x0005000010213C00L, // Super Ultra Star Shooter USA 0x0005000010215F00L, // Spellcaster's Assistant EUR 0x0005000010216100L, // Spellcaster's Assistant USA 0x0005000010216D00L, // Shadow Archery USA 0x0005000010216E00L, // Shadow Archery EUR 0x0005000010217C00L, // Sinister Assistant EUR 0x0005000010217F00L, // Sinister Assistant USA 0x0005000010219C00L, // Space Hunted: The Lost Levels EUR 0x000500001021B200L, // Captain U USA 0x000500001021C000L, // Seasonal Assistant USA 0x000500001021C100L, // Seasonal Assistant EUR 0x000500001021C600L, // Captain U EUR 0x000500001021C900L, // Reversi 32 EUR 0x000500021016E601L, // ZaciSa's Last Stand Demo USA 0x000500021017E201L, // 3Souls (Demo) EUR 0x000500021017E701L, // 3Souls (Demo) USA 0x000500021017EB01L, // SDK Paint Demo USA 0x00050002101A2E01L, // XType+ (体験版) JPN 0x00050002101AA301L, // SDK Spriter DEMO USA 0x00050002101AEB01L, // SDK Paint Demo EUR 0x00050002101B9701L, // SDK Spriter DEMO EUR 0x00050002101BB301L, // ZaciSa: Defense of the Crayon Dimension! Demo EUR 0x00050002101BCF01L, // Word Search by POWGI (Demo) USA 0x00050002101C3901L, // Word Search by POWGI (Demo) EUR 0x00050002101C4A01L, // Word Party (Demo) EUR 0x00050002101C5501L, // Word Party (Demo) USA 0x00050002101C5901L, // サイコロの森 3-in-1(体験版) JPN 0x00050002101CF901L, // Word Puzzles by POWGI (Demo) EUR 0x00050002101CFA01L, // Word Puzzles by POWGI (Demo) USA 0x00050002101D8B01L, // ヒラメキパズル テトグラム(体験版) JPN 0x00050002101FCE01L, // Word Logic by POWGI (Demo) USA 0x00050002101FFA01L, // Word Logic by POWGI (Demo) EUR 0x000500021020D101L, // Pic-a-Pix Colour (Demo) EUR 0x000500021020D701L, // Pic-a-Pix Color (Demo) USA }; void NWF_Fix() { auto titleID = OSGetTitleID(); bool found = false; for (auto &tid : NWFPatchTitleList) { if (tid == titleID) { DEBUG_FUNCTION_LINE_WARN("Title using \"LibPlatform::Input::GetInstance()\" detected!"); found = true; break; } } if (!found) { return; } // Some games using the NWF (like Dot Arcade) keep a thread called "PlatformInputAppStateListenerThread" running, // which will cause a DSI exception if the title is not closing fast enough. auto *curThread = OSGetCurrentThread(); __OSLockScheduler(curThread); int state = OSDisableInterrupts(); OSThread *t = *((OSThread **) 0x100567F8); while (t) { if (std::string_view(t->name) == "PlatformInputAppStateListenerThread") { t->priority = 0x80; OSReport("Set priority to %d for thread \"%s\" (%08X) to prevent it from running/crashing\n", t->priority, t->name, t); } t = t->activeLink.next; } OSRestoreInterrupts(state); __OSUnlockScheduler(curThread); } DECL_FUNCTION(void, __PPCExit, uint32_t u1) { NWF_Fix(); CallHook(WUMS_HOOK_APPLICATION_ENDS); CallHook(WUMS_HOOK_FINI_WUT_SOCKETS); CallHook(WUMS_HOOK_FINI_WUT_DEVOPTAB); if (gSDMountRefCount > 0) { FSAInit(); auto client = FSAAddClient(nullptr); if (client) { FSAUnmount(client, "/vol/external01/", FSA_UNMOUNT_FLAG_BIND_MOUNT); } gSDMountRefCount = 0; } if (gModuleData->number_acquired_rpls > 0) { DEBUG_FUNCTION_LINE_VERBOSE("Release RPLs acquired by modules"); for (uint32_t i = 0; i < gModuleData->number_acquired_rpls; i++) { DEBUG_FUNCTION_LINE_VERBOSE("OSDynLoad_Release(0x%08X)", gModuleData->acquired_rpls[i]); OSDynLoad_Release((void *) gModuleData->acquired_rpls[i]); } } real___PPCExit(u1); } DECL_FUNCTION(FSStatus, FSMount, FSClient *client, FSCmdBlock *cmd, FSMountSource *source, char *target, uint32_t bytes, FSErrorFlag errorMask) { if (std::string_view(target) == "/vol/external01") { if (gSDMountRefCount > 0) { gSDMountRefCount++; return FS_STATUS_OK; } auto res = real_FSMount(client, cmd, source, target, bytes, errorMask); if (res == FS_STATUS_OK) { gSDMountRefCount++; } return res; } return real_FSMount(client, cmd, source, target, bytes, errorMask); } DECL_FUNCTION(FSStatus, FSUnmount, FSClient *client, FSCmdBlock *cmd, const char *target, FSErrorFlag errorMask) { if (std::string_view(target) == "/vol/external01") { gSDMountRefCount--; if (gSDMountRefCount <= 0) { gSDMountRefCount = 0; return real_FSUnmount(client, cmd, target, errorMask); } return FS_STATUS_OK; } return real_FSUnmount(client, cmd, target, errorMask); } DECL_FUNCTION(FSError, FSAMount, FSAClientHandle client, const char *source, const char *target, FSAMountFlags flags, void *arg_buf, uint32_t arg_len) { if (std::string_view(target) == "/vol/external01") { if (gSDMountRefCount > 0) { gSDMountRefCount++; return FS_ERROR_OK; } auto res = real_FSAMount(client, source, target, flags, arg_buf, arg_len); if (res == FS_ERROR_OK || res == FS_ERROR_ALREADY_EXISTS) { gSDMountRefCount++; } return res; } return real_FSAMount(client, source, target, flags, arg_buf, arg_len); } DECL_FUNCTION(FSError, FSAUnmount, FSAClientHandle client, const char *mountedTarget, FSAUnmountFlags flags) { if (std::string_view(mountedTarget) == "/vol/external01") { gSDMountRefCount--; if (gSDMountRefCount <= 0) { auto res = real_FSAUnmount(client, mountedTarget, flags); gSDMountRefCount = 0; return res; } return FS_ERROR_OK; } return real_FSAUnmount(client, mountedTarget, flags); } function_replacement_data_t sdrefcount_function_replacements[] = { REPLACE_FUNCTION(__PPCExit, LIBRARY_COREINIT, __PPCExit), REPLACE_FUNCTION(FSMount, LIBRARY_COREINIT, FSMount), REPLACE_FUNCTION(FSUnmount, LIBRARY_COREINIT, FSUnmount), REPLACE_FUNCTION(FSAMount, LIBRARY_COREINIT, FSAMount), REPLACE_FUNCTION(FSAUnmount, LIBRARY_COREINIT, FSAUnmount)}; uint32_t sdrefcount_function_replacements_size = sizeof(sdrefcount_function_replacements) / sizeof(function_replacement_data_t);