Skip to content

Commit f681d34

Browse files
committed
add fnt_extract
1 parent 72c0c7c commit f681d34

File tree

3 files changed

+144
-0
lines changed

3 files changed

+144
-0
lines changed

toolset/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ add_spike_subdir(shader)
1313
add_spike_subdir(arh)
1414
add_spike_subdir(asm)
1515
add_spike_subdir(map)
16+
add_spike_subdir(font)
1617

1718
if(NOT RELEASEVER)
1819
add_spike_subdir(dev)

toolset/font/CMakeLists.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
project(FNTExtract)
2+
3+
build_target(
4+
NAME
5+
fnt_extract
6+
TYPE
7+
ESMODULE
8+
VERSION
9+
1
10+
SOURCES
11+
fnt_extract.cpp
12+
INCLUDES
13+
../common
14+
${TPD_PATH}/spike/3rd_party/json
15+
LINKS
16+
xeno
17+
AUTHOR
18+
"Lukas Cone"
19+
DESCR
20+
"Extract Xenoblade X font to json and texture"
21+
START_YEAR
22+
2023)

toolset/font/fnt_extract.cpp

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/* FNTExtract
2+
Copyright(C) 2023 Lukas Cone
3+
4+
This program is free software : you can redistribute it and / or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with this program.If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
18+
#include "nlohmann/json.hpp"
19+
#include "project.h"
20+
#include "spike/app_context.hpp"
21+
#include "spike/except.hpp"
22+
#include "spike/io/binreader_stream.hpp"
23+
#include "spike/io/binwritter_stream.hpp"
24+
#include "spike/master_printer.hpp"
25+
#include "spike/util/unicode.hpp"
26+
#include "texture.hpp"
27+
28+
std::string_view filters[]{
29+
".fnt$",
30+
};
31+
32+
static AppInfo_s appInfo{
33+
.filteredLoad = true,
34+
.header = FNTExtract_DESC " v" FNTExtract_VERSION ", " FNTExtract_COPYRIGHT
35+
"Lukas Cone",
36+
.filters = filters,
37+
};
38+
39+
AppInfo_s *AppInitModule() { return &appInfo; }
40+
41+
struct Char {
42+
uint16 character;
43+
uint16 unkChracter;
44+
uint8 charWidth;
45+
uint8 charBegin;
46+
};
47+
48+
template <> void FByteswapper(Char &item, bool) { FArraySwapper<uint16>(item); }
49+
50+
struct FNTHeader {
51+
uint32 byteSize; // 2 = utf-16
52+
uint32 fileSize;
53+
uint32 unk1; // 0x10
54+
uint32 textureOffset;
55+
uint32 gridWidth;
56+
uint32 gridHeight;
57+
uint32 numChars;
58+
uint32 unk2[4];
59+
uint32 numColumns;
60+
uint32 numRows;
61+
};
62+
63+
template <> void FByteswapper(FNTHeader &item, bool) { FArraySwapper(item); }
64+
65+
void AppProcessFile(AppContext *ctx) {
66+
std::string fntData = ctx->GetBuffer();
67+
FNTHeader *fntHeader = reinterpret_cast<FNTHeader *>(fntData.data());
68+
69+
FByteswapper(*fntHeader);
70+
71+
if (fntHeader->byteSize != 2) {
72+
throw std::runtime_error("Unknown font byte size");
73+
}
74+
75+
auto hdr = MTXT::Mount(fntData);
76+
77+
{
78+
if (hdr->id != MTXT::ID) {
79+
throw es::InvalidHeaderError(hdr->id);
80+
}
81+
MTXTAddrLib arrdLib;
82+
FByteswapper(*const_cast<MTXT::Header *>(hdr));
83+
ctx->NewImage(
84+
MakeContext(hdr, arrdLib, fntData.data() + fntHeader->textureOffset));
85+
}
86+
87+
std::span<Char> chars(reinterpret_cast<Char *>(fntHeader + 1),
88+
fntHeader->numChars);
89+
90+
nlohmann::json main;
91+
auto &grid = main["grid"];
92+
grid["width"] = fntHeader->gridWidth;
93+
grid["height"] = fntHeader->gridHeight;
94+
grid["rows"] = fntHeader->numRows;
95+
grid["columns"] = fntHeader->numColumns;
96+
auto &rows = main["rows"] = nlohmann::json::array();
97+
98+
size_t curCharId = 0;
99+
100+
for (auto c : chars) {
101+
FByteswapper(c);
102+
103+
if (curCharId % fntHeader->numRows == 0) {
104+
rows.emplace_back(nlohmann::json::array());
105+
}
106+
107+
auto &curRow = rows.back();
108+
auto &curChar = curRow.emplace_back();
109+
uint16 chars[2]{c.character, 0};
110+
curChar["character"] = es::ToUTF8(chars);
111+
chars[0] = c.unkChracter;
112+
curChar["unkChracter"] = es::ToUTF8(chars);
113+
curChar["offset"] = c.charBegin;
114+
curChar["width"] = c.charWidth;
115+
116+
curCharId++;
117+
}
118+
119+
ctx->NewFile(ctx->workingFile.ChangeExtension2("json")).str << std::setw(2)
120+
<< main;
121+
}

0 commit comments

Comments
 (0)