Skip to content

Instantly share code, notes, and snippets.

@MikuAuahDark
Last active September 19, 2017 10:24
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/e05364882c6ff4465cb2eef599d08d51 to your computer and use it in GitHub Desktop.
Save MikuAuahDark/e05364882c6ff4465cb2eef599d08d51 to your computer and use it in GitHub Desktop.
libhonoka2 Lua(JIT) FFI binding
-- libhonoka2 FFI binding
-- Copyright (c) 2038 Dark Energy Processor Corporation
--
-- Permission is hereby granted, free of charge, to any person obtaining a
-- copy of this software and associated documentation files (the "Software"),
-- to deal in the Software without restriction, including without limitation
-- the rights to use, copy, modify, merge, publish, distribute, sublicense,
-- and/or sell copies of the Software, and to permit persons to whom the
-- Software is furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in
-- all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-- IN THE SOFTWARE.
--
-- libhonoka2 FFI binding is licensed under MIT license.
-- libhonoka is licensed under HonokaMiku 6 license (restrictive)!
local ffi = require("ffi")
local lh = ffi.load("honoka")
local honoka = {
--[[-------------------------------------------------------------
-- Error codes --
-------------------------------------------------------------]]--
ERR_OK = 0, -- No error
ERR_DECRYPTUNKNOWN = 1, -- No method found to decrypt this file
ERR_BUFFERTOOSMALL = 2, -- Buffer to store the headers is too small
ERR_INVALIDMETHOD = 3, -- Invalid decryption method
ERR_V3UNIMPLEMENTED = 4, -- Unimplemented V3+ decryption method
ERR_INVALIDARG = 5, -- Invalid argument
ERR_UNIMPLEMENTED = 6, -- Method unimplemented
_err_strlookup = {
[0] = "No error",
[1] = "No method found to decrypt this file",
[2] = "Buffer to store the headers is too small",
[3] = "Invalid decryption method",
[4] = "Unimplemented V3+ decryption method",
[5] = "Invalid argument",
[6] = "Method unimplemented"
},
--[[-------------------------------------------------------------
-- Pre-defined prefix for game files --
-------------------------------------------------------------]]--
KEY_SIF_EN = "BFd3EnkcKa",
KEY_SIF_WW = "BFd3EnkcKa",
KEY_SIF_JP = "Hello",
KEY_SIF_TW = "M2o2B7i3M6o6N88",
KEY_SIF_CN = "iLbs0LpvJrXm3zjdhAr4",
--[[-------------------------------------------------------------
-- Decryption modes --
-------------------------------------------------------------]]--
decrypt_none = "honokamiku_decrypt_none",
decrypt_v1 = "honokamiku_decrypt_version1",
decrypt_v2 = "honokamiku_decrypt_version2",
decrypt_v3 = "honokamiku_decrypt_version3",
decrypt_v4 = "honokamiku_decrypt_version4",
decrypt_v5 = "honokamiku_decrypt_version5",
decrypt_v6 = "honokamiku_decrypt_version6",
decrypt_auto = "honokamiku_decrypt_auto",
--[[-------------------------------------------------------------
-- Game file IDs --
-------------------------------------------------------------]]--
gamefile_unknown = "honokamiku_gamefile_unknown",
gamefile_en = "honokamiku_gamefile_ww",
gamefile_jp = "honokamiku_gamefile_jp",
gamefile_tw = "honokamiku_gamefile_tw",
gamefile_cn = "honokamiku_gamefile_cn",
gamefile_ww = "honokamiku_gamefile_ww",
_gamefile_lookup = {
[0] = "honokamiku_gamefile_unknown",
"honokamiku_gamefile_en",
"honokamiku_gamefile_jp",
"honokamiku_gamefile_tw",
"honokamiku_gamefile_ww"
},
}
ffi.cdef [[
typedef enum honokamiku_decrypt_mode
{
honokamiku_decrypt_none,
honokamiku_decrypt_version1,
honokamiku_decrypt_version2,
honokamiku_decrypt_version3,
honokamiku_decrypt_version4,
honokamiku_decrypt_version5,
honokamiku_decrypt_version6,
honokamiku_decrypt_auto = (-1)
} honokamiku_decrypt_mode;
typedef enum honokamiku_gamefile_id
{
honokamiku_gamefile_unknown,
honokamiku_gamefile_ww,
honokamiku_gamefile_jp,
honokamiku_gamefile_tw,
honokamiku_gamefile_cn,
} honokamiku_gamefile_id;
typedef struct honokamiku_context honokamiku_context;
const char *honokamiku_version_string();
size_t honokamiku_version();
size_t honokamiku_context_size();
size_t honokamiku_header_size(honokamiku_decrypt_mode decrypt_mode);
int honokamiku_decrypt_init(
honokamiku_context *dctx,
honokamiku_decrypt_mode decrypt_mode,
honokamiku_gamefile_id gamefile_id,
const char *gamefile_prefix,
const char *filename,
const void *file_header
);
honokamiku_gamefile_id honokamiku_decrypt_init_auto(
honokamiku_context *dctx,
const char *filename,
const void *file_header
);
int honokamiku_decrypt_final_init(
honokamiku_context *dctx,
honokamiku_gamefile_id gamefile_id,
const unsigned int *key_tables,
int name_sum,
const char *filename,
const void *next_header
);
int honokamiku_decrypt_is_final_init(honokamiku_context *dctx);
int honokamiku_encrypt_init(
honokamiku_context *dctx,
honokamiku_decrypt_mode decrypt_mode,
honokamiku_gamefile_id gamefile_id,
const char *gamefile_prefix,
const unsigned int *key_tables,
int name_sum,
const char *filename,
void *header_out,
size_t header_size
);
void honokamiku_decrypt_block(
honokamiku_context *dctx,
void *buffer,
size_t buffer_size
);
int honokamiku_jump_offset(
honokamiku_context *dctx,
unsigned int offset
);
]]
-- Recreate decrypter context struct
ffi.cdef(string.format("struct honokamiku_context{uint8_t _[%d];};", tonumber(lh.honokamiku_context_size())))
-- Wrapper function for calling method which returns HONOKAMIKU_ERR_*
local function wrap_hm_func(method)
return function(...)
local ret = method(select(1, ...))
assert(ret == honoka.ERR_OK, honoka._err_strlookup[ret])
end
end
-- Function declaration
function honoka.decrypt_init(dm, gid, gp, f, fh)
local dctx = ffi.new("honokamiku_context")
local ret = lh.honokamiku_decrypt_init(dctx, dm, gid, gp, f, ffi.cast("const void*", fh))
assert(ret == honoka.ERR_OK, honoka._err_strlookup[ret])
return dctx
end
function honoka.decrypt_init_auto(filename, header)
local dctx = ffi.new("honokamiku_context")
local type = lh.honokamiku_decrypt_init_auto(dctx, filename, ffi.cast("const void*", header))
assert(type ~= honoka.gamefile_unknown, "No method found to decrypt this file")
return dctx, honoka._gamefile_lookup[tonumber(type)]
end
function honoka.decrypt_final_init(dctx, gid, kt, ns, filename, header)
if type(kt) == "table" then
kt = ffi.new("unsigned int[64]", kt)
end
local ret = lh.honokamiku_decrypt_final_init(dctx, gid, kt, ns, filename, ffi.cast("const void*", header))
assert(ret == honoka.ERR_OK, honoka._err_strlookup[ret])
end
function honoka.decrypt_is_final_init(dctx)
return lh.honokamiku_decrypt_is_final_init(dctx) > 0
end
function honoka.encrypt_init(dm, gid, gp, kt, ns, filename)
if type(kt) == "table" then
kt = ffi.new("unsigned int[64]", kt)
end
local dctx = ffi.new("honokamiku_context")
local header_out = ffi.new("char[16]")
local ret = lh.honokamiku_encrypt_init(dctx, dm, gid, gp, kt, ns, filename, header_out, 16)
assert(ret == honoka.ERR_OK, honoka._err_strlookup[ret])
return dctx, ffi.string(header_out, lh.honokamiku_header_size(dm))
end
function honoka.decrypt_block(dctx, str)
local b = ffi.new("char[?]", #str, str)
lh.honokamiku_decrypt_block(dctx, b, #str)
return ffi.string(b, #str)
end
function honoka.jump_offset(dctx, dest)
local ret = lh.honokamiku_jump_offset(dctx, dest)
assert(ret == honoka.ERR_OK, honoka._err_strlookup[ret])
end
honoka._VERSION = ffi.string(lh.honokamiku_version_string())
honoka._VERSION_NUMBER = tonumber(lh.honokamiku_version())
ffi.metatype("honokamiku_context", {__index = {
final_init = honoka.decrypt_final_init,
is_final_init = honoka.decrypt_is_final_init,
decrypt_block = honoka.decrypt_block,
jump_offset = honoka.jump_offset
}})
--[[ Example Usage
local honoka = require("honoka2")
local a = assert(io.open("tx_startup_w1136.texb", "rb"))
local dctx, gid = honoka.decrypt_init_auto("tx_startup_w1136.texb", a:read(4))
if dctx:is_final_init() then
dctx:final_init(gid, nil, -1, "tx_startup_w1136.texb", a:read(12))
end
local b = a:read("*a")
local c = dctx:decrypt_block(b)
assert(c:sub(1, 4) == "TEXB")
-- End Example Usage ]]
return honoka
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment