// This one can load both leftover map files on game discs (like Zelda), and mapfiles // produced by SaveSymbolMap below. bool PPCSymbolDB::LoadMap(const std::string& filename) { File::IOFile f(filename, "r"); if (!f) return false; bool started = false; char line[512]; while (fgets(line, 512, f.GetHandle())) { if (strlen(line) < 4) continue; char temp[256]; sscanf(line, "%255s", temp); if (strcmp(temp, "UNUSED")==0) continue; if (strcmp(temp, ".text")==0) {started = true; continue;}; if (strcmp(temp, ".init")==0) {started = true; continue;}; if (strcmp(temp, "Starting")==0) continue; if (strcmp(temp, "extab")==0) continue; if (strcmp(temp, ".ctors")==0) break; //uh? if (strcmp(temp, ".dtors")==0) break; if (strcmp(temp, ".rodata")==0) continue; if (strcmp(temp, ".data")==0) continue; if (strcmp(temp, ".sbss")==0) continue; if (strcmp(temp, ".sdata")==0) continue; if (strcmp(temp, ".sdata2")==0) continue; if (strcmp(temp, "address")==0) continue; if (strcmp(temp, "-----------------------")==0) continue; if (strcmp(temp, ".sbss2")==0) break; if (temp[1] == ']') continue; if (!started) continue; u32 address, vaddress, size, unknown; char name[512]; sscanf(line, "%08x %08x %08x %i %511s", &address, &size, &vaddress, &unknown, name); const char *namepos = strstr(line, name); if (namepos != nullptr) //would be odd if not :P strcpy(name, namepos); name[strlen(name) - 1] = 0; // we want the function names only .... TODO: or do we really? aren't we wasting information here? for (size_t i = 0; i < strlen(name); i++) { if (name[i] == ' ') name[i] = 0x00; if (name[i] == '(') name[i] = 0x00; } // Check if this is a valid entry. if (strcmp(name, ".text") != 0 || strcmp(name, ".init") != 0 || strlen(name) > 0) { AddKnownSymbol(vaddress | 0x80000000, size, name); // ST_FUNCTION } } Index(); return true; }
// This one can load both leftover map files on game discs (like Zelda), and mapfiles // produced by SaveSymbolMap below. // bad=true means carefully load map files that might not be from exactly the right version bool PPCSymbolDB::LoadMap(const std::string& filename, bool bad) { File::IOFile f(filename, "r"); if (!f) return false; // four columns are used in American Mensa Academy map files and perhaps other games bool started = false, four_columns = false; int good_count = 0, bad_count = 0; char line[512]; while (fgets(line, 512, f.GetHandle())) { size_t length = strlen(line); if (length < 4) continue; if (length == 34 && strcmp(line, " address Size address offset\n") == 0) { four_columns = true; continue; } char temp[256]; sscanf(line, "%255s", temp); if (strcmp(temp, "UNUSED")==0) continue; if (strcmp(temp, ".text")==0) {started = true; continue;}; if (strcmp(temp, ".init")==0) {started = true; continue;}; if (strcmp(temp, "Starting")==0) continue; if (strcmp(temp, "extab")==0) continue; if (strcmp(temp, ".ctors")==0) break; //uh? if (strcmp(temp, ".dtors")==0) break; if (strcmp(temp, ".rodata")==0) continue; if (strcmp(temp, ".data")==0) continue; if (strcmp(temp, ".sbss")==0) continue; if (strcmp(temp, ".sdata")==0) continue; if (strcmp(temp, ".sdata2")==0) continue; if (strcmp(temp, "address")==0) continue; if (strcmp(temp, "-----------------------")==0) continue; if (strcmp(temp, ".sbss2")==0) break; if (temp[1] == ']') continue; if (!started) continue; u32 address, vaddress, size, offset, unknown; char name[512], container[512]; if (four_columns) { // sometimes there is no unknown number, and sometimes it is because it is an entry of something else if (length > 37 && line[37]==' ') { unknown = 0; sscanf(line, "%08x %08x %08x %08x %511s", &address, &size, &vaddress, &offset, name); char *s = strstr(line, "(entry of "); if (s) { sscanf(s + 10, "%511s", container); char *s2 = (strchr(container, ')')); if (s2 && container[0]!='.') { s2[0] = '\0'; strcat(container, "::"); strcat(container, name); strcpy(name, container); } } } else { sscanf(line, "%08x %08x %08x %08x %i %511s", &address, &size, &vaddress, &offset, &unknown, name); } } // some entries in the table have a function name followed by " (entry of " followed by a container name, followed by ")" // instead of a space followed by a number followed by a space followed by a name else if (length > 27 && line[27] != ' ' && strstr(line, "(entry of ")) { unknown = 0; sscanf(line, "%08x %08x %08x %511s", &address, &size, &vaddress, name); char *s = strstr(line, "(entry of "); if (s) { sscanf(s + 10, "%511s", container); char *s2 = (strchr(container, ')')); if (s2 && container[0] != '.') { s2[0] = '\0'; strcat(container, "::"); strcat(container, name); strcpy(name, container); } } } else { sscanf(line, "%08x %08x %08x %i %511s", &address, &size, &vaddress, &unknown, name); } const char *namepos = strstr(line, name); if (namepos != nullptr) //would be odd if not :P strcpy(name, namepos); name[strlen(name) - 1] = 0; // we want the function names only .... TODO: or do we really? aren't we wasting information here? for (size_t i = 0; i < strlen(name); i++) { if (name[i] == ' ') name[i] = 0x00; if (name[i] == '(') name[i] = 0x00; } // Check if this is a valid entry. if (strcmp(name, ".text") != 0 && strcmp(name, ".init") != 0 && strlen(name) > 0) { vaddress |= 0x80000000; bool good = !bad; if (!good) { // check for BLR before function u32 opcode = PowerPC::HostRead_Instruction(vaddress - 4); if (opcode == 0x4e800020) { // check for BLR at end of function opcode = PowerPC::HostRead_Instruction(vaddress + size - 4); if (opcode == 0x4e800020) good = true; } } if (good) { ++good_count; AddKnownSymbol(vaddress | 0x80000000, size, name); // ST_FUNCTION } else { ++bad_count; } } } Index(); if (bad) SuccessAlertT("Loaded %d good functions, ignored %d bad functions.", good_count, bad_count); return true; }
// This one can load both leftover map files on game discs (like Zelda), and mapfiles // produced by SaveSymbolMap below. // bad=true means carefully load map files that might not be from exactly the right version bool PPCSymbolDB::LoadMap(const std::string& filename, bool bad) { File::IOFile f(filename, "r"); if (!f) return false; // four columns are used in American Mensa Academy map files and perhaps other games bool four_columns = false; int good_count = 0; int bad_count = 0; char line[512]; std::string section_name; while (fgets(line, 512, f.GetHandle())) { size_t length = strlen(line); if (length < 4) continue; if (length == 34 && strcmp(line, " address Size address offset\n") == 0) { four_columns = true; continue; } char temp[256]; sscanf(line, "%255s", temp); if (strcmp(temp, "UNUSED") == 0) continue; // Support CodeWarrior and Dolphin map if (StringEndsWith(line, " section layout\n") || strcmp(temp, ".text") == 0 || strcmp(temp, ".init") == 0) { section_name = temp; continue; } // Skip four columns' header. // // Four columns example: // // .text section layout // Starting Virtual // address Size address // ----------------------- if (strcmp(temp, "Starting") == 0) continue; if (strcmp(temp, "address") == 0) continue; if (strcmp(temp, "-----------------------") == 0) continue; // Skip link map. // // Link map example: // // Link map of __start // 1] __start(func, weak) found in os.a __start.c // 2] __init_registers(func, local) found in os.a __start.c // 3] _stack_addr found as linker generated symbol // ... // 10] EXILock(func, global) found in exi.a EXIBios.c if (StringEndsWith(temp, "]")) continue; // TODO - Handle/Write a parser for: // - Memory map // - Link map // - Linker generated symbols if (section_name.empty()) continue; u32 address, vaddress, size, offset, alignment; char name[512], container[512]; if (four_columns) { // sometimes there is no alignment value, and sometimes it is because it is an entry of // something else if (length > 37 && line[37] == ' ') { alignment = 0; sscanf(line, "%08x %08x %08x %08x %511s", &address, &size, &vaddress, &offset, name); char* s = strstr(line, "(entry of "); if (s) { sscanf(s + 10, "%511s", container); char* s2 = (strchr(container, ')')); if (s2 && container[0] != '.') { s2[0] = '\0'; strcat(container, "::"); strcat(container, name); strcpy(name, container); } } } else { sscanf(line, "%08x %08x %08x %08x %i %511s", &address, &size, &vaddress, &offset, &alignment, name); } } // some entries in the table have a function name followed by " (entry of " followed by a // container name, followed by ")" // instead of a space followed by a number followed by a space followed by a name else if (length > 27 && line[27] != ' ' && strstr(line, "(entry of ")) { alignment = 0; sscanf(line, "%08x %08x %08x %511s", &address, &size, &vaddress, name); char* s = strstr(line, "(entry of "); if (s) { sscanf(s + 10, "%511s", container); char* s2 = (strchr(container, ')')); if (s2 && container[0] != '.') { s2[0] = '\0'; strcat(container, "::"); strcat(container, name); strcpy(name, container); } } } else { sscanf(line, "%08x %08x %08x %i %511s", &address, &size, &vaddress, &alignment, name); } const char* namepos = strstr(line, name); if (namepos != nullptr) // would be odd if not :P strcpy(name, namepos); name[strlen(name) - 1] = 0; if (name[strlen(name) - 1] == '\r') name[strlen(name) - 1] = 0; // Check if this is a valid entry. if (strlen(name) > 0) { // Can't compute the checksum if not in RAM bool good = !bad && PowerPC::HostIsInstructionRAMAddress(vaddress) && PowerPC::HostIsInstructionRAMAddress(vaddress + size - 4); if (!good) { // check for BLR before function PowerPC::TryReadInstResult read_result = PowerPC::TryReadInstruction(vaddress - 4); if (read_result.valid && read_result.hex == 0x4e800020) { // check for BLR at end of function read_result = PowerPC::TryReadInstruction(vaddress + size - 4); good = read_result.valid && read_result.hex == 0x4e800020; } } if (good) { ++good_count; if (section_name == ".text" || section_name == ".init") AddKnownSymbol(vaddress, size, name, Symbol::Type::Function); else AddKnownSymbol(vaddress, size, name, Symbol::Type::Data); } else { ++bad_count; } } } Index(); if (bad) SuccessAlertT("Loaded %d good functions, ignored %d bad functions.", good_count, bad_count); return true; }