mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-13 22:32:03 +00:00
Adds parsing of the top-level directory for ADFS images.
This commit is contained in:
parent
4636d8dfb7
commit
8311ac4a7c
@ -50,7 +50,10 @@ std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetDFSCatalogue(const std::s
|
||||
new_file.name = name;
|
||||
new_file.load_address = uint32_t(details->samples[0][file_offset] | (details->samples[0][file_offset+1] << 8) | ((details->samples[0][file_offset+6]&0x0c) << 14));
|
||||
new_file.execution_address = uint32_t(details->samples[0][file_offset+2] | (details->samples[0][file_offset+3] << 8) | ((details->samples[0][file_offset+6]&0xc0) << 10));
|
||||
new_file.is_protected = names->samples[0][file_offset + 7] & 0x80;
|
||||
if(names->samples[0][file_offset + 7] & 0x80) {
|
||||
// File is locked; it may not be altered or deleted.
|
||||
new_file.flags |= File::Flags::Locked;
|
||||
}
|
||||
|
||||
long data_length = long(details->samples[0][file_offset+4] | (details->samples[0][file_offset+5] << 8) | ((details->samples[0][file_offset+6]&0x30) << 12));
|
||||
int start_sector = details->samples[0][file_offset+7] | ((details->samples[0][file_offset+6]&0x03) << 8);
|
||||
@ -69,11 +72,16 @@ std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetDFSCatalogue(const std::s
|
||||
new_file.data.insert(new_file.data.end(), next_sector->samples[0].begin(), next_sector->samples[0].begin() + length_from_sector);
|
||||
data_length -= length_from_sector;
|
||||
}
|
||||
if(!data_length) catalogue->files.push_back(new_file);
|
||||
if(!data_length) catalogue->files.push_back(std::move(new_file));
|
||||
}
|
||||
|
||||
return catalogue;
|
||||
}
|
||||
|
||||
/*
|
||||
Primary resource used: "Acorn 8-Bit ADFS Filesystem Structure";
|
||||
http://mdfs.net/Docs/Comp/Disk/Format/ADFS
|
||||
*/
|
||||
std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetADFSCatalogue(const std::shared_ptr<Storage::Disk::Disk> &disk) {
|
||||
auto catalogue = std::make_unique<Catalogue>();
|
||||
Storage::Encodings::MFM::Parser parser(true, disk);
|
||||
@ -101,5 +109,73 @@ std::unique_ptr<Catalogue> Analyser::Static::Acorn::GetADFSCatalogue(const std::
|
||||
case 3: catalogue->bootOption = Catalogue::BootOption::ExecBOOT; break;
|
||||
}
|
||||
|
||||
// Parse the root directory, at least.
|
||||
for(std::size_t file_offset = 0x005; file_offset < 0x4cb; file_offset += 0x1a) {
|
||||
// Obtain the name, which will be at most ten characters long, and will
|
||||
// be terminated by either a NULL character or a \r.
|
||||
char name[11];
|
||||
std::size_t c = 0;
|
||||
for(; c < 10; c++) {
|
||||
const char next = root_directory[file_offset + c] & 0x7f;
|
||||
name[c] = next;
|
||||
if(next == '\0' || next == '\r') break;
|
||||
}
|
||||
name[c] = '\0';
|
||||
|
||||
// Skip if the name is empty.
|
||||
if(!strlen(name)) continue;
|
||||
|
||||
// Populate a file then.
|
||||
File new_file;
|
||||
new_file.name = name;
|
||||
new_file.flags =
|
||||
(root_directory[file_offset + 0] & 0x80 ? File::Flags::Readable : 0) |
|
||||
(root_directory[file_offset + 1] & 0x80 ? File::Flags::Writable : 0) |
|
||||
(root_directory[file_offset + 2] & 0x80 ? File::Flags::Locked : 0) |
|
||||
(root_directory[file_offset + 3] & 0x80 ? File::Flags::IsDirectory : 0) |
|
||||
(root_directory[file_offset + 4] & 0x80 ? File::Flags::ExecuteOnly : 0) |
|
||||
(root_directory[file_offset + 5] & 0x80 ? File::Flags::PubliclyReadable : 0) |
|
||||
(root_directory[file_offset + 6] & 0x80 ? File::Flags::PubliclyWritable : 0) |
|
||||
(root_directory[file_offset + 7] & 0x80 ? File::Flags::PubliclyExecuteOnly : 0) |
|
||||
(root_directory[file_offset + 8] & 0x80 ? File::Flags::IsPrivate : 0);
|
||||
|
||||
new_file.load_address =
|
||||
(uint32_t(root_directory[file_offset + 0x0a]) << 0) |
|
||||
(uint32_t(root_directory[file_offset + 0x0b]) << 8) |
|
||||
(uint32_t(root_directory[file_offset + 0x0c]) << 16) |
|
||||
(uint32_t(root_directory[file_offset + 0x0d]) << 24);
|
||||
|
||||
new_file.execution_address =
|
||||
(uint32_t(root_directory[file_offset + 0x0e]) << 0) |
|
||||
(uint32_t(root_directory[file_offset + 0x0f]) << 8) |
|
||||
(uint32_t(root_directory[file_offset + 0x10]) << 16) |
|
||||
(uint32_t(root_directory[file_offset + 0x11]) << 24);
|
||||
|
||||
new_file.sequence_number = root_directory[file_offset + 0x19];
|
||||
|
||||
const uint32_t size =
|
||||
(uint32_t(root_directory[file_offset + 0x12]) << 0) |
|
||||
(uint32_t(root_directory[file_offset + 0x13]) << 8) |
|
||||
(uint32_t(root_directory[file_offset + 0x14]) << 16) |
|
||||
(uint32_t(root_directory[file_offset + 0x15]) << 24);
|
||||
|
||||
uint32_t start_sector =
|
||||
(uint32_t(root_directory[file_offset + 0x16]) << 0) |
|
||||
(uint32_t(root_directory[file_offset + 0x17]) << 8) |
|
||||
(uint32_t(root_directory[file_offset + 0x18]) << 16);
|
||||
|
||||
new_file.data.reserve(size);
|
||||
while(new_file.data.size() < size) {
|
||||
const Storage::Encodings::MFM::Sector *const sector = parser.get_sector(start_sector / (80 * 16), (start_sector / 16) % 80, start_sector % 16);
|
||||
if(!sector) break;
|
||||
|
||||
const auto length_from_sector = std::min(size - new_file.data.size(), sector->samples[0].size());
|
||||
new_file.data.insert(new_file.data.end(), sector->samples[0].begin(), sector->samples[0].begin() + ssize_t(length_from_sector));
|
||||
++start_sector;
|
||||
}
|
||||
|
||||
catalogue->files.push_back(std::move(new_file));
|
||||
}
|
||||
|
||||
return catalogue;
|
||||
}
|
||||
|
@ -19,19 +19,38 @@ namespace Acorn {
|
||||
|
||||
struct File {
|
||||
std::string name;
|
||||
uint32_t load_address;
|
||||
uint32_t execution_address;
|
||||
bool is_protected;
|
||||
uint32_t load_address = 0;
|
||||
uint32_t execution_address = 0;
|
||||
|
||||
enum Flags: uint16_t {
|
||||
Readable = 1 << 0,
|
||||
Writable = 1 << 1,
|
||||
Locked = 1 << 2,
|
||||
IsDirectory = 1 << 3,
|
||||
ExecuteOnly = 1 << 4,
|
||||
PubliclyReadable = 1 << 5,
|
||||
PubliclyWritable = 1 << 6,
|
||||
PubliclyExecuteOnly = 1 << 7,
|
||||
IsPrivate = 1 << 8,
|
||||
};
|
||||
uint16_t flags = Flags::Readable | Flags::Readable | Flags::PubliclyReadable | Flags::PubliclyWritable;
|
||||
uint8_t sequence_number = 0;
|
||||
|
||||
std::vector<uint8_t> data;
|
||||
|
||||
/// Describes a single chunk of file data; these relate to the tape and ROM filing system.
|
||||
/// The File-level fields contain a 'definitive' version of the load and execution addresses,
|
||||
/// but both of those filing systems also store them per chunk.
|
||||
///
|
||||
/// Similarly, the file-level data will contain the aggregate data of all chunks.
|
||||
struct Chunk {
|
||||
std::string name;
|
||||
uint32_t load_address;
|
||||
uint32_t execution_address;
|
||||
uint16_t block_number;
|
||||
uint16_t block_length;
|
||||
uint8_t block_flag;
|
||||
uint32_t next_address;
|
||||
uint32_t load_address = 0;
|
||||
uint32_t execution_address = 0;
|
||||
uint16_t block_number = 0;
|
||||
uint16_t block_length = 0;
|
||||
uint32_t next_address = 0;
|
||||
uint8_t block_flag = 0;
|
||||
|
||||
bool header_crc_matched;
|
||||
bool data_crc_matched;
|
||||
|
@ -77,8 +77,8 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &me
|
||||
if(!files.empty()) {
|
||||
bool is_basic = true;
|
||||
|
||||
// protected files are always for *RUNning only
|
||||
if(files.front().is_protected) is_basic = false;
|
||||
// If a file is execute-only, that means *RUN.
|
||||
if(files.front().flags & File::Flags::ExecuteOnly) is_basic = false;
|
||||
|
||||
// check also for a continuous threading of BASIC lines; if none then this probably isn't BASIC code,
|
||||
// so that's also justification to *RUN
|
||||
|
@ -109,7 +109,12 @@ static std::unique_ptr<File> GetNextFile(std::deque<File::Chunk> &chunks) {
|
||||
file->name = file->chunks.front().name;
|
||||
file->load_address = file->chunks.front().load_address;
|
||||
file->execution_address = file->chunks.front().execution_address;
|
||||
file->is_protected = !!(file->chunks.back().block_flag & 0x01); // I think the last flags are the ones that count; TODO: check.
|
||||
// I think the final chunk's flags are the ones that count; TODO: check.
|
||||
if(file->chunks.back().block_flag & 0x01) {
|
||||
// File is locked, which in more generalised terms means it is
|
||||
// for execution only.
|
||||
file->flags |= File::Flags::ExecuteOnly;
|
||||
}
|
||||
|
||||
// copy all data into a single big block
|
||||
for(File::Chunk chunk : file->chunks) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user