Last active
August 22, 2023 22:45
-
-
Save uyjulian/1e22b0d4c38ffe1e9cb1f90f1aec7df3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Better code have been released since then; see Trails Research Group: https://github.com/Trails-Research-Group | |
fu = require"file_util" | |
--struct = require"struct" | |
--serpent = require"serpent" | |
bit = require"bit" | |
ffi = require"ffi" | |
jit = require"jit" | |
jit.off() | |
ffi.cdef [[ | |
void *malloc( size_t numbytes ); | |
int free( void *region ); | |
]] | |
io.stdout\setvbuf("no") | |
io.stderr\setvbuf("no") | |
new_array = (ctype, size) -> ffi.gc(ffi.cast(ctype .. "*", ffi.C.malloc(ffi.sizeof(ctype) * size)), ffi.C.free) | |
uint32p_t = ffi.typeof("uint32_t*") | |
uint16p_t = ffi.typeof("uint16_t*") | |
uint8p_t = ffi.typeof("uint8_t*") | |
uint8pp_t = ffi.typeof("uint8_t**") | |
uint64_t = ffi.typeof("uint64_t") | |
uint32_t = ffi.typeof("uint32_t") | |
uint16_t = ffi.typeof("uint16_t") | |
uint8_t = ffi.typeof("uint8_t") | |
int64_t = ffi.typeof("uint64_t") | |
int32_t = ffi.typeof("uint32_t") | |
int16_t = ffi.typeof("uint16_t") | |
int8_t = ffi.typeof("uint8_t") | |
ptr2number = (offs, num, unsigned) -> | |
kind = nil | |
if num == 1 | |
if unsigned | |
kind = uint8_t | |
else | |
kind = int8_t | |
elseif num == 2 | |
if unsigned | |
kind = uint16_t | |
else | |
kind = int16_t | |
elseif num == 4 | |
if unsigned | |
kind = uint32_t | |
else | |
kind = int32_t | |
elseif num == 8 | |
if unsigned | |
kind = uint64_t | |
else | |
kind = int64_t | |
else | |
print("unknown num") | |
fillmeup = kind(0) | |
for i = 1, num | |
fillmeup = kind(bit.bor(fillmeup, bit.lshift(ffi.cast(uint8p_t, offs + (num - i))[0], (num - i) * 8))) | |
return fillmeup | |
bswap16 = (n) -> | |
bit.band(bit.bor(bit.rshift(n, 8), bit.lshift(n, 8)), 0xffff) | |
bswap32 = (n) -> | |
bit.band(bit.bor(bit.band(bit.rshift(n, 24), 0xff), bit.band(bit.lshift(n, 8), 0xff0000), bit.band(bit.rshift(n, 8), 0xff00), bit.band(bit.lshift(n, 24), 0xff000000)), 0xffffffff) | |
SWAP2 = (v) -> uint16_t(bit.bor(bit.lshift(uint32_t(v), 8), bit.rshift(uint16_t(v), 8))) | |
LOWORD = (v) -> uint16_t(bit.band(v, 0xFFFF)) | |
HIWORD = (v) -> uint16_t(bit.band(bit.rshift(v, 16), 0xFFFF)) | |
min = (x, y) -> (((x) < (y)) and (x) or (y)) | |
in1 = fu.readfile(arg[1]) | |
unknownFunction = (pbInput, width, height, bpp) -> | |
inputBuf = pbInput | |
if bpp == 16 | |
width /= 2 | |
elseif bpp == 8 | |
width /= 4 | |
outputBuf = new_array("uint32_t", width * height) | |
ffi.fill(outputBuf, width * height * 4, 0xFE) | |
h = 0 | |
while h < height | |
w = 0 | |
while w < width | |
v11 = w + width * h | |
p = outputBuf + v11 | |
count = 8 | |
while count ~= 0 | |
ffi.copy(p, inputBuf, 0x10) | |
p += width * 4 | |
inputBuf += 0x10 | |
count -= 1 | |
w += 4 | |
h += 8 | |
return outputBuf | |
unknownFunction2 = (pPalette, pvEncodedIndex, width, height, paletteCount) -> | |
size = bit.rshift(width * height, 7) | |
v16 = pvEncodedIndex | |
if bit.rshift(width * height, 8) <= 1 | |
v16 += 1 | |
else | |
v16 += bit.arshift(size, 1) | |
outputBuf = new_array("uint8_t", width * height) | |
dst = outputBuf | |
v24 = pvEncodedIndex | |
v18 = new_array("uint8_t", size) | |
pIndexPointer = new_array("uint8_t*", size) | |
colorIndex = 0 | |
for i = 0, tonumber(paletteCount - 1) | |
if bit.band(pPalette[i], 0xFF000000) == 0 | |
colorIndex = i | |
break | |
v22 = 0 | |
for i = 0, tonumber(size - 1) | |
if v22 ~= 0 | |
v19 = bit.band(v24[0], 0xF) | |
if v19 ~= 0 | |
v18[i] = v19 + 1 | |
else | |
v18[i] = 0 | |
v22 = 0 | |
v24 += 1 | |
else | |
v19 = bit.rshift(bit.band(v24[0], 0xF0), 4) | |
if v19 ~= 0 | |
v18[i] = v19 + 1 | |
else | |
v18[i] = 0 | |
v22 = 1 | |
pIndexPointer[i] = v16 | |
v16 += v18[i] | |
v24 = pvEncodedIndex | |
v14 = v16[0] | |
v16 += 1 | |
v23 = v16 | |
for i = 0, tonumber(size - 1) | |
if v18[i] ~= 0 | |
if v14 ~= 0 | |
if v14 == 1 | |
for j = 0, 128 - 1 | |
dst[0] = pIndexPointer[i][v23[0]] | |
v23 += 1 | |
dst += 1 | |
else | |
if v23[0] ~= 0 | |
if v23[0] == 1 | |
v23 += 1 | |
v9 = 0 | |
for k = 0, 16 - 1 | |
v10 = 0 | |
val = pIndexPointer[i][k] | |
while v23[0] ~= 255 | |
if v9 ~= 0 | |
ffi.fill(dst + v10, v23[0], val) | |
v10 += v23[0] | |
v9 = 0 | |
else | |
v10 += v23[0] | |
v9 = 1 | |
v23 += 1 | |
v23 += 1 | |
dst += 128 | |
else | |
v23 += 1 | |
for l = 0, 128 - 1 | |
dst[0] = pIndexPointer[i][v23[0]] | |
v23 += 1 | |
dst += 1 | |
else | |
v21 = 0 | |
for m = 0, 128 - 1 | |
if v21 ~= 0 | |
dst[0] = pIndexPointer[i][bit.band(v23[0], 0xF)] | |
v23 += 1 | |
v21 = 0 | |
else | |
dst[0] = pIndexPointer[i][bit.rshift(bit.band(v23[0], 0xF0), 4)] | |
v21 = 1 | |
dst += 1 | |
else | |
ffi.fill(dst, 0x80, colorIndex) | |
dst += 128 | |
return outputBuf | |
uncompressBlock = (buffer, output, baseoutput, outputsize) -> | |
bufferptr = buffer | |
outputptr = output | |
bufferptr += 1 | |
blockSize = uint32_t(0) | |
bitCount = uint32_t(8) | |
bits = ptr2number(bufferptr, 1, true) | |
bufferptr += 1 | |
shouldBreak = false | |
getBit = -> | |
xbit = uint32_t(0) | |
if bitCount == 0 | |
bits = ptr2number(bufferptr, 2, true) | |
bufferptr += 2 | |
bitCount = uint32_t(16) | |
bitCount -= 1 | |
xbit = bit.band(bits, 1) | |
bits = bit.rshift(bits, 1) | |
return xbit | |
getBits = (bitCount) -> | |
xbits = uint32_t(0) | |
while bitCount ~= 0 | |
xbits = bit.bor(bit.lshift(xbits, 1), getBit()) | |
bitCount -= 1 | |
return xbits | |
jit.off(getBit) | |
jit.off(getBits) | |
while true | |
backwardOffset = uint32_t(0) | |
while getBit() == 0 | |
outputptr[0] = bufferptr[0] | |
outputptr += 1 | |
bufferptr += 1 | |
if getBit() == 0 | |
backwardOffset = bufferptr[0] | |
bufferptr += 1 | |
else | |
backwardOffset = bit.lshift(getBits(5), 8) | |
backwardOffset = bit.bor(backwardOffset, bufferptr[0]) | |
bufferptr += 1 | |
if backwardOffset == 0 | |
break | |
elseif backwardOffset == 1 | |
length = uint32_t(0) | |
if getBit() == 0 | |
length = getBits(4) | |
else | |
length = bit.lshift(getBits(4), 8) | |
length = bit.bor(length, bufferptr[0]) | |
bufferptr += 1 | |
length += 0xE | |
ffi.fill(outputptr, length, bufferptr[0]) | |
bufferptr += 1 | |
outputptr += length | |
continue | |
bytesDuplicate = uint32_t(0) | |
cond = true | |
while cond | |
if getBit() ~= 0 | |
bytesDuplicate = uint32_t(2) | |
break | |
if getBit() ~= 0 | |
bytesDuplicate = uint32_t(3) | |
break | |
if getBit() ~= 0 | |
bytesDuplicate = uint32_t(4) | |
break | |
if getBit() ~= 0 | |
bytesDuplicate = uint32_t(5) | |
break | |
if getBit() == 0 | |
bytesDuplicate = bufferptr[0] + 0xE | |
bufferptr += 1 | |
else | |
bytesDuplicate = getBits(3) + 6 | |
cond = false | |
if (uint32_t(outputptr - baseoutput) > 0x3FFF0) or (uint32_t(outputptr - baseoutput) > outputsize) | |
shouldBreak = true | |
break | |
buf = outputptr - backwardOffset | |
while bytesDuplicate ~= 0 | |
outputptr[0] = buf[0] | |
outputptr += 1 | |
buf += 1 | |
bytesDuplicate -= 1 | |
return bufferptr, outputptr, shouldBreak | |
decompress_FALCOM3 = (inbuffer) -> | |
inbufferptr = inbuffer | |
compressed_size = ptr2number(inbufferptr, 4, true) | |
inbufferptr += 4 | |
uncompressed_size = ptr2number(inbufferptr, 4, true) | |
inbufferptr += 4 | |
chunks = ptr2number(inbufferptr, 4, true) | |
inbufferptr += 4 | |
buffer = new_array("uint8_t", compressed_size + 0x10) | |
ffi.fill(buffer, compressed_size + 0x10, 0) | |
ffi.copy(buffer, inbufferptr, compressed_size) | |
bufferptr = buffer | |
output = new_array("uint8_t", uncompressed_size) | |
ffi.fill(output, uncompressed_size, 0) | |
outputptr = output | |
for i = 1, tonumber(chunks) | |
size = ptr2number(bufferptr, 2, true) | |
bufferptr += 2 | |
num1, num2, shouldBreak = uncompressBlock(bufferptr, outputptr, output, uncompressed_size) | |
if shouldBreak | |
break | |
bufferptr = num1 | |
outputptr = num2 | |
flag = ptr2number(bufferptr, 1, true) | |
bufferptr += 1 | |
if flag == 0 or uint32_t(outputptr - output) >= uncompressed_size | |
break | |
return output, uncompressed_size | |
in1a = new_array("uint8_t", #in1) | |
ffi.copy(in1a, in1) | |
xxhash = require"xxhash" | |
decodeITP = (inData) -> | |
indexBuffer = nil | |
width = uint32_t(0) | |
height = uint32_t(0) | |
paletteBuffer = new_array("uint32_t", 256) | |
ffi.fill(paletteBuffer, ffi.sizeof(uint32_t) * 256, 0) | |
paletteCount = uint32_t(0) | |
header = ptr2number(inData, 2, true) | |
inData += 4 -- 2 for padding | |
if header == 0x03EE | |
origSize = ptr2number(inData, 4, true) | |
inData += 4 | |
ccpiMagic = ptr2number(inData, 4, true) | |
inData += 4 | |
if ccpiMagic ~= 1229996867 --CCPI | |
error("CCPI not detected") | |
ccpiVersion = ptr2number(inData, 2, true) | |
inData += 2 | |
paletteCount = ptr2number(inData, 2, true) | |
inData += 2 | |
ccpiColumnStrideBit = ptr2number(inData, 1, true) | |
inData += 1 | |
ccpiRowStrideBit = ptr2number(inData, 1, true) | |
inData += 1 | |
width = ptr2number(inData, 2, true) | |
inData += 2 | |
height = ptr2number(inData, 2, true) | |
inData += 2 | |
ccpiUnknown = ptr2number(inData, 1, true) | |
inData += 1 | |
ccpiFlags = ptr2number(inData, 1, true) | |
inData += 1 | |
print("x", tonumber(origSize), tonumber(ccpiMagic), tonumber(ccpiVersion), tonumber(paletteCount), tonumber(ccpiColumnStrideBit), tonumber(ccpiRowStrideBit), tonumber(width), tonumber(height), tonumber(ccpiUnknown), tonumber(ccpiFlags)) | |
if width < 16 or height < 16 | |
error("Invalid dimensions") | |
pvCcpi = nil | |
if bit.band(ccpiFlags, 0x80) ~= 0 | |
pvCcpi, unc = decompress_FALCOM3(inData) --this is Falcom_decompress_3 | |
else | |
pvCcpi = new_array("uint8_t", origSize - 16) | |
ffi.fill(pvCcpi, origSize - 16, 0) | |
ffi.copy(pvCcpi, inData, origSize - 16) | |
if ccpiVersion ~= 6 and ccpiVersion ~= 7 | |
error("unknown ccpi version") | |
indexBuffer = new_array("uint8_t", width * height) | |
ffi.fill(indexBuffer, width * height, 0) | |
rowStride = uint32_t(bit.lshift(1, ccpiRowStrideBit)) | |
columnStride = uint32_t(bit.lshift(1, ccpiColumnStrideBit)) | |
curRow = uint32_t(0) | |
nextRow = min(rowStride, height) | |
hasName = (bit.band(ccpiFlags, 0x02) ~= 0) and 8 or 0 | |
pbCcpiBuffer = nil | |
if paletteCount ~= 0 | |
pbCcpiBuffer = pvCcpi + (4 + hasName) + (paletteCount * 4) | |
ffi.copy(paletteBuffer, pvCcpi, paletteCount * 4) | |
else | |
pbCcpiBuffer = pvCcpi + hasName | |
compressedPalette = new_array("uint8_t", 0x400 * 4) | |
ffi.fill(compressedPalette, 0x400 * 4, 0) | |
heightCount = uint32_t((height + rowStride - 1) / rowStride) | |
while heightCount ~= 0 | |
curColumn = uint32_t(0) | |
nextColumn = uint32_t(min(columnStride, width)) | |
widthCount = uint32_t((width + columnStride - 1) / columnStride) | |
while widthCount ~= 0 | |
n = pbCcpiBuffer[0] | |
--length = nil | |
pbCcpiBuffer += 1 | |
pbCompressedPalette = nil | |
if ccpiVersion >= 7 | |
--length = math.min(0xFF, n * 4) | |
p = ffi.cast(uint32p_t, pbCcpiBuffer) | |
p1 = ffi.cast(uint32p_t, compressedPalette) | |
p2 = p1 + n | |
p3 = p2 + n | |
p4 = p3 + n | |
count = n | |
while count ~= 0 | |
index = p[0] | |
p += 1 | |
index2 = bit.bor(SWAP2(LOWORD(index)), bit.lshift(SWAP2(HIWORD(index)), 16)) | |
p1[0] = index | |
p4[0] = bswap32(index) | |
p2[0] = index2 | |
p3[0] = bswap32(index2) | |
p1 += 1 | |
p2 += 1 | |
p3 += 1 | |
p4 += 1 | |
count -= 1 | |
pbCompressedPalette = compressedPalette | |
else | |
pbCompressedPalette = pbCcpiBuffer | |
pbCcpiBuffer += (n * 4) | |
pbIndex = indexBuffer + (curRow * width) | |
pbIndex += curColumn | |
row = curRow | |
while row < nextRow | |
column = curColumn | |
while (column < nextColumn) and (row < nextRow) | |
indexCount = uint8_t(0) | |
index = pbCcpiBuffer[0] | |
p = nil | |
if index == 0xFF | |
p = pbCompressedPalette + (4 * pbCcpiBuffer[-1]) | |
indexCount = pbCcpiBuffer[1] | |
pbCcpiBuffer += 1 | |
else | |
p = pbCompressedPalette + (index * 4) | |
indexCount = uint8_t(1) | |
while true | |
if (nextRow - row == 1) | |
pbIndex[0] = p[0] | |
pbIndex += 1 | |
p += 1 | |
if (nextColumn - column == 1) | |
p += 1 | |
else | |
pbIndex[0] = p[0] | |
pbIndex += 1 | |
p += 1 | |
p += 2 | |
elseif (nextColumn - column == 1) | |
pbIndex[0] = p[0] | |
p += 1 | |
pbIndex[width] = p[0] | |
p += 1 | |
pbIndex += 1 | |
p += 2 | |
else | |
pbIndex[0] = p[0] | |
p += 1 | |
pbIndex[1] = p[0] | |
p += 1 | |
pbIndex[width] = p[0] | |
p += 1 | |
pbIndex[width + 1] = p[0] | |
p += 1 | |
pbIndex += 2 | |
indexCount -= 1 | |
if indexCount == 0 | |
break | |
p -= 4 | |
column += 2 | |
if column >= nextColumn | |
column = curColumn | |
if nextRow - row ~= 1 | |
pbIndex += (2 * width - (nextColumn - curColumn)) | |
row += 2 | |
pbCcpiBuffer += 1 | |
column += 2 | |
if nextRow - row ~= 1 | |
pbIndex += (2 * width - (nextColumn - curColumn)) | |
row += 2 | |
curColumn += columnStride | |
nextColumn = min(nextColumn + columnStride, width) | |
widthCount -= 1 | |
curRow += rowStride | |
nextRow = min(nextRow + rowStride, height) | |
heightCount -= 1 | |
elseif header == 0x03E8 or header == 0x03EA or header == 0x03EC or header == 0x03ED | |
width = ptr2number(inData, 4, true) | |
inData += 4 | |
if width == 0 | |
metaInfoSize = ptr2number(inData, 4, true) | |
inData += 4 | |
inData += metaInfoSize --TODO: verify if set or add? | |
width = ptr2number(inData, 4, true) | |
inData += 4 | |
height = ptr2number(inData, 4, true) | |
indexBuffer = new_array("uint8_t", width * height) | |
inData += 4 | |
paletteCount = 256 | |
if header == 0x03EC or header == 0x03ED | |
paletteCount = ptr2number(inData, 4, true) | |
inData += 4 | |
if header == 0x03EA or header == 0x03EC or header == 0x03ED | |
cpaletteSize = ptr2number(inData, 4, true) | |
paletteBufferUnc = decompress_FALCOM3(inData) | |
ffi.copy(paletteBuffer, paletteBufferUnc, paletteCount * 4) | |
inData += 4 + cpaletteSize | |
if header == 0x03ED | |
inData += 4 --original color index size (we don't need this) | |
indexBufferUnc = decompress_FALCOM3(inData) | |
ffi.copy(indexBuffer, indexBufferUnc, width * height) | |
--Now, what to do with this data... | |
if header == 0x03ED | |
p = paletteBuffer + 1 | |
count = paletteCount - 1 | |
while count ~= 0 | |
p[0] += p[-1] | |
p += 1 | |
count -= 1 | |
indexBuffer2 = unknownFunction2(paletteBuffer, indexBuffer, width, height, paletteCount) | |
ffi.copy(indexBuffer, indexBuffer2, width * height) | |
if header == 0x03ED or header == 0x03EC | |
indexBuffer2 = unknownFunction(indexBuffer, width, height, 8) --deswizzle? | |
ffi.copy(indexBuffer, indexBuffer2, width * height) | |
elseif header == 0x03E8 | |
ffi.copy(paletteBuffer, inData, paletteCount * 4) | |
inData += paletteCount * 4 | |
ffi.copy(indexBuffer, inData, width * height) | |
inData += width * height | |
else | |
error("dead code path") | |
else | |
print(arg[1], tonumber(header)) | |
error("unsupported file") | |
return indexBuffer, width, height, paletteBuffer, paletteCount | |
img, width, height, clut, clutNum = decodeITP(in1a) | |
-- fu.writefile("indexBuffer.bin", ffi.string(indexBuffer, width * height)) | |
-- fu.writefile("palette.bin", ffi.string(uncompressedPalette, paletteCount * 4)) | |
pbBitmap = new_array("uint32_t", width * height) | |
for i = 0, tonumber((width * height) - 1) | |
pbBitmap[i] = clut[img[i]] | |
proc = require"ipc.proc" | |
stdincmd = (s, cmd) -> | |
handle = proc.spawn(cmd, {stdin: true}) | |
for a in s\gmatch'.' | |
handle\write(a) | |
handle\write(proc.EOF) | |
handle\wait() | |
c8888topngfile = (outfile, sizew, sizeh, depth, fmt, s) -> | |
stdincmd(s, fu.shellargs("convert", "-size", tonumber(sizew) .. "x" .. tonumber(sizeh), "-depth", tostring(depth), fmt .. ":fd:0", outfile)) | |
--os.execute(fu.shellargs("optipng", outfile)) | |
-- os.execute(fu.shellargs("cwebp", "-lossless", "-exact", outfile .. ".png", "-o", outfile)) | |
-- os.execute(fu.shellargs("rm", outfile .. ".png")) | |
c8888topngfile(arg[1] .. ".png", width, height, 8, "rgba", ffi.string(pbBitmap, width * height * 4)) | |
-- fu.writefile("pbBitmap.bin", ffi.string(pbBitmap, width * height * 4)) | |
-- --fu.writefile("pbBitmap.bin", ffi.string(pbBitmap, width * height * 4)) | |
-- res = xxhash.xxh32(ffi.string(img, width * height), 0xBACD7814) | |
-- res2 = xxhash.xxh32(ffi.string(pbBitmap, width * height * 4), 0xBACD7814) | |
-- --let's try gaps | |
-- rez = uint32_t(0) | |
-- for i = 1, tonumber(height) | |
-- rowHash = uint32_t(xxhash.xxh32(ffi.string(img + (width * (i - 1)), width), 0xBACD7814)) | |
-- rez = uint32_t(bit.bxor((rez * 11), rowHash)) | |
-- print(res, res2, tonumber(rez)) --NONE of these match up with PPSSPP xxh32 texture replacement hash!!!! | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment