Created
August 11, 2022 13:49
-
-
Save draqoon/bb61b2e25ba5e4351afc79b48b79b129 to your computer and use it in GitHub Desktop.
Tools for Extracting Terraria 1.4 Tmod.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.IO.Compression; | |
using System.Text; | |
using System.Security.Cryptography; | |
using Microsoft.Xna.Framework.Graphics; | |
class Program { | |
static void Main( string[] _ ) => new Program().MainProc(); | |
private void MainProc() { | |
var outRootDirectory = @"C:\Temp"; | |
var tmodFiles = new string[] { | |
@"C:\Program Files (x86)\Steam\steamapps\workshop\content\1281930\2824688072\2022.6\CalamityMod.tmod", | |
//@"C:\Program Files (x86)\Steam\steamapps\workshop\content\1281930\2824688266\2022.6\CalamityModMusic.tmod", | |
//@"C:\Program Files (x86)\Steam\steamapps\workshop\content\1281930\2563309347\2022.7\MagicStorage.tmod", | |
//@"C:\Program Files (x86)\Steam\steamapps\workshop\content\1281930\2563851005\2022.6\WMITF.tmod", | |
//@"C:\Program Files (x86)\Steam\steamapps\workshop\content\1281930\2565540604\2022.7\AutoTrash.tmod", | |
}; | |
foreach( var tmodFile in tmodFiles ) { | |
this.ExtractTmod( tmodFile, outRootDirectory ); | |
} | |
} | |
private void ExtractTmod( string tmodFile, string outRootDirectory ) { | |
using var stream = File.OpenRead( tmodFile ); | |
using var reader = new BinaryReader( stream ); | |
if( Encoding.ASCII.GetString( reader.ReadBytes( 4 ) ) != "TMOD" ) { | |
throw new Exception( "Magic Header != \"TMOD\"" ); | |
} | |
var ModLoaderVersion = new Version( reader.ReadString() ); | |
var hash = reader.ReadBytes( 20 ); | |
var Signature = reader.ReadBytes( 256 ); | |
reader.ReadInt32(); | |
var pos = stream.Position; | |
if( !SHA1.Create().ComputeHash( stream ).SequenceEqual( hash ) ) { | |
throw new Exception( "ModLoader Load Error : Hash Mismatch Corrupted" ); | |
} | |
stream.Position = pos; | |
var modName = reader.ReadString(); | |
var modVersion = new Version( reader.ReadString() ); | |
var fileCount = reader.ReadInt32(); | |
Console.WriteLine( $"modName: {modName}\nmodVersion: {modVersion}\nfileCount: {fileCount}" ); | |
var outTopDirectory = Path.Combine( outRootDirectory, Path.GetFileNameWithoutExtension( tmodFile ) ); | |
var files = new List<(string Name, int Offset, int Length, int CompressedLength)>(); | |
var offset = 0; | |
for( int i = 0; i < fileCount; i++ ) { | |
var name = reader.ReadString(); | |
var length = reader.ReadInt32(); | |
var compressedLength = reader.ReadInt32(); | |
files.Add( (name, offset, length, compressedLength) ); | |
offset += compressedLength; | |
} | |
var dataStartPos = (int)stream.Position; | |
for( var i = 0; i < files.Count; i++ ) { | |
var f = files[i]; | |
var dataOffset = f.Offset + dataStartPos; | |
Console.WriteLine( $"[{i,4}] {f.Name}" ); | |
byte[] data; | |
if( f.Length != f.CompressedLength ) { | |
//圧縮されている | |
data = new byte[f.CompressedLength]; | |
stream.Position = dataOffset; | |
stream.Read( data, 0, data.Length ); | |
using var ms = new MemoryStream( data ); | |
using var ds = new DeflateStream( ms, CompressionMode.Decompress ); | |
using var decompressMs = new MemoryStream(); | |
ds.CopyTo( decompressMs ); | |
decompressMs.Position = 0; | |
data = decompressMs.ToArray(); | |
} | |
else { | |
//圧縮されていない | |
data = new byte[f.Length]; | |
stream.Position = dataOffset; | |
stream.Read( data, 0, data.Length ); | |
} | |
var outFile = Path.Combine( outTopDirectory, f.Name ); | |
var outDirectory = Path.GetDirectoryName( outFile ); | |
Directory.CreateDirectory( outDirectory ); | |
using var outStream = new FileStream( outFile, FileMode.OpenOrCreate, FileAccess.Write ); | |
using var outWriter = new BinaryWriter( outStream ); | |
outWriter.Write( data ); | |
if( outFile.EndsWith( ".rawimg" ) ) { | |
var imgFile = Path.ChangeExtension( outFile, ".png" ); | |
this.SaveAsPng( data, imgFile ); | |
} | |
} | |
} | |
/// <summary> | |
/// save .rawimg file to PNG. | |
/// </summary> | |
/// <param name="data">.rawimg binary data</param> | |
/// <param name="pngFile">PNG filename</param> | |
/// <remarks>require FNA.dll, FNA3D.dll & SDL2.dll.</remarks> | |
private unsafe void SaveAsPng( byte[] data, string pngFile ) { | |
using var ms = new MemoryStream( data ); | |
using var reader = new BinaryReader( ms ); | |
var formatVersion = reader.ReadInt32(); | |
if( formatVersion != 1 ) { | |
throw new Exception( "Unknown RawImg Format Version: " + formatVersion ); | |
} | |
var width = reader.ReadInt32(); | |
var height = reader.ReadInt32(); | |
var imgData = reader.ReadBytes( width * height * 4 ); | |
fixed( byte* pixels = &imgData[0] ) { | |
using var outStream = new FileStream( pngFile, FileMode.Create ); | |
FNA3D.WritePNGStream( outStream, width, height, width, height, (IntPtr)((void*)pixels) ); | |
} | |
imgData = null; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment