diff --git a/Readme.md b/Readme.md index 5cb8f67..fb67c42 100644 --- a/Readme.md +++ b/Readme.md @@ -6,7 +6,7 @@ Also, it supports for droping file, save decoded image and show cursor moving in tiles. Futhermore, the window is flexible for changing size and support for zooming in and out with converting the client coordinate to logical coordinate. -The main purpose is for analyzing game font or textures. See [TileDB](https://github.com/YuriSizuku/TileViewer/wiki/Font-Database) and [TexDB](https://github.com/YuriSizuku/TileViewer/wiki/Texture-Database) in detail. +The main purpose is for analyzing game font or textures. See [FontDB](https://github.com/YuriSizuku/TileViewer/wiki/Font-Database) and [TexDB](https://github.com/YuriSizuku/TileViewer/wiki/Texture-Database) in detail. ![tile_test1](asset/picture/tile_test1.png) (example of decoding a 2bpp tile font) @@ -405,6 +405,8 @@ chmod +x script/*.sh * [x] 16bpp(rgb565), 24bpp(rgb888), 32bpp(rgba8888) * [x] plugincfg, endian, channel_first, bgr, flip ([v0.3.4.3](https://github.com/YuriSizuku/TileViewer/releases/tag/v0.3.3.7)) * [x] plugin lua decoder ([v0.2](https://github.com/YuriSizuku/TileViewer/releases/tag/v0.2)) + * [x] set/get raw data, set/get tilecfg, tilenav + * [x] raw memory operations, memnew, memdel, memread, memwrite ([v0.3.5](https://github.com/YuriSizuku/TileViewer/releases/tag/v0.3.5)) * [x] plugin C decoder (dll, so) ([v0.3](https://github.com/YuriSizuku/TileViewer/releases/tag/v0.3)) * UI diff --git a/asset/plugin/9nine_fnt_switch.lua b/asset/plugin/9nine_fnt_switch.lua new file mode 100644 index 0000000..560d1f6 --- /dev/null +++ b/asset/plugin/9nine_fnt_switch.lua @@ -0,0 +1,185 @@ +---@diagnostic disable : lowercase-global, missing-fields, undefined-global, duplicate-doc-field, undefined-field + +io = require("io") + +version = "v0.1" +description = "[lua_9nine_fnt::init] lua plugin to decode 9nine fnt lz77 format" + +-- global declear +g_datap = nil --- @type lightuserdata +g_tilecfg = {} ---@type tilecfg_t +g_ntile = 0 ---@type integer +g_tilesp = nil ---@type lightuserdata + +---@class fnt_t +---@field magic string +---@field version integer +---@field fsize integer +---@field distop integer -- distance between baseline and top/bottom +---@field disbottom integer + +---@class fntglphy_t +---@field bearingx integer +---@field bearingy integer +---@field actualw integer +---@field actualh integer +---@field advancew integer +---@field reserve1 integer +---@field texturew integer +---@field textureh integer +---@field zsize integer +---@field ref_glphyaddr integer + +g_fnt = {} +g_fntglphys = {} -- start from 1 +g_glphylist = {} -- list for render tiles, remove duplicate + +---@param indata lightuserdata +---@param outdata lightuserdata +---@param inoffset? integer +---@param outoffset? integer +---@param offsetbits? integer +---@return integer +function lz77_decode(indata, outdata, insize, inoffset, outoffset, offsetbits) +--[[ + MSB XXXXXXXX YYYYYYYY LSB + val len backOffset + size (16-OFFSET_BITS) OFFSET_BITS + + index8, 8 * [val16 | val8] +--]] + + if insize == nil then insize = memsize(indata) - inoffset end + if inoffset == nil then inoffset = 0 end + if outoffset == nil then outoffset = 0 end + if offsetbits == nil then offsetbits = 10 end + + local incur, outcur = inoffset, outoffset + local outsize = memsize(outdata) - outoffset + while incur - inoffset < insize do + local index8 = memreadi(indata, 1, incur) + incur = incur + 1 + for j = 0, 8 - 1 do + local flag = (index8 >> j) & 1 + if flag == 0 then -- direct byte output + if(incur - inoffset >= insize) then break end -- sometimes no other bytes in the end + local d = memreadi(indata, 1, incur) + memwrite(outdata, d, 1, outcur) + incur = incur + 1 + outcur = outcur + 1 + else -- seek from output + local backseekval = memreadi(indata, 2, incur) + backseekval = ((backseekval & 0xff) << 8) + ((backseekval >> 8) & 0xff) -- big endian + local backoffsetmask = (1<> offsetbits) + 3 -- length must larger than 3 + local backoffset = (backseekval & backoffsetmask) + 1 + for _ = 1, backlength do -- push char to output one by one + memwrite(outdata, outdata, 1, outcur, outcur - backoffset) + outcur = outcur + 1 + end + incur = incur + 2 + end + end + end + return outcur - outoffset +end + +---@param p lightuserdata +---@return fnt_t, table +function parse_fnt(p) + FNT_FMT, FNTGLPHY_FMT = "= g_tilecfg.w then goto continue end + if y >= g_tilecfg.h then goto continue end + local offset1 = 4 * (y * g_tilecfg.w + x) + local offset2 = y * g_fntglphys[glphyi].texturew + x + local d = memreadi(outdatap, 1, offset2) + local pixel = d + (d << 8) + (d << 16) + (255 << 24) + memwrite(g_tilesp, pixel, 4, outoffset + offset1) + ::continue:: + end + end + + i = i + 1 + -- if i > 10 then break end + end + memdel(outdatap) + return g_tilesp, g_ntile * g_tilecfg.w * g_tilecfg.h, 0 +end + +function decode_post() + log("[lua_9nine_fnt::post] decode finished") + set_tilenav({index=0, offset=-1}) + set_tilestyle({scale=1.0}) + if g_tilesp ~= nil then + memdel(g_tilesp) + end + return true +end + +log(" ", description, version) \ No newline at end of file diff --git a/asset/plugin/lualib/util_declear.lua b/asset/plugin/lualib/util_declear.lua index 7e5ecc8..fbb1257 100644 --- a/asset/plugin/lualib/util_declear.lua +++ b/asset/plugin/lualib/util_declear.lua @@ -25,6 +25,41 @@ -- use log(...) to redirect to log window function log(...) end +-- memory manipulate with c + +---@param size integer +---@return lightuserdata ... +function memnew(size) end --c api + +---@param p lightuserdata +---@return boolean ... +function memdel(p) end --c api + +---@param p lightuserdata +---@return integer ... +function memsize(p) end --c api + +---@param p lightuserdata +---@param size? integer +---@param offset? integer +---@return integer ... +function memreadi(p, size, offset) end --c api + +---@param p lightuserdata +---@param size? integer +---@param offset? integer +---@return string ... +function memreads(p, size, offset) end --c api + +---@param p lightuserdata +---@param data integer|string|lightuserdata +---@param size? integer +---@param offset1 ? integer p offset +---@param offset2 ? integer data offset +---@return integer ... +function memwrite(p, data, size, offset1, offset2) end --c api + +-- set or get tile informations ---@return tilecfg_t ... function get_tilecfg() end -- c api @@ -50,9 +85,12 @@ function get_rawsize() end -- c api ---@param offset? integer ---@param size? integer ---@return string ... ----@option function get_rawdata(offset, size) end --c api +-- get the lightuserdata pointer for userptr +---@return lightuserdata ... +function get_rawdatap() end --c api + ---@return boolean ... function decode_pre() end -- c callback @@ -62,6 +100,11 @@ function decode_pre() end -- c callback ---@return integer ... pixel value packed in rgba function decode_pixel(i, x, y) end -- c callback +---@return lightuserdata | string ... pixels memory +---@return integer ... npixels +---@return integer ... offset +function decode_pixels() end -- c callback + ---@return boolean ... function decode_post() end -- c callback diff --git a/asset/plugin/narcissus_lbg_psp.lua b/asset/plugin/narcissus_lbg_psp.lua index 2b3b2be..40b6c04 100644 --- a/asset/plugin/narcissus_lbg_psp.lua +++ b/asset/plugin/narcissus_lbg_psp.lua @@ -56,7 +56,6 @@ function decode_pre() if(g_tilecfg.size > 0) then g_datasize = math.min(g_datasize, g_tilecfg.size) end g_data = get_rawdata(0x20, 0); - --- prepare decoding data w, h = 512, 272 for i=0, w*h do diff --git a/src/plugin.h b/src/plugin.h index e7c2f06..e2a44c0 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -35,11 +35,14 @@ extern "C" { #define OPTIONAL #define REQUIRED #undef STDCALL +#define STDCALL +#if 0 #if defined(_MSC_VER) || defined(__TINYC__) #define STDCALL __stdcall -#else +#else // warning: ‘stdcall’ attribute ignored [-Wattributes] #define STDCALL __attribute__((stdcall)) #endif +#endif struct pixel_t { diff --git a/src/plugin_lua.c b/src/plugin_lua.c index a717485..546d6de 100644 --- a/src/plugin_lua.c +++ b/src/plugin_lua.c @@ -3,6 +3,7 @@ * developed by devseed */ +#include #include #include #include @@ -15,13 +16,29 @@ extern struct tilecfg_t g_tilecfg; extern struct tilenav_t g_tilenav; extern struct tilestyle_t g_tilestyle; +struct tile_decoder_t g_decoder_lua; + +struct memblock_t +{ + void *p; + size_t n; +}; + static struct decode_context_t { lua_State *L; - const uint8_t *rawdata; - size_t rawsize; + union + { + struct + { + const uint8_t *rawdata; + size_t rawsize; + }; + struct memblock_t rawblock; + }; } s_decode_context = {NULL}; + static int capi_log(lua_State* L) { int nargs = lua_gettop(L); @@ -40,6 +57,133 @@ static int capi_log(lua_State* L) return 0; } +// function memnew(size) +static int capi_memnew(lua_State *L) +{ + if(lua_gettop(L) < 1 && !lua_isinteger(L, 1)) return 0; + size_t size = lua_tointeger(L, 1); + struct memblock_t *block = malloc(size + sizeof(struct memblock_t)); + block->n = size; + block->p = (uint8_t*)block + sizeof(struct memblock_t); + lua_pushlightuserdata(L, (void*) block); + return 1; +} + +// function memdel(p) +static int capi_memdel(lua_State *L) +{ + if(lua_gettop(L) < 1 && !lua_islightuserdata(L, 1)) return 0; + struct memblock_t *block = lua_touserdata(L, 1); + free(block); + lua_pushboolean(L, true); + return 1; +} + +// function memsize(p) +static int capi_memsize(lua_State *L) +{ + if(lua_gettop(L) < 1 && !lua_islightuserdata(L, 1)) return 0; + struct memblock_t *block = (struct memblock_t *)lua_touserdata(L, 1); + lua_pushinteger(L, block->n); + return 1; +} + +// function memreadi(p, size, offset) +static int capi_memreadi(lua_State *L) +{ + if(lua_gettop(L) < 2 && !lua_islightuserdata(L, 1)) return 0; + int nargs = lua_gettop(L); + struct memblock_t *block = (struct memblock_t *)lua_touserdata(L, 1); + + size_t offset = 0; + if(nargs > 2) offset = lua_tointeger(L, 3); + if(offset >= block->n) goto capi_memreadi_fail; + size_t size = 1; + if(nargs > 1) size = lua_tointeger(L, 2); + if(offset + size > block->n) goto capi_memreadi_fail; + + lua_Integer I = 0; + memcpy(&I, (uint8_t*)block->p + offset, size); + lua_pushinteger(L, I); + return 1; + +capi_memreadi_fail: + lua_pushnil(L); + return 1; +} + +// memreads(p, size, offset) +static int capi_memreads(lua_State *L) +{ + if(lua_gettop(L) < 1 && !lua_islightuserdata(L, 1)) return 0; + int nargs = lua_gettop(L); + struct memblock_t *block = (struct memblock_t *)lua_touserdata(L, 1); + + size_t offset = 0; + if(nargs > 2) offset = lua_tointeger(L, 3); + if(offset >= block->n) goto capi_memreads_fail; + size_t size = block->n - offset; + if(nargs > 1) size = lua_tointeger(L, 2); + if(offset + size > block->n) goto capi_memreads_fail; + + lua_pushlstring(L, (uint8_t*)block->p + offset, size); + return 1; + +capi_memreads_fail: + lua_pushnil(L); + return 1; +} + +// memwrite(p, data, size, offset1, offset2) +static int capi_memwrite(lua_State *L) +{ + if(lua_gettop(L) < 2 && !lua_islightuserdata(L, 1)) return 0; + int nargs = lua_gettop(L); + struct memblock_t *block = (struct memblock_t *)lua_touserdata(L, 1); + + size_t offset1 = 0; + if(nargs > 3) offset1 = lua_tointeger(L, 4); + if(offset1 >= block->n) goto capi_memwrite_fail; + size_t size = block->n - offset1; + if(nargs > 2) size = lua_tointeger(L, 3); + if(offset1 + size > block->n) goto capi_memwrite_fail; + + if (lua_isinteger(L, 2)) // the integer can also be string + { + lua_Integer I = lua_tointeger(L, 2); + memcpy((uint8_t*)block->p + offset1, &I, size); + } + else if(lua_isstring(L, 2)) + { + size_t size2 = 0; + size_t offset2 = 0; + if(nargs > 4) offset2 = lua_tointeger(L, 5); + const char *data = lua_tolstring(L, 2, &size2); + if(offset2 >= size2) goto capi_memwrite_fail; + size2 -= offset2; + if(size > size2) size = size2; + memcpy((uint8_t*)block->p + offset1, data + offset2, size); + } + else if(lua_isuserdata(L, 2)) + { + struct memblock_t *block2 = (struct memblock_t *)lua_touserdata(L, 2); + size_t offset2 = 0; + if(nargs > 4) offset2 = lua_tointeger(L, 5); + if(offset2 >= block2->n) goto capi_memwrite_fail; + size_t size2 = block2->n - offset2; + if(size > size2) size = size2; + memcpy((uint8_t*)block->p + offset1, (uint8_t*)block2->p + offset2, size); + } + + else goto capi_memwrite_fail; + lua_pushinteger(L, size); + return 1; + +capi_memwrite_fail: + lua_pushinteger(L, 0); + return 1; +} + static int capi_get_tilecfg(lua_State* L) { lua_newtable(L); @@ -251,8 +395,20 @@ static int capi_get_rawdata(lua_State *L) return 1; } +static int capi_get_rawdatap(lua_State *L) +{ + lua_pushlightuserdata(L, (void*)&s_decode_context.rawblock); + return 1; +} + static void register_capis(lua_State *L) { + lua_register(L, "memnew", capi_memnew); + lua_register(L, "memdel", capi_memdel); + lua_register(L, "memsize", capi_memsize); + lua_register(L, "memreadi", capi_memreadi); + lua_register(L, "memreads", capi_memreads); + lua_register(L, "memwrite", capi_memwrite); lua_register(L, "get_tilecfg", capi_get_tilecfg); lua_register(L, "set_tilecfg", capi_set_tilecfg); lua_register(L, "get_tilenav", capi_get_tilenav); @@ -261,6 +417,7 @@ static void register_capis(lua_State *L) lua_register(L, "set_tilestyle", capi_set_tilestyle); lua_register(L, "get_rawsize", capi_get_rawsize); lua_register(L, "get_rawdata", capi_get_rawdata); + lua_register(L, "get_rawdatap", capi_get_rawdatap); } PLUGIN_STATUS STDCALL decode_open_lua(const char *luastr, void **context) @@ -282,6 +439,13 @@ PLUGIN_STATUS STDCALL decode_open_lua(const char *luastr, void **context) return STATUS_SCRIPTERROR; } + lua_getglobal(L, "decode_pixels"); + if(!lua_isfunction(L, -1)) + { + g_decoder_lua.decodeall = NULL; + } + lua_pop(L, 1); + register_capis(L); s_decode_context.L = L; *context = &s_decode_context; @@ -406,6 +570,50 @@ PLUGIN_STATUS STDCALL decode_pixel_lua(void *context, return STATUS_OK; } +PLUGIN_STATUS decode_pixels_lua(void *context, + const uint8_t* data, size_t datasize, + const struct tilefmt_t *fmt, struct pixel_t *pixels[], + size_t *npixel, bool remain_index) +{ + s_msg[0] = '\0'; + lua_State *L = ((struct decode_context_t*) context)->L; + lua_getglobal(L, "decode_pixels"); + if(!lua_isfunction(L, -1)) + { + lua_pop(L, 1); + if(s_msg[strlen(s_msg) - 1] =='\n') s_msg[strlen(s_msg) - 1] = '\0'; + return STATUS_CALLBACKERROR; + } + + lua_call(L, 0, 3); + *npixel = lua_tointeger(L, 2); + size_t offset = lua_tointeger(L, 3); + if(lua_isuserdata(L, 1)) + { + struct memblock_t* block = lua_touserdata(L, 1); + *pixels = (struct pixel_t *)((uint8_t*)block->p + offset); + } + else if (lua_isstring(L, 1)) + { + const char *data = lua_tostring(L, 1); + *pixels = (struct pixel_t *) data; + } + else + { + goto decode_pixels_fail; + } + lua_pop(L, 3); + + if(s_msg[strlen(s_msg) - 1] =='\n') s_msg[strlen(s_msg) - 1] = '\0'; + return STATUS_OK; + +decode_pixels_fail: + *npixel = 0; + *pixels = NULL; + return STATUS_FAIL; +} + + PLUGIN_STATUS STDCALL decode_pre_lua(void *context, const uint8_t* rawdata, size_t rawsize, struct tilecfg_t *cfg) { @@ -456,7 +664,7 @@ struct tile_decoder_t g_decoder_lua = { .version = 340, .size = sizeof(struct tile_decoder_t), .msg = s_msg, .context = NULL, .open = decode_open_lua, .close = decode_close_lua, - .decodeone = decode_pixel_lua, .decodeall = NULL, + .decodeone = decode_pixel_lua, .decodeall = decode_pixels_lua, .pre=decode_pre_lua, .post=decode_post_lua, .sendui=decode_sendui_lua, .recvui=decode_recvui_lua, }; \ No newline at end of file