mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-11-15 17:00:38 +00:00
Compare commits
402 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de17b0c59c | ||
|
|
1663345195 | ||
|
|
5c3184baa7 | ||
|
|
9e39a0cf16 | ||
|
|
36a67139d9 | ||
|
|
33caf06bc1 | ||
|
|
f639f7ac24 | ||
|
|
11f9bf9fd9 | ||
|
|
373cf2d752 | ||
|
|
dc18951d0b | ||
|
|
29b9d27c33 | ||
|
|
f6ade446c6 | ||
|
|
6238fcbb46 | ||
|
|
2f6698edac | ||
|
|
04e8751f9b | ||
|
|
7262f74f7f | ||
|
|
0b6af978ff | ||
|
|
2cacaac9fd | ||
|
|
d39d8f9b61 | ||
|
|
09fd38e776 | ||
|
|
db2a6f63ff | ||
|
|
d9dd16fbb9 | ||
|
|
3c0c31cf10 | ||
|
|
d9bf16deef | ||
|
|
3798dbcb47 | ||
|
|
2a70a9a20e | ||
|
|
6f92f85f5d | ||
|
|
b7851601a4 | ||
|
|
cb2a96c413 | ||
|
|
69c0843a0d | ||
|
|
0171ea9da7 | ||
|
|
e493af25e0 | ||
|
|
62427576e9 | ||
|
|
e80ea00872 | ||
|
|
ce1cd15978 | ||
|
|
2a604bd73a | ||
|
|
bcc0a5bd1e | ||
|
|
32d9e8a09d | ||
|
|
d0f5ac322d | ||
|
|
05c86e4c24 | ||
|
|
1d76967a08 | ||
|
|
b9ffd61252 | ||
|
|
b4e1f6b729 | ||
|
|
451fc0b394 | ||
|
|
79ca9f617e | ||
|
|
a602686551 | ||
|
|
4083aed12f | ||
|
|
717de3608c | ||
|
|
88aae0dd26 | ||
|
|
499fc44004 | ||
|
|
07325be930 | ||
|
|
481020bd08 | ||
|
|
5642f601a3 | ||
|
|
b42a12f051 | ||
|
|
a5879b46f1 | ||
|
|
9a14cc2620 | ||
|
|
a777df13db | ||
|
|
78d7bc310f | ||
|
|
ae3978241c | ||
|
|
390e39ef30 | ||
|
|
5ef595e01a | ||
|
|
371623e3fa | ||
|
|
7eb669cef3 | ||
|
|
4eac10c15c | ||
|
|
495368f28d | ||
|
|
a2d15f301b | ||
|
|
d3c9849908 | ||
|
|
249c51933b | ||
|
|
cf586f7af2 | ||
|
|
b805c91cb3 | ||
|
|
2a0e8e2eb0 | ||
|
|
49aa6895b6 | ||
|
|
3f71628600 | ||
|
|
f729eae48a | ||
|
|
f6877d82cd | ||
|
|
9ed671f595 | ||
|
|
a2b9db3afd | ||
|
|
49ea4302b6 | ||
|
|
59f76b1036 | ||
|
|
6fa3631c80 | ||
|
|
b24f96ac0f | ||
|
|
201af08f5f | ||
|
|
b9b1b24f67 | ||
|
|
d28a88e193 | ||
|
|
c02698edcd | ||
|
|
41bfe8edce | ||
|
|
9c66f6ef57 | ||
|
|
3a6ee3a37c | ||
|
|
7227f01ebc | ||
|
|
2bf686675e | ||
|
|
c179cf410b | ||
|
|
ab4bb4cca3 | ||
|
|
2462305e0b | ||
|
|
e383771fb6 | ||
|
|
bba9bfcbd6 | ||
|
|
94f86fe1d1 | ||
|
|
63c13254d7 | ||
|
|
7d6092452f | ||
|
|
6c2098c5d2 | ||
|
|
70bef7d6c9 | ||
|
|
87df32284d | ||
|
|
fda2e39296 | ||
|
|
fe58061a2d | ||
|
|
9a59f7c540 | ||
|
|
25ab9acb57 | ||
|
|
ecd4e7d1df | ||
|
|
024200ebc4 | ||
|
|
ad9828de3c | ||
|
|
d6e974a3ab | ||
|
|
c482160b5b | ||
|
|
d9a50d0c9e | ||
|
|
7acb97e78e | ||
|
|
c409c47ae5 | ||
|
|
2caab2c9cf | ||
|
|
0feb4130fe | ||
|
|
c90dcd7d59 | ||
|
|
2884dcab5b | ||
|
|
ff69b83e05 | ||
|
|
2a2a2a46f9 | ||
|
|
cb8d2885aa | ||
|
|
3e55071d41 | ||
|
|
af568d6a57 | ||
|
|
8f746824d6 | ||
|
|
334ab130da | ||
|
|
b8966f5e37 | ||
|
|
096f9d4b96 | ||
|
|
b6c6296c29 | ||
|
|
8173806537 | ||
|
|
af12310504 | ||
|
|
5694cdf9a9 | ||
|
|
dab850103b | ||
|
|
c646141bdf | ||
|
|
ca02ed6b09 | ||
|
|
5b22902d03 | ||
|
|
4e70a7665c | ||
|
|
650402df39 | ||
|
|
fead0d781c | ||
|
|
3896b75b3b | ||
|
|
ea2a1daadf | ||
|
|
5968a367e8 | ||
|
|
b439cc3e35 | ||
|
|
3675a8ac75 | ||
|
|
e16b485a7b | ||
|
|
b51879bbec | ||
|
|
f26c5bfebc | ||
|
|
4d5e7eb755 | ||
|
|
7d14681fc9 | ||
|
|
0e5380ad1a | ||
|
|
2494b44647 | ||
|
|
3d33f44eca | ||
|
|
5f9eaef9f2 | ||
|
|
b0a9464d2b | ||
|
|
5d4453fb1e | ||
|
|
ca3ebf33a8 | ||
|
|
853e2ff5c0 | ||
|
|
967d9ffe87 | ||
|
|
27a93fa86a | ||
|
|
770f6f75d5 | ||
|
|
2a700dee9c | ||
|
|
c88ea9ce30 | ||
|
|
45956e9128 | ||
|
|
baf68f3f81 | ||
|
|
9d63be5cc7 | ||
|
|
c78bde1fb2 | ||
|
|
574b993985 | ||
|
|
7485a43364 | ||
|
|
0363b1dd96 | ||
|
|
94658ae225 | ||
|
|
76c5dc40e3 | ||
|
|
6ba4a5b3ab | ||
|
|
18b3152a6c | ||
|
|
3412bbf33a | ||
|
|
278bfafae5 | ||
|
|
b4be581879 | ||
|
|
39a31b3a97 | ||
|
|
2d6e9d7875 | ||
|
|
f3d6adbe1d | ||
|
|
0350d8f58f | ||
|
|
2aca55a895 | ||
|
|
4772768c1f | ||
|
|
adfc2cde84 | ||
|
|
6cab41b58e | ||
|
|
adedc502e3 | ||
|
|
388567c9c6 | ||
|
|
c0872cb97b | ||
|
|
e2b2f4d8a5 | ||
|
|
6a7d6e16b7 | ||
|
|
f6f5818767 | ||
|
|
fe0768dad7 | ||
|
|
d6edc33dd2 | ||
|
|
d78c1eb9ed | ||
|
|
15794e07f7 | ||
|
|
d8e4f963c0 | ||
|
|
f93d254a64 | ||
|
|
25f50d19e2 | ||
|
|
752f5d31d4 | ||
|
|
83407133d6 | ||
|
|
742c991deb | ||
|
|
3caf65822f | ||
|
|
0eb5da5fd4 | ||
|
|
337d4c0fed | ||
|
|
5ffc9fef43 | ||
|
|
1a10e10fd6 | ||
|
|
1997346988 | ||
|
|
0ed13167ba | ||
|
|
285f3a267e | ||
|
|
2969324015 | ||
|
|
72dac4d6e2 | ||
|
|
ed72df79a6 | ||
|
|
bf852ef038 | ||
|
|
e31be83832 | ||
|
|
0b99602bc7 | ||
|
|
3b2b4bc525 | ||
|
|
bd8c008610 | ||
|
|
0491133329 | ||
|
|
4b20273d42 | ||
|
|
a4f844cd6a | ||
|
|
8fe2b51d6f | ||
|
|
c819be57a5 | ||
|
|
040ad65a5e | ||
|
|
f9437314cb | ||
|
|
ebf67d39cf | ||
|
|
ca2a289a3f | ||
|
|
cd49a64c2b | ||
|
|
b547792799 | ||
|
|
2f72349e7b | ||
|
|
8f82d3a28d | ||
|
|
7a9f5d1fa1 | ||
|
|
b8d4adf0e7 | ||
|
|
d08427f1ba | ||
|
|
12e1086838 | ||
|
|
274919fd6a | ||
|
|
096f1d9534 | ||
|
|
5ab48845e3 | ||
|
|
4acd2d9fd6 | ||
|
|
0458489a4f | ||
|
|
9c1d2e7b9c | ||
|
|
ba371d971d | ||
|
|
9828119bdd | ||
|
|
d72c7c9cc0 | ||
|
|
eb2f5babd9 | ||
|
|
872fdc2a10 | ||
|
|
845a0cb998 | ||
|
|
3f3f73a56f | ||
|
|
4bb670ac15 | ||
|
|
9e721c5c43 | ||
|
|
9ffa2a5e23 | ||
|
|
e5e279a48d | ||
|
|
f7ac1073b0 | ||
|
|
a8c060ff26 | ||
|
|
2fdecebe66 | ||
|
|
cdc55e721e | ||
|
|
b55a932b2b | ||
|
|
8e2f519e15 | ||
|
|
5b3ff31f85 | ||
|
|
c623c2b1f6 | ||
|
|
97b04b26a3 | ||
|
|
5ccfde54f5 | ||
|
|
9df08780db | ||
|
|
e01537c2f4 | ||
|
|
154be08a53 | ||
|
|
e3927ca3e6 | ||
|
|
fb711001e9 | ||
|
|
52edf9d20c | ||
|
|
62b2fd3325 | ||
|
|
716abed8b8 | ||
|
|
b6ab8a19be | ||
|
|
1d5514b29d | ||
|
|
32dd30adab | ||
|
|
2ed0c1f732 | ||
|
|
4fecf8b498 | ||
|
|
c9c31af8ea | ||
|
|
e604ef314b | ||
|
|
e3d3fe7c1a | ||
|
|
280e65775a | ||
|
|
33958d37fd | ||
|
|
dc773dba5e | ||
|
|
cdb74a9e26 | ||
|
|
600b0a6dbd | ||
|
|
3f82312943 | ||
|
|
08e0aa5b77 | ||
|
|
3c6cfef070 | ||
|
|
11803aa34c | ||
|
|
2b472c6f37 | ||
|
|
a914ee12c3 | ||
|
|
49d3d9c096 | ||
|
|
0df6378713 | ||
|
|
c0fd4d1334 | ||
|
|
c87db265dd | ||
|
|
d3f5dccf5b | ||
|
|
61ea4b2668 | ||
|
|
355aa4b94c | ||
|
|
ca95e96402 | ||
|
|
1e27b5d720 | ||
|
|
11c4097cba | ||
|
|
12ec3678b8 | ||
|
|
7df083c0b2 | ||
|
|
c74942362c | ||
|
|
c61aea7eac | ||
|
|
8356aed5b0 | ||
|
|
3ef7b5860d | ||
|
|
276448a914 | ||
|
|
9993e574a0 | ||
|
|
81c47b297d | ||
|
|
5bb8557555 | ||
|
|
c4524d20f0 | ||
|
|
4c9ff19cf3 | ||
|
|
451e89afff | ||
|
|
826df09a3e | ||
|
|
dfa39947ce | ||
|
|
88d182cfd1 | ||
|
|
d2b8a4aa2c | ||
|
|
5812d197a5 | ||
|
|
6810dce2ed | ||
|
|
3e2bba866d | ||
|
|
4035d9db86 | ||
|
|
1cec6fa918 | ||
|
|
b82d464b12 | ||
|
|
3f5b3d10fd | ||
|
|
af5d161fe4 | ||
|
|
e2e19db294 | ||
|
|
0b055a563e | ||
|
|
4d362bb791 | ||
|
|
0670231606 | ||
|
|
ea2bfbcb1f | ||
|
|
28f4808dbb | ||
|
|
0debcaa8d1 | ||
|
|
f1558587c3 | ||
|
|
d39c2e81d0 | ||
|
|
07fe4415fe | ||
|
|
00ee106f4a | ||
|
|
a2c7588e1c | ||
|
|
5898bec0e9 | ||
|
|
8f71041e7e | ||
|
|
4fd89ced29 | ||
|
|
55522bf59b | ||
|
|
de4566ec26 | ||
|
|
078cd88bcd | ||
|
|
e9902f0907 | ||
|
|
e5679b9152 | ||
|
|
7d29c3d7d9 | ||
|
|
1212603414 | ||
|
|
9929199f31 | ||
|
|
a7a622e118 | ||
|
|
225922e5bc | ||
|
|
465ece5ea3 | ||
|
|
8de1fbc52f | ||
|
|
29620c0c4f | ||
|
|
3a3b20df11 | ||
|
|
85aefc8d90 | ||
|
|
52ad2ff89c | ||
|
|
8356fc5970 | ||
|
|
e61aec89ea | ||
|
|
363ffee738 | ||
|
|
ce2ddf1636 | ||
|
|
dd29c8245a | ||
|
|
fdee2103ad | ||
|
|
b72fcfc60b | ||
|
|
3f6e5c2383 | ||
|
|
2ae3291a30 | ||
|
|
c216129b37 | ||
|
|
2fdc2407dd | ||
|
|
5205fac739 | ||
|
|
8a51f5d8ab | ||
|
|
9c7993f8f1 | ||
|
|
8f49e190d8 | ||
|
|
e11a08b587 | ||
|
|
5f0fdcfd8d | ||
|
|
1053ff7f23 | ||
|
|
de6a08539a | ||
|
|
219392e3a0 | ||
|
|
2ade7495c7 | ||
|
|
1acd448838 | ||
|
|
a8ed7a38f8 | ||
|
|
5208fc3e33 | ||
|
|
3dfbd23867 | ||
|
|
994a366770 | ||
|
|
d1b061fac5 | ||
|
|
6e73c93c89 | ||
|
|
5544ab0b8a | ||
|
|
8bec2087eb | ||
|
|
50e27f926d | ||
|
|
ecc88316bd | ||
|
|
7689c6dd07 | ||
|
|
bec0aeb04c | ||
|
|
2f8ee5545e | ||
|
|
015ad062ac | ||
|
|
ae6608e2a7 | ||
|
|
ca78898e93 | ||
|
|
a6ee92da32 | ||
|
|
b2eb0b5e67 | ||
|
|
685fadbfdf | ||
|
|
638934e6b0 | ||
|
|
cd2e170175 | ||
|
|
68b5436f59 | ||
|
|
b2f074048d | ||
|
|
3147974879 | ||
|
|
1a53c093b8 | ||
|
|
e4d26d4d81 | ||
|
|
b3e25bb6a6 | ||
|
|
37d5b79b96 | ||
|
|
75fef90a14 |
1
.github/workflows/build_and_test.yml
vendored
1
.github/workflows/build_and_test.yml
vendored
@@ -20,6 +20,7 @@ jobs:
|
||||
8.0.x
|
||||
9.0.x
|
||||
10.0.x
|
||||
11.0.x
|
||||
|
||||
- name: Run tests
|
||||
run: dotnet test
|
||||
|
||||
1
.github/workflows/check_pr.yml
vendored
1
.github/workflows/check_pr.yml
vendored
@@ -15,6 +15,7 @@ jobs:
|
||||
8.0.x
|
||||
9.0.x
|
||||
10.0.x
|
||||
11.0.x
|
||||
|
||||
- name: Build
|
||||
run: dotnet build
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.lscache
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
|
||||
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@@ -10,7 +10,7 @@
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/ExtractionTool/bin/Debug/net10.0/ExtractionTool.dll",
|
||||
"program": "${workspaceFolder}/ExtractionTool/bin/Debug/net11.0/ExtractionTool.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
@@ -24,7 +24,7 @@
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/InfoPrint/bin/Debug/net10.0/InfoPrint.dll",
|
||||
"program": "${workspaceFolder}/InfoPrint/bin/Debug/net11.0/InfoPrint.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0;net11.0</TargetFrameworks>
|
||||
<OutputType>Exe</OutputType>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
|
||||
@@ -10,7 +10,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Version>2.3.0</Version>
|
||||
<Version>3.3.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Support All Frameworks -->
|
||||
@@ -20,23 +20,23 @@
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`)) OR $(TargetFramework.StartsWith(`net9`)) OR $(TargetFramework.StartsWith(`net10`))">
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`)) OR $(TargetFramework.StartsWith(`net9`)) OR $(TargetFramework.StartsWith(`net10`)) OR $(TargetFramework.StartsWith(`net11`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
|
||||
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
|
||||
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0;net10.0;net11.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- These are needed for dealing with native Windows DLLs -->
|
||||
<ItemGroup Condition="'$(RuntimeIdentifier)'=='win-x86'">
|
||||
<ContentWithTargetPath Include="..\SabreTools.Serialization\runtimes\win-x86\native\StormLib.dll">
|
||||
<ContentWithTargetPath Include="..\runtimes\win-x86\native\StormLib.dll">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
|
||||
<TargetPath>StormLib.dll</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(RuntimeIdentifier)'=='win-x64'">
|
||||
<ContentWithTargetPath Include="..\SabreTools.Serialization\runtimes\win-x64\native\StormLib.dll">
|
||||
<ContentWithTargetPath Include="..\runtimes\win-x64\native\StormLib.dll">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
|
||||
<TargetPath>StormLib.dll</TargetPath>
|
||||
@@ -44,13 +44,13 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SabreTools.Serialization\SabreTools.Serialization.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.Wrappers\SabreTools.Wrappers.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.CommandLine" Version="[1.4.0]" />
|
||||
<PackageReference Include="SabreTools.IO" Version="[1.9.1]" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="10.0.3" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
|
||||
<PackageReference Include="SabreTools.CommandLine" Version="1.6.0" />
|
||||
<PackageReference Include="SabreTools.IO" Version="2.4.0" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="11.0.0" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -3,8 +3,8 @@ using System.IO;
|
||||
using SabreTools.CommandLine;
|
||||
using SabreTools.CommandLine.Inputs;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Serialization;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
using SabreTools.Wrappers;
|
||||
|
||||
namespace ExtractionTool.Features
|
||||
{
|
||||
@@ -113,218 +113,52 @@ namespace ExtractionTool.Features
|
||||
/// <param name="path">File path</param>
|
||||
private void ExtractFile(string file)
|
||||
{
|
||||
Console.WriteLine($"Attempting to extract all files from {file}");
|
||||
using Stream stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
|
||||
// Get the extension for certain checks
|
||||
string extension = Path.GetExtension(file).ToLower().TrimStart('.');
|
||||
|
||||
// Get the first 16 bytes for matching
|
||||
byte[] magic = new byte[16];
|
||||
try
|
||||
{
|
||||
int read = stream.Read(magic, 0, 16);
|
||||
stream.SeekIfPossible(0, SeekOrigin.Begin);
|
||||
Console.WriteLine($"Attempting to extract all files from {file}");
|
||||
using Stream stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
|
||||
// Read the first 32 bytes
|
||||
byte[] magic = stream.PeekBytes(32);
|
||||
|
||||
// Get the file type
|
||||
string extension = Path.GetExtension(file).TrimStart('.');
|
||||
WrapperType ft = WrapperFactory.GetFileType(magic ?? [], extension);
|
||||
|
||||
// Print out the file format
|
||||
Console.WriteLine($"File format found: {ft}");
|
||||
|
||||
// Setup the wrapper to extract
|
||||
var wrapper = WrapperFactory.CreateWrapper(ft, stream);
|
||||
|
||||
// If we don't have a wrapper
|
||||
if (wrapper is null)
|
||||
{
|
||||
Console.WriteLine($"Either {ft} is not supported or something went wrong during parsing!");
|
||||
Console.WriteLine();
|
||||
return;
|
||||
}
|
||||
|
||||
// If the wrapper is not extractable
|
||||
if (wrapper is not IExtractable extractable)
|
||||
{
|
||||
Console.WriteLine($"{ft} is not supported for extraction!");
|
||||
Console.WriteLine();
|
||||
return;
|
||||
}
|
||||
|
||||
// Print the preamble
|
||||
Console.WriteLine($"Attempting to extract from '{wrapper.Description()}'");
|
||||
Console.WriteLine();
|
||||
|
||||
// Attempt the extraction
|
||||
Directory.CreateDirectory(OutputPath);
|
||||
extractable.Extract(OutputPath, Debug);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (Debug) Console.Error.WriteLine(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the file type
|
||||
WrapperType ft = WrapperFactory.GetFileType(magic, extension);
|
||||
var wrapper = WrapperFactory.CreateWrapper(ft, stream);
|
||||
|
||||
// Create the output directory
|
||||
Directory.CreateDirectory(OutputPath);
|
||||
|
||||
// Print the preamble
|
||||
Console.WriteLine($"Attempting to extract from '{wrapper?.Description() ?? "UNKNOWN"}'");
|
||||
Console.WriteLine();
|
||||
|
||||
switch (wrapper)
|
||||
{
|
||||
// 7-zip
|
||||
case SevenZip sz:
|
||||
sz.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// Atari 7800 Cart
|
||||
case Atari7800Cart a7800:
|
||||
a7800.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// Atari Lynx Cart
|
||||
case AtariLynxCart lynx:
|
||||
lynx.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// BFPK archive
|
||||
case BFPK bfpk:
|
||||
bfpk.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// BSP
|
||||
case BSP bsp:
|
||||
bsp.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// bzip2
|
||||
case BZip2 bzip2:
|
||||
bzip2.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// CD-ROM bin file
|
||||
case CDROM cdrom:
|
||||
cdrom.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// CFB
|
||||
case CFB cfb:
|
||||
cfb.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// fwNES FDS file
|
||||
case FDS fds:
|
||||
fds.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// GCF
|
||||
case GCF gcf:
|
||||
gcf.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// gzip
|
||||
case GZip gzip:
|
||||
gzip.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// InstallShield Archive V3 (Z)
|
||||
case InstallShieldArchiveV3 isv3:
|
||||
isv3.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// IS-CAB archive
|
||||
case InstallShieldCabinet iscab:
|
||||
iscab.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// ISO 9660 volume
|
||||
case ISO9660 iso9660:
|
||||
iso9660.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// LZ-compressed file, KWAJ variant
|
||||
case LZKWAJ kwaj:
|
||||
kwaj.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// LZ-compressed file, QBasic variant
|
||||
case LZQBasic qbasic:
|
||||
qbasic.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// LZ-compressed file, SZDD variant
|
||||
case LZSZDD szdd:
|
||||
szdd.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// Microsoft Cabinet archive
|
||||
case MicrosoftCabinet mscab:
|
||||
mscab.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// MoPaQ (MPQ) archive
|
||||
case MoPaQ mpq:
|
||||
mpq.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// New Executable
|
||||
case NewExecutable nex:
|
||||
nex.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// NES Cart
|
||||
case NESCart nes:
|
||||
nes.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// PAK
|
||||
case PAK pak:
|
||||
pak.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// PFF
|
||||
case PFF pff:
|
||||
pff.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// PKZIP
|
||||
case PKZIP pkzip:
|
||||
pkzip.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// Portable Executable
|
||||
case PortableExecutable pex:
|
||||
pex.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// Quantum
|
||||
case Quantum quantum:
|
||||
quantum.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// RAR
|
||||
case RAR rar:
|
||||
rar.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// SGA
|
||||
case SGA sga:
|
||||
sga.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// Tape Archive
|
||||
case TapeArchive tar:
|
||||
tar.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// VBSP
|
||||
case VBSP vbsp:
|
||||
vbsp.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// VPK
|
||||
case VPK vpk:
|
||||
vpk.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// WAD3
|
||||
case WAD3 wad:
|
||||
wad.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// xz
|
||||
case XZ xz:
|
||||
xz.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// XZP
|
||||
case XZP xzp:
|
||||
xzp.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// ZSTD
|
||||
case ZSTD zstd:
|
||||
zstd.Extract(OutputPath, Debug);
|
||||
break;
|
||||
|
||||
// Everything else
|
||||
default:
|
||||
Console.WriteLine("Not a supported extractable file format, skipping...");
|
||||
Console.WriteLine();
|
||||
break;
|
||||
Console.WriteLine(Debug ? ex : "[Exception opening file, please try again]");
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ using SabreTools.CommandLine;
|
||||
using SabreTools.CommandLine.Inputs;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Serialization;
|
||||
using SabreTools.Serialization.Wrappers;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
using SabreTools.Wrappers;
|
||||
|
||||
namespace InfoPrint.Features
|
||||
{
|
||||
@@ -35,10 +35,11 @@ namespace InfoPrint.Features
|
||||
private const string _hashName = "hash";
|
||||
internal readonly FlagInput HashInput = new(_hashName, ["-c", "--hash"], "Output file hashes");
|
||||
|
||||
#if NETCOREAPP
|
||||
private const string _jsonName = "json";
|
||||
internal readonly FlagInput JsonInput = new(_jsonName, ["-j", "--json"], "Print info as JSON");
|
||||
#endif
|
||||
|
||||
private const string _recursiveName = "recursive";
|
||||
internal readonly FlagInput RecursiveInput = new(_recursiveName, ["-r", "--recursive"], "Recursively print info from embedded files");
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -57,12 +58,15 @@ namespace InfoPrint.Features
|
||||
/// </summary>
|
||||
public bool Hash { get; private set; }
|
||||
|
||||
#if NETCOREAPP
|
||||
/// <summary>
|
||||
/// Enable JSON output
|
||||
/// </summary>
|
||||
public bool Json { get; private set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Enable outputting subfile information
|
||||
/// </summary>
|
||||
public bool Recursive { get; private set; }
|
||||
|
||||
public MainFeature()
|
||||
: base(DisplayName, _flags, _description)
|
||||
@@ -72,9 +76,8 @@ namespace InfoPrint.Features
|
||||
Add(DebugInput);
|
||||
Add(HashInput);
|
||||
Add(FileOnlyInput);
|
||||
#if NETCOREAPP
|
||||
Add(JsonInput);
|
||||
#endif
|
||||
Add(RecursiveInput);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -84,9 +87,8 @@ namespace InfoPrint.Features
|
||||
Debug = GetBoolean(_debugName);
|
||||
Hash = GetBoolean(_hashName);
|
||||
FileOnly = GetBoolean(_fileOnlyName);
|
||||
#if NETCOREAPP
|
||||
Json = GetBoolean(_jsonName);
|
||||
#endif
|
||||
Recursive = GetBoolean(_recursiveName);
|
||||
|
||||
// Loop through the input paths
|
||||
for (int i = 0; i < Inputs.Count; i++)
|
||||
@@ -158,8 +160,8 @@ namespace InfoPrint.Features
|
||||
{
|
||||
using Stream stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
|
||||
// Read the first 16 bytes
|
||||
byte[] magic = stream.PeekBytes(16);
|
||||
// Read the first 32 bytes
|
||||
byte[] magic = stream.PeekBytes(32);
|
||||
|
||||
// Get the file type
|
||||
string extension = Path.GetExtension(file).TrimStart('.');
|
||||
@@ -179,27 +181,29 @@ namespace InfoPrint.Features
|
||||
return;
|
||||
}
|
||||
|
||||
#if NETCOREAPP
|
||||
// If the wrapper is not printable
|
||||
if (wrapper is not IPrintable printable)
|
||||
{
|
||||
Console.WriteLine($"{ft} is not supported for printing!");
|
||||
Console.WriteLine();
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have the JSON flag
|
||||
if (Json)
|
||||
{
|
||||
// Create the output data
|
||||
string serializedData = wrapper.ExportJSON();
|
||||
string serializedData = printable.ExportJSON(Recursive);
|
||||
|
||||
// Write the output data
|
||||
using var jsw = new StreamWriter(File.OpenWrite($"{filenameBase}.json"));
|
||||
jsw.WriteLine(serializedData);
|
||||
jsw.Flush();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Create the output data
|
||||
var builder = wrapper.ExportStringBuilder();
|
||||
if (builder is null)
|
||||
{
|
||||
Console.WriteLine("No item information could be generated");
|
||||
return;
|
||||
}
|
||||
var builder = new StringBuilder();
|
||||
printable.PrintInformation(builder, Recursive);
|
||||
|
||||
// Only print to console if enabled
|
||||
if (!FileOnly)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0;net11.0</TargetFrameworks>
|
||||
<OutputType>Exe</OutputType>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
|
||||
@@ -10,7 +10,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Version>2.3.0</Version>
|
||||
<Version>3.3.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Support All Frameworks -->
|
||||
@@ -20,21 +20,21 @@
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`)) OR $(TargetFramework.StartsWith(`net9`)) OR $(TargetFramework.StartsWith(`net10`))">
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`)) OR $(TargetFramework.StartsWith(`net9`)) OR $(TargetFramework.StartsWith(`net10`)) OR $(TargetFramework.StartsWith(`net11`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
|
||||
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
|
||||
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0;net10.0;net11.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SabreTools.Serialization\SabreTools.Serialization.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.Wrappers\SabreTools.Wrappers.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.CommandLine" Version="[1.4.0]" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="[1.6.1]" />
|
||||
<PackageReference Include="SabreTools.IO" Version="[1.9.1]" />
|
||||
<PackageReference Include="SabreTools.CommandLine" Version="1.6.0" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="2.2.0" />
|
||||
<PackageReference Include="SabreTools.IO" Version="2.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2018-2025 Matt Nadareski
|
||||
Copyright (c) 2018-2026 Matt Nadareski
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
||||
67
README.MD
67
README.MD
@@ -2,20 +2,10 @@
|
||||
|
||||
[](https://github.com/SabreTools/SabreTools.Serialization/actions/workflows/build_and_test.yml)
|
||||
|
||||
This library comprises of serializers that both read and write from files and streams to the dedicated models as well as convert to and from the common internal models. This library is partially used by the current parsing and writing code but none of the internal model serialization is used.
|
||||
This meta-library comprises of serializers that both read and write from files and streams to the dedicated models as well as convert to and from the common internal models. This library is partially used by the current parsing and writing code but none of the internal model serialization is used.
|
||||
|
||||
Find the link to the Nuget package [here](https://www.nuget.org/packages/SabreTools.Serialization).
|
||||
|
||||
The following non-project libraries (or ports thereof) are used for file handling:
|
||||
|
||||
- [GrindCore.SharpCompress](https://github.com/Nanook/GrindCore.SharpCompress) - Common archive format extraction
|
||||
- [StormLibSharp](https://github.com/robpaveza/stormlibsharp) - MoPaQ extraction [Unused in non-Windows builds due to Windows-specific libraries]
|
||||
|
||||
The following projects have influenced this library:
|
||||
|
||||
- [libmspack](https://github.com/kyz/libmspack) - Documentation around the MS-CAB format and associated compression methods.
|
||||
- [Unshield](https://github.com/twogood/unshield/) - InstallShield CAB extraction tool that influenced internal handling
|
||||
|
||||
## Releases
|
||||
|
||||
For the most recent stable build, download the latest release here: [Releases Page](https://github.com/SabreTools/SabreTools.Serialization/releases)
|
||||
@@ -24,7 +14,7 @@ For the latest WIP build here: [Rolling Release](https://github.com/SabreTools/S
|
||||
|
||||
## InfoPrint
|
||||
|
||||
**InfoPrint** is a reference implementation for the deserialization and printing features of the library, packaged as a standalone executable for all supported platforms. It will attempt to detect and display information about many supported file types, optionally both hashing the file and outputting the information to a JSON file (.NET Core 3.1 and above only).
|
||||
**InfoPrint** is a reference implementation for the deserialization and printing features of the library, packaged as a standalone executable for all supported platforms. It will attempt to detect and display information about many supported file types, optionally both hashing the file and outputting the information to a JSON file.
|
||||
|
||||
```text
|
||||
InfoPrint <options> file|directory ...
|
||||
@@ -35,6 +25,7 @@ Options:
|
||||
-c, --hash Output file hashes
|
||||
-f, --file Print to file only
|
||||
-j, --json Print info as JSON
|
||||
-r, --recursive Recursively print info from embedded files
|
||||
```
|
||||
|
||||
## ExtractionTool
|
||||
@@ -67,6 +58,7 @@ Options:
|
||||
| Half-Life 2 Level (VBSP) | |
|
||||
| InstallShield Archive V3 (Z) | |
|
||||
| InstallShield CAB | |
|
||||
| ISO9660 | ISO and BIN files |
|
||||
| Microsoft cabinet file | Does not support LZX or Quantum compression |
|
||||
| Microsoft LZ-compressed files | KWAJ, QBasic, and SZDD variants |
|
||||
| MoPaQ game data archive (MPQ) | Windows only |
|
||||
@@ -81,45 +73,22 @@ Options:
|
||||
| Tape archive (TAR) | |
|
||||
| Valve Package File (VPK) | |
|
||||
| XBox Package File (XZP) | |
|
||||
| Xbox DVD Filesystem (XISO) | |
|
||||
| xz archive (XZ) | .NET Framework 4.6.2 and greater |
|
||||
|
||||
## Interfaces
|
||||
|
||||
Below is a table representing the various conversion interfaces that are implemented within this library.
|
||||
|
||||
| Interface Name | Source Type | Destination Type |
|
||||
| --- | --- | --- |
|
||||
| `SabreTools.Serialization.Interfaces.IByteReader<TModel>` | `byte[]?` | `TModel` |
|
||||
| `SabreTools.Serialization.Interfaces.IByteWriter<TModel>` | `TModel` | `byte[]?` |
|
||||
| `SabreTools.Serialization.Interfaces.ICrossModel<TSource, TDest>` | `TSource`/`TDest` | `TDest`/`TSource` |
|
||||
| `SabreTools.Serialization.Interfaces.IFileReader<TModel>` | `string?` path | `TModel` |
|
||||
| `SabreTools.Serialization.Interfaces.IFileWriter<TModel>` | `TModel` | `string?` path |
|
||||
| `SabreTools.Serialization.Interfaces.IStreamReader<TModel>` | `Stream?` | `TModel` |
|
||||
| `SabreTools.Serialization.Interfaces.IStreamWriter<TModel>` | `TModel` | `Stream?` |
|
||||
| `SabreTools.Serialization.Interfaces.IStringReader<TModel>` | `string?` representation | `TModel` |
|
||||
| `SabreTools.Serialization.Interfaces.IStringWriter<TModel>` | `TModel` | `string?` representation |
|
||||
|
||||
Below is a table representing the various non-conversion interfaces that are implemented within this library.
|
||||
|
||||
| Interface Name | Purpose |
|
||||
| --- | --- |
|
||||
| `SabreTools.Serialization.Wrappers.IExtractable` | Marks a wrapper as able to be extracted |
|
||||
| `SabreTools.Serialization.Wrappers.IPrintable` | Marks a wrapper as able to print model information |
|
||||
| `SabreTools.Serialization.Wrappers.IWrapper` | Represents an item with a description and JSON serializable state, allowing for extensions |
|
||||
| `SabreTools.Serialization.Wrappers.IWrapper<TModel>` | Wraps a model with source data, allowing for extensions |
|
||||
| ZArchive | |
|
||||
|
||||
## Namespaces
|
||||
|
||||
Below is a table of all namespaces within the library and what they represent
|
||||
Below are a list of the included namespaces with links to their README files:
|
||||
|
||||
| Namespace | Description |
|
||||
| --- | --- |
|
||||
| `SabreTools.Data.Attributes` | Common attributes for data marking and manipulation |
|
||||
| `SabreTools.Data.Extensions` | Extension methods related to models |
|
||||
| `SabreTools.Data.Models` | Models representing different file and structure types |
|
||||
| `SabreTools.Data.ObjectIdentifier` | Object Identifier (OID) parsing |
|
||||
| `SabreTools.Serialization.CrossModel` | Convert between models; mainly used for metadata files converting to and from a common, `Dictionary`-based model |
|
||||
| `SabreTools.Serialization.Interfaces` | Interfaces used commonly throughout the library |
|
||||
| `SabreTools.Serialization.Readers` | Convert from external sources to models |
|
||||
| `SabreTools.Serialization.Wrappers` | Classes that wrap serialization and models to allow for including extension properties |
|
||||
| `SabreTools.Serialization.Writers` | Convert from models to external sources |
|
||||
- [SabreTools.Data.Extensions](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Data.Extensions)
|
||||
- [SabreTools.Data.Models](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Data.Models)
|
||||
- [SabreTools.Metadata.DatFiles](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Metadata.DatFiles)
|
||||
- [SabreTools.Metadata.DatItems](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Metadata.Datitems)
|
||||
- [SabreTools.Metadata.Filter](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Metadata.Filter)
|
||||
- [SabreTools.Metadata.Reports](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Metadata.Reports)
|
||||
- [SabreTools.ObjectIdentifier](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.ObjectIdentifier)
|
||||
- [SabreTools.Serialization.CrossModel](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Serialization.CrossModel)
|
||||
- [SabreTools.Serialization.Readers](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Serialization.Readers)
|
||||
- [SabreTools.Serialization.Writers](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Serialization.Writers)
|
||||
- [SabreTools.Wrappers](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Wrappers)
|
||||
|
||||
141
SabreTools.Data.Extensions.Test/Atari7800CartExtensionsTests.cs
Normal file
141
SabreTools.Data.Extensions.Test/Atari7800CartExtensionsTests.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using SabreTools.Data.Models.Atari7800;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class Atari7800CartExtensionsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData((AudioDevice)0, "POKEY - none, No YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
|
||||
// POKEY
|
||||
[InlineData(AudioDevice.Pokey440, "POKEY - @440, No YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
|
||||
[InlineData(AudioDevice.Pokey450, "POKEY - @450, No YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
|
||||
[InlineData(AudioDevice.Pokey450Plus440, "POKEY - @450+@440, No YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
|
||||
[InlineData(AudioDevice.Pokey800, "POKEY - @800, No YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
|
||||
[InlineData(AudioDevice.Pokey4000, "POKEY - @4000, No YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
|
||||
[InlineData((AudioDevice)0x0007, "Unknown 7, No YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
|
||||
// YM2151
|
||||
[InlineData(AudioDevice.YM2151460, "POKEY - none, YM2151 @460, No COVOX @430, No ADPCM Audio Stream @420")]
|
||||
// COVOX
|
||||
[InlineData(AudioDevice.COVOX430, "POKEY - none, No YM2151 @460, COVOX @430, No ADPCM Audio Stream @420")]
|
||||
// ADPCM
|
||||
[InlineData(AudioDevice.ADPCMAudioStream420, "POKEY - none, No YM2151 @460, No COVOX @430, ADPCM Audio Stream @420")]
|
||||
public void FromAudioDeviceTest(AudioDevice audio, string expected)
|
||||
{
|
||||
string actual = audio.FromAudioDevice();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData((CartType)0, "N/A")]
|
||||
[InlineData(CartType.PokeyAt4000, "pokey at $4000")]
|
||||
[InlineData(CartType.SupergameBankSwitched, "supergame bank switched")]
|
||||
[InlineData(CartType.SupergameRamAt4000, "supergame ram at $4000")]
|
||||
[InlineData(CartType.RomAt4000, "rom at $4000")]
|
||||
[InlineData(CartType.Bank6At4000, "bank 6 at $4000")]
|
||||
[InlineData(CartType.BankedRam, "banked ram")]
|
||||
[InlineData(CartType.PokeyAt450, "pokey at $450")]
|
||||
[InlineData(CartType.MirrorRamAt4000, "mirror ram at $4000")]
|
||||
[InlineData(CartType.ActivisionBanking, "activision banking")]
|
||||
[InlineData(CartType.AbsoluteBanking, "absolute banking")]
|
||||
[InlineData(CartType.PokeyAt440, "pokey at $440")]
|
||||
[InlineData(CartType.Ym2151At460461, "ym2151 at $460/$461")]
|
||||
[InlineData(CartType.Souper, "souper")]
|
||||
[InlineData(CartType.Banksets, "banksets")]
|
||||
[InlineData(CartType.HaltBankedRam, "halt banked ram")]
|
||||
[InlineData(CartType.PokeyAt800, "pokey@800")]
|
||||
public void FromCartTypeTest(CartType type, string expected)
|
||||
{
|
||||
string actual = type.FromCartType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ControllerType.None, "none")]
|
||||
[InlineData(ControllerType.Joystick, "7800 joystick")]
|
||||
[InlineData(ControllerType.Lightgun, "lightgun")]
|
||||
[InlineData(ControllerType.Paddle, "paddle")]
|
||||
[InlineData(ControllerType.Trakball, "trakball")]
|
||||
[InlineData(ControllerType.VcsJoystick, "2600 joystick")]
|
||||
[InlineData(ControllerType.VcsDriving, "2600 driving")]
|
||||
[InlineData(ControllerType.VcsKeypad, "2600 keypad")]
|
||||
[InlineData(ControllerType.STMouse, "ST mouse")]
|
||||
[InlineData(ControllerType.AmigaMouse, "Amiga mouse")]
|
||||
[InlineData(ControllerType.AtariVoxSaveKey, "AtariVox/SaveKey")]
|
||||
[InlineData(ControllerType.SNES2Atari, "SNES2Atari")]
|
||||
[InlineData(ControllerType.Mega7800, "Mega7800")]
|
||||
public void FromControllerTypeTest(ControllerType type, string expected)
|
||||
{
|
||||
string actual = type.FromControllerType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(Interrupt.None, "None")]
|
||||
[InlineData(Interrupt.Pokey1, "POKEY 1 (@450 | @800 | @4000)")]
|
||||
[InlineData(Interrupt.Pokey2, "POKEY 2 (@440)")]
|
||||
[InlineData(Interrupt.YM2151, "YM2151")]
|
||||
public void FromInterruptTest(Interrupt interrupt, string expected)
|
||||
{
|
||||
string actual = interrupt.FromInterrupt();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(Mapper.Linear, "Linear")]
|
||||
[InlineData(Mapper.SuperGame, "SuperGame")]
|
||||
[InlineData(Mapper.Activision, "Activision")]
|
||||
[InlineData(Mapper.Absolute, "Absolute")]
|
||||
[InlineData(Mapper.Souper, "Souper")]
|
||||
public void FromMapperTest(Mapper mapper, string expected)
|
||||
{
|
||||
string actual = mapper.FromMapper();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(MapperOptions.SuperGameNone, "Option at @4000 - none, Standard ROM")]
|
||||
[InlineData(MapperOptions.SuperGame16KRAM, "Option at @4000 - 16K RAM, Standard ROM")]
|
||||
[InlineData(MapperOptions.SuperGame8KEXRAMA8, "Option at @4000 - 8K EXRAM/A8, Standard ROM")]
|
||||
[InlineData(MapperOptions.SuperGame32KEXRAMM2, "Option at @4000 - 32K EXRAM/M2, Standard ROM")]
|
||||
[InlineData(MapperOptions.SuperGameEXROM, "Option at @4000 - EXROM, Standard ROM")]
|
||||
[InlineData(MapperOptions.SuperGameEXFIX, "Option at @4000 - EXFIX, Standard ROM")]
|
||||
[InlineData(MapperOptions.SuperGame32KEXRAMX2, "Option at @4000 - 32k EXRAM/X2, Standard ROM")]
|
||||
[InlineData(MapperOptions.BanksetRom, "Option at @4000 - none, Bankset ROM")]
|
||||
public void FromMapperOptionsTest(MapperOptions options, string expected)
|
||||
{
|
||||
string actual = options.FromMapperOptions();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SaveDevice.None, "None")]
|
||||
[InlineData(SaveDevice.HSC, "HSC")]
|
||||
[InlineData(SaveDevice.SaveKeyAtariVox, "SaveKey/AtariVox")]
|
||||
public void FromSaveDeviceTest(SaveDevice device, string expected)
|
||||
{
|
||||
string actual = device.FromSaveDevice();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SlotPassthroughDevice.None, "None")]
|
||||
[InlineData(SlotPassthroughDevice.XM, "XM")]
|
||||
public void FromSlotPassthroughDeviceTest(SlotPassthroughDevice device, string expected)
|
||||
{
|
||||
string actual = device.FromSlotPassthroughDevice();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData((TVType)0, "NTSC, Component, Single-region")]
|
||||
[InlineData(TVType.PAL, "PAL, Component, Single-region")]
|
||||
[InlineData(TVType.Composite, "NTSC, Composite, Single-region")]
|
||||
[InlineData(TVType.MultiRegion, "NTSC, Component, Multi-region")]
|
||||
public void FromTVTypeTest(TVType type, string expected)
|
||||
{
|
||||
string actual = type.FromTVType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using SabreTools.Data.Models.AtariLynx;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class AtariLynxCartExtensionsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(Rotation.NoRotation, "No rotation (horizontal, buttons right)")]
|
||||
[InlineData(Rotation.RotateLeft, "Rotate left (vertical, buttons down)")]
|
||||
[InlineData(Rotation.RotateRight, "Rotate right (vertical, buttons up)")]
|
||||
public void FromRotationTest(Rotation rotation, string expected)
|
||||
{
|
||||
string actual = rotation.FromRotation();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
65
SabreTools.Data.Extensions.Test/CDROMExtensionsTests.cs
Normal file
65
SabreTools.Data.Extensions.Test/CDROMExtensionsTests.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System.IO;
|
||||
using SabreTools.Data.Models.CDROM;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class CDROMExtensionsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(new byte[0], SectorMode.UNKNOWN)]
|
||||
[InlineData(new byte[] { 0x00 }, SectorMode.MODE0)]
|
||||
[InlineData(new byte[] { 0x01 }, SectorMode.MODE1)]
|
||||
[InlineData(new byte[] { 0x02 }, SectorMode.UNKNOWN)]
|
||||
[InlineData(new byte[] { 0x02, 0x00, 0x00, 0x00 }, SectorMode.MODE2_FORM1)]
|
||||
[InlineData(new byte[] { 0x02, 0x00, 0x00, 0x20 }, SectorMode.MODE2_FORM2)]
|
||||
[InlineData(new byte[] { 0x03 }, SectorMode.UNKNOWN)]
|
||||
public void GetSectorModeTest(byte[] bytes, SectorMode expected)
|
||||
{
|
||||
var stream = new MemoryStream(bytes);
|
||||
SectorMode actual = stream.GetSectorMode();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SectorMode.UNKNOWN, 2336)]
|
||||
[InlineData(SectorMode.MODE0, 2336)]
|
||||
[InlineData(SectorMode.MODE1, 2048)]
|
||||
[InlineData(SectorMode.MODE2, 2336)]
|
||||
[InlineData(SectorMode.MODE2_FORM1, 2048)]
|
||||
[InlineData(SectorMode.MODE2_FORM2, 2324)]
|
||||
public void GetUserDataSizeTest(SectorMode mode, long expected)
|
||||
{
|
||||
long actual = mode.GetUserDataSize();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SectorMode.UNKNOWN, 2064)]
|
||||
[InlineData(SectorMode.MODE0, 2064)]
|
||||
[InlineData(SectorMode.MODE1, 2064)]
|
||||
[InlineData(SectorMode.MODE2, 2064)]
|
||||
[InlineData(SectorMode.MODE2_FORM1, 2072)]
|
||||
[InlineData(SectorMode.MODE2_FORM2, 2072)]
|
||||
public void GetUserDataEndTest(SectorMode mode, long expected)
|
||||
{
|
||||
long actual = mode.GetUserDataEnd();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SectorMode.UNKNOWN, 16)]
|
||||
[InlineData(SectorMode.MODE0, 16)]
|
||||
[InlineData(SectorMode.MODE1, 16)]
|
||||
[InlineData(SectorMode.MODE2, 16)]
|
||||
[InlineData(SectorMode.MODE2_FORM1, 24)]
|
||||
[InlineData(SectorMode.MODE2_FORM2, 24)]
|
||||
public void GetUserDataStart(SectorMode mode, long expected)
|
||||
{
|
||||
long actual = mode.GetUserDataStart();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
// TODO: Figure out how to add UserDataStream tests
|
||||
}
|
||||
}
|
||||
17
SabreTools.Data.Extensions.Test/CFBExtensionsTests.cs
Normal file
17
SabreTools.Data.Extensions.Test/CFBExtensionsTests.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class CFBExtensionsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(null, null)]
|
||||
[InlineData(new byte[0], "")]
|
||||
// TODO: Create more artifical tests
|
||||
public void DecodeStreamNameTest(byte[]? bytes, string? expected)
|
||||
{
|
||||
string? actual = bytes.DecodeStreamName();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
138
SabreTools.Data.Extensions.Test/ISO9660ExtensionsTests.cs
Normal file
138
SabreTools.Data.Extensions.Test/ISO9660ExtensionsTests.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using SabreTools.Data.Models.ISO9660;
|
||||
using SabreTools.Numerics;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class ISO9660ExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_Generic_SectorLength()
|
||||
{
|
||||
VolumeDescriptor vd = new GenericVolumeDescriptor();
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(sectorLength, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_PVD_ValidBothEndian_ValidBlockSize_BlockSize()
|
||||
{
|
||||
VolumeDescriptor vd = new PrimaryVolumeDescriptor
|
||||
{
|
||||
LogicalBlockSize = new BothInt16(2048, 2048)
|
||||
};
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(2048, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_PVD_ValidBothEndian_InvalidBlockSize_SectorLength()
|
||||
{
|
||||
VolumeDescriptor vd = new PrimaryVolumeDescriptor
|
||||
{
|
||||
LogicalBlockSize = new BothInt16(2352, 2352)
|
||||
};
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(4096, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_PVD_InvalidBothEndian_ValidLE_LEValue()
|
||||
{
|
||||
VolumeDescriptor vd = new PrimaryVolumeDescriptor
|
||||
{
|
||||
LogicalBlockSize = new BothInt16(2048, -1)
|
||||
};
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(2048, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_PVD_InvalidBothEndian_ValidBE_BEValue()
|
||||
{
|
||||
VolumeDescriptor vd = new PrimaryVolumeDescriptor
|
||||
{
|
||||
LogicalBlockSize = new BothInt16(-1, 2048)
|
||||
};
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(2048, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_PVD_InvalidBothEndian_BothInvalid_SectorLength()
|
||||
{
|
||||
VolumeDescriptor vd = new PrimaryVolumeDescriptor
|
||||
{
|
||||
LogicalBlockSize = new BothInt16(-1, -2)
|
||||
};
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(sectorLength, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_SVD_ValidBothEndian_ValidBlockSize_BlockSize()
|
||||
{
|
||||
VolumeDescriptor vd = new SupplementaryVolumeDescriptor
|
||||
{
|
||||
LogicalBlockSize = new BothInt16(2048, 2048)
|
||||
};
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(2048, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_SVD_ValidBothEndian_InvalidBlockSize_SectorLength()
|
||||
{
|
||||
VolumeDescriptor vd = new SupplementaryVolumeDescriptor
|
||||
{
|
||||
LogicalBlockSize = new BothInt16(2352, 2352)
|
||||
};
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(4096, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_SVD_InvalidBothEndian_ValidLE_LEValue()
|
||||
{
|
||||
VolumeDescriptor vd = new SupplementaryVolumeDescriptor
|
||||
{
|
||||
LogicalBlockSize = new BothInt16(2048, -1)
|
||||
};
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(2048, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_SVD_InvalidBothEndian_ValidBE_BEValue()
|
||||
{
|
||||
VolumeDescriptor vd = new SupplementaryVolumeDescriptor
|
||||
{
|
||||
LogicalBlockSize = new BothInt16(-1, 2048)
|
||||
};
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(2048, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLogicalBlockSize_SVD_InvalidBothEndian_BothInvalid_SectorLength()
|
||||
{
|
||||
VolumeDescriptor vd = new SupplementaryVolumeDescriptor
|
||||
{
|
||||
LogicalBlockSize = new BothInt16(-1, -2)
|
||||
};
|
||||
short sectorLength = 4096;
|
||||
short actual = vd.GetLogicalBlockSize(sectorLength);
|
||||
Assert.Equal(sectorLength, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
using SabreTools.Data.Models.InstallShieldCabinet;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class InstallShieldCabinetExtensionsTests
|
||||
{
|
||||
#region IsCompressed
|
||||
|
||||
[Fact]
|
||||
public void IsCompressed_Null_True()
|
||||
{
|
||||
FileDescriptor? fd = null;
|
||||
bool actual = fd.IsCompressed();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsCompressed_Compressed_True()
|
||||
{
|
||||
FileDescriptor? fd = new FileDescriptor { Flags = FileFlags.FILE_COMPRESSED };
|
||||
bool actual = fd.IsCompressed();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsCompressed_NotCompressed_False()
|
||||
{
|
||||
FileDescriptor? fd = new FileDescriptor { Flags = 0 };
|
||||
bool actual = fd.IsCompressed();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IsInvalid
|
||||
|
||||
[Fact]
|
||||
public void IsInvalid_Null_True()
|
||||
{
|
||||
FileDescriptor? fd = null;
|
||||
bool actual = fd.IsInvalid();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsInvalid_Invalid_True()
|
||||
{
|
||||
FileDescriptor? fd = new FileDescriptor { Flags = FileFlags.FILE_INVALID };
|
||||
bool actual = fd.IsInvalid();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsInvalid_Valid_False()
|
||||
{
|
||||
FileDescriptor? fd = new FileDescriptor { Flags = 0 };
|
||||
bool actual = fd.IsInvalid();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IsObfuscated
|
||||
|
||||
[Fact]
|
||||
public void IsObfuscated_Null_False()
|
||||
{
|
||||
FileDescriptor? fd = null;
|
||||
bool actual = fd.IsObfuscated();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsObfuscated_Obfuscated_True()
|
||||
{
|
||||
FileDescriptor? fd = new FileDescriptor { Flags = FileFlags.FILE_OBFUSCATED };
|
||||
bool actual = fd.IsObfuscated();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsObfuscated_NotObfuscated_False()
|
||||
{
|
||||
FileDescriptor? fd = new FileDescriptor { Flags = 0 };
|
||||
bool actual = fd.IsObfuscated();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IsSplit
|
||||
|
||||
[Fact]
|
||||
public void IsSplit_Null_False()
|
||||
{
|
||||
FileDescriptor? fd = null;
|
||||
bool actual = fd.IsSplit();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsSplit_Split_True()
|
||||
{
|
||||
FileDescriptor? fd = new FileDescriptor { Flags = FileFlags.FILE_SPLIT };
|
||||
bool actual = fd.IsSplit();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsSplit_NotSplitFalse()
|
||||
{
|
||||
FileDescriptor? fd = new FileDescriptor { Flags = 0 };
|
||||
bool actual = fd.IsSplit();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetMajorVersion
|
||||
|
||||
[Fact]
|
||||
public void GetMajorVersion_NullCabinet_NegativeOne()
|
||||
{
|
||||
Cabinet? cabinet = null;
|
||||
int actual = cabinet.GetMajorVersion();
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetMajorVersion_NullHeader_NegativeOne()
|
||||
{
|
||||
CommonHeader? cabinet = null;
|
||||
int actual = cabinet.GetMajorVersion();
|
||||
Assert.Equal(-1, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0x00000000, 0)]
|
||||
[InlineData(0x00000004, 4)]
|
||||
[InlineData(0x01000000, 0)]
|
||||
[InlineData(0x01004000, 4)]
|
||||
[InlineData(0x02000000, 0)]
|
||||
[InlineData(0x02000190, 4)]
|
||||
[InlineData(0x04000000, 0)]
|
||||
[InlineData(0x04000190, 4)]
|
||||
public void GetMajorVersionTest(uint version, int expected)
|
||||
{
|
||||
CommonHeader? cabinet = new CommonHeader { Version = version };
|
||||
int actual = cabinet.GetMajorVersion();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
3564
SabreTools.Data.Extensions.Test/MetadataExtensionsTests.cs
Normal file
3564
SabreTools.Data.Extensions.Test/MetadataExtensionsTests.cs
Normal file
File diff suppressed because it is too large
Load Diff
206
SabreTools.Data.Extensions.Test/NESCartExtensionsTests.cs
Normal file
206
SabreTools.Data.Extensions.Test/NESCartExtensionsTests.cs
Normal file
@@ -0,0 +1,206 @@
|
||||
using SabreTools.Data.Models.NES;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class NESCartExtensionsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(ConsoleType.StandardSystem, "Nintendo Entertainment System/Family Computer")]
|
||||
[InlineData(ConsoleType.VSUnisystem, "VS Unisystem")]
|
||||
[InlineData(ConsoleType.PlayChoice10, "PlayChoice-10 (8 KB of Hint Screen data stored after CHR data)")]
|
||||
[InlineData(ConsoleType.ExtendedConsoleType, "Extended Console Type")]
|
||||
public void FromConsoleTypeTest(ConsoleType type, string expected)
|
||||
{
|
||||
string actual = type.FromConsoleType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(CPUPPUTiming.RP2C02, "RP2C02 (NTSC NES)")]
|
||||
[InlineData(CPUPPUTiming.RP2C07, "RP2C07 (Licensed PAL NES)")]
|
||||
[InlineData(CPUPPUTiming.MultipleRegion, "Multiple-region")]
|
||||
[InlineData(CPUPPUTiming.UA6538, "UA6538 (Dendy)")]
|
||||
public void FromCPUPPUTimingTest(CPUPPUTiming timing, string expected)
|
||||
{
|
||||
string actual = timing.FromCPUPPUTiming();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(DefaultExpansionDevice.Unspecified, "Unspecified")]
|
||||
[InlineData(DefaultExpansionDevice.StandardControllers, "Standard NES/Famicom controllers")]
|
||||
[InlineData(DefaultExpansionDevice.NESFourScore, "NES Four Score/Satellite with two additional standard controllers")]
|
||||
[InlineData(DefaultExpansionDevice.FamicomFourPlayersAdapter, "Famicom Four Players Adapter with two additional standard controllers using the 'simple' protocol")]
|
||||
[InlineData(DefaultExpansionDevice.VsSystem4016, "Vs. System (1P via $4016)")]
|
||||
[InlineData(DefaultExpansionDevice.VsSystem4017, "Vs. System (1P via $4017)")]
|
||||
[InlineData(DefaultExpansionDevice.Reserved06, "Reserved (0x06)")]
|
||||
[InlineData(DefaultExpansionDevice.VsZapper, "Vs. Zapper")]
|
||||
[InlineData(DefaultExpansionDevice.Zapper4017, "Zapper ($4017)")]
|
||||
[InlineData(DefaultExpansionDevice.TwoZappers, "Two Zappers")]
|
||||
[InlineData(DefaultExpansionDevice.BandaiHyperShotLightgun, "Bandai Hyper Shot Lightgun")]
|
||||
[InlineData(DefaultExpansionDevice.PowerPadSideA, "Power Pad Side A")]
|
||||
[InlineData(DefaultExpansionDevice.PowerPadSideB, "Power Pad Side B")]
|
||||
[InlineData(DefaultExpansionDevice.FamilyTrainerSideA, "Family Trainer Side A")]
|
||||
[InlineData(DefaultExpansionDevice.FamilyTrainerSideB, "Family Trainer Side B")]
|
||||
[InlineData(DefaultExpansionDevice.ArkanoidVausControllerNES, "Arkanoid Vaus Controller (NES)")]
|
||||
[InlineData(DefaultExpansionDevice.ArkanoidVausControllerFamicom, "Arkanoid Vaus Controller (Famicom)")]
|
||||
[InlineData(DefaultExpansionDevice.TwoVausControllersPlusFamicomDataRecorder, "Two Vaus Controllers plus Famicom Data Recorder")]
|
||||
[InlineData(DefaultExpansionDevice.KonamiHyperShotController, "Konami Hyper Shot Controller")]
|
||||
[InlineData(DefaultExpansionDevice.CoconutsPachinkoController, "Coconuts Pachinko Controller")]
|
||||
[InlineData(DefaultExpansionDevice.ExcitingBoxingPunchingBag, "Exciting Boxing Punching Bag (Blowup Doll)")]
|
||||
[InlineData(DefaultExpansionDevice.JissenMahjongController, "Jissen Mahjong Controller")]
|
||||
[InlineData(DefaultExpansionDevice.YonezawaPartyTap, "米澤 (Yonezawa) Party Tap")]
|
||||
[InlineData(DefaultExpansionDevice.OekaKidsTablet, "Oeka Kids Tablet")]
|
||||
[InlineData(DefaultExpansionDevice.SunsoftBarcodeBattler, "Sunsoft Barcode Battler")]
|
||||
[InlineData(DefaultExpansionDevice.MiraclePianoKeyboard, "Miracle Piano Keyboard")]
|
||||
[InlineData(DefaultExpansionDevice.PokkunMoguraaTapTapMat, "Pokkun Moguraa Tap-tap Mat (Whack-a-Mole Mat and Mallet)")]
|
||||
[InlineData(DefaultExpansionDevice.TopRider, "Top Rider (Inflatable Bicycle)")]
|
||||
[InlineData(DefaultExpansionDevice.DoubleFisted, "Double-Fisted (Requires or allows use of two controllers by one player)")]
|
||||
[InlineData(DefaultExpansionDevice.Famicom3DSystem, "Famicom 3D System")]
|
||||
[InlineData(DefaultExpansionDevice.DoremikkoKeyboard, "Doremikko Keyboard")]
|
||||
[InlineData(DefaultExpansionDevice.ROBGyromite, "R.O.B. Gyromite")]
|
||||
[InlineData(DefaultExpansionDevice.FamicomDataRecorder, "Famicom Data Recorder ('silent' keyboard)")]
|
||||
[InlineData(DefaultExpansionDevice.ASCIITurboFile, "ASCII Turbo File")]
|
||||
[InlineData(DefaultExpansionDevice.IGSStorageBattleBox, "IGS Storage Battle Box")]
|
||||
[InlineData(DefaultExpansionDevice.FamilyBASICKeyboardPlusFamicomDataRecorder, "Family BASIC Keyboard plus Famicom Data Recorder")]
|
||||
[InlineData(DefaultExpansionDevice.DongdaPECKeyboard, "东达 (Dōngdá) PEC Keyboard")]
|
||||
[InlineData(DefaultExpansionDevice.BitCorpBit79Keyboard, "普澤 (Pǔzé, a.k.a. Bit Corp.) Bit-79 Keyboard")]
|
||||
[InlineData(DefaultExpansionDevice.SuborKeyboard, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard")]
|
||||
[InlineData(DefaultExpansionDevice.SuborKeyboardPlusMacroWinnersMouse, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Macro Winners Mouse")]
|
||||
[InlineData(DefaultExpansionDevice.SuborKeyboardPlusSuborMouse4016, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Subor Mouse via $4016")]
|
||||
[InlineData(DefaultExpansionDevice.SNESMouse4016, "SNES Mouse ($4016)")]
|
||||
[InlineData(DefaultExpansionDevice.Multicart, "Multicart")]
|
||||
[InlineData(DefaultExpansionDevice.TwoSNESControllers, "Two SNES controllers replacing the two standard NES controllers")]
|
||||
[InlineData(DefaultExpansionDevice.RacerMateBicycle, "RacerMate Bicycle")]
|
||||
[InlineData(DefaultExpansionDevice.UForce, "U-Force")]
|
||||
[InlineData(DefaultExpansionDevice.ROBStackUp, "R.O.B. Stack-Up")]
|
||||
[InlineData(DefaultExpansionDevice.CityPatrolmanLightgun, "City Patrolman Lightgun")]
|
||||
[InlineData(DefaultExpansionDevice.SharpC1CassetteInterface, "Sharp C1 Cassette Interface")]
|
||||
[InlineData(DefaultExpansionDevice.StandardControllerWithSwappedInputs, "Standard Controller with swapped Left-Right/Up-Down/B-A")]
|
||||
[InlineData(DefaultExpansionDevice.ExcaliburSudokuPad, "Excalibur Sudoku Pad")]
|
||||
[InlineData(DefaultExpansionDevice.ABLPinball, "ABL Pinball")]
|
||||
[InlineData(DefaultExpansionDevice.GoldenNuggetCasinoExtraButtons, "Golden Nugget Casino extra buttons")]
|
||||
[InlineData(DefaultExpansionDevice.KedaKeyboard, "科达 (Kēdá) Keyboard")]
|
||||
[InlineData(DefaultExpansionDevice.SuborKeyboardPlusSuborMouse4017, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Subor Mouse via $4017")]
|
||||
[InlineData(DefaultExpansionDevice.PortTestController, "Port test controller")]
|
||||
[InlineData(DefaultExpansionDevice.BandaiMultiGamePlayerGamepad, "Bandai Multi Game Player Gamepad buttons")]
|
||||
[InlineData(DefaultExpansionDevice.VenomTVDanceMat, "Venom TV Dance Mat")]
|
||||
[InlineData(DefaultExpansionDevice.LGTVRemoteControl, "LG TV Remote Control")]
|
||||
[InlineData(DefaultExpansionDevice.FamicomNetworkController, "Famicom Network Controller")]
|
||||
[InlineData(DefaultExpansionDevice.KingFishingController, "King Fishing Controller")]
|
||||
[InlineData(DefaultExpansionDevice.CroakyKaraokeController, "Croaky Karaoke Controller")]
|
||||
[InlineData(DefaultExpansionDevice.KingwonKeyboard, "科王 (Kēwáng, a.k.a. Kingwon) Keyboard")]
|
||||
[InlineData(DefaultExpansionDevice.ZechengKeyboard, "泽诚 (Zéchéng) Keyboard")]
|
||||
[InlineData(DefaultExpansionDevice.SuborKeyboardPlusL90RotatedPS2Mouse4017, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus L90-rotated PS/2 mouse in $4017")]
|
||||
[InlineData(DefaultExpansionDevice.PS2KeyboardInUM6578PS2PortPS2Mouse4017, "PS/2 Keyboard in UM6578 PS/2 port, PS/2 Mouse via $4017")]
|
||||
[InlineData(DefaultExpansionDevice.PS2MouseInUM6578PS2Port, "PS/2 Mouse in UM6578 PS/2 port")]
|
||||
[InlineData(DefaultExpansionDevice.YuxingMouse4016, "裕兴 (Yùxìng) Mouse via $4016")]
|
||||
[InlineData(DefaultExpansionDevice.SuborKeyboardPlusYuxingMouse4016, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus 裕兴 (Yùxìng) Mouse mouse in $4016")]
|
||||
[InlineData(DefaultExpansionDevice.GigggleTVPump, "Gigggle TV Pump")]
|
||||
[InlineData(DefaultExpansionDevice.BBKKeyboardPlusR90RotatedPS2Mouse4017, "步步高 (Bùbùgāo, a.k.a. BBK) Keyboard plus R90-rotated PS/2 mouse in $4017")]
|
||||
[InlineData(DefaultExpansionDevice.MagicalCooking, "Magical Cooking")]
|
||||
[InlineData(DefaultExpansionDevice.SNESMouse4017, "SNES Mouse ($4017)")]
|
||||
[InlineData(DefaultExpansionDevice.Zapper4016, "Zapper ($4016)")]
|
||||
[InlineData(DefaultExpansionDevice.ArkanoidVausControllerPrototype, "Arkanoid Vaus Controller (Prototype)")]
|
||||
[InlineData(DefaultExpansionDevice.TVMahjongGameController, "TV 麻雀 Game (TV Mahjong Game) Controller")]
|
||||
[InlineData(DefaultExpansionDevice.MahjongGekitouDensetsuController, "麻雀激闘伝説 (Mahjong Gekitou Densetsu) Controller")]
|
||||
[InlineData(DefaultExpansionDevice.SuborKeyboardPlusXInvertedPS2Mouse4017, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus X-inverted PS/2 mouse in $4017")]
|
||||
[InlineData(DefaultExpansionDevice.IBMPCXTKeyboard, "IBM PC/XT Keyboard")]
|
||||
[InlineData(DefaultExpansionDevice.SuborKeyboardPlusMegaBookMouse, "小霸王 (Xiǎobàwáng, a.k.a. Subor) Keyboard plus Mega Book Mouse")]
|
||||
public void FromDefaultExpansionDeviceTest(DefaultExpansionDevice device, string expected)
|
||||
{
|
||||
string actual = device.FromDefaultExpansionDevice();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ExtendedConsoleType.RegularSystem, "Regular NES/Famicom/Dendy")]
|
||||
[InlineData(ExtendedConsoleType.NintendoVsSystem, "Nintendo Vs. System")]
|
||||
[InlineData(ExtendedConsoleType.Playchoice10, "Playchoice 10")]
|
||||
[InlineData(ExtendedConsoleType.RegularFamicloneDecimalMode, "Regular Famiclone, but with CPU that supports Decimal Mode")]
|
||||
[InlineData(ExtendedConsoleType.RegularNESWithEPSM, "Regular NES/Famicom with EPSM module or plug-through cartridge")]
|
||||
[InlineData(ExtendedConsoleType.VRTechnologyVT01, "V.R. Technology VT01 with red/cyan STN palette")]
|
||||
[InlineData(ExtendedConsoleType.VRTechnologyVT02, "V.R. Technology VT02")]
|
||||
[InlineData(ExtendedConsoleType.VRTechnologyVT03, "V.R. Technology VT03")]
|
||||
[InlineData(ExtendedConsoleType.VRTechnologyVT09, "V.R. Technology VT09")]
|
||||
[InlineData(ExtendedConsoleType.VRTechnologyVT32, "V.R. Technology VT32")]
|
||||
[InlineData(ExtendedConsoleType.VRTechnologyVT369, "V.R. Technology VT369")]
|
||||
[InlineData(ExtendedConsoleType.UMCUM6578, "UMC UM6578")]
|
||||
[InlineData(ExtendedConsoleType.FamicomNetworkSystem, "Famicom Network System")]
|
||||
[InlineData(ExtendedConsoleType.ReservedD, "Reserved (0x0D)")]
|
||||
[InlineData(ExtendedConsoleType.ReservedE, "Reserved (0x0E)")]
|
||||
[InlineData(ExtendedConsoleType.ReservedF, "Reserved (0x0F)")]
|
||||
public void FromExtendedConsoleTypeTest(ExtendedConsoleType type, string expected)
|
||||
{
|
||||
string actual = type.FromExtendedConsoleType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(NametableArrangement.Vertical, "Vertical")]
|
||||
[InlineData(NametableArrangement.Horizontal, "Horizontal")]
|
||||
public void FromNametableArrangementTest(NametableArrangement arrangement, string expected)
|
||||
{
|
||||
string actual = arrangement.FromNametableArrangement();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(TVSystem.NTSC, "NTSC")]
|
||||
[InlineData(TVSystem.PAL, "PAL")]
|
||||
public void FromTVSystemTest(TVSystem system, string expected)
|
||||
{
|
||||
string actual = system.FromTVSystem();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(TVSystemExtended.NTSC, "NTSC")]
|
||||
[InlineData(TVSystemExtended.DualCompatible1, "Dual-compatible (0x01)")]
|
||||
[InlineData(TVSystemExtended.PAL, "PAL")]
|
||||
[InlineData(TVSystemExtended.DualCompatible3, "Dual-compatible (0x03)")]
|
||||
public void FromTVSystemExtendedTest(TVSystemExtended system, string expected)
|
||||
{
|
||||
string actual = system.FromTVSystemExtended();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(VsHardwareType.VsUnisystem, "Vs. Unisystem (normal)")]
|
||||
[InlineData(VsHardwareType.VsUnisystemRBIBaseballProtection, "Vs. Unisystem (RBI Baseball protection)")]
|
||||
[InlineData(VsHardwareType.VsUnisystemTKOBoxingProtection, "Vs. Unisystem (TKO Boxing protection)")]
|
||||
[InlineData(VsHardwareType.VsUnisystemSuperXeviousProtection, "Vs. Unisystem (Super Xevious protection)")]
|
||||
[InlineData(VsHardwareType.VsUnisystemVsIceClimberJapanProtection, "Vs. Unisystem (Vs. Ice Climber Japan protection)")]
|
||||
[InlineData(VsHardwareType.VsDualSystem, "Vs. Dual System (normal)")]
|
||||
[InlineData(VsHardwareType.VsDualSystemRaidOnBungelingBayProtection, "Vs. Dual System (Raid on Bungeling Bay protection)")]
|
||||
public void FromVsHardwareTypeTest(VsHardwareType type, string expected)
|
||||
{
|
||||
string actual = type.FromVsHardwareType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(VsSystemType.AnyRP2C03RC2C03Variant, "Any RP2C03/RC2C03 variant")]
|
||||
[InlineData(VsSystemType.Reserved1, "Reserved (0x01)")]
|
||||
[InlineData(VsSystemType.RP2C040001, "RP2C04-0001")]
|
||||
[InlineData(VsSystemType.RP2C040002, "RP2C04-0002")]
|
||||
[InlineData(VsSystemType.RP2C040003, "RP2C04-0003")]
|
||||
[InlineData(VsSystemType.RP2C040004, "RP2C04-0004")]
|
||||
[InlineData(VsSystemType.Reserved6, "Reserved (0x06)")]
|
||||
[InlineData(VsSystemType.Reserved7, "Reserved (0x07)")]
|
||||
[InlineData(VsSystemType.RC2C0501, "RC2C05-01 (signature unknown)")]
|
||||
[InlineData(VsSystemType.RC2C0502, "RC2C05-02 ($2002 AND $3F =$3D)")]
|
||||
[InlineData(VsSystemType.RC2C0503, "RC2C05-03 ($2002 AND $1F =$1C)")]
|
||||
[InlineData(VsSystemType.RC2C0504, "RC2C05-04 ($2002 AND $1F =$1B)")]
|
||||
[InlineData(VsSystemType.ReservedC, "Reserved (0x0C)")]
|
||||
[InlineData(VsSystemType.ReservedD, "Reserved (0x0D)")]
|
||||
[InlineData(VsSystemType.ReservedE, "Reserved (0x0E)")]
|
||||
[InlineData(VsSystemType.ReservedF, "Reserved (0x0F)")]
|
||||
public void FromVsSystemTypeTest(VsSystemType type, string expected)
|
||||
{
|
||||
string actual = type.FromVsSystemType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using SabreTools.Data.Models.NewExecutable;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class NewExecutableExtensionsTests
|
||||
{
|
||||
#region IsIntegerType
|
||||
|
||||
[Fact]
|
||||
public void IsIntegerType_RTIE_HighBitSet_True()
|
||||
{
|
||||
var entry = new ResourceTypeInformationEntry { TypeID = 0xFFFF };
|
||||
bool actual = entry.IsIntegerType();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsIntegerType_RTIE_HighBitClear_False()
|
||||
{
|
||||
var entry = new ResourceTypeInformationEntry { TypeID = 0x0000 };
|
||||
bool actual = entry.IsIntegerType();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsIntegerType_RTRE_HighBitSet_True()
|
||||
{
|
||||
var entry = new ResourceTypeResourceEntry { ResourceID = 0xFFFF };
|
||||
bool actual = entry.IsIntegerType();
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsIntegerType_RTRE_HighBitClear_False()
|
||||
{
|
||||
var entry = new ResourceTypeResourceEntry { ResourceID = 0x0000 };
|
||||
bool actual = entry.IsIntegerType();
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetEntryType
|
||||
|
||||
[Theory]
|
||||
[InlineData(0x00, SegmentEntryType.Unused)]
|
||||
[InlineData(0x01, SegmentEntryType.FixedSegment)]
|
||||
[InlineData(0xAA, SegmentEntryType.FixedSegment)]
|
||||
[InlineData(0xFE, SegmentEntryType.FixedSegment)]
|
||||
[InlineData(0xFF, SegmentEntryType.MoveableSegment)]
|
||||
public void GetEntryTypeTest(byte segmentIndicator, SegmentEntryType expected)
|
||||
{
|
||||
var entry = new EntryTableBundle { SegmentIndicator = segmentIndicator };
|
||||
SegmentEntryType actual = entry.GetEntryType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
129
SabreTools.Data.Extensions.Test/NintendoDiscExtensionsTests.cs
Normal file
129
SabreTools.Data.Extensions.Test/NintendoDiscExtensionsTests.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using SabreTools.Data.Models.NintendoDisc;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class NintendoDiscExtensionsTests
|
||||
{
|
||||
#region GetPlatform
|
||||
|
||||
[Fact]
|
||||
public void GetPlatform_Object_WiiMagic_Wii()
|
||||
{
|
||||
var header = new DiscHeader { WiiMagic = Constants.WiiMagicWord };
|
||||
Platform actual = header.GetPlatform();
|
||||
Assert.Equal(Platform.Wii, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPlatform_Object_GCMagic_GC()
|
||||
{
|
||||
var header = new DiscHeader { GCMagic = Constants.GCMagicWord };
|
||||
Platform actual = header.GetPlatform();
|
||||
Assert.Equal(Platform.GameCube, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPlatform_Object_ValidGameID_GC()
|
||||
{
|
||||
var header = new DiscHeader { GameId = "GABC01" };
|
||||
Platform actual = header.GetPlatform();
|
||||
Assert.Equal(Platform.GameCube, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPlatform_Object_InvalidGameID_Unknown()
|
||||
{
|
||||
var header = new DiscHeader { GameId = "XABC01" };
|
||||
Platform actual = header.GetPlatform();
|
||||
Assert.Equal(Platform.Unknown, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPlatform_Object_DefaultHeader_Unknown()
|
||||
{
|
||||
var header = new DiscHeader();
|
||||
Platform actual = header.GetPlatform();
|
||||
Assert.Equal(Platform.Unknown, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPlatform_Bytes_WiiMagic_Wii()
|
||||
{
|
||||
byte[] header =
|
||||
[
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00-0x07
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x08-0x0F
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10-0x17
|
||||
0x5D, 0x1C, 0x9E, 0xA3, 0x00, 0x00, 0x00, 0x00, // 0x18-0x1F
|
||||
];
|
||||
|
||||
Platform actual = header.GetPlatform();
|
||||
Assert.Equal(Platform.Wii, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPlatform_Bytes_GCMagic_GC()
|
||||
{
|
||||
byte[] header =
|
||||
[
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00-0x07
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x08-0x0F
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10-0x17
|
||||
0x00, 0x00, 0x00, 0x00, 0xC2, 0x33, 0x9F, 0x3D, // 0x18-0x1F
|
||||
];
|
||||
|
||||
Platform actual = header.GetPlatform();
|
||||
Assert.Equal(Platform.GameCube, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPlatform_Bytes_ValidGameID_GC()
|
||||
{
|
||||
byte[] header = [(byte)'G', (byte)'A', (byte)'B', (byte)'C'];
|
||||
|
||||
Platform actual = header.GetPlatform();
|
||||
Assert.Equal(Platform.GameCube, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPlatform_Bytes_InvalidGameID_Unknown()
|
||||
{
|
||||
byte[] header = [(byte)'G', (byte)'A', (byte)'B', 0x00];
|
||||
|
||||
Platform actual = header.GetPlatform();
|
||||
Assert.Equal(Platform.Unknown, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPlatform_Bytes_DefaultHeader_Unknown()
|
||||
{
|
||||
byte[] header = [];
|
||||
|
||||
Platform actual = header.GetPlatform();
|
||||
Assert.Equal(Platform.Unknown, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IsGameCubeTitleType
|
||||
|
||||
[Theory]
|
||||
[InlineData('\0', false)]
|
||||
[InlineData('a', false)]
|
||||
[InlineData('A', false)]
|
||||
[InlineData('d', false)]
|
||||
[InlineData('D', true)]
|
||||
[InlineData('g', false)]
|
||||
[InlineData('G', true)]
|
||||
[InlineData('r', false)]
|
||||
[InlineData('R', true)]
|
||||
public void IsGameCubeTitleTypeTest(char c, bool expected)
|
||||
{
|
||||
bool actual = c.IsGameCubeTitleType();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
26
SabreTools.Data.Extensions.Test/OperaFSExtensionsTests.cs
Normal file
26
SabreTools.Data.Extensions.Test/OperaFSExtensionsTests.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using SabreTools.Data.Models.OperaFS;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class OperaFSExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void DirectoryDescriptor_EqualsExactly_Empty()
|
||||
{
|
||||
var dir1 = new DirectoryDescriptor();
|
||||
var dir2 = new DirectoryDescriptor();
|
||||
bool actual = dir1.EqualsExactly(dir2);
|
||||
Assert.True(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DirectoryRecord_EqualsExactly_Empty()
|
||||
{
|
||||
var dr1 = new DirectoryRecord();
|
||||
var dr2 = new DirectoryRecord();
|
||||
bool actual = dr1.EqualsExactly(dr2);
|
||||
Assert.True(actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0;net9.0;net10.0;net11.0</TargetFrameworks>
|
||||
<IsPackable>false</IsPackable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="10.1.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.11.1" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="4.0.1">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SabreTools.Data.Extensions\SabreTools.Data.Extensions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,10 +1,9 @@
|
||||
using System;
|
||||
using SabreTools.Data.Extensions;
|
||||
using SabreTools.Data.Models.ASN1;
|
||||
using Xunit;
|
||||
|
||||
#pragma warning disable IDE0230 // Use UTF-8 string literal
|
||||
namespace SabreTools.Serialization.Test.Extensions
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class TypeLengthValueTests
|
||||
{
|
||||
@@ -14,7 +13,7 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_EOC()
|
||||
{
|
||||
string expected = "Type: V_ASN1_EOC";
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_EOC, Length = 0, Value = null };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_EOC, Length = 0, Value = null };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -23,7 +22,7 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_ZeroLength()
|
||||
{
|
||||
string expected = "Type: V_ASN1_NULL, Length: 0";
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_NULL, Length = 0, Value = null };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_NULL, Length = 0, Value = null };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -32,7 +31,7 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_InvalidConstructed()
|
||||
{
|
||||
string expected = "Type: V_ASN1_OBJECT, V_ASN1_CONSTRUCTED, Length: 1, Value: [INVALID DATA TYPE]";
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT | ASN1Type.V_ASN1_CONSTRUCTED, Length = 1, Value = false };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT | ASN1Type.V_ASN1_CONSTRUCTED, Length = 1, Value = false };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -41,8 +40,8 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_ValidConstructed()
|
||||
{
|
||||
string expected = "Type: V_ASN1_OBJECT, V_ASN1_CONSTRUCTED, Length: 3, Value:\n Type: V_ASN1_BOOLEAN, Length: 1, Value: True";
|
||||
var boolTlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01 } };
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT | ASN1Type.V_ASN1_CONSTRUCTED, Length = 3, Value = new Data.Models.ASN1.TypeLengthValue[] { boolTlv } };
|
||||
var boolTlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01 } };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT | ASN1Type.V_ASN1_CONSTRUCTED, Length = 3, Value = new TypeLengthValue[] { boolTlv } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -51,7 +50,7 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_InvalidDataType()
|
||||
{
|
||||
string expected = "Type: V_ASN1_OBJECT, Length: 1, Value: [INVALID DATA TYPE]";
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT, Length = 1, Value = false };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT, Length = 1, Value = false };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -60,7 +59,7 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_InvalidLength()
|
||||
{
|
||||
string expected = "Type: V_ASN1_NULL, Length: 1, Value: [NO DATA]";
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_NULL, Length = 1, Value = Array.Empty<byte>() };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_NULL, Length = 1, Value = Array.Empty<byte>() };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -69,7 +68,7 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_InvalidBooleanLength()
|
||||
{
|
||||
string expected = "Type: V_ASN1_BOOLEAN, Length: 2 [Expected length of 1], Value: True";
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 2, Value = new byte[] { 0x01 } };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 2, Value = new byte[] { 0x01 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -78,7 +77,7 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_InvalidBooleanArrayLength()
|
||||
{
|
||||
string expected = "Type: V_ASN1_BOOLEAN, Length: 1 [Expected value length of 1], Value: True";
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01, 0x00 } };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01, 0x00 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -87,7 +86,7 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_ValidBoolean()
|
||||
{
|
||||
string expected = "Type: V_ASN1_BOOLEAN, Length: 1, Value: True";
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01 } };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BOOLEAN, Length = 1, Value = new byte[] { 0x01 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -96,7 +95,7 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_ValidInteger()
|
||||
{
|
||||
string expected = "Type: V_ASN1_INTEGER, Length: 1, Value: 1";
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_INTEGER, Length = 1, Value = new byte[] { 0x01 } };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_INTEGER, Length = 1, Value = new byte[] { 0x01 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -105,7 +104,7 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_ValidBitString_NoBits()
|
||||
{
|
||||
string expected = "Type: V_ASN1_BIT_STRING, Length: 1, Value with 0 unused bits";
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BIT_STRING, Length = 1, Value = new byte[] { 0x00 } };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BIT_STRING, Length = 1, Value = new byte[] { 0x00 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -114,7 +113,7 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_ValidBitString_Bits()
|
||||
{
|
||||
string expected = "Type: V_ASN1_BIT_STRING, Length: 1, Value with 1 unused bits: 01";
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BIT_STRING, Length = 1, Value = new byte[] { 0x01, 0x01 } };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BIT_STRING, Length = 1, Value = new byte[] { 0x01, 0x01 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -123,7 +122,7 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_ValidOctetString()
|
||||
{
|
||||
string expected = "Type: V_ASN1_OCTET_STRING, Length: 1, Value: 01";
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OCTET_STRING, Length = 1, Value = new byte[] { 0x01 } };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OCTET_STRING, Length = 1, Value = new byte[] { 0x01 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -132,7 +131,7 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_ValidObject()
|
||||
{
|
||||
string expected = "Type: V_ASN1_OBJECT, Length: 3, Value: 0.1.2.3 (/ITU-T/1/2/3)";
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT, Length = 3, Value = new byte[] { 0x01, 0x02, 0x03 } };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT, Length = 3, Value = new byte[] { 0x01, 0x02, 0x03 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -141,7 +140,7 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_ValidUTF8String()
|
||||
{
|
||||
string expected = "Type: V_ASN1_UTF8STRING, Length: 3, Value: ABC";
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_UTF8STRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_UTF8STRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -150,7 +149,7 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_ValidPrintableString()
|
||||
{
|
||||
string expected = "Type: V_ASN1_PRINTABLESTRING, Length: 3, Value: ABC";
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_PRINTABLESTRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_PRINTABLESTRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -159,7 +158,7 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_ValidTeletexString()
|
||||
{
|
||||
string expected = "Type: V_ASN1_TELETEXSTRING, Length: 3, Value: ABC";
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_TELETEXSTRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_TELETEXSTRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -168,7 +167,7 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_ValidIA5String()
|
||||
{
|
||||
string expected = "Type: V_ASN1_IA5STRING, Length: 3, Value: ABC";
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_IA5STRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_IA5STRING, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -177,7 +176,7 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_InvalidUTCTime()
|
||||
{
|
||||
string expected = "Type: V_ASN1_UTCTIME, Length: 3, Value: ABC";
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_UTCTIME, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_UTCTIME, Length = 3, Value = new byte[] { 0x41, 0x42, 0x43 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -186,7 +185,7 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_ValidUTCTime()
|
||||
{
|
||||
string expected = "Type: V_ASN1_UTCTIME, Length: 3, Value: 1980-01-01 00:00:00";
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_UTCTIME, Length = 3, Value = new byte[] { 0x31, 0x39, 0x38, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31, 0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30 } };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_UTCTIME, Length = 3, Value = new byte[] { 0x31, 0x39, 0x38, 0x30, 0x2D, 0x30, 0x31, 0x2D, 0x30, 0x31, 0x20, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -195,7 +194,7 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_ValidBmpString()
|
||||
{
|
||||
string expected = "Type: V_ASN1_BMPSTRING, Length: 6, Value: ABC";
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_BMPSTRING, Length = 6, Value = new byte[] { 0x41, 0x00, 0x42, 0x00, 0x43, 0x00 } };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_BMPSTRING, Length = 6, Value = new byte[] { 0x41, 0x00, 0x42, 0x00, 0x43, 0x00 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
@@ -204,7 +203,7 @@ namespace SabreTools.Serialization.Test.Extensions
|
||||
public void Format_ValidUnformatted()
|
||||
{
|
||||
string expected = "Type: V_ASN1_OBJECT_DESCRIPTOR, Length: 1, Value: 01";
|
||||
var tlv = new Data.Models.ASN1.TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT_DESCRIPTOR, Length = 1, Value = new byte[] { 0x01 } };
|
||||
var tlv = new TypeLengthValue { Type = ASN1Type.V_ASN1_OBJECT_DESCRIPTOR, Length = 1, Value = new byte[] { 0x01 } };
|
||||
string actual = tlv.Format();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
274
SabreTools.Data.Extensions.Test/UDFExtensionsTests.cs
Normal file
274
SabreTools.Data.Extensions.Test/UDFExtensionsTests.cs
Normal file
@@ -0,0 +1,274 @@
|
||||
using SabreTools.Data.Models.UDF;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class UDFExtensionsTests
|
||||
{
|
||||
#region DecodeDCharacters
|
||||
|
||||
[Fact]
|
||||
public void DecodeDCharacters_Null_Empty()
|
||||
{
|
||||
byte[]? data = null;
|
||||
Assert.Equal(string.Empty, data.DecodeDCharacters());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DecodeDCharacters_8Bit_Decoded()
|
||||
{
|
||||
byte[] data = [8, 0x41, 0x62, 0xE4];
|
||||
Assert.Equal("Abä", data.DecodeDCharacters());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DecodeDCharacters_16Bit_Decoded()
|
||||
{
|
||||
byte[] data = [16, 0x00, 0x41, 0x30, 0x42];
|
||||
Assert.Equal("Aあ", data.DecodeDCharacters());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DecodeDCharacters_UnknownCompression_Null()
|
||||
{
|
||||
byte[] data = [1, 0x41];
|
||||
Assert.Null(data.DecodeDCharacters());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DecodeDString_UsesLengthByte()
|
||||
{
|
||||
byte[] data = [8, 0x41, 0x42, 0x43, 0x00, 3];
|
||||
Assert.Equal("AB", data.DecodeDString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DecodeDString_ZeroLength_Empty()
|
||||
{
|
||||
byte[] data = [8, 0x41, 0x00, 0];
|
||||
Assert.Equal(string.Empty, data.DecodeDString());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region TryGetSector
|
||||
|
||||
[Fact]
|
||||
public void TryGetSector_Type1_StartPlusBlock()
|
||||
{
|
||||
var volume = CreateVolume(new Type1PartitionMap { PartitionNumber = 0 });
|
||||
bool actual = volume.TryGetSector(UDFExtensions.GetKey(0, 10), out long sector);
|
||||
Assert.True(actual);
|
||||
Assert.Equal(1010, sector);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetSector_Type1_OutsidePartition_False()
|
||||
{
|
||||
var volume = CreateVolume(new Type1PartitionMap { PartitionNumber = 0 });
|
||||
bool actual = volume.TryGetSector(UDFExtensions.GetKey(0, 100), out _);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetSector_UnknownPartitionReference_False()
|
||||
{
|
||||
var volume = CreateVolume(new Type1PartitionMap { PartitionNumber = 0 });
|
||||
bool actual = volume.TryGetSector(UDFExtensions.GetKey(1, 0), out _);
|
||||
Assert.False(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetSector_Metadata_ThroughMetadataFile()
|
||||
{
|
||||
// Metadata File at block 5 of partition 0: blocks 0-1 of the metadata partition are blocks 20-21,
|
||||
// blocks 2-3 are blocks 40-41
|
||||
var volume = CreateVolume(
|
||||
new Type1PartitionMap { PartitionNumber = 0 },
|
||||
new MetadataPartitionMap { PartitionNumber = 0, MetadataFileLocation = 5, MetadataMirrorFileLocation = 6 });
|
||||
volume.FileEntries[UDFExtensions.GetKey(0, 5)] = CreateShortAdEntry((4096, 20), (4096, 40));
|
||||
|
||||
Assert.True(volume.TryGetSector(UDFExtensions.GetKey(1, 1), out long first));
|
||||
Assert.Equal(1021, first);
|
||||
Assert.True(volume.TryGetSector(UDFExtensions.GetKey(1, 2), out long second));
|
||||
Assert.Equal(1040, second);
|
||||
Assert.False(volume.TryGetSector(UDFExtensions.GetKey(1, 4), out _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TryGetSector_Metadata_FallsBackToMirror()
|
||||
{
|
||||
var volume = CreateVolume(
|
||||
new Type1PartitionMap { PartitionNumber = 0 },
|
||||
new MetadataPartitionMap { PartitionNumber = 0, MetadataFileLocation = 5, MetadataMirrorFileLocation = 6 });
|
||||
volume.FileEntries[UDFExtensions.GetKey(0, 6)] = CreateShortAdEntry((2048, 30));
|
||||
|
||||
Assert.True(volume.TryGetSector(UDFExtensions.GetKey(1, 0), out long sector));
|
||||
Assert.Equal(1030, sector);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region GetAllocationDescriptors
|
||||
|
||||
[Fact]
|
||||
public void GetAllocationDescriptors_Embedded_Empty()
|
||||
{
|
||||
var entry = new FileEntry();
|
||||
entry.ICBTag.Flags = (ushort)AllocationDescriptorType.EMBEDDED;
|
||||
entry.AllocationDescriptors = [1, 2, 3];
|
||||
|
||||
var actual = new Volume().GetAllocationDescriptors(0, entry);
|
||||
Assert.NotNull(actual);
|
||||
Assert.Empty(actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAllocationDescriptors_ZeroLengthTerminates()
|
||||
{
|
||||
var entry = CreateShortAdEntry((2048, 20), (0, 99), (2048, 40));
|
||||
|
||||
var actual = new Volume().GetAllocationDescriptors(UDFExtensions.GetKey(2, 7), entry);
|
||||
Assert.NotNull(actual);
|
||||
var ad = Assert.Single(actual);
|
||||
Assert.Equal(20u, ad.ExtentLocation.LogicalBlockNumber);
|
||||
Assert.Equal((ushort)2, ad.ExtentLocation.PartitionReferenceNumber);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAllocationDescriptors_MissingAllocationExtent_NullWithKey()
|
||||
{
|
||||
var entry = CreateShortAdEntry((2048, 20), ((3u << 30) | 2048, 50));
|
||||
|
||||
var actual = new Volume().GetAllocationDescriptors(UDFExtensions.GetKey(0, 7), entry, out long missingKey);
|
||||
Assert.Null(actual);
|
||||
Assert.Equal(UDFExtensions.GetKey(0, 50), missingKey);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAllocationDescriptors_AllocationExtentLoop_Null()
|
||||
{
|
||||
var volume = new Volume();
|
||||
var entry = CreateShortAdEntry(((3u << 30) | 2048, 50));
|
||||
volume.AllocationExtentDescriptors[UDFExtensions.GetKey(0, 50)] = new AllocationExtentDescriptor
|
||||
{
|
||||
AllocationDescriptors = CreateShortAdEntry(((3u << 30) | 2048, 50)).AllocationDescriptors,
|
||||
};
|
||||
|
||||
var actual = volume.GetAllocationDescriptors(UDFExtensions.GetKey(0, 7), entry);
|
||||
Assert.Null(actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IsValidDescriptor
|
||||
|
||||
[Theory]
|
||||
[InlineData(2)]
|
||||
[InlineData(3)]
|
||||
public void IsValidDescriptor_Valid_True(byte version)
|
||||
{
|
||||
byte[] data = CreateDescriptor(version);
|
||||
Assert.True(data.IsValidDescriptor(0, data.Length, 20));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValidDescriptor_WrongChecksum_False()
|
||||
{
|
||||
byte[] data = CreateDescriptor(2);
|
||||
data[4]++;
|
||||
Assert.False(data.IsValidDescriptor(0, data.Length, 20));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValidDescriptor_UnknownVersion_False()
|
||||
{
|
||||
byte[] data = CreateDescriptor(4);
|
||||
Assert.False(data.IsValidDescriptor(0, data.Length, 20));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValidDescriptor_WrongCrc_False()
|
||||
{
|
||||
byte[] data = CreateDescriptor(2);
|
||||
data[16] ^= 0x01;
|
||||
Assert.False(data.IsValidDescriptor(0, data.Length, 20));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValidDescriptor_CrcPastAvailable_NotChecked()
|
||||
{
|
||||
byte[] data = CreateDescriptor(2);
|
||||
data[16] ^= 0x01;
|
||||
Assert.True(data.IsValidDescriptor(0, 16, 20));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsValidDescriptor_WrongLocation_False()
|
||||
{
|
||||
byte[] data = CreateDescriptor(2);
|
||||
Assert.False(data.IsValidDescriptor(0, data.Length, 21));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Descriptor with tag location 20 whose 3 bytes after the tag have the CRC #3299 (ECMA-167 3/7.2.6)
|
||||
/// </summary>
|
||||
private static byte[] CreateDescriptor(byte version)
|
||||
{
|
||||
byte[] data = new byte[19];
|
||||
data[0] = 1;
|
||||
data[2] = version;
|
||||
data[8] = 0x99;
|
||||
data[9] = 0x32;
|
||||
data[10] = 3;
|
||||
data[12] = 20;
|
||||
data[16] = 0x70;
|
||||
data[17] = 0x6A;
|
||||
data[18] = 0x77;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
if (i != 4)
|
||||
data[4] += data[i];
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Volume with partition 0 at sector 1000 (length 50) and the given partition maps
|
||||
/// </summary>
|
||||
private static Volume CreateVolume(params PartitionMap[] maps)
|
||||
{
|
||||
return new Volume
|
||||
{
|
||||
VolumeDescriptorSequence =
|
||||
[
|
||||
new PartitionDescriptor { PartitionNumber = 0, PartitionStartingLocation = 1000, PartitionLength = 50 },
|
||||
new LogicalVolumeDescriptor { LogicalBlockSize = 2048, PartitionMaps = maps },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// File Entry with the given short allocation descriptors (extent length, position)
|
||||
/// </summary>
|
||||
private static FileEntry CreateShortAdEntry(params (uint Length, uint Position)[] extents)
|
||||
{
|
||||
var data = new byte[extents.Length * 8];
|
||||
for (int i = 0; i < extents.Length; i++)
|
||||
{
|
||||
for (int b = 0; b < 4; b++)
|
||||
{
|
||||
data[(i * 8) + b] = (byte)(extents[i].Length >> (8 * b));
|
||||
data[(i * 8) + 4 + b] = (byte)(extents[i].Position >> (8 * b));
|
||||
}
|
||||
}
|
||||
|
||||
var entry = new FileEntry { AllocationDescriptors = data };
|
||||
entry.ICBTag.Flags = (ushort)AllocationDescriptorType.SHORT;
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
59
SabreTools.Data.Extensions.Test/WiseScriptExtensionsTests.cs
Normal file
59
SabreTools.Data.Extensions.Test/WiseScriptExtensionsTests.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class WiseScriptExtensionsTests
|
||||
{
|
||||
[Theory]
|
||||
// Defined functions
|
||||
[InlineData("f0", "Add Directory to PATH")]
|
||||
[InlineData("f1", "Add to AUTOEXEC.BAT")]
|
||||
[InlineData("f2", "Add to CONFIG.SYS")]
|
||||
[InlineData("f3", "Add to SYSTEM.INI")]
|
||||
[InlineData("f8", "Read INI Value")]
|
||||
[InlineData("f9", "Get Registry Key Value")]
|
||||
[InlineData("f10", "Register Font")]
|
||||
[InlineData("f11", "Win32 System Directory")]
|
||||
[InlineData("f12", "Check Configuration")]
|
||||
[InlineData("f13", "Search for File")]
|
||||
[InlineData("f15", "Read/Write Binary File")]
|
||||
[InlineData("f16", "Set Variable")]
|
||||
[InlineData("f17", "Get Environment Variable")]
|
||||
[InlineData("f19", "Check if File/Dir Exists")]
|
||||
[InlineData("f20", "Set File Attributes")]
|
||||
[InlineData("f21", "Set Files/Buffers")]
|
||||
[InlineData("f22", "Find File in Path")]
|
||||
[InlineData("f23", "Check Disk Space")]
|
||||
[InlineData("f25", "Insert Line Into Text File")]
|
||||
[InlineData("f27", "Parse String")]
|
||||
[InlineData("f28", "Exit Installation")]
|
||||
[InlineData("f29", "Self-Register OCXs/DLLs")]
|
||||
[InlineData("f30", "Install DirectX Components")]
|
||||
[InlineData("f31", "Wizard Block")]
|
||||
[InlineData("f33", "Read/Update Text File")]
|
||||
[InlineData("f34", "Post to HTTP Server")]
|
||||
[InlineData("f35", "Prompt for Filename")]
|
||||
[InlineData("f36", "Start/Stop Service")]
|
||||
[InlineData("f38", "Check HTTP Connection")]
|
||||
// Undefined functions
|
||||
[InlineData("f4", "UNDEFINED f4")]
|
||||
[InlineData("f5", "UNDEFINED f5")]
|
||||
[InlineData("f6", "UNDEFINED f6")]
|
||||
[InlineData("f7", "UNDEFINED f7")]
|
||||
[InlineData("f14", "UNDEFINED f14")]
|
||||
[InlineData("f18", "UNDEFINED f18")]
|
||||
[InlineData("f24", "UNDEFINED f24")]
|
||||
[InlineData("f26", "UNDEFINED f26")]
|
||||
[InlineData("f32", "UNDEFINED f32")]
|
||||
[InlineData("f37", "UNDEFINED f37")]
|
||||
// External DLL
|
||||
[InlineData(null, null)]
|
||||
[InlineData("f99", "UNDEFINED f99")]
|
||||
[InlineData("func", "External: func")]
|
||||
public void FromWiseFunctionIdTest(string? functionId, string? expected)
|
||||
{
|
||||
string? actual = functionId.FromWiseFunctionId();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
SabreTools.Data.Extensions.Test/XZExtensionsTests.cs
Normal file
20
SabreTools.Data.Extensions.Test/XZExtensionsTests.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class XZExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void VariableLengthIntegerTest()
|
||||
{
|
||||
ulong expected = 123456789;
|
||||
byte[] encoded = expected.EncodeVariableLength();
|
||||
Assert.True(encoded.SequenceEqual<byte>([0x95, 0x9A, 0xEF, 0x3A]));
|
||||
|
||||
ulong actual = encoded.DecodeVariableLength(maxSize: 16, out int length);
|
||||
Assert.Equal(expected, actual);
|
||||
Assert.Equal(4, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class XboxExecutableExtensionsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(0x00000000, "0000-000")]
|
||||
[InlineData(0x4142000F, "AB-015")]
|
||||
[InlineData(0x3132F000, "12-61440")]
|
||||
public void ToFormattedXBETitleIDTest(uint value, string expected)
|
||||
{
|
||||
string actual = value.ToFormattedXBETitleID();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
SabreTools.Data.Extensions.Test/ZArchiveExtensionsTests.cs
Normal file
18
SabreTools.Data.Extensions.Test/ZArchiveExtensionsTests.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using SabreTools.Data.Models.ZArchive;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Data.Extensions.Test
|
||||
{
|
||||
public class ZArchiveExtensionsTests
|
||||
{
|
||||
[Fact]
|
||||
public void GetName_Null()
|
||||
{
|
||||
var de = new DirectoryEntry();
|
||||
NameTable nt = new NameTable();
|
||||
string? expected = null;
|
||||
string? actual = de.GetName(nt);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ using SabreTools.Data.Models.Atari7800;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class Atari7800Cart
|
||||
public static class Atari7800CartExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert a <see cref="AudioDevice"/> value to string
|
||||
@@ -12,7 +12,7 @@ namespace SabreTools.Data.Extensions
|
||||
{
|
||||
string[] devices = new string[4];
|
||||
|
||||
byte pokey = (byte)((byte)audio & 0x0F);
|
||||
byte pokey = (byte)((byte)audio & 0x07);
|
||||
devices[0] = pokey switch
|
||||
{
|
||||
0 => "POKEY - none",
|
||||
@@ -24,7 +24,7 @@ namespace SabreTools.Data.Extensions
|
||||
_ => $"Unknown {pokey}",
|
||||
};
|
||||
|
||||
byte ym2151 = (byte)(((byte)audio >> 1) & 0x01);
|
||||
byte ym2151 = (byte)(((byte)audio >> 3) & 0x01);
|
||||
devices[1] = ym2151 switch
|
||||
{
|
||||
0 => "No YM2151 @460",
|
||||
@@ -32,7 +32,7 @@ namespace SabreTools.Data.Extensions
|
||||
_ => $"Unknown {ym2151}",
|
||||
};
|
||||
|
||||
byte covox = (byte)(((byte)audio >> 2) & 0x01);
|
||||
byte covox = (byte)(((byte)audio >> 4) & 0x01);
|
||||
devices[2] = covox switch
|
||||
{
|
||||
0 => "No COVOX @430",
|
||||
@@ -40,7 +40,7 @@ namespace SabreTools.Data.Extensions
|
||||
_ => $"Unknown {covox}",
|
||||
};
|
||||
|
||||
byte adpcm = (byte)(((byte)audio >> 3) & 0x01);
|
||||
byte adpcm = (byte)(((byte)audio >> 5) & 0x01);
|
||||
devices[3] = adpcm switch
|
||||
{
|
||||
0 => "No ADPCM Audio Stream @420",
|
||||
@@ -239,7 +239,7 @@ namespace SabreTools.Data.Extensions
|
||||
{
|
||||
string[] romOptions = new string[2];
|
||||
|
||||
byte option4000 = (byte)((byte)options & 0x0F);
|
||||
byte option4000 = (byte)((byte)options & 0x07);
|
||||
romOptions[0] = option4000 switch
|
||||
{
|
||||
0 => "Option at @4000 - none",
|
||||
@@ -2,7 +2,7 @@ using SabreTools.Data.Models.AtariLynx;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class AtariLynxCart
|
||||
public static class AtariLynxCartExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert a <see cref="Rotation"/> value to string
|
||||
33
SabreTools.Data.Extensions/BSPExtensions.cs
Normal file
33
SabreTools.Data.Extensions/BSPExtensions.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using SabreTools.Data.Models.BSP;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class BSPExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert a <see cref="BspLumpType"/> value to string
|
||||
/// </summary>
|
||||
public static string FromBspLumpType(this BspLumpType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
BspLumpType.LUMP_ENTITIES => " - LUMP_ENTITIES",
|
||||
BspLumpType.LUMP_PLANES => " - LUMP_PLANES",
|
||||
BspLumpType.LUMP_TEXTURES => " - LUMP_TEXTURES",
|
||||
BspLumpType.LUMP_VERTICES => " - LUMP_VERTICES",
|
||||
BspLumpType.LUMP_VISIBILITY => " - LUMP_VISIBILITY",
|
||||
BspLumpType.LUMP_NODES => " - LUMP_NODES",
|
||||
BspLumpType.LUMP_TEXINFO => " - LUMP_TEXINFO",
|
||||
BspLumpType.LUMP_FACES => " - LUMP_FACES",
|
||||
BspLumpType.LUMP_LIGHTING => " - LUMP_LIGHTING",
|
||||
BspLumpType.LUMP_CLIPNODES => " - LUMP_CLIPNODES",
|
||||
BspLumpType.LUMP_LEAVES => " - LUMP_LEAVES",
|
||||
BspLumpType.LUMP_MARKSURFACES => " - LUMP_MARKSURFACES",
|
||||
BspLumpType.LUMP_EDGES => " - LUMP_EDGES",
|
||||
BspLumpType.LUMP_SURFEDGES => " - LUMP_SURFEDGES",
|
||||
BspLumpType.LUMP_MODELS => " - LUMP_MODELS",
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,11 @@ using System;
|
||||
using System.IO;
|
||||
using SabreTools.Data.Models.CDROM;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class CDROM
|
||||
public static class CDROMExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the sector mode for a CD-ROM stream
|
||||
@@ -106,7 +107,7 @@ namespace SabreTools.Data.Extensions
|
||||
/// <summary>
|
||||
/// Creates a stream that provides only the user data of a CDROM stream
|
||||
/// </summary>
|
||||
public class ISO9660Stream : Stream
|
||||
public class UserDataStream : Stream
|
||||
{
|
||||
// Base CDROM stream (2352-byte sector)
|
||||
private readonly Stream _baseStream;
|
||||
@@ -120,7 +121,7 @@ namespace SabreTools.Data.Extensions
|
||||
private long _isoSectorSize = Constants.Mode1DataSize;
|
||||
#pragma warning restore IDE0044
|
||||
|
||||
public ISO9660Stream(Stream inputStream)
|
||||
public UserDataStream(Stream inputStream)
|
||||
{
|
||||
if (!inputStream.CanSeek || !inputStream.CanRead)
|
||||
throw new ArgumentException("Stream must be readable and seekable.", nameof(inputStream));
|
||||
@@ -2,7 +2,7 @@ using System.Text;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class CFB
|
||||
public static class CFBExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Decode a MIME-encoded stream name stored as a byte array
|
||||
@@ -3,7 +3,7 @@ using SabreTools.Numerics;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class ISO9660
|
||||
public static class ISO9660Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the logical block size from a sector length
|
||||
@@ -2,7 +2,7 @@ using SabreTools.Data.Models.InstallShieldCabinet;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class InstallShieldCabinet
|
||||
public static class InstallShieldCabinetExtensions
|
||||
{
|
||||
#region File Descriptors
|
||||
|
||||
1688
SabreTools.Data.Extensions/MetadataExtensions.cs
Normal file
1688
SabreTools.Data.Extensions/MetadataExtensions.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@ using SabreTools.Data.Models.NES;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class NESCart
|
||||
public static class NESCartExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert a <see cref="ConsoleType"/> value to string
|
||||
@@ -155,13 +155,13 @@ namespace SabreTools.Data.Extensions
|
||||
/// <summary>
|
||||
/// Convert a <see cref="NametableArrangement"/> value to string
|
||||
/// </summary>
|
||||
public static string FromNametableArrangement(this NametableArrangement type)
|
||||
public static string FromNametableArrangement(this NametableArrangement arrangement)
|
||||
{
|
||||
return type switch
|
||||
return arrangement switch
|
||||
{
|
||||
NametableArrangement.Vertical => "Vertical",
|
||||
NametableArrangement.Horizontal => "Horizontal",
|
||||
_ => $"Unknown {(byte)type}",
|
||||
_ => $"Unknown {(byte)arrangement}",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ namespace SabreTools.Data.Extensions
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// Convert a <see cref="TVSystemExtended"/> value to string
|
||||
/// </summary>
|
||||
public static string FromTVSystemExtended(this TVSystemExtended system)
|
||||
@@ -2,7 +2,7 @@ using SabreTools.Data.Models.NewExecutable;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class NewExecutable
|
||||
public static class NewExecutableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Determine if a resource type information entry is an integer or offset
|
||||
77
SabreTools.Data.Extensions/NintendoDiscExtensions.cs
Normal file
77
SabreTools.Data.Extensions/NintendoDiscExtensions.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using SabreTools.Data.Models.NintendoDisc;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class NintendoDiscExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the platform associated with a disc header
|
||||
/// </summary>
|
||||
public static Platform GetPlatform(this DiscHeader header)
|
||||
{
|
||||
if (header.WiiMagic == Constants.WiiMagicWord)
|
||||
return Platform.Wii;
|
||||
else if (header.GCMagic == Constants.GCMagicWord)
|
||||
return Platform.GameCube;
|
||||
else if (header.GameId.Length >= 1 && IsGameCubeTitleType(header.GameId[0]))
|
||||
return Platform.GameCube;
|
||||
else
|
||||
return Platform.Unknown;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the platform associated with a disc header
|
||||
/// </summary>
|
||||
public static Platform GetPlatform(this byte[] header)
|
||||
{
|
||||
// Check for Wii magic bytes
|
||||
if (header.Length >= 0x1C)
|
||||
{
|
||||
int offset = 0x18;
|
||||
uint magic = header.ReadUInt32BigEndian(ref offset);
|
||||
if (magic == Constants.WiiMagicWord)
|
||||
return Platform.Wii;
|
||||
}
|
||||
|
||||
// Check for GameCube magic bytes
|
||||
if (header.Length >= 0x20)
|
||||
{
|
||||
int offset = 0x1C;
|
||||
uint magic = header.ReadUInt32BigEndian(ref offset);
|
||||
if (magic == Constants.GCMagicWord)
|
||||
return Platform.GameCube;
|
||||
}
|
||||
|
||||
// Check for a valid game ID
|
||||
if (header.Length >= 4)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
byte c = header[i];
|
||||
if (!((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')))
|
||||
return Platform.Unknown;
|
||||
}
|
||||
|
||||
return Platform.GameCube;
|
||||
}
|
||||
|
||||
return Platform.Unknown;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the GameId first character is a known GameCube title type prefix.
|
||||
/// Used as a fallback when the GC magic word is absent from the disc image.
|
||||
/// </summary>
|
||||
public static bool IsGameCubeTitleType(this char c)
|
||||
=> c == 'G' || c == 'D' || c == 'R';
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the byte is a known Nintendo disc title type code
|
||||
/// (first byte of the 6-char GameId, e.g. 'G'=GameCube, 'R'=GameCube,
|
||||
/// 'D'=GameCube demo, 'S'=Wii, 'F'=Wii channel)
|
||||
/// </summary>
|
||||
public static bool IsNintendoDiscTitleType(this byte b)
|
||||
=> b == 'G' || b == 'D' || b == 'R' || b == 'S' || b == 'F';
|
||||
}
|
||||
}
|
||||
78
SabreTools.Data.Extensions/OperaFSExtensions.cs
Normal file
78
SabreTools.Data.Extensions/OperaFSExtensions.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using SabreTools.Data.Models.OperaFS;
|
||||
using SabreTools.Matching;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class OperaFSExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Compare two DirectoryDescriptor objects
|
||||
/// </summary>
|
||||
/// <param name="dir1">The provided DirectoryDescriptor</param>
|
||||
/// <param name="dir2">The DirectoryDescriptor to compare against</param>
|
||||
/// <returns>True if all fields are equal, otherwise false</returns>
|
||||
public static bool EqualsExactly(this DirectoryDescriptor dir1, DirectoryDescriptor dir2)
|
||||
{
|
||||
if (dir1.NextBlock != dir2.NextBlock)
|
||||
return false;
|
||||
if (dir1.PreviousBlock != dir2.PreviousBlock)
|
||||
return false;
|
||||
if (dir1.Flags != dir2.Flags)
|
||||
return false;
|
||||
if (dir1.FirstFreeByte != dir2.FirstFreeByte)
|
||||
return false;
|
||||
if (dir1.FirstEntryOffset != dir2.FirstEntryOffset)
|
||||
return false;
|
||||
if (dir1.DirectoryRecords.Length != dir2.DirectoryRecords.Length)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < dir1.DirectoryRecords.Length; i++)
|
||||
{
|
||||
if (!dir1.DirectoryRecords[i].EqualsExactly(dir2.DirectoryRecords[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two DirectoryRecord objects
|
||||
/// </summary>
|
||||
/// <param name="dir1">The provided DirectoryRecord</param>
|
||||
/// <param name="dir2">The DirectoryRecord to compare against</param>
|
||||
/// <returns>True if all fields are equal, otherwise false</returns>
|
||||
public static bool EqualsExactly(this DirectoryRecord dr1, DirectoryRecord dr2)
|
||||
{
|
||||
if ((byte)dr1.DirectoryRecordFlags != (byte)dr2.DirectoryRecordFlags)
|
||||
return false;
|
||||
if (!dr1.UniqueIdentifier.EqualsExactly(dr2.UniqueIdentifier))
|
||||
return false;
|
||||
if (!dr1.Type.EqualsExactly(dr2.Type))
|
||||
return false;
|
||||
if (dr1.BlockSize != dr2.BlockSize)
|
||||
return false;
|
||||
if (dr1.ByteCount != dr2.ByteCount)
|
||||
return false;
|
||||
if (dr1.BlockCount != dr2.BlockCount)
|
||||
return false;
|
||||
if (dr1.Burst != dr2.Burst)
|
||||
return false;
|
||||
if (dr1.Gap != dr2.Gap)
|
||||
return false;
|
||||
if (!dr1.Filename.EqualsExactly(dr2.Filename))
|
||||
return false;
|
||||
if (dr1.LastAvatarIndex != dr2.LastAvatarIndex)
|
||||
return false;
|
||||
if (dr1.AvatarList.Length != dr2.AvatarList.Length)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < dr1.AvatarList.Length; i++)
|
||||
{
|
||||
if (dr1.AvatarList[i] != dr2.AvatarList[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,8 @@ using SabreTools.Data.Models.COFF;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class PortableExecutable
|
||||
// TODO: Add tests
|
||||
public static class PortableExecutableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert a relative virtual address to a physical one
|
||||
24
SabreTools.Data.Extensions/README.MD
Normal file
24
SabreTools.Data.Extensions/README.MD
Normal file
@@ -0,0 +1,24 @@
|
||||
# SabreTools.Data.Extensions
|
||||
|
||||
This library contains extension methods related to models. These extensions are used in both reader and wrapper code.
|
||||
|
||||
## Provided Extension Classes
|
||||
|
||||
| Class Name | Applicable Namespace |
|
||||
| --- | --- |
|
||||
| `Atari7800CartExtensions` | `SabreTools.Data.Models.Atari7800` |
|
||||
| `AtariLynxCartExtensions` | `SabreTools.Data.Models.AtariLynx` |
|
||||
| `BSPExtensions` | `SabreTools.Data.Models.BSP` |
|
||||
| `CDROMExtensions` | `SabreTools.Data.Models.CDROM` |
|
||||
| `CFBExtensions` | `SabreTools.Data.Models.CFB` |
|
||||
| `InstallShieldCabinetExtensions` | `SabreTools.Data.Models.InstallShieldCabinet` |
|
||||
| `ISO9660Extensions` | `SabreTools.Data.Models.ISO9660` |
|
||||
| `MetadataExtensions` | `SabreTools.Data.Models.Metadata` |
|
||||
| `NESCartExtensions` | `SabreTools.Data.Models.NES` |
|
||||
| `NewExecutableExtensions` | `SabreTools.Data.Models.NewExecutable` |
|
||||
| `PortableExecutableExtensions` | `SabreTools.Data.Models.COFF` |
|
||||
| `TypeLengthValueExtensions` | `SabreTools.Data.Models.ASN1` and `SabreTools.ObjectIdentifier` |
|
||||
| `VBSPExtensions` | `SabreTools.Data.Models.BSP` |
|
||||
| `WiseScriptExtensions` | `SabreTools.Data.Models.WiseInstaller` |
|
||||
| `XboxExecutableExtensions` | `SabreTools.Data.Models.XboxExecutable` |
|
||||
| `XZExtensions` | `SabreTools.Data.Models.XZ` |
|
||||
43
SabreTools.Data.Extensions/SabreTools.Data.Extensions.csproj
Normal file
43
SabreTools.Data.Extensions/SabreTools.Data.Extensions.csproj
Normal file
@@ -0,0 +1,43 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0;net11.0;netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Version>3.3.0</Version>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Description>Extension methods related to models</Description>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2026</Copyright>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<RepositoryUrl>https://github.com/SabreTools/SabreTools.Serialization</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageTags>models extensions conversion</PackageTags>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="README.md" Pack="true" PackagePath="" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NetLegacySupport.Numerics" Version="1.0.1" Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`))" />
|
||||
<PackageReference Include="SabreTools.IO" Version="2.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SabreTools.Data.Models\SabreTools.Data.Models.csproj" />
|
||||
<ProjectReference Include="..\SabreTools.ObjectIdentifier\SabreTools.ObjectIdentifier.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -2,18 +2,18 @@ using System;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using SabreTools.Data.Models.ASN1;
|
||||
using SabreTools.Data.ObjectIdentifier;
|
||||
using SabreTools.ObjectIdentifier;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class TypeLengthValue
|
||||
public static class TypeLengthValueExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Format a TypeLengthValue as a string
|
||||
/// </summary>
|
||||
/// <param name="paddingLevel">Padding level of the item when formatting</param>
|
||||
/// <returns>String representing the TypeLengthValue, if possible</returns>
|
||||
public static string Format(this Models.ASN1.TypeLengthValue tlv, int paddingLevel = 0)
|
||||
public static string Format(this TypeLengthValue tlv, int paddingLevel = 0)
|
||||
{
|
||||
// Create the left-padding string
|
||||
string padding = new(' ', paddingLevel);
|
||||
@@ -38,7 +38,7 @@ namespace SabreTools.Data.Extensions
|
||||
if (tlv.Type.HasFlag(ASN1Type.V_ASN1_CONSTRUCTED))
|
||||
#endif
|
||||
{
|
||||
if (tlv.Value is not Models.ASN1.TypeLengthValue[] valueAsObjectArray)
|
||||
if (tlv.Value is not TypeLengthValue[] valueAsObjectArray)
|
||||
{
|
||||
formatBuilder.Append(", Value: [INVALID DATA TYPE]");
|
||||
return formatBuilder.ToString();
|
||||
417
SabreTools.Data.Extensions/UDFExtensions.cs
Normal file
417
SabreTools.Data.Extensions/UDFExtensions.cs
Normal file
@@ -0,0 +1,417 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using SabreTools.Data.Models.UDF;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class UDFExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Maximum number of Allocation Extent Descriptors followed for one File Entry
|
||||
/// </summary>
|
||||
private const int MaximumAllocationExtents = 1024;
|
||||
|
||||
/// <summary>
|
||||
/// CRC-ITU-T (CCITT, polynomial 0x1021) table for descriptor CRCs
|
||||
/// </summary>
|
||||
private static readonly ushort[] CrcTable = CreateCrcTable();
|
||||
|
||||
/// <summary>
|
||||
/// Get the dictionary key of a logical block address
|
||||
/// </summary>
|
||||
/// <param name="address">Logical block address</param>
|
||||
/// <returns>((long)PartitionReferenceNumber << 32) | LogicalBlockNumber</returns>
|
||||
public static long GetKey(this LogicalBlockAddress address)
|
||||
=> GetKey(address.PartitionReferenceNumber, address.LogicalBlockNumber);
|
||||
|
||||
/// <summary>
|
||||
/// Get the dictionary key of a logical block address
|
||||
/// </summary>
|
||||
/// <param name="partitionReferenceNumber">Index of the partition map</param>
|
||||
/// <param name="logicalBlockNumber">Logical block number within the partition</param>
|
||||
/// <returns>((long)partitionReferenceNumber << 32) | logicalBlockNumber</returns>
|
||||
public static long GetKey(ushort partitionReferenceNumber, uint logicalBlockNumber)
|
||||
=> ((long)partitionReferenceNumber << 32) | logicalBlockNumber;
|
||||
|
||||
/// <summary>
|
||||
/// Get the extent type from an allocation descriptor's extent length
|
||||
/// </summary>
|
||||
public static ExtentType GetExtentType(uint extentLength)
|
||||
=> (ExtentType)(extentLength >> Constants.ExtentTypeShift);
|
||||
|
||||
/// <summary>
|
||||
/// Get the extent length in bytes from an allocation descriptor's extent length
|
||||
/// </summary>
|
||||
public static uint GetExtentLength(uint extentLength)
|
||||
=> extentLength & Constants.ExtentLengthMask;
|
||||
|
||||
/// <summary>
|
||||
/// Get the type of the allocation descriptors of an ICB
|
||||
/// </summary>
|
||||
public static AllocationDescriptorType GetAllocationDescriptorType(this ICBTag tag)
|
||||
=> (AllocationDescriptorType)(tag.Flags & Constants.AllocationDescriptorTypeMask);
|
||||
|
||||
/// <summary>
|
||||
/// Get the prevailing Logical Volume Descriptor (highest Volume Descriptor Sequence Number)
|
||||
/// </summary>
|
||||
/// <param name="volume">Volume to search</param>
|
||||
/// <returns>Logical Volume Descriptor on success, null if none was recorded</returns>
|
||||
public static LogicalVolumeDescriptor? GetLogicalVolumeDescriptor(this Volume volume)
|
||||
{
|
||||
LogicalVolumeDescriptor? prevailing = null;
|
||||
foreach (var descriptor in volume.VolumeDescriptorSequence)
|
||||
{
|
||||
if (descriptor is LogicalVolumeDescriptor lvd
|
||||
&& (prevailing is null || lvd.VolumeDescriptorSequenceNumber > prevailing.VolumeDescriptorSequenceNumber))
|
||||
{
|
||||
prevailing = lvd;
|
||||
}
|
||||
}
|
||||
|
||||
return prevailing;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the prevailing Partition Descriptor of a partition (highest Volume Descriptor Sequence Number)
|
||||
/// </summary>
|
||||
/// <param name="volume">Volume to search</param>
|
||||
/// <param name="partitionNumber">Partition number</param>
|
||||
/// <returns>Partition Descriptor on success, null if none was recorded</returns>
|
||||
public static PartitionDescriptor? GetPartitionDescriptor(this Volume volume, ushort partitionNumber)
|
||||
{
|
||||
PartitionDescriptor? prevailing = null;
|
||||
foreach (var descriptor in volume.VolumeDescriptorSequence)
|
||||
{
|
||||
if (descriptor is PartitionDescriptor pd
|
||||
&& pd.PartitionNumber == partitionNumber
|
||||
&& (prevailing is null || pd.VolumeDescriptorSequenceNumber > prevailing.VolumeDescriptorSequenceNumber))
|
||||
{
|
||||
prevailing = pd;
|
||||
}
|
||||
}
|
||||
|
||||
return prevailing;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the partition reference number of the Type 1 partition map for a partition number
|
||||
/// </summary>
|
||||
/// <param name="lvd">Logical Volume Descriptor holding the partition maps</param>
|
||||
/// <param name="partitionNumber">Partition number</param>
|
||||
/// <returns>Partition reference number on success, -1 if there is no such map</returns>
|
||||
public static int GetType1PartitionReference(this LogicalVolumeDescriptor lvd, ushort partitionNumber)
|
||||
{
|
||||
for (int i = 0; i < lvd.PartitionMaps.Length; i++)
|
||||
{
|
||||
if (lvd.PartitionMaps[i] is Type1PartitionMap map && map.PartitionNumber == partitionNumber)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Map a logical block of a partition to a logical sector of the volume
|
||||
/// Supports Type 1 partitions and Metadata Partitions (the Metadata File's File Entry must be in FileEntries)
|
||||
/// </summary>
|
||||
/// <param name="volume">Volume containing the Volume Descriptor Sequence</param>
|
||||
/// <param name="key">Logical block address key, see GetKey</param>
|
||||
/// <param name="sector">Logical sector on success</param>
|
||||
/// <returns>True if the block could be mapped, false otherwise</returns>
|
||||
public static bool TryGetSector(this Volume volume, long key, out long sector)
|
||||
{
|
||||
sector = -1;
|
||||
|
||||
var lvd = volume.GetLogicalVolumeDescriptor();
|
||||
if (lvd is null)
|
||||
return false;
|
||||
|
||||
int partitionReference = (int)(key >> 32);
|
||||
uint block = (uint)key;
|
||||
if (partitionReference < 0 || partitionReference >= lvd.PartitionMaps.Length)
|
||||
return false;
|
||||
|
||||
var map = lvd.PartitionMaps[partitionReference];
|
||||
if (map is Type1PartitionMap type1)
|
||||
{
|
||||
var pd = volume.GetPartitionDescriptor(type1.PartitionNumber);
|
||||
if (pd is null || block >= pd.PartitionLength)
|
||||
return false;
|
||||
|
||||
sector = (long)pd.PartitionStartingLocation + block;
|
||||
return true;
|
||||
}
|
||||
else if (map is MetadataPartitionMap metadata)
|
||||
{
|
||||
// The Metadata File (or its mirror) is recorded in the Type 1 partition of the same partition number
|
||||
int physical = lvd.GetType1PartitionReference(metadata.PartitionNumber);
|
||||
if (physical < 0)
|
||||
return false;
|
||||
|
||||
if (!TryGetMetadataFileBlock(volume, lvd, GetKey((ushort)physical, metadata.MetadataFileLocation), block, out long physicalKey)
|
||||
&& !TryGetMetadataFileBlock(volume, lvd, GetKey((ushort)physical, metadata.MetadataMirrorFileLocation), block, out physicalKey))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return volume.TryGetSector(physicalKey, out sector);
|
||||
}
|
||||
|
||||
// Virtual and Sparable partitions are not supported
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Map a logical block of a Metadata Partition through the Metadata File's allocation descriptors
|
||||
/// </summary>
|
||||
private static bool TryGetMetadataFileBlock(Volume volume, LogicalVolumeDescriptor lvd, long fileKey, uint block, out long physicalKey)
|
||||
{
|
||||
physicalKey = -1;
|
||||
if (!volume.FileEntries.TryGetValue(fileKey, out var fileEntry))
|
||||
return false;
|
||||
|
||||
var descriptors = volume.GetAllocationDescriptors(fileKey, fileEntry);
|
||||
if (descriptors is null)
|
||||
return false;
|
||||
|
||||
uint blockSize = lvd.LogicalBlockSize;
|
||||
long first = 0;
|
||||
foreach (var ad in descriptors)
|
||||
{
|
||||
long blocks = (GetExtentLength(ad.ExtentLength) + blockSize - 1) / blockSize;
|
||||
if (block < first + blocks)
|
||||
{
|
||||
if (GetExtentType(ad.ExtentLength) != ExtentType.RECORDED_AND_ALLOCATED)
|
||||
return false;
|
||||
|
||||
physicalKey = GetKey(ad.ExtentLocation.PartitionReferenceNumber, (uint)(ad.ExtentLocation.LogicalBlockNumber + (block - first)));
|
||||
return true;
|
||||
}
|
||||
|
||||
first += blocks;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the allocation descriptors of a File Entry, including those continued in Allocation Extent Descriptors,
|
||||
/// as long allocation descriptors (short allocation descriptors get the partition of the File Entry)
|
||||
/// </summary>
|
||||
/// <param name="volume">Volume containing the Allocation Extent Descriptors</param>
|
||||
/// <param name="key">Logical block address key of the File Entry</param>
|
||||
/// <param name="fileEntry">File Entry</param>
|
||||
/// <returns>Allocation descriptors (empty for embedded data), null if they are not supported or incomplete</returns>
|
||||
public static List<LongAllocationDescriptor>? GetAllocationDescriptors(this Volume volume, long key, FileEntry fileEntry)
|
||||
=> volume.GetAllocationDescriptors(key, fileEntry, out _);
|
||||
|
||||
/// <summary>
|
||||
/// Get the allocation descriptors of a File Entry, including those continued in Allocation Extent Descriptors,
|
||||
/// as long allocation descriptors (short allocation descriptors get the partition of the File Entry)
|
||||
/// </summary>
|
||||
/// <param name="volume">Volume containing the Allocation Extent Descriptors</param>
|
||||
/// <param name="key">Logical block address key of the File Entry</param>
|
||||
/// <param name="fileEntry">File Entry</param>
|
||||
/// <param name="missingKey">Key of the first Allocation Extent Descriptor that is not in the volume, -1 if none</param>
|
||||
/// <returns>Allocation descriptors (empty for embedded data), null if they are not supported or incomplete</returns>
|
||||
public static List<LongAllocationDescriptor>? GetAllocationDescriptors(this Volume volume, long key, FileEntry fileEntry, out long missingKey)
|
||||
{
|
||||
missingKey = -1;
|
||||
|
||||
var type = fileEntry.ICBTag.GetAllocationDescriptorType();
|
||||
if (type == AllocationDescriptorType.EMBEDDED)
|
||||
return [];
|
||||
|
||||
// Extended allocation descriptors are not allowed by UDF
|
||||
if (type != AllocationDescriptorType.SHORT && type != AllocationDescriptorType.LONG)
|
||||
return null;
|
||||
|
||||
List<LongAllocationDescriptor> result = [];
|
||||
ushort partition = (ushort)(key >> 32);
|
||||
byte[] descriptors = fileEntry.AllocationDescriptors;
|
||||
int length = descriptors.Length;
|
||||
|
||||
for (int extents = 0; extents <= MaximumAllocationExtents; extents++)
|
||||
{
|
||||
long nextKey = -1;
|
||||
int size = type == AllocationDescriptorType.SHORT ? 8 : 16;
|
||||
for (int offset = 0; offset + size <= length; offset += size)
|
||||
{
|
||||
var ad = new LongAllocationDescriptor();
|
||||
ad.ExtentLength = descriptors.ToUInt32LittleEndian(offset);
|
||||
if (type == AllocationDescriptorType.SHORT)
|
||||
{
|
||||
ad.ExtentLocation.LogicalBlockNumber = descriptors.ToUInt32LittleEndian(offset + 4);
|
||||
ad.ExtentLocation.PartitionReferenceNumber = partition;
|
||||
}
|
||||
else
|
||||
{
|
||||
ad.ExtentLocation.LogicalBlockNumber = descriptors.ToUInt32LittleEndian(offset + 4);
|
||||
ad.ExtentLocation.PartitionReferenceNumber = descriptors.ToUInt16LittleEndian(offset + 8);
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
ad.ImplementationUse[i] = descriptors[offset + 10 + i];
|
||||
}
|
||||
}
|
||||
|
||||
// A zero length terminates the allocation descriptors
|
||||
if (GetExtentLength(ad.ExtentLength) == 0)
|
||||
return result;
|
||||
|
||||
if (GetExtentType(ad.ExtentLength) == ExtentType.NEXT_EXTENT)
|
||||
{
|
||||
nextKey = ad.ExtentLocation.GetKey();
|
||||
break;
|
||||
}
|
||||
|
||||
result.Add(ad);
|
||||
}
|
||||
|
||||
if (nextKey < 0)
|
||||
return result;
|
||||
|
||||
if (!volume.AllocationExtentDescriptors.TryGetValue(nextKey, out var aed))
|
||||
{
|
||||
missingKey = nextKey;
|
||||
return null;
|
||||
}
|
||||
|
||||
partition = (ushort)(nextKey >> 32);
|
||||
descriptors = aed.AllocationDescriptors;
|
||||
length = descriptors.Length;
|
||||
}
|
||||
|
||||
// Too many continuation extents, assume a loop
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check a descriptor tag: checksum, version, CRC (when it fits into the data) and location
|
||||
/// </summary>
|
||||
/// <remarks>ECMA-167 3/7.2; receiving systems allow descriptor version 2 or 3 (3/7.2.2)</remarks>
|
||||
/// <param name="data">Data holding the descriptor</param>
|
||||
/// <param name="offset">Offset of the descriptor</param>
|
||||
/// <param name="available">Number of bytes of the descriptor available from the offset</param>
|
||||
/// <param name="location">Expected tag location (logical sector or logical block number)</param>
|
||||
/// <returns>True if the tag is valid, false otherwise</returns>
|
||||
public static bool IsValidDescriptor(this byte[] data, int offset, int available, uint location)
|
||||
{
|
||||
byte checksum = 0;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
if (i != 4)
|
||||
checksum += data[offset + i];
|
||||
}
|
||||
|
||||
if (checksum != data[offset + 4])
|
||||
return false;
|
||||
|
||||
ushort version = data.ToUInt16LittleEndian(offset + 2);
|
||||
if (version != 2 && version != 3)
|
||||
return false;
|
||||
|
||||
ushort crcLength = data.ToUInt16LittleEndian(offset + 10);
|
||||
if (16 + crcLength <= available)
|
||||
{
|
||||
if (ComputeCrc(data, offset + 16, crcLength) != data.ToUInt16LittleEndian(offset + 8))
|
||||
return false;
|
||||
}
|
||||
|
||||
return data.ToUInt32LittleEndian(offset + 12) == location;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decode OSTA Compressed Unicode d-characters (first byte = compression ID)
|
||||
/// </summary>
|
||||
/// <param name="data">Encoded characters</param>
|
||||
/// <returns>Decoded string, empty for no characters, null for an unknown compression ID</returns>
|
||||
public static string? DecodeDCharacters(this byte[]? data)
|
||||
=> DecodeDCharacters(data, data?.Length ?? 0);
|
||||
|
||||
/// <summary>
|
||||
/// Decode an OSTA Compressed Unicode dstring (last byte = number of used bytes)
|
||||
/// </summary>
|
||||
/// <param name="data">Encoded dstring field</param>
|
||||
/// <returns>Decoded string, empty for no characters, null for an unknown compression ID</returns>
|
||||
public static string? DecodeDString(this byte[]? data)
|
||||
{
|
||||
if (data is null || data.Length == 0)
|
||||
return string.Empty;
|
||||
|
||||
int length = data[data.Length - 1];
|
||||
if (length > data.Length - 1)
|
||||
length = data.Length - 1;
|
||||
|
||||
return DecodeDCharacters(data, length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decode the first length bytes of OSTA Compressed Unicode d-characters
|
||||
/// </summary>
|
||||
private static string? DecodeDCharacters(byte[]? data, int length)
|
||||
{
|
||||
if (data is null || length == 0)
|
||||
return string.Empty;
|
||||
|
||||
// 254 and 255 are the same encodings, used by UDF for deleted entries
|
||||
byte compressionId = data[0];
|
||||
var builder = new StringBuilder(length);
|
||||
if (compressionId == Constants.CompressionId8 || compressionId == 254)
|
||||
{
|
||||
for (int i = 1; i < length; i++)
|
||||
{
|
||||
builder.Append((char)data[i]);
|
||||
}
|
||||
}
|
||||
else if (compressionId == Constants.CompressionId16 || compressionId == 255)
|
||||
{
|
||||
for (int i = 1; i + 1 < length; i += 2)
|
||||
{
|
||||
builder.Append((char)data.ToUInt16BigEndian(i));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
// TODO: Replace with CRC-16/XMODEM from Hashing once its CRC code is faster
|
||||
/// <summary>
|
||||
/// Compute the CRC-ITU-T of a range of bytes
|
||||
/// </summary>
|
||||
private static ushort ComputeCrc(byte[] data, int offset, int length)
|
||||
{
|
||||
ushort crc = 0;
|
||||
for (int i = offset; i < offset + length; i++)
|
||||
{
|
||||
crc = (ushort)(CrcTable[((crc >> 8) ^ data[i]) & 0xFF] ^ (crc << 8));
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the CRC-ITU-T table
|
||||
/// </summary>
|
||||
private static ushort[] CreateCrcTable()
|
||||
{
|
||||
var table = new ushort[256];
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
ushort crc = (ushort)(i << 8);
|
||||
for (int bit = 0; bit < 8; bit++)
|
||||
{
|
||||
crc = (crc & 0x8000) != 0 ? (ushort)((crc << 1) ^ 0x1021) : (ushort)(crc << 1);
|
||||
}
|
||||
|
||||
table[i] = crc;
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
}
|
||||
}
|
||||
82
SabreTools.Data.Extensions/VBSPExtensions.cs
Normal file
82
SabreTools.Data.Extensions/VBSPExtensions.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using SabreTools.Data.Models.BSP;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class VBSPExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert a <see cref="VbspLumpType"/> value to string
|
||||
/// </summary>
|
||||
public static string FromVbspLumpType(this VbspLumpType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
VbspLumpType.LUMP_ENTITIES => " - LUMP_ENTITIES",
|
||||
VbspLumpType.LUMP_PLANES => " - LUMP_PLANES",
|
||||
VbspLumpType.LUMP_TEXTURES => " - LUMP_TEXDATA",
|
||||
VbspLumpType.LUMP_VERTICES => " - LUMP_VERTEXES",
|
||||
VbspLumpType.LUMP_VISIBILITY => " - LUMP_VISIBILITY",
|
||||
VbspLumpType.LUMP_NODES => " - LUMP_NODES",
|
||||
VbspLumpType.LUMP_TEXINFO => " - LUMP_TEXINFO",
|
||||
VbspLumpType.LUMP_FACES => " - LUMP_FACES",
|
||||
VbspLumpType.LUMP_LIGHTING => " - LUMP_LIGHTING",
|
||||
VbspLumpType.LUMP_CLIPNODES => " - LUMP_OCCLUSION",
|
||||
VbspLumpType.LUMP_LEAVES => " - LUMP_LEAVES",
|
||||
VbspLumpType.LUMP_MARKSURFACES => " - LUMP_FACEIDS",
|
||||
VbspLumpType.LUMP_EDGES => " - LUMP_EDGES",
|
||||
VbspLumpType.LUMP_SURFEDGES => " - LUMP_SURFEDGES",
|
||||
VbspLumpType.LUMP_MODELS => " - LUMP_MODELS",
|
||||
VbspLumpType.LUMP_WORLDLIGHTS => " - LUMP_WORLDLIGHTS",
|
||||
VbspLumpType.LUMP_LEAFFACES => " - LUMP_LEAFFACES",
|
||||
VbspLumpType.LUMP_LEAFBRUSHES => " - LUMP_LEAFBRUSHES",
|
||||
VbspLumpType.LUMP_BRUSHES => " - LUMP_BRUSHES",
|
||||
VbspLumpType.LUMP_BRUSHSIDES => " - LUMP_BRUSHSIDES",
|
||||
VbspLumpType.LUMP_AREAS => " - LUMP_AREAS",
|
||||
VbspLumpType.LUMP_AREAPORTALS => " - LUMP_AREAPORTALS",
|
||||
VbspLumpType.LUMP_PORTALS => " - LUMP_PORTALS / LUMP_UNUSED0 / LUMP_PROPCOLLISION",
|
||||
VbspLumpType.LUMP_CLUSTERS => " - LUMP_CLUSTERS / LUMP_UNUSED1 / LUMP_PROPHULLS",
|
||||
VbspLumpType.LUMP_PORTALVERTS => " - LUMP_PORTALVERTS / LUMP_UNUSED2 / LUMP_FAKEENTITIES / LUMP_PROPHULLVERTS",
|
||||
VbspLumpType.LUMP_CLUSTERPORTALS => " - LUMP_CLUSTERPORTALS / LUMP_UNUSED3 / LUMP_PROPTRIS",
|
||||
VbspLumpType.LUMP_DISPINFO => " - LUMP_DISPINFO",
|
||||
VbspLumpType.LUMP_ORIGINALFACES => " - LUMP_ORIGINALFACES",
|
||||
VbspLumpType.LUMP_PHYSDISP => " - LUMP_PHYSDISP",
|
||||
VbspLumpType.LUMP_PHYSCOLLIDE => " - LUMP_PHYSCOLLIDE",
|
||||
VbspLumpType.LUMP_VERTNORMALS => " - LUMP_VERTNORMALS",
|
||||
VbspLumpType.LUMP_VERTNORMALINDICES => " - LUMP_VERTNORMALINDICES",
|
||||
VbspLumpType.LUMP_DISP_LIGHTMAP_ALPHAS => " - LUMP_DISP_LIGHTMAP_ALPHAS",
|
||||
VbspLumpType.LUMP_DISP_VERTS => " - LUMP_DISP_VERTS",
|
||||
VbspLumpType.LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS => " - LUMP_DISP_LIGHTMAP_SAMPLE_POSITIONS",
|
||||
VbspLumpType.LUMP_GAME_LUMP => " - LUMP_GAME_LUMP",
|
||||
VbspLumpType.LUMP_LEAFWATERDATA => " - LUMP_LEAFWATERDATA",
|
||||
VbspLumpType.LUMP_PRIMITIVES => " - LUMP_PRIMITIVES",
|
||||
VbspLumpType.LUMP_PRIMVERTS => " - LUMP_PRIMVERTS",
|
||||
VbspLumpType.LUMP_PRIMINDICES => " - LUMP_PRIMINDICES",
|
||||
VbspLumpType.LUMP_PAKFILE => " - LUMP_PAKFILE",
|
||||
VbspLumpType.LUMP_CLIPPORTALVERTS => " - LUMP_CLIPPORTALVERTS",
|
||||
VbspLumpType.LUMP_CUBEMAPS => " - LUMP_CUBEMAPS",
|
||||
VbspLumpType.LUMP_TEXDATA_STRING_DATA => " - LUMP_TEXDATA_STRING_DATA",
|
||||
VbspLumpType.LUMP_TEXDATA_STRING_TABLE => " - LUMP_TEXDATA_STRING_TABLE",
|
||||
VbspLumpType.LUMP_OVERLAYS => " - LUMP_OVERLAYS",
|
||||
VbspLumpType.LUMP_LEAFMINDISTTOWATER => " - LUMP_LEAFMINDISTTOWATER",
|
||||
VbspLumpType.LUMP_FACE_MACRO_TEXTURE_INFO => " - LUMP_FACE_MACRO_TEXTURE_INFO",
|
||||
VbspLumpType.LUMP_DISP_TRIS => " - LUMP_DISP_TRIS",
|
||||
VbspLumpType.LUMP_PHYSCOLLIDESURFACE => " - LUMP_PHYSCOLLIDESURFACE / LUMP_PROP_BLOB",
|
||||
VbspLumpType.LUMP_WATEROVERLAYS => " - LUMP_WATEROVERLAYS",
|
||||
VbspLumpType.LUMP_LIGHTMAPPAGES => " - LUMP_LIGHTMAPPAGES / LUMP_LEAF_AMBIENT_INDEX_HDR",
|
||||
VbspLumpType.LUMP_LIGHTMAPPAGEINFOS => " - LUMP_LIGHTMAPPAGEINFOS / LUMP_LEAF_AMBIENT_INDEX",
|
||||
VbspLumpType.LUMP_LIGHTING_HDR => " - LUMP_LIGHTING_HDR",
|
||||
VbspLumpType.LUMP_WORLDLIGHTS_HDR => " - LUMP_WORLDLIGHTS_HDR",
|
||||
VbspLumpType.LUMP_LEAF_AMBIENT_LIGHTING_HDR => " - LUMP_LEAF_AMBIENT_LIGHTING_HDR",
|
||||
VbspLumpType.LUMP_LEAF_AMBIENT_LIGHTING => " - LUMP_LEAF_AMBIENT_LIGHTING",
|
||||
VbspLumpType.LUMP_XZIPPAKFILE => " - LUMP_XZIPPAKFILE",
|
||||
VbspLumpType.LUMP_FACES_HDR => " - LUMP_FACES_HDR",
|
||||
VbspLumpType.LUMP_MAP_FLAGS => " - LUMP_MAP_FLAGS",
|
||||
VbspLumpType.LUMP_OVERLAY_FADES => " - LUMP_OVERLAY_FADES",
|
||||
VbspLumpType.LUMP_OVERLAY_SYSTEM_LEVELS => " - LUMP_OVERLAY_SYSTEM_LEVELS",
|
||||
VbspLumpType.LUMP_PHYSLEVEL => " - LUMP_PHYSLEVEL",
|
||||
VbspLumpType.LUMP_DISP_MULTIBLEND => " - LUMP_DISP_MULTIBLEND",
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
#pragma warning disable SYSLIB1045 // Convert to 'GeneratedRegexAttribute'
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class WiseScript
|
||||
public static class WiseScriptExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert a Wise function ID to the formal action name
|
||||
@@ -58,7 +57,9 @@ namespace SabreTools.Data.Extensions
|
||||
|
||||
// External DLL
|
||||
null => null,
|
||||
#pragma warning disable SYSLIB1045 // Convert to 'GeneratedRegexAttribute'
|
||||
_ => Regex.IsMatch(functionId, @"^f[0-9]{1,2}$") ? $"UNDEFINED {functionId}" : $"External: {functionId}",
|
||||
#pragma warning restore SYSLIB1045
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class XZ
|
||||
public static class XZExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Decode a value from a variable-length integer
|
||||
@@ -1,14 +1,14 @@
|
||||
using System;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class XboxExecutable
|
||||
public static class XboxExecutableExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert a UInt32 to a formatted XBE title ID
|
||||
/// </summary>
|
||||
public static string? ToFormattedXBETitleID(this uint value)
|
||||
public static string ToFormattedXBETitleID(this uint value)
|
||||
{
|
||||
// Convert to a byte array
|
||||
byte[] data = BitConverter.GetBytes(value);
|
||||
36
SabreTools.Data.Extensions/ZArchiveExtensions.cs
Normal file
36
SabreTools.Data.Extensions/ZArchiveExtensions.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using SabreTools.Data.Models.ZArchive;
|
||||
|
||||
namespace SabreTools.Data.Extensions
|
||||
{
|
||||
public static class ZArchiveExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves the name of the specified node from the NameTable
|
||||
/// </summary>
|
||||
/// <param name="node">Node in the file tree</param>
|
||||
/// <param name="nameTable">ZArchive NameTable</param>
|
||||
/// <returns>UTF-8 string representing node's name</returns>
|
||||
public static string? GetName(this FileDirectoryEntry node, NameTable nameTable)
|
||||
{
|
||||
// Check for a valid offset into the NameTable
|
||||
uint nameOffset = node.NameOffsetAndTypeFlag & Constants.RootNode;
|
||||
if (nameOffset == Constants.RootNode)
|
||||
return null;
|
||||
|
||||
// Get the index into the name table
|
||||
var index = Array.IndexOf(nameTable.NameTableOffsets, nameOffset);
|
||||
if (index < 0)
|
||||
return null;
|
||||
|
||||
// Get the name entry for the requested index
|
||||
var nameEntry = nameTable.NameEntries[index];
|
||||
if (nameEntry is null)
|
||||
return null;
|
||||
|
||||
// Decode name to UTF-8
|
||||
return Encoding.UTF8.GetString(nameEntry.NodeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using SabreTools.Numerics;
|
||||
|
||||
namespace SabreTools.Data.Models.AACS
|
||||
{
|
||||
/// <summary>
|
||||
@@ -23,6 +25,6 @@ namespace SabreTools.Data.Models.AACS
|
||||
/// fields themselves. Record lengths are always multiples of 4 bytes.
|
||||
/// </summary>
|
||||
// <remarks>UInt24 not UInt32</remarks>
|
||||
public uint RecordLength { get; set; }
|
||||
public UInt24 RecordLength { get; set; } = new();
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ namespace SabreTools.Data.Models.ArchiveDotOrg
|
||||
public string? SHA1 { get; set; }
|
||||
|
||||
[XmlElement("filecount")]
|
||||
public string? FileCount { get; set; }
|
||||
public long? FileCount { get; set; }
|
||||
|
||||
/// <remarks>Is this a set of defined values?</remarks>
|
||||
[XmlElement("format")]
|
||||
@@ -1,5 +1,3 @@
|
||||
using SabreTools.Data.Attributes;
|
||||
|
||||
namespace SabreTools.Data.Models.AttractMode
|
||||
{
|
||||
/// <summary>
|
||||
@@ -1,5 +1,3 @@
|
||||
using SabreTools.Data.Attributes;
|
||||
|
||||
namespace SabreTools.Data.Models.AttractMode
|
||||
{
|
||||
public class Row
|
||||
@@ -48,6 +46,6 @@ namespace SabreTools.Data.Models.AttractMode
|
||||
|
||||
public string? PlayedTime { get; set; }
|
||||
|
||||
public string? FileIsAvailable { get; set; }
|
||||
public bool? FileIsAvailable { get; set; }
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user