Initial release
This commit is contained in:
commit
8f7e17251f
330
.gitignore
vendored
Normal file
330
.gitignore
vendored
Normal file
@ -0,0 +1,330 @@
|
|||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUNIT
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
**/Properties/launchSettings.json
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_i.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# JustCode is a .NET coding add-in
|
||||||
|
.JustCode
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# JetBrains Rider
|
||||||
|
.idea/
|
||||||
|
*.sln.iml
|
||||||
|
|
||||||
|
# CodeRush
|
||||||
|
.cr/
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
29
README
Normal file
29
README
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
## gitadora-texbintool.exe
|
||||||
|
Convert archives of tex images.
|
||||||
|
```
|
||||||
|
usage: gitadora-texbintool.exe [--no-rect/--nr] [--no-split/--ns] input_filename
|
||||||
|
--no-rect/--nr: Don't create a rect table (Some games like Jubeat don't use the rect table)
|
||||||
|
--no-split/--ns: Don't split images into separate images if they use the rect table
|
||||||
|
```
|
||||||
|
|
||||||
|
If you specify a .bin file as the `input_filename` then the tool will extract the textures into a folder with the same name as the .bin file.
|
||||||
|
If you specify a folder as the `input_filename` then the tool will create a .bin file with the same name as the folder.
|
||||||
|
|
||||||
|
`--no-rect` is used for .bin creation. It skips writing the rect section at the end of the bin file.
|
||||||
|
|
||||||
|
`--no-split` is used during .bin extraction. Texbins can have multiple files, and within those files have multiple rects/subimages.
|
||||||
|
This command will output the original images with a `metadata.xml` file containing the rect information.
|
||||||
|
When creating a .bin from a folder with a `metadata.xml`, the `metadata.xml` is used to create the .bin. Any files in the folder not listed in the `metadata.xml` will be ignored.
|
||||||
|
If you want to replace a specific subimage without modifying the original image file, you can modify the `ExternalFilename` part of the `metadata.xml` to point to the new image file while updating the X/Y (set to 0) and updating the W/H (set as required).
|
||||||
|
|
||||||
|
## gitadora-textool.exe
|
||||||
|
Convert individual tex files.
|
||||||
|
```
|
||||||
|
usage: gitadora-textool.exe input_filename
|
||||||
|
```
|
||||||
|
|
||||||
|
If you specify a .tex file as the `input_filename` then the tool will convert the .tex to .png.
|
||||||
|
|
||||||
|
If you specify a non-.tex file as the `input_filename` then the tool will try to convert the image file to .tex.
|
||||||
|
The tool uses C#'s Bitmap class to load images, so any format supported normally by C# should work (PNG, JPG, BMP, etc).
|
||||||
|
PNG is the only "officially" supported format but JPG should be safe as well, and probably others too.
|
6
gitadora-texbintool/App.config
Normal file
6
gitadora-texbintool/App.config
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<configuration>
|
||||||
|
<startup>
|
||||||
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
|
||||||
|
</startup>
|
||||||
|
</configuration>
|
693
gitadora-texbintool/Program.cs
Normal file
693
gitadora-texbintool/Program.cs
Normal file
@ -0,0 +1,693 @@
|
|||||||
|
using System;
|
||||||
|
using System.CodeDom;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
using gitadora_textool;
|
||||||
|
|
||||||
|
namespace gitadora_texbintool
|
||||||
|
{
|
||||||
|
public class EntryInfo
|
||||||
|
{
|
||||||
|
public int Id;
|
||||||
|
public int Hash;
|
||||||
|
public int Offset;
|
||||||
|
public string Filename;
|
||||||
|
|
||||||
|
// For data entry
|
||||||
|
public int Unk1;
|
||||||
|
public int CompSize;
|
||||||
|
public int DataOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct RectMetadata
|
||||||
|
{
|
||||||
|
public int ImageId;
|
||||||
|
public ushort X;
|
||||||
|
public ushort Y;
|
||||||
|
public ushort W;
|
||||||
|
public ushort H;
|
||||||
|
public EntryInfo Entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TexInfo
|
||||||
|
{
|
||||||
|
public List<RectInfo> RectInfo;
|
||||||
|
|
||||||
|
public TexInfo()
|
||||||
|
{
|
||||||
|
RectInfo = new List<RectInfo>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static int ReadInt32(BinaryReader reader)
|
||||||
|
{
|
||||||
|
var data = reader.ReadBytes(4);
|
||||||
|
Array.Reverse(data);
|
||||||
|
return BitConverter.ToInt32(data, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static byte[] Decompress(BinaryReader reader)
|
||||||
|
{
|
||||||
|
var decompSize = ReadInt32(reader);
|
||||||
|
var compSize = ReadInt32(reader);
|
||||||
|
|
||||||
|
if (compSize == 0)
|
||||||
|
{
|
||||||
|
return reader.ReadBytes(decompSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
var compData = reader.ReadBytes(compSize);
|
||||||
|
|
||||||
|
byte[] windowData = new byte[4096];
|
||||||
|
byte[] outputData = new byte[decompSize];
|
||||||
|
int compOffset = 0, decompOffset = 0, window = 4078;
|
||||||
|
|
||||||
|
uint controlByte = 0;
|
||||||
|
int bitCount = 0;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (bitCount == 0)
|
||||||
|
{
|
||||||
|
if (compOffset >= compSize)
|
||||||
|
{
|
||||||
|
//Console.WriteLine($"compOffset >= compSize: {compOffset} >= {compSize}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
controlByte = compData[compOffset++];
|
||||||
|
bitCount = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((controlByte & 0x01) != 0)
|
||||||
|
{
|
||||||
|
if (compOffset >= compSize)
|
||||||
|
{
|
||||||
|
//Console.WriteLine($"compOffset >= compSize: {compOffset} >= {compSize}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputData[decompOffset] = windowData[window] = compData[compOffset];
|
||||||
|
decompOffset++;
|
||||||
|
window++;
|
||||||
|
compOffset++;
|
||||||
|
|
||||||
|
if (decompOffset >= decompSize)
|
||||||
|
{
|
||||||
|
//Console.WriteLine($"decompOffset >= decompSize: {decompOffset} >= {decompSize}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
window &= 0xfff;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (decompOffset >= decompSize - 1)
|
||||||
|
{
|
||||||
|
//Console.WriteLine($"decompOffset >= decompSize - 1: {decompOffset} >= {decompSize} - 1");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var slideOffset = (((compData[compOffset + 1] & 0xf0) << 4) | compData[compOffset]) & 0xfff;
|
||||||
|
var slideLength = (compData[compOffset + 1] & 0x0f) + 3;
|
||||||
|
compOffset += 2;
|
||||||
|
|
||||||
|
if (decompOffset + slideLength > decompSize)
|
||||||
|
{
|
||||||
|
slideLength = decompSize - decompOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Console.WriteLine("{0:x8} {1:x8}", slideOffset, slideLength);
|
||||||
|
|
||||||
|
while (slideLength > 0)
|
||||||
|
{
|
||||||
|
outputData[decompOffset] = windowData[window] = windowData[slideOffset];
|
||||||
|
decompOffset++;
|
||||||
|
window++;
|
||||||
|
slideOffset++;
|
||||||
|
|
||||||
|
window &= 0xfff;
|
||||||
|
slideOffset &= 0xfff;
|
||||||
|
slideLength--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
controlByte >>= 1;
|
||||||
|
bitCount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputData;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CalculateHash(string input)
|
||||||
|
{
|
||||||
|
int hash = 0;
|
||||||
|
|
||||||
|
foreach (var c in input)
|
||||||
|
{
|
||||||
|
for (int i = 0; i <= 5; i++)
|
||||||
|
{
|
||||||
|
hash = (hash >> 31) & 0x4C11DB7 ^ ((hash << 1) | ((c >> i) & 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ReadDataEntrySection(BinaryReader reader, int offset, Int64 count, List<EntryInfo> entries)
|
||||||
|
{
|
||||||
|
reader.BaseStream.Seek(offset, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
entries[i].Unk1 = reader.ReadInt32();
|
||||||
|
entries[i].CompSize = reader.ReadInt32();
|
||||||
|
entries[i].DataOffset = reader.ReadInt32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<EntryInfo> ReadNameSection(BinaryReader reader, int offset)
|
||||||
|
{
|
||||||
|
reader.BaseStream.Seek(offset, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
var nampMagic = Encoding.ASCII.GetString(reader.ReadBytes(4));
|
||||||
|
if (nampMagic != "PMAN")
|
||||||
|
{
|
||||||
|
Console.WriteLine("Not a valid name section");
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var nampSectionSize = reader.ReadInt32();
|
||||||
|
var unk1 = reader.ReadBytes(8);
|
||||||
|
var fileCount = reader.ReadInt32();
|
||||||
|
var unk2 = reader.ReadBytes(8);
|
||||||
|
|
||||||
|
var stringMetadata = new List<EntryInfo>();
|
||||||
|
for (int i = 0; i < fileCount; i++)
|
||||||
|
{
|
||||||
|
int hash = reader.ReadInt32();
|
||||||
|
int id = reader.ReadInt32();
|
||||||
|
int strOffset = reader.ReadInt32();
|
||||||
|
|
||||||
|
var backupOffset = reader.BaseStream.Position;
|
||||||
|
|
||||||
|
reader.BaseStream.Seek(offset + strOffset, SeekOrigin.Begin);
|
||||||
|
var strBytes = new List<byte>();
|
||||||
|
while (reader.PeekChar() != 0)
|
||||||
|
{
|
||||||
|
strBytes.Add(reader.ReadByte());
|
||||||
|
}
|
||||||
|
|
||||||
|
var str = Encoding.ASCII.GetString(strBytes.ToArray());
|
||||||
|
|
||||||
|
stringMetadata.Add(new EntryInfo() { Offset = strOffset, Id = id, Hash = hash, Filename = str });
|
||||||
|
|
||||||
|
reader.BaseStream.Seek(backupOffset, SeekOrigin.Begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
stringMetadata.Sort((x, y) => x.Id.CompareTo(y.Id));
|
||||||
|
|
||||||
|
return stringMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<RectMetadata> ReadRectEntrySection(BinaryReader reader, int offset)
|
||||||
|
{
|
||||||
|
reader.BaseStream.Seek(offset, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
var rectMagic = Encoding.ASCII.GetString(reader.ReadBytes(4));
|
||||||
|
if (rectMagic != "TCER")
|
||||||
|
{
|
||||||
|
Console.WriteLine("Not a valid rect section");
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var unk1 = reader.ReadInt32();
|
||||||
|
var unk2 = reader.ReadInt32();
|
||||||
|
var rectSectionSize = reader.ReadInt32();
|
||||||
|
var layerCount = reader.ReadInt32();
|
||||||
|
var namOffset = reader.ReadInt32();
|
||||||
|
var rectOffset = reader.ReadInt32();
|
||||||
|
|
||||||
|
var stringMetadata = ReadNameSection(reader, offset + namOffset);
|
||||||
|
|
||||||
|
reader.BaseStream.Seek(offset + rectOffset, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
var rectInfoMetadata = new List<RectMetadata>();
|
||||||
|
for (int i = 0; i < layerCount; i++)
|
||||||
|
{
|
||||||
|
var rect = new RectMetadata();
|
||||||
|
rect.ImageId = reader.ReadInt32();
|
||||||
|
rect.X = reader.ReadUInt16();
|
||||||
|
rect.W = (ushort)(reader.ReadUInt16() - rect.X);
|
||||||
|
rect.Y = reader.ReadUInt16();
|
||||||
|
rect.H = (ushort)(reader.ReadUInt16() - rect.Y);
|
||||||
|
rect.Entry = stringMetadata[i];
|
||||||
|
rectInfoMetadata.Add(rect);
|
||||||
|
|
||||||
|
Console.WriteLine("{0:x4}x{1:x4} {2:x4}x{3:x4}", rect.X, rect.Y, rect.W, rect.H);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rectInfoMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Serialize<T>(T value, string outputFilename = null)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var xmlserializer = new XmlSerializer(typeof(T));
|
||||||
|
|
||||||
|
XmlWriterSettings settings = new XmlWriterSettings();
|
||||||
|
settings.Indent = true;
|
||||||
|
settings.IndentChars = ("\t");
|
||||||
|
|
||||||
|
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
|
||||||
|
namespaces.Add(string.Empty, string.Empty);
|
||||||
|
|
||||||
|
if (outputFilename != null)
|
||||||
|
{
|
||||||
|
using (var writer = XmlWriter.Create(outputFilename, settings))
|
||||||
|
{
|
||||||
|
xmlserializer.Serialize(writer, value, namespaces);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var stringWriter = new StringWriter();
|
||||||
|
|
||||||
|
using (var writer = XmlWriter.Create(stringWriter, settings))
|
||||||
|
{
|
||||||
|
xmlserializer.Serialize(writer, value, namespaces);
|
||||||
|
return stringWriter.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new Exception("An error occurred", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TexInfo Deserialize(string filename)
|
||||||
|
{
|
||||||
|
XmlSerializer serializer = new XmlSerializer(typeof(TexInfo));
|
||||||
|
|
||||||
|
StreamReader reader = new StreamReader(filename);
|
||||||
|
var texInfoList = (TexInfo)serializer.Deserialize(reader);
|
||||||
|
reader.Close();
|
||||||
|
|
||||||
|
for (var index = 0; index < texInfoList.RectInfo.Count; index++)
|
||||||
|
{
|
||||||
|
texInfoList.RectInfo[index] = new RectInfo
|
||||||
|
{
|
||||||
|
ExternalFilename = texInfoList.RectInfo[index].ExternalFilename,
|
||||||
|
Filename = texInfoList.RectInfo[index].Filename,
|
||||||
|
X = texInfoList.RectInfo[index].X,
|
||||||
|
Y = texInfoList.RectInfo[index].Y,
|
||||||
|
W = (ushort)(texInfoList.RectInfo[index].X + texInfoList.RectInfo[index].W),
|
||||||
|
H = (ushort)(texInfoList.RectInfo[index].Y + texInfoList.RectInfo[index].H),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return texInfoList;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ParseTexbinFile(string filename, bool splitImages = true)
|
||||||
|
{
|
||||||
|
var outputPath = Path.Combine(Path.GetDirectoryName(filename), Path.GetFileNameWithoutExtension(filename));
|
||||||
|
Directory.CreateDirectory(outputPath);
|
||||||
|
|
||||||
|
using (BinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open)))
|
||||||
|
{
|
||||||
|
var texpMagic = Encoding.ASCII.GetString(reader.ReadBytes(4));
|
||||||
|
|
||||||
|
if (texpMagic != "PXET")
|
||||||
|
{
|
||||||
|
Console.WriteLine("Not a valid texbin file");
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var unk1 = reader.ReadInt32();
|
||||||
|
var unk2 = reader.ReadInt32();
|
||||||
|
var archiveSize = reader.ReadInt32();
|
||||||
|
var unk3 = reader.ReadInt32();
|
||||||
|
var fileCount = reader.ReadInt64();
|
||||||
|
var dataOffset = reader.ReadInt32();
|
||||||
|
var rectOffset = reader.ReadInt32();
|
||||||
|
var unk4 = reader.ReadBytes(0x10);
|
||||||
|
var nameOffset = reader.ReadInt32();
|
||||||
|
var unk5 = reader.ReadInt32();
|
||||||
|
var dataEntryOffset = reader.ReadInt32();
|
||||||
|
|
||||||
|
if (fileCount == 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine("This file doesn't contain any image data.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var entries = ReadNameSection(reader, nameOffset);
|
||||||
|
ReadDataEntrySection(reader, dataEntryOffset, fileCount, entries);
|
||||||
|
|
||||||
|
var texInfo = new TexInfo();
|
||||||
|
if (rectOffset != 0)
|
||||||
|
{
|
||||||
|
var rectInfo = ReadRectEntrySection(reader, rectOffset);
|
||||||
|
foreach (var rect in rectInfo)
|
||||||
|
{
|
||||||
|
var e = new RectInfo
|
||||||
|
{
|
||||||
|
ExternalFilename = entries[rect.ImageId].Filename,
|
||||||
|
Filename = rect.Entry.Filename,
|
||||||
|
X = rect.X,
|
||||||
|
Y = rect.Y,
|
||||||
|
W = rect.W,
|
||||||
|
H = rect.H
|
||||||
|
};
|
||||||
|
texInfo.RectInfo.Add(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add code to optionally not split texture files and save a metadata file instead
|
||||||
|
if (!splitImages)
|
||||||
|
{
|
||||||
|
Serialize<TexInfo>(texInfo, Path.Combine(outputPath, "_metadata.xml"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var entry in entries)
|
||||||
|
{
|
||||||
|
reader.BaseStream.Seek(entry.DataOffset, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
var data = Decompress(reader);
|
||||||
|
|
||||||
|
if (Encoding.ASCII.GetString(data, 0, 4) == "TXDT"
|
||||||
|
|| Encoding.ASCII.GetString(data, 0, 4) == "TDXT")
|
||||||
|
{
|
||||||
|
var rectInfoList = texInfo.RectInfo.Where(x =>
|
||||||
|
String.CompareOrdinal(Path.GetFileNameWithoutExtension(entry.Filename),
|
||||||
|
x.ExternalFilename) == 0).ToList();
|
||||||
|
|
||||||
|
if (!splitImages)
|
||||||
|
{
|
||||||
|
rectInfoList.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rectInfoList.Count == 0)
|
||||||
|
{
|
||||||
|
var rectInfo = new RectInfo
|
||||||
|
{
|
||||||
|
ExternalFilename = entry.Filename,
|
||||||
|
Filename = entry.Filename,
|
||||||
|
X = 0,
|
||||||
|
Y = 0,
|
||||||
|
W = 0,
|
||||||
|
H = 0
|
||||||
|
};
|
||||||
|
rectInfoList.Add(rectInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var rectInfo in rectInfoList)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var stream = new MemoryStream(data))
|
||||||
|
{
|
||||||
|
using (var dataReader = new BinaryReader(stream))
|
||||||
|
{
|
||||||
|
byte[] extractedData;
|
||||||
|
|
||||||
|
|
||||||
|
if (!splitImages || rectInfoList.Count == 0 || rectInfo.W == 0 || rectInfo.H == 0)
|
||||||
|
{
|
||||||
|
extractedData = gitadora_textool.Program.ExtractImageCore(dataReader, null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
extractedData = gitadora_textool.Program.ExtractImageCore(dataReader, rectInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ext = ".png";
|
||||||
|
if (extractedData[0] == 'D' && extractedData[1] == 'D' && extractedData[2] == 'S' && extractedData[3] == ' ')
|
||||||
|
{
|
||||||
|
ext = ".dds";
|
||||||
|
}
|
||||||
|
|
||||||
|
var outputFilename = Path.Combine(outputPath, rectInfo.Filename);
|
||||||
|
outputFilename += ext;
|
||||||
|
|
||||||
|
Console.WriteLine("Saving {0}...", outputFilename);
|
||||||
|
|
||||||
|
File.WriteAllBytes(outputFilename, extractedData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Couldn't convert image: {0}", e.Message);
|
||||||
|
File.WriteAllBytes(Path.Combine(outputPath, entry.Filename), data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<byte> CreateNameSection(List<string> filelist_unique)
|
||||||
|
{
|
||||||
|
var nameSection = new List<byte>();
|
||||||
|
var filenameSectionSize = 0x1c + (filelist_unique.Count * 0x0c) + filelist_unique.Select(x => x.Length + 1).Sum();
|
||||||
|
if ((filenameSectionSize % 4) != 0)
|
||||||
|
filenameSectionSize += 4 - (filenameSectionSize % 4);
|
||||||
|
|
||||||
|
nameSection.AddRange(new byte[] { 0x50, 0x4D, 0x41, 0x4E });
|
||||||
|
nameSection.AddRange(BitConverter.GetBytes(filenameSectionSize));
|
||||||
|
nameSection.AddRange(new byte[] { 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00 });
|
||||||
|
nameSection.AddRange(BitConverter.GetBytes(filelist_unique.Count));
|
||||||
|
|
||||||
|
ushort a = (ushort)(1 << Convert.ToString(filelist_unique.Count >> 1, 2).Length - 1);
|
||||||
|
ushort b = (ushort)((1 << Convert.ToString(filelist_unique.Count >> 1, 2).Length) - 1);
|
||||||
|
|
||||||
|
nameSection.AddRange(BitConverter.GetBytes(a));
|
||||||
|
nameSection.AddRange(BitConverter.GetBytes(b));
|
||||||
|
nameSection.AddRange(BitConverter.GetBytes(nameSection.Count + 4));
|
||||||
|
|
||||||
|
List<uint> hashes = filelist_unique.Select(x => (uint)CalculateHash(x)).OrderBy(x => x).ToList();
|
||||||
|
string[] filelist_sorted = new string[filelist_unique.Count];
|
||||||
|
|
||||||
|
foreach (var filename in filelist_unique)
|
||||||
|
{
|
||||||
|
filelist_sorted[hashes.IndexOf((uint)CalculateHash(filename))] = filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nameBaseOffsetBase = nameSection.Count + (filelist_unique.Count * 0x0c);
|
||||||
|
for (int i = 0; i < filelist_sorted.Length; i++)
|
||||||
|
{
|
||||||
|
var filelist_idx = filelist_unique.IndexOf(filelist_sorted[i]);
|
||||||
|
|
||||||
|
nameSection.AddRange(BitConverter.GetBytes(CalculateHash(filelist_sorted[i])));
|
||||||
|
nameSection.AddRange(BitConverter.GetBytes(filelist_idx));
|
||||||
|
|
||||||
|
var nameBaseOffset = nameBaseOffsetBase;
|
||||||
|
for (int j = 0; j < filelist_idx; j++)
|
||||||
|
{
|
||||||
|
nameBaseOffset += filelist_unique[j].Length + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nameSection.AddRange(BitConverter.GetBytes(nameBaseOffset));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < filelist_unique.Count; i++)
|
||||||
|
{
|
||||||
|
nameSection.AddRange(Encoding.ASCII.GetBytes(filelist_unique[i]));
|
||||||
|
nameSection.Add(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (nameSection.Count < filenameSectionSize)
|
||||||
|
{
|
||||||
|
nameSection.Add(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nameSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CreateTexbinFile(string pathname, bool generateRectSection = true)
|
||||||
|
{
|
||||||
|
var filelist = Directory.GetFiles(pathname).Where(x => !x.ToLower().EndsWith("_metadata.xml")).ToArray();
|
||||||
|
var filelist_unique = filelist.Select(Path.GetFileNameWithoutExtension).Distinct().Where(x => !x.ToLower().EndsWith("_metadata.xml")).ToList();
|
||||||
|
filelist_unique = filelist_unique.Select(x => x.ToUpper()).ToList();
|
||||||
|
|
||||||
|
if (filelist_unique.Count != filelist.Length)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Folder has more files than expected. Are there multiple files with the same name (not including extension)?");
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var nameSection = CreateNameSection(filelist_unique);
|
||||||
|
|
||||||
|
var dataSection = new List<byte>();
|
||||||
|
var fileinfoSection = new List<byte>();
|
||||||
|
var imageRectInfo = new Dictionary<string, Tuple<ushort, ushort>>();
|
||||||
|
for (int i = 0; i < filelist_unique.Count; i++)
|
||||||
|
{
|
||||||
|
var data = File.ReadAllBytes(filelist[i]);
|
||||||
|
|
||||||
|
Console.WriteLine("Adding {0}...", filelist[i]);
|
||||||
|
|
||||||
|
if (!data.Take(4).SequenceEqual(new byte[] { 0x54, 0x58, 0x44, 0x54 })
|
||||||
|
&& !data.Take(4).SequenceEqual(new byte[] { 0x54, 0x44, 0x58, 0x54 }))
|
||||||
|
{
|
||||||
|
data = gitadora_textool.Program.CreateImageCore(data, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
fileinfoSection.AddRange(BitConverter.GetBytes(0));
|
||||||
|
fileinfoSection.AddRange(BitConverter.GetBytes(data.Length + 0x08));
|
||||||
|
fileinfoSection.AddRange(BitConverter.GetBytes(0x40 + nameSection.Count + (filelist_unique.Count * 0x0c) + dataSection.Count));
|
||||||
|
|
||||||
|
dataSection.AddRange(BitConverter.GetBytes(data.Length).Reverse());
|
||||||
|
dataSection.AddRange(BitConverter.GetBytes(0));
|
||||||
|
dataSection.AddRange(data);
|
||||||
|
|
||||||
|
imageRectInfo[filelist_unique[i]] = new Tuple<ushort, ushort>((ushort)((data[0x11] << 8) | data[0x10]), (ushort)((data[0x13] << 8) | data[0x12]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((dataSection.Count % 4) != 0)
|
||||||
|
{
|
||||||
|
var padding = 4 - (dataSection.Count % 4);
|
||||||
|
while (padding > 0)
|
||||||
|
{
|
||||||
|
dataSection.Add(0);
|
||||||
|
padding--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var rectSection = new List<byte>();
|
||||||
|
if (generateRectSection)
|
||||||
|
{
|
||||||
|
var rectInfo = new TexInfo();
|
||||||
|
|
||||||
|
if (File.Exists(Path.Combine(pathname, "_metadata.xml")))
|
||||||
|
{
|
||||||
|
rectInfo = Deserialize(Path.Combine(pathname, "_metadata.xml"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var filename in filelist_unique)
|
||||||
|
{
|
||||||
|
var data = new RectInfo
|
||||||
|
{
|
||||||
|
ExternalFilename = filename,
|
||||||
|
Filename = filename,
|
||||||
|
X = 0,
|
||||||
|
Y = 0,
|
||||||
|
W = imageRectInfo[filename].Item1,
|
||||||
|
H = imageRectInfo[filename].Item2
|
||||||
|
};
|
||||||
|
rectInfo.RectInfo.Add(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var rectNameFilelist = rectInfo.RectInfo.Select(x => x.Filename)
|
||||||
|
.Select(Path.GetFileNameWithoutExtension).Distinct()
|
||||||
|
.Where(x => !x.ToLower().EndsWith("_metadata.xml")).ToList();
|
||||||
|
|
||||||
|
var rectinfoSection = new List<byte>();
|
||||||
|
var rectNameSection = CreateNameSection(rectNameFilelist);
|
||||||
|
foreach (var data in rectInfo.RectInfo)
|
||||||
|
{
|
||||||
|
rectinfoSection.AddRange(BitConverter.GetBytes(filelist_unique.IndexOf(Path.GetFileNameWithoutExtension(data.ExternalFilename))));
|
||||||
|
rectinfoSection.AddRange(BitConverter.GetBytes(data.X));
|
||||||
|
rectinfoSection.AddRange(BitConverter.GetBytes(data.W));
|
||||||
|
rectinfoSection.AddRange(BitConverter.GetBytes(data.Y));
|
||||||
|
rectinfoSection.AddRange(BitConverter.GetBytes(data.H));
|
||||||
|
}
|
||||||
|
|
||||||
|
rectSection.AddRange(
|
||||||
|
new byte[] { 0x54, 0x43, 0x45, 0x52, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00 });
|
||||||
|
rectSection.AddRange(BitConverter.GetBytes(0x1c + rectNameSection.Count + rectinfoSection.Count));
|
||||||
|
rectSection.AddRange(BitConverter.GetBytes(rectNameFilelist.Count));
|
||||||
|
rectSection.AddRange(BitConverter.GetBytes(0x1c));
|
||||||
|
rectSection.AddRange(BitConverter.GetBytes(0x1c + rectNameSection.Count));
|
||||||
|
rectSection.AddRange(rectNameSection);
|
||||||
|
rectSection.AddRange(rectinfoSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
var outputData = new List<byte>();
|
||||||
|
outputData.AddRange(new byte[] { 0x50, 0x58, 0x45, 0x54, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00 });
|
||||||
|
outputData.AddRange(BitConverter.GetBytes(0x40 + nameSection.Count + fileinfoSection.Count + dataSection.Count + rectSection.Count)); // Archive size
|
||||||
|
outputData.AddRange(BitConverter.GetBytes(1));
|
||||||
|
outputData.AddRange(BitConverter.GetBytes(filelist_unique.Count));
|
||||||
|
outputData.AddRange(BitConverter.GetBytes(0));
|
||||||
|
outputData.AddRange(BitConverter.GetBytes(0x40 + nameSection.Count + fileinfoSection.Count));
|
||||||
|
outputData.AddRange(BitConverter.GetBytes(rectSection.Count > 0 ? 0x40 + nameSection.Count + fileinfoSection.Count + dataSection.Count : 0));
|
||||||
|
outputData.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });
|
||||||
|
outputData.AddRange(BitConverter.GetBytes(0x40)); // PMAN section offset
|
||||||
|
outputData.AddRange(BitConverter.GetBytes(0));
|
||||||
|
outputData.AddRange(BitConverter.GetBytes(0x40 + nameSection.Count));
|
||||||
|
outputData.AddRange(nameSection);
|
||||||
|
outputData.AddRange(fileinfoSection);
|
||||||
|
outputData.AddRange(dataSection);
|
||||||
|
outputData.AddRange(rectSection);
|
||||||
|
|
||||||
|
var basePath = Path.GetFileName(pathname);
|
||||||
|
if (String.IsNullOrWhiteSpace(basePath))
|
||||||
|
basePath = pathname.Replace(".\\", "").Replace("\\", "");
|
||||||
|
|
||||||
|
var outputFilename = Path.Combine(Path.GetDirectoryName(pathname), String.Format("{0}.bin", basePath));
|
||||||
|
File.WriteAllBytes(outputFilename, outputData.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length <= 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine("usage: {0} [--no-rect/--nr] [--no-split/--ns] input_filename", AppDomain.CurrentDomain.FriendlyName);
|
||||||
|
Console.WriteLine("--no-rect/--nr: Don't create a rect table (Some games like Jubeat don't use the rect table)");
|
||||||
|
Console.WriteLine("--no-split/--ns: Don't split images into separate images if they use the rect table");
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var splitImage = true;
|
||||||
|
var generateRectSection = true;
|
||||||
|
|
||||||
|
var filenames = new List<string>();
|
||||||
|
for (var index = 0; index < args.Length; index++)
|
||||||
|
{
|
||||||
|
if (String.CompareOrdinal(args[index].ToLower(), "--no-rect") == 0
|
||||||
|
|| String.CompareOrdinal(args[index].ToLower(), "--nr") == 0)
|
||||||
|
{
|
||||||
|
generateRectSection = false;
|
||||||
|
}
|
||||||
|
else if (String.CompareOrdinal(args[index].ToLower(), "--no-split") == 0
|
||||||
|
|| String.CompareOrdinal(args[index].ToLower(), "--ns") == 0)
|
||||||
|
{
|
||||||
|
splitImage = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
filenames.Add(args[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var filename in filenames)
|
||||||
|
{
|
||||||
|
if (Directory.Exists(filename))
|
||||||
|
{
|
||||||
|
CreateTexbinFile(filename, generateRectSection);
|
||||||
|
}
|
||||||
|
else if (File.Exists(filename))
|
||||||
|
{
|
||||||
|
ParseTexbinFile(filename, splitImage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
gitadora-texbintool/Properties/AssemblyInfo.cs
Normal file
36
gitadora-texbintool/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("gitadora-texbintool")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("gitadora-texbintool")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("14f21197-8ff1-4a4f-b976-aaa55378fade")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
58
gitadora-texbintool/gitadora-texbintool.csproj
Normal file
58
gitadora-texbintool/gitadora-texbintool.csproj
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{14F21197-8FF1-4A4F-B976-AAA55378FADE}</ProjectGuid>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<RootNamespace>gitadora_texbintool</RootNamespace>
|
||||||
|
<AssemblyName>gitadora-texbintool</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugType>none</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="App.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\gitadora-textool\gitadora-textool.csproj">
|
||||||
|
<Project>{8c5cc09a-436c-4caa-9213-31a4f351dbdc}</Project>
|
||||||
|
<Name>gitadora-textool</Name>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
31
gitadora-textool.sln
Normal file
31
gitadora-textool.sln
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 15
|
||||||
|
VisualStudioVersion = 15.0.27130.2010
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gitadora-textool", "gitadora-textool\gitadora-textool.csproj", "{8C5CC09A-436C-4CAA-9213-31A4F351DBDC}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "gitadora-texbintool", "gitadora-texbintool\gitadora-texbintool.csproj", "{14F21197-8FF1-4A4F-B976-AAA55378FADE}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{8C5CC09A-436C-4CAA-9213-31A4F351DBDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{8C5CC09A-436C-4CAA-9213-31A4F351DBDC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{8C5CC09A-436C-4CAA-9213-31A4F351DBDC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{8C5CC09A-436C-4CAA-9213-31A4F351DBDC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{14F21197-8FF1-4A4F-B976-AAA55378FADE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{14F21197-8FF1-4A4F-B976-AAA55378FADE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{14F21197-8FF1-4A4F-B976-AAA55378FADE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{14F21197-8FF1-4A4F-B976-AAA55378FADE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {5EDE6B8F-78F2-46C0-A47E-D3A53277210A}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
6
gitadora-textool/App.config
Normal file
6
gitadora-textool/App.config
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<configuration>
|
||||||
|
<startup>
|
||||||
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
|
||||||
|
</startup>
|
||||||
|
</configuration>
|
460
gitadora-textool/DxtUtil.cs
Normal file
460
gitadora-textool/DxtUtil.cs
Normal file
@ -0,0 +1,460 @@
|
|||||||
|
// #region License
|
||||||
|
// /*
|
||||||
|
// Microsoft Public License (Ms-PL)
|
||||||
|
// MonoGame - Copyright © 2009 The MonoGame Team
|
||||||
|
//
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This license governs use of the accompanying software. If you use the software, you accept this license. If you do not
|
||||||
|
// accept the license, do not use the software.
|
||||||
|
//
|
||||||
|
// 1. Definitions
|
||||||
|
// The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under
|
||||||
|
// U.S. copyright law.
|
||||||
|
//
|
||||||
|
// A "contribution" is the original software, or any additions or changes to the software.
|
||||||
|
// A "contributor" is any person that distributes its contribution under this license.
|
||||||
|
// "Licensed patents" are a contributor's patent claims that read directly on its contribution.
|
||||||
|
//
|
||||||
|
// 2. Grant of Rights
|
||||||
|
// (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
|
||||||
|
// each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
|
||||||
|
// (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
|
||||||
|
// each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
|
||||||
|
//
|
||||||
|
// 3. Conditions and Limitations
|
||||||
|
// (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
|
||||||
|
// (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software,
|
||||||
|
// your patent license from such contributor to the software ends automatically.
|
||||||
|
// (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution
|
||||||
|
// notices that are present in the software.
|
||||||
|
// (D) If you distribute any portion of the software in source code form, you may do so only under this license by including
|
||||||
|
// a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object
|
||||||
|
// code form, you may only do so under a license that complies with this license.
|
||||||
|
// (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees
|
||||||
|
// or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent
|
||||||
|
// permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular
|
||||||
|
// purpose and non-infringement.
|
||||||
|
// */
|
||||||
|
// #endregion License
|
||||||
|
//
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Microsoft.Xna.Framework.Graphics
|
||||||
|
{
|
||||||
|
internal static class DxtUtil
|
||||||
|
{
|
||||||
|
internal static byte[] DecompressDxt1(byte[] imageData, int width, int height)
|
||||||
|
{
|
||||||
|
using (MemoryStream imageStream = new MemoryStream(imageData))
|
||||||
|
return DecompressDxt1(imageStream, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static byte[] DecompressDxt1(Stream imageStream, int width, int height)
|
||||||
|
{
|
||||||
|
byte[] imageData = new byte[width * height * 4];
|
||||||
|
|
||||||
|
using (BinaryReader imageReader = new BinaryReader(imageStream))
|
||||||
|
{
|
||||||
|
int blockCountX = (width + 3) / 4;
|
||||||
|
int blockCountY = (height + 3) / 4;
|
||||||
|
|
||||||
|
for (int y = 0; y < blockCountY; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < blockCountX; x++)
|
||||||
|
{
|
||||||
|
DecompressDxt1Block(imageReader, x, y, blockCountX, width, height, imageData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return imageData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DecompressDxt1Block(BinaryReader imageReader, int x, int y, int blockCountX, int width, int height, byte[] imageData)
|
||||||
|
{
|
||||||
|
ushort c0 = imageReader.ReadUInt16();
|
||||||
|
ushort c1 = imageReader.ReadUInt16();
|
||||||
|
|
||||||
|
byte r0, g0, b0;
|
||||||
|
byte r1, g1, b1;
|
||||||
|
ConvertRgb565ToRgb888(c0, out r0, out g0, out b0);
|
||||||
|
ConvertRgb565ToRgb888(c1, out r1, out g1, out b1);
|
||||||
|
|
||||||
|
uint lookupTable = imageReader.ReadUInt32();
|
||||||
|
|
||||||
|
for (int blockY = 0; blockY < 4; blockY++)
|
||||||
|
{
|
||||||
|
for (int blockX = 0; blockX < 4; blockX++)
|
||||||
|
{
|
||||||
|
byte r = 0, g = 0, b = 0, a = 255;
|
||||||
|
uint index = (lookupTable >> 2 * (4 * blockY + blockX)) & 0x03;
|
||||||
|
|
||||||
|
if (c0 > c1)
|
||||||
|
{
|
||||||
|
switch (index)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
r = r0;
|
||||||
|
g = g0;
|
||||||
|
b = b0;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
r = r1;
|
||||||
|
g = g1;
|
||||||
|
b = b1;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
r = (byte)((2 * r0 + r1) / 3);
|
||||||
|
g = (byte)((2 * g0 + g1) / 3);
|
||||||
|
b = (byte)((2 * b0 + b1) / 3);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
r = (byte)((r0 + 2 * r1) / 3);
|
||||||
|
g = (byte)((g0 + 2 * g1) / 3);
|
||||||
|
b = (byte)((b0 + 2 * b1) / 3);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (index)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
r = r0;
|
||||||
|
g = g0;
|
||||||
|
b = b0;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
r = r1;
|
||||||
|
g = g1;
|
||||||
|
b = b1;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
r = (byte)((r0 + r1) / 2);
|
||||||
|
g = (byte)((g0 + g1) / 2);
|
||||||
|
b = (byte)((b0 + b1) / 2);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
r = 0;
|
||||||
|
g = 0;
|
||||||
|
b = 0;
|
||||||
|
a = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int px = (x << 2) + blockX;
|
||||||
|
int py = (y << 2) + blockY;
|
||||||
|
if ((px < width) && (py < height))
|
||||||
|
{
|
||||||
|
int offset = ((py * width) + px) << 2;
|
||||||
|
imageData[offset] = r;
|
||||||
|
imageData[offset + 1] = g;
|
||||||
|
imageData[offset + 2] = b;
|
||||||
|
imageData[offset + 3] = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static byte[] DecompressDxt3(byte[] imageData, int width, int height)
|
||||||
|
{
|
||||||
|
using (MemoryStream imageStream = new MemoryStream(imageData))
|
||||||
|
return DecompressDxt3(imageStream, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static byte[] DecompressDxt3(Stream imageStream, int width, int height)
|
||||||
|
{
|
||||||
|
byte[] imageData = new byte[width * height * 4];
|
||||||
|
|
||||||
|
using (BinaryReader imageReader = new BinaryReader(imageStream))
|
||||||
|
{
|
||||||
|
int blockCountX = (width + 3) / 4;
|
||||||
|
int blockCountY = (height + 3) / 4;
|
||||||
|
|
||||||
|
for (int y = 0; y < blockCountY; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < blockCountX; x++)
|
||||||
|
{
|
||||||
|
DecompressDxt3Block(imageReader, x, y, blockCountX, width, height, imageData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return imageData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DecompressDxt3Block(BinaryReader imageReader, int x, int y, int blockCountX, int width, int height, byte[] imageData)
|
||||||
|
{
|
||||||
|
byte a0 = imageReader.ReadByte();
|
||||||
|
byte a1 = imageReader.ReadByte();
|
||||||
|
byte a2 = imageReader.ReadByte();
|
||||||
|
byte a3 = imageReader.ReadByte();
|
||||||
|
byte a4 = imageReader.ReadByte();
|
||||||
|
byte a5 = imageReader.ReadByte();
|
||||||
|
byte a6 = imageReader.ReadByte();
|
||||||
|
byte a7 = imageReader.ReadByte();
|
||||||
|
|
||||||
|
ushort c0 = imageReader.ReadUInt16();
|
||||||
|
ushort c1 = imageReader.ReadUInt16();
|
||||||
|
|
||||||
|
byte r0, g0, b0;
|
||||||
|
byte r1, g1, b1;
|
||||||
|
ConvertRgb565ToRgb888(c0, out r0, out g0, out b0);
|
||||||
|
ConvertRgb565ToRgb888(c1, out r1, out g1, out b1);
|
||||||
|
|
||||||
|
uint lookupTable = imageReader.ReadUInt32();
|
||||||
|
|
||||||
|
int alphaIndex = 0;
|
||||||
|
for (int blockY = 0; blockY < 4; blockY++)
|
||||||
|
{
|
||||||
|
for (int blockX = 0; blockX < 4; blockX++)
|
||||||
|
{
|
||||||
|
byte r = 0, g = 0, b = 0, a = 0;
|
||||||
|
|
||||||
|
uint index = (lookupTable >> 2 * (4 * blockY + blockX)) & 0x03;
|
||||||
|
|
||||||
|
switch (alphaIndex)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
a = (byte)((a0 & 0x0F) | ((a0 & 0x0F) << 4));
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
a = (byte)((a0 & 0xF0) | ((a0 & 0xF0) >> 4));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
a = (byte)((a1 & 0x0F) | ((a1 & 0x0F) << 4));
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
a = (byte)((a1 & 0xF0) | ((a1 & 0xF0) >> 4));
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
a = (byte)((a2 & 0x0F) | ((a2 & 0x0F) << 4));
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
a = (byte)((a2 & 0xF0) | ((a2 & 0xF0) >> 4));
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
a = (byte)((a3 & 0x0F) | ((a3 & 0x0F) << 4));
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
a = (byte)((a3 & 0xF0) | ((a3 & 0xF0) >> 4));
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
a = (byte)((a4 & 0x0F) | ((a4 & 0x0F) << 4));
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
a = (byte)((a4 & 0xF0) | ((a4 & 0xF0) >> 4));
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
a = (byte)((a5 & 0x0F) | ((a5 & 0x0F) << 4));
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
a = (byte)((a5 & 0xF0) | ((a5 & 0xF0) >> 4));
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
a = (byte)((a6 & 0x0F) | ((a6 & 0x0F) << 4));
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
a = (byte)((a6 & 0xF0) | ((a6 & 0xF0) >> 4));
|
||||||
|
break;
|
||||||
|
case 14:
|
||||||
|
a = (byte)((a7 & 0x0F) | ((a7 & 0x0F) << 4));
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
a = (byte)((a7 & 0xF0) | ((a7 & 0xF0) >> 4));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++alphaIndex;
|
||||||
|
|
||||||
|
switch (index)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
r = r0;
|
||||||
|
g = g0;
|
||||||
|
b = b0;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
r = r1;
|
||||||
|
g = g1;
|
||||||
|
b = b1;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
r = (byte)((2 * r0 + r1) / 3);
|
||||||
|
g = (byte)((2 * g0 + g1) / 3);
|
||||||
|
b = (byte)((2 * b0 + b1) / 3);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
r = (byte)((r0 + 2 * r1) / 3);
|
||||||
|
g = (byte)((g0 + 2 * g1) / 3);
|
||||||
|
b = (byte)((b0 + 2 * b1) / 3);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int px = (x << 2) + blockX;
|
||||||
|
int py = (y << 2) + blockY;
|
||||||
|
if ((px < width) && (py < height))
|
||||||
|
{
|
||||||
|
int offset = ((py * width) + px) << 2;
|
||||||
|
imageData[offset] = r;
|
||||||
|
imageData[offset + 1] = g;
|
||||||
|
imageData[offset + 2] = b;
|
||||||
|
imageData[offset + 3] = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static byte[] DecompressDxt5(byte[] imageData, int width, int height)
|
||||||
|
{
|
||||||
|
using (MemoryStream imageStream = new MemoryStream(imageData))
|
||||||
|
return DecompressDxt5(imageStream, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static byte[] DecompressDxt5(Stream imageStream, int width, int height)
|
||||||
|
{
|
||||||
|
byte[] imageData = new byte[width * height * 4];
|
||||||
|
|
||||||
|
using (BinaryReader imageReader = new BinaryReader(imageStream))
|
||||||
|
{
|
||||||
|
int blockCountX = (width + 3) / 4;
|
||||||
|
int blockCountY = (height + 3) / 4;
|
||||||
|
|
||||||
|
for (int y = 0; y < blockCountY; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < blockCountX; x++)
|
||||||
|
{
|
||||||
|
DecompressDxt5Block(imageReader, x, y, blockCountX, width, height, imageData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return imageData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DecompressDxt5Block(BinaryReader imageReader, int x, int y, int blockCountX, int width, int height, byte[] imageData)
|
||||||
|
{
|
||||||
|
byte alpha0 = imageReader.ReadByte();
|
||||||
|
byte alpha1 = imageReader.ReadByte();
|
||||||
|
|
||||||
|
ulong alphaMask = (ulong)imageReader.ReadByte();
|
||||||
|
alphaMask += (ulong)imageReader.ReadByte() << 8;
|
||||||
|
alphaMask += (ulong)imageReader.ReadByte() << 16;
|
||||||
|
alphaMask += (ulong)imageReader.ReadByte() << 24;
|
||||||
|
alphaMask += (ulong)imageReader.ReadByte() << 32;
|
||||||
|
alphaMask += (ulong)imageReader.ReadByte() << 40;
|
||||||
|
|
||||||
|
ushort c0 = imageReader.ReadUInt16();
|
||||||
|
ushort c1 = imageReader.ReadUInt16();
|
||||||
|
|
||||||
|
byte r0, g0, b0;
|
||||||
|
byte r1, g1, b1;
|
||||||
|
ConvertRgb565ToRgb888(c0, out r0, out g0, out b0);
|
||||||
|
ConvertRgb565ToRgb888(c1, out r1, out g1, out b1);
|
||||||
|
|
||||||
|
uint lookupTable = imageReader.ReadUInt32();
|
||||||
|
|
||||||
|
for (int blockY = 0; blockY < 4; blockY++)
|
||||||
|
{
|
||||||
|
for (int blockX = 0; blockX < 4; blockX++)
|
||||||
|
{
|
||||||
|
byte r = 0, g = 0, b = 0, a = 255;
|
||||||
|
uint index = (lookupTable >> 2 * (4 * blockY + blockX)) & 0x03;
|
||||||
|
|
||||||
|
uint alphaIndex = (uint)((alphaMask >> 3 * (4 * blockY + blockX)) & 0x07);
|
||||||
|
if (alphaIndex == 0)
|
||||||
|
{
|
||||||
|
a = alpha0;
|
||||||
|
}
|
||||||
|
else if (alphaIndex == 1)
|
||||||
|
{
|
||||||
|
a = alpha1;
|
||||||
|
}
|
||||||
|
else if (alpha0 > alpha1)
|
||||||
|
{
|
||||||
|
a = (byte)(((8 - alphaIndex) * alpha0 + (alphaIndex - 1) * alpha1) / 7);
|
||||||
|
}
|
||||||
|
else if (alphaIndex == 6)
|
||||||
|
{
|
||||||
|
a = 0;
|
||||||
|
}
|
||||||
|
else if (alphaIndex == 7)
|
||||||
|
{
|
||||||
|
a = 0xff;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
a = (byte)(((6 - alphaIndex) * alpha0 + (alphaIndex - 1) * alpha1) / 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (index)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
r = r0;
|
||||||
|
g = g0;
|
||||||
|
b = b0;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
r = r1;
|
||||||
|
g = g1;
|
||||||
|
b = b1;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
r = (byte)((2 * r0 + r1) / 3);
|
||||||
|
g = (byte)((2 * g0 + g1) / 3);
|
||||||
|
b = (byte)((2 * b0 + b1) / 3);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
r = (byte)((r0 + 2 * r1) / 3);
|
||||||
|
g = (byte)((g0 + 2 * g1) / 3);
|
||||||
|
b = (byte)((b0 + 2 * b1) / 3);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int px = (x << 2) + blockX;
|
||||||
|
int py = (y << 2) + blockY;
|
||||||
|
if ((px < width) && (py < height))
|
||||||
|
{
|
||||||
|
int offset = ((py * width) + px) << 2;
|
||||||
|
imageData[offset] = r;
|
||||||
|
imageData[offset + 1] = g;
|
||||||
|
imageData[offset + 2] = b;
|
||||||
|
imageData[offset + 3] = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ConvertRgb565ToRgb888(ushort color, out byte r, out byte g, out byte b)
|
||||||
|
{
|
||||||
|
int temp;
|
||||||
|
|
||||||
|
temp = (color >> 11) * 255 + 16;
|
||||||
|
r = (byte)((temp / 32 + temp) / 32);
|
||||||
|
temp = ((color & 0x07E0) >> 5) * 255 + 32;
|
||||||
|
g = (byte)((temp / 64 + temp) / 64);
|
||||||
|
temp = (color & 0x001F) * 255 + 16;
|
||||||
|
b = (byte)((temp / 32 + temp) / 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ConvertArgb4444ToArgb8888(ushort color, out byte a, out byte r, out byte g, out byte b)
|
||||||
|
{
|
||||||
|
int temp;
|
||||||
|
|
||||||
|
temp = (color & 0xf000) >> 12;
|
||||||
|
a = (byte)(temp * 16 + temp);
|
||||||
|
|
||||||
|
temp = (color & 0x0f00) >> 8;
|
||||||
|
r = (byte)(temp * 16 + temp);
|
||||||
|
|
||||||
|
temp = (color & 0x00f0) >> 4;
|
||||||
|
g = (byte)(temp * 16 + temp);
|
||||||
|
|
||||||
|
temp = color & 0x000f;
|
||||||
|
b = (byte)(temp * 16 + temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
530
gitadora-textool/Program.cs
Normal file
530
gitadora-textool/Program.cs
Normal file
@ -0,0 +1,530 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Microsoft.Xna.Framework.Graphics;
|
||||||
|
|
||||||
|
namespace gitadora_textool
|
||||||
|
{
|
||||||
|
public struct RectInfo
|
||||||
|
{
|
||||||
|
public string ExternalFilename;
|
||||||
|
public string Filename;
|
||||||
|
public ushort X;
|
||||||
|
public ushort Y;
|
||||||
|
public ushort W;
|
||||||
|
public ushort H;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
static int ReadInt32(BinaryReader reader, bool endianness = false)
|
||||||
|
{
|
||||||
|
var data = reader.ReadBytes(4);
|
||||||
|
if (!endianness)
|
||||||
|
Array.Reverse(data);
|
||||||
|
return BitConverter.ToInt32(data, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ReadInt16(BinaryReader reader, bool endianness = false)
|
||||||
|
{
|
||||||
|
var data = reader.ReadBytes(2);
|
||||||
|
if (!endianness)
|
||||||
|
Array.Reverse(data);
|
||||||
|
return BitConverter.ToInt16(data, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WriteInt32(BinaryWriter writer, uint input, bool endianness = false)
|
||||||
|
{
|
||||||
|
var data = BitConverter.GetBytes(input);
|
||||||
|
if (!endianness)
|
||||||
|
Array.Reverse(data);
|
||||||
|
writer.Write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WriteInt16(BinaryWriter writer, ushort input, bool endianness = false)
|
||||||
|
{
|
||||||
|
var data = BitConverter.GetBytes(input);
|
||||||
|
if (!endianness)
|
||||||
|
Array.Reverse(data);
|
||||||
|
writer.Write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static byte[] ExtractImageCore(BinaryReader reader, RectInfo? rectInfoList = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
var magic = Encoding.ASCII.GetString(reader.ReadBytes(4));
|
||||||
|
var endianCheck1 = reader.ReadInt32();
|
||||||
|
var endianCheck2 = reader.ReadInt32();
|
||||||
|
var requiresEndianFix = endianCheck2 == 0x00010100;
|
||||||
|
|
||||||
|
reader.BaseStream.Seek(0x0c, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
var dataSize = ReadInt32(reader, requiresEndianFix) - 0x40;
|
||||||
|
var width = ReadInt16(reader, requiresEndianFix);
|
||||||
|
var height = ReadInt16(reader, requiresEndianFix);
|
||||||
|
|
||||||
|
if (!requiresEndianFix)
|
||||||
|
reader.BaseStream.Seek(0x03, SeekOrigin.Current);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
GRAYSCALE_FORMAT 0x01
|
||||||
|
GRAYSCALE_FORMAT_2 0x06
|
||||||
|
BGR_16BIT_FORMAT 0x0C
|
||||||
|
BGRA_16BIT_FORMAT 0x0D
|
||||||
|
BGR_FORMAT 0x0E
|
||||||
|
BGRA_FORMAT 0x10
|
||||||
|
BGR_8BIT_FORMAT 0x12
|
||||||
|
DXT1_FORMAT 0x16
|
||||||
|
DXT3_FORMAT 0x18
|
||||||
|
DXT5_FORMAT 0x1A
|
||||||
|
*/
|
||||||
|
|
||||||
|
var dataFormat = reader.ReadByte();
|
||||||
|
|
||||||
|
reader.BaseStream.Seek(0x40, SeekOrigin.Begin);
|
||||||
|
var bitmapData = reader.ReadBytes(dataSize);
|
||||||
|
|
||||||
|
var pixelFormat = PixelFormat.Undefined;
|
||||||
|
if (dataFormat == 0x01)
|
||||||
|
{
|
||||||
|
// GRAYSCALE_FORMAT8
|
||||||
|
pixelFormat = PixelFormat.Format8bppIndexed;
|
||||||
|
}
|
||||||
|
else if (dataFormat == 0x06)
|
||||||
|
{
|
||||||
|
// GRAYSCALE_FORMAT_2
|
||||||
|
pixelFormat = PixelFormat.Format8bppIndexed;
|
||||||
|
}
|
||||||
|
else if (dataFormat == 0x0c)
|
||||||
|
{
|
||||||
|
// BGR_16BIT_FORMAT
|
||||||
|
pixelFormat = PixelFormat.Format16bppRgb565;
|
||||||
|
}
|
||||||
|
else if (dataFormat == 0x0d)
|
||||||
|
{
|
||||||
|
// BGRA_16BIT_FORMAT
|
||||||
|
byte[] newBitmapData = new byte[width * height * 4];
|
||||||
|
for (int didx = 0, i = 0; i < height; i++)
|
||||||
|
{
|
||||||
|
for (var j = 0; j < width; j++, didx += 2)
|
||||||
|
{
|
||||||
|
ushort c = (ushort)((bitmapData[didx + 1] << 8) | bitmapData[didx]);
|
||||||
|
|
||||||
|
DxtUtil.ConvertArgb4444ToArgb8888(c, out var a, out var r, out var g, out var b);
|
||||||
|
|
||||||
|
newBitmapData[(j * 4) + (i * width * 4)] = a;
|
||||||
|
newBitmapData[(j * 4) + 1 + (i * width * 4)] = r;
|
||||||
|
newBitmapData[(j * 4) + 2 + (i * width * 4)] = g;
|
||||||
|
newBitmapData[(j * 4) + 3 + (i * width * 4)] = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitmapData = newBitmapData;
|
||||||
|
|
||||||
|
pixelFormat = PixelFormat.Format32bppArgb;
|
||||||
|
}
|
||||||
|
else if (dataFormat == 0x0e)
|
||||||
|
{
|
||||||
|
// BGR_FORMAT
|
||||||
|
pixelFormat = PixelFormat.Format24bppRgb;
|
||||||
|
}
|
||||||
|
else if (dataFormat == 0x10)
|
||||||
|
{
|
||||||
|
// BGRA_FORMAT
|
||||||
|
pixelFormat = PixelFormat.Format32bppArgb;
|
||||||
|
}
|
||||||
|
else if (dataFormat == 0x12)
|
||||||
|
{
|
||||||
|
// BGR_8BIT_FORMAT
|
||||||
|
throw new Exception("Found BGR_8BIT_FORMAT");
|
||||||
|
}
|
||||||
|
else if (dataFormat == 0x16)
|
||||||
|
{
|
||||||
|
// DXT1_FORMAT
|
||||||
|
pixelFormat = PixelFormat.Format32bppArgb;
|
||||||
|
bitmapData = DxtUtil.DecompressDxt1(bitmapData, width, height);
|
||||||
|
}
|
||||||
|
else if (dataFormat == 0x18)
|
||||||
|
{
|
||||||
|
// DXT3_FORMAT
|
||||||
|
pixelFormat = PixelFormat.Format32bppArgb;
|
||||||
|
bitmapData = DxtUtil.DecompressDxt3(bitmapData, width, height);
|
||||||
|
}
|
||||||
|
else if (dataFormat == 0x1a)
|
||||||
|
{
|
||||||
|
// DXT5_FORMAT
|
||||||
|
pixelFormat = PixelFormat.Format32bppArgb;
|
||||||
|
bitmapData = DxtUtil.DecompressDxt5(bitmapData, width, height);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("Found unknown pixel format");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < bitmapData.Length;)
|
||||||
|
{
|
||||||
|
if (pixelFormat == PixelFormat.Format16bppArgb1555)
|
||||||
|
{
|
||||||
|
var a = bitmapData[i] & 0x0f;
|
||||||
|
var r = (bitmapData[i] >> 4) & 0x0f;
|
||||||
|
var g = bitmapData[i + 1] & 0x0f;
|
||||||
|
var b = (bitmapData[i + 1] >> 4) & 0x0f;
|
||||||
|
|
||||||
|
bitmapData[i + 1] = (byte)((a << 4) | b);
|
||||||
|
bitmapData[i + 0] = (byte)((g << 4) | r);
|
||||||
|
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else if (pixelFormat == PixelFormat.Format16bppRgb565)
|
||||||
|
{
|
||||||
|
bitmapData[i] = (byte)((bitmapData[i] & 0xc0) | (bitmapData[i] & 0x3f) >> 1);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
else if (pixelFormat == PixelFormat.Format24bppRgb)
|
||||||
|
{
|
||||||
|
var t = bitmapData[i + 2];
|
||||||
|
bitmapData[i + 2] = bitmapData[i];
|
||||||
|
bitmapData[i] = t;
|
||||||
|
i += 3;
|
||||||
|
}
|
||||||
|
else if (pixelFormat == PixelFormat.Format32bppArgb)
|
||||||
|
{
|
||||||
|
var t = bitmapData[i + 2];
|
||||||
|
bitmapData[i + 2] = bitmapData[i];
|
||||||
|
bitmapData[i] = t;
|
||||||
|
i += 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pixelFormat == PixelFormat.Undefined
|
||||||
|
|| pixelFormat == PixelFormat.Format16bppArgb1555)
|
||||||
|
{
|
||||||
|
// Create DDS file
|
||||||
|
var output = new List<byte>();
|
||||||
|
|
||||||
|
output.AddRange(new byte[]
|
||||||
|
{
|
||||||
|
0x44, 0x44, 0x53, 0x20, 0x7C, 0x00, 0x00, 0x00, 0x07, 0x10, 0x08, 0x00
|
||||||
|
});
|
||||||
|
|
||||||
|
output.AddRange(BitConverter.GetBytes(height));
|
||||||
|
output.AddRange(BitConverter.GetBytes(width));
|
||||||
|
output.AddRange(BitConverter.GetBytes(dataSize));
|
||||||
|
|
||||||
|
output.AddRange(new byte[]
|
||||||
|
{
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00
|
||||||
|
});
|
||||||
|
|
||||||
|
if (pixelFormat == PixelFormat.Format16bppArgb1555)
|
||||||
|
{
|
||||||
|
output.AddRange(new byte[]
|
||||||
|
{
|
||||||
|
0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00,
|
||||||
|
0xF0, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (pixelFormat == PixelFormat.Format16bppRgb555)
|
||||||
|
{
|
||||||
|
output.AddRange(new byte[]
|
||||||
|
{
|
||||||
|
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00,
|
||||||
|
0xE0, 0x07, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
output.AddRange(new byte[]
|
||||||
|
{
|
||||||
|
0x04, 0x00, 0x00, 0x00
|
||||||
|
});
|
||||||
|
|
||||||
|
if (dataFormat == 0x16)
|
||||||
|
{
|
||||||
|
output.AddRange(Encoding.ASCII.GetBytes("DXT1"));
|
||||||
|
}
|
||||||
|
else if (dataFormat == 0x18)
|
||||||
|
{
|
||||||
|
output.AddRange(Encoding.ASCII.GetBytes("DXT3"));
|
||||||
|
}
|
||||||
|
else if (dataFormat == 0x1a)
|
||||||
|
{
|
||||||
|
output.AddRange(Encoding.ASCII.GetBytes("DXT5"));
|
||||||
|
}
|
||||||
|
|
||||||
|
output.AddRange(new byte[]
|
||||||
|
{
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
output.AddRange(bitmapData);
|
||||||
|
|
||||||
|
return output.ToArray();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var b = new Bitmap(width, height, pixelFormat);
|
||||||
|
|
||||||
|
if (pixelFormat == PixelFormat.Format8bppIndexed)
|
||||||
|
{
|
||||||
|
ColorPalette palette = b.Palette;
|
||||||
|
Color[] entries = palette.Entries;
|
||||||
|
for (int i = 0; i < 256; i++)
|
||||||
|
{
|
||||||
|
Color c = Color.FromArgb((byte)i, (byte)i, (byte)i);
|
||||||
|
entries[i] = c;
|
||||||
|
}
|
||||||
|
b.Palette = palette;
|
||||||
|
}
|
||||||
|
|
||||||
|
var boundsRect = new Rectangle(0, 0, width, height);
|
||||||
|
BitmapData bmpData = b.LockBits(boundsRect,
|
||||||
|
ImageLockMode.WriteOnly,
|
||||||
|
b.PixelFormat);
|
||||||
|
|
||||||
|
IntPtr ptr = bmpData.Scan0;
|
||||||
|
|
||||||
|
if (pixelFormat != PixelFormat.Format24bppRgb)
|
||||||
|
{
|
||||||
|
int bytes = bmpData.Stride * b.Height;
|
||||||
|
Marshal.Copy(bitmapData, 0, ptr, bytes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Because things are stupid, we have to pad the lines for 24bit images ourself...
|
||||||
|
for (int i = 0; i < height; i++)
|
||||||
|
{
|
||||||
|
Marshal.Copy(bitmapData, i * width * 3, ptr + (bmpData.Stride * i), width * 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.UnlockBits(bmpData);
|
||||||
|
|
||||||
|
// Split into separate smaller bitmap
|
||||||
|
if (rectInfoList != null)
|
||||||
|
{
|
||||||
|
var rect = new Rectangle(rectInfoList.Value.X, rectInfoList.Value.Y, rectInfoList.Value.W, rectInfoList.Value.H);
|
||||||
|
Bitmap subimage = new Bitmap(rect.Width, rect.Height);
|
||||||
|
|
||||||
|
Console.WriteLine(rect);
|
||||||
|
|
||||||
|
using (Graphics g = Graphics.FromImage(subimage))
|
||||||
|
{
|
||||||
|
g.DrawImage(b, new Rectangle(0, 0, subimage.Width, subimage.Height), rect, GraphicsUnit.Pixel);
|
||||||
|
}
|
||||||
|
|
||||||
|
b = subimage;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageConverter converter = new ImageConverter();
|
||||||
|
return (byte[])converter.ConvertTo(b, typeof(byte[]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ExtractImage(string filename)
|
||||||
|
{
|
||||||
|
using (BinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open)))
|
||||||
|
{
|
||||||
|
var data = ExtractImageCore(reader);
|
||||||
|
|
||||||
|
var ext = "png";
|
||||||
|
if (data[0] == 'D' && data[1] == 'D' && data[2] == 'S' && data[3] == ' ')
|
||||||
|
{
|
||||||
|
ext = "dds";
|
||||||
|
}
|
||||||
|
|
||||||
|
string outputFilename = String.Format("{0}.{1}", Path.Combine(Path.GetDirectoryName(filename), Path.GetFileNameWithoutExtension(filename)), ext);
|
||||||
|
Console.WriteLine(outputFilename);
|
||||||
|
File.WriteAllBytes(outputFilename, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] CreateImageCore(byte[] data, bool requiresEndianFix = false)
|
||||||
|
{
|
||||||
|
var image = Bitmap.FromStream(new MemoryStream(data));
|
||||||
|
var bmpOrig = new Bitmap(image);
|
||||||
|
|
||||||
|
var pixelFormat = image.PixelFormat;
|
||||||
|
|
||||||
|
if (pixelFormat == PixelFormat.Format8bppIndexed)
|
||||||
|
{
|
||||||
|
pixelFormat = PixelFormat.Format32bppArgb;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bmp = new Bitmap(bmpOrig.Width, bmpOrig.Height, pixelFormat);
|
||||||
|
using (Graphics gr = Graphics.FromImage(bmp))
|
||||||
|
{
|
||||||
|
gr.DrawImage(bmpOrig, new Rectangle(0, 0, bmp.Width, bmp.Height));
|
||||||
|
}
|
||||||
|
|
||||||
|
var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly,
|
||||||
|
pixelFormat);
|
||||||
|
|
||||||
|
IntPtr ptr = bmpData.Scan0;
|
||||||
|
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
|
||||||
|
byte[] rawData = new byte[bytes];
|
||||||
|
|
||||||
|
Marshal.Copy(ptr, rawData, 0, bytes);
|
||||||
|
|
||||||
|
bmp.UnlockBits(bmpData);
|
||||||
|
|
||||||
|
var stream = new MemoryStream();
|
||||||
|
using (BinaryWriter writer = new BinaryWriter(stream))
|
||||||
|
{
|
||||||
|
Func<byte[], bool> writeBytes = delegate (byte[] inputData)
|
||||||
|
{
|
||||||
|
writer.Write(requiresEndianFix ? inputData.Reverse().ToArray() : inputData);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
writeBytes(new byte[] { 0x54, 0x58, 0x44, 0x54 });
|
||||||
|
if (requiresEndianFix)
|
||||||
|
{
|
||||||
|
writeBytes(new byte[] { 0x00, 0x01, 0x00, 0x00 });
|
||||||
|
writeBytes(new byte[] { 0x00, 0x01, 0x01, 0x00 });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writeBytes(new byte[] { 0x00, 0x01, 0x02, 0x00 });
|
||||||
|
writeBytes(new byte[] { 0x00, 0x01, 0x02, 0x00 });
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteInt32(writer, (uint)(rawData.Length + 0x40), requiresEndianFix);
|
||||||
|
WriteInt16(writer, (ushort)(bmp.Width), requiresEndianFix);
|
||||||
|
WriteInt16(writer, (ushort)(bmp.Height), requiresEndianFix);
|
||||||
|
|
||||||
|
if (bmp.PixelFormat == PixelFormat.Format24bppRgb)
|
||||||
|
{
|
||||||
|
if (requiresEndianFix)
|
||||||
|
{
|
||||||
|
writeBytes(new byte[] { 0x11, 0x22, 0x10, 0x0E });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writeBytes(new byte[] { 0x11, 0x11, 0x10, 0x0E });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (bmp.PixelFormat == PixelFormat.Format32bppArgb)
|
||||||
|
{
|
||||||
|
if (requiresEndianFix)
|
||||||
|
{
|
||||||
|
writeBytes(new byte[] { 0x11, 0x22, 0x10, 0x10 });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writeBytes(new byte[] { 0x11, 0x11, 0x10, 0x10 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Expected 24bit or 32bit image. Don't know how to handle pixel format {0}", bmp.PixelFormat);
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 0x14; i++)
|
||||||
|
{
|
||||||
|
writer.Write((byte)0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bmp.PixelFormat == PixelFormat.Format24bppRgb)
|
||||||
|
{
|
||||||
|
WriteInt32(writer, 0x01, requiresEndianFix);
|
||||||
|
}
|
||||||
|
else if (bmp.PixelFormat == PixelFormat.Format32bppArgb)
|
||||||
|
{
|
||||||
|
WriteInt32(writer, 0x03, requiresEndianFix);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 0x10; i++)
|
||||||
|
{
|
||||||
|
writer.Write((byte)0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
var bpp = bmp.PixelFormat == PixelFormat.Format32bppArgb ? 4 : 3;
|
||||||
|
for (int i = 0; i < rawData.Length; i += bpp)
|
||||||
|
{
|
||||||
|
var t = rawData[i];
|
||||||
|
rawData[i] = rawData[i + 2];
|
||||||
|
rawData[i + 2] = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.Write(rawData);
|
||||||
|
}
|
||||||
|
|
||||||
|
var outputData = stream.GetBuffer();
|
||||||
|
Array.Resize(ref outputData, bytes + 0x40);
|
||||||
|
return outputData;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CreateImage(string filename)
|
||||||
|
{
|
||||||
|
string outputFilename = String.Format("{0}.tex", Path.Combine(Path.GetDirectoryName(filename), Path.GetFileNameWithoutExtension(filename)));
|
||||||
|
Console.WriteLine(outputFilename);
|
||||||
|
|
||||||
|
var rawData = File.ReadAllBytes(filename);
|
||||||
|
var data = CreateImageCore(rawData, false);
|
||||||
|
File.WriteAllBytes(outputFilename, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length < 1)
|
||||||
|
{
|
||||||
|
Console.WriteLine("usage: {0} input_filename", AppDomain.CurrentDomain.FriendlyName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var filename in args)
|
||||||
|
{
|
||||||
|
var isExtract = false;
|
||||||
|
using (BinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open)))
|
||||||
|
{
|
||||||
|
var magic = reader.ReadBytes(4);
|
||||||
|
if (magic.SequenceEqual(new byte[] { 0x54, 0x58, 0x44, 0x54 })
|
||||||
|
|| magic.SequenceEqual(new byte[] { 0x54, 0x44, 0x58, 0x54 }))
|
||||||
|
{
|
||||||
|
isExtract = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (isExtract)
|
||||||
|
{
|
||||||
|
ExtractImage(filename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CreateImage(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Error occurred: {0}", e.Message);
|
||||||
|
Environment.Exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
gitadora-textool/Properties/AssemblyInfo.cs
Normal file
36
gitadora-textool/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[assembly: AssemblyTitle("gitadora-textool")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("gitadora-textool")]
|
||||||
|
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[assembly: ComVisible(false)]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[assembly: Guid("8c5cc09a-436c-4caa-9213-31a4f351dbdc")]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
[assembly: AssemblyVersion("1.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
55
gitadora-textool/gitadora-textool.csproj
Normal file
55
gitadora-textool/gitadora-textool.csproj
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{8C5CC09A-436C-4CAA-9213-31A4F351DBDC}</ProjectGuid>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<RootNamespace>gitadora_textool</RootNamespace>
|
||||||
|
<AssemblyName>gitadora-textool</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugType>none</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Drawing" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="DxtUtil.cs" />
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="App.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
Loading…
Reference in New Issue
Block a user