Esempio n. 1
0
        public static int HandleRead(Options.ReadOptions options)
        {
            progLogger.Level = Logger.Levels.INFO;
            progLogger.Flag  = Logger.Flags.DEBUG | Logger.Flags.DEBUGCONSOLE | Logger.Flags.CLICONSOLE;
            if (options.CliColors)
            {
                progLogger.Flag = Logger.Flags.CLICOLORS;
            }
            if (options.LogFile)
            {
                progLogger.Flag = Logger.Flags.LOGFILE;
                progLogger.InitializeLogFile(Path.Combine(Environment.CurrentDirectory, $"{AssemblyInfo.AssemblyTitle}.log"));
            }

            // Input validation
            if (!ValidateInput(options.Input, isCreate: false, out _, out string inFileName, out _))
            {
                return(1);
            }

            // TXTR is big endian with ASCII
            using (FileStream readerStream = new FileStream(options.Input, FileMode.Open, FileAccess.Read, FileShare.None))
                using (EndianBinaryReader reader = new EndianBinaryReader(readerStream, isLittleEndian: false, Encoding.ASCII, leaveOpen: false))
                {
                    TXTR TXTR = new TXTR();
                    progLogger.Info($"TXTR {inFileName}");
                    progLogger.Info("==================");

                    TXTR.texFormat = reader.ReadUInt32();
                    AConverter texConverter = AConverter.Get((GX.TextureFormat)TXTR.texFormat);
                    texConverter.TexFormat = (GX.TextureFormat)TXTR.texFormat;
                    progLogger.Info($"Texture Format: {(GX.TextureFormat)TXTR.texFormat}");

                    TXTR.width         = reader.ReadUInt16();
                    texConverter.Width = TXTR.width;
                    progLogger.Info($"Width: {TXTR.width}");
                    TXTR.height         = reader.ReadUInt16();
                    texConverter.Height = TXTR.height;
                    progLogger.Info($"Height: {TXTR.height}");

                    TXTR.mipCount = reader.ReadUInt32();
                    progLogger.Info($"Mipmap Count: {TXTR.mipCount}");

                    if (texConverter.HasPalette())
                    {
                        TXTR.palFormat         = reader.ReadUInt32();
                        texConverter.PalFormat = (GX.PaletteFormat)TXTR.palFormat;
                        progLogger.Info($"Palette Format: {(GX.PaletteFormat)TXTR.palFormat}");

                        TXTR.palWidth         = reader.ReadUInt16();
                        texConverter.PalWidth = TXTR.palWidth;
                        progLogger.Info($"Palette Width: {TXTR.palWidth}");

                        TXTR.palHeight         = reader.ReadUInt16();
                        texConverter.PalHeight = TXTR.palHeight;
                        progLogger.Info($"Palette Height: {TXTR.palHeight}");
                        TXTR.palData = texConverter.FromPalette(reader.ReadBytes(texConverter.GetPaletteSize()));
                        progLogger.Info($"Palette Data: uint[{TXTR.palData.Length}]");
                    }

                    TXTR.texData = reader.ReadAllBytes();
                    progLogger.Info($"Texture Data: byte[{TXTR.texData.Length}]");
                }

            progLogger.InfoPrefix(NewLine, "Done.");
            return(0);
        }
Esempio n. 2
0
        public static int HandleExtract(Options.ExtractOptions options)
        {
            progLogger.Level = (options.Verbose) ? Logger.Levels.VERBOSE : (options.Silent) ? Logger.Levels.ERROR : Logger.Levels.INFO;
            progLogger.Flag  = Logger.Flags.DEBUG | Logger.Flags.DEBUGCONSOLE | Logger.Flags.CLICONSOLE;
            if (options.CliColors)
            {
                progLogger.Flag = Logger.Flags.CLICOLORS;
            }
            if (options.LogFile)
            {
                progLogger.Flag = Logger.Flags.LOGFILE;
                progLogger.InitializeLogFile(Path.Combine(Environment.CurrentDirectory, $"{AssemblyInfo.AssemblyTitle}.log"));
            }

            // Input validation
            if (!ValidateInput(options.Input, isCreate: false, out string inDir, out string inFileName, out string inFileExt))
            {
                return(1);
            }
            // Output validation
            if (!ValidateOutput(options.Output, isCreate: false, out _, out _, out _))
            {
                return(1);
            }

            // TXTR is big endian with ASCII
            using (FileStream readerStream = new FileStream(options.Input, FileMode.Open, FileAccess.Read, FileShare.None))
                using (EndianBinaryReader reader = new EndianBinaryReader(readerStream, isLittleEndian: false, Encoding.ASCII, leaveOpen: false))
                {
                    TXTR TXTR = new TXTR();
                    progLogger.Verbose($"TXTR {inFileName}");
                    progLogger.Verbose("==================");

                    TXTR.texFormat = reader.ReadUInt32();
                    AConverter texConverter = AConverter.Get((GX.TextureFormat)TXTR.texFormat);
                    texConverter.TexFormat = (GX.TextureFormat)TXTR.texFormat;
                    progLogger.Verbose($"Texture Format: {(GX.TextureFormat)TXTR.texFormat}");

                    TXTR.width         = reader.ReadUInt16();
                    texConverter.Width = TXTR.width;
                    progLogger.Verbose($"Width: {TXTR.width}");

                    TXTR.height         = reader.ReadUInt16();
                    texConverter.Height = TXTR.height;
                    progLogger.Verbose($"Height: {TXTR.height}");

                    if (TXTR.width < AConverter.SizeLimit || TXTR.height < AConverter.SizeLimit)
                    {
                        progLogger.Error($"Invalid dimensions: width='{TXTR.width}', height='{TXTR.height}'");
                        return(1);
                    }

                    TXTR.mipCount = reader.ReadUInt32();
                    progLogger.Verbose($"Mipmap Count: {TXTR.mipCount}");
                    if (TXTR.mipCount < 1 || TXTR.mipCount > 255)
                    {
                        progLogger.Error($"Invalid mipmap count: '{TXTR.mipCount}'");
                        return(1);
                    }

                    if (texConverter.HasPalette())
                    {
                        TXTR.palFormat         = reader.ReadUInt32();
                        texConverter.PalFormat = (GX.PaletteFormat)TXTR.palFormat;
                        progLogger.Verbose($"Palette Format: {(GX.PaletteFormat)TXTR.palFormat}");

                        TXTR.palWidth         = reader.ReadUInt16();
                        texConverter.PalWidth = TXTR.palWidth;
                        progLogger.Verbose($"Palette Width: {TXTR.palWidth}");

                        TXTR.palHeight         = reader.ReadUInt16();
                        texConverter.PalHeight = TXTR.palHeight;
                        progLogger.Verbose($"Palette Height: {TXTR.palHeight}");

                        TXTR.palData = texConverter.FromPalette(reader.ReadBytes(texConverter.GetPaletteSize()));
                        progLogger.Verbose($"Palette Data: uint[{TXTR.palData.Length}]");
                    }

                    TXTR.texData = reader.ReadAllBytes();
                    if (TXTR.texData.Length == 0)
                    {
                        progLogger.Error("Texture data is empty");
                        return(1);
                    }
                    progLogger.Verbose($"Texture Data: byte[{TXTR.texData.Length}]");

                    byte[] mipData = null;
                    ushort mipwidth = TXTR.width, mipheight = TXTR.height;
                    int    mipOffs = 0;
                    string mipFile = "";
                    for (int m = 0; m < TXTR.mipCount; m++)
                    {
                        if (!options.Mipmaps && m > 0)
                        {
                            break;
                        }

                        mipData = new byte[TXTR.texData.Length - mipOffs];
                        if (mipData.Length <= 0)
                        {
                            progLogger.Error($"Mipmap data is empty for mipmap {m+1}");
                            return(1);
                        }
                        else
                        {
                            Array.Copy(TXTR.texData, mipOffs, mipData, 0, mipData.Length);
                        }

                        progLogger.VerbosePrefix(NewLine, $"Mipmap: {m+1}");
                        progLogger.Verbose($"Mipmap Width: {mipwidth}");
                        progLogger.Verbose($"Mipmap Height: {mipheight}");
                        progLogger.Verbose($"Mipmap Offset: {mipOffs}");

                        switch ((GX.TextureFormat)TXTR.texFormat)
                        {
                        case GX.TextureFormat.I4:
                            mipData = ((I4)texConverter).From(mipData);
                            break;

                        case GX.TextureFormat.I8:
                            mipData = ((I8)texConverter).From(mipData);
                            break;

                        case GX.TextureFormat.IA4:
                            mipData = ((IA4)texConverter).From(mipData);
                            break;

                        case GX.TextureFormat.IA8:
                            mipData = ((IA8)texConverter).From(mipData);
                            break;

                        case GX.TextureFormat.C4:
                            mipData = ((C4)texConverter).FromWithPalette(mipData, TXTR.palData);
                            break;

                        case GX.TextureFormat.C8:
                            mipData = ((C8)texConverter).FromWithPalette(mipData, TXTR.palData);
                            break;

                        case GX.TextureFormat.C14X2:
                            mipData = ((C14X2)texConverter).FromWithPalette(mipData, TXTR.palData);
                            break;

                        case GX.TextureFormat.RGB565:
                            mipData = ((RGB565)texConverter).From(mipData);
                            break;

                        case GX.TextureFormat.RGB5A3:
                            mipData = ((RGB5A3)texConverter).From(mipData);
                            break;

                        case GX.TextureFormat.RGBA32:
                            mipData = ((RGBA32)texConverter).From(mipData);
                            break;

                        case GX.TextureFormat.CMPR:
                            mipData = ((CMPR)texConverter).From(mipData);
                            break;

                        default:
                            progLogger.Error($"TXTR format '{(GX.TextureFormat)TXTR.texFormat}' ('0x{TXTR.texFormat:X}') is not supported for extracting.");
                            return(1);
                        }

                        if (mipData.Length != 0)
                        {
                            progLogger.Verbose($"Mipmap Data: byte[{mipData.Length}]");

                            mipFile = $"{options.Output}{Path.DirectorySeparatorChar}{inFileName}_{m+1}.png";
                            if (ValidateOutput(mipFile, isCreate: true, out _, out _, out _))
                            {
                                progLogger.Info($"Saving mipmap {m + 1} to '{mipFile}'");
                                using (Bitmap bmp = texConverter.ToBitmap(mipData))
                                {
                                    bmp.Save(mipFile, ImageFormat.Png);
                                }
                            }
                        }
                        else
                        {
                            progLogger.Error($"Mipmap data is empty for mipmap {m+1}");
                            return(1);
                        }

                        // Mipmap levels are aligned to 32B.
                        mipOffs            += Math.Max(texConverter.GetMipmapSize(), 32);
                        mipwidth           /= 2;
                        mipheight          /= 2;
                        texConverter.Width  = mipwidth;
                        texConverter.Height = mipheight;

                        // It seems like anything below 4x4 has junk data
                        if (mipwidth < AConverter.SizeLimit || mipheight < AConverter.SizeLimit)
                        {
                            break;
                        }
                    }
                }

            progLogger.InfoPrefix(NewLine, "Done.");
            return(0);
        }