Skip to content

Instantly share code, notes, and snippets.

@uyjulian
Last active August 22, 2023 22:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save uyjulian/1e22b0d4c38ffe1e9cb1f90f1aec7df3 to your computer and use it in GitHub Desktop.
Save uyjulian/1e22b0d4c38ffe1e9cb1f90f1aec7df3 to your computer and use it in GitHub Desktop.
-- 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