Skip to content

YuriSizuku/TileViewer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

33 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Tile Viewer

GitHub tag (latest by date) GitHub Workflow Status GitHub Workflow Status

☘️ A cross platform tool to visulize and analyze texture (usually used for console game font) by tiles. It is inspired by Crystal Tile 2, and more flexible with custom lua script or C plugin (for more complex situation, such as swizzle) and command line support.

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.

tile_test1 (example of decoding a 2bpp tile font)

In addition, you can use it for data visulization tool as well.

tile_test3 (example of data visulization)

Usage

(1) cmd

Usage: TileViewer [-n] [-i <str>] [-o <str>] [-p <str>] 
    [--start <num>] [--size <num>] [--nrow <num>] 
    [--width <num>] [--height <num>] [--bpp <num>] [--nbytes <num>] [-h] [--verbose]
  -n, --nogui         decode tiles without gui
  -i, --inpath=<str>  tile file inpath
  -o, --outpath=<str> outpath for decoded file
  -p, --plugin=<str>  use lua plugin to decode
  --plugincfg=<str>   use plugincfg, default is xxx.json with xxx plugin
  --start=<num>       tile start offset
  --size=<num>        whole tile size
  --nrow=<num>        how many tiles in a row
  --width=<num>       tile width
  --height=<num>      tile height
  --bpp=<num>         tile bpp
  --nbytes=<num>      bytes number in a tile
  -h, --help          show this help message
  --verbose           generate verbose log messages

command line example

TileViewer --inpath ../asset/sample/it.bin --width 20 --height 18 --bpp 2 --nbytes 92 --outpath it.png
TileViewer --inpath ../asset/sample/c005.spc.dec --plugin ../asset/plugin/narcissus_lbg_psp.lua --outpath c005.spc.png
TileViewer --inpath ../asset/sample/ZI24.FNT --plugincfg ../asset/sample/ZI24_plugincfg.json --outpath ZI24.png

tile_test5 (example of view swizzle texture by narcissus psp, using lua plugin)

(2) gui

It will automaticly search plugins in ./plugin path. You can use these shortcut to control the view.

H|J|K|L cursor moving in tile view
CTRL+O open file
CTRL+L open log window
CTRL+S save decoded tile image
CTRL+B show border in each tile
CTRL++|WHELLUP scale up (zoom in)
CTRL+-|WHELLDOWN scale down (zoom out)
CTRL+R reset scale and fit window to best size

tile_test4 (example of using lua plugin to decode and scale scrolled view)

Develop

%%{init: {'theme':'forest'}}%%
graph LR;
m1[core_app]
m2[TileSolver]
c1[plugin_builtin]
c2[plugin_lua,cmodule]
u1[TopFrame]
u2[MainMenuBar]
u3[ConfigWindow]
u4[TileWinodw]
u5[TileView]

m1 --> u1 -- update status bar --> u2 -- load plugin, file--> m2
m1 --> u3 -- set tilecfg|tilenav --> u4
m1 --> u4 -- update tilenav --> u3
u4 -- render tiles --> u5
u5 -- update postion --> u4
u4 -- load file, decode--> m2
m2 --> c1
m2 --> c2

Loading

(1) Vscode

  • c_cpp_properties.json
{
    "configurations": [
        {
            "name": "Linux x64",
            "includePath": [
                "${workspaceFolder}/src/**", 
                "${workspaceFolder}/build_linux64/**", 
                "${workspaceFolder}/depend/lua-5.4.7/src/**", 
                "${workspaceFolder}/depend/cJSON-1.7.18/**", 
                "${workspaceFolder}/depend/wxWidgets-3.2.6/include"
            ],
            "defines": ["__WXGTK__", "__LINUX__"],
            "compilerPath": "/usr/bin/gcc",
            "cStandard": "c99",
            "cppStandard": "c++11",
            "intelliSenseMode": "linux-gcc-x64"
        }
    ],
    "version": 4
}
  • launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Linux x64",
            "type": "cppdbg",
            "request": "launch",
            "program": "${workspaceRoot}/build_linux64/TileViewer",
            "args": ["--inpath", "sample/Nobara1.bmp",  "--width", "20", "--height", "18", "--bpp 2", "--nbytes 92", 
                    "--plugin", "plugin/util_bmp.lua"],
            "stopAtEntry": false,
            "cwd": "${workspaceRoot}/asset/",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                },
                {
                    "description": "Set Disassembly Flavor to Intel",
                    "text": "-gdb-set disassembly-flavor intel",
                    "ignoreFailures": true
                }
            ]
        }
    ]
}

(2) C plugin

Implement these function for C decoder plugin, then export either struct decoder or function get_decoder, see src/plugin.h in detail.

Here's the workflow for the plugin open -> (sendui) -> ||(recvui) -> (pre) -> decode -> (post) :|| -> close.

struct tile_decoder_t
{
    uint32_t version; // required tileviewer version, for example v0.3.4 = 340
    uint32_t size; // this structure size
    void* context; // opaque pointer for decoder context, user defined struct
    const char *msg; // for passing log informations to log window
    REQUIRED CB_decode_open open; // open the decoder when loading decoder
    REQUIRED CB_decode_close close; // close the decoder when changing decoder
    REQUIRED CB_decode_pixel decodeone; // decode one pixel (fill the (i, x, y) pixel)
    REQUIRED CB_decode_pixels decodeall; // decode all pixels (if not find decodeall, it will use decodeone)
    OPTIONAL CB_decode_parse pre; // before decoding whole tiles (usually make some tmp values here)
    OPTIONAL CB_decode_parse post; // after decoding whole tiles(usually clean some tmp values here)
    OPTIONAL CB_decode_send sendui; // for setting ui widget (it will search xxx.json at first, if not found, use this)
    OPTIONAL CB_decode_recv recvui; // for getting ui widget
};

plugincfg example in built-in

{
"plugincfg": [
    {"name" : "endian", "type": "enum", 
    "options":["little", "big"], 
    "value": 1}]
}

extern c module example asset/util_stb.c

mkdir -p build_mingw64/plugin
. script/fetch_depend.sh
fetch_stb
gcc -g -Idepend/stb-lastest -Isrc -fPIC -fvisibility=hidden -static-libgcc -shared asset/plugin/util_stb.c -o build_mingw64/plugin/util_stb.dll

(3) Lua plugin

Implement decode_pre, decode_pixel and decode_post to decode tiles. You can use capi functions as below, usually in decode_pre to reduce overhead. See asset/plugin/ in detail.

As for debugging lua script, one way is to use log to print values in logwindow; the other way is to redirct stderr to file, for example TileViewer.exe -i c005.spc.dec --plugin plugin/narcissus_lbg_psp.lua >plugin_log.txt 2>&1

Notice that the lua index is start from 1 !

-- c types declear
---@class tilecfg_t
---@field start integer
---@field size integer
---@field nrow integer
---@field w integer
---@field h integer
---@field bpp integer
---@field nbytes integer

-- capis declear 
log = log -- use log(...) to redirect to log window

---@type fun() : tilecfg_t
function get_tilecfg() return {} end -- capi

---@type fun(tilecfg_t)
function set_tilecfg(cfg) end -- capi

---@type fun(): integer
function get_rawsize() return 0 end -- capi

-- get tiles bytes, usually used in decode_pre, and then use this to decode pixel
---@type fun(offset:integer, size: integer): string
function get_rawdata(offset, size) return "" end --capi

-- c callbacks implement
---@type fun() : boolean 
function decode_pre() -- callback for pre process
    -- implement your code here
    return true
end

---@type fun( i: integer, x: integer, y: integer) : integer
function decode_pixel(i, x, y) -- callback for decoding tile i, (x, y) position
    -- implement your code here
end

---@type fun() : boolean 
function decode_post() -- callback for post process
    -- implement your code here
    return true
end

You can also load extern lualib, for example, put json.lua into plugin\lualib then load this plugin by require.

json = require "plugin.lualib.json"
jstr = json.encode({ 1, 2, 3, { x = 10 } })
log(jstr)

Build

(1) Windows

install llvm-mingw if needed

init_mingwsdk() {
    echo "## init_mingwsdk ${MINGWSDK}"
    if ! [ -d ${MINGWSDK} ]; then
        if [ -n "$(uname -a | grep Linux)" ]; then
            curl -fsSL https://github.com/mstorsjo/llvm-mingw/releases/download/20240619/llvm-mingw-20240619-msvcrt-ubuntu-20.04-x86_64.tar.xz -o /tmp/llvm-mingw.tar.xz
            tar xf /tmp/llvm-mingw.tar.xz -C /tmp
            _tmppath=/tmp/llvm-mingw-20240619-msvcrt-ubuntu-20.04-x86_64 
            mv -f ${_tmppath} $MINGWSDK || echo "try to use sudo mv to $MINGWSDK" && sudo mv -f ${_tmppath} $MINGWSDK
            rm -rf /tmp/llvm-mingw.tar.xz
        else
            curl -fsSL https://github.com/mstorsjo/llvm-mingw/releases/download/20240619/llvm-mingw-20240619-msvcrt-x86_64.zip -o ~/llvm-mingw.zip
            7z x ~/llvm-mingw.zip -o$HOME
            mv -f ~/llvm-mingw-20240619-msvcrt-x86_64 $MINGWSDK
            rm -rf ~/llvm-mingw.zip
        fi
    fi
}

then fetch depends and build (choose different CC to build x86 or x64)

git clone https://github.com/YuriSizuku/TileViewer.git
cd TileViewer

# windows llvm-mingw x64 debug
sh -c "export CC=x86_64-w64-mingw32-clang BUILD_DIR=$(pwd)/build_mingw64 BUILD_TYPE=Debug && script/build_mingw.sh"

# windows llvm-mingw x86 release
sh -c "export CC=i686-w64-mingw32-clang BUILD_DIR=$(pwd)/build_mingw32 BUILD_TYPE=MinSizeRel && script/build_mingw.sh"

# linux mingw x64 debug
export CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ WINDRES=x86_64-w64-mingw32-windres BUILD_DIR=build_mingw64 && bash script/build_mingw.sh

# linux mingw x86 release (use gcc below 12 for xp)
export CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ WINDRES=i686-w64-mingw32-windres BUILD_DIR=build_mingw32 && BUILD_TYPE=MinSizeRel && bash script/build_mingw.sh

(2) Linux

install dependencies at first

# for general
sudo apt-get -y install build-essential
sudo apt-get -y install p7zip-full git cmake
sudo apt-get -y install libgtk-3-dev

# for cross x86
sudo dpkg --add-architecture i386
sudo apt-get -y update
sudo apt-get -y install gcc-multilib g++-multilib gdb-multiarch
sudo apt-get -y install crossbuild-essential-i386
sudo apt-get -y install libc6:i386 libx11-dev:i386 libxtst-dev:i386 libsm-dev:i386
sudo apt-get -y install libgtk-3-dev:i386 

# for cross armhf
sudo dpkg --add-architecture armhf
sudo apt-get -y update
sudo apt-get -y install crossbuild-essential-armhf
sudo apt-get -y install crossbuild-essential-arm64
sudo apt-get -y install libc6:armhf libx11-dev:armhf libxtst-dev:armhf libsm-dev:armhf
sudo apt-get -y install libgtk-3-dev:armhf 

linux local build

git clone https://github.com/YuriSizuku/TileViewer.git
cd TileViewer
chmod +x script/*.sh

# linux x64 debug
export CC=x86_64-linux-gnu-gcc CXX=x86_64-linux-gnu-g++ BUILD_DIR=build_linux64 BUILD_TYPE=Debug && bash script/build_linux.sh

# linux x86 debug
export CC=i686-linux-gnu-gcc CXX=i686-linux-gnu-g++ BUILD_DIR=build_linux32 BUILD_TYPE=Debug && bash script/build_linux.sh

linux cross build by docker

sudo apt-get -y install qemu-user-static binfmt-support

export DOCKER_ARCH=i386 BUILD_DIR=build_linux32_docker BUILD_TYPE=MinSizeRel && bash script/build_docker.sh
export DOCKER_ARCH=x86_64 BUILD_DIR=build_linux64_docker BUILD_TYPE=MinSizeRel && bash script/build_docker.sh
export DOCKER_ARCH=armhf BUILD_DIR=build_linuxa32_docker BUILD_TYPE=MinSizeRel USE_BUILDX=1 && bash script/build_docker.sh
export DOCKER_ARCH=aarch64 BUILD_DIR=build_linuxa64_docker BUILD_TYPE=MinSizeRel USE_BUILDX=1 && bash script/build_docker.sh

(3) macOS

# install Xcode Command Line Tools
xcode-select --install

# install Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# install dependency using Homebrew
brew install wxwidgets
brew install p7zip
brew install gtk+3
brew install cmake

git clone https://github.com/YuriSizuku/TileViewer.git
cd TileViewer
chmod +x script/*.sh
./script/build_macos.sh

Roadmap

  • Core

    • redirect log message to log window
    • implement command lines
    • decoder interface with different plugin (builtin, lua, C)
    • automaticaly reload the plugin when it changes (v0.3.2)
    • use json to transfer infromation from ui to decoder
  • Plugin

    • plugin built-in decoder, (v0.1)
      • 2|4|8 bpp, little endian
      • 3bpp (3 bytes for 8 pixels) (v0.3.3.7)
      • 16bpp(rgb565), 24bpp(rgb888), 32bpp(rgba8888)
      • plugincfg, endian, channel_first, bgr, flip (v0.3.4.3)
    • plugin lua decoder (v0.2)
    • plugin C decoder (dll, so) (v0.3)
  • UI

    • start up with hello world, cmake structure for the project
    • inital layout, left config view, right tile view, top menu, bottom status
    • select and render tiles in real time when format changes
    • scale render tile images (zoom in/out) (v0.1.5)
    • color palette load, save editor (partly sovled by plugin)
    • cmodule plugincfg in left property (v0.3.4)
    • lua plugincfg in left property (v0.3.4.2)
  • Build

    • use github action to auto build (v0.1)
    • llvm-mingw compile (x86, x64) (v0.1)
    • linux compile (x86, x64) (v0.1.2)
    • linux cross compile by docker (arm32, arm64) (v0.1.3)
    • windows xp support (by i686-w64-mingw32-gcc (below gcc 12), llvm-mingw not worked, because of tls ? ) (v0.3.3.2)
    • mac local compile (contributed by TomJinW)

Issues

Credit

wxWidget
Lua
cJSON
CrystalTile2