├── .travis.yml
├── WiiuVcExtractor
├── App.config
├── GlobalSuppressions.cs
├── packages.config
├── Libraries
│ ├── Endianness.cs
│ ├── Sdd1
│ │ ├── Sdd1Pointer.cs
│ │ ├── Interleaver.cs
│ │ ├── GolombCodeEncoder.cs
│ │ ├── BitplanesExtractor.cs
│ │ ├── Compressor.cs
│ │ ├── ContextModel.cs
│ │ └── ProbabilityEstimationModule.cs
│ ├── RomNameDictionary.cs
│ ├── RomSizeDictionary.cs
│ ├── SnesSdd1Extractor.cs
│ ├── EndianUtility.cs
│ └── SnesPcmExtractor.cs
├── RomExtractors
│ ├── IRomExtractor.cs
│ ├── PceVcExtractor.cs
│ ├── NesVcExtractor.cs
│ └── DsVcExtractor.cs
├── Properties
│ ├── PublishProfiles
│ │ ├── osx_x86_64.pubxml
│ │ ├── linux_x86_64.pubxml
│ │ └── windows_x86_64.pubxml
│ └── AssemblyInfo.cs
├── FileTypes
│ ├── PsbFileInfoEntry.cs
│ ├── SrlFile.cs
│ ├── PkgContentFile.cs
│ ├── RpxSectionHeaderSort.cs
│ ├── MdfHeader.cs
│ ├── PkgFile.cs
│ ├── PsbChunkTable.cs
│ ├── PsbNameTable.cs
│ ├── RpxSectionHeader.cs
│ ├── PkgHeader.cs
│ ├── PsbHeader.cs
│ ├── MdfPsbFile.cs
│ ├── RpxHeader.cs
│ └── PsbEntryTable.cs
├── WiiuVcExtractor.csproj
├── pceromnames.csv
├── snesromnames.csv
├── Program.cs
└── nesromnames.csv
├── WiiuVcExtractorTests
├── GlobalSuppressions.cs
├── WiiuVcExtractorTests.csproj
└── Libraries
│ ├── RomNameDictionaryTests.cs
│ └── RomSizeDictionaryTests.cs
├── License.injectgba.txt
├── WiiuVcExtractor.sln
├── .gitattributes
├── License.zlib.txt
├── .gitignore
└── README.md
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: csharp
2 | solution: WiiuVcExtractor.sln
3 | mono: none
4 | dotnet: 5.0.200
5 |
6 | script:
7 | - dotnet --info
8 | - dotnet restore
9 | - dotnet build
10 | - dotnet test
11 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/GlobalSuppressions.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 |
3 | [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "License information is available within LICENSE.txt.")]
4 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/WiiuVcExtractorTests/GlobalSuppressions.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 |
3 | [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "License information is available within LICENSE.txt.")]
4 | [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Disabling for testing code.")]
5 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/Libraries/Endianness.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.Libraries
2 | {
3 | ///
4 | /// Types of endianness.
5 | ///
6 | public enum Endianness
7 | {
8 | ///
9 | /// Represents a little-endian system.
10 | ///
11 | LittleEndian,
12 |
13 | ///
14 | /// Represents a big-endian system.
15 | ///
16 | BigEndian,
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/RomExtractors/IRomExtractor.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.RomExtractors
2 | {
3 | ///
4 | /// Rom extractor interface.
5 | ///
6 | internal interface IRomExtractor
7 | {
8 | ///
9 | /// Whether the rom is valid.
10 | ///
11 | /// true if valid, false otherwise.
12 | bool IsValidRom();
13 |
14 | ///
15 | /// Extracts a rom from a given file.
16 | ///
17 | /// path to the extracted rom.
18 | string ExtractRom();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/Properties/PublishProfiles/osx_x86_64.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | wiiuvcextractor
8 | Exe
9 | net5.0
10 | true
11 | true
12 | Release
13 | osx-x64
14 | true
15 | true
16 | bin\Release\net5.0\publish\
17 | FileSystem
18 | true
19 | false
20 |
21 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/Properties/PublishProfiles/linux_x86_64.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | wiiuvcextractor
8 | Exe
9 | net5.0
10 | true
11 | true
12 | Release
13 | linux-x64
14 | true
15 | true
16 | bin\Release\net5.0\publish\
17 | FileSystem
18 | true
19 | false
20 |
21 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/Properties/PublishProfiles/windows_x86_64.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | wiiuvcextractor
8 | Exe
9 | net5.0
10 | true
11 | true
12 | Release
13 | win-x64
14 | true
15 | true
16 | bin\Release\net5.0\publish\
17 | FileSystem
18 | true
19 | false
20 |
21 |
--------------------------------------------------------------------------------
/WiiuVcExtractorTests/WiiuVcExtractorTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net5.0
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | all
19 | runtime; build; native; contentfiles; analyzers; buildtransitive
20 |
21 |
22 | all
23 | runtime; build; native; contentfiles; analyzers; buildtransitive
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/License.injectgba.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015, Andrew Dalgleish
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 |
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/WiiuVcExtractor/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("WiiuVcExtractor")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("WiiuVcExtractor")]
12 | [assembly: AssemblyCopyright("Copyright © 2021")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("94f25bb6-2459-4f3c-a61e-f04424ebdf69")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("2.0.1.0")]
35 | [assembly: AssemblyFileVersion("2.0.1.0")]
36 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/Libraries/Sdd1/Sdd1Pointer.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.Libraries.Sdd1
2 | {
3 | ///
4 | /// S-DD1 pointer position in appended VC rom data.
5 | /// Appears as SDD1 followed by a single unsigned 4 byte integer indicating
6 | /// where the associated data can be found.
7 | ///
8 | public class Sdd1Pointer
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | /// S-DD1 pointer location.
14 | /// S-DD1 decompressed data location.
15 | public Sdd1Pointer(long ptrLocation, long datLocation)
16 | {
17 | this.PointerLocation = ptrLocation;
18 | this.DataLocation = datLocation;
19 | this.DataLength = 0;
20 | }
21 |
22 | ///
23 | /// Gets or sets location of the S-DD1 pointer in the SNES rom data.
24 | ///
25 | public long PointerLocation { get; set; }
26 |
27 | ///
28 | /// Gets or sets location of the decompressed S-DD1 data in the appended data after the SNES rom data.
29 | ///
30 | public long DataLocation { get; set; }
31 |
32 | ///
33 | /// Gets or sets length of the decompressed S-DD1 data in the appended data after the SNES rom data.
34 | ///
35 | public long DataLength { get; set; }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/FileTypes/PsbFileInfoEntry.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.FileTypes
2 | {
3 | ///
4 | /// PSB file info entry.
5 | ///
6 | public class PsbFileInfoEntry
7 | {
8 | private readonly uint nameIndex;
9 | private readonly uint length;
10 | private readonly uint offset;
11 |
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | /// file name index.
16 | /// file length.
17 | /// file offset in bytes.
18 | public PsbFileInfoEntry(uint nameIndex, uint length, uint offset)
19 | {
20 | this.nameIndex = nameIndex;
21 | this.length = length;
22 | this.offset = offset;
23 | }
24 |
25 | ///
26 | /// Gets the name index for the file.
27 | ///
28 | public uint NameIndex
29 | {
30 | get { return this.nameIndex; }
31 | }
32 |
33 | ///
34 | /// Gets the length of the file.
35 | ///
36 | public uint Length
37 | {
38 | get { return this.length; }
39 | }
40 |
41 | ///
42 | /// Gets the offset of the file in bytes.
43 | ///
44 | public uint Offset
45 | {
46 | get { return this.offset; }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/WiiuVcExtractor.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net5.0
4 | Exe
5 | false
6 | wiiuvcextractor
7 |
8 |
9 | bin\Release\WiiuVcExtractor.xml
10 |
11 |
12 |
13 | Always
14 |
15 |
16 | Always
17 |
18 |
19 | Always
20 |
21 |
22 | Always
23 |
24 |
25 | Always
26 |
27 |
28 | Always
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/WiiuVcExtractorTests/Libraries/RomNameDictionaryTests.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractorTests.Libraries
2 | {
3 | using System;
4 | using System.IO;
5 | using WiiuVcExtractor.Libraries;
6 | using Xunit;
7 |
8 | public class RomNameDictionaryTests
9 | {
10 | [Fact]
11 | public void RomNameDictionary_WhenCSVFileIsMissing_ThrowsException()
12 | {
13 | Assert.Throws(() => new RomNameDictionary(string.Empty));
14 | }
15 |
16 | [Fact]
17 | public void RomNameDictionary_WhenCSVFileExists_ContructsRomNameDictionary()
18 | {
19 | var expected = typeof(RomNameDictionary);
20 | var result = new RomNameDictionary(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "snesromnames.csv"));
21 |
22 | Assert.IsType(expected, result);
23 | }
24 |
25 | [Fact]
26 | public void GetRomName_WhenIDExists_ReturnsRomName()
27 | {
28 | var dictionary = new RomNameDictionary(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "snesromnames.csv"));
29 |
30 | var result = dictionary.GetRomName("WUP-JDBE");
31 |
32 | Assert.Equal("Rival Turf", result);
33 | }
34 |
35 | [Fact]
36 | public void GetRomName_WhenIDDoesNotExist_ReturnsEmptyString()
37 | {
38 | var dictionary = new RomNameDictionary(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "snesromnames.csv"));
39 |
40 | var result = dictionary.GetRomName("WUP-JUNKANDSUCH");
41 |
42 | Assert.Equal(string.Empty, result);
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/FileTypes/SrlFile.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.FileTypes
2 | {
3 | using System;
4 |
5 | ///
6 | /// SRL file for DS rom extraction.
7 | ///
8 | public class SrlFile
9 | {
10 | // Location of input file
11 | private readonly string path;
12 |
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// path to SRL file.
17 | /// whether to provide verbose output.
18 | public SrlFile(string srlPath, bool verbose = false)
19 | {
20 | this.path = srlPath;
21 |
22 | if (verbose)
23 | {
24 | Console.WriteLine("Constructing SRC file at {0}", srlPath);
25 | }
26 | }
27 |
28 | ///
29 | /// Gets location of input file.
30 | ///
31 | public string Path
32 | {
33 | get { return this.path; }
34 | }
35 |
36 | ///
37 | /// Whether the given path is an SRL file.
38 | ///
39 | /// path to file.
40 | /// true if it is an SRL file, false otherwise.
41 | public static bool IsSrl(string srlPath)
42 | {
43 | if (System.IO.Path.GetExtension(srlPath).CompareTo(".srl") == 0)
44 | {
45 | return true;
46 | }
47 | else
48 | {
49 | return false;
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/WiiuVcExtractorTests/Libraries/RomSizeDictionaryTests.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractorTests.Libraries
2 | {
3 | using System;
4 | using System.IO;
5 | using WiiuVcExtractor.Libraries;
6 | using Xunit;
7 |
8 | public class RomSizeDictionaryTests
9 | {
10 | [Fact]
11 | public void RomSizeDictionary_WhenCSVFileIsMissing_ThrowsException()
12 | {
13 | Assert.Throws(() => new RomSizeDictionary(string.Empty));
14 | }
15 |
16 | [Fact]
17 | public void RomSizeDictionary_WhenCSVFileExists_ContructsRomNameDictionary()
18 | {
19 | var expected = typeof(RomSizeDictionary);
20 | var result = new RomSizeDictionary(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "snesromsizes.csv"));
21 |
22 | Assert.IsType(expected, result);
23 | }
24 |
25 | [Fact]
26 | public void GetRomSize_WhenRomNameExists_ReturnsRomSize()
27 | {
28 | var dictionary = new RomSizeDictionary(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "snesromsizes.csv"));
29 |
30 | var result = dictionary.GetRomSize("ROCKMAN SOCCER");
31 |
32 | Assert.Equal(1310720, result);
33 | }
34 |
35 | [Fact]
36 | public void GetRomName_WhenRomNameDoesNotExist_ReturnsMaxRomSize()
37 | {
38 | var dictionary = new RomSizeDictionary(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "snesromsizes.csv"));
39 |
40 | var result = dictionary.GetRomSize("WUP-JUNKANDSUCH", 1024000);
41 |
42 | Assert.Equal(1024000, result);
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/WiiuVcExtractor.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30523.141
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WiiuVcExtractor", "WiiuVcExtractor\WiiuVcExtractor.csproj", "{94F25BB6-2459-4F3C-A61E-F04424EBDF69}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WiiuVcExtractorTests", "WiiuVcExtractorTests\WiiuVcExtractorTests.csproj", "{6F502CEC-0CD3-4937-A8AA-B736EFECA724}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {94F25BB6-2459-4F3C-A61E-F04424EBDF69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {94F25BB6-2459-4F3C-A61E-F04424EBDF69}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {94F25BB6-2459-4F3C-A61E-F04424EBDF69}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {94F25BB6-2459-4F3C-A61E-F04424EBDF69}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {6F502CEC-0CD3-4937-A8AA-B736EFECA724}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {6F502CEC-0CD3-4937-A8AA-B736EFECA724}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {6F502CEC-0CD3-4937-A8AA-B736EFECA724}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {6F502CEC-0CD3-4937-A8AA-B736EFECA724}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {001173A1-7CDF-4436-8B58-7337976847C8}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/FileTypes/PkgContentFile.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.FileTypes
2 | {
3 | using System;
4 | using System.IO;
5 |
6 | ///
7 | /// Individual file stored within a .pkg file.
8 | ///
9 | public class PkgContentFile
10 | {
11 | private readonly string path;
12 | private readonly byte[] content;
13 |
14 | ///
15 | /// Initializes a new instance of the class.
16 | ///
17 | /// path of the content file.
18 | /// content of the file in a byte array.
19 | public PkgContentFile(string path, byte[] contentBytes)
20 | {
21 | this.path = path;
22 | this.content = contentBytes;
23 | }
24 |
25 | ///
26 | /// Gets path of the content file.
27 | ///
28 | public string Path
29 | {
30 | get { return this.path; }
31 | }
32 |
33 | ///
34 | /// Gets content of the file.
35 | ///
36 | public byte[] Content
37 | {
38 | get { return this.content; }
39 | }
40 |
41 | ///
42 | /// Writes the content file to a given path or to a relative path if not provided.
43 | ///
44 | /// path to write the content file in the filesystem.
45 | public void Write(string writePath = "")
46 | {
47 | if (string.IsNullOrEmpty(writePath))
48 | {
49 | writePath = this.path;
50 | }
51 |
52 | // Create the parent directory
53 | Directory.CreateDirectory(Directory.GetParent(writePath).ToString());
54 |
55 | using BinaryWriter bw = new BinaryWriter(File.Open(writePath, FileMode.Create));
56 | Console.WriteLine("Writing content file {0} to {1}", this.path, writePath);
57 | bw.Write(this.content);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/FileTypes/RpxSectionHeaderSort.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.FileTypes
2 | {
3 | using System;
4 |
5 | ///
6 | /// RPX section header sorter.
7 | ///
8 | public class RpxSectionHeaderSort : IEquatable, IComparable
9 | {
10 | ///
11 | /// Gets or sets RPX section index.
12 | ///
13 | public uint Index { get; set; }
14 |
15 | ///
16 | /// Gets or sets RPX section offset.
17 | ///
18 | public uint Offset { get; set; }
19 |
20 | ///
21 | /// Compares RPX section headers.
22 | ///
23 | /// other section header to compare.
24 | /// A signed number indicating the relative values of this instance and value.
25 | public int CompareTo(RpxSectionHeaderSort other)
26 | {
27 | if (other == null)
28 | {
29 | return 1;
30 | }
31 | else
32 | {
33 | return this.Offset.CompareTo(other.Offset);
34 | }
35 | }
36 |
37 | ///
38 | /// Compares RPX section headers for equality.
39 | ///
40 | /// other section header to compare.
41 | /// true if equal, false otherwise.
42 | public bool Equals(RpxSectionHeaderSort other)
43 | {
44 | if (other == null)
45 | {
46 | return false;
47 | }
48 |
49 | return this.Offset.Equals(other.Offset);
50 | }
51 |
52 | ///
53 | /// Creates string representation of the RPX section header sort.
54 | ///
55 | /// string representation of the RPX section header sort.
56 | public override string ToString()
57 | {
58 | return "RpxSectionHeaderSort:\n" +
59 | "index: " + this.Index.ToString() + "\n" +
60 | "offset: 0x" + string.Format("{0:X}", this.Offset) + "\n";
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/Libraries/RomNameDictionary.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.Libraries
2 | {
3 | using System.Collections.Specialized;
4 | using System.IO;
5 |
6 | ///
7 | /// Dictionary that maps WUP- strings to rom names.
8 | ///
9 | public class RomNameDictionary
10 | {
11 | private OrderedDictionary dictionary;
12 |
13 | ///
14 | /// Initializes a new instance of the class from a CSV file.
15 | /// The CSV file should have no headers and two fields, the WUP ID and the rom name.
16 | ///
17 | /// path to the CSV file to read.
18 | public RomNameDictionary(string dictionaryCsvPath)
19 | {
20 | if (!File.Exists(dictionaryCsvPath))
21 | {
22 | throw new FileNotFoundException("Could not find the " + dictionaryCsvPath + " file");
23 | }
24 |
25 | // The key of the dictionary is the WUP- string (WUP-FAME) and the value is the rom name
26 | this.dictionary = new OrderedDictionary();
27 |
28 | // Read in the CSV file
29 | using var reader = new StreamReader(File.OpenRead(dictionaryCsvPath));
30 | while (!reader.EndOfStream)
31 | {
32 | var line = reader.ReadLine();
33 | var values = line.Split(',');
34 |
35 | if (!string.IsNullOrEmpty(values[0]) && !string.IsNullOrEmpty(values[1]))
36 | {
37 | this.dictionary.Add(values[0], values[1]);
38 | }
39 | }
40 | }
41 |
42 | ///
43 | /// Gets a rom name using a WUP ID.
44 | ///
45 | /// WUP ID to find.
46 | /// Aliased rom name or an empty string if the WUP ID cannot be found.
47 | public string GetRomName(string wupString)
48 | {
49 | if (this.dictionary.Contains(wupString))
50 | {
51 | return (string)this.dictionary[wupString];
52 | }
53 |
54 | return string.Empty;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/Libraries/RomSizeDictionary.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.Libraries
2 | {
3 | using System;
4 | using System.Collections.Specialized;
5 | using System.IO;
6 |
7 | ///
8 | /// Dictionary that rom names to rom sizes.
9 | ///
10 | public class RomSizeDictionary
11 | {
12 | private OrderedDictionary dictionary;
13 |
14 | ///
15 | /// Initializes a new instance of the class from a CSV file.
16 | /// The CSV file should have no headers and two fields, the rom name and the size in bytes.
17 | ///
18 | /// path to the CSV file to read.
19 | public RomSizeDictionary(string dictionaryCsvPath)
20 | {
21 | if (!File.Exists(dictionaryCsvPath))
22 | {
23 | throw new FileNotFoundException("Could not find the " + dictionaryCsvPath + " file");
24 | }
25 |
26 | // The key of the dictionary is the rom name and the value is the size in bytes
27 | this.dictionary = new OrderedDictionary();
28 |
29 | // Read in the CSV file
30 | using var reader = new StreamReader(File.OpenRead(dictionaryCsvPath));
31 | while (!reader.EndOfStream)
32 | {
33 | var line = reader.ReadLine();
34 | var values = line.Split(',');
35 |
36 | if (!string.IsNullOrEmpty(values[0]) && !string.IsNullOrEmpty(values[1]))
37 | {
38 | int romSize = Convert.ToInt32(values[1]);
39 | this.dictionary.Add(values[0], romSize);
40 | }
41 | }
42 | }
43 |
44 | ///
45 | /// Gets a rom size using a rom name.
46 | ///
47 | /// rom name to find.
48 | /// maximum size of a rom in bytes.
49 | /// Found rom size or the value of maxRomSize if an entry cannot be found.
50 | public int GetRomSize(string romName, int maxRomSize = 0)
51 | {
52 | if (this.dictionary.Contains(romName))
53 | {
54 | return (int)this.dictionary[romName];
55 | }
56 |
57 | romName = romName.Trim();
58 |
59 | if (this.dictionary.Contains(romName))
60 | {
61 | return (int)this.dictionary[romName];
62 | }
63 |
64 | return maxRomSize;
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/FileTypes/MdfHeader.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.FileTypes
2 | {
3 | using System;
4 | using System.IO;
5 | using System.Text;
6 | using WiiuVcExtractor.Libraries;
7 |
8 | ///
9 | /// MDF header class.
10 | ///
11 | public class MdfHeader
12 | {
13 | ///
14 | /// Length of the MDF header in bytes.
15 | ///
16 | public const int MDFHeaderLength = 8;
17 |
18 | private const int MDFSignatureLength = 4;
19 |
20 | private static readonly byte[] MDFSignature = { 0x6D, 0x64, 0x66, 0x00 };
21 |
22 | private readonly byte[] signature;
23 | private readonly uint length;
24 |
25 | ///
26 | /// Initializes a new instance of the class.
27 | ///
28 | /// path to the PSB file to read.
29 | public MdfHeader(string psbPath)
30 | {
31 | this.signature = new byte[MDFSignatureLength];
32 |
33 | using FileStream fs = new FileStream(psbPath, FileMode.Open, FileAccess.Read);
34 | using BinaryReader br = new BinaryReader(fs, new ASCIIEncoding());
35 |
36 | // Read in the header
37 | this.signature = br.ReadBytes(MDFSignatureLength);
38 | this.length = EndianUtility.ReadUInt32LE(br);
39 | }
40 |
41 | ///
42 | /// Gets the length of the associated MDF data.
43 | ///
44 | public uint Length
45 | {
46 | get { return this.length; }
47 | }
48 |
49 | ///
50 | /// Whether the current header is valid.
51 | ///
52 | /// true if valid, false otherwise.
53 | public bool IsValid()
54 | {
55 | // Check that the signature is correct
56 | if (this.signature[0] != MDFSignature[0] ||
57 | this.signature[1] != MDFSignature[1] ||
58 | this.signature[2] != MDFSignature[2] ||
59 | this.signature[3] != MDFSignature[3])
60 | {
61 | return false;
62 | }
63 |
64 | return true;
65 | }
66 |
67 | ///
68 | /// Generates string representation of the header.
69 | ///
70 | /// string representation of the header.
71 | public override string ToString()
72 | {
73 | return "MdfHeader:\n" +
74 | "signature: " + BitConverter.ToString(this.signature) + "\n" +
75 | "length: " + this.length.ToString() + "\n";
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/License.zlib.txt:
--------------------------------------------------------------------------------
1 |
2 | The following licenses govern use of the accompanying software, the
3 | DotNetZip library ("the software"). If you use the software, you accept
4 | these licenses. If you do not accept the license, do not use the software.
5 |
6 | The managed ZLIB code included in Ionic.Zlib.dll and Ionic.Zip.dll is
7 | modified code, based on jzlib.
8 |
9 |
10 |
11 | The following notice applies to jzlib:
12 | -----------------------------------------------------------------------
13 |
14 | Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved.
15 |
16 | Redistribution and use in source and binary forms, with or without
17 | modification, are permitted provided that the following conditions are met:
18 |
19 | 1. Redistributions of source code must retain the above copyright notice,
20 | this list of conditions and the following disclaimer.
21 |
22 | 2. Redistributions in binary form must reproduce the above copyright
23 | notice, this list of conditions and the following disclaimer in
24 | the documentation and/or other materials provided with the distribution.
25 |
26 | 3. The names of the authors may not be used to endorse or promote products
27 | derived from this software without specific prior written permission.
28 |
29 | THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
30 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
32 | INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
33 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
35 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
36 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
37 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
38 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 |
40 | -----------------------------------------------------------------------
41 |
42 | jzlib is based on zlib-1.1.3.
43 |
44 | The following notice applies to zlib:
45 |
46 | -----------------------------------------------------------------------
47 |
48 | Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler
49 |
50 | The ZLIB software is provided 'as-is', without any express or implied
51 | warranty. In no event will the authors be held liable for any damages
52 | arising from the use of this software.
53 |
54 | Permission is granted to anyone to use this software for any purpose,
55 | including commercial applications, and to alter it and redistribute it
56 | freely, subject to the following restrictions:
57 |
58 | 1. The origin of this software must not be misrepresented; you must not
59 | claim that you wrote the original software. If you use this software
60 | in a product, an acknowledgment in the product documentation would be
61 | appreciated but is not required.
62 | 2. Altered source versions must be plainly marked as such, and must not be
63 | misrepresented as being the original software.
64 | 3. This notice may not be removed or altered from any source distribution.
65 |
66 | Jean-loup Gailly jloup@gzip.org
67 | Mark Adler madler@alumni.caltech.edu
68 |
69 |
70 | -----------------------------------------------------------------------
71 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/pceromnames.csv:
--------------------------------------------------------------------------------
1 | WUP-PNFP,Air Zonk
2 | WUP-PNLP,Alien Crush
3 | WUP-PPGP,Battle Chopper
4 | WUP-PPSP,Battle Lode Runner
5 | WUP-PNKP,Blazing Lazers
6 | WUP-PPTP,Bomberman 93
7 | WUP-PNWP,Bomberman 94
8 | WUP-PN7P,Bomberman Panic Bomber
9 | WUP-PNHP,Bonk 3 Bonks Big Adventure
10 | WUP-PNBP,Bonks Adventure
11 | WUP-PNGP,Bonks Revenge
12 | WUP-PNZP,Break In
13 | WUP-PNPP,Chew Man Fu
14 | WUP-PNJP,China Warrior
15 | WUP-PNXP,Detana Twin Bee
16 | WUP-PN6P,Devils Crush
17 | WUP-PNYP,Digital Champ
18 | WUP-PNSP,Double Dungeons
19 | WUP-PN3P,Dungeon Explorer
20 | WUP-PNQP,Final Soldier
21 | WUP-PNAP,Gradius
22 | WUP-PPEP,ImageFight
23 | WUP-PPFP,ImageFight II
24 | WUP-PPCP,Legend of Hero Tonma
25 | WUP-PN5P,Lords of Thunder
26 | WUP-PN2P,Motoroader
27 | WUP-PPUP,Necromancer
28 | WUP-PNEP,Neutopia
29 | WUP-PNMP,Neutopia II
30 | WUP-PNCP,New Adventure Island
31 | WUP-PPDP,Ninja Spirit
32 | WUP-PNUP,Power Golf
33 | WUP-PPAP,R-Type
34 | WUP-PNVP,Salamander
35 | WUP-PNNP,Shockman
36 | WUP-PNTP,Soldier Blade
37 | WUP-PNDP,Super Star Soldier
38 | WUP-PNRP,Victory Run
39 | WUP-PPBP,Vigilante
40 | WUP-PN4P,World Sports Competition
41 | WUP-PNFE,Air Zonk
42 | WUP-PNLE,Alien Crush
43 | WUP-PPGE,Battle Chopper
44 | WUP-PPSE,Battle Lode Runner
45 | WUP-PNKE,Blazing Lazers
46 | WUP-PPTE,Bomberman 93
47 | WUP-PNWE,Bomberman 94
48 | WUP-PN7E,Bomberman Panic Bomber
49 | WUP-PNHE,Bonk 3 Bonks Big Adventure
50 | WUP-PNBE,Bonks Adventure
51 | WUP-PNGE,Bonks Revenge
52 | WUP-PNZE,Break In
53 | WUP-PNPE,Chew Man Fu
54 | WUP-PNJE,China Warrior
55 | WUP-PNXE,Detana Twin Bee
56 | WUP-PN6E,Devils Crush
57 | WUP-PNYE,Digital Champ
58 | WUP-PNSE,Double Dungeons
59 | WUP-PN3E,Dungeon Explorer
60 | WUP-PNQE,Final Soldier
61 | WUP-PNAE,Gradius
62 | WUP-PPEE,ImageFight
63 | WUP-PPFE,ImageFight II
64 | WUP-PPCE,Legend of Hero Tonma
65 | WUP-PN5E,Lords of Thunder
66 | WUP-PN2E,Motoroader
67 | WUP-PPUE,Necromancer
68 | WUP-PNEE,Neutopia
69 | WUP-PNME,Neutopia II
70 | WUP-PNCE,New Adventure Island
71 | WUP-PPDE,Ninja Spirit
72 | WUP-PNUE,Power Golf
73 | WUP-PPAE,R-Type
74 | WUP-PNVE,Salamander
75 | WUP-PNNE,Shockman
76 | WUP-PNTE,Soldier Blade
77 | WUP-PNDE,Super Star Soldier
78 | WUP-PNRE,Victory Run
79 | WUP-PPBE,Vigilante
80 | WUP-PN4E,World Sports Competition
81 | WUP-PNFJ,Air Zonk
82 | WUP-PNLJ,Alien Crush
83 | WUP-PPGJ,Battle Chopper
84 | WUP-PPSJ,Battle Lode Runner
85 | WUP-PNKJ,Blazing Lazers
86 | WUP-PPTJ,Bomberman 93
87 | WUP-PNWJ,Bomberman 94
88 | WUP-PN7J,Bomberman Panic Bomber
89 | WUP-PNHJ,Bonk 3 Bonks Big Adventure
90 | WUP-PNBJ,Bonks Adventure
91 | WUP-PNGJ,Bonks Revenge
92 | WUP-PNZJ,Break In
93 | WUP-PNPJ,Chew Man Fu
94 | WUP-PNJJ,China Warrior
95 | WUP-PNXJ,Detana Twin Bee
96 | WUP-PN6J,Devils Crush
97 | WUP-PNYJ,Digital Champ
98 | WUP-PNSJ,Double Dungeons
99 | WUP-PN3J,Dungeon Explorer
100 | WUP-PNQJ,Final Soldier
101 | WUP-PNAJ,Gradius
102 | WUP-PPEJ,ImageFight
103 | WUP-PPFJ,ImageFight II
104 | WUP-PPCJ,Legend of Hero Tonma
105 | WUP-PN5J,Lords of Thunder
106 | WUP-PN2J,Motoroader
107 | WUP-PPUJ,Necromancer
108 | WUP-PNEJ,Neutopia
109 | WUP-PNMJ,Neutopia II
110 | WUP-PNCJ,New Adventure Island
111 | WUP-PPDJ,Ninja Spirit
112 | WUP-PNUJ,Power Golf
113 | WUP-PPAJ,R-Type
114 | WUP-PNVJ,Salamander
115 | WUP-PNNJ,Shockman
116 | WUP-PNTJ,Soldier Blade
117 | WUP-PNDJ,Super Star Soldier
118 | WUP-PNRJ,Victory Run
119 | WUP-PPBJ,Vigilante
120 | WUP-PN4J,World Sports Competition
121 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/Libraries/Sdd1/Interleaver.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.Libraries.Sdd1
2 | {
3 | using System.Collections.Generic;
4 |
5 | ///
6 | /// S-DD1 compression interleaver.
7 | ///
8 | public class Interleaver
9 | {
10 | private readonly List codewSequence;
11 | private readonly List[] codewBuffer;
12 | private readonly byte[] bitInd;
13 | private readonly int[] byteIndex;
14 | private uint outputLength;
15 | private byte[] outputBuffer;
16 | private uint outputBufferIndex;
17 | private byte oBitInd;
18 |
19 | ///
20 | /// Initializes a new instance of the class.
21 | ///
22 | /// associated codeword sequence.
23 | /// codeword buffer.
24 | public Interleaver(ref List associatedCWSeq, ref List[] cwBuf)
25 | {
26 | this.codewSequence = associatedCWSeq;
27 | this.codewBuffer = cwBuf;
28 | this.bitInd = new byte[8];
29 | this.byteIndex = new int[8];
30 | }
31 |
32 | ///
33 | /// Prepare for compression.
34 | ///
35 | /// header.
36 | /// output buffer.
37 | public void PrepareComp(byte header, byte[] outBuf)
38 | {
39 | this.outputLength = 0;
40 | this.outputBufferIndex = 0;
41 | this.outputBuffer = outBuf;
42 | this.outputBuffer[this.outputBufferIndex] = (byte)(header << 4);
43 | this.oBitInd = 4;
44 | for (byte i = 0; i < 8; i++)
45 | {
46 | this.byteIndex[i] = 0;
47 | this.bitInd[i] = 0;
48 | }
49 | }
50 |
51 | ///
52 | /// Launch interleaver.
53 | ///
54 | /// output length.
55 | /// processed output buffer.
56 | public byte[] Launch(out uint outLen)
57 | {
58 | for (int i = 0; i < this.codewSequence.Count; i++)
59 | {
60 | if (this.MoveBit(this.codewSequence[i]) != 0)
61 | {
62 | for (byte j = 0; j < this.codewSequence[i]; j++)
63 | {
64 | this.MoveBit(this.codewSequence[i]);
65 | }
66 | }
67 | }
68 |
69 | if (this.oBitInd != 0)
70 | {
71 | ++this.outputLength;
72 | }
73 |
74 | outLen = this.outputLength;
75 |
76 | return this.outputBuffer;
77 | }
78 |
79 | private byte MoveBit(byte codeNum)
80 | {
81 | if (this.oBitInd == 0)
82 | {
83 | this.outputBuffer[this.outputBufferIndex] = 0;
84 | }
85 |
86 | byte bit = (byte)((this.codewBuffer[codeNum][this.byteIndex[codeNum]] & (0x80 >> this.bitInd[codeNum])) << this.bitInd[codeNum]);
87 | this.outputBuffer[this.outputBufferIndex] |= (byte)(bit >> this.oBitInd);
88 |
89 | if ((++this.bitInd[codeNum] & 0x08) != 0)
90 | {
91 | this.bitInd[codeNum] = 0;
92 | this.byteIndex[codeNum]++;
93 | }
94 |
95 | if ((++this.oBitInd & 0x08) != 0)
96 | {
97 | this.oBitInd = 0;
98 | this.outputBufferIndex++;
99 | ++this.outputLength;
100 | }
101 |
102 | return bit;
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/RomExtractors/PceVcExtractor.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.RomExtractors
2 | {
3 | using System;
4 | using System.IO;
5 | using WiiuVcExtractor.FileTypes;
6 |
7 | ///
8 | /// PCE VC rom extractor.
9 | ///
10 | public class PceVcExtractor : IRomExtractor
11 | {
12 | // private const int PceHeaderLength = 16;
13 | private readonly PkgFile pkgFile;
14 | private readonly bool verbose;
15 |
16 | ///
17 | /// Initializes a new instance of the class.
18 | ///
19 | /// PKG file to extract.
20 | /// whether to enable verbose output.
21 | public PceVcExtractor(PkgFile pkgFile, bool verbose = false)
22 | {
23 | this.verbose = verbose;
24 | this.pkgFile = pkgFile;
25 | }
26 |
27 | ///
28 | /// Extracts a PCE rom.
29 | ///
30 | /// path to extracted rom.
31 | public string ExtractRom()
32 | {
33 | if (this.verbose)
34 | {
35 | Console.WriteLine("Extracting rom from PKG file...");
36 | Console.WriteLine("Determining type of rom within PKG file (.pce or CD)");
37 | }
38 |
39 | // Find any PCE files within the pkg file and write them to complete extraction
40 | PkgContentFile pceFile = this.pkgFile.ContentFiles.Find(x => Path.GetExtension(x.Path).ToLower() == ".pce");
41 | if (pceFile != null)
42 | {
43 | if (this.verbose)
44 | {
45 | Console.WriteLine(".pce file found!");
46 | }
47 |
48 | pceFile.Write();
49 | return pceFile.Path;
50 | }
51 |
52 | // If no PCE files exist, attempt to find and process an HCD file and recombine all content files into a usable format
53 | PkgContentFile hcdFile = this.pkgFile.ContentFiles.Find(x => Path.GetExtension(x.Path).ToLower() == ".hcd");
54 | if (hcdFile != null)
55 | {
56 | if (this.verbose)
57 | {
58 | Console.WriteLine(".hcd file found!");
59 | }
60 |
61 | // .hcd file was found, create files for all content files
62 | foreach (var contentFile in this.pkgFile.ContentFiles)
63 | {
64 | if (this.verbose)
65 | {
66 | Console.WriteLine("Extracting {0}...", contentFile.Path);
67 | }
68 |
69 | contentFile.Write();
70 | }
71 |
72 | /*
73 | // TODO: Generate a .cue file for everything
74 | */
75 |
76 | return hcdFile.Path;
77 | }
78 |
79 | return string.Empty;
80 | }
81 |
82 | ///
83 | /// Whether the rom is valid.
84 | ///
85 | /// true if valid, false otherwise.
86 | public bool IsValidRom()
87 | {
88 | Console.WriteLine("Checking if this is a PC Engine VC title...");
89 |
90 | string entryPointExtension = Path.GetExtension(this.pkgFile.Header.EntryPoint.ToLower());
91 |
92 | // Check whether the entry point is a .pce or .hcd file (case-insensitive)
93 | if (entryPointExtension == ".pce" || entryPointExtension == ".hcd")
94 | {
95 | Console.WriteLine("PC Engine VC Rom detected! Extension {0} was found in the {1} entry point.", entryPointExtension, this.pkgFile.Path);
96 | return true;
97 | }
98 |
99 | Console.WriteLine("Not a PC Engine VC Title");
100 | return false;
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/FileTypes/PkgFile.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.FileTypes
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Text;
7 | using WiiuVcExtractor.Libraries;
8 |
9 | ///
10 | /// Extractor for .pkg files for PC Engine games.
11 | ///
12 | public class PkgFile
13 | {
14 | private readonly PkgHeader header;
15 | private readonly List contentFiles;
16 | private readonly string path;
17 |
18 | ///
19 | /// Initializes a new instance of the class.
20 | ///
21 | /// path to the .pkg file.
22 | /// whether to provide verbose output.
23 | public PkgFile(string pkgFilePath, bool verbose = false)
24 | {
25 | Console.WriteLine("Extracting PKG file...");
26 |
27 | this.contentFiles = new List();
28 |
29 | this.path = pkgFilePath;
30 |
31 | try
32 | {
33 | this.header = new PkgHeader(this.path);
34 | }
35 | catch (Exception ex)
36 | {
37 | Console.WriteLine("Could not successfully read PKG file: " + ex.Message);
38 | return;
39 | }
40 |
41 | if (verbose)
42 | {
43 | Console.WriteLine("Successfully read PKG file header as:\n{0}", this.header.ToString());
44 | }
45 |
46 | // Detect each file within the PKG file after the header and store it in memory as a PkgContentFile, start after the header
47 | using FileStream fs = new FileStream(pkgFilePath, FileMode.Open, FileAccess.Read);
48 |
49 | // Skip header
50 | fs.Seek(this.header.Length, SeekOrigin.Begin);
51 | using BinaryReader br = new BinaryReader(fs, new ASCIIEncoding());
52 | while (br.BaseStream.Position < br.BaseStream.Length)
53 | {
54 | // Read in each section, these are arranged as a LE UINT32 describing the size followed by a null-terminated filename and its content
55 | int sectionLength = br.ReadInt32LE();
56 | string sectionPath = br.ReadNullTerminatedString();
57 | byte[] sectionContent = br.ReadBytes(sectionLength);
58 | this.contentFiles.Add(new PkgContentFile(sectionPath, sectionContent));
59 | }
60 | }
61 |
62 | ///
63 | /// Gets associated .pkg header.
64 | ///
65 | public PkgHeader Header
66 | {
67 | get { return this.header; }
68 | }
69 |
70 | ///
71 | /// Gets content files stored in the .pkg file.
72 | ///
73 | public List ContentFiles
74 | {
75 | get { return this.contentFiles; }
76 | }
77 |
78 | ///
79 | /// Gets path to the .pkg file.
80 | ///
81 | public string Path
82 | {
83 | get { return this.path; }
84 | }
85 |
86 | ///
87 | /// Whether the given path is a valid .pkg file.
88 | ///
89 | /// path to the .pkg file to validate.
90 | /// true if value, false otherwise.
91 | public static bool IsPkg(string pkgFilePath)
92 | {
93 | try
94 | {
95 | PkgHeader header = new PkgHeader(pkgFilePath);
96 | return header.IsValid();
97 | }
98 | catch (Exception ex)
99 | {
100 | // If an exception is received, assume that the header could not be parsed successfully
101 | Console.WriteLine("Could not parse pkg header, this is likely not a pkg: {0}", ex.Message);
102 | return false;
103 | }
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/Libraries/Sdd1/GolombCodeEncoder.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.Libraries.Sdd1
2 | {
3 | using System.Collections.Generic;
4 |
5 | ///
6 | /// Golomb code encoder for S-DD1 compression.
7 | ///
8 | public class GolombCodeEncoder
9 | {
10 | private readonly List codewSequence;
11 | private readonly List[] codewBuffer;
12 | private readonly byte[] bitInd;
13 | private readonly byte[] mpsCount;
14 |
15 | ///
16 | /// Initializes a new instance of the class.
17 | ///
18 | /// associated codeword sequence.
19 | /// codeword buffer.
20 | public GolombCodeEncoder(ref List associatedCWSeq, ref List[] cwBuf)
21 | {
22 | this.codewSequence = associatedCWSeq;
23 | this.codewBuffer = cwBuf;
24 | this.bitInd = new byte[8];
25 | this.mpsCount = new byte[8];
26 | }
27 |
28 | ///
29 | /// Prepare for compression.
30 | ///
31 | public void PrepareComp()
32 | {
33 | for (byte i = 0; i < 8; i++)
34 | {
35 | this.mpsCount[i] = 0;
36 | this.bitInd[i] = 0;
37 | }
38 | }
39 |
40 | ///
41 | /// Put bit into the codeword buffer.
42 | ///
43 | /// code number.
44 | /// bit to put.
45 | /// byte representing the end of the run.
46 | public byte PutBit(byte codeNum, byte bit)
47 | {
48 | byte endOfRun;
49 |
50 | if (this.mpsCount[codeNum] == 0)
51 | {
52 | this.codewSequence.Add(codeNum);
53 | }
54 |
55 | if (bit != 0)
56 | {
57 | endOfRun = 1;
58 | this.OutputBit(codeNum, 1);
59 | for (byte i = 0, aux = 0x01; i < codeNum; i++, aux <<= 1)
60 | {
61 | byte auxOutputBit = 0;
62 | if ((this.mpsCount[codeNum] & aux) == 0)
63 | {
64 | auxOutputBit = 1;
65 | }
66 |
67 | this.OutputBit(codeNum, auxOutputBit);
68 | }
69 |
70 | this.mpsCount[codeNum] = 0;
71 | }
72 | else
73 | {
74 | if (++this.mpsCount[codeNum] == (1 << codeNum))
75 | {
76 | endOfRun = 1;
77 | this.OutputBit(codeNum, 0);
78 | this.mpsCount[codeNum] = 0;
79 | }
80 | else
81 | {
82 | endOfRun = 0;
83 | }
84 | }
85 |
86 | return endOfRun;
87 | }
88 |
89 | ///
90 | /// Finish compression.
91 | ///
92 | public void FinishComp()
93 | {
94 | for (byte i = 0; i < 8; i++)
95 | {
96 | if (this.mpsCount[i] != 0)
97 | {
98 | this.OutputBit(i, 0);
99 | }
100 | }
101 | }
102 |
103 | ///
104 | /// Output bit.
105 | ///
106 | /// code number.
107 | /// bit to output.
108 | public void OutputBit(byte codeNum, byte bit)
109 | {
110 | byte oBit = 0;
111 |
112 | if (bit != 0)
113 | {
114 | oBit = (byte)(0x80 >> this.bitInd[codeNum]);
115 | }
116 |
117 | if (this.bitInd[codeNum] == 0)
118 | {
119 | this.codewBuffer[codeNum].Add(0);
120 | }
121 |
122 | this.codewBuffer[codeNum][^1] |= oBit;
123 |
124 | ++this.bitInd[codeNum];
125 | this.bitInd[codeNum] &= 0x07;
126 | }
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/FileTypes/PsbChunkTable.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.FileTypes
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Text;
7 | using WiiuVcExtractor.Libraries;
8 |
9 | ///
10 | /// PSB file chunk table.
11 | ///
12 | public class PsbChunkTable
13 | {
14 | ///
15 | /// Initializes a new instance of the class.
16 | ///
17 | /// The PSB data to parse.
18 | /// The offset to begin reading chunk offset data.
19 | /// The offset to begin reading chunk length data.
20 | /// The offset to begni reading chunk data.
21 | public PsbChunkTable(byte[] psbData, long chunkOffsetsOffset, long chunkLengthsOffset, long chunkDataOffset)
22 | {
23 | // Initialize the name table from the passed data
24 | using MemoryStream ms = new MemoryStream(psbData);
25 | ms.Seek(chunkOffsetsOffset, SeekOrigin.Begin);
26 | this.Offsets = this.ReadChunkTableValues(ms);
27 |
28 | ms.Seek(chunkLengthsOffset, SeekOrigin.Begin);
29 | this.Lengths = this.ReadChunkTableValues(ms);
30 |
31 | if (this.Offsets.Count != this.Lengths.Count)
32 | {
33 | throw new InvalidOperationException("The lengths of the chunk offsets list and the chunk lengths list differ.");
34 | }
35 |
36 | // Only attempt to read in the chunks if they exist in the file
37 | if (this.Offsets.Count > 0 && psbData.Length > chunkDataOffset)
38 | {
39 | ms.Seek(chunkDataOffset, SeekOrigin.Begin);
40 |
41 | // TODO: Add code to read in chunks, may not be necessary for the GBA extraction
42 | }
43 | }
44 |
45 | ///
46 | /// Gets PSB chunk offsets. Each offset indicates when a chunk begins in the PSB chunk table.
47 | ///
48 | public List Offsets { get; }
49 |
50 | ///
51 | /// Gets PSB chunk lengths.
52 | ///
53 | public List Lengths { get; }
54 |
55 | ///
56 | /// Gets PSB chunk data (currently unused).
57 | ///
58 | public byte[] ChunkData { get; }
59 |
60 | private List ReadChunkTableValues(MemoryStream ms)
61 | {
62 | List valueList = new List();
63 |
64 | using (BinaryReader br = new BinaryReader(ms, new ASCIIEncoding(), true))
65 | {
66 | // get the offset information
67 | byte type = br.ReadByte();
68 |
69 | // Get the size of each object in bytes
70 | int countByteSize = type - 12;
71 | uint count = 0;
72 |
73 | if (countByteSize == 1)
74 | {
75 | count = br.ReadByte();
76 | }
77 | else if (countByteSize == 2)
78 | {
79 | count = EndianUtility.ReadUInt16LE(br);
80 | }
81 | else if (countByteSize == 4)
82 | {
83 | count = EndianUtility.ReadUInt32LE(br);
84 | }
85 |
86 | byte entrySizeType = br.ReadByte();
87 | int entryByteSize = entrySizeType - 12;
88 |
89 | uint value = 0;
90 |
91 | // Read in the values
92 | for (int i = 0; i < count; i++)
93 | {
94 | if (countByteSize == 1)
95 | {
96 | value = br.ReadByte();
97 | }
98 |
99 | if (entryByteSize == 2)
100 | {
101 | value = EndianUtility.ReadUInt16LE(br);
102 | }
103 | else if (entryByteSize == 4)
104 | {
105 | value = EndianUtility.ReadUInt32LE(br);
106 | }
107 |
108 | valueList.Add(value);
109 | }
110 | }
111 |
112 | return valueList;
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/Libraries/Sdd1/BitplanesExtractor.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.Libraries.Sdd1
2 | {
3 | using System.Collections.Generic;
4 | using System.Linq;
5 |
6 | ///
7 | /// S-DD1 compressor bitplanes extractor.
8 | ///
9 | public class BitplanesExtractor
10 | {
11 | private const byte HeaderMask = 0x0C;
12 |
13 | private readonly List[] bitplaneBuffer;
14 | private readonly byte[] bpBitInd;
15 |
16 | private byte bitplanesInfo;
17 | private ushort inputLength;
18 | private byte[] inputBuffer;
19 | private byte inBitInd;
20 | private byte currBitplane;
21 |
22 | ///
23 | /// Initializes a new instance of the class.
24 | ///
25 | /// bitplane buffer.
26 | public BitplanesExtractor(ref List[] bpBuffer)
27 | {
28 | this.bpBitInd = new byte[8];
29 | this.bitplaneBuffer = bpBuffer;
30 | }
31 |
32 | ///
33 | /// Prepare for compression.
34 | ///
35 | /// input data to compress.
36 | /// S-DD1 header.
37 | public void PrepareComp(byte[] inBuffer, byte header)
38 | {
39 | this.inputLength = (ushort)inBuffer.Length;
40 | this.inputBuffer = inBuffer;
41 | this.bitplanesInfo = (byte)(header & HeaderMask);
42 | }
43 |
44 | ///
45 | /// Launch bitplanes extractor.
46 | ///
47 | public void Launch()
48 | {
49 | --this.inputLength;
50 | switch (this.bitplanesInfo)
51 | {
52 | case 0x00:
53 | this.currBitplane = 1;
54 | break;
55 | case 0x04:
56 | this.currBitplane = 7;
57 | break;
58 | case 0x08:
59 | this.currBitplane = 3;
60 | break;
61 | case 0x0c:
62 | this.inBitInd = 7;
63 | for (byte i = 0; i < 8; i++)
64 | {
65 | this.bpBitInd[i] = 0;
66 | }
67 |
68 | break;
69 | }
70 |
71 | ushort counter = 0;
72 |
73 | do
74 | {
75 | switch (this.bitplanesInfo)
76 | {
77 | case 0x00:
78 | this.currBitplane ^= 0x01;
79 | this.bitplaneBuffer[this.currBitplane].Add(this.inputBuffer[counter]);
80 | break;
81 | case 0x04:
82 | this.currBitplane ^= 0x01;
83 | if ((counter & 0x000f) == 0)
84 | {
85 | this.currBitplane = (byte)((this.currBitplane + 2) & 0x07);
86 | }
87 |
88 | this.bitplaneBuffer[this.currBitplane].Add(this.inputBuffer[counter]);
89 | break;
90 | case 0x08:
91 | this.currBitplane ^= 0x01;
92 | if ((counter & 0x000f) == 0)
93 | {
94 | this.currBitplane ^= 0x02;
95 | }
96 |
97 | this.bitplaneBuffer[this.currBitplane].Add(this.inputBuffer[counter]);
98 | break;
99 | case 0x0c:
100 | for (byte i = 0; i < 8; i++)
101 | {
102 | this.PutBit(i, counter);
103 | }
104 |
105 | break;
106 | }
107 | }
108 | while (counter++ < this.inputLength);
109 | }
110 |
111 | private void PutBit(byte bitplane, ushort counter)
112 | {
113 | List currBPBuf = this.bitplaneBuffer[bitplane];
114 | byte currBitInd = this.bpBitInd[bitplane];
115 |
116 | if (currBitInd == 0)
117 | {
118 | currBPBuf.Add(0);
119 | }
120 |
121 | currBPBuf[currBPBuf.Count() - 1] |= (byte)(((this.inputBuffer[counter] & (0x80 >> this.inBitInd)) << this.inBitInd) >> currBitInd);
122 |
123 | currBitInd++;
124 | currBitInd &= 0x07;
125 |
126 | this.bpBitInd[bitplane] = currBitInd;
127 | this.inBitInd--;
128 | this.inBitInd &= 0x07;
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/FileTypes/PsbNameTable.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.FileTypes
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Text;
7 | using WiiuVcExtractor.Libraries;
8 |
9 | ///
10 | /// PSB file name table.
11 | ///
12 | public class PsbNameTable
13 | {
14 | private readonly List offsets;
15 | private readonly List jumps;
16 | private readonly List starts;
17 |
18 | ///
19 | /// Initializes a new instance of the class.
20 | ///
21 | /// PSB data byte array.
22 | /// Beginning of the names table in the PSB data.
23 | public PsbNameTable(byte[] psbData, long namesOffset)
24 | {
25 | // Initialize the name table from the passed data
26 | using MemoryStream ms = new MemoryStream(psbData);
27 | ms.Seek(namesOffset, SeekOrigin.Begin);
28 |
29 | this.offsets = this.ReadNameTableValues(ms);
30 | this.jumps = this.ReadNameTableValues(ms);
31 | this.starts = this.ReadNameTableValues(ms);
32 | }
33 |
34 | ///
35 | /// Gets PSB name table offsets (in bytes).
36 | ///
37 | public List Offsets
38 | {
39 | get { return this.offsets; }
40 | }
41 |
42 | ///
43 | /// Gets PSB name table jumps.
44 | ///
45 | public List Jumps
46 | {
47 | get { return this.jumps; }
48 | }
49 |
50 | ///
51 | /// Gets PSB name table starts.
52 | ///
53 | public List Starts
54 | {
55 | get { return this.starts; }
56 | }
57 |
58 | ///
59 | /// Gets the name at a given index of the PSB name table.
60 | ///
61 | /// name index.
62 | /// retrieved name.
63 | public string GetName(int index)
64 | {
65 | uint a = this.starts[index];
66 |
67 | // Follow one jump to skip the terminating NUL
68 | uint b = this.jumps[(int)a];
69 |
70 | string returnString = string.Empty;
71 |
72 | while (b != 0)
73 | {
74 | uint c = this.jumps[(int)b];
75 |
76 | uint d = this.offsets[(int)c];
77 |
78 | uint e = b - d;
79 |
80 | returnString = Convert.ToChar(e) + returnString;
81 |
82 | b = c;
83 | }
84 |
85 | return returnString;
86 | }
87 |
88 | private List ReadNameTableValues(MemoryStream ms)
89 | {
90 | List valueList = new List();
91 |
92 | using (BinaryReader br = new BinaryReader(ms, new ASCIIEncoding(), true))
93 | {
94 | // get the offset information
95 | byte type = br.ReadByte();
96 |
97 | // Get the size of each object in bytes
98 | int countByteSize = type - 12;
99 | uint count = 0;
100 |
101 | if (countByteSize == 1)
102 | {
103 | count = br.ReadByte();
104 | }
105 | else if (countByteSize == 2)
106 | {
107 | count = EndianUtility.ReadUInt16LE(br);
108 | }
109 | else if (countByteSize == 4)
110 | {
111 | count = EndianUtility.ReadUInt32LE(br);
112 | }
113 |
114 | byte entrySizeType = br.ReadByte();
115 | int entryByteSize = entrySizeType - 12;
116 |
117 | uint value = 0;
118 |
119 | // Read in the values
120 | for (int i = 0; i < count; i++)
121 | {
122 | if (entryByteSize == 1)
123 | {
124 | value = br.ReadByte();
125 | }
126 | else if (entryByteSize == 2)
127 | {
128 | value = EndianUtility.ReadUInt16LE(br);
129 | }
130 | else if (entryByteSize == 4)
131 | {
132 | value = EndianUtility.ReadUInt32LE(br);
133 | }
134 |
135 | valueList.Add(value);
136 | }
137 | }
138 |
139 | return valueList;
140 | }
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/Libraries/Sdd1/Compressor.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.Libraries.Sdd1
2 | {
3 | using System.Collections.Generic;
4 |
5 | ///
6 | /// S-DD1 compressor.
7 | ///
8 | public class Compressor
9 | {
10 | private readonly List[] bitplaneBuffer;
11 | private readonly List codewordsSequence;
12 | private readonly List[] codewordBuffer;
13 | private readonly BitplanesExtractor be;
14 | private readonly ContextModel cm;
15 | private readonly GolombCodeEncoder gce;
16 | private readonly ProbabilityEstimationModule pem;
17 | private readonly Interleaver interleaver;
18 |
19 | ///
20 | /// Initializes a new instance of the class.
21 | ///
22 | public Compressor()
23 | {
24 | this.codewordsSequence = new List();
25 | this.codewordBuffer = new List[8];
26 | this.bitplaneBuffer = new List[8];
27 | for (int i = 0; i < 8; i++)
28 | {
29 | this.bitplaneBuffer[i] = new List();
30 | this.codewordBuffer[i] = new List();
31 | }
32 |
33 | this.be = new BitplanesExtractor(ref this.bitplaneBuffer);
34 | this.cm = new ContextModel(ref this.bitplaneBuffer);
35 | this.gce = new GolombCodeEncoder(ref this.codewordsSequence, ref this.codewordBuffer);
36 | this.pem = new ProbabilityEstimationModule(ref this.cm, ref this.gce);
37 | this.interleaver = new Interleaver(ref this.codewordsSequence, ref this.codewordBuffer);
38 | }
39 |
40 | ///
41 | /// Compresses provided data in S-DD1 format.
42 | ///
43 | /// data to compress.
44 | /// length of the compressed output.
45 | /// buffer to store compressed output.
46 | /// compressed S-DD1 data.
47 | public byte[] Compress(byte[] inBuf, out uint outLen, byte[] outBuf)
48 | {
49 | uint minLength;
50 | byte[] buffer;
51 |
52 | outBuf = this.Compress(0, inBuf, out outLen, outBuf);
53 |
54 | minLength = outLen;
55 | buffer = new byte[outLen];
56 | for (uint i = 0; i < outLen; i++)
57 | {
58 | buffer[i] = outBuf[i];
59 | }
60 |
61 | for (byte j = 1; j < 16; j++)
62 | {
63 | outBuf = this.Compress(j, inBuf, out outLen, outBuf);
64 | if (outLen < minLength)
65 | {
66 | minLength = outLen;
67 | for (uint i = 0; i < outLen; i++)
68 | {
69 | buffer[i] = outBuf[i];
70 | }
71 | }
72 | }
73 |
74 | if (minLength < outLen)
75 | {
76 | outLen = minLength;
77 | for (uint i = 0; i < minLength; i++)
78 | {
79 | outBuf[i] = buffer[i];
80 | }
81 | }
82 |
83 | return outBuf;
84 | }
85 |
86 | ///
87 | /// Compresses provided data in S-DD1 format.
88 | ///
89 | /// S-DD1 header.
90 | /// data to compress.
91 | /// length of the compressed output.
92 | /// buffer to store compressed output.
93 | /// compressed S-DD1 data.
94 | public byte[] Compress(byte header, byte[] inBuf, out uint outLen, byte[] outBuf)
95 | {
96 | // Step 1
97 | for (byte i = 0; i < 8; i++)
98 | {
99 | this.bitplaneBuffer[i].Clear();
100 | }
101 |
102 | this.be.PrepareComp(inBuf, header);
103 | this.be.Launch();
104 |
105 | // Step 2
106 | this.codewordsSequence.Clear();
107 | for (byte i = 0; i < 8; i++)
108 | {
109 | this.codewordBuffer[i].Clear();
110 | }
111 |
112 | this.cm.PrepareComp(header);
113 | this.pem.PrepareComp(header, (ushort)inBuf.Length);
114 | this.gce.PrepareComp();
115 | this.pem.Launch();
116 |
117 | // Step 3
118 | this.interleaver.PrepareComp(header, outBuf);
119 | outBuf = this.interleaver.Launch(out outLen);
120 |
121 | return outBuf;
122 | }
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | [Xx]64/
19 | [Xx]86/
20 | [Bb]uild/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 |
85 | # Visual Studio profiler
86 | *.psess
87 | *.vsp
88 | *.vspx
89 | *.sap
90 |
91 | # TFS 2012 Local Workspace
92 | $tf/
93 |
94 | # Guidance Automation Toolkit
95 | *.gpState
96 |
97 | # ReSharper is a .NET coding add-in
98 | _ReSharper*/
99 | *.[Rr]e[Ss]harper
100 | *.DotSettings.user
101 |
102 | # JustCode is a .NET coding add-in
103 | .JustCode
104 |
105 | # TeamCity is a build add-in
106 | _TeamCity*
107 |
108 | # DotCover is a Code Coverage Tool
109 | *.dotCover
110 |
111 | # NCrunch
112 | _NCrunch_*
113 | .*crunch*.local.xml
114 | nCrunchTemp_*
115 |
116 | # MightyMoose
117 | *.mm.*
118 | AutoTest.Net/
119 |
120 | # Web workbench (sass)
121 | .sass-cache/
122 |
123 | # Installshield output folder
124 | [Ee]xpress/
125 |
126 | # DocProject is a documentation generator add-in
127 | DocProject/buildhelp/
128 | DocProject/Help/*.HxT
129 | DocProject/Help/*.HxC
130 | DocProject/Help/*.hhc
131 | DocProject/Help/*.hhk
132 | DocProject/Help/*.hhp
133 | DocProject/Help/Html2
134 | DocProject/Help/html
135 |
136 | # Click-Once directory
137 | publish/
138 |
139 | # Publish Web Output
140 | *.[Pp]ublish.xml
141 | *.azurePubxml
142 |
143 | # TODO: Un-comment the next line if you do not want to checkin
144 | # your web deploy settings because they may include unencrypted
145 | # passwords
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # NuGet Packages
150 | *.nupkg
151 | # The packages folder can be ignored because of Package Restore
152 | **/packages/*
153 | # except build/, which is used as an MSBuild target.
154 | !**/packages/build/
155 | # Uncomment if necessary however generally it will be regenerated when needed
156 | #!**/packages/repositories.config
157 | # NuGet v3's project.json files produces more ignoreable files
158 | *.nuget.props
159 | *.nuget.targets
160 |
161 | # Microsoft Azure Build Output
162 | csx/
163 | *.build.csdef
164 |
165 | # Microsoft Azure Emulator
166 | ecf/
167 | rcf/
168 |
169 | # Microsoft Azure ApplicationInsights config file
170 | ApplicationInsights.config
171 |
172 | # Windows Store app package directory
173 | AppPackages/
174 | BundleArtifacts/
175 |
176 | # Visual Studio cache files
177 | # files ending in .cache can be ignored
178 | *.[Cc]ache
179 | # but keep track of directories ending in .cache
180 | !*.[Cc]ache/
181 |
182 | # Others
183 | ClientBin/
184 | [Ss]tyle[Cc]op.*
185 | ~$*
186 | *~
187 | *.dbmdl
188 | *.dbproj.schemaview
189 | *.pfx
190 | *.publishsettings
191 | node_modules/
192 | orleans.codegen.cs
193 |
194 | # RIA/Silverlight projects
195 | Generated_Code/
196 |
197 | # Backup & report files from converting an old project file
198 | # to a newer Visual Studio version. Backup files are not needed,
199 | # because we have git ;-)
200 | _UpgradeReport_Files/
201 | Backup*/
202 | UpgradeLog*.XML
203 | UpgradeLog*.htm
204 |
205 | # SQL Server files
206 | *.mdf
207 | *.ldf
208 |
209 | # Business Intelligence projects
210 | *.rdl.data
211 | *.bim.layout
212 | *.bim_*.settings
213 |
214 | # Microsoft Fakes
215 | FakesAssemblies/
216 |
217 | # GhostDoc plugin setting file
218 | *.GhostDoc.xml
219 |
220 | # Node.js Tools for Visual Studio
221 | .ntvs_analysis.dat
222 |
223 | # Visual Studio 6 build log
224 | *.plg
225 |
226 | # Visual Studio 6 workspace options file
227 | *.opt
228 |
229 | # Visual Studio LightSwitch build output
230 | **/*.HTMLClient/GeneratedArtifacts
231 | **/*.DesktopClient/GeneratedArtifacts
232 | **/*.DesktopClient/ModelManifest.xml
233 | **/*.Server/GeneratedArtifacts
234 | **/*.Server/ModelManifest.xml
235 | _Pvt_Extensions
236 |
237 | # LightSwitch generated files
238 | GeneratedArtifacts/
239 | ModelManifest.xml
240 |
241 | # Paket dependency manager
242 | .paket/paket.exe
243 |
244 | # FAKE - F# Make
245 | .fake/
--------------------------------------------------------------------------------
/WiiuVcExtractor/FileTypes/RpxSectionHeader.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.FileTypes
2 | {
3 | using System.IO;
4 | using System.Text;
5 | using WiiuVcExtractor.Libraries;
6 |
7 | ///
8 | /// RPX section header.
9 | ///
10 | internal class RpxSectionHeader
11 | {
12 | ///
13 | /// Length of the section header in bytes.
14 | ///
15 | public const int SectionHeaderLength = 40;
16 |
17 | ///
18 | /// Flag indicating zlib compression.
19 | ///
20 | public const uint SectionHeaderRplZlib = 0x08000000;
21 |
22 | ///
23 | /// Flag indicating CRC section.
24 | ///
25 | public const uint SectionHeaderRplCrcs = 0x80000003;
26 |
27 | ///
28 | /// Section chunk size.
29 | ///
30 | public const uint ChunkSize = 16384;
31 |
32 | private readonly uint name;
33 | private readonly uint type;
34 | private readonly uint address;
35 | private readonly uint link;
36 | private readonly uint info;
37 | private readonly uint addrAlign;
38 | private readonly uint entSize;
39 | private uint offset;
40 | private uint size;
41 | private uint flags;
42 |
43 | ///
44 | /// Initializes a new instance of the class.
45 | ///
46 | /// bytes for the section to parse.
47 | public RpxSectionHeader(byte[] sectionBytes)
48 | {
49 | using MemoryStream ms = new MemoryStream(sectionBytes);
50 | using BinaryReader br = new BinaryReader(ms, new ASCIIEncoding());
51 |
52 | // Read in the header
53 | this.name = EndianUtility.ReadUInt32BE(br);
54 | this.type = EndianUtility.ReadUInt32BE(br);
55 | this.flags = EndianUtility.ReadUInt32BE(br);
56 | this.address = EndianUtility.ReadUInt32BE(br);
57 | this.offset = EndianUtility.ReadUInt32BE(br);
58 | this.size = EndianUtility.ReadUInt32BE(br);
59 | this.link = EndianUtility.ReadUInt32BE(br);
60 | this.info = EndianUtility.ReadUInt32BE(br);
61 | this.addrAlign = EndianUtility.ReadUInt32BE(br);
62 | this.entSize = EndianUtility.ReadUInt32BE(br);
63 | }
64 |
65 | ///
66 | /// Gets section name.
67 | ///
68 | public uint Name
69 | {
70 | get { return this.name; }
71 | }
72 |
73 | ///
74 | /// Gets section type.
75 | ///
76 | public uint Type
77 | {
78 | get { return this.type; }
79 | }
80 |
81 | ///
82 | /// Gets or sets section flags.
83 | ///
84 | public uint Flags
85 | {
86 | get { return this.flags; } set { this.flags = this.Flags; }
87 | }
88 |
89 | ///
90 | /// Gets address.
91 | ///
92 | public uint Address
93 | {
94 | get { return this.address; }
95 | }
96 |
97 | ///
98 | /// Gets or sets section offset.
99 | ///
100 | public uint Offset
101 | {
102 | get { return this.offset; } set { this.offset = this.Offset; }
103 | }
104 |
105 | ///
106 | /// Gets or sets section size.
107 | ///
108 | public uint Size
109 | {
110 | get { return this.size; } set { this.size = this.Size; }
111 | }
112 |
113 | ///
114 | /// Gets link.
115 | ///
116 | public uint Link
117 | {
118 | get { return this.link; }
119 | }
120 |
121 | ///
122 | /// Gets info.
123 | ///
124 | public uint Info
125 | {
126 | get { return this.info; }
127 | }
128 |
129 | ///
130 | /// Gets address alignment.
131 | ///
132 | public uint AddrAlign
133 | {
134 | get { return this.addrAlign; }
135 | }
136 |
137 | ///
138 | /// Gets entry size.
139 | ///
140 | public uint EntSize
141 | {
142 | get { return this.entSize; }
143 | }
144 |
145 | ///
146 | /// Creates string representation of the RPX section header.
147 | ///
148 | /// string representation of the RPX section header.
149 | public override string ToString()
150 | {
151 | return "RpxSectionHeader:\n" +
152 | "name: " + this.name.ToString() + "\n" +
153 | "type: " + this.type.ToString() + "\n" +
154 | "flags: " + this.flags.ToString() + "\n" +
155 | "address: 0x" + string.Format("{0:X}", this.address) + "\n" +
156 | "offset: 0x" + string.Format("{0:X}", this.offset) + "\n" +
157 | "size: " + this.size.ToString() + "\n" +
158 | "link: " + this.link.ToString() + "\n" +
159 | "info: " + this.info.ToString() + "\n" +
160 | "addrAlign: " + this.addrAlign.ToString() + "\n" +
161 | "entSize: " + this.entSize.ToString() + "\n";
162 | }
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/Libraries/Sdd1/ContextModel.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.Libraries.Sdd1
2 | {
3 | using System.Collections.Generic;
4 |
5 | ///
6 | /// S-DD1 context model.
7 | ///
8 | public class ContextModel
9 | {
10 | private readonly List[] bitplaneBuffer;
11 | private readonly byte[] bpBitInd;
12 | private readonly int[] byteIndex;
13 | private readonly ushort[] prevBitplaneBits;
14 | private byte bitplanesInfo;
15 | private byte contextBitsInfo;
16 | private byte bitNumber;
17 | private byte currBitplane;
18 |
19 | ///
20 | /// Initializes a new instance of the class.
21 | ///
22 | /// bitplane buffer.
23 | public ContextModel(ref List[] bpBuffer)
24 | {
25 | this.prevBitplaneBits = new ushort[8];
26 | this.bpBitInd = new byte[8];
27 | this.byteIndex = new int[8];
28 | this.bitplaneBuffer = bpBuffer;
29 | }
30 |
31 | ///
32 | /// Gets or sets associated Probability Estimation Module.
33 | ///
34 | public ProbabilityEstimationModule PEM { get; set; }
35 |
36 | ///
37 | /// Prepare for compression.
38 | ///
39 | /// S-DD1 header.
40 | public void PrepareComp(byte header)
41 | {
42 | this.bitplanesInfo = (byte)(header & 0x0c);
43 | this.contextBitsInfo = (byte)(header & 0x03);
44 | for (int i = 0; i < 8; i++)
45 | {
46 | this.byteIndex[i] = 0;
47 | this.bpBitInd[i] = 0;
48 | this.prevBitplaneBits[i] = 0;
49 | }
50 |
51 | this.bitNumber = 0;
52 | switch (this.bitplanesInfo)
53 | {
54 | case 0x00:
55 | this.currBitplane = 1;
56 | break;
57 | case 0x04:
58 | this.currBitplane = 7;
59 | break;
60 | case 0x08:
61 | this.currBitplane = 3;
62 | break;
63 | }
64 | }
65 |
66 | ///
67 | /// Gets bit from the bitplane.
68 | ///
69 | /// array with [bit, context].
70 | public byte[] GetBit()
71 | {
72 | byte bit;
73 | byte currContext;
74 |
75 | switch (this.bitplanesInfo)
76 | {
77 | case 0x00:
78 | this.currBitplane ^= 0x01;
79 | break;
80 | case 0x04:
81 | this.currBitplane ^= 0x01;
82 | if ((this.bitNumber & 0x7f) == 0)
83 | {
84 | this.currBitplane = (byte)((this.currBitplane + 2) & 0x07);
85 | }
86 |
87 | break;
88 | case 0x08:
89 | this.currBitplane ^= 0x01;
90 | if ((this.bitNumber & 0x7f) == 0)
91 | {
92 | this.currBitplane ^= 0x02;
93 | }
94 |
95 | break;
96 | case 0x0c:
97 | this.currBitplane = (byte)(this.bitNumber & 0x07);
98 | break;
99 | }
100 |
101 | currContext = (byte)((this.currBitplane & 0x01) << 4);
102 |
103 | switch (this.contextBitsInfo)
104 | {
105 | case 0x00:
106 | currContext |= (byte)(((this.prevBitplaneBits[this.currBitplane] & 0x01c0) >> 5) | (this.prevBitplaneBits[this.currBitplane] & 0x0001));
107 | break;
108 | case 0x01:
109 | currContext |= (byte)(((this.prevBitplaneBits[this.currBitplane] & 0x0180) >> 5) | (this.prevBitplaneBits[this.currBitplane] & 0x0001));
110 | break;
111 | case 0x02:
112 | currContext |= (byte)(((this.prevBitplaneBits[this.currBitplane] & 0x00c0) >> 5) | (this.prevBitplaneBits[this.currBitplane] & 0x0001));
113 | break;
114 | case 0x03:
115 | currContext |= (byte)(((this.prevBitplaneBits[this.currBitplane] & 0x0180) >> 5) | (this.prevBitplaneBits[this.currBitplane] & 0x0003));
116 | break;
117 | }
118 |
119 | if (this.byteIndex[this.currBitplane] == this.bitplaneBuffer[this.currBitplane].Count)
120 | {
121 | bit = this.PEM.GetMPS(currContext);
122 | }
123 | else
124 | {
125 | bit = 0;
126 |
127 | if ((this.bitplaneBuffer[this.currBitplane][this.byteIndex[this.currBitplane]] & (0x80 >> this.bpBitInd[this.currBitplane])) != 0)
128 | {
129 | bit = 1;
130 | }
131 |
132 | if (((++this.bpBitInd[this.currBitplane]) & 0x08) != 0)
133 | {
134 | this.bpBitInd[this.currBitplane] = 0;
135 | this.byteIndex[this.currBitplane]++;
136 | }
137 | }
138 |
139 | this.prevBitplaneBits[this.currBitplane] <<= 1;
140 | this.prevBitplaneBits[this.currBitplane] |= bit;
141 |
142 | this.bitNumber++;
143 |
144 | byte[] returnArray = new byte[2];
145 | returnArray[0] = bit;
146 | returnArray[1] = currContext;
147 |
148 | return returnArray;
149 | }
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/snesromnames.csv:
--------------------------------------------------------------------------------
1 | WUP-JCZE,AXELAY
2 | WUP-JCZP,Axelay
3 | WUP-JCZJ,AXELAY(アクスレイ)
4 | WUP-JBVP,Brawl Brothers
5 | WUP-JBVE,Brawl Brothers
6 | WUP-JCVE,BREATH OF FIRE
7 | WUP-JBEP,Breath of Fire II
8 | WUP-JBEE,Breath of Fire II
9 | WUP-JCVP,Breath of Fire
10 | WUP-JCDE,Castlevania Dracula X
11 | WUP-JCDP,Castlevania Dracula X
12 | WUP-JA5P,Contra III The Alien Wars
13 | WUP-JA5E,Contra III The Alien Wars
14 | WUP-JBNE,Cybernator
15 | WUP-JBNP,Cybernator
16 | WUP-JCWE,DEMONS CREST
17 | WUP-JCWP,Demons Crest
18 | WUP-JAGE,Donkey Kong Country 2 Diddys Kong Quest
19 | WUP-JAGP,Donkey Kong Country 2 Diddys Kong Quest
20 | WUP-JCXE,Donkey Kong Country 3 Dixie Kongs Double Trouble
21 | WUP-JCXP,Donkey Kong Country 3 Dixie Kongs Double Trouble
22 | WUP-JACE,Donkey Kong Country
23 | WUP-JACP,Donkey Kong Country
24 | WUP-JBBE,EarthBound
25 | WUP-JBBP,EarthBound
26 | WUP-JA8P,Final Fight
27 | WUP-JA8E,Final Fight
28 | WUP-JBLE,Final Fight 2
29 | WUP-JBLP,Final Fight 2
30 | WUP-JBUE,Final Fight 3
31 | WUP-JBUP,Final Fight 3
32 | WUP-JARJ,F-ZERO
33 | WUP-JARE,F-Zero
34 | WUP-JARP,F-ZERO
35 | WUP-JC7E,Genghis Khan II Clan of the Grey Wolf
36 | WUP-JBKE,Harvest Moon
37 | WUP-JBKP,Harvest Moon
38 | WUP-JAEE,Kirby Super Star
39 | WUP-JAEP,Kirby Super Star
40 | WUP-JASE,Kirbys Dream Course
41 | WUP-JASP,Kirbys Dream Course
42 | WUP-JANE,Kirbys Dream Land 3
43 | WUP-JANP,Kirbys Dream Land 3
44 | WUP-JAQP,Mario’s Super Picross
45 | WUP-JCNE,MEGA MAN 7
46 | WUP-JCNP,Mega Man 7
47 | WUP-JBAE,Mega Man X
48 | WUP-JBAP,Mega Man X
49 | WUP-JBTE,Mega Man X2
50 | WUP-JBTP,Mega Man X2
51 | WUP-JCPE,Mega Man X3
52 | WUP-JCPP,Mega Man X3
53 | WUP-JC3E,Metal Marines
54 | WUP-JBBJ,MOTHER2 ギーグの逆襲
55 | WUP-JCUE,Natsume Championship Wrestling
56 | WUP-JCUP,Natsume Championship Wrestling
57 | WUP-JCLE,Nobunagas Ambition
58 | WUP-JCLP,Nobunagas Ambition
59 | WUP-JC4E,PAC-ATTACK
60 | WUP-JC4P,Pac-Attack
61 | WUP-JDKE,Pac-Man 2 The New Adventures
62 | WUP-JDKP,Pac-Man 2 The New Adventures
63 | WUP-JA7E,Pilotwings
64 | WUP-JA7P,Pilotwings
65 | WUP-JCBP,Popn Twinbee
66 | WUP-JCFP,Popn TwinBee Rainbow Bell Adventures
67 | WUP-JCBJ,Popnツインビー
68 | WUP-JDBE,Rival Turf
69 | WUP-JBCE,Romance of the Three Kingdoms IV Wall of Fire
70 | WUP-JBCP,Romance of the three Kingdoms IV Wall of Fire
71 | WUP-JBVJ,RUSHING BEAT 乱 複製都市
72 | WUP-JAMP,Street Fighter II The World Warrior
73 | WUP-JCGE,Street Fighter Alpha 2
74 | WUP-JCGP,Street Fighter Alpha 2
75 | WUP-JAYE,Street Fighter II Turbo Hyper Fighting
76 | WUP-JAYP,Street Fighter II Turbo Hyper Fighting
77 | WUP-JAME,Street Fighter II The World Warrior
78 | WUP-JA9P,Super Castlevania IV
79 | WUP-JA9E,Super Castlevania IV
80 | WUP-JDAJ,SUPER E.D.F. EARTH DEFENSE FORCE
81 | WUP-JDAE,SUPER E.D.F. Earth Defense Force
82 | WUP-JATE,Super Ghoulsn Ghosts
83 | WUP-JATP,Super Ghoulsn Ghosts
84 | WUP-JAKE,Super Mario Kart
85 | WUP-JAKP,Super Mario Kart
86 | WUP-JABE,Super Mario RPG Legend of the Seven Stars
87 | WUP-JABP,Super Mario RPG Legend of the Seven Stars
88 | WUP-JAAE,Super Mario World
89 | WUP-JAAP,Super Mario World
90 | WUP-JAJP,Super Metroid
91 | WUP-JAJE,Super Metroid
92 | WUP-JB8E,Super Punch-Out!!
93 | WUP-JB8P,Super Punch-Out!!
94 | WUP-JAVE,Super Street Fighter II The New Challengers
95 | WUP-JAVP,Super Street FighterⅡ The New Challengers
96 | WUP-JCLJ,SUPER 信長の野望・全国版
97 | WUP-JENE,THE IGNITION FACTOR
98 | WUP-JALP,The Legend of The Mystical Ninja
99 | WUP-JALE,The Legend of The Mystical Ninja
100 | WUP-JADP,The Legend of Zelda A Link to the Past
101 | WUP-JADE,The Legend of Zelda A Link to the Past
102 | WUP-JBQP,Uncharted Waters New Horizons
103 | WUP-JBQE,Uncharted Waters New Horizons
104 | WUP-JBJE,Vegas Stakes
105 | WUP-JBJP,Vegas Stakes
106 | WUP-JCTE,Wild Guns
107 | WUP-JCTP,Wild Guns
108 | WUP-JC6J,アルバートオデッセイ
109 | WUP-JAWJ,カービィのきらきらきっず
110 | WUP-JASJ,カービィボウル
111 | WUP-JAZJ,かまいたちの夜
112 | WUP-JALJ,がんばれゴエモン ゆき姫救出絵巻
113 | WUP-JAUJ,がんばれゴエモン2 奇天烈将軍マッギネス
114 | WUP-JAXJ,がんばれゴエモン3 獅子重禄兵衛のからくり卍固め
115 | WUP-JCKJ,くにおくんのドッジボールだよ全員集合!
116 | WUP-JBWJ,クロックタワー
117 | WUP-JC4J,コズモギャング ザ パズル
118 | WUP-JC2J,すーぱーぐっすんおよよ
119 | WUP-JAVJ,スーパーストリートファイターⅡ ザ ニューチャレンジャーズ
120 | WUP-JCJJ,スーパーチャイニーズワールド
121 | WUP-JACJ,スーパードンキーコング
122 | WUP-JAGJ,スーパードンキーコング2 ディクシー&ディディー
123 | WUP-JCXJ,スーパードンキーコング3 謎のクレミス島
124 | WUP-JB8J,スーパーパンチアウト!!
125 | WUP-JBGJ,スーパーファミコンウォーズ
126 | WUP-JABJ,スーパーマリオRPG
127 | WUP-JAKJ,スーパーマリオカート
128 | WUP-JAAJ,スーパーマリオワールド
129 | WUP-JAJJ,スーパーメトロイド
130 | WUP-JBMJ,スーパーワギャンランド
131 | WUP-JC7J,スーパー蒼き狼と白き牝鹿 元朝秘史
132 | WUP-JCRJ,すってはっくん
133 | WUP-JAMJ,ストリートファイターⅡ ザ ワールド ウォーリアー
134 | WUP-JAYJ,ストリートファイターⅡ ターボ ハイパー ファイティング
135 | WUP-JCGJ,ストリートファイターZERO2
136 | WUP-JADJ,ゼルダの伝説 神々のトライフォース
137 | WUP-JB4J,タクティクスオウガ
138 | WUP-JCFJ,ツインビー レインボーベルアドベンチャー
139 | WUP-JCWJ,デモンズブレイゾン 魔界村 紋章編
140 | WUP-JA7J,パイロットウイングス
141 | WUP-JBHJ,はじまりの森
142 | WUP-JA3J,パネルでポン
143 | WUP-JCAJ,バハムート ラグーン
144 | WUP-JBFJ,ファイアーエムブレム トラキア776
145 | WUP-JAHJ,ファイアーエムブレム 紋章の謎
146 | WUP-JAFJ,ファイアーエムブレム 聖戦の系譜
147 | WUP-JA8J,ファイナルファイト
148 | WUP-JBUJ,ファイナルファイト タフ
149 | WUP-JBLJ,ファイナルファイト2
150 | WUP-JBZJ,ファイナルファンタジーIV
151 | WUP-JB7J,ファイナルファンタジーUSA ミスティッククエスト
152 | WUP-JB6J,ファイナルファンタジーV
153 | WUP-JBYJ,ファイナルファンタジーVI
154 | WUP-JA6J,ファミコン探偵倶楽部 PARTⅡ うしろに立つ少女(スーパーファミコン版)
155 | WUP-JCVJ,ブレス オブ ファイア 竜の戦士
156 | WUP-JBEJ,ブレス オブ ファイアⅡ 使命の子
157 | WUP-JA2J,ヘラクレスの栄光Ⅲ 神々の沈黙
158 | WUP-JCYJ,ヘラクレスの栄光Ⅳ 神々からの贈り物
159 | WUP-JCCJ,マーヴェラス ~もうひとつの宝島~
160 | WUP-JAQJ,マリオのスーパーピクロス
161 | WUP-JC3J,ミリティア
162 | WUP-JDLJ,メタルスレイダーグローリー ディレクターズカット
163 | WUP-JC5J,ライブ・ア・ライブ
164 | WUP-JCHJ,ラストバイブルⅢ
165 | WUP-JDBJ,ラッシング・ビート
166 | WUP-JDGJ,ルドラの秘宝
167 | WUP-JCNJ,ロックマン7 宿命の対決!
168 | WUP-JBAJ,ロックマンX
169 | WUP-JBTJ,ロックマンX2
170 | WUP-JCPJ,ロックマンX3
171 | WUP-JB3J,ロマンシング サ・ガ
172 | WUP-JB5J,ロマンシング サ・ガ2
173 | WUP-JB9J,ロマンシング サ・ガ3
174 | WUP-JBCJ,三國志Ⅳ
175 | WUP-JB2J,伝説のオウガバトル
176 | WUP-JBQJ,大航海時代Ⅱ
177 | WUP-JC8J,太閤立志伝
178 | WUP-JCSJ,学校であった怖い話
179 | WUP-JCMJ,平成 新・鬼ヶ島 前編
180 | WUP-JCQJ,平成 新・鬼ヶ島 後編
181 | WUP-JCEJ,弟切草
182 | WUP-JA9J,悪魔城ドラキュラ
183 | WUP-JCDJ,悪魔城ドラキュラXX
184 | WUP-JAEJ,星のカービィ スーパーデラックス
185 | WUP-JANJ,星のカービィ3
186 | WUP-JA4J,真・女神転生
187 | WUP-JBRJ,真・女神転生Ⅱ
188 | WUP-JBSJ,真・女神転生if...
189 | WUP-JBXJ,聖剣伝説2
190 | WUP-JEPJ,豪血寺一族
191 | WUP-JATJ,超魔界村
192 | WUP-JBNJ,重装機兵ヴァルケン
193 | WUP-JA5J,魂斗羅スピリッツ
194 | WUP-JC9J,魔神転生
--------------------------------------------------------------------------------
/WiiuVcExtractor/Libraries/Sdd1/ProbabilityEstimationModule.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.Libraries.Sdd1
2 | {
3 | ///
4 | /// Probability Estimation Module for S-DD1 compression.
5 | ///
6 | public class ProbabilityEstimationModule
7 | {
8 | private readonly ContextModel cm;
9 | private readonly GolombCodeEncoder gce;
10 | private readonly State[] stateEvolutionTable;
11 | private readonly SDD1ContextInfo[] contextInfo;
12 | private uint inputLength;
13 |
14 | ///
15 | /// Initializes a new instance of the class.
16 | ///
17 | /// associated context model.
18 | /// associated golomb code encoder.
19 | public ProbabilityEstimationModule(ref ContextModel associatedCM, ref GolombCodeEncoder associatedGCE)
20 | {
21 | this.cm = associatedCM;
22 | this.cm.PEM = this;
23 | this.gce = associatedGCE;
24 | this.contextInfo = new SDD1ContextInfo[32];
25 |
26 | this.stateEvolutionTable = new State[]
27 | {
28 | new State(0, 25, 25),
29 | new State(0, 2, 1),
30 | new State(0, 3, 1),
31 | new State(0, 4, 2),
32 | new State(0, 5, 3),
33 | new State(1, 6, 4),
34 | new State(1, 7, 5),
35 | new State(1, 8, 6),
36 | new State(1, 9, 7),
37 | new State(2, 10, 8),
38 | new State(2, 11, 9),
39 | new State(2, 12, 10),
40 | new State(2, 13, 11),
41 | new State(3, 14, 12),
42 | new State(3, 15, 13),
43 | new State(3, 16, 14),
44 | new State(3, 17, 15),
45 | new State(4, 18, 16),
46 | new State(4, 19, 17),
47 | new State(5, 20, 18),
48 | new State(5, 21, 19),
49 | new State(6, 22, 20),
50 | new State(6, 23, 21),
51 | new State(7, 24, 22),
52 | new State(7, 24, 23),
53 | new State(0, 26, 1),
54 | new State(1, 27, 2),
55 | new State(2, 28, 4),
56 | new State(3, 29, 8),
57 | new State(4, 30, 12),
58 | new State(5, 31, 16),
59 | new State(6, 32, 18),
60 | new State(7, 24, 22),
61 | };
62 | }
63 |
64 | ///
65 | /// Prepare for compression.
66 | ///
67 | /// S-DD1 header.
68 | /// data length.
69 | public void PrepareComp(byte header, ushort length)
70 | {
71 | for (byte i = 0; i < 32; i++)
72 | {
73 | this.contextInfo[i].Status = 0;
74 | this.contextInfo[i].MPS = 0;
75 | }
76 |
77 | this.inputLength = length;
78 | if (((header & 0x0c) != 0x0c) && ((length & 0x0001) != 0))
79 | {
80 | this.inputLength++;
81 | }
82 |
83 | this.inputLength <<= 3;
84 | }
85 |
86 | ///
87 | /// Get MPS value for given context ID.
88 | ///
89 | /// context ID.
90 | /// MPS value.
91 | public byte GetMPS(byte context)
92 | {
93 | return this.contextInfo[context].MPS;
94 | }
95 |
96 | ///
97 | /// Launches the probability estimation module.
98 | ///
99 | public void Launch()
100 | {
101 | byte bit;
102 | byte context;
103 | byte currStatus;
104 | State currState;
105 |
106 | for (uint i = 0; i < this.inputLength; i++)
107 | {
108 | byte[] bitReturnBuffer = this.cm.GetBit();
109 | bit = bitReturnBuffer[0];
110 | context = bitReturnBuffer[1];
111 | currStatus = this.contextInfo[context].Status;
112 | currState = this.stateEvolutionTable[currStatus];
113 | bit ^= this.contextInfo[context].MPS;
114 | byte endOfRun = this.gce.PutBit(currState.CodeNum, bit);
115 | if (endOfRun != 0)
116 | {
117 | if (bit != 0)
118 | {
119 | if ((currStatus & 0xfe) == 0)
120 | {
121 | this.contextInfo[context].MPS ^= 0x01;
122 | }
123 |
124 | this.contextInfo[context].Status = currState.NextIfLPS;
125 | }
126 | else
127 | {
128 | this.contextInfo[context].Status = currState.NextIfMPS;
129 | }
130 | }
131 | }
132 |
133 | this.gce.FinishComp();
134 | }
135 |
136 | ///
137 | /// Struct representing the current PEM state.
138 | ///
139 | private struct State
140 | {
141 | public byte CodeNum;
142 | public byte NextIfMPS;
143 | public byte NextIfLPS;
144 |
145 | public State(byte code, byte mps, byte lps)
146 | {
147 | this.CodeNum = code;
148 | this.NextIfMPS = mps;
149 | this.NextIfLPS = lps;
150 | }
151 | }
152 |
153 | ///
154 | /// Struct representing context information for the S-DD1.
155 | ///
156 | private struct SDD1ContextInfo
157 | {
158 | public byte Status;
159 | public byte MPS;
160 |
161 | public SDD1ContextInfo(byte stat, byte mps)
162 | {
163 | this.Status = stat;
164 | this.MPS = mps;
165 | }
166 | }
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/FileTypes/PkgHeader.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.FileTypes
2 | {
3 | using System.IO;
4 | using System.Text;
5 | using WiiuVcExtractor.Libraries;
6 |
7 | ///
8 | /// .pkg file header for PC Engine games.
9 | ///
10 | public class PkgHeader
11 | {
12 | private const int EntryPointLength = 0x40;
13 | private const int OptionsLength = 0x20;
14 |
15 | private readonly uint pkgLength;
16 | private readonly uint headerContentLength;
17 | private readonly string headerFilename;
18 |
19 | // Appears to be flags for the emulator, but unclear as to the specific purpose
20 | private readonly byte[] options;
21 |
22 | private readonly string entryPoint;
23 | private readonly byte[] entryPointBytes;
24 |
25 | // Appears to be identical to the first entry point in most cases, but more data is needed
26 | private readonly string entryPoint2;
27 | private readonly byte[] entryPoint2Bytes;
28 |
29 | // Store the total length of the file for later validation
30 | private readonly long fileLength;
31 |
32 | ///
33 | /// Initializes a new instance of the class.
34 | ///
35 | /// path to the .pkg file.
36 | public PkgHeader(string pkgFilePath)
37 | {
38 | // read in the pceconfig.bin information and interpret it as the file header
39 | using (FileStream fs = new FileStream(pkgFilePath, FileMode.Open, FileAccess.Read))
40 | {
41 | using BinaryReader br = new BinaryReader(fs, new ASCIIEncoding());
42 |
43 | // Read in the header (Add 4 to consider the length part of the size)
44 | this.pkgLength = br.ReadUInt32LE() + 4;
45 |
46 | // Add 4 to calculate the header length since we are considering the pkgLength to be part of it (for easier offset calculation elsewhere)
47 | this.headerContentLength = br.ReadUInt32LE();
48 | this.headerFilename = br.ReadNullTerminatedString();
49 |
50 | this.options = br.ReadBytes(OptionsLength);
51 |
52 | this.entryPointBytes = br.ReadBytes(EntryPointLength);
53 | this.entryPoint2Bytes = br.ReadBytes(EntryPointLength);
54 |
55 | // Parse the entry point name from each set of bytes
56 | this.entryPoint = this.entryPointBytes.ReadNullTerminatedString();
57 | this.entryPoint2 = this.entryPoint2Bytes.ReadNullTerminatedString();
58 | }
59 |
60 | this.fileLength = new FileInfo(pkgFilePath).Length;
61 | }
62 |
63 | ///
64 | /// Gets the length of the .pkg file.
65 | ///
66 | public uint PkgLength
67 | {
68 | get { return this.pkgLength; }
69 | }
70 |
71 | ///
72 | /// Gets the length of the .pkg file header.
73 | ///
74 | public uint Length
75 | {
76 | get { return this.headerContentLength + (uint)this.headerFilename.Length + 9; }
77 | }
78 |
79 | ///
80 | /// Gets the filename from the .pkg file header.
81 | ///
82 | public string Filename
83 | {
84 | get { return this.headerFilename; }
85 | }
86 |
87 | ///
88 | /// Gets the options of the .pkg file.
89 | ///
90 | public byte[] Options
91 | {
92 | get { return this.options; }
93 | }
94 |
95 | ///
96 | /// Gets the first entry point of the .pkg file.
97 | ///
98 | public string EntryPoint
99 | {
100 | get { return this.entryPoint; }
101 | }
102 |
103 | ///
104 | /// Gets the second entry point of the .pkg file.
105 | ///
106 | public string EntryPoint2
107 | {
108 | get { return this.entryPoint2; }
109 | }
110 |
111 | ///
112 | /// Whether the .pkg file header is valid.
113 | ///
114 | /// true if valid, false otherwise.
115 | public bool IsValid()
116 | {
117 | // Ensure the interpreted length from the header matches the actual file length
118 | if (this.pkgLength != this.fileLength)
119 | {
120 | return false;
121 | }
122 |
123 | // Ensure the header length is non-zero
124 | if (this.headerContentLength < 1)
125 | {
126 | return false;
127 | }
128 |
129 | // Ensure header filename is populated
130 | if (string.IsNullOrEmpty(this.headerFilename))
131 | {
132 | return false;
133 | }
134 |
135 | // Ensure entry point is populated
136 | if (string.IsNullOrEmpty(this.entryPoint))
137 | {
138 | return false;
139 | }
140 |
141 | // Ensure entry point 2 is populated
142 | if (string.IsNullOrEmpty(this.entryPoint2))
143 | {
144 | return false;
145 | }
146 |
147 | return true;
148 | }
149 |
150 | ///
151 | /// Generates a string summary of the .pkg file header.
152 | ///
153 | /// string summary of the .pkg file header.
154 | public override string ToString()
155 | {
156 | return "PkgHeader:\n" +
157 | "pkgLength: " + this.pkgLength.ToString() + "\n" +
158 | "headerLength" + this.Length.ToString() + "\n" +
159 | "headerContentLength: " + this.headerContentLength.ToString() + "\n" +
160 | "headerFilename: " + this.headerFilename + "\n" +
161 | "entryPoint: " + this.entryPoint + "\n" +
162 | "entryPoint2: " + this.entryPoint2 + "\n";
163 | }
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/Libraries/SnesSdd1Extractor.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.Libraries
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Text;
8 | using WiiuVcExtractor.Libraries.Sdd1;
9 |
10 | ///
11 | /// SNES S-DD1 extractor (recompresses S-DD1 data into the SNES rom).
12 | ///
13 | public class SnesSdd1Extractor
14 | {
15 | private static readonly byte[] Sdd1Signature = { 0x53, 0x44, 0x44, 0x31 }; // SDD1 ASCII
16 | private readonly byte[] romData;
17 | private readonly byte[] rawSdd1Data;
18 | private readonly long sdd1DataOffset;
19 | private readonly List sdd1Pointers;
20 |
21 | ///
22 | /// Initializes a new instance of the class.
23 | ///
24 | /// SNES rom data.
25 | /// Decompressed S-DD1 data.
26 | /// Offset to the beginning of the S-DD1 data in rawSdd1Data.
27 | public SnesSdd1Extractor(byte[] snesRomData, byte[] rawSdd1Data, long sdd1DataOffset)
28 | {
29 | this.sdd1DataOffset = sdd1DataOffset;
30 | this.romData = snesRomData;
31 | this.rawSdd1Data = rawSdd1Data;
32 | this.sdd1Pointers = new List();
33 | }
34 |
35 | ///
36 | /// Extracts the decompressed S-DD1 data, compresses it, and injects it into the SNES rom data.
37 | ///
38 | /// processed SNES rom data.
39 | public byte[] ExtractSdd1Data()
40 | {
41 | Console.WriteLine("Extracting S-DD1 Data...");
42 |
43 | Console.WriteLine("Appended rom data size: {0}", this.rawSdd1Data.Length);
44 |
45 | byte[] processedRom = new byte[this.romData.Length];
46 | Array.Copy(this.romData, processedRom, this.romData.Length);
47 |
48 | // Find all SDD1 signatures in the rom and store them
49 | using (MemoryStream ms = new MemoryStream(this.romData))
50 | {
51 | using BinaryReader br = new BinaryReader(ms, new ASCIIEncoding());
52 |
53 | // Continue reading SDD1 pointer data until we run out
54 | while (true)
55 | {
56 | long index = br.BaseStream.Position;
57 |
58 | if (index + Sdd1Signature.Length >= br.BaseStream.Length)
59 | {
60 | break;
61 | }
62 |
63 | byte[] signatureBuffer = br.ReadBytes(Sdd1Signature.Length);
64 |
65 | // If we didn't find a signature at this location, seek to the next character and restart the loop
66 | if (!signatureBuffer.SequenceEqual(Sdd1Signature))
67 | {
68 | br.BaseStream.Seek(-3, SeekOrigin.Current);
69 | continue;
70 | }
71 |
72 | // Store the pointer location in the rom (PointerLocation) and the
73 | // offset in the rawSdd1Data (DataLocation)
74 | // We must add the sdd1DataOffset to the read location offset to get to the right
75 | // position in the rawSdd1Data (appendedData)
76 | this.sdd1Pointers.Add(new Sdd1Pointer(index, br.ReadUInt32LE() + this.sdd1DataOffset));
77 |
78 | // Set the previous data length based on the current offset - the last offset
79 | if (this.sdd1Pointers.Count > 1)
80 | {
81 | this.sdd1Pointers[^2].DataLength = this.sdd1Pointers[^1].DataLocation - this.sdd1Pointers[^2].DataLocation;
82 | }
83 | }
84 | }
85 |
86 | if (this.sdd1Pointers.Count == 0)
87 | {
88 | Console.WriteLine("No S-DD1 data found, continuing...");
89 | return processedRom;
90 | }
91 |
92 | this.sdd1Pointers[^1].DataLength = this.rawSdd1Data.Length - this.sdd1Pointers[^1].DataLocation;
93 |
94 | Console.WriteLine("{0} S-DD1 pointers found", this.sdd1Pointers.Count);
95 | Console.WriteLine("Reading S-DD1 data into memory...");
96 |
97 | Compressor compressor = new Compressor();
98 |
99 | byte[] outBuffer = new byte[0x40000];
100 |
101 | int pointerCount = 0;
102 |
103 | // Find the decompressed data for each pointer, compress it, and replace the first 8 bytes of each pointer location (SDD1UINT becomes the compressed data)
104 | foreach (var sdd1ptr in this.sdd1Pointers)
105 | {
106 | pointerCount++;
107 |
108 | // Output a period each 50 pointers to indicate progress
109 | if (pointerCount % 50 == 0)
110 | {
111 | Console.Write(".");
112 | }
113 |
114 | // Read the decompressed data
115 | byte[] decompressedData = new byte[sdd1ptr.DataLength];
116 | using (MemoryStream ms = new MemoryStream(this.rawSdd1Data))
117 | {
118 | using BinaryReader br = new BinaryReader(ms, new ASCIIEncoding());
119 | br.BaseStream.Seek(sdd1ptr.DataLocation, SeekOrigin.Begin);
120 | decompressedData = br.ReadBytes((int)sdd1ptr.DataLength);
121 | }
122 |
123 | // Compress the decompressed data
124 | byte[] compressedData = compressor.Compress(decompressedData, out uint outLength, outBuffer);
125 |
126 | // Replace with the recompressed data
127 | using MemoryStream msb = new MemoryStream(processedRom);
128 | using BinaryWriter bw = new BinaryWriter(msb, new ASCIIEncoding());
129 | bw.BaseStream.Seek(sdd1ptr.PointerLocation, SeekOrigin.Begin);
130 | bw.Write(compressedData, 0, (int)outLength);
131 | }
132 |
133 | Console.WriteLine();
134 | Console.WriteLine("Replaced all S-DD1 pointers with compressed S-DD1 data");
135 |
136 | return processedRom;
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/FileTypes/PsbHeader.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.FileTypes
2 | {
3 | using System;
4 | using System.IO;
5 | using System.Text;
6 | using WiiuVcExtractor.Libraries;
7 |
8 | ///
9 | /// PSB file header.
10 | ///
11 | public class PsbHeader
12 | {
13 | ///
14 | /// Length of a PSB file header in bytes.
15 | ///
16 | public const int PsbHeaderLength = 40;
17 |
18 | private const int PsbSignatureLength = 4;
19 |
20 | /*
21 | // Header offsets and lengths (currently unused)
22 | // private const int HeaderTitleOffset = 0;
23 | // private const int HeaderTitleLength = 21;
24 | // private const int HeaderRomSizeOffset = 23;
25 | // private const int HeaderSramSizeOffset = 24;
26 | // private const int HeaderFixedValueOffset = 26;
27 | // private const int HeaderChecksumComplementOffset = 28;
28 | // private const int HeaderChecksumOffset = 30;
29 | // private const int HeaderTypeOffset = 4;
30 | // private const int HeaderUnknownOffset = 8;
31 | // private const int HeaderNamesOffset = 12;
32 | // private const int HeaderStringsOffset = 16;
33 | // private const int HeaderStringsDataOffset = 20;
34 | // private const int HeaderChunkOffsetsOffset = 24;
35 | // private const int HeaderChunkLengthsOffset = 28;
36 | // private const int HeaderChunkDataOffset = 32;
37 | // private const int HeaderEntriesOffset = 36;
38 | */
39 |
40 | private static readonly byte[] PsbSignature = { 0x50, 0x53, 0x42, 0x00 };
41 |
42 | private readonly byte[] signature;
43 | private readonly uint type;
44 | private readonly uint unknown;
45 | private readonly uint namesOffset;
46 | private readonly uint stringsOffset;
47 | private readonly uint stringsDataOffset;
48 | private readonly uint chunkOffsetsOffset;
49 | private readonly uint chunkLengthsOffset;
50 | private readonly uint chunkDataOffset;
51 | private readonly uint entriesOffset;
52 |
53 | ///
54 | /// Initializes a new instance of the class.
55 | ///
56 | /// path to the PSB file.
57 | public PsbHeader(string psbPath)
58 | {
59 | this.signature = new byte[PsbSignatureLength];
60 |
61 | using FileStream fs = new FileStream(psbPath, FileMode.Open, FileAccess.Read);
62 | using BinaryReader br = new BinaryReader(fs, new ASCIIEncoding());
63 |
64 | // Read in the header
65 | this.signature = br.ReadBytes(PsbSignatureLength);
66 | this.type = EndianUtility.ReadUInt32LE(br);
67 | this.unknown = EndianUtility.ReadUInt32LE(br);
68 | this.namesOffset = EndianUtility.ReadUInt32LE(br);
69 | this.stringsOffset = EndianUtility.ReadUInt32LE(br);
70 | this.stringsDataOffset = EndianUtility.ReadUInt32LE(br);
71 | this.chunkOffsetsOffset = EndianUtility.ReadUInt32LE(br);
72 | this.chunkLengthsOffset = EndianUtility.ReadUInt32LE(br);
73 | this.chunkDataOffset = EndianUtility.ReadUInt32LE(br);
74 | this.entriesOffset = EndianUtility.ReadUInt32LE(br);
75 | }
76 |
77 | ///
78 | /// Gets PSB file names offset in bytes.
79 | ///
80 | public uint NamesOffset
81 | {
82 | get { return this.namesOffset; }
83 | }
84 |
85 | ///
86 | /// Gets PSB file strings offset in bytes.
87 | ///
88 | public uint StringsOffset
89 | {
90 | get { return this.stringsOffset; }
91 | }
92 |
93 | ///
94 | /// Gets PSB file strings data offset in bytes.
95 | ///
96 | public uint StringsDataOffset
97 | {
98 | get { return this.stringsDataOffset; }
99 | }
100 |
101 | ///
102 | /// Gets PSB file chunk offsets offset in bytes.
103 | ///
104 | public uint ChunkOffsetsOffset
105 | {
106 | get { return this.chunkOffsetsOffset; }
107 | }
108 |
109 | ///
110 | /// Gets PSB file chunk lengths offset in bytes.
111 | ///
112 | public uint ChunkLengthsOffset
113 | {
114 | get { return this.chunkLengthsOffset; }
115 | }
116 |
117 | ///
118 | /// Gets PSB file chunk data offset in bytes.
119 | ///
120 | public uint ChunkDataOffset
121 | {
122 | get { return this.chunkDataOffset; }
123 | }
124 |
125 | ///
126 | /// Gets PSB file entries offset in bytes.
127 | ///
128 | public uint EntriesOffset
129 | {
130 | get { return this.entriesOffset; }
131 | }
132 |
133 | ///
134 | /// whether the PSB header is valid.
135 | ///
136 | /// true if valid, false otherwise.
137 | public bool IsValid()
138 | {
139 | // Check that the signature is correct
140 | if (this.signature[0] != PsbSignature[0] ||
141 | this.signature[1] != PsbSignature[1] ||
142 | this.signature[2] != PsbSignature[2] ||
143 | this.signature[3] != PsbSignature[3])
144 | {
145 | return false;
146 | }
147 |
148 | return true;
149 | }
150 |
151 | ///
152 | /// Provides a string representation of the PSB header.
153 | ///
154 | /// string representation of the PSB header.
155 | public override string ToString()
156 | {
157 | return "PsbHeader:\n" +
158 | "signature: " + BitConverter.ToString(this.signature) + "\n" +
159 | "type: " + this.type.ToString() + "\n" +
160 | "unknown: " + this.unknown.ToString() + "\n" +
161 | "namesOffset: " + this.namesOffset.ToString() + "\n" +
162 | "stringsOffset: " + this.stringsOffset.ToString() + "\n" +
163 | "stringsDataOffset: " + this.stringsDataOffset.ToString() + "\n" +
164 | "chunkOffsetsOffset: " + this.chunkOffsetsOffset.ToString() + "\n" +
165 | "chunkLengthsOffset: " + this.chunkLengthsOffset.ToString() + "\n" +
166 | "chunkDataOffset: " + this.chunkDataOffset.ToString() + "\n" +
167 | "entriesOffset: " + this.entriesOffset.ToString() + "\n";
168 | }
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/Program.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using WiiuVcExtractor.FileTypes;
7 | using WiiuVcExtractor.RomExtractors;
8 |
9 | ///
10 | /// Main entrypoint for Wii U VC Extractor CLI.
11 | ///
12 | public class Program
13 | {
14 | private const string WiiUVcExtractorVersion = "2.0.1";
15 |
16 | ///
17 | /// Prints usage information.
18 | ///
19 | public static void PrintUsage()
20 | {
21 | Console.WriteLine("=====================================");
22 | Console.WriteLine("Wii U Virtual Console Extractor " + WiiUVcExtractorVersion);
23 | Console.WriteLine("=====================================");
24 | Console.WriteLine("Extracts roms from Virtual Console games dumped by DDD or from the SNES Mini.");
25 | Console.WriteLine(string.Empty);
26 | Console.WriteLine("Usage:");
27 | Console.WriteLine("wiiuvcextractor [-v] [rpx_or_psb.m_file]");
28 | Console.WriteLine(" - Extract a rom from a Virtual Console dump");
29 | Console.WriteLine(string.Empty);
30 | Console.WriteLine("wiiuvcextractor --version");
31 | Console.WriteLine(" - Display current version");
32 | Console.WriteLine(string.Empty);
33 | Console.WriteLine(string.Empty);
34 | Console.WriteLine("Usage Examples:");
35 | Console.WriteLine("wiiuvcextractor alldata.psb.m");
36 | Console.WriteLine("wiiuvcextractor WUP-FAME.rpx");
37 | Console.WriteLine("wiiuvcextractor CLV-P-SAAAE.sfrom");
38 | Console.WriteLine("wiiuvcextractor pce.pkg");
39 | Console.WriteLine("wiiuvcextractor -v WUP-JBBE.rpx");
40 | }
41 |
42 | ///
43 | /// Prints version information.
44 | ///
45 | public static void PrintVersion()
46 | {
47 | Console.WriteLine(WiiUVcExtractorVersion);
48 | }
49 |
50 | ///
51 | /// Main entrypoint.
52 | ///
53 | /// user-provided arguments.
54 | public static void Main(string[] args)
55 | {
56 | if (args.Length == 0 || args.Length > 2)
57 | {
58 | PrintUsage();
59 | return;
60 | }
61 |
62 | if (args.Length == 1)
63 | {
64 | if (args[0] == "--version")
65 | {
66 | PrintVersion();
67 | return;
68 | }
69 | }
70 |
71 | bool verbose = false;
72 | if (args[0] == "-v")
73 | {
74 | verbose = true;
75 | Console.WriteLine("Verbose output mode is set");
76 | }
77 |
78 | string sourcePath = args[^1];
79 |
80 | if (verbose)
81 | {
82 | Console.WriteLine("Source extract file is " + Path.GetFullPath(sourcePath));
83 | }
84 |
85 | if (!File.Exists(sourcePath))
86 | {
87 | Console.WriteLine("Could not find file at " + sourcePath + ". Please ensure that your filename is correct.");
88 | return;
89 | }
90 |
91 | Console.WriteLine("============================================================================");
92 | Console.WriteLine("Starting extraction of rom from " + sourcePath + "...");
93 | Console.WriteLine("============================================================================");
94 |
95 | string extractedRomPath = string.Empty;
96 |
97 | RpxFile rpxFile = null;
98 | PsbFile psbFile = null;
99 | PkgFile pkgFile = null;
100 | SrlFile srlFile = null;
101 |
102 | // Identifies filetype of the file argument,
103 | // then instantiates file with file's location and verbose
104 | if (RpxFile.IsRpx(sourcePath))
105 | {
106 | Console.WriteLine("RPX file detected!");
107 | rpxFile = new RpxFile(sourcePath, verbose);
108 | }
109 | else if (PsbFile.IsPsb(sourcePath))
110 | {
111 | Console.WriteLine("PSB file detected!");
112 | psbFile = new PsbFile(sourcePath, verbose);
113 | }
114 | else if (PkgFile.IsPkg(sourcePath))
115 | {
116 | pkgFile = new PkgFile(sourcePath, verbose);
117 | }
118 | else if (SrlFile.IsSrl(sourcePath))
119 | {
120 | Console.WriteLine("SRL file detected!");
121 | srlFile = new SrlFile(sourcePath, verbose);
122 | }
123 |
124 | // Create the list of rom extractors
125 | List romExtractors = new List();
126 |
127 | if (rpxFile != null)
128 | {
129 | romExtractors.Add(new NesVcExtractor(rpxFile, verbose));
130 | romExtractors.Add(new SnesVcExtractor(rpxFile.DecompressedPath, verbose));
131 | romExtractors.Add(new FdsVcExtractor(rpxFile, verbose));
132 | }
133 | else if (psbFile != null)
134 | {
135 | romExtractors.Add(new GbaVcExtractor(psbFile, verbose));
136 | }
137 | else if (pkgFile != null)
138 | {
139 | romExtractors.Add(new PceVcExtractor(pkgFile, verbose));
140 | }
141 | else if (Path.GetExtension(sourcePath) == ".sfrom")
142 | {
143 | romExtractors.Add(new SnesVcExtractor(sourcePath, verbose));
144 | }
145 | else if (srlFile != null)
146 | {
147 | romExtractors.Add(new DsVcExtractor(srlFile, verbose));
148 | }
149 |
150 | // Check with each extractor until a valid rom is found,
151 | // Then extract the rom with the appropriate extractor
152 | foreach (var romExtractor in romExtractors)
153 | {
154 | if (romExtractor.IsValidRom())
155 | {
156 | extractedRomPath = romExtractor.ExtractRom();
157 | break;
158 | }
159 | }
160 |
161 | // Clean up any existing unmanaged resources
162 | if (rpxFile != null)
163 | {
164 | rpxFile.Dispose();
165 | }
166 |
167 | if (psbFile != null)
168 | {
169 | psbFile.Dispose();
170 | }
171 |
172 | if (!string.IsNullOrEmpty(extractedRomPath))
173 | {
174 | Console.WriteLine("============================================================================");
175 | Console.WriteLine(sourcePath + " has been extracted to " + extractedRomPath + " successfully.");
176 | Console.WriteLine("============================================================================");
177 | }
178 | else
179 | {
180 | Console.WriteLine("============================================================================");
181 | Console.WriteLine("FAILURE: Could not successfully identify the rom type for " + sourcePath);
182 | Console.WriteLine("============================================================================");
183 | }
184 | }
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/FileTypes/MdfPsbFile.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.FileTypes
2 | {
3 | using System;
4 | using System.IO;
5 | using System.Security.Cryptography;
6 | using System.Text;
7 | using Ionic.Zlib;
8 | using Meisui.Random;
9 |
10 | ///
11 | /// MDF PSB file.
12 | ///
13 | public class MdfPsbFile : IDisposable
14 | {
15 | private const string FixedSeed = "MX8wgGEJ2+M47";
16 | private const byte XorKeyLength = 0x50;
17 |
18 | private readonly MdfHeader mdfHeader;
19 | private readonly byte[] xorKey;
20 | private readonly bool verbose;
21 | private readonly string path;
22 | private readonly string decompressedPath;
23 | private byte[] mdfData;
24 | private bool disposedValue;
25 |
26 | ///
27 | /// Initializes a new instance of the class.
28 | ///
29 | /// path to the PSB file.
30 | /// whether to provide verbose output.
31 | public MdfPsbFile(string psbFilePath, bool verbose = false)
32 | {
33 | this.verbose = verbose;
34 | Console.WriteLine("Decompressing PSB file...");
35 |
36 | this.path = psbFilePath;
37 | this.decompressedPath = this.path + ".extract";
38 |
39 | // Remove the temp file if it exists
40 | if (File.Exists(this.decompressedPath))
41 | {
42 | if (verbose)
43 | {
44 | Console.WriteLine("File exists at {0}, deleting...", this.decompressedPath);
45 | }
46 |
47 | File.Delete(this.decompressedPath);
48 | }
49 |
50 | this.mdfHeader = new MdfHeader(this.path);
51 |
52 | if (verbose)
53 | {
54 | Console.WriteLine("MDF Header content:\n{0}", this.mdfHeader.ToString());
55 | }
56 |
57 | if (verbose)
58 | {
59 | Console.WriteLine("Generating XOR key for MDF decryption...");
60 | }
61 |
62 | this.xorKey = this.GenerateXorKey(this.path);
63 |
64 | if (verbose)
65 | {
66 | Console.WriteLine("Reading bytes from {0}...", this.path);
67 | }
68 |
69 | this.mdfData = File.ReadAllBytes(this.path);
70 |
71 | this.DecryptMdfData();
72 |
73 | this.DecompressMdfData();
74 | }
75 |
76 | ///
77 | /// Gets path to the decompressed MDF file.
78 | ///
79 | public string DecompressedPath
80 | {
81 | get { return this.decompressedPath; }
82 | }
83 |
84 | ///
85 | /// Whether a given path is a valid MDF PSB file.
86 | ///
87 | /// path to the PSB file to test.
88 | /// true if valid, false otherwise.
89 | public static bool IsMdfPsb(string psbFilePath)
90 | {
91 | MdfHeader header = new MdfHeader(psbFilePath);
92 | return header.IsValid();
93 | }
94 |
95 | ///
96 | /// Dispose PsbFile.
97 | ///
98 | public void Dispose()
99 | {
100 | // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
101 | this.Dispose(disposing: true);
102 | GC.SuppressFinalize(this);
103 | }
104 |
105 | ///
106 | /// Dispose MdfPsbFile.
107 | ///
108 | /// whether disposing the file.
109 | protected virtual void Dispose(bool disposing)
110 | {
111 | if (!this.disposedValue)
112 | {
113 | if (disposing)
114 | {
115 | // dispose managed state (managed objects)
116 | }
117 |
118 | if (File.Exists(this.decompressedPath))
119 | {
120 | Console.WriteLine("Deleting file {0}", this.decompressedPath);
121 | File.Delete(this.decompressedPath);
122 | }
123 |
124 | // set large fields to null (if any)
125 | this.disposedValue = true;
126 | }
127 | }
128 |
129 | private byte[] GenerateXorKey(string fileName)
130 | {
131 | byte[] fixedSeed = Encoding.ASCII.GetBytes(FixedSeed);
132 | byte[] fileNameAsBytes = Encoding.ASCII.GetBytes(Path.GetFileName(fileName));
133 | int hashSeedLength = fixedSeed.Length + fileNameAsBytes.Length;
134 | byte[] hashSeed = new byte[hashSeedLength];
135 |
136 | Array.Copy(fixedSeed, hashSeed, fixedSeed.Length);
137 | Array.Copy(fileNameAsBytes, 0, hashSeed, fixedSeed.Length, fileNameAsBytes.Length);
138 |
139 | byte[] hashAsBytes = { };
140 |
141 | using (MD5 md5Hash = MD5.Create())
142 | {
143 | hashAsBytes = md5Hash.ComputeHash(hashSeed);
144 | }
145 |
146 | uint[] hashAsUint32 = new uint[4];
147 |
148 | for (int i = 0; i < 4; i++)
149 | {
150 | hashAsUint32[i] = BitConverter.ToUInt32(hashAsBytes, i * 4);
151 | }
152 |
153 | MersenneTwister mt19937 = new MersenneTwister(hashAsUint32);
154 |
155 | byte[] keyBuffer = new byte[XorKeyLength];
156 |
157 | for (int i = 0; i < XorKeyLength / 4; i++)
158 | {
159 | uint buffer = mt19937.Genrand_Int32();
160 |
161 | Array.Copy(BitConverter.GetBytes(buffer), 0, keyBuffer, i * 4, 4);
162 | }
163 |
164 | return keyBuffer;
165 | }
166 |
167 | private void DecryptMdfData()
168 | {
169 | if (this.verbose)
170 | {
171 | Console.WriteLine("Decrypting MDF data...");
172 | }
173 |
174 | // Copy the PSB data into decryptedData
175 | byte[] decryptedData = new byte[this.mdfData.Length];
176 | Array.Copy(this.mdfData, decryptedData, this.mdfData.Length);
177 |
178 | // Iterate through the data and XOR the key to decrypt it
179 | for (int i = 0; i < decryptedData.Length - MdfHeader.MDFHeaderLength; i++)
180 | {
181 | decryptedData[i + MdfHeader.MDFHeaderLength] ^= this.xorKey[i % XorKeyLength];
182 | }
183 |
184 | this.mdfData = decryptedData;
185 | if (this.verbose)
186 | {
187 | Console.WriteLine("Decryption complete");
188 | }
189 | }
190 |
191 | private void DecompressMdfData()
192 | {
193 | if (this.verbose)
194 | {
195 | Console.WriteLine("Decompressing MDF data...");
196 | }
197 |
198 | byte[] compressedData = new byte[this.mdfData.Length - MdfHeader.MDFHeaderLength];
199 | byte[] decompressedData;
200 |
201 | // Copy the current mdf data to compressed data without the MDF header
202 | Array.Copy(this.mdfData, MdfHeader.MDFHeaderLength, compressedData, 0, this.mdfData.Length - MdfHeader.MDFHeaderLength);
203 |
204 | using (MemoryStream compressedStream = new MemoryStream(compressedData))
205 | {
206 | using ZlibStream deflateStream = new ZlibStream(compressedStream, CompressionMode.Decompress);
207 | using MemoryStream decompressedStream = new MemoryStream();
208 | deflateStream.CopyTo(decompressedStream);
209 | decompressedData = decompressedStream.ToArray();
210 | }
211 |
212 | // Write all of the decompressed data to the decompressedPath
213 | File.WriteAllBytes(this.decompressedPath, decompressedData);
214 |
215 | Console.WriteLine("Decompression to {0} completed", this.decompressedPath);
216 | }
217 | }
218 | }
219 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/FileTypes/RpxHeader.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.FileTypes
2 | {
3 | using System;
4 | using System.IO;
5 | using System.Text;
6 | using WiiuVcExtractor.Libraries;
7 |
8 | ///
9 | /// RPX file header.
10 | ///
11 | internal class RpxHeader
12 | {
13 | private const int ElfSignatureLength = 0x10;
14 | private const ushort ElfType = 0xFE01;
15 | private static readonly byte[] ElfSignature = { 0x7F, 0x45, 0x4C, 0x46 };
16 |
17 | private readonly byte[] identity;
18 | private readonly ushort type;
19 | private readonly ushort machine;
20 | private readonly uint version;
21 | private readonly uint entryPoint;
22 | private readonly uint phOffset;
23 | private readonly uint shOffset;
24 | private readonly uint flags;
25 | private readonly ushort ehSize;
26 | private readonly ushort phEntSize;
27 | private readonly ushort phNum;
28 | private readonly ushort shEntSize;
29 | private readonly ushort shNum;
30 | private readonly ushort shStrIndex;
31 | private ulong sHeaderDataElfOffset;
32 |
33 | ///
34 | /// Initializes a new instance of the class.
35 | ///
36 | /// path to RPX file.
37 | public RpxHeader(string rpxFilePath)
38 | {
39 | this.identity = new byte[ElfSignatureLength];
40 |
41 | using (FileStream fs = new FileStream(rpxFilePath, FileMode.Open, FileAccess.Read))
42 | {
43 | using BinaryReader br = new BinaryReader(fs, new ASCIIEncoding());
44 |
45 | // Read in the header
46 | this.identity = br.ReadBytes(ElfSignatureLength);
47 | this.type = EndianUtility.ReadUInt16BE(br);
48 | this.machine = EndianUtility.ReadUInt16BE(br);
49 | this.version = EndianUtility.ReadUInt32BE(br);
50 | this.entryPoint = EndianUtility.ReadUInt32BE(br);
51 | this.phOffset = EndianUtility.ReadUInt32BE(br);
52 | this.shOffset = EndianUtility.ReadUInt32BE(br);
53 | this.flags = EndianUtility.ReadUInt32BE(br);
54 | this.ehSize = EndianUtility.ReadUInt16BE(br);
55 | this.phEntSize = EndianUtility.ReadUInt16BE(br);
56 | this.phNum = EndianUtility.ReadUInt16BE(br);
57 | this.shEntSize = EndianUtility.ReadUInt16BE(br);
58 | this.shNum = EndianUtility.ReadUInt16BE(br);
59 | this.shStrIndex = EndianUtility.ReadUInt16BE(br);
60 | }
61 |
62 | this.sHeaderDataElfOffset = (ulong)(this.shOffset + (this.shNum * this.shEntSize));
63 | }
64 |
65 | ///
66 | /// Gets count of section headers.
67 | ///
68 | public ushort SectionHeaderCount
69 | {
70 | get { return this.shNum; }
71 | }
72 |
73 | ///
74 | /// Gets section headers' offset.
75 | ///
76 | public uint SectionHeaderOffset
77 | {
78 | get { return this.shOffset; }
79 | }
80 |
81 | ///
82 | /// Gets or sets section header data offset.
83 | ///
84 | public ulong SectionHeaderDataElfOffset
85 | {
86 | get { return this.sHeaderDataElfOffset; } set { this.sHeaderDataElfOffset = this.SectionHeaderDataElfOffset; }
87 | }
88 |
89 | ///
90 | /// Gets identity (ELF signature).
91 | ///
92 | public byte[] Identity
93 | {
94 | get { return this.identity; }
95 | }
96 |
97 | ///
98 | /// Gets type.
99 | ///
100 | public ushort Type
101 | {
102 | get { return this.type; }
103 | }
104 |
105 | ///
106 | /// Gets machine.
107 | ///
108 | public ushort Machine
109 | {
110 | get { return this.machine; }
111 | }
112 |
113 | ///
114 | /// Gets version.
115 | ///
116 | public uint Version
117 | {
118 | get { return this.version; }
119 | }
120 |
121 | ///
122 | /// Gets entry point.
123 | ///
124 | public uint EntryPoint
125 | {
126 | get { return this.entryPoint; }
127 | }
128 |
129 | ///
130 | /// Gets program header offset.
131 | ///
132 | public uint PhOffset
133 | {
134 | get { return this.phOffset; }
135 | }
136 |
137 | ///
138 | /// Gets flags.
139 | ///
140 | public uint Flags
141 | {
142 | get { return this.flags; }
143 | }
144 |
145 | ///
146 | /// Gets header size.
147 | ///
148 | public ushort EhSize
149 | {
150 | get { return this.ehSize; }
151 | }
152 |
153 | ///
154 | /// Gets program header table entry size.
155 | ///
156 | public ushort PhEntSize
157 | {
158 | get { return this.phEntSize; }
159 | }
160 |
161 | ///
162 | /// Gets count of program header table entries.
163 | ///
164 | public ushort PhNum
165 | {
166 | get { return this.phNum; }
167 | }
168 |
169 | ///
170 | /// Gets section header entry size.
171 | ///
172 | public ushort ShEntSize
173 | {
174 | get { return this.shEntSize; }
175 | }
176 |
177 | ///
178 | /// Gets section header index for the entry containing section names.
179 | ///
180 | public ushort ShStrIndex
181 | {
182 | get { return this.shStrIndex; }
183 | }
184 |
185 | ///
186 | /// Whether the RPX file is valid.
187 | ///
188 | /// true if valid, false otherwise.
189 | public bool IsValid()
190 | {
191 | // Check that the signature is correct
192 | if (this.identity[0] != ElfSignature[0] ||
193 | this.identity[1] != ElfSignature[1] ||
194 | this.identity[2] != ElfSignature[2] ||
195 | this.identity[3] != ElfSignature[3])
196 | {
197 | return false;
198 | }
199 |
200 | // Check that the type is correct
201 | if (this.type != ElfType)
202 | {
203 | return false;
204 | }
205 |
206 | return true;
207 | }
208 |
209 | ///
210 | /// Creates string representation of the RPX file.
211 | ///
212 | /// string representation of the file.
213 | public override string ToString()
214 | {
215 | return "RpxHeader:\n" +
216 | "identity: " + BitConverter.ToString(this.identity) + "\n" +
217 | "type: " + this.type.ToString() + "\n" +
218 | "machine: " + this.machine.ToString() + "\n" +
219 | "version: " + this.version.ToString() + "\n" +
220 | "entryPoint: 0x" + string.Format("{0:X}", this.entryPoint) + "\n" +
221 | "phOffset: 0x" + string.Format("{0:X}", this.phOffset) + "\n" +
222 | "shOffset: 0x" + string.Format("{0:X}", this.shOffset) + "\n" +
223 | "flags: " + this.flags.ToString() + "\n" +
224 | "ehSize: " + this.ehSize.ToString() + "\n" +
225 | "phEntSize: " + this.phEntSize.ToString() + "\n" +
226 | "phNum: " + this.phNum.ToString() + "\n" +
227 | "shEntSize: " + this.shEntSize.ToString() + "\n" +
228 | "shNum: " + this.shNum.ToString() + "\n" +
229 | "shStrIndex: " + this.shStrIndex.ToString() + "\n" +
230 | "sHeaderDataElfOffset: 0x" + string.Format("{0:X}", this.sHeaderDataElfOffset) + "\n";
231 | }
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/nesromnames.csv:
--------------------------------------------------------------------------------
1 | WUP-FBGE,Adventure Island
2 | WUP-FCXE,Adventures of Lolo
3 | WUP-FAJE,Balloon Fight
4 | WUP-FBLE,Baseball
5 | WUP-FC2E,Bases Loaded
6 | WUP-FEBE,Blaster Master
7 | WUP-FB8E,Castlevania II Simons Quest
8 | WUP-FCHE,Castlevania III Draculas Curse
9 | WUP-FB3E,Castlevania
10 | WUP-FE4E,City Connection
11 | WUP-FBQE,Clu Clu Land
12 | WUP-FDSE,Crash n the Boys Street Challenge
13 | WUP-FEWE,DIG DUG II
14 | WUP-FDEE,DIG DUG
15 | WUP-FAWE,Donkey Kong Jr
16 | WUP-FDDE,Donkey Kong Jr Math
17 | WUP-FAFE,Donkey Kong
18 | WUP-FBRE,Donkey Kong 3
19 | WUP-FC8E,Double Dragon II The Revenge
20 | WUP-FE8E,Double Dragon III The Sacred Stones
21 | WUP-FB7E,Double Dragon
22 | WUP-FB5E,Dr Mario
23 | WUP-FEHE,Duck Hunt
24 | WUP-FBDE,EarthBound Beginnings
25 | WUP-FAGE,Excitebike
26 | WUP-FA6E,Galaga
27 | WUP-FC6E,Gargoyles Quest II The Demon Darkness
28 | WUP-FBHE,GHOSTSN GOBLINS
29 | WUP-FBSE,Golf
30 | WUP-FATE,Gradius
31 | WUP-FHAE,Hogans Alley
32 | WUP-FACE,Ice Climber
33 | WUP-FBUE,Ice Hockey
34 | WUP-FBBE,Kid Icarus
35 | WUP-FADE,Kirbys Adventure
36 | WUP-FC9E,Life Force
37 | WUP-FAVE,Lode Runner
38 | WUP-FCQE,Mach Rider
39 | WUP-FD9E,MAPPY-LAND
40 | WUP-FAEE,Mario Bros
41 | WUP-FANE,Mega Man
42 | WUP-FAPE,Mega Man 2
43 | WUP-FAZE,Mega Man 3
44 | WUP-FA7E,Mega Man 4
45 | WUP-FC5E,Mega Man 5
46 | WUP-FCSE,MEGA MAN 6
47 | WUP-FA8E,Metroid
48 | WUP-FCCE,Mighty Bomb Jack
49 | WUP-FDKE,Mighty Final Fight
50 | WUP-FB4E,NES Open Tournament Golf
51 | WUP-FE5E,Ninja Gaiden II The Dark Sword of Chaos
52 | WUP-FCBE,Ninja Gaiden
53 | WUP-FE6E,Ninja Gaiden III The Ancient Ship of Doom
54 | WUP-FCZE,Pac-Land
55 | WUP-FARE,PAC-MAN
56 | WUP-FBNE,Pinball
57 | WUP-FAKE,Punch-Out!! Featuring Mr Dream
58 | WUP-FB6E,Renegade
59 | WUP-FESE,River City Ransom
60 | WUP-FDUE,SCAT
61 | WUP-FDTE,Shadow of the Ninja
62 | WUP-FDYE,SkyKid
63 | WUP-FCPE,Soccer
64 | WUP-FAYE,Solomons Key
65 | WUP-FASE,Spelunker
66 | WUP-FE2E,StarTropics
67 | WUP-FD6E,STINGER
68 | WUP-FDLE,Street Fighter 2010 The Final Fight
69 | WUP-FCEE,Super C
70 | WUP-FB2E,Super Dodge Ball
71 | WUP-FAAE,Super Mario Bros
72 | WUP-FAHE,Super Mario Bros 2
73 | WUP-FABE,Super Mario Bros 3
74 | WUP-FA9E,Super Mario Bros The Lost Levels
75 | WUP-FE7E,TECMO BOWL
76 | WUP-FBME,Tennis
77 | WUP-FD2E,THE ADVENTURES OF BAYOU BiLLY
78 | WUP-FBAE,The Legend of Zelda
79 | WUP-FDAE,Ufouria The Saga
80 | WUP-FBPE,Urban Champion
81 | WUP-FCFE,Volleyball
82 | WUP-FEZE,VS Excitebike
83 | WUP-FBVE,Warios Woods
84 | WUP-FEUE,Wild Gunman
85 | WUP-FA4E,Wrecking Crew
86 | WUP-FALE,Xevious
87 | WUP-FAME,Yoshi
88 | WUP-FBCE,Zelda II - The Adventure of Link
89 | WUP-FE3E,Zodas Revenge Star Tropics II
90 | WUP-FA2J,マッピー
91 | WUP-FA4J,レッキングクルー
92 | WUP-FA4P,Wrecking Crew
93 | WUP-FA5J,ふぁみこんむかし話 新・鬼ヶ島(前後編)
94 | WUP-FA6J,ギャラガ
95 | WUP-FA6P,Galaga
96 | WUP-FA7J,ロックマン4 新たなる野望!!
97 | WUP-FA7P,Mega Man 4
98 | WUP-FA8J,メトロイド
99 | WUP-FA8P,Metroid
100 | WUP-FA9J,スーパーマリオブラザーズ2
101 | WUP-FA9P,Super Mario Bros The Lost Levels
102 | WUP-FAAJ,スーパーマリオブラザーズ
103 | WUP-FAAP,Super Mario Bros
104 | WUP-FABJ,スーパーマリオブラザーズ3
105 | WUP-FABP,Super Mario Bros 3
106 | WUP-FACJ,アイスクライマー
107 | WUP-FACP,Ice Climber
108 | WUP-FADJ,星のカービィ 夢の泉の物語
109 | WUP-FADP,Kirbys Adventure
110 | WUP-FAEJ,マリオブラザーズ
111 | WUP-FAEP,Mario Bros
112 | WUP-FAFJ,ドンキーコング
113 | WUP-FAFP,Donkey Kong
114 | WUP-FAGJ,エキサイトバイク
115 | WUP-FAGP,Excitebike
116 | WUP-FAHJ,スーパーマリオUSA
117 | WUP-FAHP,Super Mario Bros 2
118 | WUP-FAJJ,バルーンファイト
119 | WUP-FAJP,Balloon Fight
120 | WUP-FAKJ,パンチアウト
121 | WUP-FAKP,Punch-Out!!
122 | WUP-FALJ,ゼビウス
123 | WUP-FALP,Xevious
124 | WUP-FAMJ,ヨッシーのたまご
125 | WUP-FAMP,Mario&Yoshi
126 | WUP-FANJ,ロックマン
127 | WUP-FANP,Mega Man
128 | WUP-FAPJ,ロックマン2 Drワイリーの謎
129 | WUP-FAPP,Mega Man 2
130 | WUP-FAQJ,ダウンタウン熱血行進曲 それゆけ大運動会
131 | WUP-FARJ,パックマン
132 | WUP-FARP,PAC-MAN
133 | WUP-FASJ,スペランカー
134 | WUP-FASP,Spelunker
135 | WUP-FATJ,グラディウス
136 | WUP-FATP,Gradius
137 | WUP-FAUJ,ツインビー
138 | WUP-FAVJ,ロードランナー
139 | WUP-FAVP,Lode Runner
140 | WUP-FAWJ,ドンキーコングJR
141 | WUP-FAWP,Donkey Kong Jr
142 | WUP-FAXJ,忍者じゃじゃ丸くん
143 | WUP-FAYJ,ソロモンの鍵
144 | WUP-FAYP,Solomons Key
145 | WUP-FAZJ,ロックマン3 Drワイリーの最期!?
146 | WUP-FAZP,Mega Man 3
147 | WUP-FB2J,熱血高校ドッジボール部
148 | WUP-FB2P,Super Dodge Ball
149 | WUP-FB3J,悪魔城ドラキュラ
150 | WUP-FB3P,Castlevania
151 | WUP-FB4J,マリオオープンゴルフ
152 | WUP-FB4P,NES Open Tournament Golf
153 | WUP-FB5J,ドクターマリオ
154 | WUP-FB5P,Dr Mario
155 | WUP-FB6J,熱血硬派くにおくん
156 | WUP-FB6P,Renegade
157 | WUP-FB7J,ダブルドラゴン
158 | WUP-FB7P,Double Dragon
159 | WUP-FB8J,ドラキュラⅡ 呪いの封印
160 | WUP-FB8P,Castlevania Ⅱ Simons Quest
161 | WUP-FB9J,ELEVATOR ACTION
162 | WUP-FBAJ,ゼルダの伝説
163 | WUP-FBAP,The Legend of Zelda
164 | WUP-FBBJ,光神話 パルテナの鏡
165 | WUP-FBBP,Kid Icarus
166 | WUP-FBCJ,リンクの冒険
167 | WUP-FBCP,Zelda II - The Adventure of Link
168 | WUP-FBDJ,MOTHER
169 | WUP-FBDP,EarthBound Beginnings
170 | WUP-FBEJ,いっき
171 | WUP-FBGJ,高橋名人の冒険島
172 | WUP-FBGP,Adventure Island
173 | WUP-FBHJ,魔界村
174 | WUP-FBHP,Ghostsn Goblins
175 | WUP-FBKJ,ドルアーガの塔
176 | WUP-FBLJ,ベースボール
177 | WUP-FBLP,Baseball
178 | WUP-FBMJ,テニス
179 | WUP-FBMP,Tennis
180 | WUP-FBNJ,ピンボール
181 | WUP-FBNP,Pinball
182 | WUP-FBPJ,アーバンチャンピオン
183 | WUP-FBPP,Urban Champion
184 | WUP-FBQJ,クルクルランド
185 | WUP-FBQP,Clu Clu Land
186 | WUP-FBRJ,ドンキーコング3
187 | WUP-FBRP,Donkey Kong 3
188 | WUP-FBSJ,ゴルフ
189 | WUP-FBSP,GOLF
190 | WUP-FBTJ,ファイナルファンタジー
191 | WUP-FBUJ,アイスホッケー
192 | WUP-FBUP,Ice Hockey
193 | WUP-FBVJ,ワリオの森
194 | WUP-FBVP,Warios Woods
195 | WUP-FBWJ,BUBBLE BOBBLE
196 | WUP-FBXJ,ファイナルファンタジーII
197 | WUP-FBYJ,ファイナルファンタジーIII
198 | WUP-FBZJ,ダウンタウンスペシャル くにおくんの時代劇だよ全員集合!
199 | WUP-FC2J,燃えろ!!プロ野球
200 | WUP-FC3J,イー・アル・カンフー
201 | WUP-FC4J,けっきょく南極大冒険
202 | WUP-FC5J,ロックマン5 ブルースの罠!?
203 | WUP-FC5P,Mega Man 5
204 | WUP-FC6J,レッドアリーマーⅡ
205 | WUP-FC6P,Gargoyles Quest II The Demon Darkness
206 | WUP-FC7J,謎の村雨城
207 | WUP-FC8P,Double Dragon II The Revenge
208 | WUP-FC9J,沙羅曼蛇
209 | WUP-FC9P,Life Force
210 | WUP-FCAJ,ファミコン探偵倶楽部 消えた後継者(前後編)
211 | WUP-FCBJ,忍者龍剣伝
212 | WUP-FCBP,Ninja Gaiden
213 | WUP-FCCJ,マイティボンジャック
214 | WUP-FCCP,Mighty Bomb Jack
215 | WUP-FCDJ,がんばれゴエモン!からくり道中
216 | WUP-FCEJ,SUPER魂斗羅
217 | WUP-FCEP,Super C
218 | WUP-FCFJ,バレーボール
219 | WUP-FCFP,Volleyball
220 | WUP-FCGJ,ファイアーエムブレム 暗黒竜と光の剣
221 | WUP-FCHJ,悪魔城伝説
222 | WUP-FCHP,Castlevania III Draculas Curse
223 | WUP-FCJJ,半熟英雄
224 | WUP-FCKJ,影の伝説
225 | WUP-FCLJ,熱血高校ドッジボール部 サッカー編
226 | WUP-FCMJ,飛龍の拳 奥義の書
227 | WUP-FCMP,Flying Dragon The Secret Scroll
228 | WUP-FCNJ,ファイアーエムブレム 外伝
229 | WUP-FCPJ,サッカー
230 | WUP-FCPP,Soccer
231 | WUP-FCQJ,マッハライダー
232 | WUP-FCQP,Mach Rider
233 | WUP-FCRJ,いけいけ!熱血ホッケー部「すべってころんで大乱闘」
234 | WUP-FCSJ,ロックマン6 史上最大の戦い!!
235 | WUP-FCSP,Mega Man 6
236 | WUP-FCTJ,バトルシティー
237 | WUP-FCUJ,ワギャンランド
238 | WUP-FCVJ,サラダの国のトマト姫
239 | WUP-FCWJ,スーパーチャイニーズ
240 | WUP-FCWP,Kung-Fu Heroes
241 | WUP-FCXJ,アドベンチャーズ オブ ロロ
242 | WUP-FCXP,Adventures of Lolo
243 | WUP-FCYJ,ジョイメカファイト
244 | WUP-FCZJ,パックランド
245 | WUP-FCZP,PAC-LAND
246 | WUP-FD2P,The Adventures of Bayou Billy
247 | WUP-FD3J,月風魔伝
248 | WUP-FD4J,ESPER DREAM(エスパードリーム)
249 | WUP-FD5J,がんばれゴエモン2
250 | WUP-FD6J,もえろツインビー シナモン博士を救え!
251 | WUP-FD7J,すごろクエスト ダイスの戦士たち
252 | WUP-FD8J,セクロス
253 | WUP-FD9J,マッピーランド
254 | WUP-FD9P,Mappy-Land
255 | WUP-FDAJ,へべれけ
256 | WUP-FDAP,Ufouria THE SAGA
257 | WUP-FDBJ,バベルの塔
258 | WUP-FDCJ,クインティ
259 | WUP-FDDJ,ドンキーコングJRの算数遊び
260 | WUP-FDDP,Donkey Kong Jr Math
261 | WUP-FDEJ,ディグダグ
262 | WUP-FDEP,Dig Dug
263 | WUP-FDFJ,ドラゴンバスター
264 | WUP-FDGJ,つっぱり大相撲
265 | WUP-FDHJ,ナッツ&ミルク
266 | WUP-FDJJ,五目ならべ 連珠
267 | WUP-FDKJ,マイティファイナルファイト
268 | WUP-FDKP,Mighty Final Fight
269 | WUP-FDLJ,2010 ストリートファイター
270 | WUP-FDLP,Street Fighter 2010 The Final Fight
271 | WUP-FDMJ,デビルワールド
272 | WUP-FDMP,Devil World
273 | WUP-FDNJ,ワイワイワールド2 SOS!!パセリ城
274 | WUP-FDPJ,がんばれゴエモン外伝2 天下の財宝
275 | WUP-FDRJ,妖怪道中記
276 | WUP-FDSJ,びっくり熱血新記録! はるかなる金メダル
277 | WUP-FDSP,Crash n the Boys Street Challenge
278 | WUP-FDTP,Shadow of the Ninja
279 | WUP-FDUP,SCAT
280 | WUP-FDVJ,スマッシュピンポン
281 | WUP-FDWJ,ファミコンウォーズ
282 | WUP-FDXJ,ワルキューレの冒険 時の鍵伝説
283 | WUP-FDYJ,スカイキッド
284 | WUP-FDZJ,POOYAN(プーヤン)
285 | WUP-FE2P,StarTropics
286 | WUP-FE3P,Zodas Revenge StarTropics II
287 | WUP-FE5P,Ninja Gaiden II The Dark Sword of Chaos
288 | WUP-FE6P,Ninja Gaiden III The Ancient Ship of Doom
289 | WUP-FE7P,Tecmo Bowl
290 | WUP-FE8P,Double Dragon III The Sacred Stones
291 | WUP-FEAJ,アトランチスの謎
292 | WUP-FEBJ,超惑星戦記 メタファイト
293 | WUP-FEBP,Blaster Master
294 | WUP-FECJ,バイオミラクル ぼくってウパ
295 | WUP-FEDJ,チャンピオンシップ・ロードランナー
296 | WUP-FEEJ,かんしゃく玉なげカン太郎の 東海道五十三次
297 | WUP-FEFJ,ワギャンランド2
298 | WUP-FEGJ,メトロクロス
299 | WUP-FEHJ,ダックハント
300 | WUP-FEHP,Duck Hunt
301 | WUP-FEJJ,スターラスター
302 | WUP-FEKJ,がんばれゴエモン外伝 きえた黄金キセル
303 | WUP-FELJ,フィールドコンバット
304 | WUP-FEMP,Little Ninja Brothers
305 | WUP-FENP,Flying Warriors
306 | WUP-FERJ,エクセリオン
307 | WUP-FESJ,ダウンタウン熱血物語
308 | WUP-FESP,Street Gangs
309 | WUP-FETJ,メタルスレイダーグローリー
310 | WUP-FEUP,Wild Gunman
311 | WUP-FEWP,Dig Dug II
312 | WUP-FEZJ,VSエキサイトバイク
313 | WUP-FHAP,Hogans Alley
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Wii U Virtual Console Extractor
2 | Extracts Wii U Virtual Console roms from dumps created via [DDD](https://github.com/dimok789/ddd/releases) or from the SNES Mini.
3 |
4 | This currently only supports extracting GBA, DS, NES, FDS, PCE, and SNES roms. Note that most VC titles are not clean roms but have been modified from their original state.
5 |
6 | ## Installation
7 | ### Windows
8 | 1. Download the [latest Windows release zip file](https://github.com/AwakeEchidna/wiiu-vc-extractor/releases/latest/download/wiiu-vc-extractor-win-x64.zip)
9 | 2. Extract it to your local computer
10 |
11 | ### Linux
12 | 1. Download the [latest Linux release zip file](https://github.com/AwakeEchidna/wiiu-vc-extractor/releases/latest/download/wiiu-vc-extractor-linux-x64.zip)
13 | 2. Extract it to your local computer
14 |
15 | ### Mac
16 | 1. Download the [latest OSX release zip file](https://github.com/AwakeEchidna/wiiu-vc-extractor/releases/latest/download/wiiu-vc-extractor-osx-x64.zip)
17 | 2. Extract it to your local computer
18 |
19 | > If you are on an M1 Mac, you may need to install [Rosetta](https://support.apple.com/en-gb/HT211861) with `softwareupdate --install-rosetta` before the application will function. If Rosetta is not installed, you will receive a `zsh: bad CPU type in executable` error.
20 |
21 | ## Basic Usage
22 | `wiiuvcextractor `
23 |
24 | ```
25 | =====================================
26 | Wii U Virtual Console Extractor 0.7.0
27 | =====================================
28 | Extracts roms from Virtual Console games dumped by DDD or from the SNES Mini.
29 |
30 | Usage:
31 | wiiuvcextractor [-v] [rpx_or_psb.m_file]
32 | - Extract a rom from a Virtual Console dump
33 |
34 | wiiuvcextractor --version
35 | - Display current version
36 |
37 |
38 | Usage Examples:
39 | wiiuvcextractor alldata.psb.m
40 | wiiuvcextractor WUP-FAME.rpx
41 | wiiuvcextractor CLV-P-SAAAE.sfrom
42 | wiiuvcextractor pce.pkg
43 | wiiuvcextractor -v WUP-JBBE.rpx
44 | ```
45 |
46 | ## Example Runs
47 | ### NES Extraction
48 | ```
49 | wiiuvcextractor.exe WUP-FCSE.rpx
50 | ============================================================================
51 | Starting extraction of rom from WUP-FCSE.rpx...
52 | ============================================================================
53 | RPX file detected!
54 | Decompressing RPX file...
55 | Decompression complete.
56 | Checking if this is an NES VC title...
57 | Checking WUP-FCSE.rpx.extract...
58 | NES Rom Detected!
59 | Virtual Console Title: WUP-FCSE
60 | NES Title: MEGA MAN 6
61 | Getting number of PRG and CHR pages...
62 | PRG Pages: 32
63 | CHR Pages: 0
64 | Total NES rom size: 524304 Bytes
65 | Fixing VC NES Header...
66 | Getting rom data...
67 | Writing to MEGA MAN 6.nes...
68 | Writing NES rom header...
69 | Writing NES rom data...
70 | NES rom has been created successfully at MEGA MAN 6.nes
71 | ============================================================================
72 | WUP-FCSE.rpx has been extracted to MEGA MAN 6.nes successfully.
73 | ============================================================================
74 | ```
75 |
76 | ### FDS Extraction
77 | ```
78 | wiiuvcextractor.exe WUP-FA9E.rpx
79 | ============================================================================
80 | Starting extraction of rom from WUP-FA9E.rpx...
81 | ============================================================================
82 | RPX file detected!
83 | Decompressing RPX file...
84 | Decompression complete.
85 | Checking if this is an NES VC title...
86 | Checking WUP-FA9E.rpx.extract...
87 | Not an NES VC Title
88 | Checking if this is an SNES VC title...
89 | Checking WUP-FA9E.rpx.extract...
90 | Checking for the SNES WUP header
91 | Not an SNES VC Title
92 | Checking if this is a Famicom Disk System VC title...
93 | Checking WUP-FA9E.rpx.extract...
94 | Famicom Disk System Rom Detected!
95 | Virtual Console Title: WUP-FA9E
96 | FDS Title: Super Mario Bros The Lost Levels
97 | Total FDS rom size: 65500 Bytes
98 | Getting rom data...
99 | Writing to Super Mario Bros The Lost Levels.fds...
100 | Writing rom data...
101 | ============================================================================
102 | Famicom Disk System rom has been created successfully at Super Mario Bros The Lost Levels.fds
103 | ============================================================================
104 | ```
105 |
106 | ### SNES Extraction (rpx)
107 | ```
108 | ============================================================================
109 | Starting extraction of rom from WUP-JA7E.rpx...
110 | ============================================================================
111 | RPX file detected!
112 | Decompressing RPX file...
113 | Decompression complete.
114 | Checking if this is an NES VC title...
115 | Checking WUP-JA7E.rpx.extract...
116 | Not an NES VC Title
117 | Checking if this is an SNES VC title...
118 | Checking WUP-JA7E.rpx.extract...
119 | SNES Rom Detected!
120 | Virtual Console Title: WUP-JA7E
121 | SNES Title: Pilotwings
122 | SNES Header Name: PILOTWINGS
123 | Getting size of rom...
124 | Total SNES rom size: 524288 Bytes
125 | Getting rom data...
126 | Extracting PCM Data...
127 | Found the first PCM offset at 163937
128 | Reading PCM data into memory...
129 | Writing to Pilotwings.sfc...
130 | Writing SNES rom data...
131 | SNES rom has been created successfully at Pilotwings.sfc
132 | ============================================================================
133 | WUP-JA7E.rpx has been extracted to Pilotwings.sfc successfully.
134 | ============================================================================
135 | ```
136 |
137 | ### SNES Extraction (sfrom)
138 | ```
139 | ============================================================================
140 | Starting extraction of rom from CLV-P-SAAHE.sfrom...
141 | ============================================================================
142 | Checking if this is an SNES VC title...
143 | Checking CLV-P-SAAHE.sfrom...
144 | SNES Rom Detected!
145 | Virtual Console Title: WUP-JAJE
146 | SNES Title: Super Metroid
147 | SNES Header Name: Super Metroid
148 | Getting size of rom...
149 | Total SNES rom size: 3145728 Bytes
150 | Getting rom data...
151 | Extracting PCM Data...
152 | Found the first PCM offset at 2609193
153 | Reading PCM data into memory...
154 | Writing to Super Metroid.sfc...
155 | Writing SNES rom data...
156 | SNES rom has been created successfully at Super Metroid.sfc
157 | ============================================================================
158 | CLV-P-SAAHE.sfrom has been extracted to Super Metroid.sfc successfully.
159 | ============================================================================
160 | ```
161 |
162 | ### GBA Extraction
163 | ```
164 | wiiuvcextractor.exe alldata.psb.m
165 | ============================================================================
166 | Starting extraction of rom from alldata.psb.m...
167 | ============================================================================
168 | PSB file detected!
169 | Decompressing PSB file...
170 | Checking for PSB data file alldata.bin...
171 | Found rom subfile at aawre1.120.m
172 | Offset: 36358144
173 | Length: 2408199
174 | Decompressing rom...
175 | Decompressing PSB file...
176 | Checking if this is a GBA VC title...
177 | Checking aawre1.120.m.extract...
178 | GBA Rom Detected!
179 | GBA Rom Code: AWRE
180 | GBA Title: Advance Wars
181 | Writing to Advance Wars.gba...
182 | GBA rom has been created successfully at Advance Wars.gba
183 | ============================================================================
184 | alldata.psb.m has been extracted to Advance Wars.gba successfully.
185 | ============================================================================
186 | ```
187 |
188 | ### PCE Extraction
189 | ```
190 | wiiuvcextractor.exe pce.pkg
191 | ============================================================================
192 | Starting extraction of rom from pce.pkg...
193 | ============================================================================
194 | Extracting PKG file...
195 | Checking if this is a PC Engine VC title...
196 | PC Engine VC Rom detected! Extension .pce was found in the pce.pkg entry point.
197 | Writing content file blazinglazers.pce to blazinglazers.pce
198 | ============================================================================
199 | pce.pkg has been extracted to blazinglazers.pce successfully.
200 | ============================================================================
201 | ```
202 |
203 | ## Credits
204 | Decompression of rpx files is possible due to the following tool created by 0CBH0: https://github.com/0CBH0/wiiurpxtool
205 |
206 | Decompression and decryption of psb.m files is possible due to research and code created by ajd4096: https://github.com/ajd4096/inject_gba
207 |
208 | Extraction of Famicom Disk System games made possible by einstein95: https://gist.github.com/einstein95/6545066905680466cdf200c4cc8ca4f0
209 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/RomExtractors/NesVcExtractor.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.RomExtractors
2 | {
3 | using System;
4 | using System.IO;
5 | using System.Text;
6 | using WiiuVcExtractor.FileTypes;
7 | using WiiuVcExtractor.Libraries;
8 |
9 | ///
10 | /// NES VC rom extractor.
11 | ///
12 | public class NesVcExtractor : IRomExtractor
13 | {
14 | private const int NesHeaderLength = 16;
15 | private const int VcNameLength = 8;
16 | private const int VcNamePadding = 8;
17 | private const int PrgPageSize = 16384;
18 | private const int ChrPageSize = 8192;
19 | private const int CharacterBreak = 0x1A;
20 | private const int BrokenNesHeaderOffset = 0x3;
21 | private const int PrgPageOffset = 0x4;
22 | private const int ChrPageOffset = 0x5;
23 | private const string NesDictionaryCsvPath = "nesromnames.csv";
24 |
25 | private static readonly byte[] NesHeaderCheck = { 0x4E, 0x45, 0x53 };
26 |
27 | private readonly RpxFile rpxFile;
28 | private readonly RomNameDictionary nesDictionary;
29 | private readonly byte[] nesRomHeader;
30 | private readonly bool verbose;
31 |
32 | private string extractedRomPath;
33 | private string romName;
34 | private long romPosition;
35 | private string vcName;
36 | private long vcNamePosition;
37 | private byte[] nesRomData;
38 |
39 | ///
40 | /// Initializes a new instance of the class.
41 | ///
42 | /// RPX file to parse.
43 | /// whether to provide verbose output.
44 | public NesVcExtractor(RpxFile rpxFile, bool verbose = false)
45 | {
46 | this.verbose = verbose;
47 | string nesDictionaryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, NesDictionaryCsvPath);
48 |
49 | this.nesDictionary = new RomNameDictionary(nesDictionaryPath);
50 | this.nesRomHeader = new byte[NesHeaderLength];
51 | this.romPosition = 0;
52 | this.vcNamePosition = 0;
53 |
54 | this.rpxFile = rpxFile;
55 | }
56 |
57 | ///
58 | /// Extracts NES rom from an RPX file.
59 | ///
60 | /// path of the extracted rom.
61 | public string ExtractRom()
62 | {
63 | // Quiet down the console during the extraction valid rom check
64 | var consoleOutputStream = Console.Out;
65 | Console.SetOut(TextWriter.Null);
66 | if (this.IsValidRom())
67 | {
68 | Console.SetOut(consoleOutputStream);
69 |
70 | // Browse to the romPosition in the file and look for the WUP string 16 bytes before
71 | using FileStream fs = new FileStream(this.rpxFile.DecompressedPath, FileMode.Open, FileAccess.Read);
72 | using BinaryReader br = new BinaryReader(fs, new ASCIIEncoding());
73 | br.BaseStream.Seek(this.vcNamePosition, SeekOrigin.Begin);
74 |
75 | // read in the VC rom name
76 | this.vcName = Encoding.ASCII.GetString(br.ReadBytes(VcNameLength));
77 | this.romName = this.nesDictionary.GetRomName(this.vcName);
78 |
79 | // If a rom name could not be determined, prompt the user
80 | if (string.IsNullOrEmpty(this.romName))
81 | {
82 | Console.WriteLine("Could not determine NES rom name, please enter your desired filename:");
83 | this.romName = Console.ReadLine();
84 | }
85 |
86 | Console.WriteLine("Virtual Console Title: " + this.vcName);
87 | Console.WriteLine("NES Title: " + this.romName);
88 |
89 | this.extractedRomPath = this.romName + ".nes";
90 |
91 | br.ReadBytes(VcNamePadding);
92 |
93 | // We are currently at the NES header's position again, read past it
94 | br.ReadBytes(NesHeaderLength);
95 |
96 | // Determine the NES rom's size
97 | Console.WriteLine("Getting number of PRG and CHR pages...");
98 |
99 | byte prgPages = this.nesRomHeader[PrgPageOffset];
100 | byte chrPages = this.nesRomHeader[ChrPageOffset];
101 |
102 | Console.WriteLine("PRG Pages: " + prgPages);
103 | Console.WriteLine("CHR Pages: " + chrPages);
104 |
105 | int prgPageSize = prgPages * PrgPageSize;
106 | int chrPageSize = chrPages * ChrPageSize;
107 |
108 | if (this.verbose)
109 | {
110 | Console.WriteLine("PRG page size: {0}", prgPageSize);
111 | Console.WriteLine("CHR page size: {0}", chrPageSize);
112 | }
113 |
114 | int romSize = prgPageSize + chrPageSize + NesHeaderLength;
115 | Console.WriteLine("Total NES rom size: " + romSize + " Bytes");
116 |
117 | // Fix the NES header
118 | Console.WriteLine("Fixing VC NES Header...");
119 | this.nesRomHeader[BrokenNesHeaderOffset] = CharacterBreak;
120 |
121 | Console.WriteLine("Getting rom data...");
122 | this.nesRomData = br.ReadBytes(romSize - NesHeaderLength);
123 |
124 | Console.WriteLine("Writing to " + this.extractedRomPath + "...");
125 |
126 | using (BinaryWriter bw = new BinaryWriter(File.Open(this.extractedRomPath, FileMode.Create)))
127 | {
128 | Console.WriteLine("Writing NES rom header...");
129 | bw.Write(this.nesRomHeader, 0, NesHeaderLength);
130 | Console.WriteLine("Writing NES rom data...");
131 | bw.Write(this.nesRomData);
132 | }
133 |
134 | Console.WriteLine("NES rom has been created successfully at " + this.extractedRomPath);
135 | }
136 |
137 | return this.extractedRomPath;
138 | }
139 |
140 | ///
141 | /// Whether the associated rom is valid.
142 | ///
143 | /// true if valid, false otherwise.
144 | public bool IsValidRom()
145 | {
146 | Console.WriteLine("Checking if this is an NES VC title...");
147 |
148 | // First check if this is a valid ELF file:
149 | if (this.rpxFile != null)
150 | {
151 | Console.WriteLine("Checking " + this.rpxFile.DecompressedPath + "...");
152 | if (!File.Exists(this.rpxFile.DecompressedPath))
153 | {
154 | Console.WriteLine("Could not find decompressed RPX at " + this.rpxFile.DecompressedPath);
155 | return false;
156 | }
157 |
158 | byte[] headerBuffer = new byte[NesHeaderLength];
159 |
160 | // Search the decompressed RPX file for the NES header
161 | using FileStream fs = new FileStream(this.rpxFile.DecompressedPath, FileMode.Open, FileAccess.Read);
162 | using BinaryReader br = new BinaryReader(fs, new ASCIIEncoding());
163 | while (br.BaseStream.Position != br.BaseStream.Length)
164 | {
165 | byte[] buffer = br.ReadBytes(NesHeaderLength);
166 |
167 | // If the buffer matches the first byte of the NES header, check the following 15 bytes
168 | if (buffer[0] == NesHeaderCheck[0])
169 | {
170 | Array.Copy(buffer, headerBuffer, NesHeaderLength);
171 |
172 | // Check the rest of the signature
173 | if (headerBuffer[1] == NesHeaderCheck[1] && headerBuffer[2] == NesHeaderCheck[2])
174 | {
175 | bool headerValid = true;
176 |
177 | // Ensure the last 8 bytes of the header are 0x00
178 | for (int i = 0; i < 8; i++)
179 | {
180 | if (headerBuffer[i + 8] != 0x00)
181 | {
182 | headerValid = false;
183 | }
184 | }
185 |
186 | if (headerValid)
187 | {
188 | // The rom position is a header length before the current stream position
189 | this.romPosition = br.BaseStream.Position - NesHeaderLength;
190 | this.vcNamePosition = this.romPosition - 16;
191 | Array.Copy(headerBuffer, 0, this.nesRomHeader, 0, NesHeaderLength);
192 | Console.WriteLine("NES Rom Detected!");
193 | return true;
194 | }
195 | }
196 | }
197 | }
198 | }
199 |
200 | Console.WriteLine("Not an NES VC Title");
201 |
202 | return false;
203 | }
204 | }
205 | }
206 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/RomExtractors/DsVcExtractor.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.RomExtractors
2 | {
3 | using System;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Security.Cryptography;
7 | using System.Text;
8 | using System.Xml.Linq;
9 | using WiiuVcExtractor.FileTypes;
10 |
11 | ///
12 | /// DS VC rom extractor.
13 | ///
14 | public class DsVcExtractor : IRomExtractor
15 | {
16 | ///
17 | /// Beginning of apparent junk data.
18 | ///
19 | public const int StartJunkOffset = 0x1000;
20 |
21 | ///
22 | /// Beginning of game data, just after end of junk.
23 | ///
24 | public const int StartGameOffset = 0x4000;
25 |
26 | // Name of ds rom name dictionary
27 | private const string DSDictionaryXmlPath = "dsromnames.xml";
28 |
29 | // Nintendo logo at 0xC0
30 | private readonly byte[] nintenLogo =
31 | {
32 | 0x24, 0xFF, 0xAE, 0x51, 0x69, 0x9A,
33 | 0xA2, 0x21, 0x3D, 0x84, 0x82, 0x0A, 0x84, 0xE4, 0x09,
34 | 0xAD, 0x11, 0x24, 0x8B, 0x98, 0xC0, 0x81, 0x7F, 0x21,
35 | 0xA3, 0x52, 0xBE, 0x19, 0x93, 0x09, 0xCE, 0x20, 0x10,
36 | 0x46, 0x4A, 0x4A, 0xF8, 0x27, 0x31, 0xEC, 0x58, 0xC7,
37 | 0xE8, 0x33, 0x82, 0xE3, 0xCE, 0xBF, 0x85, 0xF4, 0xDF,
38 | 0x94, 0xCE, 0x4B, 0x09, 0xC1, 0x94, 0x56, 0x8A, 0xC0,
39 | 0x13, 0x72, 0xA7, 0xFC, 0x9F, 0x84, 0x4D, 0x73, 0xA3,
40 | 0xCA, 0x9A, 0x61, 0x58, 0x97, 0xA3, 0x27, 0xFC, 0x03,
41 | 0x98, 0x76, 0x23, 0x1D, 0xC7, 0x61, 0x03, 0x04, 0xAE,
42 | 0x56, 0xBF, 0x38, 0x84, 0x00, 0x40, 0xA7, 0x0E, 0xFD,
43 | 0xFF, 0x52, 0xFE, 0x03, 0x6F, 0x95, 0x30, 0xF1, 0x97,
44 | 0xFB, 0xC0, 0x85, 0x60, 0xD6, 0x80, 0x25, 0xA9, 0x63,
45 | 0xBE, 0x03, 0x01, 0x4E, 0x38, 0xE2, 0xF9, 0xA2, 0x34,
46 | 0xFF, 0xBB, 0x3E, 0x03, 0x44, 0x78, 0x00, 0x90, 0xCB,
47 | 0x88, 0x11, 0x3A, 0x94, 0x65, 0xC0, 0x7C, 0x63, 0x87,
48 | 0xF0, 0x3C, 0xAF, 0xD6, 0x25, 0xE4, 0x8B, 0x38, 0x0A,
49 | 0xAC, 0x72, 0x21, 0xD4, 0xF8, 0x07,
50 | };
51 |
52 | // First 8 bytes of secure area
53 | private readonly byte[] decSecArea =
54 | {
55 | 0xFF, 0xDE, 0xFF, 0xE7, 0xFF, 0xDE,
56 | 0xFF, 0xE7,
57 | };
58 |
59 | // Path to ds rom name dictionary
60 | private readonly string dsDictionaryPath;
61 |
62 | // Array of ds game serial numbers
63 | private readonly string[] dsMD5;
64 |
65 | // Array of ds game titles
66 | private readonly string[] dsGameTitles;
67 |
68 | // The file whose location will be accessed for reading data
69 | private readonly SrlFile srlFile;
70 |
71 | // VC names
72 | private readonly string vcName;
73 |
74 | private readonly bool verbose = false;
75 |
76 | // Game data to be edited and written to output file
77 | private byte[] game;
78 |
79 | // Location of output file
80 | private string finalPath;
81 |
82 | // DS name
83 | private string dsName;
84 |
85 | private bool hasName;
86 |
87 | ///
88 | /// Initializes a new instance of the class.
89 | ///
90 | /// SRL file to extract.
91 | /// Whether to provide verbose output.
92 | public DsVcExtractor(SrlFile srlFile, bool verbose)
93 | {
94 | this.srlFile = srlFile;
95 | this.verbose = verbose;
96 |
97 | this.dsDictionaryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DSDictionaryXmlPath);
98 |
99 | XElement dataFile = XElement.Load(this.dsDictionaryPath);
100 | this.dsMD5 = (from rom in dataFile.Descendants("rom")
101 | select (string)rom.Attribute("md5")).ToArray();
102 | this.dsGameTitles = (from rom in dataFile.Descendants("game")
103 | select (string)rom.Attribute("name")).ToArray();
104 |
105 | this.finalPath = Path.GetFileNameWithoutExtension(srlFile.Path) + "nds";
106 |
107 | this.vcName = Path.GetFileNameWithoutExtension(srlFile.Path);
108 | this.dsName = this.vcName;
109 |
110 | this.hasName = false;
111 | }
112 |
113 | ///
114 | /// Check for the Nintendo logo at offsets 0xC0 to 0x15B.
115 | ///
116 | /// Whether the provided SRL file is a valid rom.
117 | public bool IsValidRom()
118 | {
119 | Console.WriteLine("Checking if this is a Nintendo DS VC title...");
120 |
121 | if (this.srlFile != null)
122 | {
123 | if (!File.Exists(this.srlFile.Path))
124 | {
125 | Console.WriteLine("Could not find SRL file at " +
126 | this.srlFile.Path);
127 | return false;
128 | }
129 |
130 | using FileStream fs = new FileStream(this.srlFile.Path, FileMode.Open, FileAccess.Read);
131 | using BinaryReader br = new BinaryReader(fs, new ASCIIEncoding());
132 |
133 | // Read up to 0xC0
134 | br.ReadBytes(0xC0);
135 |
136 | // Check Nintendo logo
137 | for (int i = 0; i < this.nintenLogo.Length; i++)
138 | {
139 | if (this.nintenLogo[i] != br.ReadByte())
140 | {
141 | Console.WriteLine("Not a valid DS VC Title");
142 | return false;
143 | }
144 | }
145 |
146 | Console.WriteLine("DS Rom Detected!");
147 | return true;
148 | }
149 |
150 | Console.WriteLine("Not a DS VC Title");
151 | return false;
152 | }
153 |
154 | ///
155 | /// Extracts DS ROM from SRL file.
156 | /// TODO: Finish decryption.
157 | ///
158 | /// Path to extracted rom.
159 | public string ExtractRom()
160 | {
161 | // Reads in game for editing
162 | using (FileStream fs = new FileStream(this.srlFile.Path, FileMode.Open, FileAccess.Read))
163 | {
164 | using BinaryReader br = new BinaryReader(fs, new ASCIIEncoding());
165 | this.game = br.ReadBytes((int)fs.Length);
166 | }
167 |
168 | Console.WriteLine("Overwriting junk data...");
169 |
170 | // Overwrites junk data with zeroes
171 | for (int i = StartJunkOffset; i < StartGameOffset; i++)
172 | {
173 | this.game[i] = 0x00;
174 | }
175 |
176 | // First, check if encrypted - otherwise, skip decryption
177 | bool decCheck = true;
178 |
179 | for (int i = 0; i < 8 && decCheck; i++)
180 | {
181 | if (this.decSecArea[i] != this.game[0x4000 + i])
182 | {
183 | decCheck = false;
184 | }
185 | }
186 |
187 | // TODO - actually implement the decryption lol
188 | //
189 | // If encrypted, proceed with decryption
190 | if (!decCheck)
191 | {
192 | }
193 |
194 | // Identify name of file using MD5 hash and set as output path
195 | if (this.verbose)
196 | {
197 | Console.WriteLine("Getting MD5 hash.");
198 | }
199 |
200 | MD5 md5Hash = MD5.Create();
201 | byte[] data = md5Hash.ComputeHash(this.game);
202 | StringBuilder sBuilder = new StringBuilder();
203 | for (int i = 0; i < data.Length; i++)
204 | {
205 | sBuilder.Append(data[i].ToString("x2"));
206 | }
207 |
208 | string hashString = sBuilder.ToString();
209 |
210 | Console.WriteLine("MD5 of overwritten file is " + hashString);
211 |
212 | // Attempt to find matching MD5 and then gametitle
213 | // IF this fails, the SRL file name is used instead
214 | for (int i = 0; i < this.dsMD5.Length; i++)
215 | {
216 | if (hashString.ToUpper().CompareTo(this.dsMD5[i]) == 0)
217 | {
218 | this.finalPath = this.finalPath.Substring(0, this.finalPath.Length - Path.GetFileName(this.finalPath).Length);
219 |
220 | this.finalPath += this.dsGameTitles[i] + ".nds";
221 |
222 | this.dsName = Path.GetFileNameWithoutExtension(this.finalPath);
223 | this.hasName = true;
224 |
225 | i = this.dsMD5.Length;
226 | }
227 | }
228 |
229 | Console.WriteLine("Virtual Console Title: " + this.vcName);
230 | if (this.hasName)
231 | {
232 | Console.WriteLine("DS Title: " + this.dsName);
233 | }
234 | else
235 | {
236 | Console.WriteLine("DS title not found");
237 | }
238 |
239 | Console.WriteLine("Writing game data...");
240 |
241 | // Ouputs final game file
242 | using (FileStream fs = new FileStream(this.finalPath, FileMode.Create))
243 | {
244 | using BinaryWriter bw = new BinaryWriter(fs, new ASCIIEncoding());
245 | bw.Write(this.game);
246 | }
247 |
248 | Console.WriteLine("DS rom has been created successfully at " +
249 | Path.GetFileName(this.finalPath));
250 |
251 | // Return location of final game file to Program.cs
252 | return Path.GetFileName(this.finalPath);
253 | }
254 | }
255 | }
256 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/Libraries/EndianUtility.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.Libraries
2 | {
3 | using System;
4 | using System.IO;
5 |
6 | ///
7 | /// Provides various helper methods for converting between big-endian and little-endian data.
8 | ///
9 | public static class EndianUtility
10 | {
11 | static EndianUtility()
12 | {
13 | Endianness = BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian;
14 | }
15 |
16 | ///
17 | /// Gets or sets endianness of the current system.
18 | ///
19 | public static Endianness Endianness { get; set; }
20 |
21 | ///
22 | /// Reverses a big-endian byte array if on a little-endian system to convert it to big-endian.
23 | /// If the current system is big-endian, no modification is performed.
24 | ///
25 | /// byte array to potentially reverse.
26 | /// big-endian byte array.
27 | public static byte[] ReverseBE(this byte[] b)
28 | {
29 | // Only reverse if we are on a little endian system
30 | if (Endianness == Endianness.LittleEndian)
31 | {
32 | Array.Reverse(b);
33 | }
34 |
35 | return b;
36 | }
37 |
38 | ///
39 | /// Reverses a byte array if on a big-endian system to convert it to little-endian.
40 | /// If the current system is little-endian, no modification is performed.
41 | ///
42 | /// byte array to potentially reverse.
43 | /// little-endian byte array.
44 | public static byte[] ReverseLE(this byte[] b)
45 | {
46 | // Only reverse if we are on a big endian system
47 | if (Endianness == Endianness.BigEndian)
48 | {
49 | Array.Reverse(b);
50 | }
51 |
52 | return b;
53 | }
54 |
55 | ///
56 | /// Reads an unsigned 16-bit integer from a big-endian source.
57 | ///
58 | /// binary reader used to read the data.
59 | /// unsigned 16-bit integer in current system endianness.
60 | public static ushort ReadUInt16BE(this BinaryReader br)
61 | {
62 | return BitConverter.ToUInt16(br.ReadBytesRequired(sizeof(ushort)).ReverseBE(), 0);
63 | }
64 |
65 | ///
66 | /// Reads a signed 16-bit integer from a big-endian source.
67 | ///
68 | /// binary reader used to read the data.
69 | /// signed 16-bit integer in current system endianness.
70 | public static short ReadInt16BE(this BinaryReader br)
71 | {
72 | return BitConverter.ToInt16(br.ReadBytesRequired(sizeof(short)).ReverseBE(), 0);
73 | }
74 |
75 | ///
76 | /// Reads an unsigned 32-bit integer from a big-endian source.
77 | ///
78 | /// binary reader used to read the data.
79 | /// unsigned 32-bit integer in current system endianness.
80 | public static uint ReadUInt32BE(this BinaryReader br)
81 | {
82 | return BitConverter.ToUInt32(br.ReadBytesRequired(sizeof(uint)).ReverseBE(), 0);
83 | }
84 |
85 | ///
86 | /// Reads a signed 32-bit integer from a big-endian source.
87 | ///
88 | /// binary reader used to read the data.
89 | /// signed 32-bit integer in current system endianness.
90 | public static int ReadInt32BE(this BinaryReader br)
91 | {
92 | return BitConverter.ToInt32(br.ReadBytesRequired(sizeof(int)).ReverseBE(), 0);
93 | }
94 |
95 | ///
96 | /// Writes an unsigned 16-bit integer in big-endian format.
97 | ///
98 | /// binary reader used to write the data.
99 | /// value to write.
100 | public static void WriteUInt16BE(this BinaryWriter bw, ushort value)
101 | {
102 | bw.Write(BitConverter.GetBytes(value).ReverseBE());
103 | }
104 |
105 | ///
106 | /// Writes an unsigned 32-bit integer in big-endian format.
107 | ///
108 | /// binary reader used to write the data.
109 | /// value to write.
110 | public static void WriteUInt32BE(this BinaryWriter bw, uint value)
111 | {
112 | bw.Write(BitConverter.GetBytes(value).ReverseBE());
113 | }
114 |
115 | ///
116 | /// Reads an unsigned 16-bit integer from a little-endian source.
117 | ///
118 | /// binary reader used to read the data.
119 | /// unsigned 16-bit integer in current system endianness.
120 | public static ushort ReadUInt16LE(this BinaryReader br)
121 | {
122 | return BitConverter.ToUInt16(br.ReadBytesRequired(sizeof(ushort)).ReverseLE(), 0);
123 | }
124 |
125 | ///
126 | /// Reads a signed 16-bit integer from a little-endian source.
127 | ///
128 | /// binary reader used to read the data.
129 | /// signed 16-bit integer in current system endianness.
130 | public static short ReadInt16LE(this BinaryReader br)
131 | {
132 | return BitConverter.ToInt16(br.ReadBytesRequired(sizeof(short)).ReverseLE(), 0);
133 | }
134 |
135 | ///
136 | /// Reads an unsigned 24-bit integer from a little-endian source.
137 | ///
138 | /// binary reader used to read the data.
139 | /// unsigned 24-bit integer in current system endianness.
140 | public static uint ReadUInt24LE(this BinaryReader br)
141 | {
142 | byte[] returnArray = new byte[4];
143 | byte[] buffer = br.ReadBytesRequired(3);
144 |
145 | returnArray[0] = buffer[0];
146 | returnArray[1] = buffer[1];
147 | returnArray[2] = buffer[2];
148 | returnArray[3] = 0;
149 |
150 | return BitConverter.ToUInt32(returnArray.ReverseLE(), 0);
151 | }
152 |
153 | ///
154 | /// Reads an unsigned 32-bit integer from a little-endian source.
155 | ///
156 | /// binary reader used to read the data.
157 | /// unsigned 32-bit integer in current system endianness.
158 | public static uint ReadUInt32LE(this BinaryReader br)
159 | {
160 | return BitConverter.ToUInt32(br.ReadBytesRequired(sizeof(uint)).ReverseLE(), 0);
161 | }
162 |
163 | ///
164 | /// Reads a signed 32-bit integer from a little-endian source.
165 | ///
166 | /// binary reader used to read the data.
167 | /// signed 32-bit integer in current system endianness.
168 | public static int ReadInt32LE(this BinaryReader br)
169 | {
170 | return BitConverter.ToInt32(br.ReadBytesRequired(sizeof(int)).ReverseLE(), 0);
171 | }
172 |
173 | ///
174 | /// Writes an unsigned 16-bit integer in little-endian format.
175 | ///
176 | /// binary reader used to write the data.
177 | /// value to write.
178 | public static void WriteUInt16LE(this BinaryWriter bw, ushort value)
179 | {
180 | bw.Write(BitConverter.GetBytes(value).ReverseLE());
181 | }
182 |
183 | ///
184 | /// Writes an unsigned 32-bit integer in little-endian format.
185 | ///
186 | /// binary reader used to write the data.
187 | /// value to write.
188 | public static void WriteUInt32LE(this BinaryWriter bw, uint value)
189 | {
190 | bw.Write(BitConverter.GetBytes(value).ReverseLE());
191 | }
192 |
193 | ///
194 | /// Reads a certain number of bytes from a stream and throws an exception is fewer bytes are received.
195 | ///
196 | /// binary reader used to read the data.
197 | /// number of bytes to read.
198 | /// bytes read.
199 | public static byte[] ReadBytesRequired(this BinaryReader br, int byteCount)
200 | {
201 | var result = br.ReadBytes(byteCount);
202 |
203 | if (result.Length != byteCount)
204 | {
205 | throw new EndOfStreamException(string.Format("{0} bytes required from stream, but only {1} returned.", byteCount, result.Length));
206 | }
207 |
208 | return result;
209 | }
210 |
211 | ///
212 | /// Reads a null-terminated string from a stream.
213 | ///
214 | /// binary reader used to read the data.
215 | /// null-terminated string.
216 | public static string ReadNullTerminatedString(this BinaryReader stream)
217 | {
218 | string str = string.Empty;
219 | char ch;
220 | while ((int)(ch = stream.ReadChar()) != 0)
221 | {
222 | str = str + ch;
223 | }
224 |
225 | return str;
226 | }
227 |
228 | ///
229 | /// Reads a null-terminated string from a byte array.
230 | ///
231 | /// byte array to read.
232 | /// null-terminated string.
233 | public static string ReadNullTerminatedString(this byte[] bytes)
234 | {
235 | string str = string.Empty;
236 |
237 | using (MemoryStream ms = new MemoryStream(bytes))
238 | {
239 | using BinaryReader br = new BinaryReader(ms);
240 | str = br.ReadNullTerminatedString();
241 | }
242 |
243 | return str;
244 | }
245 | }
246 | }
247 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/Libraries/SnesPcmExtractor.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.Libraries
2 | {
3 | using System;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 |
8 | ///
9 | /// SNES PCM audio extractor class.
10 | ///
11 | public class SnesPcmExtractor
12 | {
13 | private const int PCMBlockLength = 9;
14 | private const long DMIN = 2147483648;
15 | private static readonly byte[] PCMSignature = { 0x50, 0x43, 0x4D, 0x46 }; // PCMF in ASCII
16 |
17 | private readonly byte[] romData;
18 | private readonly byte[] rawPcmData;
19 | private readonly byte[] brrBuffer;
20 | private byte[] pcmData;
21 | private byte[] brrData;
22 | private int pcmDataOffset;
23 |
24 | private int p1;
25 | private int p2;
26 |
27 | ///
28 | /// Initializes a new instance of the class.
29 | ///
30 | /// bytes of SNES rom data.
31 | /// bytes of raw PCM audio data.
32 | public SnesPcmExtractor(byte[] snesRomData, byte[] rawPcmData)
33 | {
34 | this.p1 = 0;
35 | this.p2 = 0;
36 | this.brrBuffer = new byte[PCMBlockLength];
37 | this.pcmDataOffset = -1;
38 | this.romData = snesRomData;
39 | this.rawPcmData = rawPcmData;
40 | }
41 |
42 | ///
43 | /// Extracts PCM audio data and injects it as BRR into the rom data.
44 | ///
45 | /// rom data with all PCM audio converted to BRR.
46 | public byte[] ExtractPcmData()
47 | {
48 | Console.WriteLine("Extracting PCM Data...");
49 |
50 | byte[] processedRom = new byte[this.romData.Length];
51 | Array.Copy(this.romData, processedRom, this.romData.Length);
52 |
53 | // Find the first PCMSignature in the rom
54 | for (int i = 0; i < this.romData.Length; i++)
55 | {
56 | if (this.romData[i] == PCMSignature[0])
57 | {
58 | if (this.romData.Length >= i + PCMSignature.Length)
59 | {
60 | if (this.romData[i + 1] == PCMSignature[1] && this.romData[i + 2] == PCMSignature[2] && this.romData[i + 3] == PCMSignature[3])
61 | {
62 | Console.WriteLine("Found the first PCM offset at " + i);
63 | this.pcmDataOffset = i;
64 | break;
65 | }
66 | }
67 | }
68 | }
69 |
70 | if (this.pcmDataOffset == -1)
71 | {
72 | Console.WriteLine("No PCM data found, continuing...");
73 | return processedRom;
74 | }
75 |
76 | Console.WriteLine("Reading PCM data into memory...");
77 |
78 | int pcmDataLength = this.romData.Length - this.pcmDataOffset;
79 |
80 | this.pcmData = new byte[pcmDataLength];
81 | this.brrData = new byte[pcmDataLength];
82 |
83 | // Copy all the data from pcmDataOffset into pcmData
84 | Array.Copy(this.romData, this.pcmDataOffset, this.pcmData, 0, pcmDataLength);
85 | Array.Copy(this.pcmData, this.brrData, pcmDataLength);
86 |
87 | using (MemoryStream ms = new MemoryStream(this.pcmData))
88 | {
89 | using BinaryReader br = new BinaryReader(ms, new ASCIIEncoding());
90 | long lastPcmBlockOffset = 0;
91 |
92 | // Continue reading PCM data until we run out
93 | while (true)
94 | {
95 | long index = br.BaseStream.Position;
96 | long romOffset = index + this.pcmDataOffset;
97 |
98 | if (index + PCMSignature.Length >= br.BaseStream.Length)
99 | {
100 | break;
101 | }
102 |
103 | byte[] signatureBuffer = br.ReadBytes(PCMSignature.Length);
104 |
105 | // If we didn't find a signature at this location, seek to the next character and restart the loop
106 | if (!signatureBuffer.SequenceEqual(PCMSignature))
107 | {
108 | br.BaseStream.Seek(-3, SeekOrigin.Current);
109 | continue;
110 | }
111 |
112 | long pcmBlockOffset = br.ReadUInt32LE();
113 | pcmBlockOffset &= 0xffffff;
114 |
115 | if (((pcmBlockOffset % 16) != 0) || pcmBlockOffset < lastPcmBlockOffset)
116 | {
117 | pcmBlockOffset = lastPcmBlockOffset + 16;
118 | }
119 |
120 | // Encode the brr block
121 | this.EncodeBrrBlock(pcmBlockOffset);
122 |
123 | if ((this.pcmData[index + 7] & 1) == 1)
124 | {
125 | this.brrBuffer[0] |= 1;
126 | }
127 |
128 | if ((this.pcmData[index + 7] & 2) == 2)
129 | {
130 | this.brrBuffer[0] |= 2;
131 | }
132 |
133 | using (MemoryStream msb = new MemoryStream(processedRom))
134 | {
135 | using BinaryWriter bw = new BinaryWriter(msb, new ASCIIEncoding());
136 | bw.BaseStream.Seek(romOffset, SeekOrigin.Begin);
137 |
138 | bw.Write(this.brrBuffer);
139 | }
140 |
141 | br.ReadBytes(1);
142 |
143 | lastPcmBlockOffset = pcmBlockOffset;
144 | }
145 | }
146 |
147 | return processedRom;
148 | }
149 |
150 | private void EncodeBrrBlock(long pcmoffset)
151 | {
152 | using MemoryStream ms = new MemoryStream(this.rawPcmData);
153 | using BinaryReader br = new BinaryReader(ms, new ASCIIEncoding());
154 | short[] samples = new short[16];
155 |
156 | if ((pcmoffset * 2) + 32 > br.BaseStream.Length)
157 | {
158 | Console.WriteLine("INVALID PCM OFFSET: " + (pcmoffset * 2));
159 | return;
160 | }
161 |
162 | br.BaseStream.Seek(pcmoffset * 2, SeekOrigin.Begin);
163 |
164 | for (int i = 0; i < 16; i++)
165 | {
166 | samples[i] = br.ReadInt16BE();
167 | }
168 |
169 | // Set all elements in the brrBuffer to 0
170 | Array.Clear(this.brrBuffer, 0, this.brrBuffer.Length);
171 |
172 | this.AdpcmBlockMash(samples);
173 | }
174 |
175 | private void AdpcmBlockMash(short[] pcmSamples)
176 | {
177 | int smin = 0;
178 | int kmin = 0;
179 | double dmin = DMIN;
180 |
181 | for (int s = 13; s >= 0; s--)
182 | {
183 | for (int k = 0; k < 4; k++)
184 | {
185 | double d = this.AdpcmMash(s, k, pcmSamples, false);
186 | if (d < dmin)
187 | {
188 | kmin = k;
189 | dmin = d;
190 | smin = s;
191 | }
192 |
193 | if (dmin == 0.0)
194 | {
195 | break;
196 | }
197 | }
198 |
199 | if (dmin == 0.0)
200 | {
201 | break;
202 | }
203 | }
204 |
205 | this.brrBuffer[0] = (byte)((smin << 4) | (kmin << 2));
206 | this.AdpcmMash(smin, kmin, pcmSamples, true);
207 | }
208 |
209 | private double AdpcmMash(int shiftAmount, int filter, short[] pcmSamples, bool write)
210 | {
211 | double d2 = 0.0;
212 | int vlin = 0;
213 | int l1 = this.p1;
214 | int l2 = this.p2;
215 | int step = 1 << shiftAmount;
216 |
217 | for (int i = 0; i < 16; i++)
218 | {
219 | switch (filter)
220 | {
221 | case 0:
222 | break;
223 |
224 | case 1:
225 | vlin = l1 >> 1;
226 | vlin += (-l1) >> 5;
227 | break;
228 |
229 | case 2:
230 | vlin = l1;
231 | vlin += (-(l1 + (l1 >> 1))) >> 5;
232 | vlin -= l2 >> 1;
233 | vlin += l2 >> 5;
234 | break;
235 |
236 | default:
237 | vlin = l1;
238 | vlin += (-(l1 + (l1 << 2) + (l1 << 3))) >> 7;
239 | vlin -= l2 >> 1;
240 | vlin += (l2 + (l2 >> 1)) >> 4;
241 | break;
242 | }
243 |
244 | int d = (pcmSamples[i] >> 1) - vlin;
245 | int da = Math.Abs(d);
246 |
247 | if (da > 16384 && da < 32768)
248 | {
249 | d -= 32768 * (d >> 24);
250 | }
251 |
252 | int dp = d + (step << 2) + (step >> 2);
253 | int c = 0;
254 |
255 | if (dp > 0)
256 | {
257 | if (step > 1)
258 | {
259 | c = dp / (step >> 1);
260 | }
261 | else
262 | {
263 | c = dp << 1;
264 | }
265 |
266 | if (c > 15)
267 | {
268 | c = 15;
269 | }
270 | }
271 |
272 | c -= 8;
273 | dp = c << (shiftAmount - 1);
274 |
275 | if (shiftAmount > 12)
276 | {
277 | dp = (dp >> 14) & ~0x7FF;
278 | }
279 |
280 | c &= 0x0f;
281 | l2 = l1;
282 | l1 = this.Sshort(this.Clamp16(vlin + dp) * 2);
283 |
284 | d = pcmSamples[i] - l1;
285 | d2 += (double)d * (double)d;
286 |
287 | if (write)
288 | {
289 | this.brrBuffer[(i >> 1) + 1] |= (byte)(c << (4 - ((i & 0x01) << 2)));
290 | }
291 | }
292 |
293 | if (write)
294 | {
295 | this.p1 = l1;
296 | this.p2 = l2;
297 | }
298 |
299 | return d2;
300 | }
301 |
302 | private int Sshort(int n)
303 | {
304 | if (n > 0x7FFF)
305 | {
306 | return n - 0x10000;
307 | }
308 | else if (n < -0x8000)
309 | {
310 | return n & 0x7FFF;
311 | }
312 |
313 | return n;
314 | }
315 |
316 | private int Clamp16(int n)
317 | {
318 | if (n > 0x7FFF)
319 | {
320 | return 0x7FFF - (n >> 24);
321 | }
322 |
323 | return n;
324 | }
325 | }
326 | }
327 |
--------------------------------------------------------------------------------
/WiiuVcExtractor/FileTypes/PsbEntryTable.cs:
--------------------------------------------------------------------------------
1 | namespace WiiuVcExtractor.FileTypes
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Text;
7 | using WiiuVcExtractor.Libraries;
8 |
9 | ///
10 | /// PSB file entry table.
11 | ///
12 | public class PsbEntryTable
13 | {
14 | private readonly List names;
15 | private readonly List offsets;
16 | private readonly List fileInfoEntries;
17 | private readonly float version;
18 | private readonly string id;
19 |
20 | ///
21 | /// Initializes a new instance of the class.
22 | ///
23 | /// PSB file content.
24 | /// offset to the beginning of the file info entries in bytes.
25 | /// filename string list.
26 | /// PSB string string list.
27 | public PsbEntryTable(byte[] psbData, long entriesOffset, List nameStrings, List strings)
28 | {
29 | long entryDataOffset = 0;
30 |
31 | // Initialize the name table from the passed data
32 | using MemoryStream ms = new MemoryStream(psbData);
33 | ms.Seek(entriesOffset, SeekOrigin.Begin);
34 |
35 | byte type = (byte)ms.ReadByte();
36 | if (type != 0x21)
37 | {
38 | throw new InvalidOperationException("The type of the entries data is incorrect.");
39 | }
40 |
41 | this.names = this.ReadEntryTableValues(ms);
42 | this.offsets = this.ReadEntryTableValues(ms);
43 |
44 | entryDataOffset = ms.Position;
45 |
46 | if (this.names.Count != this.offsets.Count)
47 | {
48 | throw new InvalidOperationException("The lengths of the entry names list and the entry offsets list differ.");
49 | }
50 |
51 | for (int i = 0; i < this.names.Count; i++)
52 | {
53 | string nameString = nameStrings[(int)this.names[i]];
54 | long offset = entryDataOffset + this.offsets[i];
55 |
56 | ms.Seek(offset, SeekOrigin.Begin);
57 |
58 | switch (nameString)
59 | {
60 | case "id":
61 | this.id = this.ReadId(ms, strings);
62 | break;
63 |
64 | case "version":
65 | this.version = this.ReadVersion(ms);
66 | break;
67 |
68 | case "file_info":
69 | this.fileInfoEntries = this.ReadFileInfo(ms, nameStrings);
70 | break;
71 | }
72 | }
73 | }
74 |
75 | ///
76 | /// Gets file info entries for the PSB file.
77 | ///
78 | public List FileInfoEntries
79 | {
80 | get { return this.fileInfoEntries; }
81 | }
82 |
83 | ///
84 | /// Gets PSB file version.
85 | ///
86 | public float Version
87 | {
88 | get { return this.version; }
89 | }
90 |
91 | ///
92 | /// Gets PSB file ID.
93 | ///
94 | public string Id
95 | {
96 | get { return this.id; }
97 | }
98 |
99 | private List ReadEntryTableValues(MemoryStream ms)
100 | {
101 | List valueList = new List();
102 |
103 | using (BinaryReader br = new BinaryReader(ms, new ASCIIEncoding(), true))
104 | {
105 | // get the offset information
106 | byte type = br.ReadByte();
107 |
108 | // Get the size of each object in bytes
109 | int countByteSize = type - 12;
110 | uint count = 0;
111 |
112 | if (countByteSize == 1)
113 | {
114 | count = br.ReadByte();
115 | }
116 | else if (countByteSize == 2)
117 | {
118 | count = EndianUtility.ReadUInt16LE(br);
119 | }
120 | else if (countByteSize == 4)
121 | {
122 | count = EndianUtility.ReadUInt32LE(br);
123 | }
124 |
125 | byte entrySizeType = br.ReadByte();
126 | int entryByteSize = entrySizeType - 12;
127 |
128 | uint value = 0;
129 |
130 | // Read in the values
131 | for (int i = 0; i < count; i++)
132 | {
133 | if (entryByteSize == 1)
134 | {
135 | value = br.ReadByte();
136 | }
137 | else if (entryByteSize == 2)
138 | {
139 | value = EndianUtility.ReadUInt16LE(br);
140 | }
141 | else if (entryByteSize == 4)
142 | {
143 | value = EndianUtility.ReadUInt32LE(br);
144 | }
145 |
146 | valueList.Add(value);
147 | }
148 | }
149 |
150 | return valueList;
151 | }
152 |
153 | private float ReadVersion(MemoryStream ms)
154 | {
155 | float returnFloat = 0.0F;
156 |
157 | using (BinaryReader br = new BinaryReader(ms, new ASCIIEncoding(), true))
158 | {
159 | byte type = br.ReadByte();
160 | if (type != 30)
161 | {
162 | throw new InvalidOperationException("The type of the version in the PSB file is not a 4-byte float.");
163 | }
164 |
165 | returnFloat = br.ReadSingle();
166 | }
167 |
168 | return returnFloat;
169 | }
170 |
171 | private string ReadId(MemoryStream ms, List strings)
172 | {
173 | uint stringIndex = 0;
174 |
175 | using (BinaryReader br = new BinaryReader(ms, new ASCIIEncoding(), true))
176 | {
177 | byte type = br.ReadByte();
178 | if (type < 21 || type > 24)
179 | {
180 | throw new InvalidOperationException("The type of the id in the PSB file is not an index into the strings array.");
181 | }
182 |
183 | int byteSize = type - 20;
184 |
185 | if (byteSize == 1)
186 | {
187 | stringIndex = br.ReadByte();
188 | }
189 | else if (byteSize == 2)
190 | {
191 | stringIndex = br.ReadUInt16LE();
192 | }
193 | else if (byteSize == 4)
194 | {
195 | stringIndex = br.ReadUInt32LE();
196 | }
197 | }
198 |
199 | if (stringIndex >= strings.Count)
200 | {
201 | throw new InvalidOperationException("The string index of the id in the PSB file is not a valid index in the strings array.");
202 | }
203 |
204 | return strings[(int)stringIndex];
205 | }
206 |
207 | private List ReadFileInfo(MemoryStream ms, List nameStrings)
208 | {
209 | List fileNameIndexes = new List();
210 | List fileOffsets = new List();
211 | List fileInfos = new List();
212 | long entryDataOffset = 0;
213 |
214 | using (BinaryReader br = new BinaryReader(ms, new ASCIIEncoding(), true))
215 | {
216 | byte type = br.ReadByte();
217 | if (type != 33)
218 | {
219 | throw new InvalidOperationException("The type of the id in the PSB file is not for a FileInfo section.");
220 | }
221 |
222 | fileNameIndexes = this.ReadEntryTableValues(ms);
223 | fileOffsets = this.ReadEntryTableValues(ms);
224 |
225 | entryDataOffset = ms.Position;
226 |
227 | if (fileNameIndexes.Count != fileOffsets.Count)
228 | {
229 | throw new InvalidOperationException("The lengths of the FileInfo entry names list and the FileInfo entry offsets list differ.");
230 | }
231 |
232 | // populate the file info list
233 | for (int i = 0; i < fileNameIndexes.Count; i++)
234 | {
235 | string nameString = nameStrings[(int)fileNameIndexes[i]];
236 | long offset = entryDataOffset + fileOffsets[i];
237 |
238 | ms.Seek(offset, SeekOrigin.Begin);
239 |
240 | byte fiType = br.ReadByte();
241 |
242 | if (fiType != 32)
243 | {
244 | throw new InvalidOperationException("The type of the id in the FileInfo section is not for an object array.");
245 | }
246 |
247 | List fiOffsets = new List();
248 | fiOffsets = this.ReadEntryTableValues(ms);
249 |
250 | long baseOffset = ms.Position;
251 |
252 | if (fiOffsets.Count != 2)
253 | {
254 | throw new InvalidOperationException("The number of FileInfo offsets if not 2.");
255 | }
256 |
257 | // Get the file offset and length
258 | uint fileOffset = 0;
259 | uint fileLength = 0;
260 |
261 | ms.Seek(baseOffset + fiOffsets[0], SeekOrigin.Begin);
262 | fileOffset = this.ReadFileInfoMetadata(ms);
263 |
264 | ms.Seek(baseOffset + fiOffsets[1], SeekOrigin.Begin);
265 | fileLength = this.ReadFileInfoMetadata(ms);
266 |
267 | PsbFileInfoEntry fi = new PsbFileInfoEntry(fileNameIndexes[i], fileLength, fileOffset);
268 | fileInfos.Add(fi);
269 | }
270 | }
271 |
272 | return fileInfos;
273 | }
274 |
275 | private uint ReadFileInfoMetadata(MemoryStream ms)
276 | {
277 | uint returnValue = 0;
278 |
279 | using (BinaryReader br = new BinaryReader(ms, new ASCIIEncoding(), true))
280 | {
281 | byte type = br.ReadByte();
282 |
283 | if (type == 4)
284 | {
285 | returnValue = 0;
286 | }
287 | else if (type >= 5 && type <= 12)
288 | {
289 | int intByteSize = type - 4;
290 |
291 | if (intByteSize == 1)
292 | {
293 | returnValue = br.ReadByte();
294 | }
295 | else if (intByteSize == 2)
296 | {
297 | returnValue = br.ReadUInt16LE();
298 | }
299 | else if (intByteSize == 3)
300 | {
301 | returnValue = br.ReadUInt24LE();
302 | }
303 | else if (intByteSize == 4)
304 | {
305 | returnValue = br.ReadUInt32LE();
306 | }
307 | else
308 | {
309 | Console.WriteLine("intByteSize: " + intByteSize);
310 | }
311 | }
312 | }
313 |
314 | return returnValue;
315 | }
316 | }
317 | }
318 |
--------------------------------------------------------------------------------