diff --git a/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift b/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift index e2981da95..55c1761e7 100644 --- a/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift @@ -45,6 +45,8 @@ class ElectronDocument: MachineDocument { } override func readFromURL(url: NSURL, ofType typeName: String) throws { + electron.analyse(url) + if let pathExtension = url.pathExtension { switch pathExtension.lowercaseString { case "uef": diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.h b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.h index 4d23a3536..61e3754d8 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.h +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.h @@ -17,6 +17,8 @@ - (void)setROM:(nonnull NSData *)rom slot:(int)slot; - (BOOL)openUEFAtURL:(nonnull NSURL *)URL; +- (void)analyse:(NSURL *)url; + @property (nonatomic, assign) BOOL useFastLoadingHack; @property (nonatomic, assign) BOOL useTelevisionOutput; diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.mm b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.mm index 4da7966e5..bda7bf9d3 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.mm +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSElectron.mm @@ -10,6 +10,7 @@ #include "Electron.hpp" #import "CSMachine+Subclassing.h" +#include "StaticAnalyser.hpp" #include "TapeUEF.hpp" @implementation CSElectron { @@ -20,6 +21,10 @@ return &_electron; } +- (void)analyse:(NSURL *)url { + StaticAnalyser::GetTargets([url fileSystemRepresentation]); +} + - (void)setOSROM:(nonnull NSData *)rom { @synchronized(self) { _electron.set_rom(Electron::ROMSlotOS, rom.length, (const uint8_t *)rom.bytes); diff --git a/Storage/Cartridge/Formats/AcornROM.cpp b/Storage/Cartridge/Formats/AcornROM.cpp index f6d29742a..afa397353 100644 --- a/Storage/Cartridge/Formats/AcornROM.cpp +++ b/Storage/Cartridge/Formats/AcornROM.cpp @@ -8,8 +8,52 @@ #include "AcornROM.hpp" +#include +#include + using namespace Storage::Cartridge; AcornROM::AcornROM(const char *file_name) { + // the file should be exactly 16 kb + struct stat file_stats; + stat(file_name, &file_stats); + if(file_stats.st_size != 0x4000) throw ErrorNotAcornROM; + + // grab contents + FILE *file = fopen(file_name, "rb"); + if(!file) throw ErrorNotAcornROM; + size_t data_length = (size_t)file_stats.st_size; + std::vector contents(data_length); + fread(&contents[0], 1, (size_t)(data_length), file); + fclose(file); + + // perform sanity checks... + + // is a copyright string present? + uint8_t copyright_offset = contents[7]; + if( + contents[copyright_offset] != 0x00 || + contents[copyright_offset+1] != 0x28 || + contents[copyright_offset+2] != 0x43 || + contents[copyright_offset+3] != 0x29 + ) throw ErrorNotAcornROM; + + // is the language entry point valid? + if(!( + (contents[0] == 0x00 && contents[1] == 0x00 && contents[2] == 0x00) || + (contents[0] != 0x00 && contents[2] >= 0x80 && contents[2] < 0xc0) + )) throw ErrorNotAcornROM; + + // is the service entry point valid? + if(!(contents[5] >= 0x80 && contents[5] < 0xc0)) throw ErrorNotAcornROM; + + // probability of a random binary blob that isn't an Acorn ROM proceeding to here: + // 1/(2^32) * + // ( ((2^24)-1)/(2^24)*(1/4) + 1/(2^24) ) * + // 1/4 + // = something very improbable — around 1/16th of 1 in 2^32, but not exactly. + + // enshrine + _segments.emplace_back(0x8000, 0xc000, std::move(contents)); }