diff --git a/Analyser/Static/Sega/StaticAnalyser.cpp b/Analyser/Static/Sega/StaticAnalyser.cpp index c8d733c27..56c2c4663 100644 --- a/Analyser/Static/Sega/StaticAnalyser.cpp +++ b/Analyser/Static/Sega/StaticAnalyser.cpp @@ -11,6 +11,9 @@ #include "Target.hpp" Analyser::Static::TargetList Analyser::Static::Sega::GetTargets(const Media &media, const std::string &file_name, TargetPlatform::IntType potential_platforms) { + if(media.cartridges.empty()) + return {}; + TargetList targets; std::unique_ptr target(new Target); @@ -23,10 +26,53 @@ Analyser::Static::TargetList Analyser::Static::Sega::GetTargets(const Media &med target->model = Target::Model::MasterSystem; } + // If this is a Master System title, look for a ROM header. + if(target->model == Target::Model::MasterSystem) { + const auto &data = media.cartridges.front()->get_segments()[0].data; + + // First try to locate a header. + size_t header_offset = 0; + size_t potential_offsets[] = {0x1ff0, 0x3ff0, 0x7ff0}; + for(auto potential_offset: potential_offsets) { + if(!memcmp(&data[potential_offset], "TMR SEGA", 8)) { + header_offset = potential_offset; + break; + } + } + + // If a header was found, use it to crib region. + if(header_offset) { + // Treat export titles as European by default; decline to + // do so only if (US) or (NTSC) is in the file name. + const uint8_t region = data[header_offset + 0x0f] >> 4; + switch(region) { + default: break; + case 4: { + std::string lowercase_name = file_name; + std::transform(lowercase_name.begin(), lowercase_name.end(), lowercase_name.begin(), ::tolower); + if(lowercase_name.find("(jp)") != std::string::npos) { + target->region = + (lowercase_name.find("(us)") == std::string::npos && + lowercase_name.find("(ntsc)") == std::string::npos) ? Target::Region::Europe : Target::Region::USA; + } + } break; + } + + // Also check for a Codemasters header. + // If one is found, set the paging scheme appropriately. + const uint16_t inverse_checksum = uint16_t(0x10000 - (data[0x7fe6] | (data[0x7fe7] << 8))); + if( + data[0x7fe3] >= 0x87 && data[0x7fe3] < 0x96 && // i.e. game is dated between 1987 and 1996 + (inverse_checksum&0xff) == data[0x7fe8] && + (inverse_checksum >> 8) == data[0x7fe9] && // i.e. the standard checksum appears to be present + !data[0x7fea] && !data[0x7feb] && !data[0x7fec] && !data[0x7fed] && !data[0x7fee] && !data[0x7fef] + ) { + target->paging_scheme = Target::PagingScheme::Codemasters; + } + } + } + target->media.cartridges = media.cartridges; - - if(!target->media.empty()) - targets.push_back(std::move(target)); - + targets.push_back(std::move(target)); return targets; } diff --git a/Analyser/Static/Sega/Target.hpp b/Analyser/Static/Sega/Target.hpp index 391b0e159..dcab665a1 100644 --- a/Analyser/Static/Sega/Target.hpp +++ b/Analyser/Static/Sega/Target.hpp @@ -19,7 +19,20 @@ struct Target: public ::Analyser::Static::Target { SG1000 }; + enum class Region { + Japan, + USA, + Europe + }; + + enum class PagingScheme { + Sega, + Codemasters + }; + Model model = Model::MasterSystem; + Region region = Region::Japan; + PagingScheme paging_scheme = PagingScheme::Sega; }; }