80 Commits
Author SHA1 Message Date
DeterousandGitHub 1935404928 Don't reverse CRC32 2025-11-22 18:25:01 +09:00
DeterousandGitHub 80e515faeb Update packages 2025-11-22 18:07:28 +09:00
DeterousandGitHub 03dc5d7a0e NET10 GHA 2025-11-22 18:05:32 +09:00
DeterousandGitHub fac8168d32 Update GHA 2025-11-22 18:02:34 +09:00
DeterousandGitHub 45d3b3fc7a Support NET10 2025-11-22 18:01:51 +09:00
DeterousandGitHub 85fbd11621 Don't reverse CRC32 hash 2025-11-22 18:00:52 +09:00
DeterousandGitHub 2c7e6423cf Update README.md 2025-08-20 20:43:19 +09:00
DeterousandGitHub 4492862051 Final fix 2025-08-19 23:09:11 +09:00
DeterousandGitHub b1cb4bccd4 Update README.md 2025-08-19 21:28:39 +09:00
DeterousandGitHub 2379e7ef5a Verify command 2025-08-19 21:27:28 +09:00
DeterousandGitHub ce5b610f92 Print All option for info
`-a`/`--all` for irdkit info command
2025-08-19 11:37:11 +09:00
DeterousandGitHub 048d70b473 Bump version 2024-11-16 12:24:15 +09:00
Matt NadareskiandGitHub 0761450a8d Update SabreTools libraries and add .NET 9 support (#1)
* Update SabreTools libraries and add .NET 9 support

* Update build script

* Handle net9.0-specific complaints
2024-11-16 11:45:12 +09:00
Deterous 82d53db68f Bump version 2024-04-11 18:18:45 +09:00
Deterous f8a9c8515d Relax ISO requirements 2024-04-11 18:18:17 +09:00
Deterous 0ef06c36e9 Fix rename functionality, mention in reademe 2024-03-25 13:27:56 +09:00
Deterous c1c25b5f5e Fix issue from moving to ST.Hashing 2024-03-13 12:45:20 +09:00
Deterous c175fedbdb Fix bug for small encrypted regions 2024-03-12 19:42:23 +09:00
Deterous f7879bf9e8 Finalise old dotnet support 2024-03-12 19:08:42 +09:00
Deterous fed9d6ea57 Trim DiscUtils further 2024-03-06 14:14:37 +09:00
Deterous 41946b6bc5 Use trimmed DiscUtils 2024-03-06 12:09:30 +09:00
Deterous d585c4b534 Update ST.Hashing version 2024-03-06 12:09:15 +09:00
Deterous 1886e14a9b Use a trimmed DiscUtils 2024-03-05 23:34:38 +09:00
Deterous e8f4d88711 Use ST.Hashing for SHA1 2024-03-05 15:15:40 +09:00
Deterous 0dbc483a91 Use ST.Hashing for MD5 2024-03-05 15:02:27 +09:00
Deterous b6bb5f0582 Attempt to support net40 with ST.Hashing 2024-03-05 14:52:30 +09:00
Deterous ea3f6d3f96 Support .NET Framework using Newtonsoft 2024-03-05 13:02:25 +09:00
Deterous 4aa6aca25e Support building LibIRD with older .NET Core 2024-03-05 12:33:18 +09:00
Deterous a93c727ba2 Fix spacing 2024-03-05 12:11:34 +09:00
Deterous 55cab8202d Subsume used DiscUtils libraries 2024-03-05 12:01:25 +09:00
Deterous 1fa7380f06 Move DiscUtils to within LibIRD subfolder 2024-03-05 11:01:54 +09:00
DeterousandGitHub a0acd91a55 Update README.md 2024-03-01 12:35:06 +09:00
Deterous 6edb959e49 Bump IRDKit to v0.6.1 2024-02-27 13:24:08 +09:00
Deterous 96b36fcabe Add rename functionality 2024-02-27 12:49:14 +09:00
Deterous 9d03dbb7b1 Perform hashing in one read pass 2024-02-20 16:37:15 +09:00
Deterous c7b49d0c80 Initial work towards one-pass hashing 2024-02-19 21:38:06 +09:00
Deterous 5ce81f9663 Bump version 2024-02-18 17:28:21 +09:00
Deterous f74220efea Read ISO in larger chunks 2024-02-18 16:59:04 +09:00
Deterous 6e529f0050 Reuse ISO hash if searching redump 2024-02-16 11:11:51 +09:00
Deterous b862057123 Only print warning for non-contiguous files 2024-02-11 12:39:13 +09:00
Deterous cf19596ea7 Alphabetical order, key file path 2024-02-08 09:38:46 +09:00
Deterous 6af41a9bb3 Include dependencies in nupkg 2024-02-07 15:43:23 +09:00
Deterous 89c23622bd Remove DiscUtils as a public dependency 2024-02-07 14:41:14 +09:00
Deterous 8ab6a9d694 Fix file hash for split files with irregular filesize 2024-02-07 09:32:56 +09:00
Deterous bc3df52e75 Cleanup 2024-02-05 09:20:59 +09:00
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
Deterous 4b0d796bd2 Refactor, IRDKit can take directories 2023-11-30 11:05:22 +13:00
Deterous 11ed114507 Hash ISO for searching redump 2023-11-25 03:11:24 +13:00
Deterous 357a8df11b IRDKit minimum functionality 2023-11-25 02:39:47 +13:00
Deterous 652cd4aa52 Generate PIC for hybrid discs using layerbreak 2023-11-25 02:08:29 +13:00
Deterous 43e8517bf0 Refactor for .NET8 2023-11-24 21:46:16 +13:00
Deterous f86a89c04a Move printing into IRDKit 2023-11-16 11:58:27 +13:00
Deterous e665391e47 Start work on CLI tool 2023-11-16 11:45:21 +13:00
Deterous b794ec8f96 Parse PS3_DISC.SFB for redump-style IRDs 2023-11-11 19:50:50 +13:00
Deterous 5a9cce39ae Update README 2023-11-10 22:16:44 +13:00
Deterous 2fcf5fcae3 Print IRD fields, add a READEME 2023-11-10 22:09:00 +13:00
18 changed files with 2789 additions and 755 deletions
+73
View File
@@ -0,0 +1,73 @@
name: Build
on:
push: {}
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
project: [IRDKit]
runtime: [win-x86, win-x64, win-arm64, linux-x64, linux-arm64, osx-x64, osx-arm64]
framework: [net10.0]
steps:
- uses: actions/checkout@v5
with:
submodules: 'true'
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x
- name: Build
run: dotnet publish ${{ matrix.project }}/${{ matrix.project }}.csproj -f ${{ matrix.framework }} -r ${{ matrix.runtime }} -c Release -p:DebugType=None -p:DebugSymbols=false
- name: Upload build
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}
path: ${{ matrix.project }}/bin/Release/${{ matrix.framework }}/${{ matrix.runtime }}/publish/*
pack:
runs-on: windows-latest
strategy:
matrix:
project: [LibIRD]
steps:
- uses: actions/checkout@v5
with:
submodules: 'true'
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v2
- name: Setup NuGet
uses: NuGet/setup-nuget@v2
with:
nuget-version: 'latest'
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x
- name: Restore NuGet packages
run: nuget restore LibIRD/LibIRD.csproj
- name: Build
run: msbuild ${{ matrix.project }}/${{ matrix.project }}.csproj /p:Configuration=Release
- name: Pack
run: dotnet pack ${{ matrix.project }}/${{ matrix.project }}.csproj -c Release -o ./artifacts
- name: Upload nupkg
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.project }}
path: ./artifacts/*.nupkg
+3
View File
@@ -0,0 +1,3 @@
[submodule "DiscUtils"]
path = LibIRD/DiscUtils
url = https://github.com/Deterous/DiscUtils
-60
View File
@@ -1,60 +0,0 @@
using LibIRD;
using System;
using System.IO;
using System.Text;
namespace BuildIRD
{
internal class Program
{
static void Main()
{
Console.OutputEncoding = Encoding.UTF8;
// Create new reproducible redump-style IRD with a key file
try
{
// Read key from .key file
byte[] discKey = File.ReadAllBytes("./game.key");
IRD ird1 = new ReIRD("./game.iso", discKey);
ird1.Write("./test1.ird");
Console.WriteLine("IRD created using .key:");
// Read IRD and print details to console
IRD ird = IRD.Read("./test1.ird");
Console.WriteLine("IRD Version: " + ird.Version);
Console.WriteLine("Title ID: " + ird.TitleID);
Console.WriteLine("Title: " + ird.Title);
Console.WriteLine("System Version: " + ird.SystemVersion);
Console.WriteLine("Game Version: " + ird.GameVersion);
Console.WriteLine("App Version: " + ird.AppVersion);
}
catch (FileNotFoundException e)
{
Console.WriteLine("File not found: " + e.FileName);
}
// Create new reproducible redump-style IRD with a GetKey log
try
{
IRD ird2 = new ReIRD("./game.iso", "./log.getkey.log");
ird2.Write("./test2.ird");
Console.WriteLine("IRD created using .getkey.log:");
// Read IRD and print details to console
IRD ird = IRD.Read("./test2.ird");
Console.WriteLine("IRD Version: " + ird.Version);
Console.WriteLine("Title ID: " + ird.TitleID);
Console.WriteLine("Title: " + ird.Title);
Console.WriteLine("System Version: " + ird.SystemVersion);
Console.WriteLine("Game Version: " + ird.GameVersion);
Console.WriteLine("App Version: " + ird.AppVersion);
}
catch (FileNotFoundException e)
{
Console.WriteLine("File not found: " + e.FileName);
}
}
}
}
-14
View File
@@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<RuntimeIdentifiers>win-x86;win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<Version>0.1</Version>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\LibIRD\LibIRD.csproj" />
</ItemGroup>
</Project>
+44
View File
@@ -0,0 +1,44 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Assembly Properties -->
<OutputType>Exe</OutputType>
<TargetName>irdkit</TargetName>
<AssemblyName>irdkit</AssemblyName>
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<LangVersion>latest</LangVersion>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>false</SelfContained>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>1.0.1</Version>
<!-- Package Properties -->
<Authors>Deterous</Authors>
<Description>Library for ISO Rebuild Data</Description>
<Copyright>Copyright (c) Deterous 2023-2025</Copyright>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/Deterous/LibIRD</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>ps3 iso ird redump</PackageTags>
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
</PropertyGroup>
<ItemGroup>
<None Include="./README.md" Pack="true" PackagePath="" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="SabreTools.Hashing" Version="1.6.0" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.9.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LibIRD\LibIRD.csproj" />
</ItemGroup>
</Project>
+1181
View File
File diff suppressed because it is too large Load Diff
+52
View File
@@ -0,0 +1,52 @@
# How to use IRDKit
IRDKit is a tool that allows direct use of LibIRD functionality from the command line interface.
For full help instructions, run `irdkit help`
## Creating ISOs
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`
To print all info in the IRD use `-a` or `--all`
## 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=`
## Renaming IRDs
For all options, run `irdkit help rename`
To rename an IRD (or folder of IRDs) according to a redump DAT, run `irdkit rename -d redump.dat game.ird`
## Verifying with an IRD
To verify that a folder containing files from a PS3 disc match the file hashes within an IRD, run `irdkit verify game.ird folder/of/files`
+14 -10
View File
@@ -5,9 +5,17 @@ VisualStudioVersion = 17.7.34202.233
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibIRD", "LibIRD\LibIRD.csproj", "{4A80C34B-D4C5-4536-BEAE-46218BC09980}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibIRD", "LibIRD\LibIRD.csproj", "{4A80C34B-D4C5-4536-BEAE-46218BC09980}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BuildIRD", "BuildIRD\BuildIRD.csproj", "{A2FC2D93-1C3E-46E2-9D69-11F9DDA8CFC7}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRDKit", "IRDKit\IRDKit.csproj", "{9060E3AF-E4FB-436F-93A1-5C47389CB88E}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PrintParams", "PrintParams\PrintParams.csproj", "{B5D9C0AD-EA8D-486E-A1D7-ED2C3C7163BC}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A8BB1F9F-FC8E-471C-9647-6FFF91D63BA7}"
ProjectSection(SolutionItems) = preProject
.gitattributes = .gitattributes
.gitignore = .gitignore
.gitmodules = .gitmodules
LICENSE.txt = LICENSE.txt
publish-irdkit.ps1 = publish-irdkit.ps1
README.md = README.md
EndProjectSection
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -19,14 +27,10 @@ Global
{4A80C34B-D4C5-4536-BEAE-46218BC09980}.Debug|Any CPU.Build.0 = Debug|Any CPU {4A80C34B-D4C5-4536-BEAE-46218BC09980}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4A80C34B-D4C5-4536-BEAE-46218BC09980}.Release|Any CPU.ActiveCfg = Release|Any CPU {4A80C34B-D4C5-4536-BEAE-46218BC09980}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4A80C34B-D4C5-4536-BEAE-46218BC09980}.Release|Any CPU.Build.0 = Release|Any CPU {4A80C34B-D4C5-4536-BEAE-46218BC09980}.Release|Any CPU.Build.0 = Release|Any CPU
{A2FC2D93-1C3E-46E2-9D69-11F9DDA8CFC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9060E3AF-E4FB-436F-93A1-5C47389CB88E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A2FC2D93-1C3E-46E2-9D69-11F9DDA8CFC7}.Debug|Any CPU.Build.0 = Debug|Any CPU {9060E3AF-E4FB-436F-93A1-5C47389CB88E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A2FC2D93-1C3E-46E2-9D69-11F9DDA8CFC7}.Release|Any CPU.ActiveCfg = Release|Any CPU {9060E3AF-E4FB-436F-93A1-5C47389CB88E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A2FC2D93-1C3E-46E2-9D69-11F9DDA8CFC7}.Release|Any CPU.Build.0 = Release|Any CPU {9060E3AF-E4FB-436F-93A1-5C47389CB88E}.Release|Any CPU.Build.0 = Release|Any CPU
{B5D9C0AD-EA8D-486E-A1D7-ED2C3C7163BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B5D9C0AD-EA8D-486E-A1D7-ED2C3C7163BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B5D9C0AD-EA8D-486E-A1D7-ED2C3C7163BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B5D9C0AD-EA8D-486E-A1D7-ED2C3C7163BC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
+1
Submodule LibIRD/DiscUtils added at 445a572137
+882 -255
View File
File diff suppressed because it is too large Load Diff
+27 -11
View File
@@ -1,26 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<!-- Assembly Properties --> <!-- Assembly Properties -->
<TargetFrameworks>net6.0;net7.0</TargetFrameworks> <TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers> <CheckEolTargetFramework>false</CheckEolTargetFramework>
<Version>0.1.0</Version> <LangVersion>latest</LangVersion>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>1.0.1</Version>
<PackageOutputPath>../nupkg</PackageOutputPath>
<!-- Avoid inexact read with 'System.IO.Stream.Read' -->
<NoWarn>CA2022</NoWarn>
<!-- 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)2023 Deterous</Copyright> <Copyright>Copyright (c) Deterous 2023-2025</Copyright>
<RepositoryUrl>https://github.com/Deterous/LibIRD</RepositoryUrl> <PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/Deterous/LibIRD/</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PackageTags>ps3 iso ird</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>
<PackageReference Include="DiscUtils.Core" Version="0.16.13" /> <None Include="./README.md" Pack="true" PackagePath="" />
<PackageReference Include="DiscUtils.Iso9660" Version="0.16.13" /> </ItemGroup>
<PackageReference Include="DiscUtils.Streams" Version="0.16.13" />
<PackageReference Include="System.IO.Hashing" Version="7.0.0" /> <!-- Support for old .NET versions -->
<ItemGroup Condition="$(TargetFramework.StartsWith(`net2`))">
<PackageReference Include="Net30.LinqBridge" Version="1.3.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SabreTools.Hashing" Version="1.6.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+159
View File
@@ -0,0 +1,159 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Newtonsoft.Json;
namespace LibIRD
{
/// <summary>
/// PS3_DISC.SFB file parsing
/// </summary>
public class PS3_DiscSFB
{
/// <summary>
/// PS3_DISC.SFB file signature
/// </summary>
/// <remarks>{ 0x2E, 0x53, 0x46, 0x42 }</remarks>
public static readonly string Magic = ".SFB";
/// <summary>
/// PS3_DISC.SFB file version
/// </summary>
/// <remarks>Typically v1, { 0x00, 0x01 }</remarks>
public ushort Version { get; private set; }
/// <summary>
/// A field within the PS3_DISC.SFB file
/// </summary>
/// <remarks>string Key, string Value</remarks>
public Dictionary<string, string> Field { get; private set; }
/// <summary>
/// Constructor using a PARAM.SFO file path
/// </summary>
/// <param name="sfbPath">Full file path to the PS3_DISC.SFB file</param>
public PS3_DiscSFB(string sfbPath)
{
// Read file as a stream, and parse file
using FileStream fs = new(sfbPath, FileMode.Open, FileAccess.Read);
Parse(fs);
}
/// <summary>
/// Parse PS3_DISC.SFB from stream
/// </summary>
/// <param name="sfbStream">SFB file stream</param>
public PS3_DiscSFB(Stream sfbStream)
{
// Parse file stream
Parse(sfbStream);
}
/// <summary>
/// Read fields from PS3_DISC.SFB
/// </summary>
/// <param name="sfbStream">File stream for PS3_DISC.SFB</param>
/// <exception cref="FileLoadException"></exception>
private void Parse(Stream sfbStream)
{
// Read binary stream
using BinaryReader br = new(sfbStream);
// Check file signature is correct
string magic = Encoding.ASCII.GetString(br.ReadBytes(4));
if (magic != PS3_DiscSFB.Magic)
throw new FileLoadException("Unexpected PS3_DISC.SFB file");
// Read SFB file version
byte[] buf = br.ReadBytes(2);
Array.Reverse(buf);
Version = BitConverter.ToUInt16(buf, 0);
// Process all field headers
sfbStream.Seek(0x20, SeekOrigin.Begin);
string field = Encoding.ASCII.GetString(br.ReadBytes(0x10)).Trim('\0');
Field = [];
while (field != null && field != "")
{
// Find location of value
buf = br.ReadBytes(4);
Array.Reverse(buf);
int offset = BitConverter.ToInt32(buf, 0);
buf = br.ReadBytes(4);
Array.Reverse(buf);
int length = BitConverter.ToInt32(buf, 0);
// Access and store value
long pos = sfbStream.Position;
sfbStream.Seek(offset, SeekOrigin.Begin);
Field[field] = Encoding.ASCII.GetString(br.ReadBytes(length)).Trim('\0');
sfbStream.Seek(pos + 8, SeekOrigin.Begin);
// Attempt to read new field
field = Encoding.ASCII.GetString(br.ReadBytes(0x10)).Trim('\0');
}
}
/// <summary>
/// Prints formatted parameters extracted from PS3_DISC.SFB to console
/// </summary>
/// <param name="printPath">Optionally print to text file</param>
public void Print(string printPath = null, string isoName = null)
{
// Build string from parameters
StringBuilder printText = new();
if (isoName != null)
printText.AppendLine($"PS3_DISC.SFB Contents: {isoName}");
else
printText.AppendLine("PS3_DISC.SFB Contents:");
printText.AppendLine("======================");
// Loop through all parameters in PARAM.SFO
foreach (KeyValuePair<string, string> field in Field)
printText.AppendLine(field.Key + ": " + field.Value);
// Blank line
printText.Append(Environment.NewLine);
// If no path given, print to console
if (printPath == null)
{
// Ensure UTF-8 will display properly in console
Console.OutputEncoding = Encoding.UTF8;
// Print formatted string to console
Console.Write(printText);
}
else
{
// Write data to file
File.AppendAllText(printPath, printText.ToString());
}
}
/// <summary>
/// Prints parameters extracted from PS3_DISC.SFB to a json object
/// </summary>
/// <param name="jsonPath">Optionally print to json file</param>
public void PrintJson(string jsonPath = null)
{
// Serialise PS3_Disc.SFB data to a JSON object
string json = JsonConvert.SerializeObject(Field, Formatting.Indented);
// If no path given, output to console
if (jsonPath == null)
{
// Ensure UTF-8 will display properly in console
Console.OutputEncoding = Encoding.UTF8;
// Print formatted string to console
Console.Write(json);
}
else
{
// Write to path
File.AppendAllText(jsonPath, json);
}
}
}
}
+89 -145
View File
@@ -1,6 +1,8 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using Newtonsoft.Json;
namespace LibIRD namespace LibIRD
{ {
@@ -18,91 +20,14 @@ namespace LibIRD
/// <summary> /// <summary>
/// PARAM.SFO file version /// PARAM.SFO file version
/// </summary> /// </summary>
/// <remarks>Typically { 0x01 0x01 0x00 0x00 } (v1.1)</remarks> /// <remarks>Typically { 0x01, 0x01, 0x00, 0x00 } (v1.1)</remarks>
public uint Version { get; private set; } public uint Version { get; private set; }
/// <summary> /// <summary>
/// The location of the first byte of the Key Table /// A field within the PS3_DISC.SFB file
/// </summary> /// </summary>
public uint KeyTableStart { get; private set; } /// <remarks>string Key, string Value</remarks>
public Dictionary<string, string> Field { get; private set; }
/// <summary>
/// The location of the first byte of the Data Table
/// </summary>
public uint DataTableStart { get; private set; }
/// <summary>
/// The number of parameters in the table
/// </summary>
public uint ParamCount { get; private set; }
/// <summary>
/// Parameter, a single entry in parameter table
/// </summary>
public class Param
{
/// <summary>
/// Offset of key, relative to KeyTableStart
/// </summary>
public ushort KeyOffset { get; internal set; }
/// <summary>
/// Format of parameter
/// </summary>
/// <remarks>0x0400 is string, 0x0404 is uint</remarks>
public ushort DataFormat { get; internal set; }
/// <summary>
/// Number of bytes used for parameter
/// </summary>
public uint DataLength { get; internal set; }
/// <summary>
/// Total number of bytes for parameter
/// </summary>
/// <remarks>DataTotal - DataLength is padding of 0x00</remarks>
public uint DataTotal { get; internal set; }
/// <summary>
/// offset of parameter, relative to DataTableStart
/// </summary>
public uint DataOffset { get; internal set; }
/// <summary>
/// The name of the parameter
/// </summary>
public string Name { get; internal set; }
/// <summary>
/// The value of the parameter, if it is a string
/// </summary>
public string StringValue { get; internal set; } = null;
/// <summary>
/// The value of the parameter, if it is a UInt32
/// </summary>
public int IntValue { get; internal set; } = 0;
}
/// <summary>
/// The parameters in the table
/// </summary>
/// <remarks><see cref="ParamCount"/> Params in the table</remarks>
public Param[] Params { get; private set; }
/// <summary>
/// String index overloading, gets string value of given key
/// </summary>
/// <param name="key">Parameter to be retreived</param>
/// <returns>The string value of the given key</returns>
public string this[string key]
{
get
{
int index = Array.FindIndex(Params, param => param.Name == key);
return Params[index].StringValue;
}
}
/// <summary> /// <summary>
/// Constructor using a PARAM.SFO file stream /// Constructor using a PARAM.SFO file stream
@@ -117,14 +42,9 @@ namespace LibIRD
/// <summary> /// <summary>
/// Constructor using a PARAM.SFO file path /// Constructor using a PARAM.SFO file path
/// </summary> /// </summary>
/// <param name="sfoPath">Full file path to the PARAM.SFO</param> /// <param name="sfoPath">Full file path to the PARAM.SFO file</param>
/// <exception cref="ArgumentNullException"></exception>
public ParamSFO(string sfoPath) public ParamSFO(string sfoPath)
{ {
// Validate file path
if (sfoPath == null || sfoPath.Length <= 0)
throw new ArgumentNullException(nameof(sfoPath));
// Read file as a stream, and parse file // Read file as a stream, and parse file
using FileStream fs = new(sfoPath, FileMode.Open, FileAccess.Read); using FileStream fs = new(sfoPath, FileMode.Open, FileAccess.Read);
Parse(fs); Parse(fs);
@@ -143,97 +63,121 @@ namespace LibIRD
// Check file signature is correct // Check file signature is correct
string magic = Encoding.ASCII.GetString(br.ReadBytes(4)); string magic = Encoding.ASCII.GetString(br.ReadBytes(4));
if (magic != ParamSFO.Magic) if (magic != ParamSFO.Magic)
throw new FileLoadException("Not a valid PARAM.SFO file"); throw new FileLoadException("Unexpected PARAM.SFO file");
// Parse header // Parse header
Version = br.ReadUInt32(); Version = br.ReadUInt32();
KeyTableStart = br.ReadUInt32(); uint keyTableStart = br.ReadUInt32();
DataTableStart = br.ReadUInt32(); uint dataTableStart = br.ReadUInt32();
ParamCount = br.ReadUInt32(); uint paramCount = br.ReadUInt32();
// Parse parameter metadata // Parse parameter metadata
Params = new Param[ParamCount]; ushort[] keyOffset = new ushort[paramCount];
for (int i = 0; i < ParamCount; i++) uint[] dataFormat = new uint[paramCount];
uint[] dataLength = new uint[paramCount];
uint[] dataTotal = new uint[paramCount];
uint[] dataOffset = new uint[paramCount];
for (int i = 0; i < paramCount; i++)
{ {
Params[i] = new Param keyOffset[i] = br.ReadUInt16();
{ dataFormat[i] = br.ReadUInt16();
KeyOffset = br.ReadUInt16(), dataLength[i] = br.ReadUInt32();
DataFormat = br.ReadUInt16(), dataTotal[i] = br.ReadUInt32();
DataLength = br.ReadUInt32(), dataOffset[i] = br.ReadUInt32();
DataTotal = br.ReadUInt32(),
DataOffset = br.ReadUInt32()
};
} }
// Parse parameters // Parse parameters
for (int i = 0; i < ParamCount; i++) Field = [];
for (int i = 0; i < paramCount; i++)
{ {
// Move stream to ith key // Move stream to ith key
sfoStream.Position = KeyTableStart + Params[i].KeyOffset; sfoStream.Position = keyTableStart + keyOffset[i];
// Determine ith key length // Determine ith key length
uint keyLen = ((i == ParamCount - 1) ? DataTableStart - KeyTableStart : Params[i + 1].KeyOffset) uint keyLen = ((i == paramCount - 1) ? dataTableStart - keyTableStart : keyOffset[i + 1])
- Params[i].KeyOffset; - keyOffset[i];
// Read ith key name // Read ith key name
Params[i].Name = Encoding.ASCII.GetString(br.ReadBytes((int) keyLen)).TrimEnd('\0'); string key = Encoding.ASCII.GetString(br.ReadBytes((int)keyLen)).TrimEnd('\0');
// Move stream to ith data // Move stream to ith data
sfoStream.Position = DataTableStart + Params[i].DataOffset; sfoStream.Position = dataTableStart + dataOffset[i];
// Read ith data, based on data format // Read ith data, based on data format
switch (Params[i].DataFormat) Field[key] = dataFormat[i] switch
{ {
case 0x0400: // Non-null-terminated UTF-8 String // Non-null-terminated UTF-8 String
Params[i].StringValue = Encoding.UTF8.GetString(br.ReadBytes((int)Params[i].DataLength)); 0x0004 => Encoding.UTF8.GetString(br.ReadBytes((int)dataLength[i])),
break; // Null-terminated UTF-8 String
case 0x0402: // Null-terminated UTF-8 String 0x0204 => Encoding.UTF8.GetString(br.ReadBytes((int)dataLength[i])).TrimEnd('\0'),
Params[i].StringValue = Encoding.UTF8.GetString(br.ReadBytes((int)Params[i].DataLength)).TrimEnd('\0'); // Integer
break; 0x0404 => br.ReadInt32().ToString(),
case 0x0404: // Integer // Unknown data format, assume null-terminated string
//if (Params[i].DataLength != 4) _ => Encoding.UTF8.GetString(br.ReadBytes((int)dataLength[i])).TrimEnd('\0'),
//throw new ArgumentException("Integer parameter not 4 bytes?"); };
Params[i].IntValue = br.ReadInt32();
break;
default: // Unknown data format, assume null-terminated string
Params[i].StringValue = Encoding.UTF8.GetString(br.ReadBytes((int)Params[i].DataLength)).TrimEnd('\0');
break;
}
} }
} }
/// <summary> /// <summary>
/// Prints formatted parameters extracted from PARAM.SFO to console /// Prints formatted parameters extracted from PARAM.SFO to console
/// </summary> /// </summary>
public void Print() /// <param name="printPath">Optionally print to text file</param>
public void Print(string printPath = null, string isoName = null)
{ {
// Build string from parameters // Build string from parameters
StringBuilder print = new("PARAM.SFO Contents:\n====================\n"); StringBuilder printText = new();
if (isoName != null)
printText.AppendLine($"PARAM.SFO Contents: {isoName}");
else
printText.AppendLine("PARAM.SFO Contents:");
printText.AppendLine("===================");
// Loop through all parameters in PARAM.SFO // Loop through all parameters in PARAM.SFO
for (int i = 0; i < ParamCount; i++) foreach (KeyValuePair<string, string> field in Field)
{ printText.AppendLine(field.Key + ": " + field.Value);
print.Append(Params[i].Name); // Blank line
print.Append(' '); printText.Append(Environment.NewLine);
for (int j = Params[i].Name.Length; j < 20; j++)
print.Append(' ');
switch (Params[i].DataFormat)
{
case 0x0404:
print.Append(Params[i].IntValue);
break;
default:
print.Append(Params[i].StringValue);
break;
}
print.Append('\n');
}
// Ensure UTF-8 will display properly // If no path given, print to console
if (printPath == null)
{
// Ensure UTF-8 will display properly in console
Console.OutputEncoding = Encoding.UTF8; Console.OutputEncoding = Encoding.UTF8;
// Print formatted string // Print formatted string to console
Console.Write(print); Console.Write(printText);
}
else
{
// Write data to file
File.AppendAllText(printPath, printText.ToString());
}
}
/// <summary>
/// Prints parameters extracted from PARAM.SFO to a json object
/// </summary>
/// <param name="jsonPath">Optionally print to json file</param>
public void PrintJson(string jsonPath = null)
{
// Serialise PS3_Disc.SFB data to a JSON object
string json = JsonConvert.SerializeObject(Field, Formatting.Indented);
// If no path given, output to console
if (jsonPath == null)
{
// Ensure UTF-8 will display properly in console
Console.OutputEncoding = Encoding.UTF8;
// Print formatted string to console
Console.Write(json);
}
else
{
// Write to path
File.AppendAllText(jsonPath, json);
}
} }
} }
} }
+40
View File
@@ -0,0 +1,40 @@
## How to use 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, the following are some examples.
### Reproducible, redump-style IRDs
The standard way of generating a reproducible, redump-style IRD is with a redump ISO file and a redump key file (e.g. http://redump.org/disc/28721/key/):
```cs
byte[] discKey = File.ReadAllBytes("./game.key");
IRD ird = new ReIRD("./game.iso", discKey);
```
Alternatively, the disc key can be extracted from a [GetKey](https://archive.org/download/GetKeyR2GameOS.7z/GetKey-r2-GameOS.7z) log file obtained from a PS3 (or when dumping using [ManaGunZ](https://github.com/Zarh/ManaGunZ/))
```cs
IRD ird = new ReIRD("./game.iso", "./game.getkey.log");
```
### Custom IRDs
Functionality is also provided for creating IRDs with a custom disc key, disc ID, and PIC:
```cs
byte[] discKey = new byte[16];
byte[] discID = new byte[16];
byte[] PIC = new byte[115];
// Set vars to desired values
IRD ird = new IRD("./game.iso", discKey, discID, discPIC);
```
As before, a GetKey log file can also be used with an ISO to create a custom IRD, with the disc key, disc ID, and PIC all being extracted from the log file (rather than just the key for redump-style IRDs).
```cs
IRD ird = new IRD("./game.iso", "./game.getkey.log");
```
Finally, an existing IRD file can be read to create an IRD:
```cs
IRD ird = IRD.Read("./game.ird")
```
An IRD can be then be tweaked, its fields printed, and written to a new IRD:
```cs
ird.UID = 0x9F1A51D8;
ird.Print();
ird.Write("game2.ird");
```
+81 -100
View File
@@ -1,6 +1,5 @@
using System; using System;
using System.IO; using System.IO;
using System.IO.Hashing;
namespace LibIRD namespace LibIRD
{ {
@@ -51,15 +50,6 @@ namespace LibIRD
/// </summary> /// </summary>
public class ReIRD : IRD public class ReIRD : IRD
{ {
#region Properties
/// <summary>
/// ISO file size
/// </summary>
public long Size { get; private set; }
#endregion
#region Constructors #region Constructors
/// <summary> /// <summary>
@@ -67,57 +57,35 @@ 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>
/// <exception cref="ArgumentNullException"></exception> public ReIRD(string isoPath, string getKeyLog) : base(isoPath, getKeyLog, true) { }
/// <exception cref="FileNotFoundException"></exception>
/// <exception cref="InvalidDataException"></exception>
public ReIRD(string isoPath, string getKeyLog) : base(isoPath, getKeyLog)
{
// Generate Unique Identifier using ISO CRC32
UID = GenerateUID(isoPath);
// Determine ISO file size
Size = CalculateSize(isoPath);
// Generate Data 2 using Disc ID
DiscID = GenerateID(Size);
// Check that GetKey log matches expected Disc ID
//if (!((ReadOnlySpan<byte>)Data2Key).SequenceEqual(d2))
// throw new InvalidDataException("Unexpected Disc ID in .getkey.log");
// Generate Disc PIC
byte[] pic = GeneratePIC(Size);
// Check that GetKey log matches expected PIC
if (!((ReadOnlySpan<byte>)PIC).SequenceEqual(pic))
throw new InvalidDataException("Unexpected PIC in .getkey.log");
}
/// <summary> /// <summary>
/// Constructor with optional additional region to generate a specific Disc ID /// Constructor with optional additional region to generate a specific Disc ID
/// </summary> /// </summary>
/// <param name="isoPath">Path to the ISO</param> /// <param name="isoPath">Path to the ISO</param>
/// <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="uid">Unique ID, crc32 hash of ISO</param>
/// <param name="region">Disc Region</param> /// <param name="region">Disc Region</param>
/// <exception cref="ArgumentNullException"></exception> public ReIRD(string isoPath, byte[] key, long? layerbreak = null, uint? uid = null, Region region = Region.NONE) : base()
/// <exception cref="FileNotFoundException"></exception>
public ReIRD(string isoPath, byte[] key, Region region = Region.NONE)
{ {
// Generate Unique Identifier using ISO CRC32 // If the ISO CRC32 is provided, use it as the Unique ID
UID = GenerateUID(isoPath); UID = uid == null ? 0x00000000 : (uint)uid;
// 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); PIC = GeneratePIC(isoPath, size, layerbreak * SectorSize);
// Generate IRD fields // Generate IRD fields
GenerateIRD(isoPath); GenerateIRD(isoPath, true);
} }
#endregion #endregion
@@ -131,12 +99,12 @@ namespace LibIRD
private static byte[] GenerateID(long size, Region region = Region.NONE) private static byte[] GenerateID(long size, Region region = Region.NONE)
{ {
if (size > BDLayerSize) // if BD-50, Disc ID is fixed if (size > BDLayerSize) // if BD-50, Disc ID is fixed
return new byte[]{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, return [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
else // else if BD-25, Disc ID has a byte referring to disc region else // else if BD-25, Disc ID has a byte referring to disc region
{ {
return new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, return [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0x00, 0x02, 0x00, (byte)region, 0x00, 0x00, 0x00, 0x01 }; 0x00, 0x02, 0x00, (byte)region, 0x00, 0x00, 0x00, 0x01 ];
} }
} }
@@ -147,34 +115,71 @@ 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 = BDLayerSize, 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
if (layerbreak == 0 || (layerbreak != BDLayerSize && layerbreak >= size))
throw new ArgumentException("Layerbreak in bytes must be a positive integer less than the ISO Size", nameof(size));
// TODO: Generate correct PICs for Hybrid PS3 discs (BD-50 with layerbreak value other than 12219392) // Validate provided layerbreak
if (layerbreak != null)
{
if (layerbreak <= 0 || (layerbreak >= size))
throw new ArgumentException("Layerbreak in bytes must be a positive integer less than the ISO Size", nameof(size));
if (layerbreak >= 2 * BDLayerSize || layerbreak % SectorSize != 0)
throw new ArgumentException("Unexpected layerbreak value", nameof(size));
}
else if (size > 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);
DiscUtils.Iso9660.CDReader reader = new(fs);
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
byte[] pic; byte[] pic;
if (size > BDLayerSize) // if BD-50 if (size > BDLayerSize) // if BD-50
{ {
// num_sectors + layer_sector_end (0x00100000) + sectors_between_layers (0x01358C00 - 0x00CA73FE) - 3 // Layer 0 start sector = 0x01000000
byte[] total_sectors = BitConverter.GetBytes((uint)(size / SectorSize + 8067071)); long l0_start_sector = 1048576;
// Layer 0 end sector = start sector + layerbreak - 2
long l0_end_sector = ((long)layerbreak / SectorSize) + l0_start_sector - 2;
// Convert end sector location to hex values for PIC
byte[] l0es = [(byte)((l0_end_sector >> 24) & 0xFF),
(byte)((l0_end_sector >> 16) & 0xFF),
(byte)((l0_end_sector >> 8) & 0xFF),
(byte)((l0_end_sector >> 0) & 0xFF)];
// Layer 1 start sector = end of disc (0x01EFFFFE) - layerbreak + 2
long l1_start_sector = 32505854 - ((long)layerbreak! / SectorSize) + 2;
// Convert start of start sector location to hex values for PIC
byte[] l1ss = [(byte)((l1_start_sector >> 24) & 0xFF),
(byte)((l1_start_sector >> 16) & 0xFF),
(byte)((l1_start_sector >> 8) & 0xFF),
(byte)((l1_start_sector >> 0) & 0xFF)];
// Total sectors used = num_sectors + Layer 0 start + sectors_between_layers (usually 0x01358C00 - 0x00CA73FE - 3)
long total_sectors = (size / SectorSize) + l0_start_sector + (l1_start_sector - l0_end_sector - 3);
byte[] ts = BitConverter.GetBytes((uint)total_sectors);
// Define the PIC
pic = [
// Initial portion of PIC (24 bytes) // Initial portion of PIC (24 bytes)
pic = new byte[]{
// [4098 bytes] [2x 0x00] ["DI"] [v1] [10units] [DI num] // [4098 bytes] [2x 0x00] ["DI"] [v1] [10units] [DI num]
0x10, 0x02, 0x00, 0x00, 0x44, 0x49, 0x01, 0x10, 0x00, 0x00, 0x20, 0x00, 0x10, 0x02, 0x00, 0x00, 0x44, 0x49, 0x01, 0x10, 0x00, 0x00, 0x20, 0x00,
// ["BDR"] [2 layers] // ["BDR"] [2 layers]
0x42, 0x44, 0x4F, 0x01, 0x21, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x44, 0x4F, 0x01, 0x21, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
// Total sectors used on disc (4 bytes) // Total sectors used on disc (4 bytes)
total_sectors[3], total_sectors[2], total_sectors[1], total_sectors[0], ts[3], ts[2], ts[1], ts[0],
// 1st Layer sector start location (4 bytes) // 1st Layer sector start location (4 bytes)
0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
// 1st Layer sector end location (4 bytes) // 1st Layer sector end location (4 bytes), 0x00CA73FE for default BD layerbreak of 12219392
0x00, 0xCA, 0x73, 0xFE, l0es[0], l0es[1], l0es[2], l0es[3],
// 32 bytes of zeros // 32 bytes of zeros
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -184,13 +189,13 @@ namespace LibIRD
// ["BDR"] [2 layers] // ["BDR"] [2 layers]
0x42, 0x44, 0x4F, 0x01, 0x21, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x44, 0x4F, 0x01, 0x21, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
// Total sectors used on disc // Total sectors used on disc
total_sectors[3], total_sectors[2], total_sectors[1], total_sectors[0], ts[3], ts[2], ts[1], ts[0],
// 2nd Layer sector start location // 2nd Layer sector start location, 0x01358C00 for default BD layerbreak of 12219392
0x01, 0x35, 0x8C, 0x00, l1ss[0], l1ss[1], l1ss[2], l1ss[3],
// 2nd Layer sector end location // 2nd Layer sector end location
0x01, 0xEF, 0xFF, 0xFE, 0x01, 0xEF, 0xFF, 0xFE,
// Remaining 32 bytes are zeroes // Remaining 32 bytes are zeroes
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ];
// 3k3y style: 0x03 at last byte // 3k3y style: 0x03 at last byte
if (exactIRD) if (exactIRD)
pic[114] = 0x03; pic[114] = 0x03;
@@ -203,8 +208,10 @@ namespace LibIRD
// Layer sector end location: num_sectors + layer_sector_end (0x00100000) - 2 // Layer sector end location: num_sectors + layer_sector_end (0x00100000) - 2
byte[] end_sector = BitConverter.GetBytes((uint)(size / SectorSize + 1048574)); byte[] end_sector = BitConverter.GetBytes((uint)(size / SectorSize + 1048574));
// Define the PIC
pic = [
// Initial portion of PIC (24 bytes) // Initial portion of PIC (24 bytes)
pic = new byte[]{ 0x10, 0x02, 0x00, 0x00, 0x44, 0x49, 0x01, 0x08, 0x00, 0x00, 0x20, 0x00, 0x10, 0x02, 0x00, 0x00, 0x44, 0x49, 0x01, 0x08, 0x00, 0x00, 0x20, 0x00,
0x42, 0x44, 0x4F, 0x01, 0x11, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x44, 0x4F, 0x01, 0x11, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
// Total sectors used on disc (4 bytes) // Total sectors used on disc (4 bytes)
total_sectors[3], total_sectors[2], total_sectors[1], total_sectors[0], total_sectors[3], total_sectors[2], total_sectors[1], total_sectors[0],
@@ -217,56 +224,30 @@ namespace LibIRD
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ];
} }
return pic; return pic;
} }
/// <summary>
/// Generates the UID field by computing the CRC32 hash of the ISO
/// </summary>
/// <param name="isoPath">Path to the ISO</param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="FileNotFoundException"></exception>
private static uint GenerateUID(string isoPath)
{
// Validate ISO path
if (isoPath == null || isoPath.Length <= 0)
throw new ArgumentNullException(nameof(isoPath));
// Check file exists
var iso = new FileInfo(isoPath);
if (!iso.Exists)
throw new FileNotFoundException(nameof(isoPath));
// Compute CRC32 hash
byte[] crc32;
using (FileStream fs = File.OpenRead(isoPath))
{
Crc32 hasher = new();
hasher.Append(fs);
crc32 = hasher.GetCurrentHash();
}
// Redump ISO CRC32 hash is used as the Unique ID in the reproducible IRD
return BitConverter.ToUInt32(crc32, 0);
}
/// <summary> /// <summary>
/// Calculates ISO file size /// Calculates ISO file size
/// </summary> /// </summary>
/// <param name="isoPath">Path to the ISO</param> /// <param name="isoPath">Path to the ISO</param>
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentException"></exception>
/// <exception cref="FileNotFoundException"></exception> /// <exception cref="FileNotFoundException"></exception>
private static long CalculateSize(string isoPath) private static long CalculateSize(string isoPath)
{ {
// Validate ISO path
if (isoPath == null || isoPath.Length <= 0)
throw new ArgumentNullException(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));
-25
View File
@@ -1,25 +0,0 @@
using LibIRD;
using System;
using System.IO;
namespace PrintParams
{
internal class Program
{
static void Main()
{
string filename = "./PARAM.SFO";
if (File.Exists(filename))
{
ParamSFO paramSFO = new("./PARAM.SFO");
Console.WriteLine("PARAM.SFO for: " + paramSFO["TITLE_ID"] + '\n');
paramSFO.Print();
}
else
{
Console.WriteLine(filename + " not found");
}
}
}
}
-14
View File
@@ -1,14 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<RuntimeIdentifiers>win-x86;win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<Version>0.1</Version>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\LibIRD\LibIRD.csproj" />
</ItemGroup>
</Project>
+23 -1
View File
@@ -1 +1,23 @@
# WIP # Library for ISO Rebuild Data (LibIRD)
.NET library for generating, writing, and reading IRD files. Functionality is provided for deterministically generating IRDs such that a redump ISO and key have a 1-to-1 correspondence with an IRD file.
## What is an IRD?
IRD files contain a summary of what data is on a PlayStation 3 disc. It can be used to rebuild ISOs from files (JB folders). IRD files are also useful for decrypting ISOs after they have been dumped on a PC blu-ray drive, such as for the purposes of legally emulating your PS3 games.
## How to use IRDKit
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`
- 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`
- Renaming an IRD or folder of IRDs using a redump DAT: `irdkit rename -d redump.dat game.ird`
- Verify a JB Folder with an IRD: `irdkit verify game.ird folder`
For detailed usage, read more [here](IRDKit).
## 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).