mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-11 08:30:55 +00:00
Puts enough in place for a GUI-led installation process.
... and provides a lot of the Objective-C wiring necessary to expose that to Swift.
This commit is contained in:
parent
b0f551c307
commit
5acd97c860
@ -13,6 +13,10 @@
|
|||||||
|
|
||||||
using namespace ROM;
|
using namespace ROM;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr Name MaxName = Name::SpectrumPlus3;
|
||||||
|
}
|
||||||
|
|
||||||
Request::Request(Name name, bool optional) {
|
Request::Request(Name name, bool optional) {
|
||||||
node.name = name;
|
node.name = name;
|
||||||
node.is_optional = optional;
|
node.is_optional = optional;
|
||||||
@ -173,8 +177,16 @@ void Request::Node::visit(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<Description> ROM::all_descriptions() {
|
||||||
|
std::vector<Description> result;
|
||||||
|
for(int name = 1; name <= MaxName; name++) {
|
||||||
|
result.push_back(Description(ROM::Name(name)));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<Description> Description::from_crc(uint32_t crc32) {
|
std::optional<Description> Description::from_crc(uint32_t crc32) {
|
||||||
for(int name = 1; name <= SpectrumPlus3; name++) {
|
for(int name = 1; name <= MaxName; name++) {
|
||||||
const Description candidate = Description(ROM::Name(name));
|
const Description candidate = Description(ROM::Name(name));
|
||||||
|
|
||||||
const auto found_crc = std::find(candidate.crc32s.begin(), candidate.crc32s.end(), crc32);
|
const auto found_crc = std::find(candidate.crc32s.begin(), candidate.crc32s.end(), crc32);
|
||||||
@ -232,8 +244,8 @@ Description::Description(Name name) {
|
|||||||
case Name::Macintosh128k: *this = Description(name, "Macintosh", "the Macintosh 128k ROM", "mac128k.rom", 64*1024, 0x6d0c8a28u); break;
|
case Name::Macintosh128k: *this = Description(name, "Macintosh", "the Macintosh 128k ROM", "mac128k.rom", 64*1024, 0x6d0c8a28u); break;
|
||||||
case Name::Macintosh512k: *this = Description(name, "Macintosh", "the Macintosh 512k ROM", "mac512k.rom", 64*1024, 0xcf759e0d); break;
|
case Name::Macintosh512k: *this = Description(name, "Macintosh", "the Macintosh 512k ROM", "mac512k.rom", 64*1024, 0xcf759e0d); break;
|
||||||
case Name::MacintoshPlus: {
|
case Name::MacintoshPlus: {
|
||||||
const std::initializer_list<uint32_t> crc32s = { 0x4fa5b399, 0x7cacd18f, 0xb2102e8e };
|
const std::initializer_list<uint32_t> crcs = { 0x4fa5b399, 0x7cacd18f, 0xb2102e8e };
|
||||||
*this = Description(name, "Macintosh", "the Macintosh Plus ROM", "macplus.rom", 128*1024, crc32s);
|
*this = Description(name, "Macintosh", "the Macintosh Plus ROM", "macplus.rom", 128*1024, crcs);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Name::AtariSTTOS100: *this = Description(name, "AtariST", "the UK TOS 1.00 ROM", "tos100.img", 192*1024, 0x1a586c64u); break;
|
case Name::AtariSTTOS100: *this = Description(name, "AtariST", "the UK TOS 1.00 ROM", "tos100.img", 192*1024, 0x1a586c64u); break;
|
||||||
@ -250,8 +262,8 @@ Description::Description(Name name) {
|
|||||||
case Name::Spectrum128k: *this = Description(name, "ZXSpectrum", "the 128kb ROM", "128.rom", 32 * 1024, 0x2cbe8995u); break;
|
case Name::Spectrum128k: *this = Description(name, "ZXSpectrum", "the 128kb ROM", "128.rom", 32 * 1024, 0x2cbe8995u); break;
|
||||||
case Name::SpecrumPlus2: *this = Description(name, "ZXSpectrum", "the +2 ROM", "plus2.rom", 32 * 1024, 0xe7a517dcu); break;
|
case Name::SpecrumPlus2: *this = Description(name, "ZXSpectrum", "the +2 ROM", "plus2.rom", 32 * 1024, 0xe7a517dcu); break;
|
||||||
case Name::SpectrumPlus3: {
|
case Name::SpectrumPlus3: {
|
||||||
const std::initializer_list<uint32_t> crc32s = { 0x96e3c17a, 0xbe0d9ec4 };
|
const std::initializer_list<uint32_t> crcs = { 0x96e3c17a, 0xbe0d9ec4 };
|
||||||
*this = Description(name, "ZXSpectrum", "the +2a/+3 ROM", "plus3.rom", 64 * 1024, crc32s);
|
*this = Description(name, "ZXSpectrum", "the +2a/+3 ROM", "plus3.rom", 64 * 1024, crcs);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Name::AcornBASICII: *this = Description(name, "Electron", "the Acorn BASIC II ROM", "basic.rom", 16*1024, 0x79434781u); break;
|
case Name::AcornBASICII: *this = Description(name, "Electron", "the Acorn BASIC II ROM", "basic.rom", 16*1024, 0x79434781u); break;
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -135,7 +136,7 @@ struct Description {
|
|||||||
/// CRC32s for all known acceptable copies of this ROM; intended to allow a host platform
|
/// CRC32s for all known acceptable copies of this ROM; intended to allow a host platform
|
||||||
/// to test user-provided ROMs of unknown provenance. **Not** intended to be used
|
/// to test user-provided ROMs of unknown provenance. **Not** intended to be used
|
||||||
/// to exclude ROMs where the user's intent is otherwise clear.
|
/// to exclude ROMs where the user's intent is otherwise clear.
|
||||||
std::vector<uint32_t> crc32s;
|
std::set<uint32_t> crc32s;
|
||||||
|
|
||||||
/// Constructs the @c Description that correlates to @c name.
|
/// Constructs the @c Description that correlates to @c name.
|
||||||
Description(Name name);
|
Description(Name name);
|
||||||
@ -145,14 +146,19 @@ struct Description {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename FileNameT, typename CRC32T> Description(
|
template <typename FileNameT, typename CRC32T> Description(
|
||||||
Name name, std::string machine_name, std::string descriptive_name, FileNameT file_names, size_t size, CRC32T crc32s = CRC32T(0)
|
Name name, std::string machine_name, std::string descriptive_name, FileNameT file_names, size_t size, CRC32T crc32s = uint32_t(0)
|
||||||
) : name{name}, machine_name{machine_name}, descriptive_name{descriptive_name}, file_names{file_names}, size{size}, crc32s{crc32s} {
|
) : name{name}, machine_name{machine_name}, descriptive_name{descriptive_name}, file_names{file_names}, size{size}, crc32s{crc32s} {
|
||||||
if(this->crc32s.size() == 1 && !this->crc32s[0]) {
|
// Slightly lazy: deal with the case where the constructor wasn't provided with any
|
||||||
|
// CRCs by spotting that the set has exactly one member, which has value 0. The alternative
|
||||||
|
// would be to provide a partial specialisation that never put anything into the set.
|
||||||
|
if(this->crc32s.size() == 1 && !*this->crc32s.begin()) {
|
||||||
this->crc32s.clear();
|
this->crc32s.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::vector<Description> all_descriptions();
|
||||||
|
|
||||||
struct Request {
|
struct Request {
|
||||||
Request(Name name, bool optional = false);
|
Request(Name name, bool optional = false);
|
||||||
Request() {}
|
Request() {}
|
||||||
|
@ -137,22 +137,18 @@ class MachineDocument:
|
|||||||
volumeSlider.floatValue = userDefaultsVolume()
|
volumeSlider.floatValue = userDefaultsVolume()
|
||||||
}
|
}
|
||||||
|
|
||||||
private var missingROMs: [CSMissingROM] = []
|
private var missingROMs: String = ""
|
||||||
func configureAs(_ analysis: CSStaticAnalyser) {
|
func configureAs(_ analysis: CSStaticAnalyser) {
|
||||||
self.machineDescription = analysis
|
self.machineDescription = analysis
|
||||||
|
|
||||||
actionLock.lock()
|
actionLock.lock()
|
||||||
drawLock.lock()
|
drawLock.lock()
|
||||||
|
|
||||||
let missingROMs = NSMutableArray()
|
|
||||||
if let machine = CSMachine(analyser: analysis, missingROMs: missingROMs) {
|
if let machine = CSMachine(analyser: analysis, missingROMs: missingROMs) {
|
||||||
self.machine = machine
|
self.machine = machine
|
||||||
machine.setVolume(userDefaultsVolume())
|
machine.setVolume(userDefaultsVolume())
|
||||||
setupMachineOutput()
|
setupMachineOutput()
|
||||||
} else {
|
} else {
|
||||||
// Store the selected machine and list of missing ROMs, and
|
|
||||||
// show the missing ROMs dialogue.
|
|
||||||
self.missingROMs = missingROMs.map({$0 as! CSMissingROM})
|
|
||||||
requestRoms()
|
requestRoms()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -437,30 +433,7 @@ class MachineDocument:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func populateMissingRomList() {
|
func populateMissingRomList() {
|
||||||
// Fill in the missing details; first build a list of all the individual
|
romRequesterText!.stringValue = self.romRequestBaseText + self.missingROMs
|
||||||
// line items.
|
|
||||||
var requestLines: [String] = []
|
|
||||||
for missingROM in self.missingROMs {
|
|
||||||
if let descriptiveName = missingROM.descriptiveName {
|
|
||||||
requestLines.append("• " + descriptiveName)
|
|
||||||
} else {
|
|
||||||
requestLines.append("• " + missingROM.fileName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Suffix everything up to the penultimate line with a semicolon;
|
|
||||||
// the penultimate line with a semicolon and a conjunctive; the final
|
|
||||||
// line with a full stop.
|
|
||||||
for x in 0 ..< requestLines.count {
|
|
||||||
if x < requestLines.count - 2 {
|
|
||||||
requestLines[x].append(";")
|
|
||||||
} else if x < requestLines.count - 1 {
|
|
||||||
requestLines[x].append("; and")
|
|
||||||
} else {
|
|
||||||
requestLines[x].append(".")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
romRequesterText!.stringValue = self.romRequestBaseText + requestLines.joined(separator: "\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func romReceiverView(_ view: CSROMReceiverView, didReceiveFileAt URL: URL) {
|
func romReceiverView(_ view: CSROMReceiverView, didReceiveFileAt URL: URL) {
|
||||||
@ -474,7 +447,7 @@ class MachineDocument:
|
|||||||
// Try to match by size first, CRC second. Accept that some ROMs may have
|
// Try to match by size first, CRC second. Accept that some ROMs may have
|
||||||
// some additional appended data. Arbitrarily allow them to be up to 10kb
|
// some additional appended data. Arbitrarily allow them to be up to 10kb
|
||||||
// too large.
|
// too large.
|
||||||
var index = 0
|
/* var index = 0
|
||||||
for missingROM in self.missingROMs {
|
for missingROM in self.missingROMs {
|
||||||
if fileData.count >= missingROM.size && fileData.count < missingROM.size + 10*1024 {
|
if fileData.count >= missingROM.size && fileData.count < missingROM.size + 10*1024 {
|
||||||
// Trim to size.
|
// Trim to size.
|
||||||
@ -506,7 +479,7 @@ class MachineDocument:
|
|||||||
}
|
}
|
||||||
|
|
||||||
index = index + 1
|
index = index + 1
|
||||||
}
|
}*/
|
||||||
|
|
||||||
if didInstallRom {
|
if didInstallRom {
|
||||||
if self.missingROMs.count == 0 {
|
if self.missingROMs.count == 0 {
|
||||||
|
@ -33,20 +33,14 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) {
|
|||||||
CSMachineKeyboardInputModeJoystick,
|
CSMachineKeyboardInputModeJoystick,
|
||||||
};
|
};
|
||||||
|
|
||||||
@interface CSMissingROM: NSObject
|
|
||||||
@property (nonatomic, readonly, nonnull) NSString *machineName;
|
|
||||||
@property (nonatomic, readonly, nonnull) NSString *fileName;
|
|
||||||
@property (nonatomic, readonly, nullable) NSString *descriptiveName;
|
|
||||||
@property (nonatomic, readonly) NSUInteger size;
|
|
||||||
@property (nonatomic, readonly, nonnull) NSArray<NSNumber *> *crc32s;
|
|
||||||
@end
|
|
||||||
|
|
||||||
// Deliberately low; to ensure CSMachine has been declared as an @class already.
|
// Deliberately low; to ensure CSMachine has been declared as an @class already.
|
||||||
#import "CSAtari2600.h"
|
#import "CSAtari2600.h"
|
||||||
#import "CSZX8081.h"
|
#import "CSZX8081.h"
|
||||||
|
|
||||||
@interface CSMachine : NSObject
|
@interface CSMachine : NSObject
|
||||||
|
|
||||||
|
+ (BOOL)attemptInstallROM:(NSURL *)url;
|
||||||
|
|
||||||
- (nonnull instancetype)init NS_UNAVAILABLE;
|
- (nonnull instancetype)init NS_UNAVAILABLE;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -56,7 +50,7 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) {
|
|||||||
@param missingROMs An array that is filled with a list of ROMs that the machine requested but which
|
@param missingROMs An array that is filled with a list of ROMs that the machine requested but which
|
||||||
were not found; populated only if this `init` has failed.
|
were not found; populated only if this `init` has failed.
|
||||||
*/
|
*/
|
||||||
- (nullable instancetype)initWithAnalyser:(nonnull CSStaticAnalyser *)result missingROMs:(nullable inout NSMutableArray<CSMissingROM *> *)missingROMs NS_DESIGNATED_INITIALIZER;
|
- (nullable instancetype)initWithAnalyser:(nonnull CSStaticAnalyser *)result missingROMs:(nullable inout NSString *)missingROMs NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
- (float)idealSamplingRateFromRange:(NSRange)range;
|
- (float)idealSamplingRateFromRange:(NSRange)range;
|
||||||
@property (readonly, getter=isStereo) BOOL stereo;
|
@property (readonly, getter=isStereo) BOOL stereo;
|
||||||
|
@ -74,28 +74,6 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
__unsafe_unretained CSMachine *machine;
|
__unsafe_unretained CSMachine *machine;
|
||||||
};
|
};
|
||||||
|
|
||||||
@interface CSMissingROM (/*Mutability*/)
|
|
||||||
@property (nonatomic, nonnull, copy) NSString *machineName;
|
|
||||||
@property (nonatomic, nonnull, copy) NSString *fileName;
|
|
||||||
@property (nonatomic, nullable, copy) NSString *descriptiveName;
|
|
||||||
@property (nonatomic, readwrite) NSUInteger size;
|
|
||||||
@property (nonatomic, copy) NSArray<NSNumber *> *crc32s;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CSMissingROM
|
|
||||||
|
|
||||||
@synthesize machineName=_machineName;
|
|
||||||
@synthesize fileName=_fileName;
|
|
||||||
@synthesize descriptiveName=_descriptiveName;
|
|
||||||
@synthesize size=_size;
|
|
||||||
@synthesize crc32s=_crc32s;
|
|
||||||
|
|
||||||
- (NSString *)description {
|
|
||||||
return [NSString stringWithFormat:@"%@/%@, %lu bytes, CRCs: %@", _fileName, _descriptiveName, (unsigned long)_size, _crc32s];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CSMachine {
|
@implementation CSMachine {
|
||||||
SpeakerDelegate _speakerDelegate;
|
SpeakerDelegate _speakerDelegate;
|
||||||
ActivityObserver _activityObserver;
|
ActivityObserver _activityObserver;
|
||||||
@ -126,7 +104,7 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
NSMutableArray<dispatch_block_t> *_inputEvents;
|
NSMutableArray<dispatch_block_t> *_inputEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithAnalyser:(CSStaticAnalyser *)result missingROMs:(inout NSMutableArray<CSMissingROM *> *)missingROMs {
|
- (instancetype)initWithAnalyser:(CSStaticAnalyser *)result missingROMs:(inout NSString *)missingROMs {
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if(self) {
|
if(self) {
|
||||||
_analyser = result;
|
_analyser = result;
|
||||||
@ -783,4 +761,8 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
_timer = nil;
|
_timer = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ (BOOL)attemptInstallROM:(NSURL *)url {
|
||||||
|
return CSInstallROM(url);
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -9,3 +9,4 @@
|
|||||||
#include "ROMMachine.hpp"
|
#include "ROMMachine.hpp"
|
||||||
|
|
||||||
ROMMachine::ROMFetcher CSROMFetcher(ROM::Request *missing = nullptr);
|
ROMMachine::ROMFetcher CSROMFetcher(ROM::Request *missing = nullptr);
|
||||||
|
BOOL CSInstallROM(NSURL *);
|
||||||
|
@ -11,24 +11,75 @@
|
|||||||
|
|
||||||
#import "NSBundle+DataResource.h"
|
#import "NSBundle+DataResource.h"
|
||||||
#import "NSData+StdVector.h"
|
#import "NSData+StdVector.h"
|
||||||
|
#import "NSData+CRC32.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
NSString *directoryFor(const ROM::Description &description) {
|
||||||
|
return [@"ROMImages/" stringByAppendingString:[NSString stringWithUTF8String:description.machine_name.c_str()]];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSArray<NSURL *> *urlsFor(const ROM::Description &description, const std::string &file_name) {
|
||||||
|
NSMutableArray<NSURL *> *const urls = [[NSMutableArray alloc] init];
|
||||||
|
NSArray<NSURL *> *const supportURLs = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask];
|
||||||
|
NSString *const subdirectory = directoryFor(description);
|
||||||
|
|
||||||
|
for(NSURL *supportURL in supportURLs) {
|
||||||
|
[urls addObject:[[supportURL URLByAppendingPathComponent:subdirectory]
|
||||||
|
URLByAppendingPathComponent:[NSString stringWithUTF8String:file_name.c_str()]]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL CSInstallROM(NSURL *url) {
|
||||||
|
NSData *const data = [NSData dataWithContentsOfURL:url];
|
||||||
|
if(!data) return NO;
|
||||||
|
|
||||||
|
// Try for a direct CRC match.
|
||||||
|
std::optional<ROM::Description> target_description;
|
||||||
|
target_description = ROM::Description::from_crc(uint32_t(data.crc32.integerValue));
|
||||||
|
|
||||||
|
// See whether there's an acceptable trimming that creates a CRC match.
|
||||||
|
if(!target_description) {
|
||||||
|
const std::vector<ROM::Description> descriptions = ROM::all_descriptions();
|
||||||
|
for(const auto &description: descriptions) {
|
||||||
|
if(description.size > data.length) continue;
|
||||||
|
|
||||||
|
NSData *const trimmedData = [data subdataWithRange:NSMakeRange(0, description.size)];
|
||||||
|
if(description.crc32s.find(uint32_t(trimmedData.crc32.unsignedIntValue)) != description.crc32s.end()) {
|
||||||
|
target_description = description;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no destination was found, stop.
|
||||||
|
if(!target_description) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the data to its destination and report success.
|
||||||
|
NSURL *const targetURL = [urlsFor(*target_description, target_description->file_names[0]) firstObject];
|
||||||
|
[data writeToURL:targetURL atomically:YES];
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
ROMMachine::ROMFetcher CSROMFetcher(ROM::Request *missing) {
|
ROMMachine::ROMFetcher CSROMFetcher(ROM::Request *missing) {
|
||||||
return [missing] (const ROM::Request &roms) -> ROM::Map {
|
return [missing] (const ROM::Request &roms) -> ROM::Map {
|
||||||
NSArray<NSURL *> *const supportURLs = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask];
|
|
||||||
|
|
||||||
ROM::Map results;
|
ROM::Map results;
|
||||||
for(const auto &description: roms.all_descriptions()) {
|
for(const auto &description: roms.all_descriptions()) {
|
||||||
for(const auto &file_name: description.file_names) {
|
for(const auto &file_name: description.file_names) {
|
||||||
NSData *fileData;
|
NSData *fileData;
|
||||||
NSString *const subdirectory = [@"ROMImages/" stringByAppendingString:[NSString stringWithUTF8String:description.machine_name.c_str()]];
|
|
||||||
|
|
||||||
// Check for this file first within the application support directories.
|
// Check for this file first within the application support directories.
|
||||||
for(NSURL *supportURL in supportURLs) {
|
for(NSURL *fileURL in urlsFor(description, file_name)) {
|
||||||
NSURL *const fullURL = [[supportURL URLByAppendingPathComponent:subdirectory]
|
fileData = [NSData dataWithContentsOfURL:fileURL];
|
||||||
URLByAppendingPathComponent:[NSString stringWithUTF8String:file_name.c_str()]];
|
|
||||||
fileData = [NSData dataWithContentsOfURL:fullURL];
|
|
||||||
if(fileData) break;
|
if(fileData) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +88,7 @@ ROMMachine::ROMFetcher CSROMFetcher(ROM::Request *missing) {
|
|||||||
fileData = [[NSBundle mainBundle]
|
fileData = [[NSBundle mainBundle]
|
||||||
dataForResource:[NSString stringWithUTF8String:file_name.c_str()]
|
dataForResource:[NSString stringWithUTF8String:file_name.c_str()]
|
||||||
withExtension:nil
|
withExtension:nil
|
||||||
subdirectory:subdirectory];
|
subdirectory:directoryFor(description)];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store an appropriate result.
|
// Store an appropriate result.
|
||||||
@ -47,7 +98,9 @@ ROMMachine::ROMFetcher CSROMFetcher(ROM::Request *missing) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: sever all found ROMs from roms and store to missing, if provided.
|
if(missing) {
|
||||||
|
*missing = roms.subtract(results);
|
||||||
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user