Skip to content
Merged
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 @@ -153,6 +153,7 @@ target_sources(redumper
"filesystem/udf/udf.ixx"
"filesystem/udf/udf_browser.ixx"
"filesystem/udf/udf_defs.ixx"
"filesystem/udf/udf_size.ixx"
"hash/block_hasher.ixx"
"hash/md5.ixx"
"hash/sha1.ixx"
Expand Down
37 changes: 24 additions & 13 deletions dvd/dvd_dump.ixx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import dvd.nintendo;
import dvd.xbox;
import filesystem.iso9660;
import filesystem.udf;
import filesystem.udf_size;
import interval_set;
import options;
import range;
Expand Down Expand Up @@ -415,6 +416,7 @@ struct FilesystemContext
bool search = true;
bool udf = false;
std::vector<std::pair<uint32_t, uint32_t>> udf_vds;
std::optional<udf::ExtentDescriptor> udf_reserve_vds;
};


Expand Down Expand Up @@ -449,19 +451,23 @@ std::optional<std::pair<uint32_t, bool>> filesystem_search_size(FilesystemContex
}
}

if(ctx.udf)
// A valid primary AVDP is sufficient to detect UDF even when the volume
// recognition sequence is absent or not recognized.
if(lba == udf::AVDP_PRIMARY_LBA)
{
if(lba == udf::AVDP_PRIMARY_LBA)
if(auto const &avdp = (udf::AnchorVolumeDescriptorPointer &)data[0]; avdp.descriptor_tag.tag_identifier == udf::TagIdentifier::ANCHOR_POINTER && avdp.descriptor_tag.tag_location == lba)
{
if(auto const &avdp = (udf::AnchorVolumeDescriptorPointer &)data[0]; avdp.descriptor_tag.tag_identifier == udf::TagIdentifier::ANCHOR_POINTER)
{
// ordering is intentional
ctx.udf_vds.emplace_back(avdp.reserve_vds.location, scale_up(avdp.reserve_vds.length, FORM1_DATA_SIZE));
ctx.udf_vds.emplace_back(avdp.main_vds.location, scale_up(avdp.main_vds.length, FORM1_DATA_SIZE));
}
ctx.udf = true;
// ordering is intentional
ctx.udf_vds.emplace_back(avdp.reserve_vds.location, scale_up(avdp.reserve_vds.length, FORM1_DATA_SIZE));
ctx.udf_vds.emplace_back(avdp.main_vds.location, scale_up(avdp.main_vds.length, FORM1_DATA_SIZE));
ctx.udf_reserve_vds = avdp.reserve_vds;
}
}

if(!ctx.udf_vds.empty() && ctx.udf_vds.back().first + ctx.udf_vds.back().second <= lba)
if(ctx.udf)
{
if(!ctx.udf_vds.empty() && (uint64_t)ctx.udf_vds.back().first + ctx.udf_vds.back().second <= lba)
{
std::vector<uint8_t> sector_data_file(ctx.udf_vds.back().second * FORM1_DATA_SIZE);
std::vector<State> sector_state_file(ctx.udf_vds.back().second);
Expand All @@ -482,17 +488,22 @@ std::optional<std::pair<uint32_t, bool>> filesystem_search_size(FilesystemContex
{
auto const &partition = (udf::PartitionDescriptor &)sector_data_file[i * FORM1_DATA_SIZE];

sectors_count = std::max(sectors_count, partition.partition_starting_location + partition.partition_length);
uint64_t partition_end = (uint64_t)partition.partition_starting_location + partition.partition_length;
if(partition_end <= std::numeric_limits<uint32_t>::max())
sectors_count = std::max(sectors_count, (uint32_t)partition_end);
}
else if(tag.tag_identifier == udf::TagIdentifier::TERMINATING)
break;
}

ctx.udf_vds.clear();

if(sectors_count)
// account for trailing AVDP
ss = std::make_pair(sectors_count + 1, true);
auto reserve_vds = ctx.udf_reserve_vds.value_or(udf::ExtentDescriptor{});
if(auto volume_sectors_count = udf::get_volume_sectors_count(sectors_count, reserve_vds.location, reserve_vds.length, FORM1_DATA_SIZE); volume_sectors_count)
{
ss = std::make_pair(*volume_sectors_count, true);
ctx.udf = false;
}
}
else
ctx.udf_vds.pop_back();
Expand Down
30 changes: 30 additions & 0 deletions filesystem/udf/udf_size.ixx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module;
#include <algorithm>
#include <cstdint>
#include <limits>
#include <optional>

export module filesystem.udf_size;



export namespace gpsxre::udf
{

constexpr std::optional<uint32_t> get_volume_sectors_count(uint32_t partition_end, uint32_t reserve_vds_location, uint32_t reserve_vds_length, uint32_t sector_size)
{
if(!partition_end || !sector_size)
return std::nullopt;

uint64_t reserve_length = ((uint64_t)reserve_vds_length + sector_size - 1) / sector_size;
uint64_t reserve_end = reserve_length ? (uint64_t)reserve_vds_location + reserve_length : 0;
uint64_t metadata_end = std::max<uint64_t>(partition_end, reserve_end);

// Account for the trailing AVDP immediately following the filesystem metadata.
if(metadata_end >= std::numeric_limits<uint32_t>::max())
return std::nullopt;

return metadata_end + 1;
}

}
5 changes: 5 additions & 0 deletions tests/gtest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,8 @@ add_gtest(gtest_bit_copy
SOURCE "test_bit_copy.cc"
MODULES "${CMAKE_SOURCE_DIR}/utils/misc.ixx"
)

add_gtest(gtest_udf
SOURCE "test_udf.cc"
MODULES "${CMAKE_SOURCE_DIR}/filesystem/udf/udf_size.ixx"
)
38 changes: 38 additions & 0 deletions tests/gtest/test_udf.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include <cstdint>
#include <gtest/gtest.h>
#include <limits>
#include <optional>

import filesystem.udf_size;

using namespace gpsxre;


TEST(UDF, VolumeSectorsCountIncludesReserveVDSAndTrailingAVDP)
{
constexpr uint32_t sector_size = 2048;
constexpr uint32_t partition_start = 277;
constexpr uint32_t partition_length = 23728682;
EXPECT_EQ(udf::get_volume_sectors_count(partition_start + partition_length, 23728959, 32768, sector_size), 23728976);
}


TEST(UDF, VolumeSectorsCountUsesPartitionEndWithoutReserveVDS)
{
EXPECT_EQ(udf::get_volume_sectors_count(1000, 0, 0, 2048), 1001);
}


TEST(UDF, VolumeSectorsCountUsesLargestMetadataEnd)
{
EXPECT_EQ(udf::get_volume_sectors_count(1000, 500, 2048, 2048), 1001);
EXPECT_EQ(udf::get_volume_sectors_count(900, 1000, 2049, 2048), 1003);
}


TEST(UDF, VolumeSectorsCountRejectsInvalidOrOverflowingValues)
{
EXPECT_EQ(udf::get_volume_sectors_count(0, 0, 0, 2048), std::nullopt);
EXPECT_EQ(udf::get_volume_sectors_count(1000, 0, 0, 0), std::nullopt);
EXPECT_EQ(udf::get_volume_sectors_count(1000, std::numeric_limits<uint32_t>::max(), 2048, 2048), std::nullopt);
}
Loading