Skip to content

Commit

Permalink
working wta/wtp/wtb export
Browse files Browse the repository at this point in the history
  • Loading branch information
ArthurHeitmann committed Jul 22, 2024
1 parent d640f98 commit 417672c
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 89 deletions.
9 changes: 8 additions & 1 deletion lib/fileTypeUtils/wta/wtaExtractor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Future<List<String>> extractWta(String wtaPath, String? wtpPath, bool isWtb) asy
var wtpFile = await File(isWtb ? wtaPath : wtpPath!).open();
try {
for (int i = 0; i < wta.textureOffsets.length; i++) {
var texturePath = join(extractDir, "${i}_${wta.textureIdx[i].toRadixString(16).padLeft(8, "0")}.dds");
var texturePath = getWtaTexturePath(wta, i, extractDir);
texturePaths.add(texturePath);
await wtpFile.setPosition(wta.textureOffsets[i]);
var textureBytes = await wtpFile.read(wta.textureSizes[i]);
Expand All @@ -24,3 +24,10 @@ Future<List<String>> extractWta(String wtaPath, String? wtpPath, bool isWtb) asy
}
return texturePaths;
}

String getWtaTexturePath(WtaFile wta, int i, String extractDir) {
String idStr = "";
if (wta.textureIdx != null)
idStr = "_${wta.textureIdx![i].toRadixString(16).padLeft(8, "0")}";
return join(extractDir, "$i$idStr.dds");
}
109 changes: 41 additions & 68 deletions lib/fileTypeUtils/wta/wtaReader.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@

import '../../stateManagement/events/statusInfo.dart';
import '../../utils/utils.dart';
import '../utils/ByteDataWrapper.dart';

class WtaFileHeader {
static const size = 32;
String id;
int unknown;
int numTex;
int offsetTextureOffsets;
int offsetTextureSizes;
int offsetTextureFlags;
int offsetTextureIdx;
int offsetTextureInfo;
int offsetEnd;

WtaFileHeader.empty() :
id = "WTB\x00",
unknown = 1,
numTex = 0,
offsetTextureOffsets = 0,
offsetTextureSizes = 0,
offsetTextureFlags = 0,
offsetTextureIdx = 0,
offsetEnd = 0;

WtaFileHeader.read(ByteDataWrapper bytes) :
id = bytes.readString(4),
Expand All @@ -21,7 +31,7 @@ class WtaFileHeader {
offsetTextureSizes = bytes.readUint32(),
offsetTextureFlags = bytes.readUint32(),
offsetTextureIdx = bytes.readUint32(),
offsetTextureInfo = bytes.readUint32();
offsetEnd = bytes.readUint32();

void write(ByteDataWrapper bytes) {
bytes.writeString(id);
Expand All @@ -31,54 +41,14 @@ class WtaFileHeader {
bytes.writeUint32(offsetTextureSizes);
bytes.writeUint32(offsetTextureFlags);
bytes.writeUint32(offsetTextureIdx);
bytes.writeUint32(offsetTextureInfo);
bytes.writeUint32(offsetEnd);
}
}

class WtaFileTextureInfo {
late int format;
late List<int> data;

WtaFileTextureInfo(this.format, this.data);

WtaFileTextureInfo.read(ByteDataWrapper bytes) :
format = bytes.readUint32(),
data = bytes.readUint32List(4);

static Future<WtaFileTextureInfo> fromDds(String ddsPath) async {
var dds = await ByteDataWrapper.fromFile(ddsPath);
dds.position = 84;
var dxt = dds.readString(4);
dds.position = 112;
var cube = dds.readUint32();
if (!const ["DXT1", "DXT3", "DXT5"].contains(dxt))
messageLog.add("Warning: $ddsPath uses unknown DDS format $dxt. This may not work.");
var isCube = cube == 0xFE00;

int format = 0;
List<int> data = [3, isCube ? 4 : 0, 1, 0];
switch (dxt) {
case "DXT1":
format = 71;
break;
case "DXT3":
format = 74;
break;
case "DXT5":
format = 77;
break;
default:
format = 87;
break;
}

return WtaFileTextureInfo(format, data);
}

void write(ByteDataWrapper bytes) {
bytes.writeUint32(format);
for (var d in data)
bytes.writeUint32(d);
int getFileEnd() {
if (offsetEnd != 0)
return offsetEnd;
else
return offsetTextureIdx + numTex * 4;
}
}

Expand All @@ -87,11 +57,12 @@ class WtaFile {
late List<int> textureOffsets;
late List<int> textureSizes;
late List<int> textureFlags;
late List<int> textureIdx;
late List<WtaFileTextureInfo> textureInfo;
List<int>? textureIdx;

static const int albedoFlag = 0x26000020;
static const int noAlbedoFlag = 0x22000020;

WtaFile(this.header, this.textureOffsets, this.textureSizes, this.textureFlags, this.textureIdx);

WtaFile.read(ByteDataWrapper bytes) {
header = WtaFileHeader.read(bytes);
Expand All @@ -101,13 +72,10 @@ class WtaFile {
textureSizes = bytes.readUint32List(header.numTex);
bytes.position = header.offsetTextureFlags;
textureFlags = bytes.readUint32List(header.numTex);
bytes.position = header.offsetTextureIdx;
textureIdx = bytes.readUint32List(header.numTex);
bytes.position = header.offsetTextureInfo;
textureInfo = List.generate(
header.numTex,
(i) => WtaFileTextureInfo.read(bytes)
);
if (header.offsetTextureIdx > 0) {
bytes.position = header.offsetTextureIdx;
textureIdx = bytes.readUint32List(header.numTex);
}
}

static Future<WtaFile> readFromFile(String path) async {
Expand All @@ -116,7 +84,8 @@ class WtaFile {
}

Future<void> writeToFile(String path) async {
var fileSize = header.offsetTextureInfo + textureInfo.length * 0x14;
var fileSize = header.getFileEnd();
fileSize = alignTo(fileSize, 32);
var bytes = ByteDataWrapper.allocate(fileSize);
header.write(bytes);

Expand All @@ -132,14 +101,12 @@ class WtaFile {
for (var i = 0; i < textureFlags.length; i++)
bytes.writeUint32(textureFlags[i]);

bytes.position = header.offsetTextureIdx;
for (var i = 0; i < textureIdx.length; i++)
bytes.writeUint32(textureIdx[i]);
if (textureIdx != null) {
bytes.position = header.offsetTextureIdx;
for (var i = 0; i < textureIdx!.length; i++)
bytes.writeUint32(textureIdx![i]);
}

bytes.position = header.offsetTextureInfo;
for (var i = 0; i < textureInfo.length; i++)
textureInfo[i].write(bytes);

await bytes.save(path);
}

Expand All @@ -148,7 +115,13 @@ class WtaFile {
header.offsetTextureOffsets = 0x20;
header.offsetTextureSizes = alignTo(header.offsetTextureOffsets + textureOffsets.length * 4, 32);
header.offsetTextureFlags = alignTo(header.offsetTextureSizes + textureSizes.length * 4, 32);
header.offsetTextureIdx = alignTo(header.offsetTextureFlags + textureFlags.length * 4, 32);
header.offsetTextureInfo = alignTo(header.offsetTextureIdx + textureIdx.length * 4, 32);
if (textureIdx != null) {
header.offsetTextureIdx = alignTo(header.offsetTextureFlags + textureFlags.length * 4, 32);
header.offsetEnd = 0;
}
else {
header.offsetTextureIdx = 0;
header.offsetEnd = alignTo(header.getFileEnd(), 32);
}
}
}
3 changes: 1 addition & 2 deletions lib/stateManagement/hierarchy/types/DatHierarchyEntry.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,8 @@ class DatHierarchyEntry extends ExtractableHierarchyEntry {
List<Future<void>> futures = [];
datFilePaths ??= await getDatFileList(extractedPath);
RubyScriptGroupHierarchyEntry? rubyScriptGroup;
const supportedFileEndings = { ".pak", "_scp.bin", ".tmd", ".smd", ".mcd", ".ftb", ".bnk", ".bxm", ".wta", ".wtb", ".est", ".sst", ...datExtensions };
const supportedFileEndings = { ".pak", "_scp.bin", ".tmd", ".smd", ".mcd", ".ftb", ".bnk", ".wta", ".wtb", ".est", ".sst", ...bxmExtensions, ...datExtensions };
for (var file in datFilePaths) {
print(file);
if (supportedFileEndings.every((ending) => !file.endsWith(ending)))
continue;
int existingChildI = existingChildren.indexWhere((entry) => (entry as FileHierarchyEntry).path == file);
Expand Down
3 changes: 1 addition & 2 deletions lib/stateManagement/openFiles/types/FtbFileData.dart
Original file line number Diff line number Diff line change
Expand Up @@ -281,9 +281,8 @@ class FtbData extends ChangeNotifier {
// add new idx, flag info entries
for (int i = wtaFile.header.numTex; i < textureBatches.length; i++) {
wtaFile.header.numTex++;
wtaFile.textureIdx.add(randomId());
wtaFile.textureIdx!.add(randomId());
wtaFile.textureFlags.add(wtaFile.textureFlags.last);
wtaFile.textureInfo.add(wtaFile.textureInfo.last);
}
wtaFile.updateHeader();
await wtaFile.writeToFile(wtaPath);
Expand Down
2 changes: 1 addition & 1 deletion lib/stateManagement/openFiles/types/McdFileData.dart
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ class McdData extends _McdFilePart {

// glyphs
var wta = await WtaFile.readFromFile(textureWtaPath!.value);
var texId = wta.textureIdx.first;
var texId = wta.textureIdx!.first;
var texSize = await getDdsFileSize(textureWtpPath!.value);
var exportGlyphs = usedSymbols.map((usedSym) {
var sym = usedFonts[usedSym.fontId]!.supportedSymbols[usedSym.code]!;
Expand Down
35 changes: 21 additions & 14 deletions lib/stateManagement/openFiles/types/WtaWtpData.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:path/path.dart';

import '../../../fileTypeUtils/wta/wtaExtractor.dart';
import '../../../fileTypeUtils/wta/wtaReader.dart';
import '../../../utils/Disposable.dart';
import '../../../utils/utils.dart';
Expand Down Expand Up @@ -109,13 +110,13 @@ class WtaWtpData extends OpenFileData {

class WtaTextureEntry with HasUuid, Undoable implements Disposable {
final OpenFileId file;
final HexProp id;
final HexProp? id;
final StringProp path;
final BoolProp? isAlbedo;
final HexProp? flag;

WtaTextureEntry(this.file, this.id, this.path, { this.isAlbedo, this.flag }) {
id.addListener(_onPropChange);
id?.addListener(_onPropChange);
path.addListener(_onPropChange);
isAlbedo?.addListener(_onPropChange);
flag?.addListener(_onPropChange);
Expand All @@ -127,7 +128,7 @@ class WtaTextureEntry with HasUuid, Undoable implements Disposable {

@override
void dispose() {
id.dispose();
id?.dispose();
path.dispose();
isAlbedo?.dispose();
flag?.dispose();
Expand All @@ -141,7 +142,7 @@ class WtaTextureEntry with HasUuid, Undoable implements Disposable {
Undoable takeSnapshot() {
var snap = WtaTextureEntry(
file,
id.takeSnapshot() as HexProp,
id?.takeSnapshot() as HexProp?,
path.takeSnapshot() as StringProp,
isAlbedo: isAlbedo?.takeSnapshot() as BoolProp?,
flag: flag?.takeSnapshot() as HexProp?,
Expand All @@ -153,7 +154,7 @@ class WtaTextureEntry with HasUuid, Undoable implements Disposable {
@override
void restoreWith(Undoable snapshot) {
var snap = snapshot as WtaTextureEntry;
id.restoreWith(snap.id);
id?.restoreWith(snap.id!);
path.restoreWith(snap.path);
isAlbedo?.restoreWith(snap.isAlbedo!);
flag?.restoreWith(snap.flag!);
Expand All @@ -180,7 +181,8 @@ class WtaWtpTextures with HasUuid, Undoable implements Disposable {
try {
for (int i = 0; i < wta.textureOffsets.length; i++) {
messageLog.add("Extracting texture ${i + 1}/${wta.textureOffsets.length}");
var texturePath = join(extractDir, "${i}_${wta.textureIdx[i].toRadixString(16).padLeft(8, "0")}.dds");
// var texturePath = join(extractDir, "${i}_${wta.textureIdx[i].toRadixString(16).padLeft(8, "0")}.dds");
var texturePath = getWtaTexturePath(wta, i, extractDir);
await wtpFile.setPosition(wta.textureOffsets[i]);
var textureBytes = await wtpFile.read(wta.textureSizes[i]);
await File(texturePath).writeAsBytes(textureBytes);
Expand All @@ -192,7 +194,9 @@ class WtaWtpTextures with HasUuid, Undoable implements Disposable {
flag = HexProp(wta.textureFlags[i], fileId: file);
textures.add(WtaTextureEntry(
file,
HexProp(wta.textureIdx[i], fileId: file),
wta.textureIdx != null
? HexProp(wta.textureIdx![i], fileId: file)
: null,
StringProp(texturePath, fileId: file),
isAlbedo: isAlbedo,
flag: flag,
Expand All @@ -213,7 +217,15 @@ class WtaWtpTextures with HasUuid, Undoable implements Disposable {
Future<void> save() async {
var wta = await WtaFile.readFromFile(wtaPath);

wta.textureIdx = List.generate(textures.length, (index) => textures[index].id.value);
if (wta.textureIdx != null) {
if (!textures.every((e) => e.id != null)) {
showToast("Mismatch: WTA has texture indices, but some textures are missing indices!");
throw Exception("Mismatch: WTA has texture indices, but some textures are missing indices!");
}
wta.textureIdx = List.generate(textures.length, (index) => textures[index].id!.value);
}
else
wta.textureIdx = null;
wta.textureOffsets = List.filled(textures.length, -1);
wta.textureFlags = List.generate(
textures.length,
Expand All @@ -225,15 +237,10 @@ class WtaWtpTextures with HasUuid, Undoable implements Disposable {
return await File(e.path.value).length();
}));

// update texture infos
wta.textureInfo = await Future.wait(textures.map((e) async {
return await WtaFileTextureInfo.fromDds(e.path.value);
}));

wta.updateHeader();

// update offsets (4096 byte alignment)
int offset = isWtb ? alignTo(wta.header.offsetTextureInfo + wta.textureInfo.length * 20, 32) : 0;
int offset = isWtb ? alignTo(wta.header.getFileEnd(), 32) : 0;
for (int i = 0; i < wta.textureOffsets.length; i++) {
wta.textureOffsets[i] = offset;
offset += wta.textureSizes[i];
Expand Down
4 changes: 3 additions & 1 deletion lib/widgets/filesView/types/wtaWtpEditor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ class _TexturesTableConfig with CustomTableConfig {
return RowConfig(
key: Key(textures[index].uuid),
cells: [
PropCellConfig(prop: textures[index].id),
textures[index].id != null
? PropCellConfig(prop: textures[index].id!)
: CustomWidgetCellConfig(const SizedBox.shrink()),
PropCellConfig(prop: textures[index].path),
CustomWidgetCellConfig(IconButton(
icon: const Icon(Icons.folder, size: 20),
Expand Down

0 comments on commit 417672c

Please sign in to comment.