Skip to content

Instantly share code, notes, and snippets.

@MikuAuahDark
Created May 1, 2017 02:11
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 MikuAuahDark/ffc04ddb2a655ca0cd62784a05923029 to your computer and use it in GitHub Desktop.
Save MikuAuahDark/ffc04ddb2a655ca0cd62784a05923029 to your computer and use it in GitHub Desktop.
Lua script to view more information about Playground Flash File
-- Playground FLSH asset dumper.
-- It does not allow you to make new FLSH file!!!
local arg = {...}
local target_input = arg[1]
if target_input == nil then
print("Usage: lua flsh_dumper.lua <flsh path>")
print("It does not allow you to make new FLSH file!!!")
return 1
end
local input = assert(io.open(target_input, "rb"))
assert(input:read(4) == "FLSH", "Invalid FLSH file")
input:read(4) -- Skip size
local function read32_be(file)
local x = {file:read(4):byte(1,4)}
return x[1] * 16777216 + x[2] * 65536 + x[3] * 256 + x[4]
end
local function read16_be(file)
local x = {file:read(2):byte(1,2)}
return x[1] * 256 + x[2]
end
local function read_float(file)
return read32_be(file) / 65536.0
end
local function read_byte(file)
return file:read(1):byte()
end
local function read_string(file)
local size = read16_be(file)
return ({file:read(size):gsub("%z", "")})[1]
end
local function print(...)
local nt = {}
for a, b in ipairs({...}) do
nt[#nt + 1] = tostring(b)
end
io.write(table.concat(nt, "\t"), "\n")
end
-- Start!!!
print("Name", read_string(input))
do
local msf = read16_be(input)
print("MS/Frame", msf, math.floor(1000/msf).." FPS")
end
local sounds = {}
local strings_list = {}
local string_count = read16_be(input)
do
read16_be(input) -- Skip total string size
if string_count == 65535 then
-- Sound extension
local sound_count = read16_be(input)
if sound_count > 0 then
for i = 1, sound_count do
sounds[i] = {read16_be(input)}
end
end
local indexTotal = read32_be(input)
local shapeCount = read16_be(input)
assert(shapeCount == 0, "Shape decoding is not supported")
string_count = read16_be(input)
end
print()
print("Contains "..string_count.." string(s) constant")
for i = 1, string_count do
local x = read_string(input)
i = i - 1
strings_list[i] = x
print("["..i.."] = "..x)
end
end
if #sounds > 0 then
print()
print("Has sound extension")
for i = 1, #sounds do
print("["..(i - 1).."] = "..strings_list[sounds[i][1]])
end
end
local matrix_count = read32_be(input)
local float_count = read32_be(input)
local matrix_data = {}
local float_data = {}
print()
print("Contains "..float_count.." float constants")
for i = 1, float_count do
local x = read_float(input)
i = i - 1
x = math.floor(x / 32768) * (-65536) + x
float_data[i] = x
print("["..i.."] = "..x)
end
local matrix_type_id = {[0] = "MATRIX_ID", "MATRIX_T", "MATRIX_TS", "MATRIX_TG", "MATRIX_COL"}
local matrix_idx_fmt = string.format("%%%02dd", #tostring(matrix_count - 1))
print()
print("Contains "..matrix_count.." matrix data")
for i = 1, matrix_count do
local x = read_byte(input)
local y = read32_be(input)
table.insert(matrix_data, {x, y})
io.write(matrix_idx_fmt:format(i - 1), "\tType\t", matrix_type_id[x] or x, "\tData Index\t", y)
if x == 0 then
io.write("\t\tA = 0; D = 0; TX = 0; TY = 0; B = 0; C = 0;\n")
elseif x == 1 then
io.write("\t\tTX = ", float_data[y], "; TY = ", float_data[y + 1], ";\n")
elseif x == 2 then
io.write("\t\tA = ", float_data[y], "; D = ", float_data[y + 1], "; TX = ", float_data[y + 2], "; TY = ", float_data[y + 3], "; B = 0; C = 0;\n")
elseif x == 3 then
io.write("\t\tA = ", float_data[y], "; D = ", float_data[y + 1], "; B = ", float_data[y + 2], "; C = ", float_data[y + 3], "; TX = ", float_data[y + 4], "; TY = ", float_data[y + 5], ";\n")
elseif x == 4 then
io.write("\t\tR = ", float_data[y] * 255, "; G = ", float_data[y + 1] * 255, "; B = ", float_data[y + 2] * 255, "; A = ", float_data[y + 3] * 255, ";\n")
else
io.write("\n")
end
end
local instr_count = read32_be(input)
local instr_data = {}
print()
print("Contains "..instr_count.." instructions")
for i = 1, instr_count do
local x = read32_be(input)
instr_data[i - 1] = x
end
local movie_count = read16_be(input)
local movie_data = {}
print()
print("Contains "..movie_count.." movie(s)")
for i = 1, movie_count do
local x = {
name_idx = read32_be(input),
framecount = read32_be(input),
startindex = read32_be(input),
endindex = read32_be(input)
}
local name = strings_list[x.name_idx]
movie_data[i - 1] = x
if x.framecount < 32768 then
if name then
print(string.format("Movie #%d name = %s ; %d frames ; start instr idx = %d ; end instr idx = %d", i - 1, name, x.framecount, x.startindex, x.endindex))
else
print(string.format("Movie #%d unnamed movie ; %d frames ; start instr idx = %d ; end instr idx = %d", i - 1, x.framecount, x.startindex, x.endindex))
end
elseif x.framecount == 65535 then
print(string.format("Movie #%d image %s ; offsetx = %d ; offsety = %d", i - 1, name, x.startindex, x.endindex))
elseif x.framecount == 36862 then
print(string.format("Movie #%d shape %s ; offsetx = %d ; offsety = %d", i - 1, name, x.startindex, x.endindex))
else
--
print(string.format("Movie #%d %s movie", i - 1, name or "unknown"))
end
end
local max_instr_len = #string.format("%x", instr_count * 4)
local instr_idx_format = string.format("[%%%02dX]", max_instr_len)
for n, v in pairs(movie_data) do
local movie_name = strings_list[v.name_idx]
if v.framecount < 32768 then
print()
if movie_name then
print("Decoded instructions for movie #"..n.." ("..movie_name..")")
else
print("Decoded instructions for unnamed movie #"..n)
end
local instr_counter = v.startindex + 4
local function get_next_instruction()
local x = instr_data[instr_counter]
instr_counter = instr_counter + 1
return x
end
while instr_counter < v.endindex do
local instr_pos = instr_counter
local instr = get_next_instruction()
local instr_name = ""
local instr_info = {}
do
if instr == 0 then
instr_name = "SHOW_FRAME\t"
local label = get_next_instruction()
local type = get_next_instruction()
local frame_target = get_next_instruction() + 2
table.insert(instr_info, string.format("LABEL %08X", label))
table.insert(instr_info, string.format("TYPE %08X", type))
if type == 0 then
table.insert(instr_info, "STOP_INSTRUCTION")
elseif type == 1 or type == 2 then
if type == 1 then
table.insert(instr_info, "GOTO_AND_PLAY")
else
table.insert(instr_info, "GOTO_AND_STOP")
end
table.insert(instr_info, string.format("FRAMETARGET %08X", frame_target))
end
elseif instr == 1 or instr == 4 then
if instr == 1 then
instr_name = "PLACE_OBJECT"
else
instr_name = "PLACE_OBJECT_CLIP"
end
local movieid = get_next_instruction()
local matrixidx = get_next_instruction()
local matrixcolidx = get_next_instruction()
local layer = get_next_instruction()
table.insert(instr_info, string.format("MOVIEID %d MATRIXIDX %d MATRIXCOLIDX %d LAYER %d", movieid, matrixidx, matrixcolidx, layer))
if instr == 4 then
local clip = get_next_instruction()
table.insert(instr_info, string.format("CLIP %d", clip))
end
elseif instr == 2 then
instr_name = "REMOVE_OBJECT"
local layer = get_next_instruction()
table.insert(instr_info, string.format("LAYER %d", layer))
elseif instr == 3 then
instr_name = "PLAY_SOUND\t"
local sndid = get_next_instruction()
table.insert(instr_info, string.format("SNDID %d", sndid))
else
instr_name = "INVALID"
end
end
print(string.format(instr_idx_format, instr_pos * 4), string.format("%08X\t%s\t%s", instr, instr_name, table.concat(instr_info, " ")))
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment