void CBoot::Load_FST(bool _bIsWii) { if (!DVDInterface::VolumeIsValid()) return; const DiscIO::IVolume& volume = DVDInterface::GetVolume(); // copy first 32 bytes of disc to start of Mem 1 DVDRead(/*offset*/ 0, /*address*/ 0, /*length*/ 0x20, false); // copy of game id Memory::Write_U32(Memory::Read_U32(0x0000), 0x3180); u32 shift = 0; if (_bIsWii) shift = 2; u32 fst_offset = 0; u32 fst_size = 0; u32 max_fst_size = 0; volume.ReadSwapped(0x0424, &fst_offset, _bIsWii); volume.ReadSwapped(0x0428, &fst_size, _bIsWii); volume.ReadSwapped(0x042c, &max_fst_size, _bIsWii); u32 arena_high = ROUND_DOWN(0x817FFFFF - (max_fst_size << shift), 0x20); Memory::Write_U32(arena_high, 0x00000034); // load FST DVDRead(fst_offset << shift, arena_high, fst_size << shift, _bIsWii); Memory::Write_U32(arena_high, 0x00000038); Memory::Write_U32(max_fst_size << shift, 0x0000003c); }
void ExecuteCommand(UDICR& _DICR) { // _dbg_assert_(DVDINTERFACE, _DICR.RW == 0); // only DVD to Memory int GCAM = ((SConfig::GetInstance().m_SIDevice[0] == SIDEVICE_AM_BASEBOARD) && (SConfig::GetInstance().m_EXIDevice[2] == EXIDEVICE_AM_BASEBOARD)) ? 1 : 0; if (GCAM) { ERROR_LOG(DVDINTERFACE, "DVD: %08x, %08x, %08x, DMA=addr:%08x,len:%08x,ctrl:%08x", m_DICMDBUF[0].Hex, m_DICMDBUF[1].Hex, m_DICMDBUF[2].Hex, m_DIMAR.Hex, m_DILENGTH.Hex, m_DICR.Hex); // decrypt command. But we have a zero key, that simplifies things a lot. // If you get crazy dvd command errors, make sure 0x80000000 - 0x8000000c is zero'd m_DICMDBUF[0].Hex <<= 24; } switch (m_DICMDBUF[0].CMDBYTE0) { case DVDLowInquiry: if (GCAM) { // 0x29484100... // was 21 i'm not entirely sure about this, but it works well. m_DIIMMBUF.Hex = 0x21000000; } else { // small safety check, dunno if it's needed if ((m_DICMDBUF[1].Hex == 0) && (m_DILENGTH.Length == 0x20)) { u8* driveInfo = Memory::GetPointer(m_DIMAR.Address); // gives the correct output in GCOS - 06 2001/08 (61) // there may be other stuff missing ? driveInfo[4] = 0x20; driveInfo[5] = 0x01; driveInfo[6] = 0x06; driveInfo[7] = 0x08; driveInfo[8] = 0x61; // Just for fun INFO_LOG(DVDINTERFACE, "Drive Info: %02x %02x%02x/%02x (%02x)", driveInfo[6], driveInfo[4], driveInfo[5], driveInfo[7], driveInfo[8]); } } break; // "Set Extension"...not sure what it does case 0x55: INFO_LOG(DVDINTERFACE, "SetExtension"); break; // DMA Read from Disc case 0xA8: if (g_bDiscInside) { switch (m_DICMDBUF[0].CMDBYTE3) { case 0x00: // Read Sector { u32 iDVDOffset = m_DICMDBUF[1].Hex << 2; DEBUG_LOG(DVDINTERFACE, "Read: DVDOffset=%08x, DMABuffer=%08x, SrcLength=%08x, DMALength=%08x", iDVDOffset, m_DIMAR.Address, m_DICMDBUF[2].Hex, m_DILENGTH.Length); _dbg_assert_(DVDINTERFACE, m_DICMDBUF[2].Hex == m_DILENGTH.Length); if (GCAM) { if (iDVDOffset & 0x80000000) // read request to hardware buffer { u32 len = m_DILENGTH.Length / 4; switch (iDVDOffset) { case 0x80000000: ERROR_LOG(DVDINTERFACE, "GC-AM: READ MEDIA BOARD STATUS (80000000)"); for (u32 i = 0; i < len; i++) Memory::Write_U32(0, m_DIMAR.Address + i * 4); break; case 0x80000040: ERROR_LOG(DVDINTERFACE, "GC-AM: READ MEDIA BOARD STATUS (2) (80000040)"); for (u32 i = 0; i < len; i++) Memory::Write_U32(~0, m_DIMAR.Address + i * 4); Memory::Write_U32(0x00000020, m_DIMAR.Address); // DIMM SIZE, LE Memory::Write_U32(0x4743414D, m_DIMAR.Address + 4); // GCAM signature break; case 0x80000120: ERROR_LOG(DVDINTERFACE, "GC-AM: READ FIRMWARE STATUS (80000120)"); for (u32 i = 0; i < len; i++) Memory::Write_U32(0x01010101, m_DIMAR.Address + i * 4); break; case 0x80000140: ERROR_LOG(DVDINTERFACE, "GC-AM: READ FIRMWARE STATUS (80000140)"); for (u32 i = 0; i < len; i++) Memory::Write_U32(0x01010101, m_DIMAR.Address + i * 4); break; case 0x84000020: ERROR_LOG(DVDINTERFACE, "GC-AM: READ MEDIA BOARD STATUS (1) (84000020)"); for (u32 i = 0; i < len; i++) Memory::Write_U32(0x00000000, m_DIMAR.Address + i * 4); break; default: ERROR_LOG(DVDINTERFACE, "GC-AM: UNKNOWN MEDIA BOARD LOCATION %x", iDVDOffset); break; } break; } else if ((iDVDOffset == 0x1f900000) || (iDVDOffset == 0x1f900020)) { ERROR_LOG(DVDINTERFACE, "GC-AM: READ MEDIA BOARD COMM AREA (1f900020)"); memcpy(Memory::GetPointer(m_DIMAR.Address), media_buffer + iDVDOffset - 0x1f900000, m_DILENGTH.Length); for (u32 i = 0; i < m_DILENGTH.Length; i += 4) ERROR_LOG(DVDINTERFACE, "GC-AM: %08x", Memory::Read_U32(m_DIMAR.Address + i)); break; } } // Here is the actual Disk Reading if (!DVDRead(iDVDOffset, m_DIMAR.Address, m_DILENGTH.Length)) { PanicAlertT("Cant read from DVD_Plugin - DVD-Interface: Fatal Error"); } } break; case 0x40: // Read DiscID _dbg_assert_(DVDINTERFACE, m_DICMDBUF[1].Hex == 0); _dbg_assert_(DVDINTERFACE, m_DICMDBUF[2].Hex == m_DILENGTH.Length); _dbg_assert_(DVDINTERFACE, m_DILENGTH.Length == 0x20); if (!DVDRead(m_DICMDBUF[1].Hex, m_DIMAR.Address, m_DILENGTH.Length)) PanicAlertT("Cant read from DVD_Plugin - DVD-Interface: Fatal Error"); WARN_LOG(DVDINTERFACE, "Read DiscID %08x", Memory::Read_U32(m_DIMAR.Address)); break; default: _dbg_assert_msg_(DVDINTERFACE, 0, "Unknown Read Subcommand"); break; } } else { // there is no disc to read _DICR.TSTART = 0; m_DILENGTH.Length = 0; g_ErrorCode = ERROR_NO_DISK | ERROR_COVER_H; GenerateDIInterrupt(INT_DEINT); return; } break; // GC-AM case 0xAA: if (GCAM) { ERROR_LOG(DVDINTERFACE, "GC-AM: 0xAA, DMABuffer=%08x, DMALength=%08x", m_DIMAR.Address, m_DILENGTH.Length); u32 iDVDOffset = m_DICMDBUF[1].Hex << 2; unsigned int len = m_DILENGTH.Length; int offset = iDVDOffset - 0x1F900000; /* if (iDVDOffset == 0x84800000) { ERROR_LOG(DVDINTERFACE, "firmware upload"); } else*/ if ((offset < 0) || ((offset + len) > 0x40) || len > 0x40) { u32 addr = m_DIMAR.Address; if (iDVDOffset == 0x84800000) { ERROR_LOG(DVDINTERFACE, "FIRMWARE UPLOAD"); } else { ERROR_LOG(DVDINTERFACE, "ILLEGAL MEDIA WRITE"); } while (len >= 4) { ERROR_LOG(DVDINTERFACE, "GC-AM Media Board WRITE (0xAA): %08x: %08x", iDVDOffset, Memory::Read_U32(addr)); addr += 4; len -= 4; iDVDOffset += 4; } } else { u32 addr = m_DIMAR.Address; memcpy(media_buffer + offset, Memory::GetPointer(addr), len); while (len >= 4) { ERROR_LOG(DVDINTERFACE, "GC-AM Media Board WRITE (0xAA): %08x: %08x", iDVDOffset, Memory::Read_U32(addr)); addr += 4; len -= 4; iDVDOffset += 4; } } } break; // Seek (immediate) case DVDLowSeek: if (!GCAM) { // We don't care :) DEBUG_LOG(DVDINTERFACE, "Seek: offset=%08x (ignoring)", m_DICMDBUF[1].Hex << 2); } else { memset(media_buffer, 0, 0x20); media_buffer[0] = media_buffer[0x20]; // ID media_buffer[2] = media_buffer[0x22]; media_buffer[3] = media_buffer[0x23] | 0x80; int cmd = (media_buffer[0x23]<<8)|media_buffer[0x22]; ERROR_LOG(DVDINTERFACE, "GC-AM: execute buffer, cmd=%04x", cmd); switch (cmd) { case 0x00: media_buffer[4] = 1; break; case 0x1: media_buffer[7] = 0x20; // DIMM Size break; case 0x100: { // urgh static int percentage = 0; static int status = 0; percentage++; if (percentage > 100) { status++; percentage = 0; } media_buffer[4] = status; /* status: 0 - "Initializing media board. Please wait.." 1 - "Checking network. Please wait..." 2 - "Found a system disc. Insert a game disc" 3 - "Testing a game program. %d%%" 4 - "Loading a game program. %d%%" 5 - go 6 - error xx */ media_buffer[8] = percentage; media_buffer[4] = 0x05; media_buffer[8] = 0x64; break; } case 0x101: media_buffer[4] = 3; // version media_buffer[5] = 3; media_buffer[6] = 1; // xxx media_buffer[8] = 1; media_buffer[16] = 0xFF; media_buffer[17] = 0xFF; media_buffer[18] = 0xFF; media_buffer[19] = 0xFF; break; case 0x102: // get error code media_buffer[4] = 1; // 0: download incomplete (31), 1: corrupted, other error 1 media_buffer[5] = 0; break; case 0x103: memcpy(media_buffer + 4, "A89E27A50364511", 15); // serial break; #if 0 case 0x301: // unknown memcpy(media_buffer + 4, media_buffer + 0x24, 0x1c); break; case 0x302: break; #endif default: ERROR_LOG(DVDINTERFACE, "GC-AM: execute buffer (unknown)"); break; } memset(media_buffer + 0x20, 0, 0x20); m_DIIMMBUF.Hex = 0x66556677; // just a random value that works. } break; case DVDLowOffset: DEBUG_LOG(DVDINTERFACE, "DVDLowOffset: ignoring..."); break; // Request Error Code case DVDLowRequestError: ERROR_LOG(DVDINTERFACE, "Requesting error... (0x%08x)", g_ErrorCode); m_DIIMMBUF.Hex = g_ErrorCode; break; // Audio Stream (Immediate) // m_DICMDBUF[0].CMDBYTE1 = subcommand // m_DICMDBUF[1].Hex << 2 = offset on disc // m_DICMDBUF[2].Hex = Length of the stream case 0xE1: { u32 pos = m_DICMDBUF[1].Hex << 2; u32 length = m_DICMDBUF[2].Hex; // Start playing if (!g_bStream && m_DICMDBUF[0].CMDBYTE1 == 0 && pos != 0 && length != 0) { AudioPos = pos; CurrentStart = pos; CurrentLength = length; NGCADPCM::InitFilter(); g_bStream = true; } LoopStart = pos; LoopLength = length; g_bStream = (m_DICMDBUF[0].CMDBYTE1 == 0); // This command can start/stop the stream // Stop stream if (m_DICMDBUF[0].CMDBYTE1 == 1) { AudioPos = 0; LoopStart = 0; LoopLength = 0; CurrentStart = 0; CurrentLength = 0; } WARN_LOG(DVDINTERFACE, "(Audio) Stream subcmd = %08x offset = %08x length=%08x", m_DICMDBUF[0].Hex, m_DICMDBUF[1].Hex << 2, m_DICMDBUF[2].Hex); } break; // Request Audio Status (Immediate) case 0xE2: { switch (m_DICMDBUF[0].CMDBYTE1) { case 0x00: // Returns streaming status m_DIIMMBUF.Hex = (AudioPos == 0) ? 0 : 1; break; case 0x01: // Returns the current offset if (g_bStream) m_DIIMMBUF.Hex = (AudioPos - CurrentStart) >> 2; else m_DIIMMBUF.Hex = 0; break; case 0x02: // Returns the start offset if (g_bStream) m_DIIMMBUF.Hex = CurrentStart >> 2; else m_DIIMMBUF.Hex = 0; break; case 0x03: // Returns the total length if (g_bStream) m_DIIMMBUF.Hex = CurrentLength; else m_DIIMMBUF.Hex = 0; break; default: WARN_LOG(DVDINTERFACE, "(Audio): Subcommand: %02x Request Audio status %s", m_DICMDBUF[0].CMDBYTE1, g_bStream? "on":"off"); break; } }
// __________________________________________________________________________________________________ // GameCube Bootstrap 2 HLE: // copy the apploader to 0x81200000 // execute the apploader, function by function, using the above utility. bool CBoot::EmulatedBS2_GC(bool skipAppLoader) { INFO_LOG(BOOT, "Faking GC BS2..."); // Set up MSR and the BAT SPR registers. UReg_MSR& m_MSR = ((UReg_MSR&)PowerPC::ppcState.msr); m_MSR.FP = 1; m_MSR.DR = 1; m_MSR.IR = 1; m_MSR.EE = 1; PowerPC::ppcState.spr[SPR_IBAT0U] = 0x80001fff; PowerPC::ppcState.spr[SPR_IBAT0L] = 0x00000002; PowerPC::ppcState.spr[SPR_DBAT0U] = 0x80001fff; PowerPC::ppcState.spr[SPR_DBAT0L] = 0x00000002; PowerPC::ppcState.spr[SPR_DBAT1U] = 0xc0001fff; PowerPC::ppcState.spr[SPR_DBAT1L] = 0x0000002a; // Write necessary values // Here we write values to memory that the apploader does not take care of. Game info goes // to 0x80000000 according to YAGCD 4.2. // It's possible to boot DOL and ELF files without a disc inserted if (DVDInterface::VolumeIsValid()) DVDRead(/*offset*/ 0x00000000, /*address*/ 0x00000000, 0x20, false); // write disc info PowerPC::HostWrite_U32(0x0D15EA5E, 0x80000020); // Booted from bootrom. 0xE5207C22 = booted from jtag PowerPC::HostWrite_U32(Memory::REALRAM_SIZE, 0x80000028); // Physical Memory Size (24MB on retail) // TODO determine why some games fail when using a retail ID. (Seem to take different EXI paths, // see Ikaruga for example) PowerPC::HostWrite_U32( 0x10000006, 0x8000002C); // Console type - DevKit (retail ID == 0x00000003) see YAGCD 4.2.1.1.2 PowerPC::HostWrite_U32(SConfig::GetInstance().bNTSC ? 0 : 1, 0x800000CC); // Fake the VI Init of the IPL (YAGCD 4.2.1.4) PowerPC::HostWrite_U32(0x01000000, 0x800000d0); // ARAM Size. 16MB main + 4/16/32MB external // (retail consoles have no external ARAM) PowerPC::HostWrite_U32(0x09a7ec80, 0x800000F8); // Bus Clock Speed PowerPC::HostWrite_U32(0x1cf7c580, 0x800000FC); // CPU Clock Speed PowerPC::HostWrite_U32(0x4c000064, 0x80000300); // Write default DFI Handler: rfi PowerPC::HostWrite_U32(0x4c000064, 0x80000800); // Write default FPU Handler: rfi PowerPC::HostWrite_U32(0x4c000064, 0x80000C00); // Write default Syscall Handler: rfi PowerPC::HostWrite_U64((u64)CEXIIPL::GetGCTime() * (u64)40500000, 0x800030D8); // Preset time base ticks // HIO checks this // PowerPC::HostWrite_U16(0x8200, 0x000030e6); // Console type HLE::Patch(0x81300000, "OSReport"); // HLE OSReport for Apploader if (!DVDInterface::VolumeIsValid()) return false; // Load Apploader to Memory - The apploader is hardcoded to begin at 0x2440 on the disc, // but the size can differ between discs. Compare with YAGCD chap 13. const DiscIO::IVolume& volume = DVDInterface::GetVolume(); const u32 apploader_offset = 0x2440; u32 apploader_entry, apploader_size, apploader_trailer; if (skipAppLoader || !volume.ReadSwapped(apploader_offset + 0x10, &apploader_entry, false) || !volume.ReadSwapped(apploader_offset + 0x14, &apploader_size, false) || !volume.ReadSwapped(apploader_offset + 0x18, &apploader_trailer, false) || apploader_entry == (u32)-1 || apploader_size + apploader_trailer == (u32)-1) { INFO_LOG(BOOT, "GC BS2: Not running apploader!"); return false; } DVDRead(apploader_offset + 0x20, 0x01200000, apploader_size + apploader_trailer, false); // Setup pointers like real BS2 does if (SConfig::GetInstance().bNTSC) { PowerPC::ppcState.gpr[1] = 0x81566550; // StackPointer, used to be set to 0x816ffff0 PowerPC::ppcState.gpr[2] = 0x81465cc0; // Global pointer to Small Data Area 2 Base (haven't // seen anything use it...meh) PowerPC::ppcState.gpr[13] = 0x81465320; // Global pointer to Small Data Area Base (Luigi's Mansion's apploader uses it) } else { PowerPC::ppcState.gpr[1] = 0x815edca8; PowerPC::ppcState.gpr[2] = 0x814b5b20; PowerPC::ppcState.gpr[13] = 0x814b4fc0; } // TODO - Make Apploader(or just RunFunction()) debuggable!!! // Call iAppLoaderEntry. DEBUG_LOG(MASTER_LOG, "Call iAppLoaderEntry"); u32 iAppLoaderFuncAddr = 0x80003100; PowerPC::ppcState.gpr[3] = iAppLoaderFuncAddr + 0; PowerPC::ppcState.gpr[4] = iAppLoaderFuncAddr + 4; PowerPC::ppcState.gpr[5] = iAppLoaderFuncAddr + 8; RunFunction(apploader_entry); u32 iAppLoaderInit = PowerPC::Read_U32(iAppLoaderFuncAddr + 0); u32 iAppLoaderMain = PowerPC::Read_U32(iAppLoaderFuncAddr + 4); u32 iAppLoaderClose = PowerPC::Read_U32(iAppLoaderFuncAddr + 8); // iAppLoaderInit DEBUG_LOG(MASTER_LOG, "Call iAppLoaderInit"); PowerPC::ppcState.gpr[3] = 0x81300000; RunFunction(iAppLoaderInit); // iAppLoaderMain - Here we load the apploader, the DOL (the exe) and the FST (filesystem). // To give you an idea about where the stuff is located on the disc take a look at yagcd // ch 13. DEBUG_LOG(MASTER_LOG, "Call iAppLoaderMain"); do { PowerPC::ppcState.gpr[3] = 0x81300004; PowerPC::ppcState.gpr[4] = 0x81300008; PowerPC::ppcState.gpr[5] = 0x8130000c; RunFunction(iAppLoaderMain); u32 iRamAddress = PowerPC::Read_U32(0x81300004); u32 iLength = PowerPC::Read_U32(0x81300008); u32 iDVDOffset = PowerPC::Read_U32(0x8130000c); INFO_LOG(MASTER_LOG, "DVDRead: offset: %08x memOffset: %08x length: %i", iDVDOffset, iRamAddress, iLength); DVDRead(iDVDOffset, iRamAddress, iLength, false); } while (PowerPC::ppcState.gpr[3] != 0x00); // iAppLoaderClose DEBUG_LOG(MASTER_LOG, "call iAppLoaderClose"); RunFunction(iAppLoaderClose); // return PC = PowerPC::ppcState.gpr[3]; // Load patches PatchEngine::LoadPatches(); // If we have any patches that need to be applied very early, here's a good place PatchEngine::ApplyFramePatches(); return true; }
// __________________________________________________________________________________________________ // Wii Bootstrap 2 HLE: // copy the apploader to 0x81200000 // execute the apploader bool CBoot::EmulatedBS2_Wii() { INFO_LOG(BOOT, "Faking Wii BS2..."); // Setup Wii memory DiscIO::Country country_code = DiscIO::Country::COUNTRY_UNKNOWN; if (DVDInterface::VolumeIsValid()) country_code = DVDInterface::GetVolume().GetCountry(); if (SetupWiiMemory(country_code) == false) return false; // Execute the apploader bool apploaderRan = false; if (DVDInterface::VolumeIsValid() && DVDInterface::GetVolume().GetVolumeType() == DiscIO::Platform::WII_DISC) { // This is some kind of consistency check that is compared to the 0x00 // values as the game boots. This location keeps the 4 byte ID for as long // as the game is running. The 6 byte ID at 0x00 is overwritten sometime // after this check during booting. DVDRead(0, 0x3180, 4, true); // Set up MSR and the BAT SPR registers. UReg_MSR& m_MSR = ((UReg_MSR&)PowerPC::ppcState.msr); m_MSR.FP = 1; m_MSR.DR = 1; m_MSR.IR = 1; m_MSR.EE = 1; PowerPC::ppcState.spr[SPR_IBAT0U] = 0x80001fff; PowerPC::ppcState.spr[SPR_IBAT0L] = 0x00000002; PowerPC::ppcState.spr[SPR_IBAT4U] = 0x90001fff; PowerPC::ppcState.spr[SPR_IBAT4L] = 0x10000002; PowerPC::ppcState.spr[SPR_DBAT0U] = 0x80001fff; PowerPC::ppcState.spr[SPR_DBAT0L] = 0x00000002; PowerPC::ppcState.spr[SPR_DBAT1U] = 0xc0001fff; PowerPC::ppcState.spr[SPR_DBAT1L] = 0x0000002a; PowerPC::ppcState.spr[SPR_DBAT4U] = 0x90001fff; PowerPC::ppcState.spr[SPR_DBAT4L] = 0x10000002; PowerPC::ppcState.spr[SPR_DBAT5U] = 0xd0001fff; PowerPC::ppcState.spr[SPR_DBAT5L] = 0x1000002a; Memory::Write_U32(0x4c000064, 0x00000300); // Write default DSI Handler: rfi Memory::Write_U32(0x4c000064, 0x00000800); // Write default FPU Handler: rfi Memory::Write_U32(0x4c000064, 0x00000C00); // Write default Syscall Handler: rfi HLE::Patch(0x81300000, "OSReport"); // HLE OSReport for Apploader PowerPC::ppcState.gpr[1] = 0x816ffff0; // StackPointer const u32 apploader_offset = 0x2440; // 0x1c40; // Load Apploader to Memory const DiscIO::IVolume& volume = DVDInterface::GetVolume(); u32 apploader_entry, apploader_size; if (!volume.ReadSwapped(apploader_offset + 0x10, &apploader_entry, true) || !volume.ReadSwapped(apploader_offset + 0x14, &apploader_size, true) || apploader_entry == (u32)-1 || apploader_size == (u32)-1) { ERROR_LOG(BOOT, "Invalid apploader. Probably your image is corrupted."); return false; } DVDRead(apploader_offset + 0x20, 0x01200000, apploader_size, true); // call iAppLoaderEntry DEBUG_LOG(BOOT, "Call iAppLoaderEntry"); u32 iAppLoaderFuncAddr = 0x80004000; PowerPC::ppcState.gpr[3] = iAppLoaderFuncAddr + 0; PowerPC::ppcState.gpr[4] = iAppLoaderFuncAddr + 4; PowerPC::ppcState.gpr[5] = iAppLoaderFuncAddr + 8; RunFunction(apploader_entry); u32 iAppLoaderInit = PowerPC::Read_U32(iAppLoaderFuncAddr + 0); u32 iAppLoaderMain = PowerPC::Read_U32(iAppLoaderFuncAddr + 4); u32 iAppLoaderClose = PowerPC::Read_U32(iAppLoaderFuncAddr + 8); // iAppLoaderInit DEBUG_LOG(BOOT, "Run iAppLoaderInit"); PowerPC::ppcState.gpr[3] = 0x81300000; RunFunction(iAppLoaderInit); // Let the apploader load the exe to memory. At this point I get an unknown IPC command // (command zero) when I load Wii Sports or other games a second time. I don't notice // any side effects however. It's a little disconcerting however that Start after Stop // behaves differently than the first Start after starting Dolphin. It means something // was not reset correctly. DEBUG_LOG(BOOT, "Run iAppLoaderMain"); do { PowerPC::ppcState.gpr[3] = 0x81300004; PowerPC::ppcState.gpr[4] = 0x81300008; PowerPC::ppcState.gpr[5] = 0x8130000c; RunFunction(iAppLoaderMain); u32 iRamAddress = PowerPC::Read_U32(0x81300004); u32 iLength = PowerPC::Read_U32(0x81300008); u32 iDVDOffset = PowerPC::Read_U32(0x8130000c) << 2; INFO_LOG(BOOT, "DVDRead: offset: %08x memOffset: %08x length: %i", iDVDOffset, iRamAddress, iLength); DVDRead(iDVDOffset, iRamAddress, iLength, true); } while (PowerPC::ppcState.gpr[3] != 0x00); // iAppLoaderClose DEBUG_LOG(BOOT, "Run iAppLoaderClose"); RunFunction(iAppLoaderClose); apploaderRan = true; // Pass the "#002 check" // Apploader writes the IOS version and revision here, we copy it // Fake IOSv9 r2.4 if no version is found (elf loading) u32 firmwareVer = PowerPC::Read_U32(0x80003188); PowerPC::Write_U32(firmwareVer ? firmwareVer : 0x00090204, 0x80003140); // Load patches and run startup patches PatchEngine::LoadPatches(); // return PC = PowerPC::ppcState.gpr[3]; } return apploaderRan; }
bool CBoot::SetupWiiMemory(DiscIO::Country country) { static const CountrySetting SETTING_EUROPE = {"EUR", "PAL", "EU", "LE"}; static const CountrySetting SETTING_USA = {"USA", "NTSC", "US", "LU"}; static const CountrySetting SETTING_JAPAN = {"JPN", "NTSC", "JP", "LJ"}; static const CountrySetting SETTING_KOREA = {"KOR", "NTSC", "KR", "LKH"}; static const std::map<DiscIO::Country, const CountrySetting> country_settings = { {DiscIO::Country::COUNTRY_EUROPE, SETTING_EUROPE}, {DiscIO::Country::COUNTRY_USA, SETTING_USA}, {DiscIO::Country::COUNTRY_JAPAN, SETTING_JAPAN}, {DiscIO::Country::COUNTRY_KOREA, SETTING_KOREA}, // TODO: Determine if Taiwan have their own specific settings. // Also determine if there are other specific settings // for other countries. {DiscIO::Country::COUNTRY_TAIWAN, SETTING_JAPAN}}; auto entryPos = country_settings.find(country); const CountrySetting& country_setting = (entryPos != country_settings.end()) ? entryPos->second : (SConfig::GetInstance().bNTSC ? SETTING_USA : SETTING_EUROPE); // default to USA or EUR depending on game's video mode SettingsHandler gen; std::string serno; std::string settings_Filename( Common::GetTitleDataPath(TITLEID_SYSMENU, Common::FROM_SESSION_ROOT) + WII_SETTING); if (File::Exists(settings_Filename)) { File::IOFile settingsFileHandle(settings_Filename, "rb"); if (settingsFileHandle.ReadBytes((void*)gen.GetData(), SettingsHandler::SETTINGS_SIZE)) { gen.Decrypt(); serno = gen.GetValue("SERNO"); gen.Reset(); } File::Delete(settings_Filename); } if (serno.empty() || serno == "000000000") { if (Core::g_want_determinism) serno = "123456789"; else serno = gen.generateSerialNumber(); INFO_LOG(BOOT, "No previous serial number found, generated one instead: %s", serno.c_str()); } else { INFO_LOG(BOOT, "Using serial number: %s", serno.c_str()); } std::string model = "RVL-001(" + country_setting.area + ")"; gen.AddSetting("AREA", country_setting.area); gen.AddSetting("MODEL", model); gen.AddSetting("DVD", "0"); gen.AddSetting("MPCH", "0x7FFE"); gen.AddSetting("CODE", country_setting.code); gen.AddSetting("SERNO", serno); gen.AddSetting("VIDEO", country_setting.video); gen.AddSetting("GAME", country_setting.game); File::CreateFullPath(settings_Filename); { File::IOFile settingsFileHandle(settings_Filename, "wb"); if (!settingsFileHandle.WriteBytes(gen.GetData(), SettingsHandler::SETTINGS_SIZE)) { PanicAlertT("SetupWiiMemory: Can't create setting.txt file"); return false; } // Write the 256 byte setting.txt to memory. Memory::CopyToEmu(0x3800, gen.GetData(), SettingsHandler::SETTINGS_SIZE); } INFO_LOG(BOOT, "Setup Wii Memory..."); /* Set hardcoded global variables to Wii memory. These are partly collected from WiiBrew. These values are needed for the games to function correctly. A few values in this region will also be placed here by the game as it boots. They are: 0x80000038 Start of FST 0x8000003c Size of FST Size 0x80000060 Copyright code */ // When booting a WAD or the system menu, there will probably not be a disc inserted if (DVDInterface::VolumeIsValid()) DVDRead(0x00000000, 0x00000000, 0x20, false); // Game Code Memory::Write_U32(0x0D15EA5E, 0x00000020); // Another magic word Memory::Write_U32(0x00000001, 0x00000024); // Unknown Memory::Write_U32(Memory::REALRAM_SIZE, 0x00000028); // MEM1 size 24MB Memory::Write_U32(0x00000023, 0x0000002c); // Production Board Model Memory::Write_U32(0x00000000, 0x00000030); // Init Memory::Write_U32(0x817FEC60, 0x00000034); // Init // 38, 3C should get start, size of FST through apploader Memory::Write_U32(0x38a00040, 0x00000060); // Exception init Memory::Write_U32(0x8008f7b8, 0x000000e4); // Thread Init Memory::Write_U32(Memory::REALRAM_SIZE, 0x000000f0); // "Simulated memory size" (debug mode?) Memory::Write_U32(0x8179b500, 0x000000f4); // __start Memory::Write_U32(0x0e7be2c0, 0x000000f8); // Bus speed Memory::Write_U32(0x2B73A840, 0x000000fc); // CPU speed Memory::Write_U16(0x0000, 0x000030e6); // Console type Memory::Write_U32(0x00000000, 0x000030c0); // EXI Memory::Write_U32(0x00000000, 0x000030c4); // EXI Memory::Write_U32(0x00000000, 0x000030dc); // Time Memory::Write_U32(0x00000000, 0x000030d8); // Time Memory::Write_U16(0x8201, 0x000030e6); // Dev console / debug capable Memory::Write_U32(0x00000000, 0x000030f0); // Apploader Memory::Write_U32(0x01800000, 0x00003100); // BAT Memory::Write_U32(0x01800000, 0x00003104); // BAT Memory::Write_U32(0x00000000, 0x0000310c); // Init Memory::Write_U32(0x8179d500, 0x00003110); // Init Memory::Write_U32(0x04000000, 0x00003118); // Unknown Memory::Write_U32(0x04000000, 0x0000311c); // BAT Memory::Write_U32(0x93400000, 0x00003120); // BAT Memory::Write_U32(0x90000800, 0x00003124); // Init - MEM2 low Memory::Write_U32(0x93ae0000, 0x00003128); // Init - MEM2 high Memory::Write_U32(0x93ae0000, 0x00003130); // IOS MEM2 low Memory::Write_U32(0x93b00000, 0x00003134); // IOS MEM2 high Memory::Write_U32(0x00000012, 0x00003138); // Console type // 40 is copied from 88 after running apploader Memory::Write_U32(0x00090204, 0x00003140); // IOS revision (IOS9, v2.4) Memory::Write_U32(0x00062507, 0x00003144); // IOS date in USA format (June 25, 2007) Memory::Write_U16(0x0113, 0x0000315e); // Apploader Memory::Write_U32(0x0000FF16, 0x00003158); // DDR ram vendor code Memory::Write_U32(0x00000000, 0x00003160); // Init semaphore (sysmenu waits for this to clear) Memory::Write_U32(0x00090204, 0x00003188); // Expected IOS revision Memory::Write_U8(0x80, 0x0000315c); // OSInit Memory::Write_U16(0x0000, 0x000030e0); // PADInit Memory::Write_U32(0x80000000, 0x00003184); // GameID Address // Fake the VI Init of the IPL Memory::Write_U32(SConfig::GetInstance().bNTSC ? 0 : 1, 0x000000CC); // Clear exception handler. Why? Don't we begin with only zeros? for (int i = 0x3000; i <= 0x3038; i += 4) { Memory::Write_U32(0x00000000, i); } return true; }