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); }
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); }