├── .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 | --------------------------------------------------------------------------------