diff --git a/Aaru.Decryption/Aacs/HDDVD.cs b/Aaru.Decryption/Aacs/HDDVD.cs new file mode 100644 index 000000000..8a7f0914e --- /dev/null +++ b/Aaru.Decryption/Aacs/HDDVD.cs @@ -0,0 +1,65 @@ +// /*************************************************************************** +// Aaru Data Preservation Suite +// ---------------------------------------------------------------------------- +// +// Filename : HDDVD.cs +// Author(s) : Rebecca Wallander +// +// --[ Description ] ---------------------------------------------------------- +// +// HD DVD video disc helpers. +// +// --[ License ] -------------------------------------------------------------- +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// ---------------------------------------------------------------------------- +// Copyright © 2026 Rebecca Wallander +// ****************************************************************************/ + +using Aaru.CommonTypes; +using Aaru.CommonTypes.Enums; +using Aaru.CommonTypes.Interfaces; +using Aaru.CommonTypes.Structs; + +namespace Aaru.Decryption.Aacs; + +/// Aligns contiguous 2048-byte sectors to CPS units for decrypt during image conversion. +public sealed class HDDVD +{ + /// + /// HD DVD video discs always have a HVDVD_TS folder. If it doesn't have one, it's not a HD DVD video. + /// + /// IOpticalMediaImage to check for HVDVD_TS folder in. + /// IReadOnlyFilesystem to check in. + /// Partition to check in. + /// true if HVDVD_TS folder was found. + public static bool HasHdDvdVideoTsFolder(IOpticalMediaImage input, IReadOnlyFilesystem fs, Partition partition) + { + ErrorNumber error = fs.Mount(input, partition, null, null, null); + + if(error != ErrorNumber.NoError) return false; + + error = fs.Stat("HVDVD_TS", out FileEntryInfo stat); + fs.Unmount(); + + return error == ErrorNumber.NoError; + } +} \ No newline at end of file diff --git a/Aaru.Images/ZZZRawImage/HdDvdIsoProbe.cs b/Aaru.Images/ZZZRawImage/HdDvdIsoProbe.cs new file mode 100644 index 000000000..3abb8f093 --- /dev/null +++ b/Aaru.Images/ZZZRawImage/HdDvdIsoProbe.cs @@ -0,0 +1,189 @@ +// /*************************************************************************** +// Aaru Data Preservation Suite +// ---------------------------------------------------------------------------- +// +// Filename : HdDvdIsoProbe.cs +// Author(s) : Rebecca Wallander +// +// Component : Disk image plugins. +// +// --[ Description ] ---------------------------------------------------------- +// +// Detects HD DVD-Video layout on bare .iso images (HVDVD_TS folder). +// Aaru.Core.Partitions.GetAll cannot be called from this assembly because +// Aaru.Core references Aaru.Images; the partition walk below mirrors the +// non-tape, in Partitions.GetAll. If we find a simpler way to detect a +// HD DVD-Video layout, we should use that instead. +// +// --[ License ] -------------------------------------------------------------- +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see +// . +// +// ---------------------------------------------------------------------------- +// Copyright © 2026 Rebecca Wallander +// ****************************************************************************/ + +using System.Collections.Generic; +using System.Linq; +using Aaru.CommonTypes; +using Aaru.CommonTypes.Interfaces; +using Aaru.CommonTypes.Structs; +using Aaru.Decryption.Aacs; + +namespace Aaru.Images; + +public sealed partial class ZZZRawImage +{ + /// True if a filesystem under some partition exposes the HD DVD-Video HVDVD_TS folder. + bool TryDetectHdDvdVideoIso() + { + IMediaImage image = this; + IOpticalMediaImage optical = this; + PluginRegister plugins = PluginRegister.Singleton; + List parts = EnumeratePartitionsForHdDvdProbe(image); + + foreach(Partition partition in parts) + { + foreach(IFilesystem fs in plugins.Filesystems.Values) + { + if(fs is not IReadOnlyFilesystem rofs) + continue; + + if(!fs.Identify(image, partition)) + continue; + + if(HDDVD.HasHdDvdVideoTsFolder(optical, rofs, partition)) + return true; + } + } + + return false; + } + + /// + /// Lists partitions the same way as for images that are not + /// tape. + /// + static List EnumeratePartitionsForHdDvdProbe(IMediaImage image) + { + PluginRegister plugins = PluginRegister.Singleton; + List foundPartitions = []; + List childPartitions = []; + List checkedLocations = []; + + var partitionableImage = image as IPartitionableMediaImage; + + // Getting all partitions from device (e.g. tracks) + if(partitionableImage?.Partitions != null) + { + foreach(Partition imagePartition in partitionableImage.Partitions) + { + foreach(IPartition plugin in plugins.Partitions.Values) + { + if(plugin is null) continue; + + if(!plugin.GetInformation(image, out List partitions, imagePartition.Start)) continue; + + foundPartitions.AddRange(partitions); + } + + checkedLocations.Add(imagePartition.Start); + } + } + + if(!checkedLocations.Contains(0) && image.Info.Sectors > 0) + { + foreach(IPartition plugin in plugins.Partitions.Values) + { + if(plugin is null) + continue; + + if(!plugin.GetInformation(image, out List partitions, 0)) + continue; + + foundPartitions.AddRange(partitions); + } + + checkedLocations.Add(0); + } + + while(foundPartitions.Count > 0) + { + if(checkedLocations.Contains(foundPartitions[0].Start)) + { + childPartitions.Add(foundPartitions[0]); + foundPartitions.RemoveAt(0); + + continue; + } + + List children = []; + + foreach(IPartition plugin in plugins.Partitions.Values) + { + if(plugin is null) + continue; + + if(!plugin.GetInformation(image, out List partitions, foundPartitions[0].Start)) + continue; + + children.AddRange(partitions); + } + + checkedLocations.Add(foundPartitions[0].Start); + + if(children.Count > 0) + { + foundPartitions.RemoveAt(0); + + foreach(Partition child in children) + { + if(checkedLocations.Contains(child.Start)) + childPartitions.Add(child); + else + foundPartitions.Add(child); + } + } + else + { + childPartitions.Add(foundPartitions[0]); + foundPartitions.RemoveAt(0); + } + } + + if(partitionableImage is not null) + { + List startLocations = + childPartitions.ConvertAll(static detectedPartition => detectedPartition.Start); + + if(partitionableImage.Partitions != null) + { + childPartitions.AddRange(partitionableImage.Partitions.Where(imagePartition => + !startLocations.Contains(imagePartition + .Start))); + } + } + + Partition[] childArray = childPartitions.OrderBy(static part => part.Start) + .ThenBy(static part => part.Length) + .ThenBy(static part => part.Scheme) + .ToArray(); + + for(long i = 0; i < childArray.LongLength; i++) + childArray[i].Sequence = (ulong)i; + + return childArray.ToList(); + } +} \ No newline at end of file diff --git a/Aaru.Images/ZZZRawImage/Read.cs b/Aaru.Images/ZZZRawImage/Read.cs index d6257899f..378637ae6 100644 --- a/Aaru.Images/ZZZRawImage/Read.cs +++ b/Aaru.Images/ZZZRawImage/Read.cs @@ -619,6 +619,16 @@ public sealed partial class ZZZRawImage else if(gcMagic == NGC_GC_MAGIC) _imageInfo.MediaType = MediaType.GOD; } + // Check for HD DVD video discs: .iso extension, size divisible by 2048, contains HVDVD_TS folder + // Needed for identifying the correct AACS path, so we don't misidentify it as a BD video disc + if(_extension == ".iso" && + _imageInfo.SectorSize == 2048 && + _imageInfo.ImageSize % 2048 == 0 && + _imageInfo.Sectors > 0 && + (_imageInfo.MediaType == MediaType.BDR || _imageInfo.MediaType == MediaType.BDRXL) && + TryDetectHdDvdVideoIso()) + _imageInfo.MediaType = MediaType.HDDVDROM; + // Check for bare .bca sidecar (64 bytes) for GameCube/Wii discs if(_imageInfo.MediaType is MediaType.GOD or MediaType.WOD && !_mediaTags.ContainsKey(MediaTagType.DVD_BCA)) {