25 Commits
Author SHA1 Message Date
Deterous 23f8dce272 Bump version, provide build script 2024-02-04 12:09:11 +09:00
Deterous 8e45432aab Throw error for BD-Video hybrid discs without layerbreak 2024-02-03 23:15:13 +09:00
Deterous e9bb0ae00a Allow for output folder argument, bump version 2024-02-01 10:55:23 +09:00
Deterous 4116c56b7d Add verbose flag to IRDKit, improve printing 2024-02-01 10:31:07 +09:00
Deterous e30ac0961a Finalise diff function, bump version 2024-02-01 00:52:35 +09:00
Deterous 1ef9f06e35 Improve diff 2024-01-31 23:41:46 +09:00
Deterous 563b05b44f Bump version 2024-01-31 20:24:34 +09:00
Deterous 07a3a3a7f1 Correct number of files for non-contiguous ISOs 2024-01-31 20:13:42 +09:00
Deterous df9bb8c7f2 Diff command for comparing IRDs 2024-01-31 16:56:35 +09:00
Deterous 1a74e8fed0 Better path handling 2024-01-31 15:03:37 +09:00
Deterous e6f6d1f41a Hash non-contiguous files 2024-01-31 14:44:35 +09:00
Deterous 2fe56e994e Add DiscUtils submodule 2024-01-30 18:19:56 +09:00
Deterous e910312d6e Fix param parsing 2023-12-23 08:56:13 +13:00
Deterous 26bde9d753 Simplify conditionals 2023-12-23 00:07:02 +13:00
Deterous c1fa640e0b Don't assume PARAM.SFO fields exist 2023-12-22 23:53:39 +13:00
Deterous ec6caca3e9 Output file path flag 2023-12-06 13:26:05 +13:00
Deterous 8ccbc8430c Print filename for ISO info, remove trailing comma for JSON 2023-12-06 12:56:31 +13:00
Deterous a036481560 Proper printing 2023-12-05 00:02:09 +13:00
Deterous ba7bacc11c Print all data to file 2023-12-04 23:51:07 +13:00
Deterous 8b82ec3d4e Catch filenotfound errors for non-PS3 ISOs 2023-12-04 23:45:04 +13:00
Deterous 0443ba5b9f Fix info command when not given outpath 2023-12-04 23:40:46 +13:00
Deterous 493c92220a Allow info command to operate in directory 2023-12-04 23:35:52 +13:00
Deterous 602394a182 Automatically detect key files with same name 2023-11-30 13:37:27 +13:00
Deterous 338f7a00b7 Fix issue with manual IRD creation 2023-11-30 12:51:15 +13:00
Deterous 7f9fd1c9d6 Fix info printing 2023-11-30 12:36:24 +13:00
13 changed files with 1064 additions and 330 deletions
+3
View File
@@ -0,0 +1,3 @@
[submodule "DiscUtils"]
path = DiscUtils
url = https://github.com/Deterous/DiscUtils
Submodule
+1
Submodule DiscUtils added at a99992e5f4
+7 -8
View File
@@ -3,19 +3,18 @@
<PropertyGroup> <PropertyGroup>
<!-- Assembly Properties --> <!-- Assembly Properties -->
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<PackAsTool>true</PackAsTool> <TargetName>irdkit</TargetName>
<ToolCommandName>irdkit</ToolCommandName> <AssemblyName>irdkit</AssemblyName>
<PackageOutputPath>./nupkg</PackageOutputPath>
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks> <TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>0.2.0</Version> <Version>0.4.0</Version>
<!-- Package Properties --> <!-- Package Properties -->
<Authors>Deterous</Authors> <Authors>Deterous</Authors>
<Description>Library for ISO Rebuild Data</Description> <Description>Library for ISO Rebuild Data</Description>
<Copyright>Copyright (c) Deterous 2023</Copyright> <Copyright>Copyright (c) Deterous 2023-2024</Copyright>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/Deterous/LibIRD</RepositoryUrl> <RepositoryUrl>https://github.com/Deterous/LibIRD</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
@@ -29,10 +28,10 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\LibIRD\LibIRD.csproj" /> <ProjectReference Include="..\LibIRD\LibIRD.csproj" />
<ProjectReference Include="..\DiscUtils\Library\DiscUtils.Core\DiscUtils.Core.csproj" />
<ProjectReference Include="..\DiscUtils\Library\DiscUtils.Iso9660\DiscUtils.Iso9660.csproj" />
<PackageReference Include="CommandLineParser" Version="2.9.1" /> <PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="DiscUtils.Core" Version="0.16.13" /> <PackageReference Include="SabreTools.RedumpLib" Version="1.3.2" />
<PackageReference Include="DiscUtils.Iso9660" Version="0.16.13" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+722 -108
View File
File diff suppressed because it is too large Load Diff
+40 -6
View File
@@ -1,7 +1,41 @@
## How to use IRDKit # How to use IRDKit
IRDKit is a tool that allows direct use of LibIRD functionality from the command line interface. The basic usage is: IRDKit is a tool that allows direct use of LibIRD functionality from the command line interface.
``` For full help instructions, run `irdkit help`
irdkit game.iso game.ird
``` ## Creating ISOs
For detailed usage, run `irdkit --help`
For all options, run `irdkit help create`
To create an IRD from an ISO, run `irdkit create game.iso`
Multiple ISOs can be processed at once with `irdkit create game1.iso game2.iso`
Or a whole directory of ISOs can be processed with `irdkit create ./ISO`, or recursively with `irdkit create -r ./ISO`
The IRD will be created in the same folder as the ISO, with the same filename.
A different IRD path and/or filename can be defined with `-o` or `--output=`
### Key
By default, IRDs will be created by pulling keys from redump.org
A key can be manually provided with `-k` or `--key=`
A key file can be provided with `-f game.key` or `--key-file=`
A key from GetKey log file can be used with `-l game.getkey.log` or `--getkey-log=`
### PIC
By default, a PIC will be generated assuming a default layerbreak
A layerbreak value can be provided with `-b` or `--layerbreak=`
A PIC from a GetKey log file can be used with `-l game.getkey.log` or `--getkey-log=`
## Printing info
For all options, run `irdkit help info`
To print info about an ISO or IRD file, run `irdkit info game.iso` or `irdkit info game.ird`
The info can be printed to a file, e.g. `-o out.txt` or `--output=`
The info can be formatted as a JSON with `-j` or `--json`
## Comparing IRDs
For all options, run `irdkit help diff`
To compare two IRDs, run `irdkit diff game1.ird game2.ird`
The comparison can be printed to a file, e.g. `-o out.txt` or `--output=`
+26
View File
@@ -7,6 +7,20 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibIRD", "LibIRD\LibIRD.csp
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRDKit", "IRDKit\IRDKit.csproj", "{9060E3AF-E4FB-436F-93A1-5C47389CB88E}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRDKit", "IRDKit\IRDKit.csproj", "{9060E3AF-E4FB-436F-93A1-5C47389CB88E}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A8BB1F9F-FC8E-471C-9647-6FFF91D63BA7}"
ProjectSection(SolutionItems) = preProject
.gitattributes = .gitattributes
.gitignore = .gitignore
LICENSE.txt = LICENSE.txt
README.md = README.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscUtils.Core", "DiscUtils\Library\DiscUtils.Core\DiscUtils.Core.csproj", "{9E71F5CD-887D-4A61-8460-9F7E8F110B46}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscUtils.Iso9660", "DiscUtils\Library\DiscUtils.Iso9660\DiscUtils.Iso9660.csproj", "{9E1EF05F-388B-42C6-A4E7-98BE20C7C8EC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiscUtils.Streams", "DiscUtils\Library\DiscUtils.Streams\DiscUtils.Streams.csproj", "{E8D0B8B0-F165-4FA1-AC7B-194F5F48A5F9}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -21,6 +35,18 @@ Global
{9060E3AF-E4FB-436F-93A1-5C47389CB88E}.Debug|Any CPU.Build.0 = Debug|Any CPU {9060E3AF-E4FB-436F-93A1-5C47389CB88E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9060E3AF-E4FB-436F-93A1-5C47389CB88E}.Release|Any CPU.ActiveCfg = Release|Any CPU {9060E3AF-E4FB-436F-93A1-5C47389CB88E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9060E3AF-E4FB-436F-93A1-5C47389CB88E}.Release|Any CPU.Build.0 = Release|Any CPU {9060E3AF-E4FB-436F-93A1-5C47389CB88E}.Release|Any CPU.Build.0 = Release|Any CPU
{9E71F5CD-887D-4A61-8460-9F7E8F110B46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9E71F5CD-887D-4A61-8460-9F7E8F110B46}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9E71F5CD-887D-4A61-8460-9F7E8F110B46}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9E71F5CD-887D-4A61-8460-9F7E8F110B46}.Release|Any CPU.Build.0 = Release|Any CPU
{9E1EF05F-388B-42C6-A4E7-98BE20C7C8EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9E1EF05F-388B-42C6-A4E7-98BE20C7C8EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9E1EF05F-388B-42C6-A4E7-98BE20C7C8EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9E1EF05F-388B-42C6-A4E7-98BE20C7C8EC}.Release|Any CPU.Build.0 = Release|Any CPU
{E8D0B8B0-F165-4FA1-AC7B-194F5F48A5F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E8D0B8B0-F165-4FA1-AC7B-194F5F48A5F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E8D0B8B0-F165-4FA1-AC7B-194F5F48A5F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E8D0B8B0-F165-4FA1-AC7B-194F5F48A5F9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
+122 -80
View File
@@ -1,12 +1,12 @@
using DiscUtils; using DiscUtils;
using DiscUtils.Iso9660; using DiscUtils.Iso9660;
using DiscUtils.Streams;
using System; using System;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.IO.Hashing; using System.IO.Hashing;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Text.Json;
namespace LibIRD namespace LibIRD
{ {
@@ -117,7 +117,6 @@ namespace LibIRD
} }
} }
private byte[] _discKey; private byte[] _discKey;
// TODO: Link Data1Key and Disc Key
/// <summary> /// <summary>
/// D1 key /// D1 key
@@ -365,13 +364,12 @@ namespace LibIRD
/// <param name="discKey">Disc Key, byte array of length 16</param> /// <param name="discKey">Disc Key, byte array of length 16</param>
/// <param name="discID">Disc ID, byte array of length 16</param> /// <param name="discID">Disc ID, byte array of length 16</param>
/// <param name="discPIC">Disc PIC, byte array of length 115</param> /// <param name="discPIC">Disc PIC, byte array of length 115</param>
/// <param name="redump">True if redump-style IRD</param> /// <param name="redump">True if redump-style IRD (default: false)</param>
public IRD(string isoPath, byte[] discKey, byte[] discID, byte[] discPIC, bool redump = false) public IRD(string isoPath, byte[] discKey, byte[] discID, byte[] discPIC, bool redump = false)
{ {
// Parse ISO, Disc Key, Disc ID, and PIC // Parse ISO, Disc Key, Disc ID, and PIC
DiscKey = discKey; DiscKey = discKey;
GenerateD1(discKey); DiscID = discID;
GenerateD2(discID);
PIC = discPIC; PIC = discPIC;
// Generate IRD files from ISO // Generate IRD files from ISO
@@ -407,7 +405,6 @@ namespace LibIRD
private protected static byte[] GenerateD1(byte[] key) private protected static byte[] GenerateD1(byte[] key)
{ {
// Validate key // Validate key
ArgumentNullException.ThrowIfNull(key, nameof(key));
if (key.Length != 16) if (key.Length != 16)
throw new ArgumentException("Disc Key must be a byte array of length 16", nameof(key)); throw new ArgumentException("Disc Key must be a byte array of length 16", nameof(key));
@@ -441,7 +438,6 @@ namespace LibIRD
private protected static byte[] GenerateDiscKey(byte[] d1) private protected static byte[] GenerateDiscKey(byte[] d1)
{ {
// Validate key // Validate key
ArgumentNullException.ThrowIfNull(d1, nameof(d1));
if (d1.Length != 16) if (d1.Length != 16)
throw new ArgumentException("Disc Key must be a byte array of length 16", nameof(d1)); throw new ArgumentException("Disc Key must be a byte array of length 16", nameof(d1));
@@ -475,7 +471,6 @@ namespace LibIRD
private protected static byte[] GenerateD2(byte[] d2) private protected static byte[] GenerateD2(byte[] d2)
{ {
// Validate id // Validate id
ArgumentNullException.ThrowIfNull(d2, nameof(d2));
if (d2.Length != 16) throw new ArgumentException("Disc ID must be a byte array of length 16", nameof(d2)); if (d2.Length != 16) throw new ArgumentException("Disc ID must be a byte array of length 16", nameof(d2));
// Setup AES encryption // Setup AES encryption
@@ -508,7 +503,6 @@ namespace LibIRD
private protected static byte[] GenerateDiscID(byte[] d2) private protected static byte[] GenerateDiscID(byte[] d2)
{ {
// Validate id // Validate id
ArgumentNullException.ThrowIfNull(d2 , nameof(d2));
if (d2.Length != 16) if (d2.Length != 16)
throw new ArgumentException("Disc ID must be a byte array of length 16", nameof(d2)); throw new ArgumentException("Disc ID must be a byte array of length 16", nameof(d2));
@@ -541,9 +535,7 @@ namespace LibIRD
/// <exception cref="InvalidDataException"></exception> /// <exception cref="InvalidDataException"></exception>
private protected void ParseGetKeyLog(string getKeyLog) private protected void ParseGetKeyLog(string getKeyLog)
{ {
// Validate .getkey.log file path // Validate .getkey.log file path
ArgumentNullException.ThrowIfNull(getKeyLog, nameof(getKeyLog));
if (!File.Exists(getKeyLog)) if (!File.Exists(getKeyLog))
throw new FileNotFoundException(nameof(getKeyLog)); throw new FileNotFoundException(nameof(getKeyLog));
@@ -641,34 +633,59 @@ namespace LibIRD
ExtraConfig |= 0x01; ExtraConfig |= 0x01;
// Redump-style IRDs use fields from PS3_DISC.SFB // Redump-style IRDs use fields from PS3_DISC.SFB
using DiscUtils.Streams.SparseStream s = reader.OpenFile("PS3_DISC.SFB", FileMode.Open, FileAccess.Read); using SparseStream s = reader.OpenFile("PS3_DISC.SFB", FileMode.Open, FileAccess.Read);
// Parse PS3_DISC.SFB file // Parse PS3_DISC.SFB file
PS3_DiscSFB ps3_DiscSFB = new(s); PS3_DiscSFB ps3_DiscSFB = new(s);
bool title_id_found = ps3_DiscSFB.Field.TryGetValue("TITLE_ID", out string title_id); bool titleIDFound = ps3_DiscSFB.Field.TryGetValue("TITLE_ID", out string titleID);
// If a valid TITLE_ID field is present, remove the hyphen to fit into standard IRD file // If a valid TITLE_ID field is present, remove the hyphen to fit into standard IRD file
if (title_id_found && title_id.Length == 10 && title_id[4] == '-') if (titleIDFound && titleID.Length == 10 && titleID[4] == '-')
TitleID = string.Concat(title_id.AsSpan(0, 4), title_id.AsSpan(5, 5)); TitleID = string.Concat(titleID.AsSpan(0, 4), titleID.AsSpan(5, 5));
// If the version field is present, this is a multi-game disc // If the version field is present, this is a multi-game disc
// Redump-style IRDs use the VERSION field from PS3_DISC.SFB instead of VERSION from PARAM.SFO // Redump-style IRDs use the VERSION field from PS3_DISC.SFB instead of VERSION from PARAM.SFO
bool version_found = ps3_DiscSFB.Field.TryGetValue("VERSION", out string disc_version); bool discVersionFound = ps3_DiscSFB.Field.TryGetValue("VERSION", out string discVersion);
if (version_found) if (discVersionFound)
DiscVersion = disc_version; DiscVersion = discVersion;
} }
// Read PS3 Metadata from PARAM.SFO // Read PS3 Metadata from PARAM.SFO
using (DiscUtils.Streams.SparseStream s = reader.OpenFile("PS3_GAME\\PARAM.SFO", FileMode.Open, FileAccess.Read)) using (SparseStream s = reader.OpenFile("\\PS3_GAME\\PARAM.SFO", FileMode.Open, FileAccess.Read))
{ {
// Parse PARAM.SFO file // Parse PARAM.SFO file
ParamSFO paramSFO = new(s); ParamSFO paramSFO = new(s);
// If PS3_DISC.SFB did not set TitleID, use PARAM.SFO TITLE_ID // If PS3_DISC.SFB did not set TitleID, use PARAM.SFO TITLE_ID
TitleID ??= paramSFO.Field["TITLE_ID"]; if (TitleID == null)
Title = paramSFO.Field["TITLE"]; {
// If PS3_DISC.SFB did not set DiscVersion, use PARAM.SFO VERSION bool titleIDFound = paramSFO.Field.TryGetValue("TITLE_ID", out string titleID);
DiscVersion ??= paramSFO.Field["VERSION"]; if (titleIDFound)
AppVersion = paramSFO.Field["APP_VER"]; TitleID = titleID.Length == 9 ? titleID : titleID.PadRight(9, '\0')[..9];
else
TitleID = "\0\0\0\0\0\0\0\0\0";
}
// Try use Title from PARAM.SFO
bool titleFound = paramSFO.Field.TryGetValue("TITLE", out string title);
Title = titleFound ? title : String.Empty;
// If PS3_DISC.SFB did not set DiscVersion, try use PARAM.SFO VERSION
if (DiscVersion == null)
{
bool discVersionFound = paramSFO.Field.TryGetValue("VERSION", out string discVersion);
if (discVersionFound)
DiscVersion = discVersion.Length == 5 ? discVersion : discVersion.PadRight(5, '\0')[..5];
else
DiscVersion = "\0\0\0\0\0";
}
// Try use App Version from PARAM.SFO
bool appVersionFound = paramSFO.Field.TryGetValue("APP_VER", out string appVersion);
if (appVersionFound)
AppVersion = appVersion.Length == 5 ? appVersion : appVersion.PadRight(5, '\0')[..5];
else
AppVersion = "\0\0\0\0\0";
} }
// Determine system update version // Determine system update version
@@ -697,7 +714,12 @@ namespace LibIRD
FileCount = 0; FileCount = 0;
HashFiles(fs, reader, rootDir); HashFiles(fs, reader, rootDir);
if (FileCount != fileCount) if (FileCount != fileCount)
throw new InvalidFileSystemException("Unexpected ISO filesystem error: "); {
Console.WriteLine($"{isoPath} contains split files: detected {FileCount} out of {fileCount} expected files");
long[] tempFileKeys = FileKeys;
Array.Resize(ref tempFileKeys, (int)FileCount);
FileKeys = tempFileKeys;
}
Array.Sort(FileKeys, FileHashes); Array.Sort(FileKeys, FileHashes);
} }
@@ -715,33 +737,22 @@ namespace LibIRD
private void GetSystemVersion(FileStream fs, CDReader reader) private void GetSystemVersion(FileStream fs, CDReader reader)
{ {
// Determine PUP file offset via cluster // Determine PUP file offset via cluster
DiscUtils.Streams.Range<long, long>[] updateClusters = reader.PathToClusters("\\PS3_UPDATE\\PS3UPDAT.PUP"); Range<long, long>[] updateClusters = reader.PathToClusters("\\PS3_UPDATE\\PS3UPDAT.PUP");
if (updateClusters == null || updateClusters.Length <= 0) if (updateClusters == null && updateClusters.Length == 0 && updateClusters[0] == null)
{ throw new InvalidFileSystemException("Invalid file extents for PS3UPDAT.PUP");
// File too small for dedicated cluster, try get the offset from the file extents instead
DiscUtils.Streams.StreamExtent[] updateExtents = reader.PathToExtents("\\PS3_UPDATE\\PS3UPDAT.PUP");
if (updateExtents == null || updateExtents.Length <= 0)
throw new InvalidFileSystemException("Unexpected PS3UPDAT.PUP file extent in ISO filestream");
// PS3UPDAT.PUP file begins at start of first extent
UpdateOffset = updateExtents[0].Start;
// Update file ends at the last extent plus its length
UpdateEnd = updateExtents[^1].Start + updateExtents[^1].Length;
}
else
{
// PS3UPDAT.PUP file begins at first byte of dedicated cluster // PS3UPDAT.PUP file begins at first byte of dedicated cluster
UpdateOffset = updateClusters[0] != null ? SectorSize * updateClusters[0].Offset : 0; UpdateOffset = SectorSize * updateClusters[0].Offset;
// Update file ends at the last byte of the last cluster // Update file ends at the last byte of the last cluster
UpdateEnd = SectorSize * (updateClusters[^1].Offset + updateClusters[^1].Count); UpdateEnd = SectorSize * updateClusters[^1].Offset + updateClusters[^1].Count;
}
// Check PUP file Magic // Check PUP file Magic
fs.Seek(UpdateOffset, SeekOrigin.Begin); fs.Seek(UpdateOffset, SeekOrigin.Begin);
byte[] pupMagic = new byte[5]; byte[] pupMagic = new byte[5];
fs.Read(pupMagic, 0, pupMagic.Length); fs.Read(pupMagic, 0, pupMagic.Length);
// If magic is incorrect, set version to "0000" (unknown) // If magic is incorrect, set version to all nulls, "\0\0\0\0" (unknown)
if (Encoding.ASCII.GetString(pupMagic) != "SCEUF") if (Encoding.ASCII.GetString(pupMagic) != "SCEUF")
SystemVersion = "0000"; SystemVersion = "\0\0\0\0";
else else
{ {
// Determine location of version string // Determine location of version string
@@ -769,24 +780,19 @@ namespace LibIRD
private void GetHeader(FileStream fs, CDReader reader) private void GetHeader(FileStream fs, CDReader reader)
{ {
// Determine the extent of the header via cluster (Sector 0 to first data sector) // Determine the extent of the header via cluster (Sector 0 to first data sector)
DiscUtils.Streams.Range<long, long>[] sfbClusters = reader.PathToClusters("\\PS3_DISC.SFB"); Range<long, long>[] sfbClusters = reader.PathToClusters("\\PS3_DISC.SFB");
if (sfbClusters == null || sfbClusters.Length <= 0) if (sfbClusters == null && sfbClusters.Length == 0 && sfbClusters[0] == null)
{ throw new InvalidFileSystemException("Invalid file extents for PS3_DISC.SFB");
// File too small for dedicated cluster, try get the first sector from the file extents instead
DiscUtils.Streams.StreamExtent[] sfbExtents = reader.PathToExtents("\\PS3_DISC.SFB");
if (sfbExtents == null || sfbExtents.Length <= 0)
throw new InvalidFileSystemException("Unexpected PS3UPDAT.PUP file extent in ISO filestream");
FirstDataSector = sfbExtents[0].Start;
}
else
{
// End of header is at beginning of first byte of dedicated cluster // End of header is at beginning of first byte of dedicated cluster
FirstDataSector = sfbClusters[0] != null ? sfbClusters[0].Offset : 0; FirstDataSector = sfbClusters[0].Offset;
}
// Begin a GZip stream to write header to // Begin a GZip stream to write header to
using MemoryStream headerStream = new(); using MemoryStream headerStream = new();
#if NET6_0_OR_GREATER
using (GZipStream gzs = new(headerStream, CompressionLevel.SmallestSize)) using (GZipStream gzs = new(headerStream, CompressionLevel.SmallestSize))
#else
using (GZipStream gzs = new(headerStream, CompressionLevel.Optimal))
#endif
{ {
// Start reading data from the beginning of the ISO file // Start reading data from the beginning of the ISO file
fs.Seek(0, SeekOrigin.Begin); fs.Seek(0, SeekOrigin.Begin);
@@ -814,9 +820,13 @@ namespace LibIRD
{ {
// Begin a GZip stream to write footer to // Begin a GZip stream to write footer to
using MemoryStream footerStream = new(); using MemoryStream footerStream = new();
#if NET6_0_OR_GREATER
using (GZipStream gzs = new(footerStream, CompressionLevel.SmallestSize)) using (GZipStream gzs = new(footerStream, CompressionLevel.SmallestSize))
#else
using (GZipStream gzs = new(footerStream, CompressionLevel.Optimal))
#endif
{ {
// Start reading data from after last file // Start reading data from after last file (PS3UPDAT.PUP)
fs.Seek(UpdateEnd, SeekOrigin.Begin); fs.Seek(UpdateEnd, SeekOrigin.Begin);
byte[] buf = new byte[SectorSize]; byte[] buf = new byte[SectorSize];
int numBytes = (int)SectorSize; int numBytes = (int)SectorSize;
@@ -919,15 +929,33 @@ namespace LibIRD
{ {
string filePath = fileInfo.FullName; string filePath = fileInfo.FullName;
// Try get the first sector from the file extents instead // Determine the extents of the file via clusters
DiscUtils.Streams.StreamExtent[] fileExtents = reader.PathToExtents(filePath); Range<long, long>[] fileClusters = reader.PathToClusters(filePath);
if (fileExtents == null || fileExtents.Length <= 0)
throw new InvalidFileSystemException("Unexpected file extent in ISO filestream for " + filePath); // If invalid clusters were returned, we can't hash this file
if (fileExtents.Length > 1) if (fileClusters == null && fileClusters.Length == 0)
throw new InvalidFileSystemException("Non-contiguous file detected"); throw new InvalidFileSystemException($"Unexpected file extents for {filePath}");
long firstByte = fileExtents[0].Start;
int firstSector = (int)(firstByte / 2048); // Determine smallest file offset as first sector
long fileLength = fileExtents[0].Length; long smallestOffset = fileClusters[0].Offset;
for (int i = 1; i < fileClusters.Length; i++)
{
if (fileClusters[i] == null)
throw new InvalidFileSystemException($"Unexpected file extents for {filePath}");
if (fileClusters[i].Offset < smallestOffset)
smallestOffset = fileClusters[i].Offset;
}
int firstSector = (int)(smallestOffset);
// If already encountered file offset, skip this file
if (Array.Exists(FileKeys, element => element == firstSector))
continue;
if (fileClusters.Length > 1)
Console.WriteLine($"Split file detected: {filePath}");
// Add file offset to keys
FileKeys[FileCount] = firstSector; FileKeys[FileCount] = firstSector;
// Determine whether file is in encrypted or decrypted region // Determine whether file is in encrypted or decrypted region
@@ -941,13 +969,16 @@ namespace LibIRD
} }
} }
// Start reading data from the beginning of the ISO file // Hash each non-contiguous portion of the ISO file
fs.Seek(firstByte, SeekOrigin.Begin);
byte[] buf = new byte[SectorSize]; byte[] buf = new byte[SectorSize];
MD5 md5 = MD5.Create();
for (int i = 0; i < fileClusters.Length; i++)
{
// Start reading data from the beginning of the ISO file
fs.Seek(fileClusters[i].Offset * SectorSize, SeekOrigin.Begin);
int numBytes; int numBytes;
// Read all data before the first data sector // Read all data before the first data sector
MD5 md5 = MD5.Create(); for (int j = 0; j < (fileClusters[i].Count / SectorSize); j++)
for (int i = 0; i < (fileLength / SectorSize); i++)
{ {
numBytes = fs.Read(buf, 0, buf.Length); numBytes = fs.Read(buf, 0, buf.Length);
// Check that an entire sector was read // Check that an entire sector was read
@@ -955,12 +986,12 @@ namespace LibIRD
throw new InvalidFileSystemException("Disc region ended unexpectedly"); throw new InvalidFileSystemException("Disc region ended unexpectedly");
// Decrypt sector if necessary // Decrypt sector if necessary
if (encrypted) if (encrypted)
buf = DecryptSector(buf, firstSector + i); buf = DecryptSector(buf, firstSector + j);
// Hash sector // Hash sector
md5.TransformBlock(buf, 0, numBytes, null, 0); md5.TransformBlock(buf, 0, numBytes, null, 0);
} }
// Read remaining partial sector // Read remaining partial sector
if (fileLength % SectorSize != 0) if (fileClusters[i].Count % SectorSize != 0)
{ {
numBytes = fs.Read(buf, 0, buf.Length); numBytes = fs.Read(buf, 0, buf.Length);
// Check that an entire sector was read // Check that an entire sector was read
@@ -968,9 +999,10 @@ namespace LibIRD
throw new InvalidFileSystemException("Disc region ended unexpectedly"); throw new InvalidFileSystemException("Disc region ended unexpectedly");
// Decrypt partial sector if necessary // Decrypt partial sector if necessary
if (encrypted) if (encrypted)
buf = DecryptSector(buf, firstSector + (int)(fileLength / SectorSize)); buf = DecryptSector(buf, firstSector + (int)(fileClusters[i].Count / SectorSize));
// Hash partial sector // Hash partial sector
md5.TransformBlock(buf, 0, (int)(fileLength % SectorSize), null, 0); md5.TransformBlock(buf, 0, (int)(fileClusters[i].Count % SectorSize), null, 0);
}
} }
// Finalise and store MD5 hash // Finalise and store MD5 hash
@@ -1147,7 +1179,11 @@ namespace LibIRD
// Create the IRD file stream // Create the IRD file stream
using FileStream fs = new(irdPath, FileMode.Create, FileAccess.Write); using FileStream fs = new(irdPath, FileMode.Create, FileAccess.Write);
// Create a GZipped IRD file stream // Create a GZipped IRD file stream
#if NET6_0_OR_GREATER
using GZipStream gzStream = new(fs, CompressionLevel.SmallestSize); using GZipStream gzStream = new(fs, CompressionLevel.SmallestSize);
#else
using GZipStream gzStream = new(fs, CompressionLevel.Optimal);
#endif
// Write entire gzipped IRD stream to file // Write entire gzipped IRD stream to file
stream.Position = 0; stream.Position = 0;
stream.CopyTo(gzStream); stream.CopyTo(gzStream);
@@ -1239,7 +1275,7 @@ namespace LibIRD
// Read UID (for Version 8 onwards) // Read UID (for Version 8 onwards)
if (version > 7) if (version > 7)
uid = br.ReadUInt16(); uid = br.ReadUInt32();
// Read and CRC32 hash // Read and CRC32 hash
byte[] crc = br.ReadBytes(4); byte[] crc = br.ReadBytes(4);
@@ -1268,11 +1304,14 @@ namespace LibIRD
/// Prints IRD fields to console /// Prints IRD fields to console
/// </summary> /// </summary>
/// <param name="printPath">Optional path to save file to</param> /// <param name="printPath">Optional path to save file to</param>
public void Print(string printPath = null) public void Print(string printPath = null, string irdName = null)
{ {
// Build string from parameters // Build string from parameters
StringBuilder printText = new(); StringBuilder printText = new();
if (irdName == null)
printText.AppendLine("IRD Contents:"); printText.AppendLine("IRD Contents:");
else
printText.AppendLine($"IRD Contents: {irdName}");
printText.AppendLine("============="); printText.AppendLine("=============");
// Append IRD fields to string builder // Append IRD fields to string builder
@@ -1305,7 +1344,7 @@ namespace LibIRD
} }
else else
{ {
File.WriteAllText(printPath, printText.ToString()); File.AppendAllText(printPath, printText.ToString());
} }
} }
@@ -1314,7 +1353,7 @@ namespace LibIRD
/// Prints IRD fields to a json object /// Prints IRD fields to a json object
/// </summary> /// </summary>
/// <param name="jsonPath">Optionally print to json file</param> /// <param name="jsonPath">Optionally print to json file</param>
public void PrintJson(string jsonPath = null) public void PrintJson(string jsonPath = null, bool single = true)
{ {
// Build string from parameters // Build string from parameters
StringBuilder json = new(); StringBuilder json = new();
@@ -1334,11 +1373,14 @@ namespace LibIRD
json.AppendLine($" \"Extra Config\": \"{ExtraConfig:X4}\","); json.AppendLine($" \"Extra Config\": \"{ExtraConfig:X4}\",");
if (Attachments != 0x0000) if (Attachments != 0x0000)
json.AppendLine($" \"Attachments\": \"{Attachments:X4}\","); json.AppendLine($" \"Attachments\": \"{Attachments:X4}\",");
json.AppendLine($" \"Unique ID\": \"{UID:X8}"); json.AppendLine($" \"Unique ID\": \"{UID:X8}\",");
json.AppendLine($" \"Data 1 Key\": \"{Convert.ToHexString(Data1Key)}\","); json.AppendLine($" \"Data 1 Key\": \"{Convert.ToHexString(Data1Key)}\",");
json.AppendLine($" \"Data 2 Key\": \"{Convert.ToHexString(Data2Key)}\","); json.AppendLine($" \"Data 2 Key\": \"{Convert.ToHexString(Data2Key)}\",");
json.AppendLine($" \"PIC\": \"{Convert.ToHexString(PIC)}\""); json.AppendLine($" \"PIC\": \"{Convert.ToHexString(PIC)}\"");
if (single)
json.AppendLine("}"); json.AppendLine("}");
else
json.AppendLine("},");
// If no path given, output to console // If no path given, output to console
if (jsonPath == null) if (jsonPath == null)
+7 -5
View File
@@ -6,17 +6,19 @@
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>0.2.0</Version> <Version>0.4.0</Version>
<PackageOutputPath>../nupkg</PackageOutputPath>
<!-- Package Properties --> <!-- Package Properties -->
<Authors>Deterous</Authors> <Authors>Deterous</Authors>
<Description>Library for ISO Rebuild Data</Description> <Description>Library for ISO Rebuild Data</Description>
<Copyright>Copyright (c) Deterous 2023</Copyright> <Copyright>Copyright (c) Deterous 2023-2024</Copyright>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/Deterous/LibIRD/</RepositoryUrl> <RepositoryUrl>https://github.com/Deterous/LibIRD/</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PackageTags>ps3 iso ird redump</PackageTags> <PackageTags>ps3 iso ird redump</PackageTags>
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression> <PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -24,9 +26,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DiscUtils.Core" Version="0.16.13" /> <ProjectReference Include="..\DiscUtils\Library\DiscUtils.Core\DiscUtils.Core.csproj" />
<PackageReference Include="DiscUtils.Iso9660" Version="0.16.13" /> <ProjectReference Include="..\DiscUtils\Library\DiscUtils.Iso9660\DiscUtils.Iso9660.csproj" />
<PackageReference Include="DiscUtils.Streams" Version="0.16.13" /> <ProjectReference Include="..\DiscUtils\Library\DiscUtils.Streams\DiscUtils.Streams.csproj" />
<PackageReference Include="System.IO.Hashing" Version="8.0.0" /> <PackageReference Include="System.IO.Hashing" Version="8.0.0" />
</ItemGroup> </ItemGroup>
+4 -5
View File
@@ -33,12 +33,8 @@ namespace LibIRD
/// Constructor using a PARAM.SFO file path /// Constructor using a PARAM.SFO file path
/// </summary> /// </summary>
/// <param name="sfbPath">Full file path to the PS3_DISC.SFB file</param> /// <param name="sfbPath">Full file path to the PS3_DISC.SFB file</param>
/// <exception cref="ArgumentNullException"></exception>
public PS3_DiscSFB(string sfbPath) public PS3_DiscSFB(string sfbPath)
{ {
// Validate file path
ArgumentNullException.ThrowIfNull(sfbPath, nameof(sfbPath));
// Read file as a stream, and parse file // Read file as a stream, and parse file
using FileStream fs = new(sfbPath, FileMode.Open, FileAccess.Read); using FileStream fs = new(sfbPath, FileMode.Open, FileAccess.Read);
Parse(fs); Parse(fs);
@@ -103,10 +99,13 @@ namespace LibIRD
/// Prints formatted parameters extracted from PS3_DISC.SFB to console /// Prints formatted parameters extracted from PS3_DISC.SFB to console
/// </summary> /// </summary>
/// <param name="printPath">Optionally print to text file</param> /// <param name="printPath">Optionally print to text file</param>
public void Print(string printPath = null) public void Print(string printPath = null, string isoName = null)
{ {
// Build string from parameters // Build string from parameters
StringBuilder printText = new(); StringBuilder printText = new();
if (isoName != null)
printText.AppendLine($"PS3_DISC.SFB Contents: {isoName}");
else
printText.AppendLine("PS3_DISC.SFB Contents:"); printText.AppendLine("PS3_DISC.SFB Contents:");
printText.AppendLine("======================"); printText.AppendLine("======================");
+5 -2
View File
@@ -127,12 +127,15 @@ namespace LibIRD
/// Prints formatted parameters extracted from PARAM.SFO to console /// Prints formatted parameters extracted from PARAM.SFO to console
/// </summary> /// </summary>
/// <param name="printPath">Optionally print to text file</param> /// <param name="printPath">Optionally print to text file</param>
public void Print(string printPath = null) public void Print(string printPath = null, string isoName = null)
{ {
// Build string from parameters // Build string from parameters
StringBuilder printText = new(); StringBuilder printText = new();
if (isoName != null)
printText.AppendLine($"PARAM.SFO Contents: {isoName}");
else
printText.AppendLine("PARAM.SFO Contents:"); printText.AppendLine("PARAM.SFO Contents:");
printText.AppendLine("===================="); printText.AppendLine("===================");
// Loop through all parameters in PARAM.SFO // Loop through all parameters in PARAM.SFO
foreach (KeyValuePair<string, string> field in Field) foreach (KeyValuePair<string, string> field in Field)
+41 -43
View File
@@ -1,6 +1,8 @@
using System; using DiscUtils.Iso9660;
using System;
using System.IO; using System.IO;
using System.IO.Hashing; using System.IO.Hashing;
using System.Security;
namespace LibIRD namespace LibIRD
{ {
@@ -51,15 +53,6 @@ namespace LibIRD
/// </summary> /// </summary>
public class ReIRD : IRD public class ReIRD : IRD
{ {
#region Properties
/// <summary>
/// ISO file size
/// </summary>
private long Size { get; set; }
#endregion
#region Constructors #region Constructors
/// <summary> /// <summary>
@@ -67,25 +60,11 @@ namespace LibIRD
/// </summary> /// </summary>
/// <param name="isoPath">Path to the ISO</param> /// <param name="isoPath">Path to the ISO</param>
/// <param name="getKeyLog">Path to the GetKey log file</param> /// <param name="getKeyLog">Path to the GetKey log file</param>
/// <param name="layerbreak">Layerbreak value, in sectors</param>
/// <exception cref="InvalidDataException"></exception> /// <exception cref="InvalidDataException"></exception>
public ReIRD(string isoPath, string getKeyLog, long? layerbreak = null) : base(isoPath, getKeyLog, true) public ReIRD(string isoPath, string getKeyLog) : base(isoPath, getKeyLog, true)
{ {
// Generate Unique Identifier using ISO CRC32 // Generate Unique Identifier using ISO CRC32
UID = GenerateUID(isoPath); UID = GenerateUID(isoPath);
// Determine ISO file size
Size = CalculateSize(isoPath);
// Generate Data 2 using Disc ID
DiscID = GenerateID(Size);
// Generate Disc PIC
byte[] pic = GeneratePIC(Size, layerbreak * SectorSize);
// Check that GetKey log matches expected PIC
if (!((ReadOnlySpan<byte>)PIC).SequenceEqual(pic))
throw new InvalidDataException("Unexpected PIC in .getkey.log");
} }
/// <summary> /// <summary>
@@ -95,22 +74,22 @@ namespace LibIRD
/// <param name="key">Disc Key, redump-style (AES encrypted Data 1)</param> /// <param name="key">Disc Key, redump-style (AES encrypted Data 1)</param>
/// <param name="layerbreak">Layerbreak value, in sectors</param> /// <param name="layerbreak">Layerbreak value, in sectors</param>
/// <param name="region">Disc Region</param> /// <param name="region">Disc Region</param>
public ReIRD(string isoPath, byte[] key, long? layerbreak = null, Region region = Region.NONE) public ReIRD(string isoPath, byte[] key, long? layerbreak = null, Region region = Region.NONE) : base()
{ {
// Generate Unique Identifier using ISO CRC32 // Generate Unique Identifier using ISO CRC32
UID = GenerateUID(isoPath); UID = GenerateUID(isoPath);
// Determine ISO file size // Determine ISO file size
Size = CalculateSize(isoPath); long size = CalculateSize(isoPath);
// Set Disc Key // Set Disc Key
DiscKey = key; DiscKey = key;
// Generate Data 2 using Disc ID // Generate Data 2 using Disc ID
DiscID = GenerateID(Size, region); DiscID = GenerateID(size, region);
// Generate Disc PIC // Generate Disc PIC
PIC = GeneratePIC(Size, layerbreak * SectorSize); PIC = GeneratePIC(isoPath, size, layerbreak * SectorSize);
// Generate IRD fields // Generate IRD fields
GenerateIRD(isoPath, true); GenerateIRD(isoPath, true);
@@ -143,12 +122,13 @@ namespace LibIRD
/// <param name="layerbreak">Layer break value, byte at which disc layers are split across</param> /// <param name="layerbreak">Layer break value, byte at which disc layers are split across</param>
/// <param name="exactIRD">True to generate a PIC in 3k3y style (0x03 at 115th byte for BD-50 discs)</param> /// <param name="exactIRD">True to generate a PIC in 3k3y style (0x03 at 115th byte for BD-50 discs)</param>
/// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentException"></exception>
private static byte[] GeneratePIC(long size, long? layerbreak = null, bool exactIRD = false) private static byte[] GeneratePIC(string isoPath, long size, long? layerbreak = null, bool exactIRD = false)
{ {
// Validate size // Validate size
if (size <= 0 || (size % SectorSize) != 0) if (size <= 0 || (size % SectorSize) != 0)
throw new ArgumentException("ISO Size in bytes must be a positive integer multiple of 2048", nameof(size)); throw new ArgumentException("ISO Size in bytes must be a positive integer multiple of 2048", nameof(size));
// Validate layerbreak
// Validate provided layerbreak
if (layerbreak != null) if (layerbreak != null)
{ {
if (layerbreak <= 0 || (layerbreak >= size)) if (layerbreak <= 0 || (layerbreak >= size))
@@ -156,8 +136,16 @@ namespace LibIRD
if (layerbreak >= 2 * BDLayerSize || layerbreak % SectorSize != 0) if (layerbreak >= 2 * BDLayerSize || layerbreak % SectorSize != 0)
throw new ArgumentException("Unexpected layerbreak value", nameof(size)); throw new ArgumentException("Unexpected layerbreak value", nameof(size));
} }
// If layerbreak value was not set, assume it is a non-hybrid disc with default layerbreak else
long layer_break = layerbreak ?? BDLayerSize; {
// If no layerbreak provided, ensure ISO is not BD-Video hybrid
using FileStream fs = new FileStream(isoPath, FileMode.Open, FileAccess.Read) ?? throw new FileNotFoundException(isoPath);
CDReader reader = new(fs, true, true);
if (reader.DirectoryExists("\\BDMV"))
throw new ArgumentException("Layerbreak must be provided for BD-Video hybrid discs");
// Assume disc has default layerbreak
layerbreak = BDLayerSize;
}
// Generate the PIC based on the size and layerbreak of the ISO // Generate the PIC based on the size and layerbreak of the ISO
byte[] pic; byte[] pic;
@@ -167,7 +155,7 @@ namespace LibIRD
long l0_start_sector = 1048576; long l0_start_sector = 1048576;
// Layer 0 end sector = start sector + layerbreak - 2 // Layer 0 end sector = start sector + layerbreak - 2
long l0_end_sector = (layer_break / SectorSize) + l0_start_sector - 2; long l0_end_sector = ((long)layerbreak / SectorSize) + l0_start_sector - 2;
// Convert end sector location to hex values for PIC // Convert end sector location to hex values for PIC
byte[] l0es = [(byte)((l0_end_sector >> 24) & 0xFF), byte[] l0es = [(byte)((l0_end_sector >> 24) & 0xFF),
(byte)((l0_end_sector >> 16) & 0xFF), (byte)((l0_end_sector >> 16) & 0xFF),
@@ -175,7 +163,7 @@ namespace LibIRD
(byte)((l0_end_sector >> 0) & 0xFF)]; (byte)((l0_end_sector >> 0) & 0xFF)];
// Layer 1 start sector = end of disc (0x01EFFFFE) - layerbreak + 2 // Layer 1 start sector = end of disc (0x01EFFFFE) - layerbreak + 2
long l1_start_sector = 32505854 - (layer_break / SectorSize) + 2; long l1_start_sector = 32505854 - ((long)layerbreak! / SectorSize) + 2;
// Convert start of start sector location to hex values for PIC // Convert start of start sector location to hex values for PIC
byte[] l1ss = [(byte)((l1_start_sector >> 24) & 0xFF), byte[] l1ss = [(byte)((l1_start_sector >> 24) & 0xFF),
(byte)((l1_start_sector >> 16) & 0xFF), (byte)((l1_start_sector >> 16) & 0xFF),
@@ -257,11 +245,16 @@ namespace LibIRD
/// <exception cref="FileNotFoundException"></exception> /// <exception cref="FileNotFoundException"></exception>
private static uint GenerateUID(string isoPath) private static uint GenerateUID(string isoPath)
{ {
// Validate ISO path
ArgumentNullException.ThrowIfNull(isoPath, nameof(isoPath));
// Check file exists // Check file exists
var iso = new FileInfo(isoPath); FileInfo iso;
try
{
iso = new FileInfo(isoPath);
}
catch (Exception e)
{
throw new ArgumentException("Invalid ISO Path: " + e.Message);
}
if (!iso.Exists) if (!iso.Exists)
throw new FileNotFoundException(nameof(isoPath)); throw new FileNotFoundException(nameof(isoPath));
@@ -286,11 +279,16 @@ namespace LibIRD
/// <exception cref="FileNotFoundException"></exception> /// <exception cref="FileNotFoundException"></exception>
private static long CalculateSize(string isoPath) private static long CalculateSize(string isoPath)
{ {
// Validate ISO path
ArgumentNullException.ThrowIfNull(isoPath, nameof(isoPath));
// Check file exists // Check file exists
var iso = new FileInfo(isoPath); FileInfo iso;
try
{
iso = new FileInfo(isoPath);
}
catch (Exception e)
{
throw new ArgumentException("Invalid ISO Path: " + e.Message);
}
if (!iso.Exists) if (!iso.Exists)
throw new FileNotFoundException(nameof(isoPath)); throw new FileNotFoundException(nameof(isoPath));
+6 -8
View File
@@ -8,16 +8,14 @@ IRD files contain a summary of what data is on a PlayStation 3 disc. It can be u
## How to use IRDKit ## How to use IRDKit
IRDKit is a tool that allows direct use of LibIRD functionality from the command line interface. The basic usage is: IRDKit is a tool that allows direct use of LibIRD functionality from a command line interface. The basic usage is:
``` - Printing info about an ISO: `irdkit info game.iso`
irdkit create game.iso - Printing info about all ISOs and IRDs in a folder: `irdkit info .`
``` - Creating an IRD from an ISO: `irdkit create game.iso`
- Finding differences between two IRDs: `irdkit diff game1.ird game2.ird`
For detailed usage, read more [here](IRDKit). For detailed usage, read more [here](IRDKit).
## Using the LibIRD library ## Using the LibIRD library
LibIRD was originally made for creating reproducible, redump-style IRDs when dumping PS3 discs with [MPF](https://github.com/SabreTools/MPF). If you wish to integrate LibIRD into your own application, read the examples [here](LibIRD). LibIRD was originally made for creating reproducible, redump-style IRDs when dumping PS3 discs with [MPF](https://github.com/SabreTools/MPF). If you wish to integrate LibIRD into your own application, read the examples [here](LibIRD).
## Limitations
Currently, LibIRD does not produce correct IRDs for ISOs with non-contiguous files. Therefore, do not consider this library "stable" until v1.0.0 is released.
+15
View File
@@ -0,0 +1,15 @@
dotnet publish -c Release -f net8.0 -r win-x86 --self-contained=false -p:PublishSingleFile=true -p:DebugType=None IRDKit\IRDKit.csproj
dotnet publish -c Release -f net8.0 -r win-x64 --self-contained=false -p:PublishSingleFile=true -p:DebugType=None IRDKit\IRDKit.csproj
dotnet publish -c Release -f net8.0 -r win-arm64 --self-contained=false -p:PublishSingleFile=true -p:DebugType=None IRDKit\IRDKit.csproj
dotnet publish -c Release -f net8.0 -r linux-x64 --self-contained=false -p:PublishSingleFile=true -p:DebugType=None IRDKit\IRDKit.csproj
dotnet publish -c Release -f net8.0 -r linux-arm64 --self-contained=false -p:PublishSingleFile=true -p:DebugType=None IRDKit\IRDKit.csproj
dotnet publish -c Release -f net8.0 -r osx-x64 --self-contained=false -p:PublishSingleFile=true -p:DebugType=None IRDKit\IRDKit.csproj
dotnet publish -c Release -f net8.0 -r osx-arm64 --self-contained=false -p:PublishSingleFile=true -p:DebugType=None IRDKit\IRDKit.csproj
Compress-Archive -Path "./IRDKit/bin/Release/net8.0/win-x86/publish/irdkit.exe" -Destination "./irdkit-win-x86.zip" -CompressionLevel "Optimal"
Compress-Archive -Path "./IRDKit/bin/Release/net8.0/win-x64/publish/irdkit.exe" -Destination "./irdkit-win-x64.zip" -CompressionLevel "Optimal"
Compress-Archive -Path "./IRDKit/bin/Release/net8.0/win-arm64/publish/irdkit.exe" -Destination "./irdkit-win-arm64.zip" -CompressionLevel "Optimal"
Compress-Archive -Path "./IRDKit/bin/Release/net8.0/linux-x64/publish/irdkit" -Destination "./irdkit-linux-x64.zip" -CompressionLevel "Optimal"
Compress-Archive -Path "./IRDKit/bin/Release/net8.0/linux-arm64/publish/irdkit" -Destination "./irdkit-linux-arm64.zip" -CompressionLevel "Optimal"
Compress-Archive -Path "./IRDKit/bin/Release/net8.0/osx-x64/publish/irdkit" -Destination "./irdkit-osx-x64.zip" -CompressionLevel "Optimal"
Compress-Archive -Path "./IRDKit/bin/Release/net8.0/osx-arm64/publish/irdkit" -Destination "./irdkit-osx-arm64.zip" -CompressionLevel "Optimal"