-
-
Save uyjulian/bc208b17a3e269449ed27dc0041ea1bb 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
fu = require"file_util" | |
struct = require"struct" | |
serpent = require"serpent" | |
bit = require"bit" | |
ffi = require"ffi" | |
in1 = fu.readfile(arg[1]) | |
header, b = struct.unpack("c4", in1) | |
if header == "RYHP" | |
error("PhyreEngine little endian not supported") | |
if header ~= "PHYR" | |
error("not a PhyreEngine serialized file") | |
tableStructUnpack = (tbl, input, b, extra) -> | |
ttbl = {} | |
for i, v in pairs(tbl) | |
ttbl[v[1]], b = struct.unpack(extra .. v[2], input, b) | |
return ttbl, b | |
clusterHeaderGCM_struct = { | |
{"size", "I4"} | |
{"packedNamespaceSize", "I4"} | |
{"platformID", "c4"} | |
{"instanceListCount", "I4"} | |
{"arrayFixupSize", "I4"} | |
{"arrayFixupCount", "I4"} | |
{"pointerFixupSize", "I4"} | |
{"pointerFixupCount", "I4"} | |
{"pointerArrayFixupSize", "I4"} | |
{"pointerArrayFixupCount", "I4"} | |
{"pointersInArraysCount", "I4"} | |
{"userFixupCount", "I4"} | |
{"userFixupDataSize", "I4"} | |
{"totalDataSize", "I4"} | |
{"headerClassInstanceCount", "I4"} | |
{"headerClassChildCount", "I4"} | |
{"physicsEngineID", "I4"} | |
{"vramBufferSize", "I4"} | |
{"vramBufferAlignment", "I4"} | |
{"hostBufferSize", "I4"} | |
{"hostBufferAlignment", "I4"} | |
} | |
clusterHeaderGCM, b = tableStructUnpack(clusterHeaderGCM_struct, in1, b, ">") | |
if clusterHeaderGCM.platformID ~= "GCM\0" | |
error("PhyreEngine Platform ID " .. clusterHeaderGCM.platformID\gsub("%z", "") .. " not supported") | |
-- if clusterHeaderGCM.pointersInArraysCount ~= 0 | |
-- error("unsupported feature(s) used") | |
--PackedNamespace | |
namespacePacked_struct = { | |
{"header", "c4"} | |
{"size", "I4"} | |
{"typeCount", "I4"} | |
{"classCount", "I4"} | |
{"classDataMemberCount", "I4"} | |
{"stringTableSize", "I4"} | |
{"defaultBufferCount", "I4"} | |
{"defaultBufferSize", "I4"} | |
} | |
namespacePacked, b = tableStructUnpack(namespacePacked_struct, in1, b, ">") | |
if namespacePacked.header ~= "\001\002\003\004" | |
error("wrong NamespacePacked header") | |
--PackedType | |
packedType_struct = { | |
{"nameOffset", "I4"} | |
} | |
packedTypes = {} | |
for i = 1, namespacePacked.typeCount | |
packedType, b = tableStructUnpack(packedType_struct, in1, b, ">") | |
table.insert(packedTypes, packedType) --we'll fill these in later from the string table | |
--PackedClassDescriptors | |
packedClassDescriptor_struct = { | |
{"superClassID", "I4"} | |
--{"sizeInBytesAndAlignment", "I4"} --size | |
--{"sizeInBytesAndAlignment", "I4"} --alignment | |
{"sizeInAlignment", "I2"} | |
{"sizeInBytes", "I2"} | |
{"nameOffset", "I4"} | |
{"classDataMemberCount", "I4"} | |
{"offsetFromParent", "i4"} | |
{"offsetToBase", "i4"} | |
{"offsetToBaseInAllocatedBlock", "i4"} | |
{"flags", "I4"} | |
{"defaultBufferOffset", "I4"} | |
} | |
packedClassDescriptors = {} | |
for i = 1, namespacePacked.classCount | |
packedClassDescriptor, b = tableStructUnpack(packedClassDescriptor_struct, in1, b, ">") | |
table.insert(packedClassDescriptors, packedClassDescriptor) | |
--PackedDataMember | |
packedDataMember_sturct = { | |
{"nameOffset", "I4"} | |
{"typeID", "I4"} | |
{"valueOffset", "I4"} | |
{"sizeInBytes", "I4"} | |
{"flags", "I4"} | |
{"fixedArraySize", "I4"} | |
} | |
packedDataMembers = {} | |
for i = 1, namespacePacked.classDataMemberCount | |
packedDataMember, b = tableStructUnpack(packedDataMember_sturct, in1, b, ">") | |
table.insert(packedDataMembers, packedDataMember) | |
--fill in from string table | |
for i, v in pairs(packedTypes) | |
v.name = struct.unpack("s", in1, b + v.nameOffset) | |
for i, v in pairs(packedClassDescriptors) | |
v.name = struct.unpack("s", in1, b + v.nameOffset) | |
for i, v in pairs(packedDataMembers) | |
v.name = struct.unpack("s", in1, b + v.nameOffset) | |
v.typeID_name = ((#packedTypes < v.typeID) and packedClassDescriptors or packedTypes)[(#packedTypes < v.typeID) and (v.typeID - #packedTypes) or (v.typeID + 1)].name | |
b += namespacePacked.stringTableSize | |
--InstanceListHeader | |
instanceListHeader_struct = { | |
{"classID", "I4"} | |
{"count", "I4"} | |
{"size", "I4"} | |
{"objectsSize", "I4"} | |
{"arraysSize", "I4"} | |
{"pointersInArraysCount", "I4"} | |
{"arrayFixupCount", "I4"} | |
{"pointerFixupCount", "I4"} | |
{"pointerArrayFixupCount", "I4"} | |
} | |
instanceListHeaders = {} | |
for i = 1, clusterHeaderGCM.instanceListCount | |
instanceListHeader, b = tableStructUnpack(instanceListHeader_struct, in1, b, ">") | |
table.insert(instanceListHeaders, instanceListHeader) | |
type2struct_struct = { | |
PUInt32: "I4" | |
PChar: "i1" | |
PUInt8: "I1" | |
PInt32: "i4" | |
PUInt16: "I2" | |
} | |
for i, v in pairs(packedDataMembers) | |
if v.flags == 8 or v.flags == 0 | |
if type2struct_struct[v.typeID_name] ~= nil | |
v.value = (struct.unpack(">" .. type2struct_struct[v.typeID_name], in1, 1 + v.valueOffset)) | |
--read object data | |
for i, v in pairs(instanceListHeaders) | |
obj_data = "" | |
array_data = "" | |
if v.objectsSize ~= 0 | |
obj_data, b = struct.unpack("c" .. v.objectsSize, in1, b) | |
if v.arraysSize ~= 0 | |
array_data, b = struct.unpack("c" .. v.arraysSize, in1, b) | |
v.objdata = obj_data | |
v.arraydata = array_data | |
v.classID_name = packedClassDescriptors[v.classID].name | |
userFixupDataOffs = b | |
b += clusterHeaderGCM.userFixupDataSize | |
--user fixup | |
userFixup_struct = { | |
{"typeID", "I4"} | |
{"size", "I4"} | |
{"offset", "I4"} | |
} | |
userFixups = {} | |
for i = 1, clusterHeaderGCM.userFixupCount | |
userFixup, b = tableStructUnpack(userFixup_struct, in1, b, ">") | |
table.insert(userFixups, userFixup) | |
for i, v in pairs(userFixups) | |
v.typeID_name = ((#packedTypes < v.typeID) and packedClassDescriptors or packedTypes)[(#packedTypes < v.typeID) and (v.typeID - #packedTypes) or (v.typeID + 1)].name | |
v.data = struct.unpack("c" .. v.size, in1, userFixupDataOffs + v.offset) | |
--TODO: header class instance/child counts, pointer array fixup | |
for i = 1, clusterHeaderGCM.headerClassInstanceCount | |
b += 4 | |
for i = 1, clusterHeaderGCM.headerClassChildCount | |
b += 16 | |
b += clusterHeaderGCM.pointerArrayFixupSize --I have no idea how to deal with pointer-array fixups right now | |
b += clusterHeaderGCM.pointerFixupSize -- I have no idea how to deal with object pointers fixups right now | |
b += clusterHeaderGCM.arrayFixupSize -- I have no idea how to deal with data pointers fixups right now | |
--The actual data starts here | |
--Let's dump this into an actual .dds | |
format = "" | |
for i, v in pairs(userFixups) | |
if v.data == "DXT1\0" | |
format = "DXT1" | |
break | |
elseif v.data == "DXT3\0" | |
format = "DXT3" | |
break | |
elseif v.data == "DXT5\0" | |
format = "DXT5" | |
break | |
elseif v.data == "LA8\0" | |
format = "LA8" | |
break | |
elseif v.data == "L8\0" | |
format = "L8" | |
break | |
elseif v.data == "ARGB8\0" | |
format = "ARGB8" | |
break | |
elseif v.data == "RGBA8\0" | |
format = "RGBA8" | |
break | |
elseif v.data == "ARGB4444\0" | |
format = "ARGB4444" | |
break | |
elseif v.data == "RGB565\0" | |
format = "RGB565" | |
break | |
if format == "" | |
print(arg[1]) | |
error("unknown format") | |
height = 0 | |
width = 0 | |
origfilename = "" | |
for i, v in pairs(instanceListHeaders) | |
if v.classID_name == "PTexture2D" | |
if height ~= 0 or width ~= 0 | |
continue | |
k = 1 | |
k += 28 -- let's get straight to the height width | |
width, height, k = struct.unpack(">I4I4", v.objdata, k) | |
elseif v.classID_name == "PAssetReference" | |
if origfilename ~= "" | |
continue | |
origfilename = v.arraydata\gsub("%z", "") | |
if origfilename == "" | |
print(arg[1]) | |
error"invalid filename" | |
if origfilename\sub(-3, -1) ~= "png" and origfilename\sub(-3, -1) ~= "dds" and origfilename\sub(-3, -1) ~= "DDS" and origfilename\sub(-3, -1) ~= "bmp" | |
print(arg[1]) | |
error"invalid origfilename" | |
if height == 0 or width == 0 | |
print(arg[1]) | |
error"invalid dims" | |
ddsHeader = "" | |
if format == "LA8" | |
ddsHeader = struct.pack("<c4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4", "DDS\032", 124, bit.bor(0x1, 0x2, 0x4, 0x1000, 0x8), height, width, (width * 16 + 7) / 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, bit.bor(0x1, 0x40), 0, 16, 0x00FF, 0x00FF, 0x00FF, 0xFF00, 0x1000, 0, 0, 0, 0) | |
elseif format == "L8" | |
ddsHeader = struct.pack("<c4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4", "DDS\032", 124, bit.bor(0x1, 0x2, 0x4, 0x1000, 0x8), height, width, (width * 8 + 7) / 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, bit.bor(0x40), 0, 8, 0x00FF, 0x00FF, 0x00FF, 0x0000, 0x1000, 0, 0, 0, 0) | |
elseif format == "ARGB8" | |
ddsHeader = struct.pack("<c4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4", "DDS\032", 124, bit.bor(0x1, 0x2, 0x4, 0x1000, 0x8), height, width, (width * 32 + 7) / 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, bit.bor(0x1, 0x40), 0, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000, 0x1000, 0, 0, 0, 0) | |
elseif format == "RGBA8" | |
ddsHeader = struct.pack("<c4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4", "DDS\032", 124, bit.bor(0x1, 0x2, 0x4, 0x1000, 0x8), height, width, (width * 32 + 7) / 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, bit.bor(0x1, 0x40), 0, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x1000, 0, 0, 0, 0) | |
elseif format == "RGB565" | |
ddsHeader = struct.pack("<c4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4", "DDS\032", 124, bit.bor(0x1, 0x2, 0x4, 0x1000, 0x8), height, width, (width * 16 + 7) / 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, bit.bor(0x40), 0, 16, 0x0000F800, 0x000007E0, 0x0000001F, 0x00000000, 0x1000, 0, 0, 0, 0) | |
elseif format == "ARGB4444" | |
ddsHeader = struct.pack("<c4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4", "DDS\032", 124, bit.bor(0x1, 0x2, 0x4, 0x1000, 0x8), height, width, (width * 16 + 7) / 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, bit.bor(0x1, 0x40), 0, 16, 0x00000F00, 0x000000F0, 0x0000000F, 0x0000F000, 0x1000, 0, 0, 0, 0) | |
else | |
ddsHeader = struct.pack("<c4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4c4I4I4I4I4I4I4I4I4I4I4", "DDS\032", 124, bit.bor(0x1, 0x2, 0x4, 0x1000, 0x80000), height, width, math.max(1, (width + 3) / 4) * ((format == "DXT1") and 8 or 16), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0x4, format, 0, 0, 0, 0, 0, 0x1000, 0, 0, 0, 0) | |
--unswizzle if needed | |
isPowerOfTwo = (x) -> | |
if x == 0 | |
return false | |
if x == 1 --HACK: 1 is actually a 0th power of 2, but this causes the unswizzle code to be stuck in a loop | |
return false | |
return bit.band(x, x - 1) == 0 | |
xy = true | |
raw_data = in1\sub(b, b + clusterHeaderGCM.vramBufferSize - 1) | |
if (format\sub(1,3) ~= "DXT") and (isPowerOfTwo(height) and isPowerOfTwo(width)) | |
out_arr = {} | |
in_arr = {} | |
raw_data_tbl = {} | |
out_tbl = {} | |
readoffset = 1 | |
datawidthm = (((format == "ARGB8") or (format == "RGBA8")) and 4) or ((format == "L8") and 1) or 2 | |
unswizzle_tasks = {} | |
unswizzle = (outtbl, intbl, writeoffset, segsize, datawidth) -> | |
if segsize == 2 | |
if xy | |
outtbl[writeoffset ] = intbl[readoffset] | |
readoffset += 1 | |
outtbl[writeoffset + 1] = intbl[readoffset] | |
readoffset += 1 | |
outtbl[writeoffset + datawidth ] = intbl[readoffset] | |
readoffset += 1 | |
outtbl[writeoffset + datawidth + 1] = intbl[readoffset] | |
readoffset += 1 | |
else | |
outtbl[writeoffset ] = intbl[readoffset] | |
readoffset += 1 | |
outtbl[writeoffset + datawidth ] = intbl[readoffset] | |
readoffset += 1 | |
outtbl[writeoffset + 1] = intbl[readoffset] | |
readoffset += 1 | |
outtbl[writeoffset + datawidth + 1] = intbl[readoffset] | |
readoffset += 1 | |
else | |
if xy | |
table.insert(unswizzle_tasks, {outtbl, intbl, writeoffset, segsize / 2, datawidth}) | |
table.insert(unswizzle_tasks, {outtbl, intbl, writeoffset + segsize / 2, segsize / 2, datawidth}) | |
table.insert(unswizzle_tasks, {outtbl, intbl, writeoffset + datawidth * (segsize / 2), segsize / 2, datawidth}) | |
table.insert(unswizzle_tasks, {outtbl, intbl, writeoffset + datawidth * (segsize / 2) + segsize / 2, segsize / 2, datawidth}) | |
else | |
table.insert(unswizzle_tasks, {outtbl, intbl, writeoffset, segsize / 2, datawidth}) | |
table.insert(unswizzle_tasks, {outtbl, intbl, writeoffset + datawidth * (segsize / 2), segsize / 2, datawidth}) | |
table.insert(unswizzle_tasks, {outtbl, intbl, writeoffset + segsize / 2, segsize / 2, datawidth}) | |
table.insert(unswizzle_tasks, {outtbl, intbl, writeoffset + datawidth * (segsize / 2) + segsize / 2, segsize / 2, datawidth}) | |
gshub = (s) -> | |
table.insert(raw_data_tbl, s) | |
return false | |
raw_data\gsub("."\rep(datawidthm), gshub) | |
if width == height | |
table.insert(unswizzle_tasks, {out_tbl, raw_data_tbl, 1, width, width}) | |
elseif width > height | |
for w = 1, width, height | |
table.insert(unswizzle_tasks, {out_tbl, raw_data_tbl, w, height, width}) | |
elseif height > width | |
for h = 1, (height * width), width ^ 2 | |
table.insert(unswizzle_tasks, {out_tbl, raw_data_tbl, h, width, width}) | |
keep_swizzling = true | |
while keep_swizzling | |
keep_swizzling = false | |
to_work = unswizzle_tasks | |
unswizzle_tasks = {} | |
for i, v in pairs(to_work) | |
keep_swizzling = true | |
unswizzle(unpack(v)) | |
out_data = table.concat(out_tbl) | |
--deal with mipmaps later | |
--if #out_data ~= #raw_data | |
-- print(serpent.block({out_data, raw_data})) | |
-- print(arg[1]) | |
-- print(#out_data, #raw_data, #out_tbl, #raw_data_tbl, readoffset, height, width, clusterHeaderGCM.vramBufferSize) | |
-- error("unswizzle mismatch length") | |
raw_data = out_data | |
--do we need a byteswap? | |
if (format == "ARGB4444") or (format == "RGB565") | |
gshub = (s) -> | |
return s\sub(2,2) .. s\sub(1,1) | |
raw_data = raw_data\gsub("..", gshub) | |
elseif format == "RGBA8" | |
gshub = (s) -> | |
return s\sub(3,3) .. s\sub(2,2) .. s\sub(1,1) .. s\sub(4,4) | |
raw_data = raw_data\gsub("....", gshub) | |
--now paste the header and content together | |
os.execute(fu.shellargs("mkdir", "-p", fu.dirname(origfilename))) | |
fu.writefile(origfilename\sub(1, -5) .. ".dds", ddsHeader .. raw_data) | |
--Let's convert into .dae! (well, not yet) | |
print(serpent.block({header, clusterHeaderGCM, namespacePacked, packedTypes, packedClassDescriptors, packedDataMembers, instanceListHeaders, userFixups})) | |
print(b) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment