diff --git a/Makefile b/Makefile index 4e92e5e0..74544f9b 100644 --- a/Makefile +++ b/Makefile @@ -68,14 +68,9 @@ all: $(STD_TARGET_LIST) $(EXT_TARGET_LIST) clean: @rm -rf build -updatebin: $(STD_TARGET_LIST) $(STD_TOOL_RELEASE) +updatebin: $(STD_TARGET_LIST) $(TOOL_RELEASE) + $(TOOL_RELEASE) -i ./build -o ./bin basic_generic.rom kernal_generic.rom basic_mega65.rom kernal_mega65.rom basic_ultimate64.rom kernal_ultimate64.rom cp build/chargen.rom bin/chargen.rom - cp build/basic_generic.rom bin/basic_generic.rom - cp build/kernal_generic.rom bin/kernal_generic.rom - cp build/basic_mega65.rom bin/basic_mega65.rom - cp build/kernal_mega65.rom bin/kernal_mega65.rom - cp build/basic_ultimate64.rom bin/basic_ultimate64.rom - cp build/kernal_ultimate64.rom bin/kernal_ultimate64.rom # Rules - tools diff --git a/src/release.cc b/src/release.cc index bb5e9c46..b812aa5e 100644 --- a/src/release.cc +++ b/src/release.cc @@ -16,12 +16,45 @@ #include #include +typedef struct +{ + std::string romTypeName; + size_t fileSize; + size_t signatureOffset; // offset to 'OR' string + bool requireIdBASIC; + bool requireIdKERNAL; + +} ROMTypeDescriptionEntry; + +const std::vector ROM_DEFINITIONS = +{ + { + "BASIC", + 8192, // file size + 0x1F52, // signature offset + true, // whether to require BASIC ID + false // whether to require KERNAL ID + }, + + { + "KERNAL", + 8192, // file size + 0x04B9, // signature offset + false, // whether to require BASIC ID + false // whether to require KERNAL ID + }, +}; + +const uint16_t MAX_FILE_SIZE = 8192; + const std::vector STR_MEGABAS = { 0x4D, 0x45, 0x47, 0x41, 0x42, 0x41, 0x53, 0x32 }; const std::vector STR_OR = { 0x4F, 0x52 }; const std::vector STR_SNAPSHOT = { 0x28, 0x44, 0x45, 0x56, 0x45, 0x4C, 0x20, 0x53, 0x4E, 0x41, 0x50, 0x53, 0x48, 0x4F, 0x54, 0x29, 0x00 }; +const std::string STR_DEV = "DEV."; + // // Command and enviroment line settings // @@ -32,7 +65,7 @@ std::string CMD_outDir = "./bin"; std::list CMD_fileList; std::string ENV_developerId; -std::string ENV_timeString; +std::string ENV_dateString; // // Common helper functions @@ -49,38 +82,42 @@ void printUsage() // Class definitions // -enum class ROMType -{ - ROM_UNKNOWN, - ROM_BASIC, - ROM_KERNAL -}; - class ROMFile { public: ROMFile(const std::string &baseFileName); + void embedRevisionStr(const std::string &newRevisionStr); + void save(); + + const ROMTypeDescriptionEntry *descPtr; + + bool sameContent; + bool sameDeveloper; + bool sameDate; // only set if sameDeveloper + uint8_t oldDailyRelId; // only set if sameDeveloper and sameDate + private: void readSrcFile(); void readDstFile(); - void recognizeFiles(); + void recognizeSrcFile(); + void dropUnmatchingDst(); + void analyzeContent(); std::string baseFileName; std::vector srcFileContent; std::vector dstFileContent; - - ROMType romType; }; // // Global variables // +size_t GLOBAL_maxFileNameLen = 0; std::list GLOBAL_ROMFiles; // @@ -151,18 +188,17 @@ void getDate() const std::string zero = "0"; - - ENV_timeString = (year.size() == 2 ? year : zero + year) + + ENV_dateString = (year.size() == 2 ? year : zero + year) + (month.size() == 2 ? month : zero + month) + - (day.size() == 2 ? day : zero + day); + (day.size() == 2 ? day : zero + day); - if (ENV_timeString.size() != 6) ERROR("could not retrieve date"); + if (ENV_dateString.size() != 6) ERROR("could not retrieve date"); } void printBanner() { printBannerLineTop(); - std::cout << "// Preparing ROM set release " << ENV_timeString << "-" << ENV_developerId << "\n"; + std::cout << "// Preparing ROM set release" << "\n"; printBannerLineBottom(); } @@ -171,14 +207,69 @@ void readFiles() for (auto &fileName : CMD_fileList) GLOBAL_ROMFiles.push_back(ROMFile(fileName)); } -void embedVersionString() +void decideIfUpdate() { - // XXX + // Check if we have any file at all + + if (GLOBAL_ROMFiles.empty()) + { + ERROR("no files to update"); + } + + // Check if we have at least one file with changed content + + bool sameContent = true; + for (auto &file : GLOBAL_ROMFiles) + { + if (!file.sameContent) + { + sameContent = false; + break; + } + } + + if (sameContent) + { + ERROR("same content in all the files"); + } } -void writeFiles() +void saveFiles() { - // XXX + // Determine a daily revision sequence ID + + uint8_t relId = 0; + for (auto &file : GLOBAL_ROMFiles) + { + if (file.sameDeveloper && file.sameDate) + { + relId = std::max(file.oldDailyRelId, relId); + } + } + relId++; + + if (relId > 9) + { + ERROR("too many releases this day"); + } + + // Create a new revision string + + const std::string newRevStr = STR_DEV + ENV_dateString + "." + ENV_developerId + "." + std::to_string(relId); + + for (auto &file : GLOBAL_ROMFiles) + { + file.embedRevisionStr(newRevStr); + } + + std::cout << "Files for release '" << newRevStr << "':\n\n"; + + // Save the files + + for (auto &file : GLOBAL_ROMFiles) + { + file.save(); + } } // @@ -194,12 +285,12 @@ int main(int argc, char **argv) printBanner(); readFiles(); - embedVersionString(); - writeFiles(); + decideIfUpdate(); + saveFiles(); - std::cout << "\n\nXXX This tool is not finished yet\n\n"; + std::cout << "\n"; - return 1; + return 0; } // @@ -207,13 +298,21 @@ int main(int argc, char **argv) // ROMFile::ROMFile(const std::string &baseFileName) : - baseFileName(baseFileName), - romType(ROMType::ROM_UNKNOWN) + descPtr(nullptr), + sameContent(false), + sameDeveloper(false), + sameDate(false), + oldDailyRelId(0), + baseFileName(baseFileName) { + GLOBAL_maxFileNameLen = std::max(GLOBAL_maxFileNameLen, baseFileName.length()); + readSrcFile(); readDstFile(); - recognizeFiles(); + recognizeSrcFile(); + dropUnmatchingDst(); + analyzeContent(); } void ROMFile::readSrcFile() @@ -228,9 +327,9 @@ void ROMFile::readSrcFile() if (!srcFile.good()) ERROR(std::string("unable to open ROM file '") + srcFileNamePath + "'"); auto srcSize = srcFile.tellg(); - if (srcSize != 8192) ERROR(std::string("incorrect size of ROM file '") + srcFileNamePath + "'"); + if (srcSize > MAX_FILE_SIZE) ERROR(std::string("incorrect size of ROM file '") + srcFileNamePath + "'"); - srcFileContent.resize(8192); + srcFileContent.resize(srcSize); srcFile.seekg (0, srcFile.beg); srcFile.read((char *) srcFileContent.data(), srcFileContent.size()); @@ -249,45 +348,190 @@ void ROMFile::readDstFile() bool continueReading = dstFile.good(); - if (continueReading && dstFile.tellg() != 8192) continueReading = false; + if (continueReading && dstFile.tellg() != unsigned(srcFileContent.size())) continueReading = false; if (continueReading) { - dstFileContent.resize(8192); + dstFileContent.resize(srcFileContent.size()); dstFile.seekg (0, dstFile.beg); dstFile.read((char *) dstFileContent.data(), dstFileContent.size()); } - if (!dstFile.good()) dstFileContent.resize(0); + if (!dstFile.good()) dstFileContent.clear(); dstFile.close(); } -void ROMFile::recognizeFiles() +void ROMFile::recognizeSrcFile() { // Recognize source file type - std::cout << baseFileName << " "; + for (auto &SPEC : ROM_DEFINITIONS) + { + if (srcFileContent.size() != SPEC.fileSize) continue; - if ((memcmp(&srcFileContent[0x0004], STR_MEGABAS.data(), STR_MEGABAS.size()) == 0) && - (memcmp(&srcFileContent[0x1F52], STR_OR.data(), STR_OR.size()) == 0) && - (memcmp(&srcFileContent[0x1F55], STR_SNAPSHOT.data(), STR_SNAPSHOT.size()) == 0)) - { - romType = ROMType::ROM_BASIC; + if (SPEC.requireIdBASIC && memcmp(&srcFileContent[0x0004], STR_MEGABAS.data(), STR_MEGABAS.size()) != 0) + { + continue; + } + + if (SPEC.requireIdKERNAL && srcFileContent[0x1F80] != 0xF0) + { + continue; + } + + if (memcmp(&srcFileContent[SPEC.signatureOffset], STR_OR.data(), STR_OR.size()) != 0) + { + continue; + } + + auto snapshotStrOffset = SPEC.signatureOffset + STR_OR.size() + 1; + if (memcmp(&srcFileContent[snapshotStrOffset], STR_SNAPSHOT.data(), STR_SNAPSHOT.size()) != 0) + { + continue; + } + + auto zeroByteoffset = snapshotStrOffset + 0x10; + if (srcFileContent[zeroByteoffset] != 0) + { + continue; + } + + descPtr = &SPEC; + break; } - else if (srcFileContent[0x1F80] == 0xF0 && - (memcmp(&srcFileContent[0x04B9], STR_OR.data(), STR_OR.size()) == 0) && - (memcmp(&srcFileContent[0x04BC], STR_SNAPSHOT.data(), STR_SNAPSHOT.size()) == 0)) - { - romType = ROMType::ROM_KERNAL; - } - else + + if (descPtr == nullptr) { ERROR(std::string("'") + CMD_inDir + DIR_SEPARATOR + baseFileName + "' is not a supported unversioned Open ROMs image"); } +} - // XXX +void ROMFile::dropUnmatchingDst() +{ + // Drop unmatching destination file data - std::cout << baseFileName << " "; + if (!dstFileContent.empty() && descPtr->requireIdBASIC && + memcmp(&dstFileContent[0x0004], STR_MEGABAS.data(), STR_MEGABAS.size()) != 0) + { + dstFileContent.clear(); + } + + if (!dstFileContent.empty() && descPtr->requireIdKERNAL && dstFileContent[0x1F80] != 0xF0) + { + dstFileContent.clear(); + } + + if (!dstFileContent.empty() && + memcmp(&dstFileContent[descPtr->signatureOffset], STR_OR.data(), STR_OR.size()) != 0) + { + dstFileContent.clear(); + } +} + +void ROMFile::analyzeContent() +{ + auto revOffset = descPtr->signatureOffset + 3; + + if (dstFileContent.empty()) + { + return; + } + + // Check if meaningful file content is really different + + if ((memcmp(&srcFileContent[0x0000], &dstFileContent[0x0000], revOffset) == 0) && + (memcmp(&srcFileContent[revOffset + 0x10], &dstFileContent[revOffset + 0x10], 8192 - revOffset) == 0)) + { + sameContent = true; + } + + // Determine expected length of developer ID + + uint8_t devIdLen = ENV_developerId.length(); + + // Determine if old version is a devel version compatible with this tool + + if (memcmp(&dstFileContent[revOffset], STR_DEV.data(), STR_DEV.size()) != 0) + { + return; + } + + uint16_t idxDate = revOffset + STR_DEV.size(); + uint16_t idxLimit = idxDate + ENV_dateString.length(); + + uint16_t idx = idxDate; + for (; idx < idxLimit; idx++) + { + if (dstFileContent[idx] < '0' || dstFileContent[idx] > '9') + { + return; + } + } + + if (dstFileContent[idx] != '.' || + dstFileContent[idx + devIdLen + 1] != '.' || + dstFileContent[idx + devIdLen + 2] < '0' || + dstFileContent[idx + devIdLen + 2] > '9' || + dstFileContent[idx + devIdLen + 3] != 0) + { + return; + } + + // Retrieve release data + + if (memcmp(&dstFileContent[idx + 1], ENV_developerId.data(), ENV_developerId.size()) != 0) + { + return; // different developer + } + + sameDeveloper = true; + + if (memcmp(&dstFileContent[idxDate], ENV_dateString.data(), ENV_dateString.size()) != 0) + { + return; // different date + } + + sameDate = true; + oldDailyRelId = dstFileContent[idx + devIdLen + 2] - '0'; +} + +void ROMFile::embedRevisionStr(const std::string &newRevisionStr) +{ + std::string revStr = newRevisionStr; + + while (revStr.length() < 16) revStr = revStr + '\0'; + + memcpy(&srcFileContent[descPtr->signatureOffset + 3], + revStr.data(), revStr.size()); +} + +void ROMFile::save() +{ + const std::string outFileNamePath = CMD_outDir + DIR_SEPARATOR + baseFileName; + + // Print out file name and metadata + + std::string spacing; + spacing.resize(GLOBAL_maxFileNameLen + 4 - baseFileName.length(), ' '); + + std::cout << " " << outFileNamePath << spacing << descPtr->romTypeName; + + if (sameContent) + { + spacing.resize(10 - descPtr->romTypeName.length()); + std::cout << spacing << "(unchanged)"; + } + std::cout << "\n"; + + std::ofstream outFile; + outFile.open(outFileNamePath, std::ios::out | std::ios::binary | std::ios::trunc); + + if (!outFile.good()) ERROR(std::string("unable to open output file '") + outFileNamePath + "'"); + + outFile.write((char *) srcFileContent.data(), srcFileContent.size()); + + if (!outFile.good()) ERROR(std::string("unable to write file '") + outFileNamePath + "'"); + outFile.close(); }