New yaz0 system

This commit is contained in:
jdflyer 2023-01-24 21:41:20 -07:00
parent 7cc89e44ba
commit c40f15c9a7
9 changed files with 294 additions and 18 deletions

View File

@ -75,6 +75,7 @@ DOLPHIN_LIB_CC := $(WINE) tools/mwcc_compiler/1.2.5/mwcceppc.exe
FRANK_CC := $(WINE) tools/mwcc_compiler/1.2.5e/mwcceppc.exe FRANK_CC := $(WINE) tools/mwcc_compiler/1.2.5e/mwcceppc.exe
LD := $(WINE_LD) tools/mwcc_compiler/$(MWCC_VERSION)/mwldeppc.exe LD := $(WINE_LD) tools/mwcc_compiler/$(MWCC_VERSION)/mwldeppc.exe
ELF2DOL := $(BUILD_PATH)/elf2dol ELF2DOL := $(BUILD_PATH)/elf2dol
YAZ0 := $(BUILD_PATH)/yaz0.so
PYTHON := python3 PYTHON := python3
ICONV := iconv ICONV := iconv
DOXYGEN := doxygen DOXYGEN := doxygen
@ -151,7 +152,7 @@ clean_rels:
rm -f -d -r $(BUILD_DIR)/rel rm -f -d -r $(BUILD_DIR)/rel
rm -f $(BUILD_PATH)/*.rel rm -f $(BUILD_PATH)/*.rel
tools: $(ELF2DOL) tools: $(ELF2DOL) $(YAZ0)
assets: assets:
@mkdir -p game @mkdir -p game
@ -232,6 +233,7 @@ $(BUILD_DIR)/rel/%.o: rel/%.cpp
# tools # tools
include tools/elf2dol/Makefile include tools/elf2dol/Makefile
include tools/yaz0/Makefile
### Debug Print ### ### Debug Print ###
print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true

View File

@ -2,7 +2,7 @@ import os
import sys import sys
import libarc import libarc
from pathlib import Path from pathlib import Path
from oead import yaz0 import libyaz0
""" """
Extracts the game assets and stores them in the game folder Extracts the game assets and stores them in the game folder
@ -143,7 +143,7 @@ def writeFile(name, data):
if data[0:4] == bytes("Yaz0", "ascii"): if data[0:4] == bytes("Yaz0", "ascii"):
splitName = os.path.splitext(name) splitName = os.path.splitext(name)
name = splitName[0] + ".c" + splitName[1] name = splitName[0] + ".c" + splitName[1]
data = yaz0.decompress(data) data = libyaz0.decompress(data)
extractDef = None extractDef = None
splitName = os.path.splitext(name) splitName = os.path.splitext(name)

View File

@ -1,6 +1,6 @@
import struct import struct
import os import os
import yaz0 import libyaz0
import io import io
from dataclasses import dataclass, field from dataclasses import dataclass, field
@ -149,7 +149,7 @@ def read_section(index, buffer):
def read(buffer): def read(buffer):
# check if the rel is compressed # check if the rel is compressed
if struct.unpack('>I', buffer[:4])[0] == 0x59617A30: if struct.unpack('>I', buffer[:4])[0] == 0x59617A30:
buffer = yaz0.decompress(io.BytesIO(buffer)) buffer = libyaz0.decompress(io.BytesIO(buffer))
header_size = 0x40 header_size = 0x40
header = struct.unpack('>IIIIIIIIIIIIBBBBIII', buffer[:0x40]) header = struct.unpack('>IIIIIIIIIIIIBBBBIII', buffer[:0x40])

View File

@ -0,0 +1 @@
from .yaz0 import *

33
tools/libyaz0/yaz0.py Normal file
View File

@ -0,0 +1,33 @@
import ctypes
import struct
_yaz0lib = ctypes.cdll.LoadLibrary("build/yaz0.so")
if _yaz0lib == None:
print("Error: build/yaz0.so failed to load!")
def decompress(data):
header = data[0:4]
if header != bytearray("Yaz0","ascii"):
return None
decompressedSize = struct.unpack(">I",data[4:8])[0]
compressedDataBuffer = ctypes.c_buffer(bytes(data[16:]))
decompressedDataBuffer = ctypes.c_buffer(bytes(decompressedSize))
decode = _yaz0lib.yaz0_decode
decode.argtypes = [ctypes.c_char_p,ctypes.c_char_p,ctypes.c_int]
decode.restype = ctypes.c_int
decode(compressedDataBuffer,decompressedDataBuffer,decompressedSize)
return bytearray(decompressedDataBuffer)[:-1]
def compress(data):
decompresseddDataBuffer = ctypes.c_buffer(data)
compressedDataBuffer = ctypes.c_buffer(bytes(len(data)*2))
encode = _yaz0lib.yaz0_encode
encode.argtypes = [ctypes.c_char_p,ctypes.c_char_p,ctypes.c_int]
encode.restype = ctypes.c_int
size = encode(decompresseddDataBuffer,compressedDataBuffer,len(data))
header_padding = bytearray(8)
ident = bytearray("Yaz0","ascii")
sizeInt = struct.pack(">I",len(data))
return ident + sizeInt + header_padding + bytearray(compressedDataBuffer)[:size]

View File

@ -3,11 +3,9 @@ import sys
import shutil import shutil
import extract_game_assets import extract_game_assets
from pathlib import Path from pathlib import Path
import hashlib import libyaz0
import struct
import ctypes
import oead
import libarc import libarc
import threading
def getMaxDateFromDir(path): def getMaxDateFromDir(path):
@ -71,7 +69,7 @@ def convertEntry(file, path, destPath, returnData):
if mustBeCompressed == True: if mustBeCompressed == True:
if data == None: if data == None:
data = open(path / file, "rb").read() data = open(path / file, "rb").read()
data = oead.yaz0.compress(data) data = libyaz0.compress(data)
if returnData == True: if returnData == True:
if data == None and returnData == True: if data == None and returnData == True:
data = open(path / file, "rb").read() data = open(path / file, "rb").read()
@ -84,7 +82,6 @@ def convertEntry(file, path, destPath, returnData):
shutil.copy(path / file, destPath / destFileName) shutil.copy(path / file, destPath / destFileName)
return destFileName return destFileName
def copy(path, destPath): def copy(path, destPath):
for file in os.listdir(path): for file in os.listdir(path):
split = os.path.splitext(file) split = os.path.splitext(file)
@ -98,8 +95,6 @@ def copy(path, destPath):
convertEntry(file, path, destPath, False) convertEntry(file, path, destPath, False)
# copy(Path("srcArc"),Path("arcDest"))
aMemRels = """d_a_alldie.rel aMemRels = """d_a_alldie.rel
d_a_andsw2.rel d_a_andsw2.rel
d_a_bd.rel d_a_bd.rel
@ -259,7 +254,7 @@ def copyRelFiles(gamePath, buildPath, aMemList, mMemList):
relSource = open(fullPath, "rb") relSource = open(fullPath, "rb")
data = relSource.read() data = relSource.read()
relSource.close() relSource.close()
data = oead.yaz0.compress(data) data = libyaz0.compress(data)
relNew = open( relNew = open(
buildPath / "dolzel2/game/files/rel/Final/Release" / file, "wb" buildPath / "dolzel2/game/files/rel/Final/Release" / file, "wb"
) )
@ -290,14 +285,14 @@ def copyRelFiles(gamePath, buildPath, aMemList, mMemList):
if str(rel).find(rel2) != -1: if str(rel).find(rel2) != -1:
sourceRel = open(rel, "rb").read() sourceRel = open(rel, "rb").read()
open(buildPath / "RELS.arc/rels/amem/" / rel2, "wb").write( open(buildPath / "RELS.arc/rels/amem/" / rel2, "wb").write(
oead.yaz0.compress(sourceRel) libyaz0.compress(sourceRel)
) )
break break
for rel2 in mMemRels.splitlines(): for rel2 in mMemRels.splitlines():
if str(rel).find(rel2) != -1: if str(rel).find(rel2) != -1:
sourceRel = open(rel, "rb").read() sourceRel = open(rel, "rb").read()
open(buildPath / "RELS.arc/rels/mmem/" / rel2, "wb").write( open(buildPath / "RELS.arc/rels/mmem/" / rel2, "wb").write(
oead.yaz0.compress(sourceRel) libyaz0.compress(sourceRel)
) )
break break

View File

@ -1,6 +1,5 @@
rich rich
click click
yaz0
GitPython GitPython
hexdump hexdump
colorama colorama
@ -8,5 +7,4 @@ ansiwrap
watchdog watchdog
python-Levenshtein python-Levenshtein
cxxfilt cxxfilt
oead
pyelftools pyelftools

7
tools/yaz0/Makefile Normal file
View File

@ -0,0 +1,7 @@
YAZ0_CC := cc
YAZ0_CFLAGS := -fPIC -shared -O3 -Wall -s
$(YAZ0): include tools/yaz0/Makefile
@echo [tools] building yaz0.so
@$(YAZ0_CC) $(YAZ0_CFLAGS) -o $(YAZ0) tools/yaz0/yaz0.c

240
tools/yaz0/yaz0.c Normal file
View File

@ -0,0 +1,240 @@
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
//yaz0 implementation by BluRose
//patched by Prakxo
// decoder implementation by thakis of http://www.amnoid.de
// src points to the yaz0 source data (to the "real" source data, not at the header!)
// dst points to a buffer uncompressedSize bytes large (you get uncompressedSize from
// the second 4 bytes in the Yaz0 header).
int yaz0_decode(uint8_t* src, uint8_t* dst, int uncompressedSize)
{
int srcPlace = 0, dstPlace = 0; // current read/write positions
unsigned int validBitCount = 0; // number of valid bits left in "code" byte
uint8_t currCodeByte;
while (dstPlace < uncompressedSize)
{
// read new "code" byte if the current one is used up
if (validBitCount == 0)
{
currCodeByte = src[srcPlace];
++srcPlace;
validBitCount = 8;
}
if ((currCodeByte & 0x80) != 0)
{
// straight copy
dst[dstPlace] = src[srcPlace];
dstPlace++;
srcPlace++;
}
else
{
// RLE part
uint8_t byte1 = src[srcPlace];
uint8_t byte2 = src[srcPlace + 1];
srcPlace += 2;
unsigned int dist = ((byte1 & 0xF) << 8) | byte2;
unsigned int copySource = dstPlace - (dist + 1);
unsigned int numBytes = byte1 >> 4;
if (numBytes == 0)
{
numBytes = src[srcPlace] + 0x12;
srcPlace++;
}
else
{
numBytes += 2;
}
// copy run
for (unsigned int i = 0; i < numBytes; ++i)
{
dst[dstPlace] = dst[copySource];
copySource++;
dstPlace++;
}
}
// use next bit from "code" byte
currCodeByte <<= 1;
validBitCount -= 1;
}
return 0;
}
// encoder implementation by shevious, with bug fixes by notwa
typedef uint32_t uint32_t;
typedef uint8_t uint8_t;
#define MAX_RUNLEN (0xFF + 0x12)
// simple and straight encoding scheme for Yaz0
static uint32_t simpleEnc(uint8_t *src, int size, int pos, uint32_t *pMatchPos)
{
int numBytes = 1;
int matchPos = 0;
int startPos = pos - 0x1000;
int end = size - pos;
if (startPos < 0)
startPos = 0;
// maximum runlength for 3 byte encoding
if (end > MAX_RUNLEN)
end = MAX_RUNLEN;
for (int i = startPos; i < pos; i++)
{
int j;
for (j = 0; j < end; j++)
{
if (src[i + j] != src[j + pos])
break;
}
if (j > numBytes)
{
numBytes = j;
matchPos = i;
}
}
*pMatchPos = matchPos;
if (numBytes == 2)
numBytes = 1;
return numBytes;
}
// a lookahead encoding scheme for ngc Yaz0
static uint32_t nintendoEnc(uint8_t *src, int size, int pos, uint32_t *pMatchPos)
{
uint32_t numBytes = 1;
static uint32_t numBytes1;
static uint32_t matchPos;
static int prevFlag = 0;
// if prevFlag is set, it means that the previous position
// was determined by look-ahead try.
// so just use it. this is not the best optimization,
// but nintendo's choice for speed.
if (prevFlag == 1)
{
*pMatchPos = matchPos;
prevFlag = 0;
return numBytes1;
}
prevFlag = 0;
numBytes = simpleEnc(src, size, pos, &matchPos);
*pMatchPos = matchPos;
// if this position is RLE encoded, then compare to copying 1 byte and next position(pos+1) encoding
if (numBytes >= 3)
{
numBytes1 = simpleEnc(src, size, pos + 1, &matchPos);
// if the next position encoding is +2 longer than current position, choose it.
// this does not guarantee the best optimization, but fairly good optimization with speed.
if (numBytes1 >= numBytes + 2)
{
numBytes = 1;
prevFlag = 1;
}
}
return numBytes;
}
int yaz0_encode(uint8_t *src, uint8_t *dst, int srcSize)
{
int srcPos = 0;
int dstPos = 0;
int bufPos = 0;
uint8_t buf[24]; // 8 codes * 3 bytes maximum
uint32_t validBitCount = 0; // number of valid bits left in "code" byte
uint8_t currCodeByte = 0; // a bitfield, set bits meaning copy, unset meaning RLE
while (srcPos < srcSize)
{
uint32_t numBytes;
uint32_t matchPos;
numBytes = nintendoEnc(src, srcSize, srcPos, &matchPos);
if (numBytes < 3)
{
// straight copy
buf[bufPos] = src[srcPos];
bufPos++;
srcPos++;
//set flag for straight copy
currCodeByte |= (0x80 >> validBitCount);
}
else
{
//RLE part
uint32_t dist = srcPos - matchPos - 1;
uint8_t byte1, byte2, byte3;
if (numBytes >= 0x12) // 3 byte encoding
{
byte1 = 0 | (dist >> 8);
byte2 = dist & 0xFF;
buf[bufPos++] = byte1;
buf[bufPos++] = byte2;
// maximum runlength for 3 byte encoding
if (numBytes > MAX_RUNLEN)
numBytes = MAX_RUNLEN;
byte3 = numBytes - 0x12;
buf[bufPos++] = byte3;
}
else // 2 byte encoding
{
byte1 = ((numBytes - 2) << 4) | (dist >> 8);
byte2 = dist & 0xFF;
buf[bufPos++] = byte1;
buf[bufPos++] = byte2;
}
srcPos += numBytes;
}
validBitCount++;
// write eight codes
if (validBitCount == 8)
{
dst[dstPos++] = currCodeByte;
for (int j = 0; j < bufPos; j++)
dst[dstPos++] = buf[j];
currCodeByte = 0;
validBitCount = 0;
bufPos = 0;
}
}
if (validBitCount > 0)
{
dst[dstPos++] = currCodeByte;
for (int j = 0; j < bufPos; j++)
dst[dstPos++] = buf[j];
currCodeByte = 0;
validBitCount = 0;
bufPos = 0;
}
return dstPos;
}