Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ target_sources(redumper
"filesystem/iso9660/iso9660_entry.ixx"
"filesystem/iso9660/iso9660_map.ixx"
"filesystem/udf/udf.ixx"
"filesystem/udf/udf_browser.ixx"
"filesystem/udf/udf_defs.ixx"
"hash/block_hasher.ixx"
"hash/md5.ixx"
Expand Down
1 change: 1 addition & 0 deletions filesystem/udf/udf.ixx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export module filesystem.udf;

export import :browser;
export import :defs;
112 changes: 112 additions & 0 deletions filesystem/udf/udf_browser.ixx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
module;
#include <memory>
#include <optional>
#include <vector>

export module filesystem.udf:browser;

import :defs;

import cd.cdrom;
import readers.data_reader;

export namespace gpsxre::udf
{

class Browser
{
public:
const static std::optional<udf::AnchorVolumeDescriptorPointer> findAnchorVolumeDescriptorPointer(DataReader *data_reader)
{
std::vector<uint8_t> data(FORM1_DATA_SIZE);
data_reader->read(data.data(), AVDP_PRIMARY_LBA, 1);

auto const &avdp = (udf::AnchorVolumeDescriptorPointer &)data[0];
Comment thread
coderabbitai[bot] marked this conversation as resolved.

if(avdp.descriptor_tag.tag_identifier == udf::TagIdentifier::ANCHOR_POINTER)
{
return avdp;
}
else
{
return {};
}
}

const static std::optional<uint32_t> findDescriptorOffset(DataReader *data_reader, ExtentDescriptor main_vds, TagIdentifier type)
{
for(uint32_t lba = main_vds.location; lba <= main_vds.location + ((main_vds.length - 1) / FORM1_DATA_SIZE); ++lba)
{
std::vector<uint8_t> data(FORM1_DATA_SIZE);
data_reader->read(data.data(), lba, 1);

auto const &tag = (udf::DescriptorTag &)data[0];

if(tag.tag_identifier == type)
{
return lba;
}
else if(tag.tag_identifier == udf::TagIdentifier::TERMINATING)
{
break;
}
}

return {};
}

static std::shared_ptr<udf::FileEntry> rootDirectory(DataReader *data_reader)
{
// Try and find the AVDP so we can look through the volume descriptor sequence.
auto const avdp = findAnchorVolumeDescriptorPointer(data_reader);
if(avdp.has_value())
{
// Search for a partition in the volume descriptor sequence. This is probably partition 0.
auto partition_offset = findDescriptorOffset(data_reader, avdp.value().main_vds, udf::TagIdentifier::PARTITION);
if(!partition_offset.has_value())
{
return {};
}
std::vector<uint8_t> partition_data(FORM1_DATA_SIZE);

data_reader->read(partition_data.data(), partition_offset.value(), 1);
struct udf::PartitionDescriptor partition_descriptor = (udf::PartitionDescriptor &)partition_data[0];

// Search for the logical volume descriptor. This contains the extent of the file set descriptor.
auto lvd_offset = findDescriptorOffset(data_reader, avdp.value().main_vds, udf::TagIdentifier::LOGICAL);
if(!lvd_offset.has_value())
{
return {};
}
std::vector<uint8_t> lvd_data(FORM1_DATA_SIZE);
data_reader->read(lvd_data.data(), lvd_offset.value(), 1);
struct udf::LogicalVolumeDescriptor logical_volume_descriptor = (udf::LogicalVolumeDescriptor &)lvd_data[0];

auto const &file_set_descriptor_extent = (udf::long_ad &)logical_volume_descriptor.logical_volume_contents_use;

// If we have the partition the file set descriptor is found in, parse it to find the root directory ICB extent.
if(partition_descriptor.partition_number == file_set_descriptor_extent.extent_location.partition_reference_number)
{
std::vector<uint8_t> fsd_data(FORM1_DATA_SIZE);
data_reader->read(fsd_data.data(), partition_descriptor.partition_starting_location + file_set_descriptor_extent.extent_location.logical_block_number, 1);
auto const &file_set_descriptor = (udf::FileSetDescriptor &)fsd_data[0];

auto const &root_directory_icb_extent = file_set_descriptor.root_directory_icb;

// If we have the partition the root directory is in, read and return it.
if(partition_descriptor.partition_number == root_directory_icb_extent.extent_location.partition_reference_number)
{
std::vector<uint8_t> root_directory_data(FORM1_DATA_SIZE);
data_reader->read(root_directory_data.data(), partition_descriptor.partition_starting_location + root_directory_icb_extent.extent_location.logical_block_number, 1);
auto const &root_directory_file_entry = (udf::FileEntry &)root_directory_data[0];

return std::make_shared<udf::FileEntry>(root_directory_file_entry);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}
}

return {};
}
};

}
120 changes: 118 additions & 2 deletions filesystem/udf/udf_defs.ixx
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,135 @@ struct AnchorVolumeDescriptorPointer
uint8_t reserved[480];
};

struct timestamp
{
uint16_t type_and_timezone;
int16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
uint8_t centiseconds;
uint8_t hundreds_of_microseconds;
uint8_t microseconds;
};

struct charspec
{
uint8_t character_set_type;
uint8_t character_set_info[63];
};

struct lb_addr
{
uint32_t logical_block_number;
uint16_t partition_reference_number;
};

struct long_ad
{
uint32_t extent_length;
lb_addr extent_location;
uint8_t implementation_use[6];
};

struct EntityID
{
uint8_t flags;
uint8_t identifier[23];
uint8_t identifier_suffix[8];
};

struct LogicalVolumeDescriptor
{
DescriptorTag descriptor_tag;
uint32_t volume_descriptor_sequence_number;
charspec descriptor_character_set;
char logical_volume_identifier[128];
uint32_t logical_block_size;
EntityID domain_identifier;
uint8_t logical_volume_contents_use[16];
uint32_t map_table_length;
uint32_t number_of_partition_maps;
EntityID implementation_identifier;
uint8_t implementation_use[128];
ExtentDescriptor integrity_sequence_extent;
uint8_t PartitionMaps[];
};

struct FileSetDescriptor
{
DescriptorTag descriptor_tag;
timestamp recording_date_and_time;
uint16_t interchange_level;
uint16_t maximum_interchange_level;
uint32_t character_set_list;
uint32_t maximum_character_set_list;
uint32_t file_set_number;
uint32_t file_set_descriptor_number;
charspec logical_volume_identifier_character_set;
char logical_volume_identifier[128];
charspec file_set_character_set;
char file_set_identifier[32];
char copyright_file_identifier[32];
char abstract_file_identifier[32];
long_ad root_directory_icb;
EntityID domain_identifier;
long_ad next_extent;
long_ad system_stream_directory_icb;
uint8_t reserved[32];
};

struct icbtag
{
uint32_t prior_recorded_number_of_direct_entries;
uint16_t strategy_type;
uint8_t strategy_parameter[2];
uint16_t maximum_number_of_entries;
uint8_t reserved;
uint8_t file_type;
lb_addr parent_icb_location;
uint16_t flags;
};

struct FileEntry
{
DescriptorTag descriptor_tag;
icbtag icb_tag;
uint32_t uid;
uint32_t gid;
uint32_t permissions;
uint16_t file_link_count;
uint8_t record_format;
uint8_t record_display_attributes;
uint32_t record_length;
uint64_t information_length;
uint64_t logical_blocks_recorded;
timestamp access_time;
timestamp modification_time;
timestamp attribute_time;
uint32_t checkpoint;
long_ad extended_attribute_icb;
EntityID implementation_identifier;
uint64_t unique_id;
uint32_t length_of_extended_attributes;
uint32_t length_of_allocation_descriptors;
uint8_t extended_attributes_and_allocation_descriptors[];
};

struct PartitionDescriptor
{
DescriptorTag descriptor_tag;
uint32_t volume_descriptor_sequence_number;
uint16_t partition_flags;
uint16_t partition_number;
uint8_t partition_contents[32];
EntityID partition_contents;
uint8_t partition_contents_use[128];
uint32_t access_type;
uint32_t partition_starting_location;
uint32_t partition_length;
uint8_t implementation_identifier[32];
EntityID implementation_identifier;
uint8_t implementation_use[128];
uint8_t reserved[156];
};
Expand Down