Skip to content

Commit

Permalink
Clean up code and edit README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
JustAPotota committed Nov 7, 2020
1 parent 45bae32 commit 19732aa
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 28 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ WIP modding tool for the [Defold game engine](https://defold.com/).
## Planned Features
- Import assets
- Decompile/recompile assets
- Requires a [native extension](https://defold.com/manuals/extensions/) version of [lua-lz4](https://github.com/witchu/lua-lz4)
- Both features above require a [native extension](https://defold.com/manuals/extensions/) version of [lua-lz4](https://github.com/witchu/lua-lz4), specifically the `block_compress()` and `block_decompress()` functions
- Mod loading

## Installation
Expand All @@ -33,7 +33,7 @@ The archive index. It's a binary file with a header, a list of hashes of each fi
| 0x14 | 0x4 | Starting offset of the entry list |
| 0x16 | 0x4 | Starting offset of the hash list |
| 0x1a | 0x4 | The length of each hash |
| 0x1e | ??? | The MD5 hash of the index file. Its length is determined by [MD5_HASH_DIGEST_BYTE_LENGTH](https://github.com/defold/defold/blob/9991d949988c4da04f08b1aed386425035cdae3c/com.dynamo.cr/com.dynamo.cr.bob/src/com/dynamo/bob/archive/ArchiveBuilder.java#L46) (0x10 at the time of writing) |
| 0x1e | 0x10* | The MD5 hash of the index file. Its length is determined by [MD5_HASH_DIGEST_BYTE_LENGTH](https://github.com/defold/defold/blob/9991d949988c4da04f08b1aed386425035cdae3c/com.dynamo.cr/com.dynamo.cr.bob/src/com/dynamo/bob/archive/ArchiveBuilder.java#L46) (0x10 at the time of writing) |

### Hash Format
These are hashes whose lengths are defined in the index header. Each hash is stored in a block of size [HASH_MAX_LENGTH](https://github.com/defold/defold/blob/9991d949988c4da04f08b1aed386425035cdae3c/com.dynamo.cr/com.dynamo.cr.bob/src/com/dynamo/bob/archive/ArchiveBuilder.java#L44), the rest of the space gets filled in with zeros. The hash algorithm used is defined by the manifest header. Hashes seem to only be used as an identifier, not to check that the files are valid.
Expand Down
24 changes: 16 additions & 8 deletions main/main.gui_script
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
local protoc = require("pb.protoc")
local unfold = require("unfold")
local sio = require("sio")
local utils = require("utils")
Expand Down Expand Up @@ -38,6 +37,7 @@ local function hit_test(node, action_id, action)
return touch and hit
end

-- Convert hex color code to vector4
local function hex_to_v4(h)
h = string.match(h, "%x%x%x%x%x%x")
local r = tonumber(h:sub(1,2), 16)/255
Expand All @@ -61,6 +61,7 @@ local function switch_page(self, to)
self.current_page = to
end

-- GUI nodes ---------------------
local function button(self, id, normal_color, highlight_color, action_id, action, on_click)
local node = gui.get_node(id)
if hit_test(node, action_id, action) then
Expand All @@ -85,6 +86,7 @@ local function tab(self, page, action_id, action)
switch_page(self, page)
end)
end
----------------------------------

local function set_status(node, text, error)
if error then
Expand Down Expand Up @@ -124,7 +126,7 @@ end
function on_input(self, action_id, action)
-- Setup --------------------------
tab(self, "setup", action_id, action)

button(self, "select_bundle", "#2b2b2b", "#363636", action_id, action, function(self)
local code, path = diags.open_folder()
if code == 1 then
Expand All @@ -136,24 +138,25 @@ function on_input(self, action_id, action)

-- Export -------------------------
tab(self, "export", action_id, action)

button(self, "select_output", "#2b2b2b", "#363636", action_id, action, function(self)
local code, path = diags.open_folder()
if code == 1 then
self.output_path = path

gui.set_text(self.nodes.output_path_text, path)
autofit_text(self.nodes.output_path_text)
end
end)
button(self, "export_button", "#0091ea", "#00b0ff", action_id, action, function(self)
-- Validate paths -------------
local invalid_bundle = validate_bundle(self.bundle_path)
if invalid_bundle then
return set_status(self.nodes.export_status, invalid_bundle, true)
else
set_status(self.nodes.export_status, "", false)
end

if not self.output_path then
set_status(self.nodes.export_status, "You have to select an output folder!", true)
return
Expand All @@ -166,18 +169,23 @@ function on_input(self, action_id, action)
if self.output_path:sub(-1) == "/" then
self.output_path = self.output_path:sub(1,-2)
end
---------------------------------


print("Bundle path: " .. utils.to_platform(self.bundle_path))
print("Output path: " .. utils.to_platform(self.output_path))


utils.erase_dir(utils.to_platform(self.output_path))


-- Read and parse archive files
local arc_data = sio.read(self.bundle_path .. "/game.arcd")
local index = unfold.read_index(self.bundle_path .. "/game.arci")
local manifest = unfold.read_manifest(self.bundle_path .. "/game.dmanifest")
manifest = unfold.convert_hashes(manifest)
local entries = unfold.get_entries(arc_data, index, manifest)

-- Write files to output dir
set_status(self.nodes.export_status, "Writing to " .. utils.to_platform(self.output_path) .. "...", false)
for i,v in ipairs(entries) do
if v.url then
Expand All @@ -189,6 +197,7 @@ function on_input(self, action_id, action)
end
sio.write(utils.to_platform(self.output_path .. "/compiled/game.projectc"), sio.read(utils.to_platform(self.bundle_path .. "/game.projectc")))

-- Save some info about the archive for re-importing
local info = {
index = {
version = index.version,
Expand All @@ -203,7 +212,6 @@ function on_input(self, action_id, action)
}
}
}

sio.write(utils.to_platform(self.output_path .. "/info.json"), cjson.encode(info))

set_status(self.nodes.export_status, "Done! Written to " .. utils.to_platform(self.output_path), false)
Expand Down Expand Up @@ -245,4 +253,4 @@ function on_input(self, action_id, action)
print("Bundle path: " .. utils.to_platform(self.bundle_path))
print("Input path: " .. utils.to_platform(self.input_path))
end)
end
end
37 changes: 21 additions & 16 deletions unfold.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ local protoc = require("pb.protoc")
local sio = require("sio")
local utils = require("utils")

-- Set up protoc
-- Set up protoc -----------------
protoc.unknown_module = ""
protoc.unknown_type = ""
protoc.include_imports = true
protoc:load(sys.load_resource("/proto/liveupdate_ddf.proto"))
----------------------------------

-- Helper functions

-- Helper functions --------------
function M.read_int(f)
local char = f:read(4)
local total = ""
Expand Down Expand Up @@ -43,8 +45,21 @@ function M.hex_string(h)
return string.format(string.rep("%02x", #h), string.byte(h, 1, -1))
end

-- Convert hashes to human-readable format
function M.convert_hashes(manifest)
for i,v in ipairs(manifest.data.resources) do
v.hash.data = M.hex_string(v.hash.data)
v.url_hash = string.format("%x", v.url_hash)
end
--[[for i,v in ipairs(manifest.data.engine_versions) do
v.data = M.hex_string(v.data)
end]]
return manifest
end
----------------------------------


-- Main functions
-- Main functions ----------------
function M.read_index(filename)
local arc_index = assert(io.open(filename, "rb"))
local index = {}
Expand Down Expand Up @@ -85,6 +100,7 @@ function M.read_index(filename)
return index
end

-- WIP import functions, needs LZ4 NE -----------
function M.write(bundle_dir, index, manifest, entries)
local arcd = io.open(bundle_dir .. "/game.arcd", "wb")
local arci = io.open(bundle_dir .. "/game.arci", "wb")
Expand All @@ -104,7 +120,6 @@ function M.write(bundle_dir, index, manifest, entries)
end
end
end

function M.read_compiled_files(path)
local entries = {}

Expand All @@ -118,6 +133,7 @@ function M.read_compiled_files(path)
table.insert(entries, e)
end)
end
---------------------------------

function M.read_manifest(filename)
return pb.decode(".dmLiveUpdateDDF.ManifestFile", sio.read(filename))
Expand All @@ -127,17 +143,6 @@ function M.build_manifest(manifest)
return pb.encode(".dmLiveUpdateDDF.ManifestFile", sio.read(filename))
end

function M.convert_hashes(manifest)
for i,v in ipairs(manifest.data.resources) do
v.hash.data = M.hex_string(v.hash.data)
v.url_hash = string.format("%x", v.url_hash)
end
--[[for i,v in ipairs(manifest.data.engine_versions) do
v.data = M.hex_string(v.data)
end]]
return manifest
end

function M.get_entries(archive_data, index, manifest)
local entries = {}

Expand All @@ -161,4 +166,4 @@ function M.get_entries(archive_data, index, manifest)
return entries
end

return M
return M
4 changes: 2 additions & 2 deletions utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function M.dir_iter(path, for_each, for_each_dir)
end

function M.mkdir(filename)
-- Very hack-y workaround, will make a better solution
-- Should make a better solution for this
local path = (filename:sub(1,1) == "/" and "/" or "")
for i in filename:gmatch("([^/]+)/?") do
path = path .. i .. "/"
Expand Down Expand Up @@ -62,4 +62,4 @@ function M.to_platform(path)
return path
end

return M
return M

0 comments on commit 19732aa

Please sign in to comment.