diff --git a/iso9660/byte_position.py b/iso9660/byte_position.py new file mode 100644 index 0000000..61fef12 --- /dev/null +++ b/iso9660/byte_position.py @@ -0,0 +1,119 @@ +import sys +from dataclasses import dataclass +from os.path import splitext + +from csv_input import read_input +from generate import renderer + +from pprint import pprint + +def parse_bp(s): + if ' to ' in s: + start0, end0 = s.split(' to ') + if '(' in end0 and ')' in end0 and 'LEN_' in end0: + end0 = int(start0) - 1 + start, end = int(start0), int(end0) + return start, end + else: + start, end = int(start0), int(end0) + assert start <= end, (start, end) + return start, end + else: + start = int(s) + return start, start + +def bp_range(start, end): + return set(range(start, end+1)) + +reserved = 0 +def sanitize_field_name(name): + global reserved + if name == "(Reserved for future standardization)" or name == "Unused field": + reserved += 1 + return f"_res{reserved}"; + if '(' in name: + assert 'LEN_' in name, name + name = name.split('(')[0].strip() + + name = name.lower().replace(' ', '_') + return name + +def sanitize_content_name(name): + if name == 'Numerical value': + return 'numerical_value' + else: + return 'bytes' + +@dataclass +class Field: + start: int + end: int + name: str + content: str + +def parse(rows): + seen_bps = set() + seen_names = set() + + for row in rows: + start, end = parse_bp(row['BP']) + _range = bp_range(start, end) + assert seen_bps.intersection(_range) == set(), row + seen_bps = seen_bps.union(_range) + field_name = sanitize_field_name(row["Field name"]) + assert field_name not in seen_names + seen_names.add(field_name) + content_name = sanitize_content_name(row["Content"]) + + yield Field( + start=start, + end=end, + name=field_name, + content=content_name + ) + +type_dict = { + 1: 'uint8_t', + 2: 'uint16_t', + 4: 'uint16_le_be', + 8: 'uint32_le_be', +} + +def header(): + yield "#pragma once" + yield "" + yield "#include " + yield "#include " + yield "" + yield '#include "uint_le_be.hpp"' + yield "" + +def render_fields(input_name, fields): + yield f"struct {input_name} {{" + for field in fields: + field_size = (field.end - field.start) + 1 + if field.content == 'numerical_value': + assert field_size in type_dict, field + type = type_dict[field_size] + yield f"const {type} {field.name};" + else: + if field_size == 1: + yield f"const uint8_t {field.name};" + elif field_size == 0: + yield f"const uint8_t {field.name}[];" + else: + yield f"const uint8_t {field.name}[{field_size}];" + yield "};" + + for field in fields: + yield f"static_assert((offsetof (struct {input_name}, {field.name})) == {field.start - 1});" + +if __name__ == "__main__": + input_file = sys.argv[1] + input_name, _ = splitext(input_file) + rows = read_input(input_file) + fields = list(parse(rows)) + render, out = renderer() + render(header()) + render(render_fields(input_name, fields)) + sys.stdout.write(out.getvalue()) diff --git a/iso9660/csv_input.py b/iso9660/csv_input.py new file mode 120000 index 0000000..3c0b97f --- /dev/null +++ b/iso9660/csv_input.py @@ -0,0 +1 @@ +../regs/gen/csv_input.py \ No newline at end of file diff --git a/iso9660/directory_record.csv b/iso9660/directory_record.csv new file mode 100644 index 0000000..0de1021 --- /dev/null +++ b/iso9660/directory_record.csv @@ -0,0 +1,12 @@ +"BP","Field name","Content" +1,"Length of directory record (LEN_DR)","Numerical value" +2,"Extended attribute record length","Numerical value" +"3 to 10","Location of extent","Numerical value" +"11 to 18","Data length","Numerical value" +"19 to 25","Recording date and time","Recording date and time" +26,"File flags","8 bits" +27,"File unit size","Numerical value" +28,"Interleave gap size","Numerical value" +"29 to 32","Volume sequence number","Numerical value" +33,"Length of file identifier (LEN_FI)","Numerical value" +"34 to (33 + LEN_FI)","File identifier","d-characters, d1-characters, separator 1, separator 2, (00) or (01) byte" diff --git a/iso9660/directory_record.hpp b/iso9660/directory_record.hpp new file mode 100644 index 0000000..63f2c7e --- /dev/null +++ b/iso9660/directory_record.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +#include "uint_le_be.hpp" + +struct directory_record { + const uint8_t length_of_directory_record; + const uint8_t extended_attribute_record_length; + const uint32_le_be location_of_extent; + const uint32_le_be data_length; + const uint8_t recording_date_and_time[7]; + const uint8_t file_flags; + const uint8_t file_unit_size; + const uint8_t interleave_gap_size; + const uint16_le_be volume_sequence_number; + const uint8_t length_of_file_identifier; + const uint8_t file_identifier[]; +}; + +static_assert((offsetof (struct directory_record, length_of_directory_ecord)) == 0); +static_assert((offsetof (struct directory_record, extended_attribute_record_length)) == 1); +static_assert((offsetof (struct directory_record, location_of_extent)) == 2); +static_assert((offsetof (struct directory_record, data_length)) == 10); +static_assert((offsetof (struct directory_record, recording_date_and_time)) == 18); +static_assert((offsetof (struct directory_record, file_flags)) == 25); +static_assert((offsetof (struct directory_record, file_unit_size)) == 26); +static_assert((offsetof (struct directory_record, interleave_gap_size)) == 27); +static_assert((offsetof (struct directory_record, volume_sequence_number)) == 28); +static_assert((offsetof (struct directory_record, length_of_file_identifier)) == 32); +static_assert((offsetof (struct directory_record, file_identifier)) == 33); diff --git a/iso9660/directory_record.ods b/iso9660/directory_record.ods index 9cf171e..2332fc4 100644 Binary files a/iso9660/directory_record.ods and b/iso9660/directory_record.ods differ diff --git a/iso9660/generate.py b/iso9660/generate.py new file mode 120000 index 0000000..7242ef6 --- /dev/null +++ b/iso9660/generate.py @@ -0,0 +1 @@ +../regs/gen/generate.py \ No newline at end of file diff --git a/iso9660/primary_volume_descriptor.csv b/iso9660/primary_volume_descriptor.csv new file mode 100644 index 0000000..91eb67b --- /dev/null +++ b/iso9660/primary_volume_descriptor.csv @@ -0,0 +1,34 @@ +"BP","Field name","Content" +"1","Volume descriptor type","Numerical value" +"2 to 6","Standard identifier","CD001" +"7","Volume descriptor version","Numerical value" +"8","Unused field","(00) byte" +"9 to 40","System identifier","a-characters" +"41 to 72","Volume identifier","d-characters" +"73 to 80","Unused field","(00) bytes" +"81 to 88","Volume space size","Numerical value" +"89 to 120","Unused field","(00) bytes" +"121 to 124","Volume set size","Numerical value" +"125 to 128","Volume sequence number","Numerical value" +"129 to 132","Logical block size","Numerical value" +"133 to 140","Path table size","Numerical value" +"141 to 144","Location of occurrence of type L path table","Numerical value" +"145 to 148","Location of optional occurence of type L path table","Numerical value" +"149 to 152","Location of occurence of type M path table","Numerical value" +"153 to 156","Location of optional occurence of type M path table","Numerical value" +"157 to 190","Directory record for root directory","34 bytes" +"191 to 318","Volume set identifier","d-characters" +"319 to 446","Publisher identifier","a-characters" +"447 to 574","Data preparer identifier","a-characters" +"575 to 702","Application identifier","a-characters" +"703 to 739","Copyright file identifier","d-characters, separator 1, separator 2" +"740 to 776","Abstract file identifier","d-characters, separator 1, separator 3" +"777 to 813","Bibliographic file identifier","d-characters, separator 1, separator 4" +"814 to 830","Volume creation date and time","Digit(s), numerical value" +"831 to 847","Volume modification date and time","Digit(s), numerical value" +"848 to 864","Volume expiration date and time","Digit(s), numerical value" +"865 to 881","Volume effective date and time","Digit(s), numerical value" +"882","File structure version","numerical value" +"883","(Reserved for future standardization)","(00) byte" +"884 to 1395","Application use","not specified" +"1396 to 2048","(Reserved for future standardization)","(00) bytes" diff --git a/iso9660/primary_volume_descriptor.hpp b/iso9660/primary_volume_descriptor.hpp new file mode 100644 index 0000000..6aa75d6 --- /dev/null +++ b/iso9660/primary_volume_descriptor.hpp @@ -0,0 +1,76 @@ +#pragma once + +#include +#include + +#include "uint_le_be.hpp" + +struct primary_volume_descriptor { + const uint8_t volume_descriptor_type; + const uint8_t standard_identifier[5]; + const uint8_t volume_descriptor_version; + const uint8_t _res1; + const uint8_t system_identifier[32]; + const uint8_t volume_identifier[32]; + const uint8_t _res2[8]; + const uint32_le_be volume_space_size; + const uint8_t _res3[32]; + const uint16_le_be volume_set_size; + const uint16_le_be volume_sequence_number; + const uint16_le_be logical_block_size; + const uint32_le_be path_table_size; + const uint16_le_be location_of_occurrence_of_type_l_path_table; + const uint16_le_be location_of_optional_occurence_of_type_l_path_table; + const uint16_le_be location_of_occurence_of_type_m_path_table; + const uint16_le_be location_of_optional_occurence_of_type_m_path_table; + const uint8_t directory_record_for_root_directory[34]; + const uint8_t volume_set_identifier[128]; + const uint8_t publisher_identifier[128]; + const uint8_t data_preparer_identifier[128]; + const uint8_t application_identifier[128]; + const uint8_t copyright_file_identifier[37]; + const uint8_t abstract_file_identifier[37]; + const uint8_t bibliographic_file_identifier[37]; + const uint8_t volume_creation_date_and_time[17]; + const uint8_t volume_modification_date_and_time[17]; + const uint8_t volume_expiration_date_and_time[17]; + const uint8_t volume_effective_date_and_time[17]; + const uint8_t file_structure_version; + const uint8_t _res4; + const uint8_t application_use[512]; + const uint8_t _res5[653]; +}; + +static_assert((offsetof (struct primary_volume_descriptor, volume_descriptor_type)) == 0); +static_assert((offsetof (struct primary_volume_descriptor, standard_identifier)) == 1); +static_assert((offsetof (struct primary_volume_descriptor, volume_descriptor_version)) == 6); +static_assert((offsetof (struct primary_volume_descriptor, _res1)) == 7); +static_assert((offsetof (struct primary_volume_descriptor, system_identifier)) == 8); +static_assert((offsetof (struct primary_volume_descriptor, volume_identifier)) == 40); +static_assert((offsetof (struct primary_volume_descriptor, _res2)) == 72); +static_assert((offsetof (struct primary_volume_descriptor, volume_space_size)) == 80); +static_assert((offsetof (struct primary_volume_descriptor, _res3)) == 88); +static_assert((offsetof (struct primary_volume_descriptor, volume_set_size)) == 120); +static_assert((offsetof (struct primary_volume_descriptor, volume_sequence_number)) == 124); +static_assert((offsetof (struct primary_volume_descriptor, logical_block_size)) == 128); +static_assert((offsetof (struct primary_volume_descriptor, path_table_size)) == 132); +static_assert((offsetof (struct primary_volume_descriptor, location_of_occurrence_of_type_l_path_table)) == 140); +static_assert((offsetof (struct primary_volume_descriptor, location_of_optional_occurence_of_type_l_path_table)) == 144); +static_assert((offsetof (struct primary_volume_descriptor, location_of_occurence_of_type_m_path_table)) == 148); +static_assert((offsetof (struct primary_volume_descriptor, location_of_optional_occurence_of_type_m_path_table)) == 152); +static_assert((offsetof (struct primary_volume_descriptor, directory_record_for_root_directory)) == 156); +static_assert((offsetof (struct primary_volume_descriptor, volume_set_identifier)) == 190); +static_assert((offsetof (struct primary_volume_descriptor, publisher_identifier)) == 318); +static_assert((offsetof (struct primary_volume_descriptor, data_preparer_identifier)) == 446); +static_assert((offsetof (struct primary_volume_descriptor, application_identifier)) == 574); +static_assert((offsetof (struct primary_volume_descriptor, copyright_file_identifier)) == 702); +static_assert((offsetof (struct primary_volume_descriptor, abstract_file_identifier)) == 739); +static_assert((offsetof (struct primary_volume_descriptor, bibliographic_file_identifier)) == 776); +static_assert((offsetof (struct primary_volume_descriptor, volume_creation_date_and_time)) == 813); +static_assert((offsetof (struct primary_volume_descriptor, volume_modification_date_and_time)) == 830); +static_assert((offsetof (struct primary_volume_descriptor, volume_expiration_date_and_time)) == 847); +static_assert((offsetof (struct primary_volume_descriptor, volume_effective_date_and_time)) == 864); +static_assert((offsetof (struct primary_volume_descriptor, file_structure_version)) == 881); +static_assert((offsetof (struct primary_volume_descriptor, _res4)) == 882); +static_assert((offsetof (struct primary_volume_descriptor, application_use)) == 883); +static_assert((offsetof (struct primary_volume_descriptor, _res5)) == 1395); diff --git a/iso9660/test.cpp b/iso9660/test.cpp new file mode 100644 index 0000000..1fdab1d --- /dev/null +++ b/iso9660/test.cpp @@ -0,0 +1,66 @@ +#include +#include +#include + +#include "primary_volume_descriptor.hpp" +#include "directory_record.hpp" + +void write_field(const uint8_t * s, const int len) +{ + for (int i = 0; i < len; i++) { + std::cout << s[i]; + } + std::cout << '\n'; +} + +int main() +{ + std::ifstream ifs("test.iso"); + std::string content( (std::istreambuf_iterator(ifs) ), + (std::istreambuf_iterator() ) ); + + const char * str = content.c_str(); + auto pvd = reinterpret_cast(&str[2048 * 16]); + + write_field(pvd->standard_identifier, 5); + + auto root_dr = reinterpret_cast(&pvd->directory_record_for_root_directory); + + std::cout << "root directory record:" << '\n'; + std::cout << root_dr->location_of_extent.get() << '\n'; + std::cout << root_dr->data_length.get() << '\n'; + + uint32_t offset = root_dr->location_of_extent.get() * 2048; + while (true) { + std::cout << "\n\n"; + std::cout << "directory entry offset: " << std::hex << offset << '\n'; + auto dr = reinterpret_cast(&str[offset]); + if (dr->length_of_directory_record == 0) + break; + + std::cout << "length_of_directory_record: "; + std::cout << std::dec << (int)dr->length_of_directory_record << '\n'; + std::cout << "length_of_file_identifier: "; + std::cout << (int)dr->length_of_file_identifier << '\n'; + std::cout << "file_identifier: "; + write_field(dr->file_identifier, dr->length_of_file_identifier); + std::cout << std::hex << "file_flags: " << (int)dr->file_flags << '\n'; + + if (dr->file_flags == 0) { + std::cout << "location_of_extent: " << dr->location_of_extent.get() << '\n'; + std::cout << "data_length: " << std::dec << dr->data_length.get() << '\n'; + + if (dr->file_identifier[0] != '1') { + const uint32_t extent = dr->location_of_extent.get() * 2048; + auto file = reinterpret_cast(&str[extent]); + std::cout << "---begin file content---\n"; + write_field(file, dr->data_length.get()); + std::cout << "---end file content---\n"; + } + } + + offset += dr->length_of_directory_record; + } + + return 0; +} diff --git a/iso9660/uint_le_be.hpp b/iso9660/uint_le_be.hpp new file mode 100644 index 0000000..1e6ded3 --- /dev/null +++ b/iso9660/uint_le_be.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include + +template +struct uint_le_be { + const uint8_t le[(sizeof (T))]; + const uint8_t be[(sizeof (T))]; + + T get() const { + union { + uint8_t u8[(sizeof (T))]; + T uint; + } value; + static_assert((sizeof (value)) == (sizeof (T))); + + if constexpr (std::endian::native == std::endian::little) { + for (uint32_t i = 0; i < (sizeof (T)); i++) { + value.u8[i] = le[i]; + } + return value.uint; + } else { + for (uint32_t i = 0; i < (sizeof (T)); i++) { + value.u8[i] = be[i]; + } + return value.uint; + } + } +}; + +using uint16_le_be = uint_le_be; +using uint32_le_be = uint_le_be; + +static_assert((sizeof (uint16_le_be)) == 4); +static_assert((sizeof (uint32_le_be)) == 8);