diff --git a/Makefile b/Makefile index bfa98d086..58cfc69f6 100644 --- a/Makefile +++ b/Makefile @@ -98,7 +98,8 @@ DEPS += $(filter %.cpp.d, $(SRCS:.cpp=.cpp.d)) $(OUTPUT_DIR)/default.xbe: main.exe $(OUTPUT_DIR) $(CXBE) @echo "[ CXBE ] $@" - $(VE)$(CXBE) -OUT:$@ -TITLE:$(XBE_TITLE) $< $(QUIET) + $(VE)$(CXBE) -OUT:$@ -TITLE:$(XBE_TITLE) -TITLEID:$(XBE_TITLEID) \ + -REGION:$(XBE_REGION) -VERSION:$(XBE_VERSION) $< $(QUIET) $(OUTPUT_DIR): @mkdir -p $(OUTPUT_DIR); diff --git a/tools/cxbe/Exe.cpp b/tools/cxbe/Exe.cpp index a43c373b2..789e1a9c1 100644 --- a/tools/cxbe/Exe.cpp +++ b/tools/cxbe/Exe.cpp @@ -100,6 +100,7 @@ Exe::Exe(const char *x_szFilename) // read section headers { m_SectionHeader = new SectionHeader[m_Header.m_sections]; + m_SectionHeader_longname = new SectionHeader_longname[m_Header.m_sections]; printf("Exe::Exe: Reading Section Headers...\n"); @@ -115,7 +116,46 @@ Exe::Exe(const char *x_szFilename) goto cleanup; } - printf("OK %d\n", v); + // interpret long section names + if(m_SectionHeader[v].m_name[0] == '/') + { + m_SectionHeader_longname[v].m_offset = 0; + for(uint32 i = 1; i < 8; ++i) + { + char c = m_SectionHeader[v].m_name[i]; + if(!c) + break; + if(c < '0' || c > '9') // not a long section after all? + goto notlong; + m_SectionHeader_longname[v].m_offset *= 10; + m_SectionHeader_longname[v].m_offset += c - '0'; + } + m_SectionHeader_longname[v].m_longname = new char[256](); + + long tmppos = ftell(ExeFile); + fseek(ExeFile, m_Header.m_symbol_table_addr + m_SectionHeader_longname[v].m_offset, + SEEK_SET); + + uint32 i; + for(i = 0; i < 255; ++i) + { + int c = fgetc(ExeFile); + if(!c || c == EOF) + break; + m_SectionHeader_longname[v].m_longname[i] = c; + } + m_SectionHeader_longname[v].m_longname[i] = 0; + + fseek(ExeFile, tmppos, SEEK_SET); + printf("OK %d (long)\n", v, m_SectionHeader_longname[v].m_offset, + m_SectionHeader_longname[v].m_longname); + } + else + { + notlong:; + m_SectionHeader_longname[v].m_longname = NULL; + printf("OK %d\n", v); + } } } @@ -184,6 +224,7 @@ Exe::Exe(const char *x_szFilename) void Exe::ConstructorInit() { m_SectionHeader = NULL; + m_SectionHeader_longname = NULL; m_bzSection = NULL; } @@ -193,12 +234,16 @@ Exe::~Exe() if(m_bzSection != 0) { for(uint32 v = 0; v < m_Header.m_sections; v++) + { + delete[] m_SectionHeader_longname[v].m_longname; delete[] m_bzSection[v]; + } delete[] m_bzSection; } delete[] m_SectionHeader; + delete[] m_SectionHeader_longname; } // export to Exe file diff --git a/tools/cxbe/Exe.h b/tools/cxbe/Exe.h index d1adcaff5..523cef6a1 100644 --- a/tools/cxbe/Exe.h +++ b/tools/cxbe/Exe.h @@ -129,6 +129,13 @@ class Exe : public Error uint32 m_characteristics; // characteristics for this segment } __attribute((packed)) * m_SectionHeader; + // array to store long section names + struct SectionHeader_longname + { + char *m_longname; + uint32 m_offset; + } __attribute((packed)) * m_SectionHeader_longname; + // array of section data uint08 **m_bzSection; diff --git a/tools/cxbe/Main.cpp b/tools/cxbe/Main.cpp index d24b5d12d..0f21e9459 100644 --- a/tools/cxbe/Main.cpp +++ b/tools/cxbe/Main.cpp @@ -9,6 +9,7 @@ #include "Exe.h" #include "Xbe.h" +#include #include // program entry point @@ -19,18 +20,35 @@ int main(int argc, char *argv[]) char szXbeFilename[OPTION_LEN + 1] = { 0 }; char szDumpFilename[OPTION_LEN + 1] = { 0 }; char szXbeTitle[OPTION_LEN + 1] = "Untitled"; + char szXbeTitleID[OPTION_LEN + 1] = ""; + char szXbeRegions[OPTION_LEN + 1] = ""; + char szXbeVersion[OPTION_LEN + 1] = ""; char szMode[OPTION_LEN + 1] = "retail"; char szLogo[OPTION_LEN + 1] = ""; char szDebugPath[OPTION_LEN + 1] = ""; + bool bRetail; + uint32 dwTitleId = 0xFFFF0002; + uint32 dwRegions = XBEIMAGE_GAME_REGION_NA | XBEIMAGE_GAME_REGION_JAPAN | + XBEIMAGE_GAME_REGION_RESTOFWORLD | XBEIMAGE_GAME_REGION_MANUFACTURING; + uint32 dwVersion; const char *program = argv[0]; const char *program_desc = "CXBE EXE to XBE (win32 to Xbox) Relinker (Version: " VERSION ")"; Option options[] = { - { szExeFilename, NULL, "exefile" }, { szXbeFilename, "OUT", "filename" }, - { szDumpFilename, "DUMPINFO", "filename" }, { szXbeTitle, "TITLE", "title" }, - { szMode, "MODE", "{debug|retail}" }, { szLogo, "LOGO", "filename" }, - { szDebugPath, "DEBUGPATH", "path" }, { NULL } + { szExeFilename, NULL, "exefile" }, + { szXbeFilename, "OUT", "filename" }, + { szDumpFilename, "DUMPINFO", "filename" }, + { szXbeTitle, "TITLE", "title" }, + { szXbeTitleID, "TITLEID", "{%c%c-%u|%x}" }, + { szXbeRegions, "REGION", + "{-|[n][j][w][m]}\n" + " -=none, n=North America, j=Japan, w=world, m=manufacturing" }, + { szXbeVersion, "VERSION", "version" }, + { szMode, "MODE", "{debug|retail}" }, + { szLogo, "LOGO", "filename" }, + { szDebugPath, "DEBUGPATH", "path" }, + { NULL } }; if(ParseOptions(argv, argc, options, szErrorMessage)) @@ -54,6 +72,91 @@ int main(int argc, char *argv[]) szXbeTitle[40] = '\0'; } + // interpret title id + if(szXbeTitleID[0]) + { + bool hex = true; + for(int i = 0; szXbeTitleID[i]; ++i) + { + if(szXbeTitleID[i] == '-') + { + hex = false; + break; + } + } + if(hex) + { + sscanf(szXbeTitleID, "%x", &dwTitleId); + } + else + { + char titlechar[2]; + unsigned titleno; + if(sscanf(szXbeTitleID, "%c%c-%u", &titlechar[0], &titlechar[1], &titleno) != 3) + { + strncpy(szErrorMessage, "invalid TITLEID", ERROR_LEN); + goto cleanup; + } + if(titleno > 0xFFFF) + { + printf("WARNING: Title ID number too high (max is 65535)\n"); + titleno = 0xFFFF; + } + dwTitleId = (titlechar[0] << 24) | (titlechar[1] << 16) | titleno; + } + } + + // interpret region flags + if(szXbeRegions[0]) + { + char c; + for(int i = 0; (c = szXbeRegions[i]); ++i) + { + switch(c) + { + case '-':; + dwRegions = 0; + goto breakfor; + case 'a':; + dwRegions = XBEIMAGE_GAME_REGION_NA | XBEIMAGE_GAME_REGION_JAPAN | + XBEIMAGE_GAME_REGION_RESTOFWORLD | + XBEIMAGE_GAME_REGION_MANUFACTURING; + goto breakfor; + case 'n':; + dwRegions |= XBEIMAGE_GAME_REGION_NA; + break; + case 'j':; + dwRegions |= XBEIMAGE_GAME_REGION_JAPAN; + break; + case 'w':; + dwRegions |= XBEIMAGE_GAME_REGION_RESTOFWORLD; + break; + case 'm':; + dwRegions |= XBEIMAGE_GAME_REGION_MANUFACTURING; + break; + default:; + printf("WARNING: Invalid region char: %c\n", c); + break; + } + } + breakfor:; + } + else + { + dwRegions = XBEIMAGE_GAME_REGION_NA | XBEIMAGE_GAME_REGION_JAPAN | + XBEIMAGE_GAME_REGION_RESTOFWORLD | XBEIMAGE_GAME_REGION_MANUFACTURING; + } + + // interpret version + if(szXbeVersion[0]) + { + dwVersion = strtoul(szXbeVersion, NULL, 0); + } + else + { + dwVersion = 0; + } + // verify we received the required parameters if(szExeFilename[0] == '\0') { @@ -90,7 +193,8 @@ int main(int argc, char *argv[]) LogoPtr = &logo; } - Xbe *XbeFile = new Xbe(ExeFile, szXbeTitle, bRetail, LogoPtr, szDebugPath); + Xbe *XbeFile = new Xbe( + ExeFile, szXbeTitle, dwTitleId, dwRegions, dwVersion, bRetail, LogoPtr, szDebugPath); if(XbeFile->GetError() != 0) { diff --git a/tools/cxbe/Xbe.cpp b/tools/cxbe/Xbe.cpp index b02b9996a..4421c4271 100644 --- a/tools/cxbe/Xbe.cpp +++ b/tools/cxbe/Xbe.cpp @@ -32,7 +32,8 @@ static size_t BasenameOffset(const std::string &path) } // construct via Exe file object -Xbe::Xbe(class Exe *x_Exe, const char *x_szTitle, bool x_bRetail, const std::vector *logo, +Xbe::Xbe(class Exe *x_Exe, const char *x_szTitle, uint32 x_dwTitleID, uint32 x_dwRegions, + uint32 x_dwVersion, bool x_bRetail, const std::vector *logo, const char *x_szDebugPath) { ConstructorInit(); @@ -125,8 +126,16 @@ Xbe::Xbe(class Exe *x_Exe, const char *x_szTitle, bool x_bRetail, const std::vec { uint32 s = 0; - while(s < 8 && x_Exe->m_SectionHeader[v].m_name[s] != '\0') - s++; + if(x_Exe->m_SectionHeader_longname[v].m_longname) + { + while(s < 255 && x_Exe->m_SectionHeader_longname[v].m_longname[s] != '\0') + s++; + } + else + { + while(s < 8 && x_Exe->m_SectionHeader[v].m_name[s] != '\0') + s++; + } mrc += s + 1; } @@ -271,8 +280,7 @@ Xbe::Xbe(class Exe *x_Exe, const char *x_szTitle, bool x_bRetail, const std::vec m_Certificate.dwTimeDate = CurrentTime; - // TODO: generate in the form CX-9999 - m_Certificate.dwTitleId = 0xFFFF0002; + m_Certificate.dwTitleId = x_dwTitleID; // title name memset(m_Certificate.wszTitleName, 0, sizeof(m_Certificate.wszTitleName)); @@ -297,10 +305,7 @@ Xbe::Xbe(class Exe *x_Exe, const char *x_szTitle, bool x_bRetail, const std::vec XBEIMAGE_MEDIA_TYPE_MEDIA_BOARD | XBEIMAGE_MEDIA_TYPE_NONSECURE_HARD_DISK | XBEIMAGE_MEDIA_TYPE_NONSECURE_MODE; - // TODO: allow configuration - m_Certificate.dwGameRegion = XBEIMAGE_GAME_REGION_MANUFACTURING | - XBEIMAGE_GAME_REGION_NA | XBEIMAGE_GAME_REGION_JAPAN | - XBEIMAGE_GAME_REGION_RESTOFWORLD; + m_Certificate.dwGameRegion = x_dwRegions; // TODO: allow configuration m_Certificate.dwGameRatings = 0xFFFFFFFF; @@ -308,8 +313,7 @@ Xbe::Xbe(class Exe *x_Exe, const char *x_szTitle, bool x_bRetail, const std::vec // always disk 0, AFAIK m_Certificate.dwDiskNumber = 0; - // TODO: allow configuration - m_Certificate.dwVersion = 0; + m_Certificate.dwVersion = x_dwVersion; // generate blank LAN, signature, and alternate signature keys { @@ -345,7 +349,7 @@ Xbe::Xbe(class Exe *x_Exe, const char *x_szTitle, bool x_bRetail, const std::vec // write section headers / section names { - m_szSectionName = new char[m_Header.dwSections][9]; + m_szSectionName = new char[m_Header.dwSections][256]; m_SectionHeader = new SectionHeader[m_Header.dwSections]; @@ -373,14 +377,31 @@ Xbe::Xbe(class Exe *x_Exe, const char *x_szTitle, bool x_bRetail, const std::vec memset(&m_SectionHeader[v].dwFlags, 0, sizeof(m_SectionHeader->dwFlags)); - if(characteristics & IMAGE_SCN_MEM_WRITE) - m_SectionHeader[v].dwFlags.bWritable = true; - - if((characteristics & IMAGE_SCN_MEM_EXECUTE) || - (characteristics & IMAGE_SCN_CNT_CODE)) - m_SectionHeader[v].dwFlags.bExecutable = true; + // check for $$XTIMAGE or $$XSIMAGE and set the correct flags + if(x_Exe->m_SectionHeader_longname[v].m_longname && + (!strcmp(x_Exe->m_SectionHeader_longname[v].m_longname, "$$XTIMAGE") || + !strcmp(x_Exe->m_SectionHeader_longname[v].m_longname, "$$XSIMAGE"))) + { + m_SectionHeader[v].dwFlags.bInsertedFile = true; + m_SectionHeader[v].dwFlags.bHeadPageRO = true; + m_SectionHeader[v].dwFlags.bTailPageRO = true; + } + else + { + if(characteristics & IMAGE_SCN_MEM_WRITE) + m_SectionHeader[v].dwFlags.bWritable = true; + + if((characteristics & IMAGE_SCN_MEM_EXECUTE) || + (characteristics & IMAGE_SCN_CNT_CODE)) + m_SectionHeader[v].dwFlags.bExecutable = true; + + char *name = (x_Exe->m_SectionHeader_longname[v].m_longname) + ? x_Exe->m_SectionHeader_longname[v].m_longname + : (char *)x_Exe->m_SectionHeader[v].m_name; + m_SectionHeader[v].dwFlags.bPreload = + (strcmp(name, ".debug") && strncmp(name, ".debug_", 7)); + } - m_SectionHeader[v].dwFlags.bPreload = true; m_SectionHeader[v].dwVirtualAddr = x_Exe->m_SectionHeader[v].m_virtual_addr + m_Header.dwPeBaseAddr; @@ -431,10 +452,21 @@ Xbe::Xbe(class Exe *x_Exe, const char *x_szTitle, bool x_bRetail, const std::vec memset(secn, 0, 8); m_SectionHeader[v].dwSectionNameAddr = hwc_secn; - while(s < 8 && x_Exe->m_SectionHeader[v].m_name[s] != '\0') + if(x_Exe->m_SectionHeader_longname[v].m_longname) { - m_szSectionName[v][s] = secn[s] = x_Exe->m_SectionHeader[v].m_name[s]; - s++; + while(s < 255 && x_Exe->m_SectionHeader_longname[v].m_longname[s] != '\0') + { + m_szSectionName[v][s] = secn[s] = x_Exe->m_SectionHeader_longname[v].m_longname[s]; + s++; + } + } + else + { + while(s < 8 && x_Exe->m_SectionHeader[v].m_name[s] != '\0') + { + m_szSectionName[v][s] = secn[s] = x_Exe->m_SectionHeader[v].m_name[s]; + s++; + } } m_szSectionName[v][s] = '\0'; diff --git a/tools/cxbe/Xbe.h b/tools/cxbe/Xbe.h index fe685a0fb..fd47a203a 100644 --- a/tools/cxbe/Xbe.h +++ b/tools/cxbe/Xbe.h @@ -26,8 +26,9 @@ class Xbe : public Error { public: // construct via Exe file object - Xbe(class Exe *x_Exe, const char *x_szTitle, bool x_bRetail, - const std::vector *logo = nullptr, const char *x_szDebugPath = nullptr); + Xbe(class Exe *x_Exe, const char *x_szTitle, uint32 x_dwTitleID, uint32 x_dwRegions, + uint32 x_dwVersion, bool x_bRetail, const std::vector *logo = nullptr, + const char *x_szDebugPath = nullptr); // deconstructor ~Xbe(); @@ -170,8 +171,8 @@ class Xbe : public Error uint32 dwCharacteristics; // characteristics } __attribute((packed)) * m_TLS; - // Xbe section names, each 8 bytes max and null terminated - char (*m_szSectionName)[9]; + // Xbe section names, each 255 bytes max and null terminated + char (*m_szSectionName)[256]; // Xbe sections uint08 **m_bzSection;