├── TextureExtraction tool ├── Icon.ico ├── Scans │ ├── Helper │ │ └── ScanObjekt.cs │ ├── Results │ │ ├── ScanResults.cs │ │ └── FinalizeResults.cs │ ├── Options │ │ └── TextureExtractorOptions.cs │ ├── Compress.cs │ └── Cutter.cs ├── App.config ├── LICENSE.TXT ├── Data │ ├── ConsoleBar.cs │ └── LogBase.cs ├── AppSettings.cs └── DolphinTextureExtraction tool.csproj ├── lib ├── AuroraLip │ ├── DiscImage │ │ ├── RVZ │ │ │ ├── DiscTypes.cs │ │ │ ├── ImageType.cs │ │ │ ├── ExceptListT.cs │ │ │ ├── CompressionTypes.cs │ │ │ ├── RvzGroupT.cs │ │ │ ├── PartDataT.cs │ │ │ ├── RawDataT.cs │ │ │ ├── ExceptionT.cs │ │ │ ├── PartT.cs │ │ │ ├── RVZVersion.cs │ │ │ └── Header.cs │ │ ├── Revolution │ │ │ ├── SigTyp.cs │ │ │ ├── Ratings.cs │ │ │ ├── SignedBlobHeader.cs │ │ │ ├── HeaderBin.cs │ │ │ ├── WiiKey.cs │ │ │ └── CMD.cs │ │ └── Dolphin │ │ │ ├── Bi2Bin.cs │ │ │ ├── FSTBin.cs │ │ │ ├── GameHeader.cs │ │ │ └── BootBin.cs │ ├── Common │ │ ├── Enums │ │ │ ├── NotificationType.cs │ │ │ └── FormatType.cs │ │ ├── Reflection.cs │ │ ├── Interfaces │ │ │ ├── IFileSystemInfo.cs │ │ │ ├── IFileRequest.cs │ │ │ ├── IFileAccess.cs │ │ │ └── IDataTime.cs │ │ ├── Node │ │ │ ├── NodeProcessor.cs │ │ │ └── Interfaces │ │ │ │ └── IBinaryObjectNode.cs │ │ ├── Exceptions.cs │ │ ├── Events.cs │ │ ├── Extensions │ │ │ └── MiscEX.cs │ │ └── XORStream.cs │ ├── Palette │ │ ├── JUTTlut.cs │ │ └── Formats │ │ │ └── PLT0.cs │ ├── Texture │ │ ├── Interfaces │ │ │ ├── IImageInfo.cs │ │ │ └── IGXTextureInfo.cs │ │ ├── Enums │ │ │ ├── JUTTransparency.cs │ │ │ ├── WrapMode.cs │ │ │ ├── GXFilterMode.cs │ │ │ ├── GXPaletteFormat.cs │ │ │ └── UImageFormats.cs │ │ ├── J3D │ │ │ ├── Enums │ │ │ │ ├── TangentMode.cs │ │ │ │ └── LoopMode.cs │ │ │ └── NameTableIO.cs │ │ ├── Formats │ │ │ ├── GBIX.cs │ │ │ ├── GCIX.cs │ │ │ ├── TPX.cs │ │ │ ├── FIPAFTEX.cs │ │ │ ├── S3G.cs │ │ │ ├── TXD.cs │ │ │ ├── GCNT.cs │ │ │ ├── RES_NLG.cs │ │ │ ├── GCT0.cs │ │ │ ├── GTX1.cs │ │ │ ├── text.cs │ │ │ ├── PIM.cs │ │ │ ├── TXTR.cs │ │ │ ├── PIL.cs │ │ │ └── TDL0.cs │ │ ├── BlockFormats │ │ │ ├── I8Block.cs │ │ │ ├── IA4Block.cs │ │ │ ├── IA8Block.cs │ │ │ ├── RGB565Block.cs │ │ │ ├── RGB5A3Block.cs │ │ │ ├── I4Block.cs │ │ │ ├── I14Block.cs │ │ │ └── RGBA32Block.cs │ │ └── JUTTexture.cs │ ├── Compression │ │ ├── Algorithms │ │ │ ├── CRILAYLA.cs │ │ │ └── Zstd.cs │ │ └── CompressionReflection.cs │ ├── Archives │ │ └── Formats │ │ │ ├── BUG.cs │ │ │ ├── CMN.cs │ │ │ ├── pBin.cs │ │ │ ├── ONE_UN.cs │ │ │ ├── TXAG.cs │ │ │ ├── RVZ.cs │ │ │ ├── MEDB.cs │ │ │ ├── NEP.cs │ │ │ ├── FBC.cs │ │ │ ├── PCKG.cs │ │ │ ├── RSC.cs │ │ │ ├── GSAGTX.cs │ │ │ ├── PAK_FE.cs │ │ │ ├── RMHG.cs │ │ │ ├── FBTI.cs │ │ │ ├── FONT.cs │ │ │ ├── NLCL.cs │ │ │ ├── ONE_SB.cs │ │ │ ├── AFS.cs │ │ │ ├── NARC.cs │ │ │ ├── PKX.cs │ │ │ ├── CPK.cs │ │ │ ├── PAC.cs │ │ │ ├── PAKb.cs │ │ │ ├── RTDP.cs │ │ │ ├── PAK_TM2.cs │ │ │ ├── TSET.cs │ │ │ ├── ARC0.cs │ │ │ ├── RKV2.cs │ │ │ └── FTEX.cs │ └── AuroraLib.csproj ├── AFSLib │ ├── Entry.cs │ ├── StreamEntryInfo.cs │ ├── NullEntry.cs │ ├── HeaderMagicType.cs │ ├── NotificationType.cs │ ├── FileEntry.cs │ ├── AttributesInfoType.cs │ ├── AFSLib.csproj │ ├── StreamEntry.cs │ ├── LICENSE │ └── Utils.cs ├── LibCPK │ ├── LibCPK.csproj │ └── README.md └── Hack.io │ ├── Hack.io.csproj │ └── README.md ├── Benchmark ├── Benchmarker.cs ├── Benchmark.csproj └── Benchmarks │ ├── ReadUInt64.cs │ ├── ValuesRevers.cs │ ├── CopyByte.cs │ └── StringBuilder.cs ├── .gitattributes ├── LICENSE └── .editorconfig /TextureExtraction tool/Icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Venomalia/DolphinTextureExtraction-tool/HEAD/TextureExtraction tool/Icon.ico -------------------------------------------------------------------------------- /lib/AuroraLip/DiscImage/RVZ/DiscTypes.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.DiscImage.RVZ 2 | { 3 | public enum DiscTypes : uint 4 | { 5 | GameCube = 1, 6 | Wii = 2, 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /lib/AuroraLip/DiscImage/RVZ/ImageType.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.DiscImage.RVZ 2 | { 3 | public enum ImageType : uint 4 | { 5 | WIA = 1464418561, 6 | RVZ = 1381390849, 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /lib/AuroraLip/Common/Enums/NotificationType.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.Common 2 | { 3 | /// 4 | /// 5 | /// 6 | public enum NotificationType 7 | { 8 | Info, 9 | Warning, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/AuroraLip/DiscImage/RVZ/ExceptListT.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Cryptography; 2 | 3 | namespace AuroraLib.DiscImage.RVZ 4 | { 5 | public readonly struct ExceptListT 6 | { 7 | public readonly ushort Offset; 8 | public readonly UInt128 Hash; 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/AFSLib/Entry.cs: -------------------------------------------------------------------------------- 1 | namespace AFSLib 2 | { 3 | /// 4 | /// Abstract class that represents an entry. All types of entries derive from Entry. 5 | /// 6 | public abstract class Entry 7 | { 8 | internal abstract Stream GetStream(); 9 | } 10 | } -------------------------------------------------------------------------------- /lib/AuroraLip/DiscImage/RVZ/CompressionTypes.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.DiscImage.RVZ 2 | { 3 | public enum CompressionTypes : uint 4 | { 5 | None = 0, 6 | PURGE = 1, 7 | BZIP2 = 2, 8 | LZMA = 3, 9 | LZMA2 = 4, 10 | Zstandard = 5, 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/AuroraLip/DiscImage/Revolution/SigTyp.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.DiscImage.Revolution 2 | { 3 | /// 4 | /// Signature types 5 | /// 6 | public enum SigTyp : uint 7 | { 8 | RSA_4096 = 0x00010000, 9 | RSA_2048 = 0x00010001, 10 | EllipticCurve = 0x00010002, 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Benchmark/Benchmarker.cs: -------------------------------------------------------------------------------- 1 | using Benchmark.Benchmarks; 2 | using BenchmarkDotNet.Running; 3 | using System.Buffers.Binary; 4 | 5 | namespace Benchmark 6 | { 7 | public class Benchmarker 8 | { 9 | static void Main(string[] args) 10 | { 11 | var Result = BenchmarkRunner.Run(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/AFSLib/StreamEntryInfo.cs: -------------------------------------------------------------------------------- 1 | namespace AFSLib 2 | { 3 | internal struct StreamEntryInfo 4 | { 5 | public uint Offset; 6 | public string Name; 7 | public uint Size; 8 | public DateTime LastWriteTime; 9 | public uint UnknownAttribute; 10 | 11 | public bool IsNull => Offset == 0 || Size == 0; 12 | } 13 | } -------------------------------------------------------------------------------- /lib/AuroraLip/Common/Reflection.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Compression; 2 | using AuroraLib.Core.Interfaces; 3 | 4 | namespace AuroraLib.Common 5 | { 6 | public static class Reflection 7 | { 8 | public static FileAccessReflection FileAccess = new(); 9 | 10 | public static CompressionReflection Compression = new(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/AFSLib/NullEntry.cs: -------------------------------------------------------------------------------- 1 | namespace AFSLib 2 | { 3 | /// 4 | /// Class that represents an empty entry with no data. 5 | /// 6 | public class NullEntry : Entry 7 | { 8 | internal NullEntry() 9 | { 10 | 11 | } 12 | 13 | internal override Stream GetStream() 14 | { 15 | return null; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /lib/AuroraLip/Palette/JUTTlut.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Texture; 2 | 3 | namespace AuroraLib.Palette 4 | { 5 | public interface IJUTPalette 6 | { 7 | /// 8 | /// specifies how the data within the palette is stored. 9 | /// 10 | GXPaletteFormat Format { get; } 11 | 12 | /// 13 | /// Palette data 14 | /// 15 | byte[] Data { get; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/AuroraLip/Common/Interfaces/IFileSystemInfo.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Core.Interfaces; 2 | 3 | namespace AuroraLib.Common 4 | { 5 | /// 6 | /// Provides standard file properties. 7 | /// 8 | public interface IFileSystemInfo : IObjectName, IDataTime 9 | { 10 | /// 11 | /// Represents the full path of the directory or file. 12 | /// 13 | string FullPath { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/AuroraLip/Common/Interfaces/IFileRequest.cs: -------------------------------------------------------------------------------- 1 | using static AuroraLib.Common.Events; 2 | 3 | namespace AuroraLib.Common 4 | { 5 | /// 6 | /// Interface to request additional files if necessary. 7 | /// 8 | internal interface IFileRequest 9 | { 10 | /// 11 | /// Event that is called when the process needs an additional file. 12 | /// 13 | FileRequestDelegate FileRequest { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/AuroraLip/DiscImage/RVZ/RvzGroupT.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.DiscImage.RVZ 2 | { 3 | public readonly struct RvzGroupT 4 | { 5 | private readonly uint dataOffset; 6 | private readonly uint packtDataSize; 7 | public readonly uint PackedSize; 8 | 9 | public readonly uint DataOffset => dataOffset << 2; 10 | public readonly uint DataSize => packtDataSize & 0x7FFFFFFF; 11 | public readonly bool IsCompressed => (packtDataSize & 0x80000000) != 0; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/AuroraLip/DiscImage/Revolution/Ratings.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.DiscImage.Revolution 2 | { 3 | public struct Ratings 4 | { 5 | public byte CERO; 6 | public byte ESRB; 7 | private byte u0; 8 | public byte FSK; 9 | public byte PEGI; 10 | public byte PEGI_Finland; 11 | public byte PEGI_Portugal; 12 | public byte BBFC; 13 | public byte ACB; 14 | public byte GRAC; 15 | private readonly byte u1, u2, u3, u4, u5, u6; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/AuroraLip/DiscImage/Dolphin/Bi2Bin.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.DiscImage.Dolphin 2 | { 3 | public struct Bi2Bin 4 | { 5 | public uint DebugMonitorSize; //0 6 | public uint SimulatedMemorySize; // 25165824 7 | public uint ArgumentOffset; //0 8 | public uint DebugFlag; //0 9 | public uint TrackLocation; //0 10 | public uint TrackSize; //0 11 | public uint CountryCode; //2 12 | public uint Unknown_1; //1 13 | public uint Unknown_2; //1 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Interfaces/IImageInfo.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.Texture.Interfaces 2 | { 3 | /// 4 | /// Interface representing an image properties with width and height. 5 | /// 6 | public interface IImageInfo 7 | { 8 | /// 9 | /// The width of the image. 10 | /// 11 | int Width { get; } 12 | 13 | /// 14 | /// The height of the image. 15 | /// 16 | int Height { get; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/AuroraLip/Common/Interfaces/IFileAccess.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Core.Interfaces; 2 | 3 | namespace AuroraLib.Common 4 | { 5 | /// 6 | /// Simple interface for a file access. 7 | /// 8 | public interface IFileAccess : IFormatRecognition 9 | { 10 | /// 11 | /// Can be read 12 | /// 13 | bool CanRead { get; } 14 | 15 | /// 16 | /// Can be Write 17 | /// 18 | bool CanWrite { get; } 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /TextureExtraction tool/Scans/Helper/ScanObjekt.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Common.Node; 3 | 4 | namespace DolphinTextureExtraction.Scans.Helper 5 | { 6 | public readonly ref struct ScanObjekt 7 | { 8 | public FormatInfo Format { get; } 9 | public int Deep { get; } 10 | public FileNode File { get; } 11 | 12 | public ScanObjekt(FileNode file, int deep) 13 | { 14 | Format = file.Data.Identify(file.Extension); 15 | Deep = deep; 16 | File = file; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/AFSLib/HeaderMagicType.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace AFSLib 3 | { 4 | /// 5 | /// Enumeration containing each type of header magic that can be found in an AFS archive. 6 | /// 7 | public enum HeaderMagicType 8 | { 9 | /// 10 | /// Some AFS files contain a 4-byte header magic with 'AFS' followed by 0x00. 11 | /// 12 | AFS_00, 13 | 14 | /// 15 | /// Some AFS files contain a 4-byte header magic with 'AFS' followed by 0x20. 16 | /// 17 | AFS_20 18 | } 19 | } -------------------------------------------------------------------------------- /lib/AuroraLip/DiscImage/RVZ/PartDataT.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.DiscImage.RVZ 2 | { 3 | public readonly struct PartDataT 4 | { 5 | public readonly uint FirstSector; 6 | public readonly uint Sectors; 7 | public readonly uint GroupIndex; 8 | public readonly uint Groups; 9 | 10 | public PartDataT(uint firstSector, uint sectors, uint groupIndex, uint groups) 11 | { 12 | FirstSector = firstSector; 13 | Sectors = sectors; 14 | GroupIndex = groupIndex; 15 | Groups = groups; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/AuroraLip/Common/Enums/FormatType.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.Common 2 | { 3 | /// 4 | /// The format type specifies the intended use of a file format. 5 | /// 6 | public enum FormatType 7 | { 8 | Unknown = default, 9 | Archive, 10 | Texture, 11 | Audio, 12 | Model, 13 | Collision, 14 | Video, 15 | Text, 16 | Font, 17 | Layout, 18 | Animation, 19 | Skript, 20 | Parameter, 21 | Executable, 22 | Effect, 23 | Shader, 24 | Rom, 25 | Iso, 26 | Else, 27 | Dummy 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/AuroraLip/DiscImage/RVZ/RawDataT.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.DiscImage.RVZ 2 | { 3 | public readonly struct RawDataT 4 | { 5 | public readonly long DataOffset; 6 | public readonly long DataSize; 7 | public readonly uint GroupIndex; 8 | public readonly uint Groups; 9 | 10 | public readonly long DataEndOffset => DataOffset + DataSize; 11 | 12 | public RawDataT(long dataOffset, long dataSize, uint groupIndex, uint groups) 13 | { 14 | DataOffset = dataOffset; 15 | DataSize = dataSize; 16 | GroupIndex = groupIndex; 17 | Groups = groups; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Enums/JUTTransparency.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.Texture 2 | { 3 | public enum JUTTransparency : byte 4 | { 5 | /// 6 | /// No Transperancy 7 | /// 8 | OPAQUE = 0x00, 9 | 10 | /// 11 | /// Only allows fully Transperant pixels to be see through 12 | /// 13 | CUTOUT = 0x01, 14 | 15 | /// 16 | /// Allows Partial Transperancy. Also known as XLUCENT 17 | /// 18 | TRANSLUCENT = 0x02, 19 | 20 | /// 21 | /// Unknown 22 | /// 23 | SPECIAL = 0xCC 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Benchmark/Benchmark.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | True 9 | Benchmark.Benchmarker 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lib/AuroraLip/DiscImage/RVZ/ExceptionT.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Core.Interfaces; 2 | 3 | namespace AuroraLib.DiscImage.RVZ 4 | { 5 | public class ExceptionT : IBinaryObject 6 | { 7 | public ushort Offset; 8 | public readonly byte[] Hash; 9 | 10 | public ExceptionT() 11 | => Hash = new byte[20]; 12 | 13 | public void BinaryDeserialize(Stream source) 14 | { 15 | Offset = source.ReadUInt16(Endian.Big); 16 | source.Read(Hash); 17 | } 18 | 19 | public void BinarySerialize(Stream dest) 20 | { 21 | dest.Write(Offset); 22 | dest.Write(Hash); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/J3D/Enums/TangentMode.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.Texture.J3D 2 | { 3 | public static partial class J3DGraph 4 | { 5 | /// 6 | /// J3D Tangent Modes 7 | /// 8 | public enum TangentMode : short 9 | { 10 | /// 11 | /// One tangent value is stored, used for both the incoming and outgoing tangents 12 | /// 13 | SYNC = 0x00, 14 | 15 | /// 16 | /// Two tangent values are stored, the incoming and outgoing tangents, respectively 17 | /// 18 | DESYNC = 0x01 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/AuroraLip/Common/Node/NodeProcessor.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Compression.Interfaces; 2 | 3 | namespace AuroraLib.Common.Node 4 | { 5 | public static class NodeProcessor 6 | { 7 | public static void Expand(this FileNode file, ICompressionDecoder decoder) 8 | { 9 | MemoryPoolStream dataDecompress = new(); 10 | try 11 | { 12 | decoder.Decompress(file.Data, dataDecompress); 13 | file.Data.Dispose(); 14 | file.Data = dataDecompress; 15 | } 16 | catch (Exception) 17 | { 18 | dataDecompress.Dispose(); 19 | throw; 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/AuroraLip/Common/Interfaces/IDataTime.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.Common 2 | { 3 | /// 4 | /// Provides typical data time properties. 5 | /// 6 | public interface IDataTime 7 | { 8 | /// 9 | /// The creation date and time in UTC format. 10 | /// 11 | DateTime CreationTimeUtc { get; } 12 | 13 | /// 14 | /// The last modification date and time in UTC format. 15 | /// 16 | DateTime LastWriteTimeUtc { get; } 17 | 18 | /// 19 | /// The last accessed date and time in UTC format. 20 | /// 21 | DateTime LastAccessTimeUtc { get; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto eol=lf 3 | 4 | # Source files 5 | *.cs text diff=csharp 6 | *.cshtml text diff=html 7 | *.csx text diff=csharp 8 | 9 | # Project and solution files 10 | *.sln text eol=crlf 11 | *.csproj text eol=crlf 12 | *.vbproj text eol=crlf 13 | *.vcxproj text eol=crlf 14 | *.vcproj text eol=crlf 15 | *.dbproj text eol=crlf 16 | *.fsproj text eol=crlf 17 | *.lsproj text eol=crlf 18 | *.wixproj text eol=crlf 19 | *.modelproj text eol=crlf 20 | *.sqlproj text eol=crlf 21 | *.wwaproj text eol=crlf 22 | *.xproj text eol=crlf 23 | *.props text eol=crlf 24 | *.filters text eol=crlf 25 | *.vcxitems text eol=crlf 26 | -------------------------------------------------------------------------------- /lib/AFSLib/NotificationType.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace AFSLib 3 | { 4 | /// 5 | /// Enumeration containing all types of notifications. 6 | /// 7 | public enum NotificationType 8 | { 9 | /// 10 | /// Notification considered as Information. 11 | /// 12 | Info, 13 | 14 | /// 15 | /// Notification considered as Warning. 16 | /// 17 | Warning, 18 | 19 | /// 20 | /// Notification considered as Error. 21 | /// 22 | Error, 23 | 24 | /// 25 | /// Notification considered as Success. 26 | /// 27 | Success 28 | } 29 | } -------------------------------------------------------------------------------- /lib/AuroraLip/Common/Exceptions.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace AuroraLib.Common 4 | { 5 | public class PaletteException : Exception 6 | { 7 | public string ExpectedIdentifier { get; set; } 8 | 9 | public PaletteException() 10 | { } 11 | 12 | public PaletteException(string message) : base(message) 13 | { 14 | } 15 | 16 | public PaletteException(string message, Exception innerException) : base(message, innerException) 17 | { 18 | } 19 | 20 | [Obsolete(DiagnosticId = "SYSLIB0051")] 21 | protected PaletteException(SerializationInfo info, StreamingContext context) : base(info, context) 22 | { 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Enums/WrapMode.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.Texture 2 | { 3 | /// 4 | /// Defines how textures handle going out of [0..1] range for texcoords. 5 | /// 6 | public enum GXWrapMode : byte 7 | { 8 | /// 9 | /// Clamps the texture to the last pixel at the edge. 10 | /// 11 | CLAMP = 0x00, 12 | 13 | /// 14 | /// Tiles the texture, creating a repeating pattern. 15 | /// 16 | REPEAT = 0x01, 17 | 18 | /// 19 | /// Tiles the texture, creating a repeating pattern by mirroring it at every integer boundary. 20 | /// 21 | MIRRORREAPEAT = 0x02 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/AFSLib/FileEntry.cs: -------------------------------------------------------------------------------- 1 | namespace AFSLib 2 | { 3 | /// 4 | /// Class that represents an entry with data referenced from a file. 5 | /// 6 | public sealed class FileEntry : DataEntry 7 | { 8 | private readonly FileInfo fileInfo; 9 | 10 | internal FileEntry(string fileNamePath, string entryName) 11 | { 12 | fileInfo = new FileInfo(fileNamePath); 13 | 14 | Name = entryName; 15 | Size = (uint)fileInfo.Length; 16 | LastWriteTime = fileInfo.LastWriteTime; 17 | UnknownAttribute = (uint)fileInfo.Length; 18 | } 19 | 20 | internal override Stream GetStream() 21 | { 22 | return fileInfo.OpenRead(); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /lib/AFSLib/AttributesInfoType.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace AFSLib 3 | { 4 | /// 5 | /// Enumeration that contains all possible attribute info locations in an AFS archive. 6 | /// 7 | public enum AttributesInfoType 8 | { 9 | /// 10 | /// The AFS file doesn't contain an attributes block. 11 | /// 12 | NoAttributes, 13 | 14 | /// 15 | /// Info about the attributes block is located at the beginning of the attributes info block. 16 | /// 17 | InfoAtBeginning, 18 | 19 | /// 20 | /// Info about the attributes block is located at the end of the attributes info block. 21 | /// 22 | InfoAtEnd 23 | } 24 | } -------------------------------------------------------------------------------- /lib/LibCPK/LibCPK.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | net8.0 6 | {BA3A00E4-4F51-4AB4-A8FB-F4B64A874449} 7 | enable 8 | disable 9 | False 10 | LibCPK 11 | $(AssemblyTitle) 12 | False 13 | AnyCPU 14 | true 15 | Copyright © 2016-present 16 | LibCPK 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lib/AuroraLip/DiscImage/RVZ/PartT.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Core.Interfaces; 2 | 3 | namespace AuroraLib.DiscImage.RVZ 4 | { 5 | public class PartT : IBinaryObject 6 | { 7 | public readonly byte[] PartKey; 8 | public PartDataT[] PartData; 9 | 10 | public PartT() 11 | { 12 | PartKey = new byte[0x10]; 13 | PartData = new PartDataT[2]; 14 | } 15 | 16 | public void BinaryDeserialize(Stream source) 17 | { 18 | source.Read(PartKey); 19 | source.Read(PartData, Endian.Big); 20 | } 21 | 22 | public void BinarySerialize(Stream dest) 23 | { 24 | dest.Write(PartKey); 25 | dest.Write(PartData, Endian.Big); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /TextureExtraction tool/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Formats/GBIX.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Core.Interfaces; 2 | 3 | namespace AuroraLib.Texture.Formats 4 | { 5 | public class GBIX : GVRT 6 | { 7 | public override bool CanWrite => false; 8 | 9 | public override IIdentifier Identifier => _identifier; 10 | 11 | private static readonly Identifier32 _identifier = new("GBIX"); 12 | 13 | protected override void Read(Stream stream) 14 | { 15 | stream.MatchThrow(_identifier); 16 | uint startOfGVRT = stream.ReadUInt32(); 17 | uint GlobalIndex = stream.ReadUInt32(Endian.Big); 18 | 19 | stream.Seek(startOfGVRT + 8, SeekOrigin.Begin); 20 | base.Read(stream); //GVRT 21 | } 22 | 23 | protected override void Write(Stream stream) 24 | { 25 | throw new NotImplementedException(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Formats/GCIX.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Core.Interfaces; 2 | 3 | namespace AuroraLib.Texture.Formats 4 | { 5 | public class GCIX : GVRT 6 | { 7 | public override bool CanWrite => false; 8 | 9 | public override IIdentifier Identifier => _identifier; 10 | 11 | private static readonly Identifier32 _identifier = new("GCIX"); 12 | 13 | protected override void Read(Stream stream) 14 | { 15 | stream.MatchThrow(_identifier); 16 | uint startOfGVRT = stream.ReadUInt32(); 17 | uint GlobalIndex = stream.ReadUInt32(Endian.Big); 18 | 19 | stream.Seek(startOfGVRT + 8, SeekOrigin.Begin); 20 | base.Read(stream); //GVRT 21 | } 22 | 23 | protected override void Write(Stream stream) 24 | { 25 | throw new NotImplementedException(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Benchmark/Benchmarks/ReadUInt64.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Core; 2 | using BenchmarkDotNet.Attributes; 3 | using System.Buffers.Binary; 4 | using System.Runtime.CompilerServices; 5 | using System.Runtime.InteropServices; 6 | 7 | namespace Benchmark.Benchmarks 8 | { 9 | [MemoryDiagnoser] 10 | public class ReadUInt64 11 | { 12 | private const int n = 10000; 13 | 14 | private const ulong SIZE = 32; 15 | 16 | [Benchmark] 17 | public void BinaryPrimitives_Read() 18 | { 19 | for (var i = 0; i < n; ++i) 20 | { 21 | BinaryPrimitives.ReverseEndianness(SIZE); 22 | } 23 | } 24 | 25 | [Benchmark] 26 | public void MemoryMarshal_Read() 27 | { 28 | for (var i = 0; i < n; ++i) 29 | { 30 | BitConverterX.Swap(SIZE); 31 | } 32 | } 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/AuroraLip/DiscImage/Revolution/SignedBlobHeader.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.DiscImage.Revolution 2 | { 3 | public abstract class SignedBlobHeader 4 | { 5 | public SigTyp SignatureType { get; } //Signature type (always 65537 for RSA-2048) 6 | public readonly byte[] Certificate = new byte[256]; 7 | public readonly byte[] SigPad = new byte[60]; 8 | 9 | public SignedBlobHeader(Stream source) 10 | { 11 | SignatureType = source.Read(Endian.Big); 12 | source.Read(Certificate); 13 | source.Read(SigPad); 14 | } 15 | 16 | public void Write(Stream dest) 17 | { 18 | dest.Write(SignatureType, Endian.Big); 19 | dest.Write(Certificate); 20 | dest.Write(SigPad); 21 | WriteData(dest); 22 | } 23 | 24 | protected abstract void WriteData(Stream dest); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/AFSLib/AFSLib.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | net8.0 6 | {09C23352-8261-447B-9BB5-4C82DAF3CFFD} 7 | enable 8 | disable 9 | False 10 | MIT 11 | AFSLib 12 | $(AssemblyTitle) 13 | False 14 | AFSLib is a library that can extract, create and manipulate AFS files. The AFS format is used in many games from companies like Sega. 15 | AFSLib 16 | Copyright © MaikelChan 2022-present 17 | MaikelChan 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lib/AuroraLip/Compression/Algorithms/CRILAYLA.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Compression.Interfaces; 2 | using AuroraLib.Core.Interfaces; 3 | using System.IO.Compression; 4 | 5 | namespace AuroraLib.Compression.Algorithms 6 | { 7 | public class CRILAYLA : ICompressionAlgorithm, IHasIdentifier 8 | { 9 | public virtual IIdentifier Identifier => _identifier; 10 | 11 | private static readonly Identifier64 _identifier = new("CRILAYLA"); 12 | 13 | public bool IsMatch(Stream stream, ReadOnlySpan extension = default) 14 | => stream.Length > 0x10 && stream.Match(_identifier); 15 | 16 | public void Decompress(Stream source, Stream destination) 17 | => LibCPK.CRILAYLA.Decompress(source, destination); 18 | 19 | public void Compress(ReadOnlySpan source, Stream destination, CompressionLevel level = CompressionLevel.Optimal) 20 | => LibCPK.CRILAYLA.Compress(source, destination); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/AFSLib/StreamEntry.cs: -------------------------------------------------------------------------------- 1 | namespace AFSLib 2 | { 3 | /// 4 | /// Class that represents an entry with data referenced from a stream. 5 | /// 6 | public sealed class StreamEntry : DataEntry 7 | { 8 | private readonly Stream baseStream; 9 | private readonly uint baseStreamDataOffset; 10 | 11 | internal StreamEntry(Stream baseStream, StreamEntryInfo info) 12 | { 13 | this.baseStream = baseStream; 14 | baseStreamDataOffset = info.Offset; 15 | 16 | Name = info.Name; 17 | Size = info.Size; 18 | LastWriteTime = info.LastWriteTime; 19 | UnknownAttribute = info.UnknownAttribute; 20 | } 21 | 22 | internal override Stream GetStream() 23 | { 24 | baseStream.Position = baseStreamDataOffset; 25 | return new SubStream(baseStream, 0, Size, true); 26 | } 27 | 28 | public Stream GetSubStream() => GetStream(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/AuroraLip/Common/Node/Interfaces/IBinaryObjectNode.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Core.Interfaces; 2 | 3 | namespace AuroraLib.Common.Node.Interfaces 4 | { 5 | /// 6 | /// Represents a node object that can be serialized and deserialized. 7 | /// 8 | public interface IBinaryObjectNode : IBinaryObject, IDisposable, IObjectName, IDataTime, IFileAccess 9 | { 10 | /// 11 | /// Deserializes the binary data for this node from the specified . 12 | /// 13 | /// The containing the binary data to deserialize. 14 | void BinaryDeserialize(FileNode source); 15 | 16 | /// 17 | /// Serializes the binary data of this node as a . 18 | /// 19 | /// A containing the serialized binary data. 20 | FileNode BinarySerialize(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /lib/Hack.io/Hack.io.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | net8.0 6 | {CD2CA20A-E6AD-4B1E-9D04-FB98970B61D5} 7 | enable 8 | disable 9 | False 10 | Hack.io 11 | $(AssemblyTitle) 12 | False 13 | Hack.io 14 | Copyright © 2022-present 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Benchmark/Benchmarks/ValuesRevers.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Benchmark.Benchmarks 4 | { 5 | [MemoryDiagnoser] 6 | public class ReverseArray 7 | { 8 | private const int n = 10000; 9 | 10 | private const int SIZE = 32; 11 | private readonly byte[] src = new byte[SIZE]; 12 | 13 | [Benchmark] 14 | public void ArrayReverse() 15 | { 16 | for (var i = 0; i < n; ++i) 17 | { 18 | for (var offset = 0; offset < SIZE - 1; offset += 4) 19 | { 20 | Array.Reverse(src, offset, 4); 21 | } 22 | } 23 | } 24 | 25 | [Benchmark] 26 | public void SpanSliceReverse() 27 | { 28 | for (var i = 0; i < n; ++i) 29 | { 30 | for (var offset = 0; offset < SIZE - 1; offset += 4) 31 | { 32 | src.AsSpan().Slice(offset, 4).Reverse(); 33 | } 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/BUG.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Common.Node; 3 | using AuroraLib.Core.Interfaces; 4 | 5 | namespace AuroraLib.Archives.Formats 6 | { 7 | /// 8 | /// UbiSoft BUG Archive 9 | /// 10 | public class BUG : BIG 11 | { 12 | public override IIdentifier Identifier => _identifier; 13 | 14 | private static readonly Identifier32 _identifier = new((byte)'B', (byte)'U', (byte)'G', 0); 15 | 16 | private static readonly byte[] _key = new[] { (byte)0xB3, (byte)0x98, (byte)0xCC, (byte)0x66 }; 17 | 18 | public BUG() 19 | { 20 | } 21 | 22 | public BUG(string name) : base(name) 23 | { 24 | } 25 | 26 | public BUG(FileNode source) : base(source) 27 | { 28 | } 29 | 30 | protected override void Deserialize(Stream ArchiveFile) 31 | { 32 | ArchiveFile.MatchThrow(Identifier); 33 | XORStream stream = new(ArchiveFile, _key); 34 | ReadData(stream); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/AuroraLip/DiscImage/Revolution/HeaderBin.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.DiscImage.Dolphin; 2 | 3 | namespace AuroraLib.DiscImage.Revolution 4 | { 5 | public class HeaderBin : GameHeader 6 | { 7 | /// 8 | /// 'False' don't work on retail consoles 9 | /// 10 | public bool UseVerification; 11 | 12 | /// 13 | /// 'False' don't work on retail consoles 14 | /// 15 | public bool UseEncryption; 16 | 17 | public HeaderBin(Stream source) : base(source) 18 | { } 19 | 20 | protected override void ReadData(Stream dest) 21 | { 22 | GameName = dest.ReadString(64); 23 | UseVerification = dest.ReadUInt8() == 0; 24 | UseEncryption = dest.ReadUInt8() == 0; 25 | } 26 | 27 | protected override void WriteData(Stream dest) 28 | { 29 | dest.WriteString(GameName, 64, 0); 30 | dest.WriteByte((byte)(UseVerification ? 0 : 1)); 31 | dest.WriteByte((byte)(UseEncryption ? 0 : 1)); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Enums/GXFilterMode.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.Texture 2 | { 3 | /// 4 | /// FilterMode specifies what type of filtering the file should use for min/mag. 5 | /// 6 | public enum GXFilterMode : byte 7 | { 8 | /// 9 | /// Point Sampling, No Mipmap 10 | /// 11 | Nearest = 0x00, 12 | 13 | /// 14 | /// Bilinear Filtering, No Mipmap 15 | /// 16 | Linear = 0x01, 17 | 18 | /// 19 | /// Point Sampling, Discrete Mipmap 20 | /// 21 | NearestMipmapNearest = 0x02, 22 | 23 | /// 24 | /// Bilinear Filtering, Discrete Mipmap 25 | /// 26 | NearestMipmapLinear = 0x03, 27 | 28 | /// 29 | /// Point Sampling, Linear MipMap 30 | /// 31 | LinearMipmapNearest = 0x04, 32 | 33 | /// 34 | /// Trilinear Filtering 35 | /// 36 | LinearMipmapLinear = 0x05 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/BlockFormats/I8Block.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Texture.PixelFormats; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace AuroraLib.Texture.BlockFormats 5 | { 6 | /// 7 | /// Represents a block of I8 (Intensity 8-bit) pixels. Each block has a size of 8x4 pixels. 8 | /// 9 | public readonly struct I8Block : IBlock 10 | { 11 | /// 12 | public int BlockWidth => 8; 13 | 14 | /// 15 | public int BlockHeight => 4; 16 | 17 | /// 18 | public int BitsPerPixel => 8; 19 | 20 | /// 21 | public void DecodeBlock(ReadOnlySpan data, Span pixels) 22 | { 23 | ReadOnlySpan temp = MemoryMarshal.Cast(data); 24 | temp.CopyTo(pixels); 25 | } 26 | 27 | /// 28 | public void EncodeBlock(Span pixels, Span data) 29 | { 30 | ReadOnlySpan temp = MemoryMarshal.Cast(pixels); 31 | temp.CopyTo(data); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Venomalia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/AFSLib/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 MaikelChan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /TextureExtraction tool/LICENSE.TXT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Venomalia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/BlockFormats/IA4Block.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Texture.PixelFormats; 2 | using System.Runtime.InteropServices; 3 | using SixLabors.ImageSharp.PixelFormats; 4 | 5 | namespace AuroraLib.Texture.BlockFormats 6 | { 7 | /// 8 | /// Represents a block of IA4 pixels. Each block has a size of 8x4 pixels. 9 | /// 10 | public readonly struct IA4Block : IBlock 11 | { 12 | /// 13 | public int BlockWidth => 8; 14 | 15 | /// 16 | public int BlockHeight => 4; 17 | 18 | /// 19 | public int BitsPerPixel => 8; 20 | 21 | /// 22 | public void DecodeBlock(ReadOnlySpan data, Span pixels) 23 | { 24 | ReadOnlySpan temp = MemoryMarshal.Cast(data); 25 | temp.CopyTo(pixels); 26 | } 27 | 28 | /// 29 | public void EncodeBlock(Span pixels, Span data) 30 | { 31 | ReadOnlySpan temp = MemoryMarshal.Cast(pixels); 32 | temp.CopyTo(data); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/J3D/Enums/LoopMode.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.Texture.J3D 2 | { 3 | public static partial class J3DGraph 4 | { 5 | /// 6 | /// J3D Looping Modes 7 | /// 8 | public enum LoopMode : byte 9 | { 10 | /// 11 | /// Play Once then Stop. 12 | /// 13 | ONCE = 0x00, 14 | 15 | /// 16 | /// Play Once then Stop and reset to the first frame. 17 | /// 18 | ONCERESET = 0x01, 19 | 20 | /// 21 | /// Constantly play the animation. 22 | /// 23 | REPEAT = 0x02, 24 | 25 | /// 26 | /// Play the animation to the end. then reverse the animation and play to the start, then Stop. 27 | /// 28 | ONCEANDMIRROR = 0x03, 29 | 30 | /// 31 | /// Play the animation to the end. then reverse the animation and play to the start, repeat. 32 | /// 33 | REPEATANDMIRROR = 0x04 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/AFSLib/Utils.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace AFSLib 4 | { 5 | internal static class Utils 6 | { 7 | internal static uint Pad(uint value, uint alignment) 8 | { 9 | uint mod = value % alignment; 10 | if (mod != 0) return value + (alignment - mod); 11 | else return value; 12 | } 13 | 14 | internal static void FillStreamWithZeroes(Stream stream, uint length) 15 | { 16 | byte[] padding = new byte[length]; 17 | stream.Write(padding, 0, (int)length); 18 | } 19 | 20 | internal static void CopySliceTo(this Stream origin, Stream destination, int bytesCount) 21 | { 22 | byte[] buffer = new byte[65536]; 23 | int count; 24 | 25 | while ((count = origin.Read(buffer, 0, Math.Min(buffer.Length, bytesCount))) != 0) 26 | { 27 | destination.Write(buffer, 0, count); 28 | bytesCount -= count; 29 | } 30 | } 31 | 32 | internal static string GetStringFromBytes(byte[] bytes) 33 | { 34 | return Encoding.Default.GetString(bytes).Replace("\0", ""); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Enums/GXPaletteFormat.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.Texture 2 | { 3 | /// 4 | /// PaletteFormat specifies how the data within the palette is stored. 5 | /// Only C4, C8, and C14X2 use palettes. For all other formats the type is zero. 6 | /// 7 | public enum GXPaletteFormat : byte 8 | { 9 | /// 10 | /// The IA8 format is used for storing 8 bit intensity values, along with a separate alpha channel. 11 | /// Greyscale + Alpha - 16 bits/pixel (bpp) | Block Width: 4 | Block height: 4 | Block size: 32 bytes 12 | /// 13 | IA8 = 0x00, 14 | 15 | /// 16 | /// 16 bit color values without alpha. alpha use 0xff. 17 | /// Colour - 16 bits/pixel (bpp) | Block Width: 4 | Block height: 4 | Block size: 32 bytes 18 | /// 19 | RGB565 = 0x01, 20 | 21 | /// 22 | /// It is used for storing either 15 bit color values without alpha, or 12 bit color values with a 3 bit alpha channel. 23 | /// Colour + Alpha - 16 bits/pixel (bpp) | Block Width: 4 | Block height: 4 | Block size: 32 bytes 24 | /// 25 | RGB5A3 = 0x02 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Formats/TPX.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Core.Interfaces; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace AuroraLib.Texture.Formats 10 | { 11 | public class TPX : JUTTexture, IFileAccess, IHasIdentifier 12 | { 13 | public bool CanRead => true; 14 | 15 | public bool CanWrite => false; 16 | 17 | public virtual IIdentifier Identifier => Magic; 18 | 19 | public static readonly Identifier32 Magic = new(302581280); 20 | 21 | public bool IsMatch(Stream stream, ReadOnlySpan extension = default) 22 | => stream.Length > 12 && stream.Match(Magic) && stream.At(0x100, s => s.Match(TPL.Magic)); 23 | 24 | protected override void Read(Stream stream) 25 | { 26 | stream.MatchThrow(Magic); 27 | stream.Position += 252; 28 | long HeaderStart = stream.Position; 29 | stream.MatchThrow(TPL.Magic); 30 | TPL.ProcessStream(stream, HeaderStart, this); 31 | } 32 | 33 | protected override void Write(Stream stream) => throw new NotImplementedException(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/AuroraLip/Common/Events.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.Common 2 | { 3 | /// 4 | /// Events that make it possible to influence the behavior of the library. 5 | /// 6 | public static class Events 7 | { 8 | /// 9 | /// Event that is called when a process wants to report something. 10 | /// 11 | public static NotificationDelegate NotificationEvent = DefaultNotification; 12 | 13 | /// 14 | /// Represents the method that will handle the NotificationEvent. 15 | /// 16 | /// Type of notification. 17 | /// The notification message. 18 | public delegate void NotificationDelegate(NotificationType type, string message); 19 | 20 | /// 21 | /// Represents the method to request a missing file. 22 | /// 23 | /// 24 | /// 25 | public delegate Stream FileRequestDelegate(string Name); 26 | 27 | private static void DefaultNotification(NotificationType type, string message) 28 | => Console.WriteLine($"{type}: {message}"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/AuroraLip/DiscImage/Revolution/WiiKey.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | 3 | namespace AuroraLib.DiscImage.Revolution 4 | { 5 | public static class WiiKey 6 | { 7 | /// 8 | /// Wii Common Key 9 | /// 10 | public static readonly byte[] CKey = new byte[] { 176, 123, 5, 203, 217, 74, 35, 21, 134, 80, 232, 7, 220, 219, 48, 86 }; 11 | 12 | /// 13 | /// Wii Korean Common Key 14 | /// 15 | public static readonly byte[] KKey = new byte[] { 108, 141, 91, 182, 36, 20, 57, 157, 189, 2, 191, 156, 37, 193, 106, 141 }; 16 | 17 | /// 18 | /// vWii Common Key 19 | /// 20 | public static readonly byte[] VKey = new byte[] { 118, 189, 189, 122, 244, 60, 10, 171, 19, 1, 75, 60, 41, 170, 112, 86 }; 21 | 22 | /// 23 | /// Generates the keys 24 | /// 25 | static WiiKey() 26 | { 27 | byte[] gKey = MiscEX.RKey(42, 16); 28 | byte[] gIV = MiscEX.RKey(13, 16); 29 | MiscEX.AESDecrypt(CKey, gKey, gIV); 30 | MiscEX.AESDecrypt(KKey, gKey, gIV); 31 | MiscEX.AESDecrypt(VKey, gKey, gIV); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /TextureExtraction tool/Scans/Results/ScanResults.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace DolphinTextureExtraction.Scans.Results 8 | { 9 | public class ScanResults 10 | { 11 | /// 12 | /// the time the scan process has taken 13 | /// 14 | public TimeSpan TotalTime { get; internal set; } 15 | 16 | /// 17 | /// 18 | /// 19 | public bool IsCompleted { get; internal set; } 20 | 21 | /// 22 | /// Size of all files to be searched in bytes. 23 | /// 24 | public double WorkeLength { get; internal set; } 25 | 26 | /// 27 | /// Size of all already searched files in bytes. 28 | /// 29 | public double ProgressLength { get; internal set; } = 0; 30 | 31 | /// 32 | /// Full path to the log file. 33 | /// 34 | public string LogFullPath { get; internal set; } 35 | 36 | public override string ToString() 37 | { 38 | StringBuilder sb = new(); 39 | sb.AppendLine($"Scan time: {TotalTime.TotalSeconds:.000}s"); 40 | return sb.ToString(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/BlockFormats/IA8Block.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Texture.PixelFormats; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace AuroraLib.Texture.BlockFormats 5 | { 6 | /// 7 | /// Represents a block of IA8 pixels. Each block has a size of 4x4 pixels. 8 | /// 9 | public readonly struct IA8Block : IBlock 10 | { 11 | /// 12 | public int BlockWidth => 4; 13 | 14 | /// 15 | public int BlockHeight => 4; 16 | 17 | /// 18 | public int BitsPerPixel => 16; 19 | 20 | /// 21 | public void DecodeBlock(ReadOnlySpan data, Span pixels) 22 | { 23 | for (int i = 0; i < pixels.Length; i++) 24 | { 25 | pixels[i] = MemoryMarshal.AsRef(data.Slice(i * 2, 2)); 26 | pixels[i].PackedValue = BitConverterX.Swap(pixels[i].PackedValue); 27 | } 28 | } 29 | 30 | /// 31 | public void EncodeBlock(Span pixels, Span data) 32 | { 33 | for (int i = 0; i < pixels.Length; i++) 34 | { 35 | ushort value = BitConverterX.Swap(pixels[i].PackedValue); 36 | MemoryMarshal.Write(data.Slice(i * 2, 2), ref value); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/AuroraLip/DiscImage/Revolution/CMD.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.DiscImage.Revolution 2 | { 3 | public class CMD 4 | { 5 | public uint ContentId; 6 | public ushort Index; 7 | public ContentType Type; 8 | public ulong Size; 9 | public readonly byte[] Hash = new byte[20]; 10 | 11 | public CMD(Stream stream) 12 | { 13 | ContentId = stream.ReadUInt32(Endian.Big); 14 | Index = stream.ReadUInt16(Endian.Big); 15 | Type = stream.Read(Endian.Big); 16 | Size = stream.ReadUInt64(Endian.Big); 17 | stream.Read(Hash); 18 | } 19 | 20 | public void Write(Stream dest) 21 | { 22 | dest.Write(ContentId, Endian.Big); 23 | dest.Write(Index, Endian.Big); 24 | dest.Write(Type, Endian.Big); 25 | dest.Write(Size, Endian.Big); 26 | dest.Write(Hash); 27 | } 28 | 29 | public enum ContentType : ushort 30 | { 31 | Normal = 0x0001, 32 | DLC = 0x4001, 33 | Shared = 0x8001, 34 | } 35 | 36 | public byte[] GetContentIV() 37 | { 38 | byte[] iv_bits = BitConverter.GetBytes(Index); 39 | byte[] iv = new byte[16]; 40 | iv[0] = iv_bits[1]; 41 | iv[1] = iv_bits[0]; 42 | return iv; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/BlockFormats/RGB565Block.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Texture.PixelFormats; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace AuroraLib.Texture.BlockFormats 5 | { 6 | /// 7 | /// Represents a block of RGB565 pixels. Each block has a size of 4x4 pixels. 8 | /// 9 | public readonly struct RGB565Block : IBlock 10 | { 11 | /// 12 | public int BlockWidth => 4; 13 | 14 | /// 15 | public int BlockHeight => 4; 16 | 17 | /// 18 | public int BitsPerPixel => 16; 19 | 20 | /// 21 | public void DecodeBlock(ReadOnlySpan data, Span pixels) 22 | { 23 | for (int i = 0; i < pixels.Length; i++) 24 | { 25 | pixels[i] = MemoryMarshal.AsRef(data.Slice(i * 2, 2)); 26 | pixels[i].PackedValue = BitConverterX.Swap(pixels[i].PackedValue); 27 | } 28 | } 29 | 30 | /// 31 | public void EncodeBlock(Span pixels, Span data) 32 | { 33 | for (int i = 0; i < pixels.Length; i++) 34 | { 35 | ushort value = BitConverterX.Swap(pixels[i].PackedValue); 36 | MemoryMarshal.Write(data.Slice(i * 2, 2), ref value); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/BlockFormats/RGB5A3Block.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Texture.PixelFormats; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace AuroraLib.Texture.BlockFormats 5 | { 6 | /// 7 | /// Represents a block of RGB5A3 pixels. Each block has a size of 4x4 pixels. 8 | /// 9 | public readonly struct RGB5A3Block : IBlock 10 | { 11 | /// 12 | public int BlockWidth => 4; 13 | 14 | /// 15 | public int BlockHeight => 4; 16 | 17 | /// 18 | public int BitsPerPixel => 16; 19 | 20 | /// 21 | public void DecodeBlock(ReadOnlySpan data, Span pixels) 22 | { 23 | for (int i = 0; i < pixels.Length; i++) 24 | { 25 | pixels[i] = MemoryMarshal.AsRef(data.Slice(i * 2, 2)); 26 | pixels[i].PackedValue = BitConverterX.Swap(pixels[i].PackedValue); 27 | } 28 | } 29 | 30 | /// 31 | public void EncodeBlock(Span pixels, Span data) 32 | { 33 | for (int i = 0; i < pixels.Length; i++) 34 | { 35 | ushort value = BitConverterX.Swap(pixels[i].PackedValue); 36 | MemoryMarshal.Write(data.Slice(i * 2, 2), ref value); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/AuroraLip/DiscImage/RVZ/RVZVersion.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.DiscImage.RVZ 2 | { 3 | public readonly struct RvzVersion : IComparable 4 | { 5 | public readonly byte Major; 6 | public readonly byte Minor; 7 | public readonly byte Build; 8 | private readonly byte beta; 9 | 10 | public RvzVersion(byte major, byte minor = 0, byte build = 0, byte beta = 0) 11 | { 12 | Major = major; 13 | Minor = minor; 14 | Build = build; 15 | this.beta = beta; 16 | } 17 | 18 | public readonly bool IsBeta => beta != 0x00 && beta != 0xff; 19 | 20 | public override string ToString() 21 | { 22 | string versionString = Build == 0 ? $"{Major}.{Minor}" : $"{Major}.{Minor}.{Build}"; 23 | if (IsBeta) 24 | { 25 | versionString += $" beta {beta}"; 26 | } 27 | return versionString; 28 | } 29 | 30 | public unsafe int CompareTo(RvzVersion other) 31 | { 32 | if (Major != other.Major) 33 | return Major.CompareTo(other.Major); 34 | if (Minor != other.Minor) 35 | return Minor.CompareTo(other.Minor); 36 | if (Build != other.Build) 37 | return Build.CompareTo(other.Build); 38 | return beta.CompareTo(other.beta); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/AuroraLip/Common/Extensions/MiscEX.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | 3 | namespace AuroraLib.Common 4 | { 5 | public static class MiscEX 6 | { 7 | public static byte[] RKey(int value, int length) 8 | { 9 | byte[] data = new byte[length]; 10 | new Random(value).NextBytes(data); 11 | return data; 12 | } 13 | 14 | public static void AESDecrypt(byte[] cipherData, byte[] key, byte[] IV, CipherMode cipherMode = CipherMode.CBC, PaddingMode paddingMode = PaddingMode.Zeros) 15 | { 16 | Aes aes = Aes.Create(); 17 | aes.KeySize = key.Length * 8; 18 | aes.Mode = cipherMode; 19 | aes.Padding = paddingMode; 20 | aes.Key = key; 21 | aes.IV = IV; 22 | aes.CreateDecryptor().TransformBlock(cipherData, 0, cipherData.Length, cipherData,0); 23 | } 24 | 25 | public static void AESEncrypt(byte[] cipherData, byte[] key, byte[] IV, CipherMode cipherMode = CipherMode.CBC, PaddingMode paddingMode = PaddingMode.Zeros) 26 | { 27 | Aes aes = Aes.Create(); 28 | aes.KeySize = key.Length * 8; 29 | aes.Mode = cipherMode; 30 | aes.Padding = paddingMode; 31 | aes.Key = key; 32 | aes.IV = IV; 33 | aes.CreateEncryptor().TransformBlock(cipherData, 0, cipherData.Length, cipherData, 0); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = space 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.{sln,csproj}] 11 | end_of_line = crlf 12 | charset = utf-8-bom 13 | 14 | [*.sln] 15 | indent_style = tab 16 | 17 | [*.csproj] 18 | indent_style = space 19 | indent_size = 2 20 | 21 | [*.cs] 22 | indent_size = 4 23 | csharp_style_var_elsewhere = false:none 24 | csharp_style_var_for_built_in_types = false:suggestion 25 | csharp_style_var_when_type_is_apparent = false:suggestion 26 | csharp_prefer_braces = when_multiline:suggestion 27 | csharp_style_expression_bodied_methods = when_on_single_line:suggestion 28 | csharp_style_expression_bodied_constructors = when_on_single_line:suggestion 29 | csharp_style_expression_bodied_operators = when_on_single_line:suggestion 30 | csharp_style_expression_bodied_properties = when_on_single_line:suggestion 31 | csharp_style_expression_bodied_indexers = when_on_single_line:suggestion 32 | csharp_style_expression_bodied_accessors = when_on_single_line:suggestion 33 | csharp_style_expression_bodied_lambdas = when_on_single_line:suggestion 34 | csharp_style_expression_bodied_local_functions = when_on_single_line:suggestion 35 | csharp_new_line_before_open_brace = all 36 | csharp_style_unused_value_assignment_preference = unused_local_variable:suggestion 37 | 38 | # IDE0058: Expression value is never used 39 | dotnet_diagnostic.IDE0058.severity = none 40 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/BlockFormats/I4Block.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Texture.PixelFormats; 2 | 3 | namespace AuroraLib.Texture.BlockFormats 4 | { 5 | /// 6 | /// Represents a block of I4 (Intensity 4-bit) pixels. Each block has a size of 8x8 pixels. 7 | /// 8 | public readonly struct I4Block : IBlock 9 | { 10 | /// 11 | public int BlockWidth => 8; 12 | 13 | /// 14 | public int BlockHeight => 8; 15 | 16 | /// 17 | public int BitsPerPixel => 4; 18 | 19 | /// 20 | public void DecodeBlock(ReadOnlySpan data, Span pixels) 21 | { 22 | for (int i = 0; i < data.Length; i++) 23 | { 24 | pixels[i * 2].PackedValue = (byte)((data[i] & 0xF0) | (data[i] >> 4)); 25 | pixels[i * 2 + 1].PackedValue = (byte)((data[i] << 4) | (data[i] & 0x0F)); 26 | } 27 | } 28 | 29 | /// 30 | public void EncodeBlock(Span pixels, Span data) 31 | { 32 | for (int i = 0; i < data.Length; i++) 33 | { 34 | byte highNibble = (byte)(pixels[i * 2].PackedValue & 0x0F); 35 | byte lowNibble = (byte)(pixels[i * 2 + 1].PackedValue & 0x0F); 36 | data[i] = (byte)((highNibble << 4) | lowNibble); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/LibCPK/README.md: -------------------------------------------------------------------------------- 1 | CriPakTools-mod 2 | =========== 3 | forked from uyjulian/CriPakTools 4 | 5 | This tool is based off of code by Falo , Nanashi3 ,esperknight and uyjulian 6 | 7 | I forked and added batch reimport and compress code . 8 | 9 | Thanks for KenTse 's CRILAYLA compression method 10 | 11 | 12 | * Add Batch Mode 13 | * Add compression option 14 | * Fix GTOC & ETOC 15 | * Fix CPK header 16 | 17 | * Still need to do: 18 | * Add GUI 19 | 20 | 21 | 22 | =========== 23 | 24 | Tool to extract/update contents of CRIWARE's CPK archive format. (aka CRI FileMajik) 25 | This is based off of code uploaded by Falo's code released on the Xentax forums (http://forum.xentax.com/viewtopic.php?f=10&t=10646) which was futher modified by Nanashi3 (http://forums.fuwanovel.org/index.php?/topic/1785-request-for-psp-hackers/page-4), which is then further modified by esperknight (https://github.com/esperknight/CriPakTools). 26 | I cleaned up the command line flags and enable to extract 0 byte CRILAYLA compressed files. 27 | If something breaks, open an issue. 28 | To print out options see CriPackTools -h 29 | 30 | Compiling 31 | ========= 32 | Change directory to where the `CriPakTools.sln` file is located, then run `xbuild` if you have Mono. Output file should be in `CriPakTools/bin/CriPackTools.exe`. Otherwise, just open the `CriPakTools.sln` file in Visual Studio 2013 and build. 33 | 34 | TODO: 35 | * Add more error checking 36 | * Clean up code 37 | * Add option to create an archive 38 | -------------------------------------------------------------------------------- /lib/AuroraLip/Palette/Formats/PLT0.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Core.Interfaces; 3 | using AuroraLib.Texture; 4 | 5 | namespace AuroraLib.Palette.Formats 6 | { 7 | public class PLT0 : IHasIdentifier, IJUTPalette 8 | { 9 | public GXPaletteFormat Format { get; set; } 10 | 11 | public byte[] Data { get; set; } 12 | 13 | public virtual IIdentifier Identifier => _identifier; 14 | 15 | private static readonly Identifier32 _identifier = new("PLT0"); 16 | 17 | public PLT0(Stream stream) => Read(stream); 18 | 19 | protected void Read(Stream stream) 20 | { 21 | stream.MatchThrow(_identifier); 22 | uint TotalSize = stream.ReadUInt32(Endian.Big); 23 | uint FormatVersion = stream.ReadUInt32(Endian.Big); 24 | uint Offset = stream.ReadUInt32(Endian.Big); 25 | 26 | uint SectionOffsets = stream.ReadUInt32(Endian.Big); 27 | uint StringOffset = stream.ReadUInt32(Endian.Big); 28 | Format = (GXPaletteFormat)stream.ReadUInt32(Endian.Big); 29 | short colors = stream.ReadInt16(Endian.Big); 30 | ushort pad = stream.ReadUInt16(Endian.Big); 31 | uint PathOffset = stream.ReadUInt32(Endian.Big); 32 | uint DataOffset = stream.ReadUInt32(Endian.Big); 33 | stream.Seek(SectionOffsets, SeekOrigin.Begin); 34 | Data = new byte[colors * 2]; 35 | stream.Read(Data); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/BlockFormats/I14Block.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Texture.PixelFormats; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace AuroraLib.Texture.BlockFormats 5 | { 6 | /// 7 | /// Represents a block of I14 (Intensity 14-bit) pixels. Each block has a size of 8x4 pixels. 8 | /// 9 | public readonly struct I14Block : IBlock 10 | { 11 | /// 12 | public int BlockWidth => 4; 13 | 14 | /// 15 | public int BlockHeight => 4; 16 | 17 | /// 18 | public int BitsPerPixel => 16; 19 | 20 | /// 21 | public void DecodeBlock(ReadOnlySpan data, Span pixels) 22 | { 23 | for (int i = 0; i < pixels.Length; i++) 24 | { 25 | pixels[i] = MemoryMarshal.AsRef(data.Slice(i * 2, 2)); 26 | pixels[i].PackedValue = BitConverterX.Swap(pixels[i].PackedValue); 27 | pixels[i].PackedValue &= 0x3FFF; 28 | } 29 | } 30 | 31 | /// 32 | public void EncodeBlock(Span pixels, Span data) 33 | { 34 | for (int i = 0; i < pixels.Length; i++) 35 | { 36 | pixels[i].PackedValue &= 0x3FFF; 37 | ushort value = BitConverterX.Swap(pixels[i].PackedValue); 38 | MemoryMarshal.Write(data.Slice(i * 2, 2), ref value); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/CMN.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Common.Node; 3 | using System; 4 | 5 | namespace AuroraLib.Archives.Formats 6 | { 7 | /// 8 | /// Treyarch CMN Archive. 9 | /// 10 | public sealed class CMN : ArchiveNode 11 | { 12 | public override bool CanWrite => false; 13 | 14 | public CMN() 15 | { 16 | } 17 | 18 | public CMN(string name) : base(name) 19 | { 20 | } 21 | 22 | public CMN(FileNode source) : base(source) 23 | { 24 | } 25 | 26 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 27 | => extension.Contains(".cmn", StringComparison.InvariantCultureIgnoreCase) && stream.ReadUInt32() < 2048 && stream.ReadUInt32() != 0; 28 | 29 | protected override void Deserialize(Stream source) 30 | { 31 | uint files = source.ReadUInt32(); 32 | for (int i = 0; i < files; i++) 33 | { 34 | FileEntrie entrie = source.Read(); 35 | Add(new FileNode($"File_{i}_{entrie.Type}_{entrie.Hash}", new SubStream(source, entrie.Size, entrie.Offset))); 36 | } 37 | } 38 | 39 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 40 | 41 | private struct FileEntrie 42 | { 43 | public uint Hash; // ? 44 | public uint Type; //0x0 - 0x1 45 | public uint Offset; 46 | public uint Size; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Benchmark/Benchmarks/CopyByte.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Benchmark.Benchmarks 5 | { 6 | [MemoryDiagnoser] 7 | public class CopyByte 8 | { 9 | private const int n = 10000; 10 | 11 | private const int SIZE = 32; 12 | private readonly byte[] src = new byte[SIZE]; 13 | private readonly byte[] dst = new byte[SIZE]; 14 | 15 | [Benchmark] 16 | public void ArrayCopy() 17 | { 18 | for (var i = 0; i < n; ++i) 19 | { 20 | Array.Copy(src, dst, SIZE); 21 | } 22 | } 23 | 24 | [Benchmark] 25 | public void SpanCopyTo() 26 | { 27 | for (var i = 0; i < n; ++i) 28 | { 29 | src.AsSpan().CopyTo(dst.AsSpan()); 30 | } 31 | } 32 | 33 | [Benchmark] 34 | public void BufferBlockCopy() 35 | { 36 | for (var i = 0; i < n; ++i) 37 | { 38 | Buffer.BlockCopy(src, 0, dst, 0, SIZE); 39 | } 40 | } 41 | 42 | [Benchmark] 43 | public void MarshalCopy() 44 | { 45 | for (var i = 0; i < n; ++i) 46 | { 47 | GCHandle handle = GCHandle.Alloc(dst, GCHandleType.Pinned); 48 | try 49 | { 50 | IntPtr rawDataPtr = handle.AddrOfPinnedObject(); 51 | Marshal.Copy(src, 0, rawDataPtr, SIZE); 52 | } 53 | finally 54 | { 55 | handle.Free(); 56 | } 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/pBin.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common.Node; 2 | using AuroraLib.Core.Interfaces; 3 | 4 | namespace AuroraLib.Archives.Formats 5 | { 6 | /// 7 | /// Natsume Harvest Moon: Animal Parade Archive 8 | /// 9 | public sealed class PBin : ArchiveNode, IHasIdentifier 10 | { 11 | public override bool CanWrite => false; 12 | 13 | public IIdentifier Identifier => _identifier; 14 | 15 | private static readonly Identifier32 _identifier = new("pBin"); 16 | 17 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 18 | => stream.Match(_identifier); 19 | 20 | protected override void Deserialize(Stream source) 21 | { 22 | source.MatchThrow(_identifier); 23 | 24 | uint unknown1 = source.ReadUInt32(Endian.Big); 25 | uint unknown2 = source.ReadUInt32(Endian.Big); 26 | uint unknown3 = source.ReadUInt32(Endian.Big); 27 | uint unknown4 = source.ReadUInt32(Endian.Big); 28 | uint count = source.ReadUInt32(Endian.Big); 29 | 30 | for (int i = 0; i < count; i++) 31 | { 32 | uint size = source.ReadUInt32(Endian.Big); 33 | uint offset = source.ReadUInt32(Endian.Big); 34 | string type = source.ReadString(4); 35 | uint unknown = source.ReadUInt32(Endian.Big); 36 | FileNode file = new($"Entry{i}_{type}", new SubStream(source, size, offset)); 37 | Add(file); 38 | } 39 | } 40 | 41 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/ONE_UN.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Common.Node; 3 | using AuroraLib.Core.Interfaces; 4 | 5 | namespace AuroraLib.Archives.Formats 6 | { 7 | /// 8 | /// Archive use in Sonic Unleashed 9 | /// 10 | public sealed class ONE_UN : ArchiveNode, IHasIdentifier 11 | { 12 | public override bool CanWrite => false; 13 | 14 | public IIdentifier Identifier => _identifier; 15 | 16 | private static readonly Identifier32 _identifier = new("one."); 17 | 18 | public ONE_UN() 19 | { 20 | } 21 | 22 | public ONE_UN(string name) : base(name) 23 | { 24 | } 25 | 26 | public ONE_UN(FileNode source) : base(source) 27 | { 28 | } 29 | 30 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 31 | => stream.Match(_identifier) && stream.At(4, S => stream.ReadUInt32()) <= 1024 * 4; 32 | 33 | protected override void Deserialize(Stream source) 34 | { 35 | source.MatchThrow(_identifier); 36 | 37 | uint numEntries = source.ReadUInt32(); 38 | for (int i = 0; i < numEntries; i++) 39 | { 40 | string entryFilename = source.ReadString(56); 41 | uint entryOffset = source.ReadUInt32(); 42 | uint entryLength = source.ReadUInt32(); 43 | 44 | FileNode Sub = new(entryFilename, new SubStream(source, entryLength, entryOffset)); 45 | Add(Sub); 46 | } 47 | } 48 | 49 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/AuroraLip/DiscImage/RVZ/Header.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Core.Interfaces; 2 | using System.Buffers; 3 | 4 | namespace AuroraLib.DiscImage.RVZ 5 | { 6 | public class Header : IBinaryObject 7 | { 8 | public ImageType Magic; 9 | public RvzVersion Version; 10 | public RvzVersion VersionCompatible; 11 | public uint DiscTSize; 12 | public readonly byte[] DiscHash; 13 | public long IsoFileSize; 14 | public long ThisFileSize; 15 | public readonly byte[] HeaderHash; 16 | 17 | public Header() 18 | { 19 | DiscHash = new byte[0x14]; 20 | HeaderHash = new byte[0x14]; 21 | } 22 | 23 | public Header(Stream source) : this() => BinaryDeserialize(source); 24 | 25 | public void BinaryDeserialize(Stream source) 26 | { 27 | Magic = source.Read(Endian.Big); 28 | Version = source.Read(Endian.Big); 29 | VersionCompatible = source.Read(Endian.Big); 30 | DiscTSize = source.Read(Endian.Big); 31 | source.Read(DiscHash); 32 | IsoFileSize = source.Read(Endian.Big); 33 | ThisFileSize = source.Read(Endian.Big); 34 | source.Read(HeaderHash); 35 | } 36 | 37 | public void BinarySerialize(Stream dest) 38 | { 39 | dest.Write(Magic); 40 | dest.Write(Version); 41 | dest.Write(VersionCompatible); 42 | dest.Write(DiscTSize); 43 | dest.Write(DiscHash); 44 | dest.Write(IsoFileSize); 45 | dest.Write(ThisFileSize); 46 | dest.Write(HeaderHash); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /TextureExtraction tool/Scans/Options/TextureExtractorOptions.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Texture; 2 | using DolphinTextureExtraction.Scans.Results; 3 | using System.Text; 4 | 5 | namespace DolphinTextureExtraction.Scans.Options 6 | { 7 | public class TextureExtractorOptions : ScanOptions 8 | { 9 | /// 10 | /// Should Mipmaps files be extracted? 11 | /// 12 | public bool Mips = AppSettings.Mips; 13 | 14 | /// 15 | /// use Arbitrary Mipmap Detection. 16 | /// 17 | public bool ArbitraryMipmapDetection = AppSettings.ArbitraryMipmapDetection; 18 | 19 | /// 20 | /// Extracts all raw images that are found 21 | /// 22 | public bool Raw = AppSettings.Raw; 23 | 24 | /// 25 | /// Tries to Imitate dolphin mipmap detection. 26 | /// 27 | public bool DolphinMipDetection = AppSettings.DolphinMipDetection; 28 | 29 | /// 30 | /// Combine texture pairs to get an RGBA texture. 31 | /// 32 | public bool CombinedRGBA = AppSettings.CombinedRGBA; 33 | 34 | public override string ToString() 35 | { 36 | StringBuilder sb = new(); 37 | ToString(sb); 38 | sb.Append(", Enable Mips:"); 39 | sb.Append(Mips); 40 | sb.Append(", Raw:"); 41 | sb.Append(Raw); 42 | sb.Append(", DolphinMipDetection:"); 43 | sb.Append(DolphinMipDetection); 44 | sb.Append(", ArbitraryMipmapDetection:"); 45 | sb.Append(ArbitraryMipmapDetection); 46 | return sb.ToString(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/TXAG.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Common.Node; 3 | using AuroraLib.Core.Interfaces; 4 | 5 | namespace AuroraLib.Archives.Formats 6 | { 7 | public sealed class TXAG : ArchiveNode, IHasIdentifier 8 | { 9 | public override bool CanWrite => false; 10 | 11 | public IIdentifier Identifier => _identifier; 12 | 13 | private static readonly Identifier32 _identifier = new("TXAG"); 14 | 15 | public TXAG() 16 | { 17 | } 18 | 19 | public TXAG(string name) : base(name) 20 | { 21 | } 22 | 23 | public TXAG(FileNode source) : base(source) 24 | { 25 | } 26 | 27 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 28 | => stream.Match(_identifier); 29 | 30 | protected override void Deserialize(Stream source) 31 | { 32 | source.MatchThrow(_identifier); 33 | 34 | ushort unk = source.ReadUInt16(Endian.Big); 35 | ushort fileCount = source.ReadUInt16(Endian.Big); 36 | 37 | for (int i = 0; i < fileCount; i++) 38 | { 39 | uint offset = source.ReadUInt32(Endian.Big); 40 | uint length = source.ReadUInt32(Endian.Big); 41 | string fileName = source.ReadString(32); 42 | 43 | if (string.IsNullOrWhiteSpace(fileName)) 44 | fileName = $"entry{i}.GVR"; 45 | 46 | FileNode file = new(fileName, new SubStream(source, length, offset)); 47 | Add(file); 48 | } 49 | } 50 | 51 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/BlockFormats/RGBA32Block.cs: -------------------------------------------------------------------------------- 1 | using SixLabors.ImageSharp.PixelFormats; 2 | 3 | namespace AuroraLib.Texture.BlockFormats 4 | { 5 | /// 6 | /// Represents a block of RGBA32 pixels. Each block has a size of 4x4 pixels. 7 | /// The 64-byte block is organized in a zigzag pattern. 8 | /// 9 | public readonly struct RGBA32Block : IBlock 10 | { 11 | /// 12 | public int BlockWidth => 4; 13 | 14 | /// 15 | public int BlockHeight => 4; 16 | 17 | /// 18 | public int BitsPerPixel => 32; 19 | 20 | /* 21 | * The pixel data is separated into two groups: 22 | * A and R are encoded in the first group, and G and B are encoded in the second group. 23 | * The data is organized as follows: 24 | * ARARARARARARARAR 25 | * ARARARARARARARAR 26 | * GBGBGBGBGBGBGBGB 27 | * GBGBGBGBGBGBGBGB 28 | */ 29 | 30 | /// 31 | public void DecodeBlock(ReadOnlySpan data, Span pixels) 32 | { 33 | for (int i = 0; i < pixels.Length; i++) 34 | pixels[i] = new Rgba32(data[(i * 2) + 1], data[(i * 2) + 32], data[(i * 2) + 33], data[(i * 2)]); 35 | } 36 | 37 | /// 38 | public void EncodeBlock(Span pixels, Span data) 39 | { 40 | for (int i = 0; i < pixels.Length; i++) 41 | { 42 | data[i * 2] = pixels[i].A; 43 | data[(i * 2) + 01] = pixels[i].R; 44 | data[(i * 2) + 32] = pixels[i].G; 45 | data[(i * 2) + 33] = pixels[i].B; 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Formats/FIPAFTEX.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Core.Exceptions; 3 | using AuroraLib.Core.Interfaces; 4 | 5 | namespace AuroraLib.Texture.Formats 6 | { 7 | // The actual file type is likely 'FIPA' 8 | // with a switch on subtypes (ex: 'FTEX') 9 | // but that would be more complicated 10 | // to deal with! 11 | public class FIPAFTEX : JUTTexture, IFileAccess, IHasIdentifier 12 | { 13 | public bool CanRead => true; 14 | 15 | public bool CanWrite => false; 16 | 17 | public IIdentifier Identifier => magic; 18 | 19 | private static readonly Identifier64 magic = new("FIPAFTEX"); 20 | 21 | public static bool Matcher(Stream stream, ReadOnlySpan extension = default) 22 | { 23 | if (!stream.Match(magic)) 24 | return false; 25 | 26 | return true; 27 | } 28 | 29 | public bool IsMatch(Stream stream, ReadOnlySpan extension = default) 30 | => Matcher(stream, extension); 31 | 32 | protected override void Read(Stream stream) 33 | { 34 | stream.MatchThrow(magic); 35 | 36 | // These files contain one or more TPLs 37 | // so we use a helper function to parse the TPL 38 | // after finding the start of the stream 39 | while (stream.Search(TPL.Magic.AsSpan().ToArray())) 40 | { 41 | long HeaderStart = stream.Position; 42 | stream.Skip(4); // skip TPL magic 43 | TPL.ProcessStream(stream, HeaderStart, this); 44 | } 45 | } 46 | 47 | protected override void Write(Stream ArchiveFile) 48 | { 49 | throw new NotImplementedException(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/RVZ.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Archives.Formats.Nintendo; 2 | using AuroraLib.Core.Interfaces; 3 | using AuroraLib.DiscImage.Dolphin; 4 | using AuroraLib.DiscImage.Revolution; 5 | using AuroraLib.DiscImage.RVZ; 6 | 7 | namespace AuroraLib.Archives.Formats 8 | { 9 | public sealed partial class RVZ : WiiDisk, IHasIdentifier 10 | { 11 | public override bool CanWrite => false; 12 | 13 | public IIdentifier Identifier => _identifier; 14 | 15 | private static readonly Identifier32 _identifier = new(82, 86, 90, 1); 16 | 17 | private RvzStream rvzStream; 18 | 19 | public new GameHeader Header { get => header; set => header = value; } 20 | 21 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 22 | => stream.Match(_identifier); 23 | 24 | protected override void Deserialize(Stream source) 25 | { 26 | rvzStream = new(source); 27 | if (rvzStream.RVZDiscT.DiscType == DiscTypes.GameCube) 28 | { 29 | ProcessData(rvzStream, this); 30 | } 31 | else 32 | { 33 | Header = new HeaderBin(rvzStream) 34 | { 35 | UseVerification = false, 36 | UseEncryption = false 37 | }; 38 | ProcessPartitionData(rvzStream); 39 | } 40 | } 41 | 42 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 43 | 44 | protected override void Dispose(bool disposing) 45 | { 46 | base.Dispose(disposing); 47 | if (disposing) 48 | { 49 | rvzStream?.Dispose(); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/MEDB.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Common.Node; 3 | using AuroraLib.Core.Interfaces; 4 | 5 | namespace AuroraLib.Archives.Formats 6 | { 7 | /// 8 | /// Rune Factory (Tides) archive format 9 | /// 10 | public sealed class MEDB : ArchiveNode, IHasIdentifier 11 | { 12 | public override bool CanWrite => false; 13 | 14 | public IIdentifier Identifier => _identifier; 15 | 16 | private static readonly Identifier32 _identifier = new("MEDB"); 17 | 18 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 19 | => stream.Match(_identifier); 20 | 21 | protected override void Deserialize(Stream source) 22 | { 23 | source.MatchThrow(_identifier); 24 | 25 | // We know there are textures here, just search for them 26 | while (source.Search("HXTB")) 27 | { 28 | long entrystart = source.Position; 29 | if (!source.Match("HXTB")) 30 | continue; 31 | source.Seek(0x14, SeekOrigin.Current); 32 | uint total_size = source.ReadUInt32(Endian.Big); 33 | 34 | if (total_size > source.Length - entrystart) 35 | { 36 | source.Search("HXTB"); 37 | total_size = (uint)(source.Position - entrystart); 38 | } 39 | 40 | FileNode Sub = new($"entry_{Count + 1}.hxtb", new SubStream(source, total_size, entrystart)); 41 | Add(Sub); 42 | 43 | source.Position = entrystart + total_size; 44 | } 45 | } 46 | 47 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Formats/S3G.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | 3 | namespace AuroraLib.Texture.Formats 4 | { 5 | public class S3G : JUTTexture, IFileAccess 6 | { 7 | public bool CanRead => true; 8 | 9 | public bool CanWrite => false; 10 | 11 | public static string Extension => ".s3g"; 12 | 13 | public bool IsMatch(Stream stream, ReadOnlySpan extension = default) 14 | { 15 | if (stream.Length <= 0x20 || !extension.Contains(Extension, StringComparison.InvariantCultureIgnoreCase)) 16 | { 17 | return false; 18 | } 19 | 20 | Header header = stream.Read
(); 21 | int size = GXImageFormat.CMPR.CalculatedDataSize((int)header.Width, (int)header.Height); 22 | return size + stream.Position == stream.Length; 23 | } 24 | 25 | protected override void Read(Stream stream) 26 | { 27 | Header header = stream.Read
(); 28 | Add(new(stream, Span.Empty, GXImageFormat.CMPR, GXPaletteFormat.RGB5A3, 0, (int)header.Width, (int)header.Height, 0) 29 | { 30 | LODBias = 0, 31 | MagnificationFilter = GXFilterMode.Nearest, 32 | MinificationFilter = GXFilterMode.Nearest, 33 | WrapS = GXWrapMode.CLAMP, 34 | WrapT = GXWrapMode.CLAMP, 35 | EnableEdgeLOD = false, 36 | MinLOD = 0, 37 | MaxLOD = 0 38 | }); 39 | } 40 | 41 | protected override void Write(Stream stream) => throw new NotImplementedException(); 42 | 43 | public struct Header 44 | { 45 | public uint unk; 46 | public uint unk2; //Images 47 | public uint Width; 48 | public uint Height; 49 | public uint unk3; // mips? 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /TextureExtraction tool/Data/ConsoleBar.cs: -------------------------------------------------------------------------------- 1 | namespace DolphinTextureExtraction 2 | { 3 | public class ConsoleBar 4 | { 5 | 6 | public ConsoleColor Color { get; private set; } 7 | public double Max { get; private set; } 8 | public int Length { get; private set; } 9 | public double Value { get; set; } = 0; 10 | 11 | public int CursorTop { get; set; } 12 | public int CursorLeft { get; set; } 13 | 14 | public ConsoleBar(double max, int length = 30, ConsoleColor color = ConsoleColor.Green) 15 | { 16 | CursorTop = Console.CursorTop; 17 | CursorLeft = Console.CursorLeft; 18 | Max = max; 19 | Length = length; 20 | Color = color; 21 | } 22 | 23 | public void Print() 24 | { 25 | lock (Console.Out) 26 | lock (Console.Error) 27 | { 28 | Console.SetCursorPosition(CursorLeft, CursorTop); 29 | ConsoleColor color = Console.ForegroundColor; 30 | Console.ForegroundColor = Color; 31 | 32 | Console.Write("│"); 33 | float PL = (float)(Value * Length / Max); 34 | if (PL >= 1) Console.Write("".PadLeft((int)PL, '█')); 35 | if (PL < Length) 36 | { 37 | if ((PL - (int)PL) >= 0.75) Console.Write('▓'); 38 | else if ((PL - (int)PL) >= 0.5) Console.Write('▒'); 39 | else if ((PL - (int)PL) >= 0.25) Console.Write('░'); 40 | else Console.Write(' '); 41 | Console.Write("".PadLeft(Length - (int)PL - 1, ' ')); 42 | } 43 | Console.Write("│"); 44 | Console.ForegroundColor = color; 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/NEP.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Common.Node; 3 | 4 | namespace AuroraLib.Archives.Formats 5 | { 6 | /// 7 | /// SEGA NEP Archive 8 | /// 9 | public sealed class NEP : ArchiveNode 10 | { 11 | public override bool CanWrite => false; 12 | 13 | public NEP() 14 | { 15 | } 16 | 17 | public NEP(string name) : base(name) 18 | { 19 | } 20 | 21 | public NEP(FileNode source) : base(source) 22 | { 23 | } 24 | 25 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) => extension == ".NEP"; 26 | 27 | protected override void Deserialize(Stream source) 28 | { 29 | while (source.Position + 0x20 < source.Length) 30 | { 31 | long pos = source.Position; 32 | Entry entry = source.Read(Endian.Big); 33 | string name = source.ReadCString(); 34 | name = name.Replace("..\\", string.Empty); 35 | if (!Contains(name)) 36 | { 37 | FileNode Sub = new(name, new SubStream(source, entry.Size, pos + entry.Offset)); 38 | Add(Sub); 39 | } 40 | source.Seek(pos + entry.TotalSize, SeekOrigin.Begin); 41 | } 42 | } 43 | 44 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 45 | 46 | private struct Entry 47 | { 48 | public Types Type; 49 | public uint Offset; 50 | public uint Size; 51 | public uint TotalSize; 52 | 53 | public enum Types : uint 54 | { 55 | GVR = 0, //texture 56 | GNM = 1, //model 57 | PEF = 2, 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/FBC.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common.Node; 2 | 3 | namespace AuroraLib.Archives.Formats 4 | { 5 | /// 6 | /// H.a.n.d. Fables Chocobo archive. 7 | /// 8 | public sealed class FBC : ArchiveNode 9 | { 10 | public override bool CanWrite => false; 11 | 12 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 13 | => extension.SequenceEqual("FBC"); 14 | 15 | private const string Bres = "bresþÿ"; 16 | 17 | public FBC() 18 | { 19 | } 20 | 21 | public FBC(string name) : base(name) 22 | { 23 | } 24 | 25 | public FBC(FileNode source) : base(source) 26 | { 27 | } 28 | 29 | protected override void Deserialize(Stream source) 30 | { 31 | //we do not know the header, so we skip it 32 | source.Seek(150, SeekOrigin.Begin); 33 | 34 | //FBC seem to contain only bres files 35 | while (source.Search(Bres)) 36 | { 37 | long entrystart = source.Position; 38 | if (!source.Match(Bres)) 39 | continue; 40 | ushort Version = source.ReadUInt16(Endian.Big); 41 | uint TotalSize = source.ReadUInt32(Endian.Big); 42 | 43 | if (TotalSize > source.Length - entrystart) 44 | { 45 | source.Search(Bres); 46 | TotalSize = (uint)(source.Position - entrystart); 47 | } 48 | 49 | FileNode Sub = new($"entry_{Count + 1}.bres", new SubStream(source, TotalSize, entrystart)); 50 | Add(Sub); 51 | 52 | source.Position = entrystart + TotalSize; 53 | } 54 | } 55 | 56 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/AuroraLip/AuroraLib.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | net8.0 6 | {CE629181-0090-46D0-81BC-16682E497573} 7 | enable 8 | disable 9 | False 10 | 1.0.$([System.DateTime]::Now.ToString(`MMdd.HHmm`)) 11 | $(Version) 12 | $(Version) 13 | MIT 14 | AuroraLib 15 | $(AssemblyTitle) 16 | False 17 | true 18 | AuroraLip 19 | Copyright © 2022-present 20 | 21 | 22 | 23 | False 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/PCKG.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Common.Node; 3 | using AuroraLib.Core.Interfaces; 4 | 5 | namespace AuroraLib.Archives.Formats 6 | { 7 | /// 8 | /// Cing Little King's Story Archive 9 | /// 10 | public sealed class PCKG : ArchiveNode, IHasIdentifier 11 | { 12 | public override bool CanWrite => false; 13 | 14 | public IIdentifier Identifier => _identifier; 15 | 16 | private static readonly Identifier32 _identifier = new("PCKG"); 17 | 18 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 19 | => stream.Match(_identifier); 20 | 21 | private const string Bres = "bresþÿ"; 22 | 23 | public PCKG() 24 | { 25 | } 26 | 27 | public PCKG(string name) : base(name) 28 | { 29 | } 30 | 31 | public PCKG(FileNode source) : base(source) 32 | { 33 | } 34 | 35 | protected override void Deserialize(Stream source) 36 | { 37 | //PCKG_CING seem to contain only bres files 38 | while (source.Search(Bres)) 39 | { 40 | long entrystart = source.Position; 41 | if (!source.Match(Bres)) 42 | continue; 43 | ushort Version = source.ReadUInt16(Endian.Big); 44 | uint TotalSize = source.ReadUInt32(Endian.Big); 45 | 46 | if (TotalSize > source.Length - entrystart) 47 | { 48 | source.Search(Bres); 49 | TotalSize = (uint)(source.Position - entrystart); 50 | } 51 | 52 | FileNode file = new($"entry_{Count + 1}.bres", new SubStream(source, TotalSize, entrystart)); 53 | Add(file); 54 | 55 | source.Position = entrystart + TotalSize; 56 | } 57 | } 58 | 59 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/RSC.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Common.Node; 3 | using System.Xml.Linq; 4 | 5 | namespace AuroraLib.Archives.Formats 6 | { 7 | /// 8 | /// Treasure Wario World archive 9 | /// 10 | public sealed class RSC : ArchiveNode 11 | { 12 | public override bool CanWrite => false; 13 | 14 | public RSC() 15 | { 16 | } 17 | 18 | public RSC(string name) : base(name) 19 | { 20 | } 21 | 22 | public RSC(FileNode source) : base(source) 23 | { 24 | } 25 | 26 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 27 | => Matcher(stream, extension); 28 | 29 | public static bool Matcher(Stream stream, ReadOnlySpan extension = default) 30 | => extension.SequenceEqual(".RSC"); 31 | 32 | protected override void Deserialize(Stream source) 33 | { 34 | Span unk = stackalloc byte[32]; 35 | source.Read(unk); 36 | 37 | do 38 | { 39 | Entry entry = source.Read(Endian.Big); 40 | FileNode file = new($"{entry.Flag}_entry{Count}", new SubStream(source, (int)entry.Size)); 41 | Add(file); 42 | if (entry.NextOffset == 0) 43 | { 44 | break; 45 | } 46 | source.Seek(entry.NextOffset, SeekOrigin.Begin); 47 | } 48 | while (true); 49 | } 50 | 51 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 52 | 53 | private struct Entry 54 | { 55 | public uint Flag; //0-14, 1 = TPL 56 | public uint Size; 57 | public uint NextOffset; 58 | public uint Pad12; 59 | 60 | public uint Pad16; 61 | public uint Pad20; 62 | public uint Pad24; 63 | public uint Pad26; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Benchmark/Benchmarks/StringBuilder.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Core.Text; 3 | using BenchmarkDotNet.Attributes; 4 | using System.Text; 5 | 6 | namespace Benchmark.Benchmarks 7 | { 8 | [MemoryDiagnoser] 9 | public class StringBuild 10 | { 11 | private const int n = 2500; 12 | private const int n2 = 5; 13 | 14 | private const string text = "ABC0XXX.exe"; 15 | 16 | [Benchmark] 17 | public void Add() 18 | { 19 | for (var i = 0; i < n; ++i) 20 | { 21 | string s = text; 22 | for (var i2 = 0; i2 < n2; ++i2) 23 | { 24 | s += text[..7]; 25 | } 26 | } 27 | } 28 | 29 | 30 | [Benchmark] 31 | public void Concat() 32 | { 33 | for (var i = 0; i < n; ++i) 34 | { 35 | string s = text; 36 | for (var i2 = 0; i2 < n2; ++i2) 37 | { 38 | s = string.Concat(s, text.AsSpan()[..7]); 39 | } 40 | } 41 | } 42 | 43 | [Benchmark] 44 | public void StringBuilder() 45 | { 46 | for (var i = 0; i < n; ++i) 47 | { 48 | StringBuilder sb = new(text); 49 | for (var i2 = 0; i2 < n2; ++i2) 50 | { 51 | sb.Append(text.AsSpan()[..7]); 52 | } 53 | sb.ToString(); 54 | } 55 | } 56 | 57 | [Benchmark] 58 | public void ValueStringBuilder() 59 | { 60 | for (var i = 0; i < n; ++i) 61 | { 62 | ValueStringBuilder sb = new(); 63 | sb.Append(text.AsSpan()); 64 | sb.Dispose(); 65 | for (var i2 = 0; i2 < n2; ++i2) 66 | { 67 | sb.Append(text.AsSpan()[..7]); 68 | } 69 | sb.AsSpan(); 70 | } 71 | } 72 | 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/GSAGTX.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Common.Node; 3 | using System.Xml.Linq; 4 | 5 | namespace AuroraLib.Archives.Formats 6 | { 7 | /// 8 | /// Genius Sonority "GSAGTX" file format, although the real file extension is unknown. 9 | /// It basically contains an GTX texture with frames/"windows" into the texture. 10 | /// Based on Pokémon XD (GXXP01). 11 | /// 12 | public sealed class GSAGTX : ArchiveNode, IFileAccess 13 | { 14 | public override bool CanWrite => false; 15 | 16 | public GSAGTX() 17 | { 18 | } 19 | 20 | public GSAGTX(string name) : base(name) 21 | { 22 | } 23 | 24 | public GSAGTX(FileNode source) : base(source) 25 | { 26 | } 27 | 28 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 29 | => Matcher(stream, extension); 30 | 31 | public static bool Matcher(Stream stream, ReadOnlySpan extension = default) 32 | => extension.SequenceEqual(".GSAGTX") && stream.Length > 128; 33 | 34 | protected override void Deserialize(Stream stream) 35 | { 36 | Header header = stream.Read
(Endian.Big); 37 | FileNode file = new("BaseTexture.GTX", new SubStream(stream, stream.Length - header.TextureOffset, header.TextureOffset)); 38 | Add(file); 39 | } 40 | 41 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 42 | 43 | public struct Header 44 | { 45 | public uint Header00; 46 | 47 | /// 48 | /// Contrains entries to "windows" into the texture to display 49 | /// 50 | public uint FramesOffset; 51 | 52 | public uint Header08; 53 | 54 | /// 55 | /// Offset from the file to an GTX texture used as the base. 56 | /// 57 | public uint TextureOffset; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/PAK_FE.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Common.Node; 3 | using AuroraLib.Core.Buffers; 4 | using AuroraLib.Core.Interfaces; 5 | 6 | namespace AuroraLib.Archives.Formats 7 | { 8 | /// 9 | /// Intelligent Systems Fire Emblem Archive 10 | /// 11 | public sealed class PAK_FE : ArchiveNode, IHasIdentifier 12 | { 13 | public override bool CanWrite => false; 14 | 15 | public IIdentifier Identifier => _identifier; 16 | 17 | private static readonly Identifier32 _identifier = new("pack"); 18 | 19 | public PAK_FE() 20 | { 21 | } 22 | 23 | public PAK_FE(string name) : base(name) 24 | { 25 | } 26 | 27 | public PAK_FE(FileNode source) : base(source) 28 | { 29 | } 30 | 31 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 32 | => stream.Match(_identifier); 33 | 34 | protected override void Deserialize(Stream source) 35 | { 36 | source.MatchThrow(_identifier); 37 | 38 | ushort NrEntries = source.ReadUInt16(Endian.Big); 39 | ushort Unk = source.ReadUInt16(Endian.Big); 40 | 41 | using SpanBuffer entries = new (NrEntries); 42 | source.Read(entries, Endian.Big); 43 | 44 | for (int i = 0; i < NrEntries; i++) 45 | { 46 | source.Seek(entries[i].name, SeekOrigin.Begin); 47 | string name = source.ReadCString(); 48 | 49 | FileNode file = new(name, new SubStream(source, entries[i].size, entries[i].data)); 50 | Add(file); 51 | } 52 | } 53 | 54 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 55 | 56 | private readonly struct Entrie 57 | { 58 | public readonly uint Unk; 59 | public readonly uint name; 60 | public readonly uint data; 61 | public readonly uint size; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/RMHG.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Common.Node; 3 | using AuroraLib.Core.Interfaces; 4 | 5 | namespace AuroraLib.Archives.Formats 6 | { 7 | /// 8 | /// Grasshopper Manufacture RMHG Archive 9 | /// 10 | // base https://github.com/Zheneq/Noesis-Plugins/blob/b47579012af3b43c1e10e06639325d16ece81f71/fmt_fatalframe_rsl.py 11 | public sealed class RMHG : ArchiveNode, IHasIdentifier 12 | { 13 | public override bool CanWrite => false; 14 | 15 | public IIdentifier Identifier => _identifier; 16 | 17 | private static readonly Identifier32 _identifier = new("RMHG"); 18 | 19 | public RMHG() 20 | { 21 | } 22 | 23 | public RMHG(string name) : base(name) 24 | { 25 | } 26 | 27 | public RMHG(FileNode source) : base(source) 28 | { 29 | } 30 | 31 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 32 | => stream.Match(_identifier); 33 | 34 | protected override void Deserialize(Stream source) 35 | { 36 | source.MatchThrow(_identifier); 37 | 38 | uint count = source.ReadUInt32(); 39 | uint DataOffset = source.ReadUInt32(); 40 | uint unknown2 = source.ReadUInt32(); 41 | uint dataSize = source.ReadUInt32(); 42 | 43 | source.Seek(DataOffset, SeekOrigin.Begin); 44 | 45 | for (int i = 0; i < count; i++) 46 | { 47 | uint offset = source.ReadUInt32(); 48 | uint size = source.ReadUInt32(); 49 | uint[] unknown = new uint[6];// 0-2 unknown | 3-5 padding ? 50 | for (int r = 0; r < 6; r++) 51 | { 52 | unknown[r] = source.ReadUInt32(); 53 | } 54 | if (size != 0) 55 | Add(new FileNode("Entry" + i, new SubStream(source, size, offset))); 56 | } 57 | } 58 | 59 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/FBTI.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Common.Node; 3 | using AuroraLib.Core.Interfaces; 4 | 5 | namespace AuroraLib.Archives.Formats 6 | { 7 | /// 8 | /// Rune Factory (Frontier) archive format 9 | /// 10 | public sealed class FBTI : ArchiveNode, IHasIdentifier 11 | { 12 | public override bool CanWrite => false; 13 | 14 | public IIdentifier Identifier => _identifier; 15 | 16 | private static readonly Identifier32 _identifier = new("FBTI"); 17 | 18 | public FBTI() 19 | { 20 | } 21 | 22 | public FBTI(string name) : base(name) 23 | { 24 | } 25 | 26 | public FBTI(FileNode source) : base(source) 27 | { 28 | } 29 | 30 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 31 | => stream.Match(_identifier); 32 | 33 | protected override void Deserialize(Stream source) 34 | { 35 | source.MatchThrow(_identifier); 36 | int version = int.Parse(source.ReadString(4)); 37 | uint file_count = source.ReadUInt32(Endian.Big); 38 | uint start_offset = source.ReadUInt32(Endian.Big); // always 0x10 39 | 40 | Span entries = stackalloc FileEntry[(int)file_count]; 41 | source.Read(entries, Endian.Big); 42 | 43 | for (int i = 0; i < file_count; i++) 44 | { 45 | string name = NLCM.GetName(source, entries[i].Offset, entries[i].Size, i); 46 | Add(new FileNode(name, new SubStream(source, entries[i].Size, entries[i].Offset))); 47 | } 48 | } 49 | 50 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 51 | 52 | private readonly struct FileEntry 53 | { 54 | public readonly uint Offset; 55 | public readonly uint Size; 56 | 57 | public FileEntry(uint offset, uint size) 58 | { 59 | Offset = offset; 60 | Size = size; 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/FONT.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common.Node; 2 | using AuroraLib.Core.Interfaces; 3 | using static AuroraLib.Texture.Formats.TXTRCC; 4 | 5 | namespace AuroraLib.Archives.Formats 6 | { 7 | /// 8 | /// Square Enix FFCC Font data archive. 9 | /// 10 | public sealed class FONT : ArchiveNode, IHasIdentifier 11 | { 12 | public override bool CanWrite => false; 13 | 14 | public IIdentifier Identifier => _identifier; 15 | 16 | private static readonly Identifier32 _identifier = new("FONT"); 17 | 18 | public FONT() 19 | { 20 | } 21 | 22 | public FONT(string name) : base(name) 23 | { 24 | } 25 | 26 | public FONT(FileNode source) : base(source) 27 | { 28 | } 29 | 30 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 31 | => stream.Length > 0xD0 && stream.Match(_identifier) && stream.ReadUInt32(Endian.Big) + 0x10 == stream.Length; 32 | 33 | protected override void Deserialize(Stream source) 34 | { 35 | CCPropertieNote root = source.Read(Endian.Big); 36 | 37 | long contentSize = source.Position + root.ContentSize - 0x10; 38 | while (source.Position < contentSize) 39 | { 40 | long offset = source.Position; 41 | CCPropertieNote propertie = source.Read(Endian.Big); 42 | FileNode file = new(propertie.Identifier.ToString(), new SubStream(source, propertie.ContentSize + 0x10, offset)); 43 | 44 | if (propertie.Identifier == 1381259348) 45 | { 46 | CCPropertieNote nameNote = source.Read(Endian.Big); 47 | file.Name = source.ReadString((int)nameNote.ContentSize); 48 | } 49 | Add(file); 50 | source.Seek(offset + propertie.ContentSize + 0x10, SeekOrigin.Begin); 51 | } 52 | } 53 | 54 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/NLCL.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Common.Node; 3 | using AuroraLib.Core.Interfaces; 4 | 5 | namespace AuroraLib.Archives.Formats 6 | { 7 | /// 8 | /// Rune Factory (Tides) archive format 9 | /// 10 | public sealed class NLCL : ArchiveNode, IHasIdentifier 11 | { 12 | public override bool CanWrite => false; 13 | 14 | public IIdentifier Identifier => _identifier; 15 | 16 | private static readonly Identifier32 _identifier = new("NLCL"); 17 | 18 | public NLCL() 19 | { 20 | } 21 | 22 | public NLCL(string name) : base(name) 23 | { 24 | } 25 | 26 | public NLCL(FileNode source) : base(source) 27 | { 28 | } 29 | 30 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 31 | => stream.Match(_identifier); 32 | 33 | protected override void Deserialize(Stream source) 34 | { 35 | source.MatchThrow(_identifier); 36 | 37 | // This archive can have other things in it. But it 38 | // isn't clear to me how the each file is sourced 39 | // there is a count but no offset... 40 | while (source.Search("HXTB")) 41 | { 42 | long entrystart = source.Position; 43 | if (!source.Match("HXTB")) 44 | continue; 45 | source.Seek(0x14, SeekOrigin.Current); 46 | uint total_size = source.ReadUInt32(Endian.Big); 47 | 48 | if (total_size > source.Length - entrystart) 49 | { 50 | source.Search("HXTB"); 51 | total_size = (uint)(source.Position - entrystart); 52 | } 53 | 54 | FileNode Sub = new($"entry_{Count + 1}.hxtb", new SubStream(source, total_size, entrystart)); 55 | Add(Sub); 56 | 57 | source.Position = entrystart + total_size; 58 | } 59 | } 60 | 61 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/ONE_SB.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common.Node; 2 | 3 | namespace AuroraLib.Archives.Formats 4 | { 5 | /// 6 | /// Archive use in the Sonic Storybook Series 7 | /// 8 | public sealed class ONE_SB : ArchiveNode 9 | { 10 | public override bool CanWrite => false; 11 | 12 | public int Version = -1; 13 | 14 | public ONE_SB() 15 | { 16 | } 17 | 18 | public ONE_SB(string name) : base(name) 19 | { 20 | } 21 | 22 | public ONE_SB(FileNode source) : base(source) 23 | { 24 | } 25 | 26 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 27 | => Matcher(stream, extension); 28 | 29 | public static bool Matcher(Stream stream, ReadOnlySpan extension = default) 30 | => extension.Contains(".one", StringComparison.InvariantCultureIgnoreCase) && stream.At(4, SeekOrigin.Begin, S => S.ReadUInt32(Endian.Big)) == 16; 31 | 32 | protected override void Deserialize(Stream source) 33 | { 34 | uint numEntries = source.ReadUInt32(Endian.Big); 35 | uint offset = source.ReadUInt32(Endian.Big); //16 36 | uint unk = source.ReadUInt32(Endian.Big); 37 | Version = source.ReadInt32(Endian.Big); // 0 Sonic and the Secret Rings or -1 for Sonic and the Black Knight 38 | 39 | source.Seek(offset, SeekOrigin.Begin); 40 | 41 | for (int i = 0; i < numEntries; i++) 42 | { 43 | string entryFilename = source.ReadString(32) + ".prs"; 44 | 45 | uint entryIndex = source.ReadUInt32(Endian.Big); 46 | uint entryOffset = source.ReadUInt32(Endian.Big); 47 | uint entryLength = source.ReadUInt32(Endian.Big); 48 | uint entryUnk = source.ReadUInt32(Endian.Big); 49 | 50 | FileNode Sub = new(entryFilename, new SubStream(source, entryLength, entryOffset)); 51 | Add(Sub); 52 | } 53 | } 54 | 55 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/AFS.cs: -------------------------------------------------------------------------------- 1 | using AFSLib; 2 | using AuroraLib.Common.Node; 3 | using AuroraLib.Core.Interfaces; 4 | 5 | namespace AuroraLib.Archives.Formats 6 | { 7 | /// 8 | /// CRIWARE AFS archive 9 | /// 10 | public sealed class AFS : ArchiveNode, IHasIdentifier 11 | { 12 | public override bool CanWrite => false; 13 | 14 | public IIdentifier Identifier => _identifierA; 15 | 16 | public static readonly Identifier32 _identifierA = new("AFS "); 17 | 18 | public static readonly Identifier32 _identifierB = new((byte)'A', (byte)'F', (byte)'S', 0); 19 | 20 | private AFSLib.AFS AFSBase; 21 | 22 | public AFS() 23 | { 24 | } 25 | 26 | public AFS(string name) : base(name) 27 | { 28 | } 29 | 30 | public AFS(FileNode source) : base(source) 31 | { 32 | } 33 | 34 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 35 | { 36 | Identifier32 identifier = stream.Read(); 37 | return identifier == _identifierA || identifier == _identifierB; 38 | } 39 | 40 | protected override void Deserialize(Stream source) 41 | { 42 | AFSBase = new AFSLib.AFS(source); 43 | 44 | foreach (Entry item in AFSBase.Entries) 45 | { 46 | if (item is StreamEntry Streamitem) 47 | { 48 | Add(new FileNode(Streamitem.SanitizedName, Streamitem.GetSubStream())); 49 | } 50 | } 51 | } 52 | 53 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 54 | 55 | private bool disposedValue; 56 | 57 | protected override void Dispose(bool disposing) 58 | { 59 | // Call base class implementation. 60 | base.Dispose(disposing); 61 | if (!disposedValue) 62 | { 63 | if (disposing && AFSBase != null) 64 | { 65 | AFSBase.Dispose(); 66 | } 67 | disposedValue = true; 68 | } 69 | 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/NARC.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Common.Node; 3 | using AuroraLib.Core.Buffers; 4 | using AuroraLib.Core.Interfaces; 5 | 6 | namespace AuroraLib.Archives.Formats 7 | { 8 | /// 9 | /// Treasure Sin and Punishment archive 10 | /// 11 | public sealed class NARC : ArchiveNode, IHasIdentifier 12 | { 13 | public override bool CanWrite => false; 14 | 15 | public IIdentifier Identifier => _identifier; 16 | 17 | private static readonly Identifier32 _identifier = new("NARC"); 18 | 19 | public NARC() 20 | { 21 | } 22 | 23 | public NARC(string name) : base(name) 24 | { 25 | } 26 | 27 | public NARC(FileNode source) : base(source) 28 | { 29 | } 30 | 31 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 32 | => stream.Length > 0x20 && stream.Match(_identifier); 33 | 34 | protected override void Deserialize(Stream source) 35 | { 36 | source.MatchThrow(_identifier); 37 | uint NrEntries = source.ReadUInt32(Endian.Big); 38 | uint StringTableSize = source.ReadUInt32(Endian.Big); 39 | uint dataTableOffset = source.ReadUInt32(Endian.Big); 40 | 41 | using SpanBuffer entries = new(NrEntries); 42 | source.Read(entries,Endian.Big); 43 | long nameTableOffset = source.Position; 44 | 45 | foreach (NARCEntry entry in entries) 46 | { 47 | source.Position = nameTableOffset + entry.NameOffset; 48 | FileNode Sub = new(source.ReadCString(), new SubStream(source, entry.DataSize, dataTableOffset + entry.DataOffset)); 49 | Add(Sub); 50 | } 51 | } 52 | 53 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 54 | 55 | private readonly struct NARCEntry 56 | { 57 | public readonly uint Unknown; 58 | public readonly uint NameOffset; 59 | public readonly uint DataOffset; 60 | public readonly uint DataSize; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/AuroraLip/Compression/Algorithms/Zstd.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Compression.Interfaces; 2 | using AuroraLib.Core.Buffers; 3 | using AuroraLib.Core.Interfaces; 4 | using System.IO.Compression; 5 | using ZstdSharp; 6 | 7 | namespace AuroraLib.Compression.Algorithms 8 | { 9 | public class Zstd : ICompressionAlgorithm, IHasIdentifier 10 | { 11 | public IIdentifier Identifier => _identifier; 12 | 13 | private static readonly Identifier32 _identifier = new(0xFD2FB528); 14 | 15 | public bool IsMatch(Stream stream, ReadOnlySpan extension = default) 16 | => stream.Length > 0x10 && stream.Match(_identifier); 17 | 18 | public void Compress(ReadOnlySpan source, Stream destination, CompressionLevel level = CompressionLevel.Optimal) 19 | { 20 | int zslevel = level switch 21 | { 22 | CompressionLevel.NoCompression => 0, 23 | CompressionLevel.Fastest => 5, 24 | CompressionLevel.Optimal => 10, 25 | CompressionLevel.SmallestSize => 20, 26 | _ => throw new NotImplementedException(), 27 | }; 28 | Compress(source, destination, zslevel); 29 | } 30 | 31 | public static void Compress(ReadOnlySpan source, Stream destination, int level) 32 | { 33 | using SpanBuffer destinationSpan = new(Compressor.GetCompressBound(source.Length)); 34 | using Compressor compressor = new(level); 35 | int length = compressor.Wrap(source, destinationSpan); 36 | destination.Write(destinationSpan.Span[..length]); 37 | } 38 | 39 | public void Decompress(Stream source, Stream destination) 40 | { 41 | using SpanBuffer sourceSpan = new((int)(source.Length - source.Position)); 42 | source.Read(sourceSpan); 43 | ulong DecompressedSize = Decompressor.GetDecompressedSize(sourceSpan); 44 | using SpanBuffer destinationSpan = new((int)DecompressedSize); 45 | using Decompressor decompressor = new(); 46 | int length = decompressor.Unwrap(sourceSpan, destinationSpan); 47 | destination.Write(destinationSpan.Span[..length]); 48 | } 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Formats/TXD.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Core.Interfaces; 3 | using RenderWareNET.Enums; 4 | using RenderWareNET.Plugins; 5 | using RenderWareNET.Structs; 6 | 7 | namespace AuroraLib.Texture.Formats 8 | { 9 | public class TXD : JUTTexture, IHasIdentifier, IFileAccess 10 | { 11 | public bool CanRead => true; 12 | 13 | public bool CanWrite => false; 14 | 15 | public virtual IIdentifier Identifier => _identifier; 16 | 17 | private static readonly Identifier32 _identifier = new(22); 18 | 19 | public bool IsMatch(Stream stream, ReadOnlySpan extension = default) 20 | { 21 | RWPluginHeader mainHeader = stream.Read(); 22 | RWPluginHeader structHeader = stream.Read(); 23 | return mainHeader.Identifier == PluginID.TextureDictionary && mainHeader.Version == structHeader.Version && structHeader.Identifier == PluginID.Struct; 24 | } 25 | 26 | protected override void Read(Stream stream) 27 | { 28 | TextureDictionary textureDictionary = new(stream); 29 | foreach (TextureNative tx in textureDictionary) 30 | { 31 | if (tx.Properties.Platform != TexturePlatformID.GC) 32 | { 33 | throw new NotSupportedException(); 34 | } 35 | using MemoryStream ms = new(tx.Properties.ImageData); 36 | GXImageFormat format = (GXImageFormat)tx.Properties.Format; 37 | GXPaletteFormat palettFormat = tx.Properties.TLOTFormat switch 38 | { 39 | FourCCType.IA8 => GXPaletteFormat.IA8, 40 | FourCCType.RGB565 => GXPaletteFormat.RGB565, 41 | FourCCType.RGB5A3 => GXPaletteFormat.RGB5A3, 42 | _ => GXPaletteFormat.IA8, 43 | }; 44 | TexEntry entry = new(ms, tx.Properties.TLOT, format, palettFormat, format.GetMaxPaletteColours(), tx.Properties.Width, tx.Properties.Height, tx.Properties.Images - 1); 45 | Add(entry); 46 | } 47 | } 48 | 49 | protected override void Write(Stream stream) => throw new NotImplementedException(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/JUTTexture.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Core.Interfaces; 2 | 3 | namespace AuroraLib.Texture 4 | { 5 | /* 6 | * base on https://github.com/SuperHackio/Hack.io 7 | */ 8 | 9 | /// 10 | /// The base class of all textures. 11 | /// 12 | public abstract partial class JUTTexture : List, IDisposable, IObjectName 13 | { 14 | /// 15 | /// The full path of this file. 16 | /// 17 | public string Name { get; set; } = null; 18 | 19 | public JUTTexture() 20 | { } 21 | 22 | public JUTTexture(Stream stream) => Read(stream); 23 | 24 | public JUTTexture(string filepath) 25 | { 26 | FileStream fs = new(filepath, FileMode.Open); 27 | Read(fs); 28 | fs.Close(); 29 | Name = filepath; 30 | } 31 | 32 | public virtual void Save(string filepath) 33 | { 34 | FileStream fs = new(filepath, FileMode.Create); 35 | Write(fs); 36 | fs.Close(); 37 | Name = filepath; 38 | } 39 | 40 | public virtual void Save(Stream stream) => Write(stream); 41 | 42 | public virtual void Open(Stream stream) => Read(stream); 43 | 44 | protected abstract void Read(Stream stream); 45 | 46 | protected abstract void Write(Stream stream); 47 | 48 | public override int GetHashCode() 49 | => HashCode.Combine(base.GetHashCode(), Name); 50 | 51 | #region Dispose 52 | 53 | private bool disposedValue; 54 | 55 | protected virtual void Dispose(bool disposing) 56 | { 57 | if (!disposedValue) 58 | { 59 | if (disposing) 60 | { 61 | foreach (var item in this) 62 | item.Dispose(); 63 | } 64 | disposedValue = true; 65 | } 66 | } 67 | 68 | ~JUTTexture() 69 | { 70 | Dispose(disposing: true); 71 | } 72 | 73 | public void Dispose() 74 | { 75 | Dispose(disposing: true); 76 | GC.SuppressFinalize(this); 77 | } 78 | 79 | #endregion Dispose 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/PKX.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common.Node; 2 | 3 | namespace AuroraLib.Archives.Formats 4 | { 5 | /// 6 | /// Genius Senority (Pokémon XD Gale of Darkness) PKX file (pokémon and some models?) 7 | /// 8 | public sealed class PKX : ArchiveNode 9 | { 10 | public override bool CanWrite => false; 11 | 12 | public PKX() 13 | { 14 | } 15 | 16 | public PKX(string name) : base(name) 17 | { 18 | } 19 | 20 | public PKX(FileNode source) : base(source) 21 | { 22 | } 23 | 24 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 25 | => Matcher(stream, extension); 26 | 27 | public static bool Matcher(Stream stream, ReadOnlySpan extension = default) 28 | => extension.Contains(".pkx", StringComparison.InvariantCultureIgnoreCase) && stream.At(0x1A, s => s.ReadUInt16(Endian.Big) == 0x0C); 29 | 30 | protected override void Deserialize(Stream source) 31 | { 32 | Header header = source.Read
(Endian.Big); 33 | 34 | if (header.Unknown1A == 0x0C) 35 | { 36 | uint archive_begin = 0x84 + header.NumEntries * 208; 37 | archive_begin = (archive_begin + 31) & ~(uint)31; // Round to next 32-byte boundary 38 | archive_begin = (archive_begin + header.Unknown08 + 31) & ~(uint)31; 39 | 40 | FileNode Sub = new("thing.GSscene", new SubStream(source, header.ArchiveSize, archive_begin)); 41 | Add(Sub); 42 | } 43 | else 44 | { 45 | throw new NotImplementedException($"Unknown header value {header.Unknown1A}"); 46 | } 47 | } 48 | 49 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 50 | 51 | public struct Header 52 | { 53 | public uint ArchiveSize; 54 | public uint Unknown04; 55 | public uint Unknown08; 56 | public uint Unknown0C; 57 | public uint NumEntries; 58 | public uint Unknown14; 59 | public ushort Unknown18; 60 | public ushort Unknown1A; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /TextureExtraction tool/Data/LogBase.cs: -------------------------------------------------------------------------------- 1 | namespace DolphinTextureExtraction 2 | { 3 | public class LogBase : StreamWriter 4 | { 5 | protected static readonly object Lock = new(); 6 | 7 | public string FullPath { get; private set; } 8 | 9 | public LogBase(Stream FullPath) : base(FullPath) 10 | => this.FullPath = string.Empty; 11 | 12 | public LogBase(string FullPath) : base(FullPath, new FileStreamOptions() { Access = FileAccess.Write, Mode = FileMode.Create, Share = FileShare.Read }) 13 | => this.FullPath = FullPath; 14 | 15 | public LogBase(string FullPath, bool append) : base(FullPath, append) 16 | => this.FullPath = FullPath; 17 | 18 | /// 19 | /// Convert a thread's id to a base 1 index, increasing in increments of 1 (Makes logs prettier) 20 | /// 21 | protected int ThreadIndex 22 | { 23 | get 24 | { 25 | int managed = Environment.CurrentManagedThreadId; 26 | if (!ThreadIndices.TryGetValue(managed, out int id)) 27 | ThreadIndices.Add(managed, id = ThreadIndices.Count + 1); 28 | 29 | return id; 30 | } 31 | } 32 | private readonly Dictionary ThreadIndices = new(); 33 | 34 | public override void WriteLine() 35 | { 36 | lock (Lock) 37 | { 38 | base.WriteLine(); 39 | } 40 | } 41 | 42 | public override void WriteLine(string value) 43 | { 44 | lock (Lock) 45 | { 46 | base.WriteLine(value); 47 | } 48 | } 49 | 50 | public override void Write(string value) 51 | { 52 | lock (Lock) 53 | { 54 | base.Write(value); 55 | } 56 | } 57 | 58 | public override void Write(ReadOnlySpan value) 59 | { 60 | lock (Lock) 61 | { 62 | base.Write(value); 63 | } 64 | } 65 | 66 | public override void Write(char[] value) 67 | { 68 | lock (Lock) 69 | { 70 | base.Write(value); 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /TextureExtraction tool/Scans/Results/FinalizeResults.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Texture; 2 | using System.Text; 3 | 4 | namespace DolphinTextureExtraction.Scans.Results 5 | { 6 | public class FinalizeResults : ScanResults 7 | { 8 | private readonly List Hash = new(); 9 | 10 | public int Optimizations { get; private set; } = 0; 11 | public int Duplicates { get; private set; } = 0; 12 | 13 | public long NewSize { get; private set; } = 0; 14 | public long OldSize { get; private set; } = 0; 15 | 16 | public double OptimizationRate => ((double)NewSize / OldSize) * 100 - 100; 17 | 18 | public override string ToString() 19 | { 20 | StringBuilder sb = new(); 21 | if (Hash.Count > 1) 22 | sb.AppendLine($"Textures processed: {Hash.Count}"); 23 | if (Optimizations > 0) 24 | sb.AppendLine($"Optimizations: {Optimizations}"); 25 | if (Duplicates > 0) 26 | sb.AppendLine($"Duplicates: {Duplicates}"); 27 | if (Optimizations > 0) 28 | { 29 | sb.AppendLine($"File size from {PathX.AddSizeSuffix(OldSize, 2)} to {PathX.AddSizeSuffix(NewSize, 2)}"); 30 | sb.AppendLine($"File size ratio: {OptimizationRate:+#.##;-#.##;0.00}%"); 31 | } 32 | sb.AppendLine($"Scan time: {TotalTime.TotalSeconds:.000}s"); 33 | return sb.ToString(); 34 | } 35 | 36 | internal void AddOptimization() 37 | { 38 | lock (this) 39 | { 40 | Optimizations++; 41 | } 42 | } 43 | 44 | internal void AddSize(long oldSize, long newSize) 45 | { 46 | lock (this) 47 | { 48 | OldSize += oldSize; 49 | NewSize += newSize; 50 | } 51 | } 52 | 53 | internal bool AddHashIfNeeded(int hash) 54 | { 55 | lock (Hash) 56 | { 57 | //Skip duplicate textures 58 | if (Hash.Contains(hash)) 59 | { 60 | Duplicates++; 61 | return true; 62 | } 63 | Hash.Add(hash); 64 | } 65 | return false; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/AuroraLip/DiscImage/Dolphin/FSTBin.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common.Node; 2 | 3 | namespace AuroraLib.DiscImage.Dolphin 4 | { 5 | public class FSTBin 6 | { 7 | public FSTEntry[] Entires; 8 | public long stringTableOffset; 9 | 10 | private readonly bool _IsGC; 11 | 12 | public FSTBin(Stream stream, bool isGC = true) 13 | { 14 | _IsGC = isGC; 15 | var root = stream.Read(Endian.Big); 16 | Entires = stream.For((int)root.Data - 1, S => S.Read(Endian.Big)); 17 | stringTableOffset = stream.Position; 18 | } 19 | 20 | public void ProcessEntres(Stream stream, DirectoryNode directory) 21 | => ProcessEntres(stream, directory, 0, Entires.Length); 22 | 23 | private int ProcessEntres(Stream stream, DirectoryNode directory, int i, int l) 24 | { 25 | while (i < l) 26 | { 27 | stream.Seek(stringTableOffset + (int)Entires[i].NameOffset, SeekOrigin.Begin); 28 | string name = stream.ReadCString(); 29 | if (Entires[i].IsDirectory) 30 | { 31 | DirectoryNode subdir = new(name); 32 | directory.Add(subdir); 33 | i = ProcessEntres(stream, subdir, i + 1, (int)Entires[i].Data - 1); 34 | } 35 | else 36 | { 37 | if (_IsGC) 38 | directory.Add(new FileNode(name, new SubStream(stream, Entires[i].Data, Entires[i].Offset))); 39 | else 40 | directory.Add(new FileNode(name, new SubStream(stream, Entires[i].Data, Entires[i].Offset << 2))); 41 | i++; 42 | } 43 | } 44 | return l; 45 | } 46 | 47 | public struct FSTEntry 48 | { 49 | public byte Flag { get; set; } 50 | public UInt24 NameOffset { get; set; } 51 | public uint Offset { get; set; } // file or parent Offset 52 | public uint Data { get; set; } // fileSize or numberOfFiles Offset 53 | 54 | public bool IsDirectory 55 | { 56 | readonly get => Flag != 0; 57 | set => Flag = (byte)(value ? 1 : 0); 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /TextureExtraction tool/Scans/Compress.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Compression; 2 | using AuroraLib.Compression.Interfaces; 3 | using DolphinTextureExtraction.Scans.Helper; 4 | using DolphinTextureExtraction.Scans.Options; 5 | using DolphinTextureExtraction.Scans.Results; 6 | 7 | namespace DolphinTextureExtraction.Scans 8 | { 9 | public class Compress : ScanBase 10 | { 11 | private readonly Type algorithm; 12 | 13 | public double CompressionRate { get; private set; } = 0; 14 | 15 | public static ScanResults StartScan(string meindirectory, string savedirectory, Type Algorithm, ScanOptions options, string logDirectory = null) 16 | => StartScan_Async(meindirectory, savedirectory, Algorithm, options, logDirectory).Result; 17 | 18 | public static async Task StartScan_Async(string meindirectory, string savedirectory, Type Algorithm, ScanOptions options, string logDirectory = null) 19 | { 20 | Compress Extractor = new(meindirectory, savedirectory, Algorithm, options, logDirectory); 21 | return await Extractor.StartScan_Async(); 22 | } 23 | 24 | internal Compress(string scanDirectory, string saveDirectory, Type Algorithm, ScanOptions options = null, string logDirectory = null) : base(scanDirectory, saveDirectory, options, logDirectory) 25 | => algorithm = Algorithm; 26 | 27 | protected override void Scan(ScanObjekt so) 28 | { 29 | ICompressionAlgorithm algo = (ICompressionAlgorithm)Activator.CreateInstance(algorithm); 30 | using FileStream destination = new(GetFullSaveDirectory(so.File.GetFullPath()), FileMode.CreateNew, FileAccess.ReadWrite, FileShare.Read); 31 | algo.Compress(so.File.Data.ToArray(), destination); 32 | AddResult(so, destination); 33 | } 34 | 35 | private void AddResult(ScanObjekt so, Stream destination) 36 | { 37 | double compressionRate = ((double)destination.Length / so.File.Data.Length - 1) * 100; 38 | lock (Result) 39 | { 40 | CompressionRate = (CompressionRate + compressionRate) / 2; 41 | } 42 | Log.Write(FileAction.Compress, so.File.GetFullPath() + $" ~{PathX.AddSizeSuffix(destination.Length, 2)}", $"Algo:{algorithm.Name} Rate:{compressionRate:+#.##;-#.##;0.00}%"); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Formats/GCNT.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Core.Interfaces; 3 | 4 | namespace AuroraLib.Texture.Formats 5 | { 6 | public class GCNT : JUTTexture, IHasIdentifier, IFileAccess 7 | { 8 | public virtual bool CanRead => true; 9 | 10 | public virtual bool CanWrite => false; 11 | 12 | public virtual IIdentifier Identifier => _identifier; 13 | 14 | private static readonly Identifier32 _identifier = new("GCNT"); 15 | private static readonly Identifier32 _alt = new("SIZE"); 16 | 17 | public bool IsMatch(Stream stream, ReadOnlySpan extension = default) 18 | => stream.Length > 0x10 && (stream.Match(_identifier) || stream.At(8, s => s.Match(_identifier))); 19 | 20 | protected override void Read(Stream stream) 21 | { 22 | long HeaderStart = stream.Position; 23 | 24 | if (!stream.Match(_identifier)) 25 | { 26 | HeaderStart = stream.Position += 4; 27 | stream.MatchThrow(_identifier); 28 | } 29 | 30 | ImageHeader ImageHeader = stream.Read(Endian.Big); 31 | 32 | TexEntry current = new(stream, ImageHeader.Format, ImageHeader.PaletteFormat, ImageHeader.Width, ImageHeader.Height, ImageHeader.Mipmaps) 33 | { 34 | LODBias = 0, 35 | MagnificationFilter = default, 36 | MinificationFilter = default, 37 | WrapS = default, 38 | WrapT = default, 39 | EnableEdgeLOD = default, 40 | MinLOD = 0, 41 | MaxLOD = ImageHeader.Mipmaps+1 42 | }; 43 | Add(current); 44 | } 45 | 46 | protected override void Write(Stream stream) => throw new NotImplementedException(); 47 | 48 | private struct ImageHeader 49 | { 50 | public uint Unknown; //0x3 51 | public ushort Offset; 52 | public ushort Version;//? 53 | public uint Size; 54 | public ushort Width; 55 | public ushort Height; 56 | public GXImageFormat Format; 57 | public GXPaletteFormat PaletteFormat; 58 | public byte Mipmaps; 59 | public byte Padding23; 60 | public uint Padding24; 61 | public uint Padding28; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/J3D/NameTableIO.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.Texture.J3D 2 | { 3 | /* 4 | * Super Hackio Incorporated 5 | * "Copyright © Super Hackio Incorporated 2020-2021" 6 | * https://github.com/SuperHackio/Hack.io 7 | */ 8 | 9 | public static class NameTableIO 10 | { 11 | public static List ReadStringTable(this Stream stream, int offset) 12 | { 13 | List names = new List(); 14 | 15 | stream.Position = offset; 16 | 17 | short stringCount = stream.ReadInt16(Endian.Big); 18 | stream.Position += 0x02; 19 | 20 | for (int i = 0; i < stringCount; i++) 21 | { 22 | stream.Position += 0x02; 23 | short nameOffset = stream.ReadInt16(Endian.Big); 24 | long saveReaderPos = stream.Position; 25 | stream.Position = offset + nameOffset; 26 | 27 | names.Add(stream.ReadCString()); 28 | 29 | stream.Position = saveReaderPos; 30 | } 31 | 32 | return names; 33 | } 34 | 35 | public static void WriteStringTable(this Stream writer, List names) 36 | { 37 | long start = writer.Position; 38 | 39 | writer.Write((short)names.Count, Endian.Big); 40 | writer.Write(new byte[2] { 0xFF, 0xFF }, 0, 2); 41 | 42 | foreach (string st in names) 43 | { 44 | writer.Write(HashString(st), Endian.Big); 45 | writer.Write(new byte[2], 0, 2); 46 | } 47 | 48 | long curOffset = writer.Position; 49 | for (int i = 0; i < names.Count; i++) 50 | { 51 | writer.Seek((int)(start + (6 + i * 4)), SeekOrigin.Begin); 52 | writer.Write((short)(curOffset - start), Endian.Big); 53 | writer.Seek((int)curOffset, SeekOrigin.Begin); 54 | 55 | writer.WriteString(names[i], 0x00); 56 | 57 | curOffset = writer.Position; 58 | } 59 | } 60 | 61 | private static ushort HashString(string str) 62 | { 63 | ushort hash = 0; 64 | 65 | foreach (char c in str) 66 | { 67 | hash *= 3; 68 | hash += (ushort)c; 69 | } 70 | 71 | return hash; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Formats/RES_NLG.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | 3 | namespace AuroraLib.Texture.Formats 4 | { 5 | public class RES_NLG : JUTTexture, IFileAccess 6 | { 7 | public bool CanRead => true; 8 | 9 | public bool CanWrite => false; 10 | 11 | public const string Extension = ".res"; 12 | public const string Extension2 = ".dmn"; 13 | 14 | public bool IsMatch(Stream stream, ReadOnlySpan extension = default) 15 | => Matcher(stream, extension); 16 | 17 | public static bool Matcher(Stream stream, ReadOnlySpan extension = default) 18 | => (extension.Contains(Extension, StringComparison.InvariantCultureIgnoreCase) || extension.Contains(Extension2, StringComparison.InvariantCultureIgnoreCase) && stream.ReadInt32(Endian.Big) == 0x20 && stream.ReadInt32(Endian.Big) != 0 && stream.ReadInt32(Endian.Big) == 1); 19 | 20 | protected override void Read(Stream stream) 21 | { 22 | Header header = stream.Read
(Endian.Big); 23 | stream.Seek(header.Offset, SeekOrigin.Begin); 24 | Entry[] entries = stream.For((int)header.Entrys, s => s.Read(Endian.Big)); 25 | for (int i = 0; i < entries.Length; i++) 26 | { 27 | stream.Seek(entries[i].Offset, SeekOrigin.Begin); 28 | 29 | if (PTLG.ReadTexture(stream, entries[i].Size, out TexEntry current)) 30 | { 31 | Add(current); 32 | } 33 | 34 | } 35 | } 36 | 37 | protected override void Write(Stream stream) => throw new NotImplementedException(); 38 | 39 | private struct Header 40 | { 41 | public uint Offset; // 0x20 42 | public uint Entrys; 43 | public uint Version; //1 44 | private uint firstEntry; 45 | 46 | public long FirstEntryOffset 47 | { 48 | get => firstEntry << 5; 49 | set => firstEntry = (uint)(value >> 5); 50 | } 51 | } 52 | 53 | private struct Entry 54 | { 55 | public uint Hash; 56 | private uint offset; 57 | public uint Size; 58 | 59 | public long Offset 60 | { 61 | get => offset << 5; 62 | set => offset = (uint)(value >> 5); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /TextureExtraction tool/AppSettings.cs: -------------------------------------------------------------------------------- 1 | using LibCPK; 2 | using System.Collections.Specialized; 3 | using System.Configuration; 4 | 5 | namespace DolphinTextureExtraction 6 | { 7 | internal static class AppSettings 8 | { 9 | public static readonly NameValueCollection Config; 10 | 11 | public static readonly bool UseConfig; 12 | 13 | public static bool DryRun = false; 14 | 15 | public static bool Force = false; 16 | 17 | public static uint Deep = 0; 18 | 19 | public static bool Mips = false; 20 | 21 | public static bool ArbitraryMipmapDetection = true; 22 | 23 | public static bool Raw = false; 24 | 25 | public static bool DolphinMipDetection = true; 26 | 27 | public static bool CombinedRGBA = false; 28 | 29 | #if DEBUG 30 | public static ParallelOptions Parallel = new() { MaxDegreeOfParallelism = 1 }; 31 | #else 32 | public static readonly ParallelOptions Parallel = new() { MaxDegreeOfParallelism = 4 }; 33 | #endif 34 | 35 | static AppSettings() 36 | { 37 | Config = ConfigurationManager.AppSettings; 38 | UseConfig = Config.HasKeys() && bool.TryParse(Config.Get("UseConfig"), out bool value) && value; 39 | 40 | if (UseConfig) 41 | { 42 | if (bool.TryParse(Config.Get("DryRun"), out value)) 43 | DryRun = value; 44 | if (bool.TryParse(Config.Get("Force"), out value)) 45 | Force = value; 46 | if (uint.TryParse(Config.Get("Deep"), out uint deep)) 47 | Deep = deep; 48 | if (int.TryParse(Config.Get("Tasks"), out int thing)) 49 | Parallel.MaxDegreeOfParallelism = thing <= 0 ? 1 : thing; 50 | if (bool.TryParse(Config.Get("Mips"), out value)) 51 | Mips = value; 52 | if (bool.TryParse(Config.Get("Raw"), out value)) 53 | Raw = value; 54 | if (bool.TryParse(Config.Get("DolphinMipDetection"), out value)) 55 | DolphinMipDetection = value; 56 | if (bool.TryParse(Config.Get("ArbitraryMipmapDetection"), out value)) 57 | ArbitraryMipmapDetection = value; 58 | if (bool.TryParse(Config.Get("CombinedRGBA"), out value)) 59 | CombinedRGBA = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /TextureExtraction tool/DolphinTextureExtraction tool.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | {89776DB8-07A1-4B36-BE77-7AB5E4237E3A} 7 | enable 8 | disable 9 | False 10 | 1.8.0.$([System.DateTime]::Now.ToString(`MMdd`)) 11 | $(Version) 12 | $(Version) 13 | MIT 14 | DolphinTextureExtraction.tool 15 | DolphinTextureExtraction 16 | False 17 | DolphinTextureExtraction.Program 18 | Icon.ico 19 | Dumps GC and Wii textures, compatible with the Dolphin texture hash. 20 | Copyright © Venomalia 2022-present 21 | https://github.com/Venomalia/DolphinTextureExtraction-tool 22 | Dolphin Texture Extraction Tool 23 | Venomalia 24 | True 25 | False 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | PreserveNewest 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Formats/GCT0.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Core.Interfaces; 3 | 4 | namespace AuroraLib.Texture.Formats 5 | { 6 | public class GCT0 : JUTTexture, IHasIdentifier, IFileAccess 7 | { 8 | public bool CanRead => true; 9 | 10 | public bool CanWrite => false; 11 | 12 | public virtual IIdentifier Identifier => _identifier; 13 | 14 | private static readonly Identifier32 _identifier = new("GCT0"); 15 | 16 | public bool IsMatch(Stream stream, ReadOnlySpan extension = default) 17 | => stream.Match(_identifier); 18 | 19 | protected override void Read(Stream stream) 20 | { 21 | stream.MatchThrow(_identifier); 22 | 23 | GXImageFormat Format = (GXImageFormat)stream.ReadUInt32(Endian.Big); 24 | ushort Width = stream.ReadUInt16(Endian.Big); 25 | ushort Height = stream.ReadUInt16(Endian.Big); 26 | byte unkflag = (byte)stream.ReadByte(); //Flag? 27 | _ = stream.ReadInt24(Endian.Big); 28 | uint ImgOffset = stream.ReadUInt32(Endian.Big); 29 | _ = stream.ReadUInt64(Endian.Big); 30 | ushort unkmip = stream.ReadUInt16(Endian.Big); //mips? 31 | _ = stream.ReadUInt16(Endian.Big); 32 | uint unknown = stream.ReadUInt32(Endian.Big); //202 33 | 34 | // we calculate the mips 35 | int mips = Format.GetMipmapsFromSize((int)(stream.Length - ImgOffset), Width, Height); 36 | 37 | // Palette are not supported? 38 | if (Format.IsPaletteFormat()) 39 | { 40 | throw new PaletteException($"{nameof(GCT0)} does not support palette formats."); 41 | } 42 | 43 | stream.Seek(ImgOffset, SeekOrigin.Begin); 44 | Add(new TexEntry(stream, null, Format, GXPaletteFormat.IA8, 0, Width, Height, mips) 45 | { 46 | LODBias = 0, 47 | MagnificationFilter = GXFilterMode.Nearest, 48 | MinificationFilter = GXFilterMode.Nearest, 49 | WrapS = GXWrapMode.CLAMP, 50 | WrapT = GXWrapMode.CLAMP, 51 | EnableEdgeLOD = false, 52 | MinLOD = 0, 53 | MaxLOD = mips 54 | }); 55 | } 56 | 57 | protected override void Write(Stream stream) 58 | { 59 | throw new NotImplementedException(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/CPK.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common.Node; 2 | using AuroraLib.Core.Interfaces; 3 | using System.Text; 4 | 5 | namespace AuroraLib.Archives.Formats 6 | { 7 | /// 8 | /// CRIWARE Compact Archive 9 | /// 10 | public sealed class CPK : ArchiveNode, IHasIdentifier 11 | { 12 | public override bool CanWrite => false; 13 | 14 | public IIdentifier Identifier => _identifier; 15 | 16 | private static readonly Identifier32 _identifier = new("CPK "); 17 | 18 | public readonly LibCPK.CPK CpkContent = new(); 19 | 20 | public CPK() 21 | { 22 | } 23 | 24 | public CPK(string name) : base(name) 25 | { 26 | } 27 | 28 | public CPK(FileNode source) : base(source) 29 | { 30 | } 31 | 32 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 33 | => stream.Match(_identifier); 34 | 35 | protected override void Deserialize(Stream source) 36 | { 37 | CpkContent.ReadCPK(source, Encoding.UTF8); 38 | 39 | foreach (var entrie in CpkContent.fileTable) 40 | { 41 | if (entrie.FileType != LibCPK.FileTypeFlag.FILE) 42 | continue; 43 | 44 | DirectoryNode dir; 45 | if (String.IsNullOrWhiteSpace(entrie.DirName)) 46 | { 47 | dir = this; 48 | } 49 | else 50 | { 51 | if (TryGet(entrie.DirName, out ObjectNode objectNode)) 52 | { 53 | dir = (DirectoryNode)objectNode; 54 | } 55 | else 56 | { 57 | dir = new DirectoryNode(Path.GetFileName(entrie.DirName)); 58 | AddPath(entrie.DirName, dir); 59 | } 60 | } 61 | 62 | // important files are available multiple times. 63 | FileNode file = new(entrie.FileName, new SubStream(source, UInt32.Parse(entrie.FileSize.ToString()), (long)entrie.FileOffset)); 64 | if (!dir.TryAdd(file)) 65 | { 66 | file.Dispose(); 67 | } 68 | } 69 | } 70 | 71 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/PAC.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Common.Node; 3 | using AuroraLib.Core.Buffers; 4 | 5 | namespace AuroraLib.Archives.Formats 6 | { 7 | /// 8 | /// Sting Entertainment PAC Archive. 9 | /// 10 | public sealed class PAC : ArchiveNode 11 | { 12 | public override bool CanWrite => false; 13 | 14 | public PAC() 15 | { 16 | } 17 | 18 | public PAC(string name) : base(name) 19 | { 20 | } 21 | 22 | public PAC(FileNode source) : base(source) 23 | { 24 | } 25 | 26 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 27 | => extension.SequenceEqual(".PAC") && stream.Length > 52428800; 28 | 29 | protected override void Deserialize(Stream source) 30 | { 31 | //try to request an external file. 32 | string datname = Path.GetFileNameWithoutExtension(Name); 33 | if (TryGetRefFile(datname + ".PAH", out FileNode refFile)) 34 | { 35 | using Stream streamPAH = refFile.Data; 36 | uint tabelEntrys = streamPAH.Read(); 37 | uint tabelOffset = streamPAH.Read(); 38 | uint tabelEnd = streamPAH.Read(); 39 | // Unknown values from 0x7 to 0x70 40 | streamPAH.Seek(tabelOffset, SeekOrigin.Begin); 41 | using SpanBuffer entries = new((int)tabelEntrys); 42 | streamPAH.Read(entries.Span); 43 | foreach (var entry in entries) 44 | { 45 | streamPAH.Seek(entry.NameOffset, SeekOrigin.Begin); 46 | string name = streamPAH.ReadCString(); 47 | FileNode file = new(name, new SubStream(source, entry.Size, entry.Offset)); 48 | Add(file); 49 | } 50 | } 51 | else 52 | { 53 | throw new Exception($"{nameof(PAC)}: could not request the file {datname}."); 54 | } 55 | } 56 | 57 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 58 | 59 | private struct PAHFileEntry 60 | { 61 | public uint Offset; 62 | public uint Size; 63 | public uint Null; 64 | public uint NameOffset; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/AuroraLip/Compression/CompressionReflection.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Compression.Interfaces; 3 | 4 | namespace AuroraLib.Compression 5 | { 6 | public class CompressionReflection : FileAccessReflection 7 | { 8 | public CompressionReflection() : base() 9 | { 10 | } 11 | 12 | public bool TryToDecompress(Stream source, out List destinations, out Type type) 13 | { 14 | destinations = new(); 15 | if (TryToDecompress(source, out Stream destination, out ICompressionDecoder decoder)) 16 | { 17 | type = decoder.GetType(); 18 | destinations.Add(destination); 19 | while (source.Position + 0x10 < source.Length) 20 | { 21 | while (source.ReadByte() == 0) 22 | { } 23 | source.Position--; 24 | if (source.Peek(s => decoder.IsMatch(s))) 25 | { 26 | destination = new MemoryPoolStream(); 27 | decoder.Decompress(source, destination); 28 | destinations.Add(destination); 29 | } 30 | else 31 | { 32 | break; 33 | } 34 | } 35 | return true; 36 | } 37 | destinations = null; 38 | type = null; 39 | return false; 40 | } 41 | 42 | public bool TryToDecompress(Stream source, out Stream destination, out ICompressionDecoder type) 43 | { 44 | destination = new MemoryPoolStream(); 45 | 46 | foreach (ICompressionDecoder decoder in Instances) 47 | { 48 | try 49 | { 50 | if (source.Peek(s => decoder.IsMatch(s))) 51 | { 52 | decoder.Decompress(source, destination); 53 | type = decoder; 54 | return true; 55 | } 56 | } 57 | catch (Exception t) 58 | { 59 | destination.SetLength(0); 60 | } 61 | } 62 | 63 | destination.Dispose(); 64 | destination = null; 65 | type = null; 66 | return false; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /TextureExtraction tool/Scans/Cutter.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Archives; 2 | using AuroraLib.Common.Node; 3 | using DolphinTextureExtraction.Scans.Helper; 4 | using DolphinTextureExtraction.Scans.Options; 5 | using DolphinTextureExtraction.Scans.Results; 6 | 7 | namespace DolphinTextureExtraction.Scans 8 | { 9 | public class Cutter : ScanBase 10 | { 11 | private readonly List Pattern; 12 | 13 | internal Cutter(string scanDirectory, string saveDirectory, ScanOptions options = null, string logDirectory = null) : base(scanDirectory, saveDirectory, options, logDirectory) { } 14 | 15 | internal Cutter(string scanDirectory, string saveDirectory, List pattern, ScanOptions options = null, string logDirectory = null) : base(scanDirectory, saveDirectory, options, logDirectory) 16 | => Pattern = pattern; 17 | 18 | public static ScanResults StartScan(string meindirectory, string savedirectory, List pattern, ScanOptions options, string logDirectory = null) 19 | => StartScan_Async(meindirectory, savedirectory, pattern, options, logDirectory).Result; 20 | 21 | public static async Task StartScan_Async(string meindirectory, string savedirectory, List pattern, ScanOptions options, string logDirectory = null) 22 | { 23 | Cutter Extractor = new(meindirectory, savedirectory, pattern, options, logDirectory); 24 | return await Extractor.StartScan_Async(); 25 | } 26 | protected override void Scan(ScanObjekt so) 27 | { 28 | if (so.Deep != 0) 29 | { 30 | Save(so); 31 | } 32 | else 33 | { 34 | ArchiveNode archive; 35 | if (Pattern == null) 36 | archive = new DataCutter(so.File.Data); 37 | else 38 | archive = new DataCutter(so.File.Data, Pattern); 39 | 40 | if (archive.Count > 0) 41 | { 42 | if (archive.Count == 1) 43 | { 44 | foreach (var item in archive) 45 | { 46 | if (item.Value is FileNode file) 47 | Save(file.Data, so.File.GetFullPath(), so.Format); 48 | else 49 | Scan(archive, so.Deep + 1); 50 | } 51 | } 52 | } 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/PAKb.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common.Node; 2 | using AuroraLib.Core.Buffers; 3 | using AuroraLib.Core.Interfaces; 4 | 5 | namespace AuroraLib.Archives.Formats 6 | { 7 | /// 8 | /// Keen Games Dawn of Discovery Archive 9 | /// 10 | public sealed class PAKb : ArchiveNode, IHasIdentifier 11 | { 12 | public override bool CanWrite => false; 13 | 14 | public IIdentifier Identifier => _identifier; 15 | 16 | private static readonly Identifier32 _identifier = new("PAKb"); 17 | 18 | public PAKb() 19 | { 20 | } 21 | 22 | public PAKb(string name) : base(name) 23 | { 24 | } 25 | 26 | public PAKb(FileNode source) : base(source) 27 | { 28 | } 29 | 30 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 31 | => stream.Match(_identifier); 32 | 33 | protected override void Deserialize(Stream source) 34 | { 35 | source.MatchThrow(_identifier); 36 | 37 | uint file_count = source.ReadUInt32(Endian.Big); 38 | 39 | using SpanBuffer file_data = new(file_count); 40 | source.Read(file_data, Endian.Big); 41 | 42 | uint names_start = file_data[(int)file_count - 1].Offset; 43 | 44 | for (int i = 0; i < file_count; i++) 45 | { 46 | source.Seek(names_start, SeekOrigin.Begin); 47 | 48 | uint expected_crc = file_data[i].Crc; 49 | uint crc = 0; 50 | string name = ""; 51 | do 52 | { 53 | crc = source.ReadUInt32(Endian.Big); 54 | uint name_size = source.ReadUInt32(Endian.Big); 55 | if (name_size == 0) 56 | { 57 | crc = expected_crc; 58 | } 59 | name = source.ReadString((int)name_size); 60 | } while (expected_crc != crc); 61 | 62 | FileNode file = new(name, new SubStream(source, file_data[i].Size, file_data[i].Offset)); 63 | Add(file); 64 | } 65 | } 66 | 67 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 68 | 69 | private readonly struct FileData 70 | { 71 | public readonly uint Crc; 72 | public readonly uint Offset; 73 | public readonly uint Size; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/AuroraLip/DiscImage/Dolphin/GameHeader.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Core.Interfaces; 2 | 3 | namespace AuroraLib.DiscImage.Dolphin 4 | { 5 | public abstract class GameHeader : IBinaryObject 6 | { 7 | public const uint WiiMagicWord = 1562156707; 8 | public const uint GCMagicWord = 3258163005; 9 | 10 | public GameID GameID; 11 | public byte DiskID; 12 | public byte Version; 13 | public bool Streaming; 14 | public byte StreamBufSize; // 0 = default BufSize 15 | 16 | public string GameName { get; set; } 17 | public DiskTypes DiskType; 18 | 19 | protected GameHeader() 20 | { } 21 | 22 | public GameHeader(Stream source) 23 | => BinaryDeserialize(source); 24 | 25 | public virtual void BinaryDeserialize(Stream source) 26 | { 27 | GameID = source.Read(); 28 | DiskID = source.ReadUInt8(); 29 | Version = source.ReadUInt8(); 30 | Streaming = source.ReadUInt8() == 1; 31 | StreamBufSize = source.ReadUInt8(); 32 | source.Position += 14; 33 | uint magicWordWii = source.ReadUInt32(Endian.Big); // GC 0 Wii 1562156707 34 | uint magicWordGC = source.ReadUInt32(Endian.Big); // GC 3258163005 Wii 0 35 | if (magicWordWii == 0 && magicWordGC == GCMagicWord) DiskType = DiskTypes.GC; 36 | else if (magicWordWii == WiiMagicWord && magicWordGC == 0) DiskType = DiskTypes.Wii; 37 | else DiskType = DiskTypes.Invalid; 38 | ReadData(source); 39 | } 40 | 41 | public virtual void BinarySerialize(Stream dest) 42 | { 43 | dest.Write(GameID); 44 | dest.WriteByte(DiskID); 45 | dest.WriteByte(Version); 46 | dest.WriteByte((byte)(Streaming ? 1 : 0)); 47 | dest.WriteByte(StreamBufSize); 48 | dest.Write(stackalloc byte[14]); 49 | if (DiskType == DiskTypes.Wii) 50 | { 51 | dest.Write(WiiMagicWord, Endian.Big); 52 | dest.Write(0); 53 | } 54 | else 55 | { 56 | dest.Write(0); 57 | dest.Write(GCMagicWord, Endian.Big); 58 | } 59 | WriteData(dest); 60 | } 61 | 62 | protected abstract void ReadData(Stream source); 63 | protected abstract void WriteData(Stream dest); 64 | } 65 | 66 | public enum DiskTypes 67 | { 68 | Invalid = 0, 69 | GC = 1, 70 | Wii = 2, 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Formats/GTX1.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Core.Interfaces; 3 | 4 | namespace AuroraLib.Texture.Formats 5 | { 6 | public class GTX1 : JUTTexture, IHasIdentifier, IFileAccess 7 | { 8 | public virtual bool CanRead => true; 9 | 10 | public virtual bool CanWrite => false; 11 | 12 | public virtual IIdentifier Identifier => _identifier; 13 | 14 | private static readonly Identifier32 _identifier = new("GTX1"); 15 | 16 | public bool IsMatch(Stream stream, ReadOnlySpan extension = default) 17 | => stream.Length > 0x10 && stream.At(4, s => s.Match(_identifier)); 18 | 19 | protected override void Read(Stream stream) 20 | { 21 | Header header = stream.Read
(Endian.Big); 22 | 23 | //hack to get mips i do not trust the header.mip value 24 | int mips = header.Format.GetMipmapsFromSize((int)header.Size, header.Width, header.Height) - 1; 25 | 26 | Add(new(stream, Span.Empty, header.Format, GXPaletteFormat.IA8, 0, header.Width, header.Height, mips) 27 | { 28 | LODBias = 0, 29 | MagnificationFilter = GXFilterMode.Nearest, 30 | MinificationFilter = GXFilterMode.Nearest, 31 | WrapS = GXWrapMode.CLAMP, 32 | WrapT = GXWrapMode.CLAMP, 33 | EnableEdgeLOD = false, 34 | MinLOD = 0, 35 | MaxLOD = mips 36 | }); 37 | } 38 | 39 | protected override void Write(Stream stream) => throw new NotImplementedException(); 40 | 41 | private struct Header 42 | { 43 | public uint Size; 44 | public Identifier32 Identifier; //GTX1 45 | public byte PahtID; 46 | public byte ImageID; 47 | public GXImageFormat Format; 48 | public byte Unk1;//255 49 | public ushort Width; 50 | public ushort Height; 51 | 52 | public uint Unk2;//0 53 | public byte Unk3;//0 80 54 | public byte data; 55 | public ushort Unk5; // 0 256 56 | public Identifier64 Name; //loobFrab 57 | 58 | public int Unk4 //1 2 3 11 59 | { 60 | get => (data >> 4); 61 | set => data = (byte)((data & 0x0F) | (value << 4)); 62 | } 63 | public int Mips 64 | { 65 | get => (data & 0xF); 66 | set => data = (byte)((data & 0xF0) | value); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/AuroraLip/DiscImage/Dolphin/BootBin.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.DiscImage.Dolphin 2 | { 3 | public class BootBin : GameHeader 4 | { 5 | public uint DebugMonitorOffset; //offset of debug monitor (dh.bin)? 6 | public uint DebugMonitorAddress; //addr(?) to load debug monitor? 7 | 8 | public uint DolOffset; //offset of main executable DOL (bootfile) 9 | public uint FSTableOffset; //offset of the FST ("fst.bin") 10 | public uint FSTableSize; 11 | public uint FSTableSizeMax; //maximum size of FST usually same as FSTSize 12 | public uint UserPosition; 13 | public uint UserLength; 14 | public uint Unknown; 15 | 16 | public BootBin(Stream source) : base(source) 17 | { } 18 | 19 | protected override void ReadData(Stream source) 20 | { 21 | GameName = source.ReadString(992); 22 | DebugMonitorOffset = source.ReadUInt32(Endian.Big); 23 | DebugMonitorAddress = source.ReadUInt32(Endian.Big); 24 | source.Skip(24); 25 | DolOffset = source.ReadUInt32(Endian.Big); //offset of main executable DOL (bootfile) 26 | FSTableOffset = source.ReadUInt32(Endian.Big); //offset of the FST ("fst.bin") 27 | FSTableSize = source.ReadUInt32(Endian.Big); 28 | FSTableSizeMax = source.ReadUInt32(Endian.Big); //maximum size of FST (usually same as FSTSize)* 29 | UserPosition = source.ReadUInt32(Endian.Big); 30 | UserLength = source.ReadUInt32(Endian.Big); 31 | Unknown = source.ReadUInt32(Endian.Big); 32 | source.Position += 4; 33 | if (DiskType == DiskTypes.Wii) 34 | { 35 | DolOffset <<= 2; 36 | FSTableOffset <<= 2; 37 | } 38 | } 39 | 40 | protected override void WriteData(Stream dest) 41 | { 42 | dest.WriteString(GameName, 992, 0); 43 | dest.Write(DebugMonitorOffset, Endian.Big); 44 | dest.Write(DebugMonitorAddress, Endian.Big); 45 | dest.Write(stackalloc byte[24]); 46 | dest.Write(DiskType == DiskTypes.GC ? DolOffset : DolOffset >> 2, Endian.Big); 47 | dest.Write(DiskType == DiskTypes.GC ? FSTableOffset : FSTableOffset >> 2, Endian.Big); 48 | dest.Write(FSTableSize, Endian.Big); 49 | dest.Write(FSTableSizeMax, Endian.Big); 50 | dest.Write(UserPosition, Endian.Big); 51 | dest.Write(UserLength, Endian.Big); 52 | dest.Write(Unknown, Endian.Big); 53 | dest.Write(0); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/RTDP.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Common.Node; 3 | using AuroraLib.Core.Interfaces; 4 | 5 | namespace AuroraLib.Archives.Formats 6 | { 7 | /// 8 | /// Imageepoch Arc Rise Archive 9 | /// 10 | public sealed class RTDP : ArchiveNode, IHasIdentifier 11 | { 12 | public override bool CanWrite => false; 13 | 14 | public IIdentifier Identifier => _identifier; 15 | 16 | private static readonly Identifier32 _identifier = new("RTDP"); 17 | 18 | public RTDP() 19 | { 20 | } 21 | 22 | public RTDP(string name) : base(name) 23 | { 24 | } 25 | 26 | public RTDP(FileNode source) : base(source) 27 | { 28 | } 29 | 30 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 31 | => stream.Match(_identifier); 32 | 33 | protected override void Deserialize(Stream source) 34 | { 35 | source.MatchThrow(_identifier); 36 | int EOH = (int)source.ReadUInt32(Endian.Big); 37 | int NrEntries = (int)source.ReadUInt32(Endian.Big); 38 | int Size = (int)source.ReadUInt32(Endian.Big); 39 | source.Position = 0x20; 40 | 41 | List Entries = new(NrEntries); 42 | for (int i = 0; i < NrEntries; i++) 43 | { 44 | Entries.Add(new RTDPEntry(source)); 45 | } 46 | 47 | foreach (var Entry in Entries) 48 | { 49 | SubStream subStream = new(source, Entry.DataSize, Entry.DataOffset + EOH); 50 | XORStream xORStream = new(subStream, 0x55); 51 | FileNode file = new(Entry.Name, xORStream); 52 | //If Duplicate... 53 | if (Contains(file)) 54 | file.Name = Path.GetFileName(Entry.Name) + Entries.IndexOf(Entry) + Path.GetExtension(Entry.Name); 55 | 56 | Add(file); 57 | } 58 | } 59 | 60 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 61 | 62 | private class RTDPEntry 63 | { 64 | public string Name { get; set; } 65 | public int DataSize { get; set; } 66 | public int DataOffset { get; set; } 67 | 68 | public RTDPEntry(Stream stream) 69 | { 70 | Name = stream.ReadString(32); 71 | DataSize = (int)stream.ReadUInt32(Endian.Big); 72 | DataOffset = (int)stream.ReadUInt32(Endian.Big); 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/AuroraLip/Common/XORStream.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Core.Buffers; 2 | 3 | namespace AuroraLib.Common 4 | { 5 | public class XORStream : Stream 6 | { 7 | public readonly Stream Base; 8 | public readonly byte[] Key; 9 | 10 | public XORStream(Stream stream, byte key) 11 | { 12 | Base = stream; 13 | Key = new byte[] { key }; 14 | } 15 | 16 | public XORStream(Stream stream, ReadOnlySpan key) 17 | { 18 | Base = stream; 19 | Key = new byte[key.Length * 2]; 20 | for (int i = 0; i < Key.Length; i++) 21 | { 22 | Key[i] = key[i % key.Length]; 23 | } 24 | } 25 | 26 | public override bool CanRead => Base.CanRead; 27 | public override bool CanSeek => Base.CanSeek; 28 | public override bool CanWrite => Base.CanWrite; 29 | public override long Length => Base.Length; 30 | public override long Position { get => Base.Position; set => Base.Position = value; } 31 | public override void Flush() => Base.Flush(); 32 | public override long Seek(long offset, SeekOrigin origin) => Base.Seek(offset, origin); 33 | public override void SetLength(long value) => Base.SetLength(value); 34 | public override int Read(byte[] buffer, int offset, int count) => Read(buffer.AsSpan(offset, count)); 35 | public override void Write(byte[] buffer, int offset, int count) => Write(buffer.AsSpan(offset, count)); 36 | 37 | public override int Read(Span buffer) 38 | { 39 | int r = Base.Read(buffer); 40 | if (Key.Length == 1) 41 | { 42 | buffer.DataXor(Key[0]); 43 | } 44 | else 45 | { 46 | int offset = (int)(Base.Position % (Key.Length / 2)); 47 | buffer.DataXor(Key.AsSpan(offset, (Key.Length / 2))); 48 | } 49 | return r; 50 | } 51 | 52 | public override void Write(ReadOnlySpan buffer) 53 | { 54 | using SpanBuffer rbuffer = new(buffer); 55 | if (Key.Length == 1) 56 | { 57 | rbuffer.Span.DataXor(Key[0]); 58 | } 59 | else 60 | { 61 | int offset = (int)(Base.Position % (Key.Length / 2)); 62 | rbuffer.Span.DataXor(Key.AsSpan(offset, (Key.Length / 2))); 63 | } 64 | Base.Write(rbuffer); 65 | } 66 | 67 | protected override void Dispose(bool disposing) 68 | { 69 | Base.Dispose(); 70 | base.Dispose(disposing); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/PAK_TM2.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Common.Node; 3 | using AuroraLib.Core.Buffers; 4 | 5 | namespace AuroraLib.Archives.Formats 6 | { 7 | /// 8 | /// Red Entertainment Tengai Makyō II Archive 9 | /// 10 | public sealed class PAK_TM2 : ArchiveNode 11 | { 12 | public override bool CanWrite => false; 13 | 14 | public PAK_TM2() 15 | { 16 | } 17 | 18 | public PAK_TM2(string name) : base(name) 19 | { 20 | } 21 | 22 | public PAK_TM2(FileNode source) : base(source) 23 | { 24 | } 25 | 26 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 27 | => Matcher(stream, extension); 28 | 29 | public static bool Matcher(Stream stream, ReadOnlySpan extension = default) 30 | { 31 | if ((extension.SequenceEqual(".pak") || extension.Length == 0 || extension.SequenceEqual(".cns")) && stream.Length > 0x20) 32 | { 33 | uint entryCount = stream.ReadUInt32(Endian.Big); 34 | if (entryCount != 0 && entryCount < 1024 && stream.Position + entryCount * 8 < stream.Length) 35 | { 36 | Entry[] entrys = stream.For((int)entryCount, s => s.Read(Endian.Big)); 37 | 38 | for (int i = 0; i < entryCount - 1; i++) 39 | { 40 | if (entrys[i].Offset + entrys[i].Size > entrys[i + 1].Offset) 41 | { 42 | return false; 43 | } 44 | } 45 | return entrys.First().Offset >= stream.Position && entrys.Last().Offset + entrys.Last().Size == stream.Length; 46 | } 47 | } 48 | return false; 49 | } 50 | 51 | protected override void Deserialize(Stream source) 52 | { 53 | uint entryCount = source.ReadUInt32(Endian.Big); 54 | using SpanBuffer entries = new SpanBuffer(entryCount); 55 | source.Read(entries, Endian.Big); 56 | 57 | for (int i = 0; i < entries.Length; i++) 58 | { 59 | FileNode file = new($"Entry_{i}", new SubStream(source, entries[i].Size, entries[i].Offset)); 60 | Add(file); 61 | } 62 | } 63 | 64 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 65 | 66 | private readonly struct Entry 67 | { 68 | public readonly uint Offset; 69 | public readonly uint Size; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/TSET.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common.Node; 2 | using AuroraLib.Core.Interfaces; 3 | using static AuroraLib.Texture.Formats.TXTRCC; 4 | 5 | namespace AuroraLib.Archives.Formats 6 | { 7 | /// 8 | /// Square Enix FFCC data Archive 9 | /// 10 | public sealed class TSET : ArchiveNode, IHasIdentifier 11 | { 12 | public override bool CanWrite => false; 13 | 14 | public IIdentifier Identifier => _identifier; 15 | 16 | private static readonly Identifier32 _identifier = new("TSET"); 17 | private static readonly Identifier32 _identifier2 = new("TEX "); 18 | private static readonly Identifier32 _identifier3 = new("OTM "); 19 | 20 | public TSET() 21 | { 22 | } 23 | 24 | public TSET(string name) : base(name) 25 | { 26 | } 27 | 28 | public TSET(FileNode source) : base(source) 29 | { 30 | } 31 | 32 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 33 | { 34 | CCPropertieNote root = stream.Read(Endian.Big); 35 | 36 | return (root.Identifier == _identifier || root.Identifier == _identifier2 || (root.Identifier == _identifier3 && stream.At(0x20, s => s.Match(_identifier)))) && root.ContentSize + 0x10 == stream.Length; 37 | } 38 | 39 | protected override void Deserialize(Stream source) 40 | { 41 | CCPropertieNote root = source.Read(Endian.Big); 42 | if (root.Identifier != _identifier) 43 | { 44 | source.Seek(0x20, SeekOrigin.Begin); 45 | root = source.Read(Endian.Big); 46 | } 47 | 48 | long contentSize = source.Position + root.ContentSize - 0x10; 49 | while (source.Position < contentSize) 50 | { 51 | long offset = source.Position; 52 | CCPropertieNote txtr = source.Read(Endian.Big); 53 | if (txtr.Identifier != 1381259348) 54 | { 55 | return; 56 | } 57 | 58 | CCPropertieNote propertie = source.Read(Endian.Big); 59 | string name = source.ReadString((int)propertie.ContentSize); 60 | 61 | FileNode file = new(name, new SubStream(source, txtr.ContentSize + 0x10, offset)); 62 | if (Contains(file)) 63 | file.Name += Count; 64 | Add(file); 65 | source.Seek(offset + txtr.ContentSize + 0x10, SeekOrigin.Begin); 66 | } 67 | } 68 | 69 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/ARC0.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Common.Node; 3 | using AuroraLib.Core.Interfaces; 4 | 5 | namespace AuroraLib.Archives.Formats 6 | { 7 | /// 8 | /// Brawl ARC0 Archive 9 | /// 10 | // ref https://github.com/soopercool101/BrawlCrate/blob/a0e5638c34bba0de783ece169d483ad7e7dcb016/BrawlLib/SSBB/ResourceNodes/Archives/ARCNode.cs 11 | public sealed class ARC0 : ArchiveNode, IHasIdentifier 12 | { 13 | public override bool CanWrite => false; 14 | 15 | public IIdentifier Identifier => _identifier; 16 | 17 | private static readonly Identifier32 _identifier = new((byte)'A', (byte)'R', (byte)'C', 0); 18 | 19 | public ARC0() 20 | { 21 | } 22 | 23 | public ARC0(string name) : base(name) 24 | { 25 | } 26 | 27 | public ARC0(FileNode source) : base(source) 28 | { 29 | } 30 | 31 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 32 | => stream.Match(_identifier) && stream.ReadByte() == 0; 33 | 34 | protected override void Deserialize(Stream source) 35 | { 36 | source.MatchThrow(_identifier); 37 | 38 | ushort version = source.ReadUInt16(Endian.Big); //257 39 | ushort files = source.ReadUInt16(Endian.Big); 40 | ulong unk = source.ReadUInt64(Endian.Big); 41 | string name = source.ReadString(48); 42 | 43 | for (int i = 0; i < files; i++) 44 | { 45 | EntryType type = (EntryType)source.ReadUInt16(Endian.Big); 46 | ushort index = source.ReadUInt16(Endian.Big); 47 | uint size = source.ReadUInt32(Endian.Big); 48 | byte groupIndex = (byte)source.ReadByte(); 49 | byte pad = (byte)source.ReadByte(); 50 | short redirectIndex = source.ReadInt16(Endian.Big); 51 | 52 | uint[] padding = new uint[5]; 53 | for (int p = 0; p < 5; p++) 54 | padding[p] = source.ReadUInt32(Endian.Big); 55 | Add(new FileNode($"{type}_{i}.pcs", new SubStream(source, size))); 56 | 57 | source.Align(size, SeekOrigin.Current, 32); 58 | } 59 | } 60 | 61 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 62 | 63 | public enum EntryType : short 64 | { 65 | None = 0x0, 66 | MiscData = 0x1, 67 | ModelData = 0x2, 68 | TextureData = 0x3, 69 | AnimationData = 0x4, 70 | SceneData = 0x5, 71 | Type6 = 0x6, 72 | GroupedArchive = 0x7, 73 | EffectData = 0x8 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/Hack.io/README.md: -------------------------------------------------------------------------------- 1 | # Hack.io 2 | **A Collection of File In/Out libraries for various formats**
3 | Previously referred to as "Hackio.IO" 4 | 5 | ## What is it? 6 | The Hack.io libraries are easy-to-use, modular C# libraries that can be used in multiple projects. I made these with the intent of just using them so I wouldn't have to constantly repeat my code when doing projects. 7 | In the end, here they are, feel free to use them in your projects. 8 | 9 | ## Library Listing 10 | 11 | - **Hack.io**
12 | Hack.io is the base library used by all the other Hack.io libraries. 13 | 14 | - **Hack.io.BCK**
15 | Bone Animations for J3D Models (SMG/SMG2, SMS, Pikmin, etc.) 16 | 17 | - **Hack.io.BMD**
18 | Library for J3D Models (SMG/SMG2, SMS, Pikmin, etc.)
19 | Heavily based on SuperBMD, but only does Read/Write to/from BMD/BDL. (no model importing and no model exporting) 20 | 21 | - **Hack.io.BTK**
22 | Texture Position Animations for J3D Models (SMG/SMG2, SMS, Pikmin, etc.) 23 | 24 | - **Hack.io.BRK**
25 | Colour Register Animations for J3D Models (SMG/SMG2, SMS, Pikmin, etc.) 26 | 27 | - **Hack.io.BTI**
28 | Library for the BTI image format. Supports all image formats and mipmaps 29 | 30 | - **Hack.io.BTP**
31 | Frame Animations for J3D Models (SMG/SMG2, SMS, etc.)
32 | This would animate a material by swapping out texture indicies 33 | 34 | - **Hack.io.BVA**
35 | Mesh Visibility Animations for J3D Models (SMG/SMG2, etc.) 36 | 37 | - **Hack.io.BPK**
38 | Palette Animations for J3D Models (SMG/SMG2, etc.) 39 | 40 | - **Hack.io.BCSV**
41 | Comma Seperated Values (SMG/SMG2) 42 | 43 | - **Hack.io.RARC**
44 | Revolution (Wii) Archives. (SMG/SMG2, SMS)
45 | **THESE ARE NOT U8 FORMATTED ARCHIVES!** 46 | 47 | - **Hack.IO.YAZ0**
48 | Library for Compressing/Decompressing data to/from YAZ0. Works with pretty much anything 49 | 50 | - **Hack.IO.YAY0**
51 | Library for Compressing/Decompressing data to/from YAY0. Works with pretty much anything 52 | 53 | ## How to use 54 | Download the Libraries you want and then reference them in your program's Assembly References.
55 | Using statements will also be needed. The statements are identical to the library name.
56 | Example:
57 | ```using Hack.io.RARC;```
58 | For Library specific tutorials, please visit it's corresponding Wiki Page. 59 | 60 | 61 | # Credits 62 | - Super Hackio - Main Programmer/Maintainer 63 | - RenolY2 aka Yoshi2 - Code reference for Hack.io.BTK & Hack.io.BRK 64 | - NoClip.website - Code reference for Hack.io.BCK, Hack.io.BTP, Hack.io.BVA & Hack.io.BPK 65 | - tarsa129 - Code reference for Hack.io.BCK because NoClip wasn't enough 66 | - Old SMG Researchers - File formats for Hack.io.CANM, Hack.io.BCSV & Hack.io.RARC 67 | - Gericom - Quick YAZ0 Compression 68 | - Daniel-McCarthy - YAY0 Compression 69 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/RKV2.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common.Node; 2 | using AuroraLib.Core.Interfaces; 3 | 4 | namespace AuroraLib.Archives.Formats 5 | { 6 | /// 7 | /// Krome Studios Star Wars Force Unleashed 8 | /// 9 | public sealed class RKV2 : ArchiveNode, IHasIdentifier 10 | { 11 | public override bool CanWrite => false; 12 | 13 | public IIdentifier Identifier => _identifier; 14 | 15 | private static readonly Identifier32 _identifier = new("RKV2"); 16 | 17 | public RKV2() 18 | { 19 | } 20 | 21 | public RKV2(string name) : base(name) 22 | { 23 | } 24 | 25 | public RKV2(FileNode source) : base(source) 26 | { 27 | } 28 | 29 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 30 | => stream.Match(_identifier); 31 | 32 | protected override void Deserialize(Stream source) 33 | { 34 | source.MatchThrow(_identifier); 35 | uint FileCount = source.ReadUInt32(Endian.Little); 36 | uint NameSize = source.ReadUInt32(Endian.Little); 37 | uint FullName_Files = source.ReadUInt32(Endian.Little); 38 | uint Dummy = source.ReadUInt32(Endian.Little); 39 | uint Info_Offset = source.ReadUInt32(Endian.Little); 40 | uint Dummy2 = source.ReadUInt32(Endian.Little); 41 | 42 | uint NameOffset = FileCount * 20 + Info_Offset; 43 | 44 | uint FullName_Offset = FileCount * 16 + (NameOffset + NameSize); 45 | 46 | source.Seek(Info_Offset, SeekOrigin.Begin); 47 | for (int i = 0; i < FileCount; i++) 48 | { 49 | uint NameOffsetForFile = (uint)source.ReadUInt32(Endian.Little); 50 | uint DummyForFile = (uint)source.ReadUInt32(Endian.Little); 51 | uint SizeForFile = (uint)source.ReadUInt32(Endian.Little); 52 | uint OffsetForFile = (uint)source.ReadUInt32(Endian.Little); 53 | uint CRCForFile = (uint)source.ReadUInt32(Endian.Little); 54 | long FilePosition = source.Position; 55 | 56 | source.Seek(NameOffsetForFile + NameOffset, SeekOrigin.Begin); 57 | string Name = source.ReadCString(); 58 | 59 | FileNode Sub = new(Name, new SubStream(source, SizeForFile, OffsetForFile)); 60 | //If Duplicate... 61 | if (Contains(Name)) 62 | Sub.Name += i; 63 | Add(Sub); 64 | 65 | // Read the file, move on to the next one 66 | source.Seek(FilePosition, SeekOrigin.Begin); 67 | } 68 | } 69 | 70 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Formats/text.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Core.Interfaces; 3 | 4 | namespace AuroraLib.Texture.Formats 5 | { 6 | public class text_AQ : JUTTexture, IHasIdentifier, IFileAccess 7 | { 8 | public virtual bool CanRead => true; 9 | 10 | public virtual bool CanWrite => false; 11 | 12 | public virtual IIdentifier Identifier => _identifier; 13 | 14 | private static readonly byte[] _bytes = new byte[] { 0x63, 0x68, 0x6E, 0x6B, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x77, 0x69, 0x69, 0x20, 0x74, 0x65, 0x78, 0x74 }; 15 | 16 | private static readonly Identifier _identifier = new(_bytes); 17 | 18 | //public static readonly Identifier32 Platform = new(_identifierbyte.AsSpan().Slice(12, 4)); // Wii 19 | //public static readonly Identifier32 Type = new(_identifierbyte.AsSpan().Slice(16, 4)); // text 20 | 21 | public bool IsMatch(Stream stream, ReadOnlySpan extension = default) 22 | => Matcher(stream, extension); 23 | 24 | public static bool Matcher(Stream stream, ReadOnlySpan extension = default) 25 | => stream.Length > 0x40 && stream.Match(_identifier); 26 | 27 | protected override void Read(Stream stream) 28 | { 29 | stream.Seek(0x10, SeekOrigin.Current); 30 | Header header = stream.Read
(Endian.Big); 31 | 32 | Add(new TexEntry(stream, null, header.Format, GXPaletteFormat.IA8, 0, (int)header.Width, (int)header.Height, (int)header.Images - 1) 33 | { 34 | LODBias = 0, 35 | MagnificationFilter = GXFilterMode.Nearest, 36 | MinificationFilter = GXFilterMode.Nearest, 37 | WrapS = GXWrapMode.CLAMP, 38 | WrapT = GXWrapMode.CLAMP, 39 | EnableEdgeLOD = false, 40 | MinLOD = 0, 41 | MaxLOD = header.Images - 1 42 | }); 43 | } 44 | 45 | protected override void Write(Stream stream) => throw new NotImplementedException(); 46 | 47 | private struct Header 48 | { 49 | public uint Magic; // "text" 1952807028 50 | public uint pad; // 0x0 51 | public uint HeaderPos; // 0x10 52 | public uint Size; 53 | 54 | private uint format; 55 | public uint Width; 56 | public uint Height; 57 | public uint Images; 58 | 59 | public uint Unk3; // 56 60 | public uint HeadeSize; // 64 61 | public uint Unk5; // 13421772 62 | public uint Unk6; // 3435973836 63 | 64 | public GXImageFormat Format 65 | { 66 | get => (GXImageFormat)format; 67 | set => format = (uint)value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/AuroraLip/Archives/Formats/FTEX.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common.Node; 2 | using AuroraLib.Core.Interfaces; 3 | 4 | namespace AuroraLib.Archives.Formats 5 | { 6 | /// 7 | /// Vanillaware Muramasa Texture Archive 8 | /// 9 | public sealed class FTEX : ArchiveNode, IHasIdentifier 10 | { 11 | public override bool CanWrite => false; 12 | 13 | public IIdentifier Identifier => _identifier; 14 | 15 | private static readonly Identifier32 _identifier = new("FTEX"); 16 | 17 | public FTEX() 18 | { 19 | } 20 | 21 | public FTEX(string name) : base(name) 22 | { 23 | } 24 | 25 | public FTEX(FileNode source) : base(source) 26 | { 27 | } 28 | 29 | public override bool IsMatch(Stream stream, ReadOnlySpan extension = default) 30 | => stream.Match(_identifier); 31 | 32 | protected override void Deserialize(Stream source) 33 | { 34 | Header header = source.Read
(); 35 | source.Seek(0x10, SeekOrigin.Current); 36 | NameEntry[] names = source.For((int)header.Entrys, s => new NameEntry(s)); 37 | 38 | source.Seek(header.Offset, SeekOrigin.Begin); 39 | for (int i = 0; i < header.Entrys; i++) 40 | { 41 | FTX0 Entry = source.Read(); 42 | source.Seek(Entry.Offset - 0x10, SeekOrigin.Current); 43 | FileNode file = new(names[i].Name, new SubStream(source, Entry.Size)); 44 | Add(file); 45 | source.Seek(Entry.Size, SeekOrigin.Current); 46 | } 47 | } 48 | 49 | protected override void Serialize(Stream dest) => throw new NotImplementedException(); 50 | 51 | private struct Header 52 | { 53 | public uint Magic; // FTEX 1480938566 54 | public uint Size; 55 | public uint Offset; 56 | public uint Entrys; 57 | 58 | public readonly uint FullSize => Size + Offset + 0x10; 59 | } 60 | 61 | private struct NameEntry 62 | { 63 | public string Name; 64 | public Propertie Properties; 65 | 66 | public NameEntry(Stream stream) 67 | { 68 | Name = stream.ReadString(0x20); 69 | Properties = stream.Read(); 70 | } 71 | 72 | public struct Propertie 73 | { 74 | public uint Padding; 75 | public uint unk1; 76 | public uint unk2; 77 | public uint unk3; 78 | } 79 | } 80 | 81 | private struct FTX0 82 | { 83 | public uint Magic; // FTX0 811095110 84 | public uint Size; 85 | public uint Offset; 86 | public uint Padding; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Enums/UImageFormats.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.Texture 2 | { 3 | public enum AImageFormats : uint 4 | { 5 | /// 6 | GXI4 = 0x00, 7 | /// 8 | GXI8 = 0x01, 9 | /// 10 | GXIA4 = 0x02, 11 | /// 12 | GXIA8 = 0x03, 13 | /// 14 | GXRGB565 = 0x04, 15 | /// 16 | GXRGB5A3 = 0x05, 17 | /// 18 | GXRGBA32 = 0x06, 19 | /// 20 | GXC4 = 0x08, 21 | /// 22 | GXC8 = 0x09, 23 | /// 24 | GXC14X2 = 0x0A, 25 | /// 26 | CMPR = 0x0E, 27 | 28 | /// 29 | /// 4 bit intensity values. 30 | /// 31 | I4 = 0xC0000000, 32 | /// 33 | /// 8 bit intensity values. 34 | /// 35 | I8 = 0xC0000001, 36 | /// 37 | /// 4 bit intensity values, along with a 8 bit alpha channel 38 | /// 39 | IA4 = 0xC0000002, 40 | /// 41 | /// 8 bit intensity values, along with a 8 bit alpha channel. 42 | /// 43 | IA8 = 0xC0000003, 44 | /// 45 | /// 16 bit color values without alpha. The alpha component is set to 0xff. 46 | /// 47 | RGB565 = 0xC0000004, 48 | /// 49 | /// Either 15 bit color values without alpha, or 12 bit color values with a 3 bit alpha channel. 50 | /// 51 | RGB5A3 = 0xC0000005, 52 | /// 53 | /// 24 bit depth true color, with an 8 bit alpha channel. 54 | /// 55 | RGBA32 = 0xC0000006, 56 | /// 57 | /// Is a 4 bit palette format. Supports up to 16 different colors. 58 | /// 59 | C4 = 0xC0000008, 60 | /// 61 | /// Is an 8 bit palette format. Supports up to 256 different colors. 62 | /// 63 | C8 = 0xC0000009, 64 | /// 65 | /// Is a 14 bit palette format. Supports up to 16384 different colors. 66 | /// 67 | C14X2 = 0xC000000A, 68 | /// 69 | /// Compressed image format. 70 | /// 71 | DXT1 = 0xC000000E, 72 | 73 | /// 74 | /// 24 bit depth true color, with an 8 bit alpha channel. The PS2 uses 128 as maximum alpha value. 75 | /// 76 | PS2RGBA32 = 0xC1000006, 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Formats/PIM.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Texture.PixelFormats; 3 | using System.Runtime.InteropServices; 4 | using SixLabors.ImageSharp.PixelFormats; 5 | 6 | namespace AuroraLib.Texture 7 | { 8 | public class PIM : JUTTexture, IFileAccess 9 | { 10 | public bool CanRead => true; 11 | 12 | public bool CanWrite => false; 13 | 14 | public const string Extension = ".PIM"; 15 | 16 | public bool IsMatch(Stream stream, ReadOnlySpan extension = default) 17 | => extension.SequenceEqual(Extension); 18 | 19 | protected override void Read(Stream stream) 20 | { 21 | PIMHeader header = stream.Read(); 22 | stream.Seek(header.POffset, SeekOrigin.Begin); 23 | Span palette = stackalloc byte[header.Colors * 4]; 24 | stream.Read(palette); 25 | stream.Seek(header.IOffset, SeekOrigin.Begin); 26 | 27 | //The game reduces the colors from RGBA32 to RGB5A3 in real time. 28 | Span paletteRGBA = MemoryMarshal.Cast(palette); 29 | Span paletteRGB5A3 = MemoryMarshal.Cast(palette); 30 | for (int c = 0; c < header.Colors; c++) 31 | { 32 | //PS2 Alpha channel must be normalized. 33 | paletteRGBA[c].A = (byte)Math.Min(paletteRGBA[c].A * 2, 255); 34 | paletteRGB5A3[c].FromRgba32(paletteRGBA[c]); 35 | paletteRGB5A3[c].PackedValue = BitConverterX.Swap(paletteRGB5A3[c].PackedValue); 36 | } 37 | 38 | AImageFormats format = header.BPP switch 39 | { 40 | 4 => AImageFormats.C4, 41 | 8 => AImageFormats.C8, 42 | 32 => AImageFormats.PS2RGBA32, 43 | _ => throw new NotImplementedException(), 44 | }; 45 | 46 | TexEntry tex = new(stream, format, header.Width, header.Height) 47 | { 48 | LODBias = 0, 49 | MagnificationFilter = GXFilterMode.Nearest, 50 | MinificationFilter = GXFilterMode.Nearest, 51 | WrapS = GXWrapMode.CLAMP, 52 | WrapT = GXWrapMode.CLAMP, 53 | EnableEdgeLOD = false, 54 | MinLOD = 0, 55 | MaxLOD = 0, 56 | PaletteFormat = GXPaletteFormat.RGB5A3 57 | }; 58 | tex.Palettes.Add(palette[..(header.Colors * 2)].ToArray()); 59 | Add(tex); 60 | } 61 | 62 | protected override void Write(Stream stream) => throw new NotImplementedException(); 63 | 64 | private struct PIMHeader 65 | { 66 | public ushort Width; 67 | public ushort Height; 68 | public ushort BPP; 69 | public ushort Colors; 70 | public uint POffset; 71 | public uint IOffset; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Formats/TXTR.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | 3 | namespace AuroraLib.Texture.Formats 4 | { 5 | public class TXTR : JUTTexture, IFileAccess 6 | { 7 | public bool CanRead => true; 8 | 9 | public bool CanWrite => false; 10 | 11 | public const string Extension = ".txtr"; 12 | 13 | public TXTR() 14 | { } 15 | 16 | public TXTR(Stream stream) : base(stream) 17 | { 18 | } 19 | 20 | public TXTR(string filepath) : base(filepath) 21 | { 22 | } 23 | 24 | public bool IsMatch(Stream stream, ReadOnlySpan extension = default) 25 | => extension.Contains(Extension, StringComparison.InvariantCultureIgnoreCase); 26 | 27 | protected override void Read(Stream stream) 28 | { 29 | TXTRImageFormat TXTRFormat = (TXTRImageFormat)stream.ReadUInt32(Endian.Big); 30 | GXImageFormat Format = (GXImageFormat)Enum.Parse(typeof(GXImageFormat), TXTRFormat.ToString()); 31 | int ImageWidth = stream.ReadUInt16(Endian.Big); 32 | int ImageHeight = stream.ReadUInt16(Endian.Big); 33 | uint Images = stream.ReadUInt32(Endian.Big); 34 | 35 | Span palettedata = Span.Empty; 36 | int ColorsCount = 0; 37 | GXPaletteFormat PaletteFormat = GXPaletteFormat.IA8; 38 | if (Format.IsPaletteFormat()) 39 | { 40 | PaletteFormat = (GXPaletteFormat)stream.ReadUInt32(Endian.Big); 41 | int CWidth = stream.ReadUInt16(Endian.Big); 42 | int CHeight = stream.ReadUInt16(Endian.Big); 43 | ColorsCount = CHeight * CWidth; 44 | palettedata = new byte[ColorsCount * 2]; 45 | stream.Read(palettedata); 46 | } 47 | 48 | TexEntry current = new TexEntry(stream, palettedata, Format, PaletteFormat, ColorsCount, ImageWidth, ImageHeight, (int)Images - 1) 49 | { 50 | LODBias = 0, 51 | MagnificationFilter = GXFilterMode.Nearest, 52 | MinificationFilter = GXFilterMode.Nearest, 53 | WrapS = GXWrapMode.CLAMP, 54 | WrapT = GXWrapMode.CLAMP, 55 | EnableEdgeLOD = false, 56 | MinLOD = 0, 57 | MaxLOD = Images - 1 58 | }; 59 | Add(current); 60 | } 61 | 62 | protected override void Write(Stream stream) 63 | { 64 | throw new NotImplementedException(); 65 | } 66 | 67 | public enum TXTRImageFormat : uint 68 | { 69 | I4 = 0x00, 70 | I8 = 0x01, 71 | IA4 = 0x02, 72 | IA8 = 0x03, 73 | C4 = 0x04, 74 | C8 = 0x05, 75 | C14X2 = 0x06, 76 | RGB565 = 0x07, 77 | RGB5A3 = 0x08, 78 | RGBA32 = 0x09, //RGBA8? 79 | CMPR = 0x0A, 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Interfaces/IGXTextureInfo.cs: -------------------------------------------------------------------------------- 1 | namespace AuroraLib.Texture.Interfaces 2 | { 3 | /// 4 | /// Defines the properties of a texture used by the GX API in the GameCube and Wii consoles. 5 | /// 6 | public interface IGXTextureInfo : IImageInfo 7 | { 8 | /// 9 | /// The image format of the texture. 10 | /// specifies how the data within the image is encoded. 11 | /// 12 | GXImageFormat Format { get; } 13 | 14 | /// 15 | /// The palette format of the texture. 16 | /// Only C4, C8, and C14X2 use palettes. 17 | /// 18 | GXPaletteFormat PaletteFormat { get; } 19 | 20 | /// 21 | /// The wrap mode for the S coordinate. 22 | /// Specifies how textures outside the vertical range [0..1] are treated for text coordinates. 23 | /// 24 | GXWrapMode WrapS { get; set; } 25 | 26 | /// 27 | /// The wrap mode for the T coordinate. 28 | /// Specifies how textures outside the horizontal range [0..1] are treated for text coordinates. 29 | /// 30 | GXWrapMode WrapT { get; set; } 31 | 32 | /// 33 | /// The magnification filter mode for the texture. 34 | /// specifies what type of filtering the file should use as magnification filter. 35 | /// 36 | GXFilterMode MagnificationFilter { get; set; } 37 | 38 | /// 39 | /// The minification filter mode for the texture. 40 | /// specifies what type of filtering the file should use as minification filter. 41 | /// 42 | GXFilterMode MinificationFilter { get; set; } 43 | 44 | /// 45 | /// The minimum level-of-detail for the texture. 46 | /// Exclude textures below a certain LOD level from being used. 47 | /// 48 | float MinLOD { get; set; } 49 | 50 | /// 51 | /// The maximum level-of-detail for the texture. 52 | /// Exclude textures above a certain LOD level from being used. 53 | /// A value larger than the actual textures should lead to culling. 54 | /// 55 | float MaxLOD { get; set; } 56 | 57 | /// 58 | /// The level-of-detail bias for the texture. 59 | /// A larger value leads to a larger camera distance before a lower LOD resolution is selected. 60 | /// 61 | float LODBias { get; set; } 62 | 63 | /// 64 | /// Indicating whether to enable edge level of detail (LOD) on the texture. 65 | /// When enabled, the LOD level is adjusted on the viewer distance to the texture's edges, resulting in smoother transitions. 66 | /// 67 | bool EnableEdgeLOD { get; set; } 68 | 69 | /// 70 | /// The number of mipmaps for the texture. 71 | /// 72 | int MipMaps { get; } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Formats/PIL.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Texture.PixelFormats; 3 | using System.Runtime.InteropServices; 4 | using SixLabors.ImageSharp.PixelFormats; 5 | 6 | namespace AuroraLib.Texture.Formats 7 | { 8 | public class PIL : JUTTexture, IFileAccess 9 | { 10 | public bool CanRead => true; 11 | 12 | public bool CanWrite => false; 13 | 14 | public const string Extension = ".PIL"; 15 | 16 | public bool IsMatch(Stream stream, ReadOnlySpan extension = default) => extension.SequenceEqual(Extension); 17 | 18 | protected override void Read(Stream stream) 19 | { 20 | uint images = stream.Read(); 21 | stream.Seek(0x10, SeekOrigin.Begin); 22 | Span buffer = stackalloc byte[0x400]; 23 | for (int i = 0; i < images; i++) 24 | { 25 | ushort width = stream.Read(); 26 | ushort height = stream.Read(); 27 | ushort BPP = stream.Read(); 28 | ushort colors = stream.Read(); 29 | string name = stream.ReadString(0x18); 30 | Span palette = buffer[..(colors * 4)]; 31 | stream.Read(palette); 32 | 33 | //The game reduces the colors from RGBA32 to RGB5A3 in real time. 34 | Span paletteRGBA = MemoryMarshal.Cast(palette); 35 | Span paletteRGB5A3 = MemoryMarshal.Cast(palette); 36 | for (int c = 0; c < colors; c++) 37 | { 38 | //PS2 Alpha channel must be normalized 39 | paletteRGBA[c].A = (byte)Math.Min(paletteRGBA[c].A * 2, 255); 40 | paletteRGB5A3[c].FromRgba32(paletteRGBA[c]); 41 | paletteRGB5A3[c].PackedValue = BitConverterX.Swap(paletteRGB5A3[c].PackedValue); 42 | } 43 | 44 | AImageFormats format = BPP switch 45 | { 46 | 4 => AImageFormats.C4, 47 | 8 => AImageFormats.C8, 48 | 32 => AImageFormats.PS2RGBA32, 49 | _ => throw new NotImplementedException(), 50 | }; 51 | 52 | TexEntry tex = new(stream, format, width, height) 53 | { 54 | LODBias = 0, 55 | MagnificationFilter = GXFilterMode.Nearest, 56 | MinificationFilter = GXFilterMode.Nearest, 57 | WrapS = GXWrapMode.CLAMP, 58 | WrapT = GXWrapMode.CLAMP, 59 | EnableEdgeLOD = false, 60 | MinLOD = 0, 61 | MaxLOD = 0, 62 | PaletteFormat = GXPaletteFormat.RGB5A3 63 | }; 64 | tex.Palettes.Add(palette[..(colors * 2)].ToArray()); 65 | Add(tex); 66 | } 67 | } 68 | protected override void Write(Stream stream) => throw new NotImplementedException(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/AuroraLip/Texture/Formats/TDL0.cs: -------------------------------------------------------------------------------- 1 | using AuroraLib.Common; 2 | using AuroraLib.Core.Buffers; 3 | using AuroraLib.Core.Interfaces; 4 | 5 | namespace AuroraLib.Texture.Formats 6 | { 7 | public class TDL0 : JUTTexture, IFileAccess, IHasIdentifier 8 | { 9 | public bool CanRead => true; 10 | 11 | public bool CanWrite => false; 12 | 13 | public virtual IIdentifier Identifier => Magic; 14 | 15 | public static readonly Identifier32 Magic = new("TDL0"); 16 | 17 | public bool IsMatch(Stream stream, ReadOnlySpan extension = default) 18 | => stream.Length > 0x40 && stream.Match(Magic); 19 | 20 | protected override void Read(Stream stream) 21 | { 22 | Header header = stream.Read
(Endian.Big); 23 | using SpanBuffer palette = new(header.PaletteSize * header.TextureCount); 24 | if (header.Format.IsPaletteFormat()) 25 | stream.At(header.PaletteStart, s => s.Read(palette)); 26 | 27 | for (int i = 0; i < header.TextureCount; i++) 28 | { 29 | TextureHeader texture = stream.Read(Endian.Big); 30 | 31 | stream.At(header.DataStart, s => 32 | { 33 | TexEntry current = new(stream, palette.Slice(header.PaletteSize * i, header.PaletteSize), header.Format, GXPaletteFormat.RGB5A3, header.PaletteSize / 2, header.TotalWidth, header.TotalHeight, header.MipmapCount) 34 | { 35 | MinLOD = 0, 36 | MaxLOD = header.MipmapCount 37 | }; 38 | Add(current); 39 | }); 40 | } 41 | } 42 | 43 | protected override void Write(Stream stream) => throw new NotImplementedException(); 44 | 45 | private struct Header 46 | { 47 | public readonly Identifier32 Magic; 48 | public readonly int unk; 49 | public readonly ushort TotalWidth; 50 | public readonly ushort TotalHeight; 51 | public readonly ushort TextureCount; 52 | public readonly ushort MipmapCount; 53 | private readonly byte format; 54 | public readonly byte unk02; 55 | public readonly ushort PaletteSize; 56 | public readonly uint DataStart; 57 | public readonly uint PaletteStart; 58 | 59 | public readonly GXImageFormat Format => format switch 60 | { 61 | 4 => GXImageFormat.C4, 62 | 5 => GXImageFormat.C8, 63 | 8 => GXImageFormat.RGB5A3, 64 | 10 => GXImageFormat.CMPR, 65 | _ => throw new NotImplementedException(), 66 | }; 67 | } 68 | 69 | private struct TextureHeader 70 | { 71 | public readonly int ID; 72 | public readonly ushort Width; 73 | public readonly ushort Height; 74 | public readonly ushort OffsetWidth; 75 | public readonly ushort OffsetHeight; 76 | } 77 | } 78 | } 79 | --------------------------------------------------------------------------------