diff --git a/.clang-format b/.clang-format index a00947d..d09158a 100644 --- a/.clang-format +++ b/.clang-format @@ -1,15 +1,106 @@ --- BasedOnStyle: LLVM -ColumnLimit: 130 +ColumnLimit: 120 --- Language: Cpp +IndentWidth: 4 +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +ColumnLimit: 130 +MaxEmptyLinesToKeep: 2 +AlignAfterOpenBracket: BlockIndent +AlignArrayOfStructures: Right +AlignEscapedNewlines: DontAlign +AlignOperands: AlignAfterOperator +AlignTrailingComments: + Kind: Always + OverEmptyLines: 1 +AllowShortBlocksOnASingleLine: Always +AllowShortFunctionsOnASingleLine: InlineOnly +AllowShortIfStatementsOnASingleLine: WithoutElse AlwaysBreakBeforeMultilineStrings: true -AlwaysBreakTemplateDeclarations: Yes +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: true CompactNamespaces: true -ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 2 +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: true DerivePointerAlignment: false -MaxEmptyLinesToKeep: 2 +FixNamespaceComments: true +IncludeBlocks: Preserve +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: BeforeHash +IndentWrappedFunctionNames: false +InsertBraces: true +InsertNewlineAtEOF: true +InsertTrailingCommas: None +KeepEmptyLinesAtEOF: false +KeepEmptyLinesAtTheStartOfBlocks: true +LambdaBodyIndentation: OuterScope +LineEnding: LF +NamespaceIndentation: None +PackConstructorInitializers: Never PointerAlignment: Left -SortIncludes: false +PPIndentWidth: -1 +QualifierAlignment: Left +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RemoveParentheses: Leave +RemoveSemicolon: false +SeparateDefinitionBlocks: Always +ShortNamespaceLines: 5 +SortIncludes: CaseInsensitive +SortUsingDeclarations: Lexicographic +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: false +SpaceBeforeParens: ControlStatementsExceptControlMacros +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInSquareBrackets: false +TabWidth: 4 +UseTab: Never ... diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..fd60fd2 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,47 @@ +Checks: '-*, + cppcoreguidelines-init-variables, + cppcoreguidelines-prefer-member-initializer, + misc-confusable-identifiers, + misc-const-correctness, + misc-definitions-in-headers, + misc-header-include-cycle, + misc-include-cleaner, + misc-misleading-bidirectional, + misc-misleading-identifier, + misc-misplaced-const, + misc-redundant-expression, + misc-unused-parameters, + misc-unused-using-decls, + modernize-concat-nested-namespaces, + modernize-deprecated-headers, + modernize-loop-convert, + modernize-pass-by-value, + modernize-redundant-void-arg, + modernize-shrink-to-fit, + modernize-use-auto, + modernize-use-bool-literals, + modernize-use-emplace, + modernize-use-nullptr, + modernize-use-starts-ends-with, + modernize-use-std-numbers, + performance-faster-string-find, + performance-for-range-copy, + performance-implicit-conversion-in-loop, + performance-inefficient-algorithm, + performance-inefficient-string-concatenation, + performance-inefficient-vector-operation, + readability-string-compare, + readability-braces-around-statements, + readability-identifier-length, + readability-identifier-naming, + readability-string-compare, + ' +WarningsAsErrors: true +FormatStyle: file +CheckOptions: + - key: readability-identifier-length.MinimumLoopCounterNameLength + value: 2 + - key: readability-identifier-length.MinimumParameterNameLength + value: 2 + - key: readability-identifier-length.MinimumVariableNameLength + value: 2 diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..b6983bb --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "llvm-vs-code-extensions.vscode-clangd", + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "ms-vscode.cmake-tools" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json index 06fc6a1..6b0a563 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,6 +10,15 @@ "test/data/public/rom1.sfc" ], "preLaunchTask": "${defaultBuildTask}", + }, + { + "name": "Test", + "type": "lldb", + "request": "launch", + "cwd": "${workspaceFolder}/test", + "program": "${workspaceFolder}/test/build/test", + "args": [], + "preLaunchTask": "CMake: build test", } ] } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index fbdef72..3c6a766 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,18 +1,36 @@ { "version": "2.0.0", "tasks": [ + { + "type": "cmake", + "label": "CMake: configure", + "command": "configure", + "targets": [ + "all" + ] + }, { "type": "cmake", "label": "CMake: build", "command": "build", + "dependsOn": [ + "CMake: configure" + ], "targets": [ "all" ], "group": { "kind": "build", "isDefault": true - }, - "problemMatcher": [] + } + }, + { + "type": "shell", + "label": "CMake: build test", + "command": "cmake -Bbuild && cmake --build build --parallel 4", + "options": { + "cwd": "${workspaceFolder}/test" + } } ] } diff --git a/CMakeLists.txt b/CMakeLists.txt index fc7bd3c..4797efc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,8 @@ project(superfamicheck LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) +include_directories(include) + if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Debug) endif() diff --git a/compile_flags.txt b/compile_flags.txt index 46e4acb..7629ac0 100644 --- a/compile_flags.txt +++ b/compile_flags.txt @@ -2,3 +2,5 @@ -std=c++20 -Wall -Wextra +-Iinclude +-isystem/usr/local/include diff --git a/include/.clang-format b/include/.clang-format new file mode 100644 index 0000000..0437d0c --- /dev/null +++ b/include/.clang-format @@ -0,0 +1,2 @@ +--- +"DisableFormat": true diff --git a/include/.clang-tidy b/include/.clang-tidy new file mode 100644 index 0000000..aa4dd1b --- /dev/null +++ b/include/.clang-tidy @@ -0,0 +1 @@ +Checks: '' diff --git a/src/ezOptionParser/MIT-LICENSE b/include/ezOptionParser/MIT-LICENSE similarity index 100% rename from src/ezOptionParser/MIT-LICENSE rename to include/ezOptionParser/MIT-LICENSE diff --git a/src/ezOptionParser/README.md b/include/ezOptionParser/README.md similarity index 100% rename from src/ezOptionParser/README.md rename to include/ezOptionParser/README.md diff --git a/src/ezOptionParser/ezOptionParser.hpp b/include/ezOptionParser/ezOptionParser.hpp similarity index 100% rename from src/ezOptionParser/ezOptionParser.hpp rename to include/ezOptionParser/ezOptionParser.hpp diff --git a/src/sfcRom.cpp b/src/sfcRom.cpp index cf884e8..909ed43 100644 --- a/src/sfcRom.cpp +++ b/src/sfcRom.cpp @@ -17,830 +17,780 @@ string sjisToString(uint8_t code); uint16_t getWord(const vector& vec, size_t offset); void putWord(vector& vec, size_t offset, uint16_t value); -sfcRom::sfcRom(const string& path) { - filepath = path; - int issues = 0; - - // Read file into buffer - image = vector(0); - { - ifstream file(path, ios::binary | ios::ate); - if (file) { - size_t fileSize = file.tellg(); - if ((fileSize & 0x3ff) == 0x200) { - hasCopierHeader = true; - imageOffset = 0x200; - ++issues; - } - - imageSize = fileSize - imageOffset; - if (imageSize < 0x8000 || imageSize > 0xc00000 || imageSize % 0x8000 != 0) - return; - - file.seekg(imageOffset, ios::beg); - image.resize(imageSize); - file.read((char*)&image[0], imageSize); - } else { - return; - } - } - - // Review possible header locations and pick best match - { - vector possibleHeaderLocations = {0x7fb0, 0xffb0, 0x40ffb0}; - vector> scoredHeaderLocations = {}; - - for (size_t loc : possibleHeaderLocations) { - int score = scoreHeaderLocation(loc); - if (score > -2) { - scoredHeaderLocations.emplace_back(score, loc); - } - } - - int top = INT_MIN; - for (auto p : scoredHeaderLocations) { - if (p.first >= top) { - headerLocation = p.second; - top = p.first; - } +sfcRom::sfcRom(const string& path) + : filepath(path) { + + int issues = 0; + + // Read file into buffer + image = vector(0); + { + ifstream file(path, ios::binary | ios::ate); + if (file) { + size_t fileSize = file.tellg(); + if ((fileSize & 0x3ff) == 0x200) { + hasCopierHeader = true; + imageOffset = 0x200; + ++issues; + } + + imageSize = fileSize - imageOffset; + if (imageSize < 0x8000 || imageSize > 0xc00000 || imageSize % 0x8000 != 0) { return; } + + file.seekg(imageOffset, ios::beg); + image.resize(imageSize); + file.read((char*)&image[0], imageSize); + } else { + return; + } } - if (headerLocation == 0) { - return; - } else { - isValid = true; - } - } + // Review possible header locations and pick best match + { + vector possibleHeaderLocations = {0x7fb0, 0xffb0, 0x40ffb0}; + vector> scoredLocations = {}; - // We're probably dealing with an SFC ROM image - getHeaderInfo(vector(image.begin() + headerLocation, image.begin() + headerLocation + 0x50)); + for (size_t loc : possibleHeaderLocations) { + int score = scoreHeaderLocation(loc); + if (score > -2) { scoredLocations.emplace_back(score, loc); } + } + int topScore = INT_MIN; + for (auto scoredLocation : scoredLocations) { + if (scoredLocation.first >= topScore) { + headerLocation = scoredLocation.second; + topScore = scoredLocation.first; + } + } - // Check title - { - hasCorrectTitle = true; - for (size_t i = 0; i < 21; ++i) { - if (sjisToString(image[headerLocation + 0x10 + i]).empty()) - hasCorrectTitle = false; - } - if (!hasCorrectTitle) { - ++issues; - hasSevereIssues = true; - } - } - - // Check ROM make up - { - if ((mode & 0xe0) != 0x20) { - correctedMode = headerLocation >= 0x8000 ? 0x21 : 0x20; - hasSevereIssues = true; - ++issues; - } else { - hasLegalMode = true; + if (headerLocation == 0) { return; } + isValid = true; } - if (mapperName.empty()) { - ++issues; - } else { - hasKnownMapper = true; - } - } - - // Check ROM size - { - if (imageSize > (1 << (romSize + 10)) || imageSize <= (1 << (romSize + 9))) { - uint32_t pot = imageSize; - int potN = 0; - while (pot >>= 1) { - if (pot & 1) - ++potN; - } - correctedRomSize = (potN > 1) ? 1 : 0; - uint32_t t = imageSize >> 10; - while (t >>= 1) - ++correctedRomSize; - if (correctedRomSize != romSize) - ++issues; - } - } + // We're probably dealing with an SFC ROM image + getHeaderInfo(vector(image.begin() + headerLocation, image.begin() + headerLocation + 0x50)); - // Check RAM size - { - if ((hasRam && ramSize > 0x0f) || (!hasRam && ramSize != 0)) { - ++issues; - } else { - hasCorrectRamSize = true; - } - } - - // Calculate checksum - { - correctedChecksum = calculateChecksum(); - correctedComplement = ~correctedChecksum; - if ((checksum != correctedChecksum) || complement != correctedComplement) { - ++issues; - } else { - hasCorrectChecksum = true; + // Check title + { + hasCorrectTitle = true; + for (size_t i = 0; i < 21; ++i) { + if (sjisToString(image[headerLocation + 0x10 + i]).empty()) { hasCorrectTitle = false; } + } + if (!hasCorrectTitle) { + ++issues; + hasSevereIssues = true; + } } - } - - if (issues || hasSevereIssues) { - hasIssues = true; - } -} - -string sfcRom::description(bool silent) const { - ostringstream os; - if (isValid) { - os << setfill('0') << hex; - - if (!silent) { - os << "ROM info for file \"" << filepath << "\"" - << "\n\n"; - - uint32_t headerAt = headerLocation + (hasNewFormatHeader ? 0 : 0x10); - os << " Header at 0x" << setw(4) << headerAt << '\n'; - os << " Title " << title << '\n'; - if (!gameCode.empty()) { - os << " Game code " << gameCode << '\n'; - } - os << " Maker code " << makerCode << '\n'; - os << " Country 0x" << setw(2) << static_cast(countryCode) << " (" << country << ")" << '\n'; - os << " Version " << version << '\n'; - os << '\n'; - - int romSizeKb = (int)(1 << romSize); - int imageSizeKb = (int)(imageSize >> 10); - if (romSize > 0x0f || romSize < 0x05) { - os << " ROM size BAD! (Actual size " << dec << imageSizeKb << hex << "KB)" << '\n'; - } else { - os << " ROM size 0x" << setw(2) << static_cast(romSize) << " (" << dec << romSizeKb << hex << "KB"; - if (romSizeKb != imageSizeKb) { - os << ", actual size " << dec << imageSizeKb << hex << "KB)" << '\n'; + // Check ROM make up + { + if ((mode & 0xe0) != 0x20) { + correctedMode = headerLocation >= 0x8000 ? 0x21 : 0x20; + hasSevereIssues = true; + ++issues; } else { - os << ")" << '\n'; + hasLegalMode = true; } - } - if (hasRam) { - if (ramSize > 0x0d) { - os << " RAM size BAD! (0x" << setw(2) << static_cast(ramSize) << ")" << '\n'; + if (mapperName.empty()) { + ++issues; } else { - os << " RAM size 0x" << setw(2) << static_cast(ramSize); - os << " (" << dec << (int)(1 << (ramSize)) << hex << "KB)" << '\n'; + hasKnownMapper = true; } - } + } - if (hasLegalMode) { - os << " Map mode 0x" << setw(2) << static_cast(mapper); - if (mapperName.empty()) { - os << '\n'; - } else { - os << " (" << mapperName << ")" << '\n'; + // Check ROM size + { + if (imageSize > (1 << (romSize + 10)) || imageSize <= (1 << (romSize + 9))) { + uint32_t pot = imageSize; + int potN = 0; + while (pot >>= 1) { + if (pot & 1) { ++potN; } + } + correctedRomSize = (potN > 1) ? 1 : 0; + uint32_t sizeBits = imageSize >> 10; + while (sizeBits >>= 1) { + ++correctedRomSize; + } + if (correctedRomSize != romSize) { ++issues; } } - } else { - os << " Map mode BAD! (ROM makeup 0x" << setw(2) << static_cast(mode) << ")" << '\n'; - } - - os << " Chipset 0x" << setw(2) << static_cast(chipset); - if (chipsetSubtype) { - os << "/" << setw(2) << static_cast(chipsetSubtype); - } - if (chipSetInfo.empty()) { - os << '\n'; - } else { - os << " (" << chipSetInfo << ")" << '\n'; - } - - os << " Speed " << (fast ? "120ns" : "200ns") << '\n'; - os << '\n'; - - os << " Checksum 0x" << setw(4) << checksum << '\n'; - os << " Complement 0x" << setw(4) << complement << '\n'; - - os << '\n'; } - if (hasIssues) { - if (silent) { - os << "Issues with \"" << filepath << "\":" << '\n'; - } else { - if (hasSevereIssues) { - os << "Severe issues were found:" << '\n'; + // Check RAM size + { + if ((hasRam && ramSize > 0x0f) || (!hasRam && ramSize != 0)) { + ++issues; } else { - os << "The following issues were found:" << '\n'; + hasCorrectRamSize = true; } - } - - if (hasCorrectTitle == false) { - os << " ROM title contains illegal characters" << '\n'; - } - if (!hasLegalMode) { - os << " ROM makeup 0x" << setw(2) << static_cast(mode) << " is not allowed"; - os << ", best guess is 0x" << setw(2) << static_cast(correctedMode) << '\n'; - } else if (!hasKnownMapper) { - os << " ROM makeup 0x" << setw(2) << static_cast(mode) << " is an unknown type" << '\n'; - } - if (correctedRomSize && romSize != correctedRomSize) { - os << " ROM size should be 0x" << setw(2) << static_cast(correctedRomSize) << '\n'; - } - if (!hasCorrectRamSize) { - if (hasRam) { - os << " RAM size specified too large" << '\n'; + } + + // Calculate checksum + { + correctedChecksum = calculateChecksum(); + correctedComplement = ~correctedChecksum; + if ((checksum != correctedChecksum) || complement != correctedComplement) { + ++issues; } else { - os << " RAM size should be 0x00" << '\n'; + hasCorrectChecksum = true; } - } - if (!hasCorrectChecksum) { - os << " Checksum/complement should be 0x" << setw(4) << correctedChecksum << "/0x" << setw(4) << correctedComplement - << '\n'; - } - if (hasCopierHeader) { - os << " File has a copier header (0x200 bytes)" << '\n'; - } - if (!silent) - os << '\n'; } - } else { - os << "File \"" << filepath << "\" is not an SFC ROM image" << '\n'; - } - return os.str(); + if (issues || hasSevereIssues) { hasIssues = true; } } +string sfcRom::description(bool silent) const { + ostringstream os; + if (isValid) { + os << setfill('0') << hex; + + if (!silent) { + os << "ROM info for file \"" << filepath << "\"" + << "\n\n"; + + uint32_t headerAt = headerLocation + (hasNewFormatHeader ? 0 : 0x10); + os << " Header at 0x" << setw(4) << headerAt << '\n'; + os << " Title " << title << '\n'; + if (!gameCode.empty()) { os << " Game code " << gameCode << '\n'; } + os << " Maker code " << makerCode << '\n'; + os << " Country 0x" << setw(2) << static_cast(countryCode) << " (" << country << ")" << '\n'; + os << " Version " << version << '\n'; + os << '\n'; + + int romSizeKb = (int)(1 << romSize); + int imageSizeKb = (int)(imageSize >> 10); + if (romSize > 0x0f || romSize < 0x05) { + os << " ROM size BAD! (Actual size " << dec << imageSizeKb << hex << "KB)" << '\n'; + } else { + os << " ROM size 0x" << setw(2) << static_cast(romSize) << " (" << dec << romSizeKb << hex << "KB"; + if (romSizeKb != imageSizeKb) { + os << ", actual size " << dec << imageSizeKb << hex << "KB)" << '\n'; + } else { + os << ")" << '\n'; + } + } + + if (hasRam) { + if (ramSize > 0x0d) { + os << " RAM size BAD! (0x" << setw(2) << static_cast(ramSize) << ")" << '\n'; + } else { + os << " RAM size 0x" << setw(2) << static_cast(ramSize); + os << " (" << dec << (int)(1 << (ramSize)) << hex << "KB)" << '\n'; + } + } + + if (hasLegalMode) { + os << " Map mode 0x" << setw(2) << static_cast(mapper); + if (mapperName.empty()) { + os << '\n'; + } else { + os << " (" << mapperName << ")" << '\n'; + } + } else { + os << " Map mode BAD! (ROM makeup 0x" << setw(2) << static_cast(mode) << ")" << '\n'; + } + + os << " Chipset 0x" << setw(2) << static_cast(chipset); + if (chipsetSubtype) { os << "/" << setw(2) << static_cast(chipsetSubtype); } + if (chipSetInfo.empty()) { + os << '\n'; + } else { + os << " (" << chipSetInfo << ")" << '\n'; + } + + os << " Speed " << (fast ? "120ns" : "200ns") << '\n'; + os << '\n'; + + os << " Checksum 0x" << setw(4) << checksum << '\n'; + os << " Complement 0x" << setw(4) << complement << '\n'; + + os << '\n'; + } + + if (hasIssues) { + if (silent) { + os << "Issues with \"" << filepath << "\":" << '\n'; + } else { + if (hasSevereIssues) { + os << "Severe issues were found:" << '\n'; + } else { + os << "The following issues were found:" << '\n'; + } + } + + if (hasCorrectTitle == false) { os << " ROM title contains illegal characters" << '\n'; } + if (!hasLegalMode) { + os << " ROM makeup 0x" << setw(2) << static_cast(mode) << " is not allowed"; + os << ", best guess is 0x" << setw(2) << static_cast(correctedMode) << '\n'; + } else if (!hasKnownMapper) { + os << " ROM makeup 0x" << setw(2) << static_cast(mode) << " is an unknown type" << '\n'; + } + if (correctedRomSize && romSize != correctedRomSize) { + os << " ROM size should be 0x" << setw(2) << static_cast(correctedRomSize) << '\n'; + } + if (!hasCorrectRamSize) { + if (hasRam) { + os << " RAM size specified too large" << '\n'; + } else { + os << " RAM size should be 0x00" << '\n'; + } + } + if (!hasCorrectChecksum) { + os << " Checksum/complement should be 0x" << setw(4) << correctedChecksum << "/0x" << setw(4) + << correctedComplement << '\n'; + } + if (hasCopierHeader) { os << " File has a copier header (0x200 bytes)" << '\n'; } + if (!silent) { os << '\n'; } + } + + } else { + os << "File \"" << filepath << "\" is not an SFC ROM image" << '\n'; + } + return os.str(); +} string sfcRom::fix(const string& path, bool silent) { - if (!isValid) - return string(); - ostringstream os; + if (!isValid) { return string(); } + + ostringstream os; - int fixedIssues = 0; - if (checksum != correctedChecksum || complement != correctedComplement) - ++fixedIssues; + int fixedIssues = 0; + if (checksum != correctedChecksum || complement != correctedComplement) { ++fixedIssues; } - if (!silent) { - os << "Writing ROM image to file \"" << path << "\"" << '\n'; - } + if (!silent) { os << "Writing ROM image to file \"" << path << "\"" << '\n'; } - if (hasCopierHeader) { - if (!silent) { - os << " Removed copier header" << '\n'; + if (hasCopierHeader) { + if (!silent) { os << " Removed copier header" << '\n'; } } - } - if (!hasCorrectTitle) { - // TODO - } + if (!hasCorrectTitle) { + // TODO + } - if (!hasLegalMode) { - image[headerLocation + 0x25] = correctedMode; - ++fixedIssues; - if (!silent) { - os << " Fixed ROM makeup" << '\n'; + if (!hasLegalMode) { + image[headerLocation + 0x25] = correctedMode; + ++fixedIssues; + if (!silent) { os << " Fixed ROM makeup" << '\n'; } } - } - if (correctedRomSize && romSize != correctedRomSize) { - image[headerLocation + 0x27] = correctedRomSize; - ++fixedIssues; - if (!silent) { - os << " Fixed ROM size" << '\n'; + if (correctedRomSize && romSize != correctedRomSize) { + image[headerLocation + 0x27] = correctedRomSize; + ++fixedIssues; + if (!silent) { os << " Fixed ROM size" << '\n'; } } - } - - if (fixedIssues) { - correctedChecksum = calculateChecksum(); - correctedComplement = ~correctedChecksum; - putWord(image, headerLocation + 0x2c, correctedComplement); - putWord(image, headerLocation + 0x2e, correctedChecksum); - if (!silent) { - os << " Fixed checksum" << '\n'; + + if (fixedIssues) { + correctedChecksum = calculateChecksum(); + correctedComplement = ~correctedChecksum; + putWord(image, headerLocation + 0x2c, correctedComplement); + putWord(image, headerLocation + 0x2e, correctedChecksum); + if (!silent) { os << " Fixed checksum" << '\n'; } } - } - if (fixedIssues || hasCopierHeader || path != filepath) { - ofstream file(path, ios::binary | ios::trunc); - if (file && file.good()) { - file.write((char*)&image[0], image.size() * sizeof(uint8_t)); + if (fixedIssues || hasCopierHeader || path != filepath) { + ofstream file(path, ios::binary | ios::trunc); + if (file && file.good()) { + file.write((char*)&image[0], image.size() * sizeof(uint8_t)); + } else { + ostringstream fail; + fail << "Cannot open file \"" << path << "\" for writing" << '\n'; + return fail.str(); + } } else { - ostringstream fail; - fail << "Cannot open file \"" << path << "\" for writing" << '\n'; - return fail.str(); + return string(); } - } else { - return string(); - } - - if (!silent) { - os << '\n'; - } - return os.str(); + + if (!silent) { os << '\n'; } + return os.str(); } int sfcRom::scoreHeaderLocation(size_t loc) const { - if (image.size() < loc + 0x50) - return -100; - int score = 0; - vector header = vector(image.begin() + loc, image.begin() + loc + 0x50); - uint16_t reset = getWord(header, 0x4c); - - // If 32K/bank mapper, reset vector must point to upper half - if ((loc & 0xffff) < 0x8000) { - if (reset < 0x8000) { - return -100; - } else { - score += 1; - reset -= 0x8000; + if (image.size() < loc + 0x50) { return -100; } + + int score = 0; + vector header = vector(image.begin() + loc, image.begin() + loc + 0x50); + uint16_t reset = getWord(header, 0x4c); + + // If 32K/bank mapper, reset vector must point to upper half + if ((loc & 0xffff) < 0x8000) { + if (reset < 0x8000) { + return -100; + } else { + score += 1; + reset -= 0x8000; + } } - } - - // Correct rom makeup byte? - { - uint8_t s_mode = header[0x25]; - uint8_t s_mapper = mode & 0x0f; - if (((s_mode & 0xe0) == 0x20) && - (s_mapper == 0x0 || s_mapper == 0x1 || s_mapper == 0x2 || s_mapper == 0x3 || s_mapper == 0x5 || s_mapper == 0xa)) { - score += 2; + + // Correct rom makeup byte? + { + uint8_t s_mode = header[0x25]; + uint8_t s_mapper = mode & 0x0f; + if (((s_mode & 0xe0) == 0x20) && + (s_mapper == 0x0 || s_mapper == 0x1 || s_mapper == 0x2 || s_mapper == 0x3 || s_mapper == 0x5 || s_mapper == 0xa)) { + score += 2; + } + } + + // Correct ROM & RAM size bytes? + { + if (header[0x27] >= 0x05 && header[0x27] <= 0x0f) { score += 2; } + if (header[0x28] >= 0x0a) { score += 1; } + } + + // Proper title characters? + { + int validChars = 0; + for (int i = 0; i < 21; ++i) { + if (!sjisToString(header[0x10 + i]).empty()) { ++validChars; } + } + if (validChars == 21) { score += 2; } } - } - - // Correct ROM & RAM size bytes? - { - if (header[0x27] >= 0x05 && header[0x27] <= 0x0f) - score += 2; - if (header[0x28] >= 0x0a) - score += 1; - } - - // Proper title characters? - { - int validChars = 0; - for (int i = 0; i < 21; ++i) { - if (!sjisToString(header[0x10 + i]).empty()) - ++validChars; + + // Reasonable reset opcode? + if (validResetOpcode(image[reset])) { + score += 2; + } else { + score -= 4; } - if (validChars == 21) - score += 2; - } - - // Reasonable reset opcode? - if (validResetOpcode(image[reset])) { - score += 2; - } else { - score -= 4; - } - - return score; + + return score; } void sfcRom::getHeaderInfo(const vector& header) { - mode = header[0x25]; - mapper = mode & 0x0f; - fast = mode & 0x10; - - hasNewFormatHeader = (header[0x2a] == 0x33); - - chipset = header[0x26]; - if (hasNewFormatHeader) { - chipsetSubtype = header[0x0f]; - } - romSize = header[0x27]; - ramSize = header[0x28]; - countryCode = header[0x29]; - - complement = getWord(header, 0x2c); - checksum = getWord(header, 0x2e); - - title = ""; - for (int i = 0x10; i < 0x10 + 21; ++i) { - title += sjisToString(header[i]); - } - - switch (mapper) { - case 0x0: - mapperName = "LoROM"; - break; - case 0x1: - mapperName = "HiROM"; - break; - case 0x2: - mapperName = "LoROM/S-DD1"; - break; - case 0x3: - mapperName = "LoROM/SA-1"; - break; - case 0x5: - mapperName = "Extended HiROM"; - break; - case 0xa: - mapperName = "Extended HiROM/SPC7110"; - break; - default: - mapperName = string(); - break; - } - - if (hasNewFormatHeader) { - makerCode = string((char*)&header[0x00], 2); - gameCode = string((char*)&header[0x02], 4); - } else { - ostringstream os; - os << "0x" << setfill('0') << setw(2) << hex << static_cast((uint8_t)header[0x2a]); - makerCode = os.str(); - gameCode = string(); - } - - chipSetInfo = string(); - switch (chipset) { - case 0x00: - chipSetInfo = "ROM"; - break; - case 0x01: - chipSetInfo = "ROM, RAM"; - hasRam = true; - break; - case 0x02: - chipSetInfo = "ROM, RAM, Battery"; - hasRam = true; - break; - case 0x03: - chipSetInfo = "ROM, DSP"; - break; - case 0x04: - chipSetInfo = "ROM, RAM, DSP"; - hasRam = true; - break; - case 0x05: - chipSetInfo = "ROM, RAM, DSP, Battery"; - hasRam = true; - break; - case 0x13: - chipSetInfo = "ROM, EXPRAM, MARIO CHIP 1"; - break; - case 0x25: - chipSetInfo = "ROM, RAM, OBC-1, Battery"; - hasRam = true; - break; - case 0x32: - chipSetInfo = "ROM, RAM, SA-1, Battery"; - hasRam = true; - break; - case 0x34: - chipSetInfo = "ROM, RAM, SA-1"; - hasRam = true; - break; - case 0x35: - chipSetInfo = "ROM, RAM, SA-1, Battery"; - hasRam = true; - break; - case 0x36: - chipSetInfo = "ROM, SA-1, Battery"; - break; - case 0x43: - chipSetInfo = "ROM, S-DD1"; - break; - case 0x45: - chipSetInfo = "ROM, RAM, S-DD1, Battery"; - hasRam = true; - break; - case 0x55: - chipSetInfo = "ROM, RAM, S-RTC, Battery"; - hasRam = true; - break; - case 0xe3: - chipSetInfo = "ROM, SGB"; - break; - case 0xe5: - chipSetInfo = "ROM, BS-X"; - break; - - case 0x14: - chipSetInfo = romSize > 0x0a ? "ROM, RAM, GSU-2" : "ROM, RAM, GSU-1"; - hasRam = true; - break; - case 0x15: - chipSetInfo = romSize > 0x0a ? "ROM, RAM, GSU-2, Battery" : "ROM, RAM, GSU-1, Battery"; - hasRam = true; - break; - case 0x1a: - chipSetInfo = "ROM, RAM, GSU-2-SP1, Battery"; - hasRam = true; - break; - - case 0xf3: - if (chipsetSubtype == 0x10) - chipSetInfo = "ROM, CX4"; - break; - case 0xf5: - if (chipsetSubtype == 0x00) - chipSetInfo = "ROM, RAM, SPC7110, Battery"; - if (chipsetSubtype == 0x02) - chipSetInfo = "ROM, RAM, ST-018, Battery"; - hasRam = true; - break; - case 0xf6: - if (chipsetSubtype == 0x01) - chipSetInfo = "ROM, ST-010/011, Battery"; - break; - case 0xf9: - if (chipsetSubtype == 0x00) - chipSetInfo = "ROM, RAM, SPC7110, RTC, Battery"; - hasRam = true; - break; - - default: - break; - } - - if (gameCode == "XBND") { - chipSetInfo = "ROM, RAM, Battery, XBand Modem"; - hasRam = true; - } - if (gameCode == "MENU") { - chipSetInfo = "ROM, RAM, Battery, MX15001TFC"; - hasRam = true; - } - - { - ostringstream os; - os << "1." << static_cast((uint8_t)header[0x2b]); - version = os.str(); - } - - switch (countryCode) { - case 0x00: - country = "Japan"; - break; - case 0x01: - country = "USA"; - break; - case 0x02: - country = "Europe"; - break; - case 0x03: - country = "Sweden"; - break; - case 0x04: - country = "Finland"; - break; - case 0x05: - country = "Denmark"; - break; - case 0x06: - country = "France"; - break; - case 0x07: - country = "Holland"; - break; - case 0x08: - country = "Spain"; - break; - case 0x09: - country = "Germany"; - break; - case 0x0a: - country = "Italy"; - break; - case 0x0b: - country = "China/Hong Kong"; - break; - case 0x0c: - country = "Indonesia"; - break; - case 0x0d: - country = "South Korea"; - break; - case 0x0f: - country = "Canada"; - break; - case 0x10: - country = "Brazil"; - break; - case 0x11: - country = "Australia"; - break; - default: - country = "Unknown"; - break; - } + mode = header[0x25]; + mapper = mode & 0x0f; + fast = mode & 0x10; + hasNewFormatHeader = (header[0x2a] == 0x33); + + chipset = header[0x26]; + if (hasNewFormatHeader) { chipsetSubtype = header[0x0f]; } + romSize = header[0x27]; + ramSize = header[0x28]; + countryCode = header[0x29]; + + complement = getWord(header, 0x2c); + checksum = getWord(header, 0x2e); + + title = ""; + for (int i = 0x10; i < 0x10 + 21; ++i) { + title += sjisToString(header[i]); + } + + switch (mapper) { + case 0x0: + mapperName = "LoROM"; + break; + case 0x1: + mapperName = "HiROM"; + break; + case 0x2: + mapperName = "LoROM/S-DD1"; + break; + case 0x3: + mapperName = "LoROM/SA-1"; + break; + case 0x5: + mapperName = "Extended HiROM"; + break; + case 0xa: + mapperName = "Extended HiROM/SPC7110"; + break; + default: + mapperName = string(); + break; + } + + if (hasNewFormatHeader) { + makerCode = string((char*)&header[0x00], 2); + gameCode = string((char*)&header[0x02], 4); + } else { + ostringstream os; + os << "0x" << setfill('0') << setw(2) << hex << static_cast((uint8_t)header[0x2a]); + makerCode = os.str(); + gameCode = string(); + } + + chipSetInfo = string(); + switch (chipset) { + case 0x00: + chipSetInfo = "ROM"; + break; + case 0x01: + chipSetInfo = "ROM, RAM"; + hasRam = true; + break; + case 0x02: + chipSetInfo = "ROM, RAM, Battery"; + hasRam = true; + break; + case 0x03: + chipSetInfo = "ROM, DSP"; + break; + case 0x04: + chipSetInfo = "ROM, RAM, DSP"; + hasRam = true; + break; + case 0x05: + chipSetInfo = "ROM, RAM, DSP, Battery"; + hasRam = true; + break; + case 0x13: + chipSetInfo = "ROM, EXPRAM, MARIO CHIP 1"; + break; + case 0x25: + chipSetInfo = "ROM, RAM, OBC-1, Battery"; + hasRam = true; + break; + case 0x32: + chipSetInfo = "ROM, RAM, SA-1, Battery"; + hasRam = true; + break; + case 0x34: + chipSetInfo = "ROM, RAM, SA-1"; + hasRam = true; + break; + case 0x35: + chipSetInfo = "ROM, RAM, SA-1, Battery"; + hasRam = true; + break; + case 0x36: + chipSetInfo = "ROM, SA-1, Battery"; + break; + case 0x43: + chipSetInfo = "ROM, S-DD1"; + break; + case 0x45: + chipSetInfo = "ROM, RAM, S-DD1, Battery"; + hasRam = true; + break; + case 0x55: + chipSetInfo = "ROM, RAM, S-RTC, Battery"; + hasRam = true; + break; + case 0xe3: + chipSetInfo = "ROM, SGB"; + break; + case 0xe5: + chipSetInfo = "ROM, BS-X"; + break; + + case 0x14: + chipSetInfo = romSize > 0x0a ? "ROM, RAM, GSU-2" : "ROM, RAM, GSU-1"; + hasRam = true; + break; + case 0x15: + chipSetInfo = romSize > 0x0a ? "ROM, RAM, GSU-2, Battery" : "ROM, RAM, GSU-1, Battery"; + hasRam = true; + break; + case 0x1a: + chipSetInfo = "ROM, RAM, GSU-2-SP1, Battery"; + hasRam = true; + break; + + case 0xf3: + if (chipsetSubtype == 0x10) { chipSetInfo = "ROM, CX4"; } + break; + case 0xf5: + if (chipsetSubtype == 0x00) { chipSetInfo = "ROM, RAM, SPC7110, Battery"; } + if (chipsetSubtype == 0x02) { chipSetInfo = "ROM, RAM, ST-018, Battery"; } + hasRam = true; + break; + case 0xf6: + if (chipsetSubtype == 0x01) { chipSetInfo = "ROM, ST-010/011, Battery"; } + break; + case 0xf9: + if (chipsetSubtype == 0x00) { chipSetInfo = "ROM, RAM, SPC7110, RTC, Battery"; } + hasRam = true; + break; + + default: + break; + } + + if (gameCode == "XBND") { + chipSetInfo = "ROM, RAM, Battery, XBand Modem"; + hasRam = true; + } + if (gameCode == "MENU") { + chipSetInfo = "ROM, RAM, Battery, MX15001TFC"; + hasRam = true; + } + + { + ostringstream os; + os << "1." << static_cast((uint8_t)header[0x2b]); + version = os.str(); + } + + switch (countryCode) { + case 0x00: + country = "Japan"; + break; + case 0x01: + country = "USA"; + break; + case 0x02: + country = "Europe"; + break; + case 0x03: + country = "Sweden"; + break; + case 0x04: + country = "Finland"; + break; + case 0x05: + country = "Denmark"; + break; + case 0x06: + country = "France"; + break; + case 0x07: + country = "Holland"; + break; + case 0x08: + country = "Spain"; + break; + case 0x09: + country = "Germany"; + break; + case 0x0a: + country = "Italy"; + break; + case 0x0b: + country = "China/Hong Kong"; + break; + case 0x0c: + country = "Indonesia"; + break; + case 0x0d: + country = "South Korea"; + break; + case 0x0f: + country = "Canada"; + break; + case 0x10: + country = "Brazil"; + break; + case 0x11: + country = "Australia"; + break; + default: + country = "Unknown"; + break; + } } uint16_t sfcRom::calculateChecksum() const { - vector img = image; - putWord(img, headerLocation + 0x2c, 0xffff); - putWord(img, headerLocation + 0x2e, 0x0000); - - size_t imageSize = img.size(); - size_t mappedSize; - - if (mapper == 0x0a && chipset == 0xf9 && chipsetSubtype == 0x00) { - // Extended HiROM/SPC7110+RTC+Battery - mappedSize = imageSize; - } else if (mapper == 0x0a && chipset == 0xf5 && chipsetSubtype == 0x00) { - // Extended HiROM/SPC7110+Battery - mappedSize = imageSize > 0x200000 ? imageSize << 1 : imageSize; - while (mappedSize > img.size()) { - size_t remaining = mappedSize - img.size(); - if (remaining > image.size()) { - remaining = image.size(); - } - if ((remaining + img.size()) > mappedSize) { - remaining = mappedSize - img.size(); - } - vector mirror(img.begin(), img.begin() + remaining); - img.insert(img.end(), mirror.begin(), mirror.end()); + vector img = image; + putWord(img, headerLocation + 0x2c, 0xffff); + putWord(img, headerLocation + 0x2e, 0x0000); + + size_t imageSize = img.size(); + size_t mappedSize = 0; + + if (mapper == 0x0a && chipset == 0xf9 && chipsetSubtype == 0x00) { + // Extended HiROM/SPC7110+RTC+Battery + mappedSize = imageSize; + } else if (mapper == 0x0a && chipset == 0xf5 && chipsetSubtype == 0x00) { + // Extended HiROM/SPC7110+Battery + mappedSize = imageSize > 0x200000 ? imageSize << 1 : imageSize; + while (mappedSize > img.size()) { + size_t remaining = mappedSize - img.size(); + if (remaining > image.size()) { remaining = image.size(); } + if ((remaining + img.size()) > mappedSize) { remaining = mappedSize - img.size(); } + vector mirror(img.begin(), img.begin() + remaining); + img.insert(img.end(), mirror.begin(), mirror.end()); + } + } else { + // Standard mapping + mappedSize = 1 << (correctedRomSize != 0 ? correctedRomSize + 10 : romSize + 10); + while (mappedSize > img.size()) { + vector mirror(img.begin() + (mappedSize >> 1), img.end()); + img.insert(img.end(), mirror.begin(), mirror.end()); + } } - } else { - // Standard mapping - mappedSize = 1 << (correctedRomSize != 0 ? correctedRomSize + 10 : romSize + 10); - while (mappedSize > img.size()) { - vector mirror(img.begin() + (mappedSize >> 1), img.end()); - img.insert(img.end(), mirror.begin(), mirror.end()); + + uint16_t sum = 0; + size_t length = img.size(); + for (size_t offset = 0; offset < length; ++offset) { + sum += img[offset]; } - } - - uint16_t sum = 0; - size_t length = img.size(); - for (size_t offset = 0; offset < length; ++offset) { - sum += img[offset]; - } - return sum; + return sum; } // Get little endian word uint16_t getWord(const vector& vec, size_t offset) { - return (uint16_t)((vec[offset]) + ((uint8_t)(vec[offset + 1]) << 8)); + return (uint16_t)((vec[offset]) + ((uint8_t)(vec[offset + 1]) << 8)); } // Put little endian word void putWord(vector& vec, size_t offset, uint16_t value) { - vec[offset] = (uint8_t)(value & 0xff); - vec[offset + 1] = (uint8_t)(value >> 8); + vec[offset] = (uint8_t)(value & 0xff); + vec[offset + 1] = (uint8_t)(value >> 8); } // Opcodes used on reset bool validResetOpcode(uint8_t op) { - switch (op) { - case 0x18: // clc - case 0x38: // sec - case 0x4c: // jmp abs - case 0x5c: // jml abs - case 0x78: // sei - case 0x80: // bra rel - case 0x9c: // stz abs - case 0xa0: // ldy #imm - case 0xa9: // lda #imm - case 0xc2: // rep - case 0xd4: // pei (zp) - case 0xd8: // cld - case 0xdc: // jmp [abs long] - case 0xe2: // sep - case 0xe6: // inc zp - case 0xea: // nop - return true; - default: - return false; - } + switch (op) { + case 0x18: // clc + case 0x38: // sec + case 0x4c: // jmp abs + case 0x5c: // jml abs + case 0x78: // sei + case 0x80: // bra rel + case 0x9c: // stz abs + case 0xa0: // ldy #imm + case 0xa9: // lda #imm + case 0xc2: // rep + case 0xd4: // pei (zp) + case 0xd8: // cld + case 0xdc: // jmp [abs long] + case 0xe2: // sep + case 0xe6: // inc zp + case 0xea: // nop + return true; + default: + return false; + } } // SJIS subset used in SNES header string sjisToString(uint8_t code) { - if (code >= 0x20 && code <= 0x7e) { - return string(1, static_cast(code)); - } else { - switch (code) { - case 0xa1: - return "\uff61"; - case 0xa2: - return "\uff62"; - case 0xa3: - return "\uff63"; - case 0xa4: - return "\uff64"; - case 0xa5: - return "\uff65"; - case 0xa6: - return "\uff66"; - case 0xa7: - return "\uff67"; - case 0xa8: - return "\uff68"; - case 0xa9: - return "\uff69"; - case 0xaa: - return "\uff6a"; - case 0xab: - return "\uff6b"; - case 0xac: - return "\uff6c"; - case 0xad: - return "\uff6d"; - case 0xae: - return "\uff6e"; - case 0xaf: - return "\uff6f"; - - case 0xb0: - return "\uff70"; - case 0xb1: - return "\uff71"; - case 0xb2: - return "\uff72"; - case 0xb3: - return "\uff73"; - case 0xb4: - return "\uff74"; - case 0xb5: - return "\uff75"; - case 0xb6: - return "\uff76"; - case 0xb7: - return "\uff77"; - case 0xb8: - return "\uff78"; - case 0xb9: - return "\uff79"; - case 0xba: - return "\uff7a"; - case 0xbb: - return "\uff7b"; - case 0xbc: - return "\uff7c"; - case 0xbd: - return "\uff7d"; - case 0xbe: - return "\uff7e"; - case 0xbf: - return "\uff7f"; - - case 0xc0: - return "\uff80"; - case 0xc1: - return "\uff81"; - case 0xc2: - return "\uff82"; - case 0xc3: - return "\uff83"; - case 0xc4: - return "\uff84"; - case 0xc5: - return "\uff85"; - case 0xc6: - return "\uff86"; - case 0xc7: - return "\uff87"; - case 0xc8: - return "\uff88"; - case 0xc9: - return "\uff89"; - case 0xca: - return "\uff8a"; - case 0xcb: - return "\uff8b"; - case 0xcc: - return "\uff8c"; - case 0xcd: - return "\uff8d"; - case 0xce: - return "\uff8e"; - case 0xcf: - return "\uff8f"; - - case 0xd0: - return "\uff90"; - case 0xd1: - return "\uff91"; - case 0xd2: - return "\uff92"; - case 0xd3: - return "\uff93"; - case 0xd4: - return "\uff94"; - case 0xd5: - return "\uff95"; - case 0xd6: - return "\uff96"; - case 0xd7: - return "\uff97"; - case 0xd8: - return "\uff98"; - case 0xd9: - return "\uff99"; - case 0xda: - return "\uff9a"; - case 0xdb: - return "\uff9b"; - case 0xdc: - return "\uff9c"; - case 0xdd: - return "\uff9d"; - case 0xde: - return "\uff9e"; - case 0xdf: - return "\uff9f"; - - default: - return string(); + if (code >= 0x20 && code <= 0x7e) { + return string(1, static_cast(code)); + } else { + switch (code) { + case 0xa1: + return "\uff61"; + case 0xa2: + return "\uff62"; + case 0xa3: + return "\uff63"; + case 0xa4: + return "\uff64"; + case 0xa5: + return "\uff65"; + case 0xa6: + return "\uff66"; + case 0xa7: + return "\uff67"; + case 0xa8: + return "\uff68"; + case 0xa9: + return "\uff69"; + case 0xaa: + return "\uff6a"; + case 0xab: + return "\uff6b"; + case 0xac: + return "\uff6c"; + case 0xad: + return "\uff6d"; + case 0xae: + return "\uff6e"; + case 0xaf: + return "\uff6f"; + + case 0xb0: + return "\uff70"; + case 0xb1: + return "\uff71"; + case 0xb2: + return "\uff72"; + case 0xb3: + return "\uff73"; + case 0xb4: + return "\uff74"; + case 0xb5: + return "\uff75"; + case 0xb6: + return "\uff76"; + case 0xb7: + return "\uff77"; + case 0xb8: + return "\uff78"; + case 0xb9: + return "\uff79"; + case 0xba: + return "\uff7a"; + case 0xbb: + return "\uff7b"; + case 0xbc: + return "\uff7c"; + case 0xbd: + return "\uff7d"; + case 0xbe: + return "\uff7e"; + case 0xbf: + return "\uff7f"; + + case 0xc0: + return "\uff80"; + case 0xc1: + return "\uff81"; + case 0xc2: + return "\uff82"; + case 0xc3: + return "\uff83"; + case 0xc4: + return "\uff84"; + case 0xc5: + return "\uff85"; + case 0xc6: + return "\uff86"; + case 0xc7: + return "\uff87"; + case 0xc8: + return "\uff88"; + case 0xc9: + return "\uff89"; + case 0xca: + return "\uff8a"; + case 0xcb: + return "\uff8b"; + case 0xcc: + return "\uff8c"; + case 0xcd: + return "\uff8d"; + case 0xce: + return "\uff8e"; + case 0xcf: + return "\uff8f"; + + case 0xd0: + return "\uff90"; + case 0xd1: + return "\uff91"; + case 0xd2: + return "\uff92"; + case 0xd3: + return "\uff93"; + case 0xd4: + return "\uff94"; + case 0xd5: + return "\uff95"; + case 0xd6: + return "\uff96"; + case 0xd7: + return "\uff97"; + case 0xd8: + return "\uff98"; + case 0xd9: + return "\uff99"; + case 0xda: + return "\uff9a"; + case 0xdb: + return "\uff9b"; + case 0xdc: + return "\uff9c"; + case 0xdd: + return "\uff9d"; + case 0xde: + return "\uff9e"; + case 0xdf: + return "\uff9f"; + + default: + return string(); + } } - } } diff --git a/src/sfcRom.hpp b/src/sfcRom.hpp index 2735f71..5bcf779 100644 --- a/src/sfcRom.hpp +++ b/src/sfcRom.hpp @@ -5,59 +5,59 @@ #include struct sfcRom { - sfcRom(const std::string& path); - - std::string description(bool silent) const; - std::string fix(const std::string& path, bool silent); - - bool isValid = false; - bool hasIssues = false; - bool hasSevereIssues = false; - - bool hasCopierHeader = false; - bool hasCorrectTitle = false; - bool hasCorrectRamSize = false; - bool hasCorrectChecksum = false; - bool hasLegalMode = false; - bool hasKnownMapper = false; - bool hasNewFormatHeader = false; - - std::string title; - std::string mapperName; - std::string chipSetInfo; - std::string makerCode; - std::string gameCode; - std::string version; - std::string country; - - uint8_t mode = 0; - uint8_t mapper = 0; - bool fast = false; - bool hasRam = false; - - uint8_t chipset = 0; - uint8_t chipsetSubtype = 0; - uint8_t romSize = 0; - uint8_t ramSize = 0; - uint8_t countryCode = 0; - - uint16_t checksum = 0; - uint16_t complement = 0; - - size_t imageSize = 0; - size_t imageOffset = 0; - size_t headerLocation = 0; - - uint8_t correctedMode = 0; - uint8_t correctedRomSize = 0; - uint16_t correctedChecksum = 0; - uint16_t correctedComplement = 0; - -private: - std::string filepath; - std::vector image; - - void getHeaderInfo(const std::vector& header); - int scoreHeaderLocation(size_t location) const; - uint16_t calculateChecksum() const; + sfcRom(const std::string& path); + + std::string description(bool silent) const; + std::string fix(const std::string& path, bool silent); + + bool isValid = false; + bool hasIssues = false; + bool hasSevereIssues = false; + + bool hasCopierHeader = false; + bool hasCorrectTitle = false; + bool hasCorrectRamSize = false; + bool hasCorrectChecksum = false; + bool hasLegalMode = false; + bool hasKnownMapper = false; + bool hasNewFormatHeader = false; + + std::string title; + std::string mapperName; + std::string chipSetInfo; + std::string makerCode; + std::string gameCode; + std::string version; + std::string country; + + uint8_t mode = 0; + uint8_t mapper = 0; + bool fast = false; + bool hasRam = false; + + uint8_t chipset = 0; + uint8_t chipsetSubtype = 0; + uint8_t romSize = 0; + uint8_t ramSize = 0; + uint8_t countryCode = 0; + + uint16_t checksum = 0; + uint16_t complement = 0; + + size_t imageSize = 0; + size_t imageOffset = 0; + size_t headerLocation = 0; + + uint8_t correctedMode = 0; + uint8_t correctedRomSize = 0; + uint16_t correctedChecksum = 0; + uint16_t correctedComplement = 0; + + private: + std::string filepath; + std::vector image; + + void getHeaderInfo(const std::vector& header); + int scoreHeaderLocation(size_t location) const; + uint16_t calculateChecksum() const; }; diff --git a/src/superfamicheck.cpp b/src/superfamicheck.cpp index e318988..2258aa1 100644 --- a/src/superfamicheck.cpp +++ b/src/superfamicheck.cpp @@ -1,119 +1,124 @@ +#include #include #include #include -#include "ezOptionParser/ezOptionParser.hpp" #include "sfcRom.hpp" using namespace std; bool fileAvailable(const std::string& path) { - ifstream file(path, ios::in); - return file.good(); + ifstream file(path, ios::in); + return file.good(); } int main(int argc, const char* argv[]) { - ez::ezOptionParser opt; - opt.overview = "SuperFamicheck 1.1.0"; - opt.syntax = "superfamicheck rom_file [options...]"; - - opt.add("", // Default - false, // Required - 0, // Number of args expected - 0, // Delimiter if expecting multiple args - "Fix ROM image", "-f", "--fix"); - - opt.add("", // Default - false, // Required - 1, // Number of args expected - 0, // Delimiter if expecting multiple args - "Output ROM image path", "-o", "--out"); - - opt.add("", // Default - false, // Required - 0, // Number of args expected - 0, // Delimiter if expecting multiple args - "Silent operation (unless issues found)", "-s", "--semisilent"); - - opt.add("", // Default - false, // Required - 0, // Number of args expected - 0, // Delimiter if expecting multiple args - "Silent operation", "-S", "--silent"); - - opt.add("", // Default - false, // Required - 0, // Number of args expected - 0, // Delimiter if expecting multiple args - "Display instructions", "-h", "--help"); - - string usage; - vector badOptions, badArgs; - opt.parse(argc, argv); - opt.getUsage(usage); - - if (opt.isSet("-h")) { - cout << usage; - return 0; - } + ez::ezOptionParser opt; + opt.overview = "SuperFamicheck 1.1.0"; + opt.syntax = "superfamicheck rom_file [options...]"; + + opt.add( + "", // Default + false, // Required + 0, // Number of args expected + 0, // Delimiter if expecting multiple args + "Fix ROM image", "-f", "--fix" + ); + + opt.add( + "", // Default + false, // Required + 1, // Number of args expected + 0, // Delimiter if expecting multiple args + "Output ROM image path", "-o", "--out" + ); + + opt.add( + "", // Default + false, // Required + 0, // Number of args expected + 0, // Delimiter if expecting multiple args + "Silent operation (unless issues found)", "-s", "--semisilent" + ); + + opt.add( + "", // Default + false, // Required + 0, // Number of args expected + 0, // Delimiter if expecting multiple args + "Silent operation", "-S", "--silent" + ); + + opt.add( + "", // Default + false, // Required + 0, // Number of args expected + 0, // Delimiter if expecting multiple args + "Display instructions", "-h", "--help" + ); + + string usage; + vector badOptions, badArgs; + opt.parse(argc, argv); + opt.getUsage(usage); + + if (opt.isSet("-h")) { + cout << usage; + return 0; + } - bool silent = opt.isSet("-s"); - bool verysilent = opt.isSet("-S"); + bool silent = opt.isSet("-s"); + bool verysilent = opt.isSet("-S"); - if (!opt.gotRequired(badOptions)) { - for (size_t i = 0; i < badOptions.size(); ++i) { - cerr << "Missing required option: " << badOptions[i] << "\n\n"; + if (!opt.gotRequired(badOptions)) { + for (const auto& badOption : badOptions) { + cerr << "Missing required option: " << badOption << "\n\n"; + } + std::cout << usage; + return 1; } - std::cout << usage; - return 1; - } - if (!opt.gotExpected(badOptions)) { - for (size_t i = 0; i < badOptions.size(); ++i) { - cerr << "Missing argument for option: " << badOptions[i] << "\n\n"; + if (!opt.gotExpected(badOptions)) { + for (const auto& badOption : badOptions) { + cerr << "Missing argument for option: " << badOption << "\n\n"; + } + std::cout << usage; + return 1; } - std::cout << usage; - return 1; - } - if (!opt.gotValid(badOptions, badArgs)) { - for (size_t i = 0; i < badOptions.size(); ++i) { - cerr << "Invalid argument for option: " << badOptions[i] << "\n\n"; + if (!opt.gotValid(badOptions, badArgs)) { + for (const auto& badOption : badOptions) { + cerr << "Invalid argument for option: " << badOption << "\n\n"; + } + std::cout << usage; + return 1; } - std::cout << usage; - return 1; - } - - string inputPath = string(); - if (opt.firstArgs.size() > 1 || opt.lastArgs.size() > 0) { - inputPath = opt.firstArgs.size() > 1 ? *opt.firstArgs.back() : *opt.lastArgs.front(); - if (!fileAvailable(inputPath)) { - cerr << "Cannot open file \"" << inputPath << "\"" << '\n'; - return 1; - } - } else { - cerr << "Missing required argument: rom_file" << "\n\n"; - std::cout << usage; - return 1; - } - - sfcRom rom(inputPath); - - if (!verysilent) { - cout << rom.description(silent); - } - - if (rom.isValid && opt.isSet("-f")) { - string outputPath = inputPath; - if (opt.isSet("-o")) { - opt.get("-o")->getString(outputPath); + + string inputPath = string(); + if (opt.firstArgs.size() > 1 || opt.lastArgs.size() > 0) { + inputPath = opt.firstArgs.size() > 1 ? *opt.firstArgs.back() : *opt.lastArgs.front(); + if (!fileAvailable(inputPath)) { + cerr << "Cannot open file \"" << inputPath << "\"" << '\n'; + return 1; + } + } else { + cerr << "Missing required argument: rom_file" + << "\n\n"; + std::cout << usage; + return 1; } - string fixDescripton = rom.fix(outputPath, silent); - if (!verysilent) { - cout << fixDescripton; + sfcRom rom(inputPath); + + if (!verysilent) { cout << rom.description(silent); } + + if (rom.isValid && opt.isSet("-f")) { + string outputPath = inputPath; + if (opt.isSet("-o")) { opt.get("-o")->getString(outputPath); } + + string fixDescripton = rom.fix(outputPath, silent); + if (!verysilent) { cout << fixDescripton; } } - } - return 0; + return 0; } diff --git a/test/compile_flags.txt b/test/compile_flags.txt deleted file mode 100644 index 071b78d..0000000 --- a/test/compile_flags.txt +++ /dev/null @@ -1,5 +0,0 @@ --I/usr/local/include --xc++ --std=c++20 --Wall --Wextra diff --git a/test/test.cpp b/test/test.cpp index 76e6e9c..d68c168 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1,8 +1,8 @@ +#include "../src/sfcRom.hpp" +#include #include #include #include -#include -#include "../src/sfcRom.hpp" // // Simple test ROMs @@ -122,54 +122,53 @@ inline constexpr auto rom_spl4 = "data/private/spl4.sfc"; inline constexpr auto rom_tmz = "data/private/tmz.sfc"; bool file_exists(const std::string& path) { - std::filesystem::path p { path }; - return std::filesystem::is_regular_file(p); + return std::filesystem::is_regular_file(std::filesystem::path(path)); } bool rom_isValid(const std::string& path, bool expected) { - if (!file_exists(path)) { - std::cout << "File '" << path << "' not found, skipping\n"; - return expected; - } - sfcRom rom(path); - return rom.isValid; + if (!file_exists(path)) { + std::cout << "File '" << path << "' not found, skipping\n"; + return expected; + } + sfcRom rom(path); + return rom.isValid; } bool rom_hasCorrectChecksum(const std::string& path, bool expected) { - if (!file_exists(path)) { - std::cout << "File '" << path << "' not found, skipping\n"; - return expected; - } - sfcRom rom(path); - return rom.hasCorrectChecksum; + if (!file_exists(path)) { + std::cout << "File '" << path << "' not found, skipping\n"; + return expected; + } + sfcRom rom(path); + return rom.hasCorrectChecksum; } TEST_CASE("sfcRom.isValid") { - REQUIRE(rom_isValid(rom0, false) == false); - REQUIRE(rom_isValid(rom1, true) == true); - REQUIRE(rom_isValid(rom_atvj, true) == true); - REQUIRE(rom_isValid(rom_atvj_bad, true) == true); - REQUIRE(rom_isValid(rom_dkm2, true) == true); - REQUIRE(rom_isValid(rom_m3, true) == true); - REQUIRE(rom_isValid(rom_mdh, true) == true); - REQUIRE(rom_isValid(rom_smw, true) == true); - REQUIRE(rom_isValid(rom_smw2, true) == true); - REQUIRE(rom_isValid(rom_so, true) == true); - REQUIRE(rom_isValid(rom_spl4, true) == true); - REQUIRE(rom_isValid(rom_tmz, true) == true); + REQUIRE(rom_isValid(rom0, false) == false); + REQUIRE(rom_isValid(rom1, true) == true); + REQUIRE(rom_isValid(rom_atvj, true) == true); + REQUIRE(rom_isValid(rom_atvj_bad, true) == true); + REQUIRE(rom_isValid(rom_dkm2, true) == true); + REQUIRE(rom_isValid(rom_m3, true) == true); + REQUIRE(rom_isValid(rom_mdh, true) == true); + REQUIRE(rom_isValid(rom_smw, true) == true); + REQUIRE(rom_isValid(rom_smw2, true) == true); + REQUIRE(rom_isValid(rom_so, true) == true); + REQUIRE(rom_isValid(rom_spl4, true) == true); + REQUIRE(rom_isValid(rom_tmz, true) == true); } TEST_CASE("sfcRom.hasCorrectChecksum") { - REQUIRE(rom_hasCorrectChecksum(rom0, false) == false); - REQUIRE(rom_hasCorrectChecksum(rom1, false) == false); - REQUIRE(rom_hasCorrectChecksum(rom_atvj, true) == true); - REQUIRE(rom_hasCorrectChecksum(rom_atvj_bad, false) == false); - REQUIRE(rom_hasCorrectChecksum(rom_dkm2, true) == true); - REQUIRE(rom_hasCorrectChecksum(rom_m3, true) == true); - REQUIRE(rom_hasCorrectChecksum(rom_mdh, true) == true); - REQUIRE(rom_hasCorrectChecksum(rom_smw, true) == true); - REQUIRE(rom_hasCorrectChecksum(rom_smw2, true) == true); - REQUIRE(rom_hasCorrectChecksum(rom_so, true) == true); - REQUIRE(rom_hasCorrectChecksum(rom_spl4, true) == true); - REQUIRE(rom_hasCorrectChecksum(rom_tmz, true) == true); + REQUIRE(rom_hasCorrectChecksum(rom0, false) == false); + REQUIRE(rom_hasCorrectChecksum(rom1, false) == false); + REQUIRE(rom_hasCorrectChecksum(rom_atvj, true) == true); + REQUIRE(rom_hasCorrectChecksum(rom_atvj_bad, false) == false); + REQUIRE(rom_hasCorrectChecksum(rom_dkm2, true) == true); + REQUIRE(rom_hasCorrectChecksum(rom_m3, true) == true); + REQUIRE(rom_hasCorrectChecksum(rom_mdh, true) == true); + REQUIRE(rom_hasCorrectChecksum(rom_smw, true) == true); + REQUIRE(rom_hasCorrectChecksum(rom_smw2, true) == true); + REQUIRE(rom_hasCorrectChecksum(rom_so, true) == true); + REQUIRE(rom_hasCorrectChecksum(rom_spl4, true) == true); + REQUIRE(rom_hasCorrectChecksum(rom_tmz, true) == true); }