naomi: printer support for f355 and tduno[2]. Fix some bios font glyphs

Thermal printer support for f355 (G2 ext bus) and tduno[2] (maple JVS)
Change game id for sgdrvsim and dragntr3 to distinguish them.
Fix cyrillic page in BIOS font.
This commit is contained in:
Flyinghead 2023-03-22 19:00:50 +01:00
parent fa525b4488
commit 78f80eb7dc
24 changed files with 2427 additions and 27 deletions

View File

@ -842,6 +842,8 @@ target_sources(${PROJECT_NAME} PRIVATE
core/hw/naomi/card_reader.cpp
core/hw/naomi/touchscreen.h
core/hw/naomi/touchscreen.cpp
core/hw/naomi/printer.h
core/hw/naomi/printer.cpp
core/hw/pvr/elan.cpp
core/hw/pvr/elan.h
core/hw/pvr/elan_struct.h
@ -920,6 +922,12 @@ target_sources(${PROJECT_NAME} PRIVATE
core/hw/sh4/sh4_sched.h
core/hw/sh4/storeq.cpp)
cmrc_add_resources(flycast-resources
fonts/printer_ascii8x16.bin
fonts/printer_ascii12x24.bin
fonts/printer_kanji16x16.bin
fonts/printer_kanji24x24.bin)
target_sources(${PROJECT_NAME} PRIVATE
core/imgread/cdi.cpp
core/imgread/chd.cpp

View File

@ -741,10 +741,6 @@ void loadGameSpecificSettings()
if (settings.content.gameId.empty())
return;
}
else
{
settings.content.gameId = naomi_game_id;
}
// Default per-game settings
loadSpecialSettings();

View File

@ -23,6 +23,7 @@
#include "stdclass.h"
#include "cfg/option.h"
#include "network/output.h"
#include "hw/naomi/printer.h"
#include <algorithm>
#include <array>
@ -1510,6 +1511,7 @@ void maple_naomi_jamma::serialize(Serializer& ser) const
{
maple_base::serialize(ser);
ser << crazy_mode;
ser << hotd2p;
ser << jvs_repeat_request;
ser << jvs_receive_length;
ser << jvs_receive_buffer;
@ -1523,6 +1525,10 @@ void maple_naomi_jamma::deserialize(Deserializer& deser)
{
maple_base::deserialize(deser);
deser >> crazy_mode;
if (deser.version() >= Deserializer::V35)
deser >> hotd2p;
else
hotd2p = settings.content.gameId == "hotd2p";
deser >> jvs_repeat_request;
deser >> jvs_receive_length;
deser >> jvs_receive_buffer;
@ -1674,7 +1680,7 @@ u32 jvs_io_board::handle_jvs_message(u8 *buffer_in, u32 length_in, u8 *buffer_ou
break;
default:
if (jvs_cmd >= 0x20 && jvs_cmd <= 0x38) // Read inputs and more
if ((jvs_cmd >= 0x20 && jvs_cmd <= 0x38) || jvs_cmd == 0x74) // Read inputs and more
{
LOGJVS("JVS Node %d: ", node_id);
u32 buttons[4] {};
@ -1910,6 +1916,25 @@ u32 jvs_io_board::handle_jvs_message(u8 *buffer_in, u32 length_in, u8 *buffer_ou
cmdi += 4;
break;
case 0x74: // Custom: used to read and write the board serial port (touch de uno)
{
u32 len = buffer_in[cmdi + 1];
for (u32 i = 0; i < len; i++)
printer::print(buffer_in[cmdi + 2 + i]);
cmdi += len + 2;
JVS_STATUS1(); // report
// data
// 00 hardware error
// 01 head up error
// 02 Vp Volt error
// 03 auto cutter error
// 04 head temp error
// 3* paper end error
JVS_OUT(0xf); // printer ok
}
break;
default:
INFO_LOG(MAPLE, "JVS: Unknown input type %x", buffer_in[cmdi]);
JVS_OUT(2); // report byte: command error

View File

@ -18,6 +18,7 @@
#include "network/output.h"
#include "hw/sh4/modules/modules.h"
#include "rend/gui.h"
#include "printer.h"
#include <algorithm>
@ -846,3 +847,42 @@ void initDriveSimSerialPipe()
pipe.reset();
serial_setPipe(&pipe);
}
G2PrinterConnection g2PrinterConnection;
u32 G2PrinterConnection::read(u32 addr, u32 size)
{
if (addr == STATUS_REG_ADDR)
{
u32 ret = printerStat;
printerStat |= 1;
DEBUG_LOG(NAOMI, "Printer status == %x", ret);
return ret;
}
else
{
INFO_LOG(NAOMI, "Unhandled G2 Ext read<%d> at %x", size, addr);
return 0;
}
}
void G2PrinterConnection::write(u32 addr, u32 size, u32 data)
{
switch (addr)
{
case DATA_REG_ADDR:
for (u32 i = 0; i < size; i++)
printer::print((char)(data >> (i * 8)));
break;
case STATUS_REG_ADDR:
DEBUG_LOG(NAOMI, "Printer status = %x", data);
printerStat &= ~1;
break;
default:
INFO_LOG(NAOMI, "Unhandled G2 Ext write<%d> at %x: %x", size, addr, data);
break;
}
}

View File

@ -33,19 +33,39 @@ void initDriveSimSerialPipe();
u32 libExtDevice_ReadMem_A0_006(u32 addr, u32 size);
void libExtDevice_WriteMem_A0_006(u32 addr, u32 data, u32 size);
class G2PrinterConnection
{
public:
u32 read(u32 addr, u32 size);
void write(u32 addr, u32 size, u32 data);
static constexpr u32 STATUS_REG_ADDR = 0x1018000;
static constexpr u32 DATA_REG_ADDR = 0x1010000;
private:
u32 printerStat = 0xf;
};
extern G2PrinterConnection g2PrinterConnection;
extern Multiboard *multiboard;
//Area 0 , 0x01000000- 0x01FFFFFF [G2 Ext. Device]
static inline u32 g2ext_readMem(u32 addr, u32 size)
{
if (addr == G2PrinterConnection::STATUS_REG_ADDR || addr == G2PrinterConnection::DATA_REG_ADDR)
return g2PrinterConnection.read(addr, size);
if (multiboard != nullptr)
return multiboard->readG2Ext(addr, size);
INFO_LOG(NAOMI, "Unhandled G2 Ext read<%d> at %x", size, addr);
return 0;
}
static inline void g2ext_writeMem(u32 addr, u32 data, u32 size)
{
if (multiboard != nullptr)
if (addr == G2PrinterConnection::STATUS_REG_ADDR || addr == G2PrinterConnection::DATA_REG_ADDR)
g2PrinterConnection.write(addr, size, data);
else if (multiboard != nullptr)
multiboard->writeG2Ext(addr, size, data);
else
INFO_LOG(NAOMI, "Unhandled G2 Ext write<%d> at %x: %x", size, addr, data);

View File

@ -40,11 +40,11 @@
#include "card_reader.h"
#include "naomi_flashrom.h"
#include "touchscreen.h"
#include "printer.h"
Cartridge *CurrentCartridge;
bool bios_loaded = false;
char naomi_game_id[33];
InputDescriptors *NaomiGameInputs;
u8 *naomi_default_eeprom;
@ -413,7 +413,7 @@ static void loadMameRom(const char *filename, LoadProgress *progress)
md5.getDigest(settings.network.md5.game);
}
// Default game name if ROM boot id isn't found
strcpy(naomi_game_id, game->name);
settings.content.gameId = game->name;
} catch (...) {
delete CurrentCartridge;
@ -595,12 +595,21 @@ void naomi_cart_LoadRom(const char* file, LoadProgress *progress)
atomiswaveForceFeedback = false;
RomBootID bootId;
if (CurrentCartridge->GetBootId(&bootId))
if (CurrentCartridge->GetBootId(&bootId)
&& (!memcmp(bootId.boardName, "NAOMI", 5) || !memcmp(bootId.boardName, "Naomi2", 6)))
{
std::string gameId = trim_trailing_ws(std::string(bootId.gameTitle[0], &bootId.gameTitle[0][32]));
if (strlen(gameId.c_str()) > 0)
strcpy(naomi_game_id, gameId.c_str());
NOTICE_LOG(NAOMI, "NAOMI GAME ID [%s] region %x players %x vertical %x", naomi_game_id, (u8)bootId.country, bootId.cabinet, bootId.vertical);
if (gameId == "SAMPLE GAME MAX LONG NAME-")
{
// Use better game names
if (!strcmp(CurrentCartridge->game->name, "sgdrvsim"))
gameId = "SEGA DRIVING SIMULATOR";
else if (!strcmp(CurrentCartridge->game->name, "dragntr3"))
gameId = "DRAGON TREASURE 3";
}
if (!gameId.empty())
settings.content.gameId = gameId;
NOTICE_LOG(NAOMI, "NAOMI GAME ID [%s] region %x players %x vertical %x", settings.content.gameId.c_str(), (u8)bootId.country, bootId.cabinet, bootId.vertical);
if (gameId == "INITIAL D"
|| gameId == "INITIAL D Ver.2"
@ -616,7 +625,7 @@ void naomi_cart_LoadRom(const char* file, LoadProgress *progress)
}
else if (gameId == "THE KING OF ROUTE66"
|| gameId == "CLUB KART IN JAPAN"
|| gameId == "SAMPLE GAME MAX LONG NAME-") // Driving Simulator
|| gameId == "SEGA DRIVING SIMULATOR")
{
if (settings.naomi.drivingSimSlave == 0)
initMidiForceFeedback();
@ -626,10 +635,17 @@ void naomi_cart_LoadRom(const char* file, LoadProgress *progress)
{
touchscreen::init();
}
if (gameId == " TOUCH DE UNOH -------------"
|| gameId == " TOUCH DE UNOH 2 -----------"
// only for F355 Deluxe
|| (gameId == "F355 CHALLENGE JAPAN" && !strcmp(CurrentCartridge->game->name, "f355")))
{
printer::init();
}
#ifdef NAOMI_MULTIBOARD
// Not a multiboard game but needs the same desktop environment
if (gameId == "SAMPLE GAME MAX LONG NAME-") // Driving Simulator
if (gameId == "SEGA DRIVING SIMULATOR")
{
initDriveSimSerialPipe();
@ -659,7 +675,7 @@ void naomi_cart_LoadRom(const char* file, LoadProgress *progress)
#endif
}
else
NOTICE_LOG(NAOMI, "NAOMI GAME ID [%s]", naomi_game_id);
NOTICE_LOG(NAOMI, "NAOMI GAME ID [%s]", settings.content.gameId.c_str());
}
void naomi_cart_ConfigureEEPROM()
@ -678,6 +694,7 @@ void naomi_cart_ConfigureEEPROM()
void naomi_cart_Close()
{
touchscreen::term();
printer::term();
delete CurrentCartridge;
CurrentCartridge = nullptr;
NaomiGameInputs = nullptr;
@ -689,6 +706,7 @@ void naomi_cart_serialize(Serializer& ser)
if (CurrentCartridge != nullptr)
CurrentCartridge->Serialize(ser);
touchscreen::serialize(ser);
printer::serialize(ser);
}
void naomi_cart_deserialize(Deserializer& deser)
@ -696,6 +714,7 @@ void naomi_cart_deserialize(Deserializer& deser)
if (CurrentCartridge != nullptr && (!settings.platform.isAtomiswave() || deser.version() >= Deserializer::V10_LIBRETRO))
CurrentCartridge->Deserialize(deser);
touchscreen::deserialize(deser);
printer::deserialize(deser);
}
int naomi_cart_GetPlatform(const char *path)

View File

@ -140,7 +140,6 @@ void naomi_cart_ConfigureEEPROM();
void naomi_cart_serialize(Serializer& ser);
void naomi_cart_deserialize(Deserializer& deser);
extern char naomi_game_id[];
extern u8 *naomi_default_eeprom;
extern Cartridge *CurrentCartridge;

View File

@ -561,25 +561,25 @@ static u8 vtennisg_eeprom_dump[] {
0x00, 0x00,
};
// printer disabled, touchscreen calibrated
// touchscreen calibrated
static u8 tduno_eeprom_dump[] {
0x5f, 0xdc, 0x10, 0x42, 0x41, 0x50, 0x31, 0x09, 0x00, 0x1a, 0x01, 0x01, 0x01, 0x00, 0x11, 0x11, 0x11, 0x11,
0x5f, 0xdc, 0x10, 0x42, 0x41, 0x50, 0x31, 0x09, 0x00, 0x1a, 0x01, 0x01, 0x01, 0x00, 0x11, 0x11, 0x11, 0x11,
0xc4, 0x10, 0x18, 0x18, 0xc4, 0x10, 0x18, 0x18, 0x14, 0x00, 0x14, 0x00, 0x16, 0x00, 0x16, 0x00, 0x97, 0x6d,
0x80, 0x3f, 0xa4, 0x4a, 0x80, 0x3f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x14, 0x00, 0x14, 0x00,
0x16, 0x00, 0x16, 0x00, 0x97, 0x6d, 0x80, 0x3f, 0xa4, 0x4a, 0x80, 0x3f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
0xe4, 0x28, 0x18, 0x18, 0xe4, 0x28, 0x18, 0x18, 0x14, 0x00, 0x14, 0x00, 0x16, 0x00, 0x16, 0x00, 0x97, 0x6d,
0x80, 0x3f, 0xa4, 0x4a, 0x80, 0x3f, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x14, 0x00, 0x14, 0x00,
0x16, 0x00, 0x16, 0x00, 0x97, 0x6d, 0x80, 0x3f, 0xa4, 0x4a, 0x80, 0x3f, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
};
// printer disabled, touchscreen calibrated
// touchscreen calibrated
static u8 tduno2_eeprom_dump[] {
0x8a, 0xd3, 0x10, 0x42, 0x42, 0x48, 0x32, 0x09, 0x00, 0x1a, 0x01, 0x01, 0x01, 0x00, 0x11, 0x11, 0x11, 0x11,
0x8a, 0xd3, 0x10, 0x42, 0x42, 0x48, 0x32, 0x09, 0x00, 0x1a, 0x01, 0x01, 0x01, 0x00, 0x11, 0x11, 0x11, 0x11,
0xd1, 0xbd, 0x18, 0x18, 0xd1, 0xbd, 0x18, 0x18, 0x28, 0x00, 0x28, 0x00, 0x2a, 0x00, 0x29, 0x00, 0xbe, 0xeb,
0x80, 0x3f, 0xaa, 0xa4, 0x80, 0x3f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00,
0x2a, 0x00, 0x29, 0x00, 0xbe, 0xeb, 0x80, 0x3f, 0xaa, 0xa4, 0x80, 0x3f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
0xb2, 0x67, 0x18, 0x18, 0xb2, 0x67, 0x18, 0x18, 0x28, 0x00, 0x28, 0x00, 0x2a, 0x00, 0x29, 0x00, 0xbe, 0xeb,
0x80, 0x3f, 0xaa, 0xa4, 0x80, 0x3f, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00,
0x2a, 0x00, 0x29, 0x00, 0xbe, 0xeb, 0x80, 0x3f, 0xaa, 0xa4, 0x80, 0x3f, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01,
0x00, 0x00, 0x4c, 0x27, 0xab, 0x37, 0x7e, 0x03, 0x49, 0x54, 0x78, 0x45, 0x61, 0x7a, 0xa3, 0x52, 0xfc, 0xca,
0xcb, 0x64, 0xbc, 0xe5, 0xa5, 0x68, 0x16, 0x42, 0x69, 0x4e, 0x4c, 0xe8, 0xfc, 0x82, 0x81, 0x78, 0xa6, 0x25,
0x63, 0x46,

1106
core/hw/naomi/printer.cpp Normal file

File diff suppressed because it is too large Load Diff

31
core/hw/naomi/printer.h Normal file
View File

@ -0,0 +1,31 @@
/*
Copyright 2023 flyinghead
This file is part of Flycast.
Flycast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Flycast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "types.h"
namespace printer
{
void init();
void term();
void print(char c);
void serialize(Serializer& ser);
void deserialize(Deserializer& deser);
}

View File

@ -371,7 +371,7 @@ void SetNaomiNetworkConfig(int node)
{
configure_maxspeed_flash(node != -1, node == 0);
}
else if (!strcmp("F355 CHALLENGE JAPAN", naomi_game_id))
else if (gameId == "F355 CHALLENGE JAPAN")
{
// FIXME need default flash
write_naomi_flash(0x230, node == -1 ? 0 : node == 0 ? 1 : 2);
@ -392,7 +392,7 @@ bool NaomiNetworkSupported()
"F355 CHALLENGE JAPAN",
// Naomi 2
"CLUB KART IN JAPAN", "INITIAL D", "INITIAL D Ver.2", "INITIAL D Ver.3", "THE KING OF ROUTE66",
"SAMPLE GAME MAX LONG NAME-" // Driving Simulator
"SEGA DRIVING SIMULATOR"
};
if (!config::NetworkEnable)
return false;

View File

@ -59,7 +59,8 @@ public:
V32,
V33,
V34,
Current = V34,
V35,
Current = V35,
Next = Current + 1,
};

17
fonts/CMakeLists.txt Normal file
View File

@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 3.10)
project(FlycastFonts)
add_executable(biosfont biosfont.cpp fontutil.cpp)
add_executable(printerfont printerfont.cpp fontutil.cpp)
target_compile_features(biosfont PRIVATE cxx_std_17)
target_compile_features(printerfont PRIVATE cxx_std_17)
set_target_properties(biosfont PROPERTIES CXX_EXTENSIONS OFF LINK_FLAGS_RELEASE -s)
set_target_properties(printerfont PROPERTIES CXX_EXTENSIONS OFF LINK_FLAGS_RELEASE -s)
target_compile_options(biosfont PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-Wall>)
target_compile_options(printerfont PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-Wall>)
find_package(PkgConfig REQUIRED)
pkg_check_modules(freetype2 IMPORTED_TARGET freetype2)
target_link_libraries(biosfont PRIVATE PkgConfig::freetype2)
target_link_libraries(printerfont PRIVATE PkgConfig::freetype2)

Binary file not shown.

455
fonts/biosfont.cpp Normal file
View File

@ -0,0 +1,455 @@
//
// Build the dreamcast BIOS font table using the specified fonts with the help of FreeType2.
// Fonts should be listed in increasing priority order since glyphs are replaced by later fonts if found.
//
// biosfont 12x24rk.pcf jiskan24.pcf neep-iso8859-1-12x24.pcf
//
// 12x24rk.pcf: Copyright 1989 by Sony Corp.
// Attrib license
// jiskan24.pcf: Licensed under Public Domain
// neep-iso8859-1-12x24.pcf: Copyright Jim Knoble <jmknoble@pobox.com>
// GPL v2+
//
#include "fontutil.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include <freetype/ftbdf.h>
#include <locale.h>
#include <stdio.h>
#include <errno.h>
#include <wchar.h>
#include <stdint.h>
#include <vector>
#include <cassert>
// BIOS table
// 288 12x24 characters (unicode encoding)
const unsigned charcodes12[] = {
// from, to
// overbar
0xaf, 0xaf,
// ASCII 33-126
'!', '~',
// Yen
0xa5, 0xa5,
// ISO-8859-1 (chars 160-255)
0xa0, 0xff,
// JIS X0201 (chars 160-255)
' ', ' ',
0xff61, 0xff9f,
// FIXME chars E0-FF are unknown???
0xff61, 0xff7f, // wrong
' ', ' ',
};
// 7078 24x24 characters (JIS X0208 encoding)
const unsigned charcodes24[] = {
// prefix, from, to
// JIS X0208 row 33, Symbols
0x21, 0x21, 0x7e,
// JIS X0208 row 34, Symbols
0x22, 0x21, 0x7e,
// JIS X0208 row 35, Roman alphabet
0x23, 0x21, 0x7e,
// JIS X0208 row 36, Hiragana
0x24, 0x21, 0x7e,
// JIS X0208 row 37, Katakana
0x25, 0x21, 0x7e,
// JIS X0208 row 38, Greek
0x26, 0x21, 0x7e,
// JIS X0208 row 39, Cyrillic
0x27, 0x21, 0x7e,
// JIS X0208 row 48
0x30, 0x21, 0x7e,
// JIS X0208 row 49
0x31, 0x21, 0x7e,
// JIS X0208 row 50
0x32, 0x21, 0x7e,
// JIS X0208 row 51
0x33, 0x21, 0x7e,
// JIS X0208 row 52
0x34, 0x21, 0x7e,
// JIS X0208 row 53
0x35, 0x21, 0x7e,
// JIS X0208 row 54
0x36, 0x21, 0x7e,
// JIS X0208 row 55
0x37, 0x21, 0x7e,
// JIS X0208 row 56
0x38, 0x21, 0x7e,
// JIS X0208 row 57
0x39, 0x21, 0x7e,
// JIS X0208 row 58
0x3a, 0x21, 0x7e,
// JIS X0208 row 59
0x3b, 0x21, 0x7e,
// JIS X0208 row 60
0x3c, 0x21, 0x7e,
// JIS X0208 row 61
0x3d, 0x21, 0x7e,
// JIS X0208 row 62
0x3e, 0x21, 0x7e,
// JIS X0208 row 63
0x3f, 0x21, 0x7e,
// JIS X0208 row 64
0x40, 0x21, 0x7e,
// JIS X0208 row 65
0x41, 0x21, 0x7e,
// JIS X0208 row 66
0x41, 0x21, 0x7e,
// JIS X0208 row 67
0x43, 0x21, 0x7e,
// JIS X0208 row 68
0x44, 0x21, 0x7e,
// JIS X0208 row 69
0x45, 0x21, 0x7e,
// JIS X0208 row 70
0x46, 0x21, 0x7e,
// JIS X0208 row 71
0x47, 0x21, 0x7e,
// JIS X0208 row 72
0x48, 0x21, 0x7e,
// JIS X0208 row 73
0x49, 0x21, 0x7e,
// JIS X0208 row 74
0x4a, 0x21, 0x7e,
// JIS X0208 row 75
0x4b, 0x21, 0x7e,
// JIS X0208 row 76
0x4c, 0x21, 0x7e,
// JIS X0208 row 77
0x4d, 0x21, 0x7e,
// JIS X0208 row 78
0x4e, 0x21, 0x7e,
// JIS X0208 row 79
0x4f, 0x21, 0x7e,
// JIS X0208 row 80
0x50, 0x21, 0x7e,
// JIS X0208 row 81
0x51, 0x21, 0x7e,
// JIS X0208 row 82
0x52, 0x21, 0x7e,
// JIS X0208 row 83
0x53, 0x21, 0x7e,
// JIS X0208 row 84
0x54, 0x21, 0x7e,
// JIS X0208 row 85
0x55, 0x21, 0x7e,
// JIS X0208 row 86
0x56, 0x21, 0x7e,
// JIS X0208 row 87
0x57, 0x21, 0x7e,
// JIS X0208 row 88
0x58, 0x21, 0x7e,
// JIS X0208 row 89
0x59, 0x21, 0x7e,
// JIS X0208 row 90
0x5a, 0x21, 0x7e,
// JIS X0208 row 91
0x5b, 0x21, 0x7e,
// JIS X0208 row 92
0x5c, 0x21, 0x7e,
// JIS X0208 row 93
0x5d, 0x21, 0x7e,
// JIS X0208 row 94
0x5e, 0x21, 0x7e,
// JIS X0208 row 95
0x5f, 0x21, 0x7e,
// JIS X0208 row 96
0x60, 0x21, 0x7e,
// JIS X0208 row 97
0x61, 0x21, 0x7e,
// JIS X0208 row 98
0x62, 0x21, 0x7e,
// JIS X0208 row 99
0x63, 0x21, 0x7e,
// JIS X0208 row 100
0x64, 0x21, 0x7e,
// JIS X0208 row 101
0x65, 0x21, 0x7e,
// JIS X0208 row 102
0x66, 0x21, 0x7e,
// JIS X0208 row 103
0x67, 0x21, 0x7e,
// JIS X0208 row 104
0x68, 0x21, 0x7e,
// JIS X0208 row 105
0x69, 0x21, 0x7e,
// JIS X0208 row 106
0x6a, 0x21, 0x7e,
// JIS X0208 row 107
0x6b, 0x21, 0x7e,
// JIS X0208 row 108
0x6c, 0x21, 0x7e,
// JIS X0208 row 109
0x6d, 0x21, 0x7e,
// JIS X0208 row 110
0x6e, 0x21, 0x7e,
// JIS X0208 row 111
0x6f, 0x21, 0x7e,
// JIS X0208 row 112
0x70, 0x21, 0x7e,
// JIS X0208 row 113
0x71, 0x21, 0x7e,
// JIS X0208 row 114
0x72, 0x21, 0x7e,
// JIS X0208 row 115
0x73, 0x21, 0x7e,
// JIS X0208 row 116
0x74, 0x21, 0x26,
// TODO Dreamcast symbols (22 chars)
// copyright U+24B8
// register U+24C7
// trademark U+2122
// up arrow U+2B06
// down U+2B07
// left U+2B05
// right U+27A1 ???
// up+right U+2B08
// down+right U+2B0A
// down+left U+2B0B
// up+left U+2B09
// circled A U+24B6
// circled B U+24B7
// circled C U+24B8
// circled D U+24B8
// circled X U+24CD
// circled Y U+24CE
// circled Z U+24CF
// squared L U+1F13B (Supplementary Multilingual Plane)
// squared R U+1F141 (Supplementary Multilingual Plane)
// start button U+1F142 (Squared S, SMP)
// VMU U+1F4DF (pager) U+1F4F1 (mobile phone)
};
static uint8_t biosfont[288 * 36 + 7078 * 72];
static FT_Library library;
static FT_Face face;
int main(int argc, char *argv[])
{
setlocale(LC_ALL, "");
loadjisx208Table();
if (argc < 2)
{
fprintf(stderr, "Usage: %s <font>\n", argv[0]);
return 1;
}
int error = FT_Init_FreeType(&library);
if (error)
{
fprintf(stderr, "FreeType init failed\n");
return 1;
}
for (int font = 1; font < argc; font++)
{
const char *fontname = argv[font];
long index = 0;
if (strlen(fontname) >= 4 && !strcmp(fontname + strlen(fontname) - 4, ".ttc"))
// 5: NotoSans Mono CJK.ttc
index = strtoul(argv[++font], nullptr, 10);
error = FT_New_Face(library, fontname, index, &face);
if (error)
{
fprintf(stderr, "Can't load %s\n", fontname);
return 1;
}
const char *registry;
const char *encoding;
FT_Get_BDF_Charset_ID(face, &encoding, &registry);
printf("%s: %s %s\n%ld glyphs\n%d fixed sizes\n%d charmaps\n", fontname, encoding, registry, face->num_glyphs, face->num_fixed_sizes, face->num_charmaps);
FT_Bitmap_Size *size = face->available_sizes;
for (int i = 0; i < face->num_fixed_sizes; i++, size++)
printf("%d: %d x %d\n", i + 1, size->width, size->height);
if (face->num_fixed_sizes == 0)
FT_Set_Pixel_Sizes(face, 0, 24);
bool jisx0201Encoding = false;
bool jisx0208Encoding = false;
if (registry != nullptr)
{
if (!strcmp(registry, "JISX0208.1983"))
{
jisx0208Encoding = true;
FT_Set_Charmap(face, face->charmaps[0]);
}
else if (!strcmp(registry, "JISX0201.1976"))
{
jisx0201Encoding = true;
FT_Set_Charmap(face, face->charmaps[0]);
}
}
/* list chars
unsigned gindex;
long charcode = FT_Get_First_Char(face, &gindex);
while (gindex != 0)
{
printf("code %lx index %d\n", charcode, gindex);
charcode = FT_Get_Next_Char(face, charcode, &gindex);
}
*/
unsigned offset = 0;
if (jisx0208Encoding)
{
offset = 288 * 36;
}
else
{
for (size_t i = 0; i < std::size(charcodes12); i += 2)
{
int from = charcodes12[i];
int to = charcodes12[i + 1];
for (int j = from; j <= to; j++)
{
int code = j;
if (jisx0201Encoding)
{
if (code >= 0xff61 && code <= 0xff9f)
code = code - 0xff61 + 0xa1;
else if (code >= 0x80) {
offset += 36;
continue;
}
}
if (!loadGlyph(face, code, 12, 24)) {
offset += 36;
continue;
}
uint8_t *p = face->glyph->bitmap.buffer;
for (int r = 0; r < 24; r++)
{
if (r & 1)
{
biosfont[offset + 1] |= p[0] >> 4;
biosfont[offset + 2] = ((p[0] & 0xf) << 4) | (p[1] >> 4);
offset += 3;
}
else
{
biosfont[offset] = p[0];
biosfont[offset + 1] = p[1];
}
p += face->glyph->bitmap.pitch;
}
}
}
printf("Final offset(12) %d 288 * 36 = %d\n", offset, 288 * 36);
}
if (jisx0201Encoding)
{
offset += 7078 * 72;
}
else
{
for (size_t range = 0; range < std::size(charcodes24); range += 3)
{
int prefix = charcodes24[range];
int from = charcodes24[range + 1];
int to = charcodes24[range + 2];
for (int j = from; j <= to; j++)
{
uint16_t jis = (prefix << 8) | j;
wchar_t u;
if (jisx0208Encoding) {
u = jis;
}
else
{
u = jisx208[jis];
if (u == 0) {
printf("JISX208 conversion failed: [%02x %02x]\n", prefix, j);
offset += 72;
continue;
}
}
if (!loadGlyph(face, u, 24, 24)) {
offset += 72;
continue;
}
const FT_GlyphSlot glyph = face->glyph;
uint8_t *p = glyph->bitmap.buffer;
// left=0 top=22 is ok
// other values need to shift the bitmap
if (glyph->bitmap.pitch == 3 && glyph->bitmap_left == 0 && glyph->bitmap_top == 22) {
memcpy(&biosfont[offset], p, 72);
offset += 72;
}
else
{
const int topFill = std::max(0, 21 - glyph->bitmap_top);
memset(&biosfont[offset], 0, topFill * 3);
offset += topFill * 3;
const int rows = std::min((int)glyph->bitmap.rows, 24 - topFill);
const int width = glyph->bitmap.width;
for (int r = 0; r < rows; r++)
{
if (width == 24)
{
memcpy(&biosfont[offset], p, 3);
}
else
{
int left = glyph->bitmap_left;
unsigned o = offset;
while (left >= 8 && o - offset < 3)
{
left -= 8;
biosfont[o++] = 0;
}
if (o - offset < 3)
{
biosfont[o++] = p[0] >> left;
if (left > 0 && o - offset < 3)
biosfont[o] = p[0] << (8 - left);
if (width > 8 && o - offset < 3)
{
biosfont[o++] |= p[1] >> left;
if (o - offset < 3)
{
if (left > 0)
biosfont[o] = p[1] << (8 - left);
if (width > 16)
biosfont[o++] |= p[2] >> left;
}
}
else
{
o++;
if (o - offset < 3)
biosfont[o] = 0;
}
}
}
offset += 3;
p += glyph->bitmap.pitch;
}
const int bottomFill = 24 - (topFill + rows);
assert(bottomFill >= 0);
memset(&biosfont[offset], 0, bottomFill * 3);
offset += bottomFill * 3;
}
}
}
}
printf("Final offset(24) %d 288 * 36 + 7078 * 72 = %d\n", offset, 288 * 36 + 7078 * 72);
FT_Done_Face(face);
}
FT_Done_FreeType(library);
FILE *f = fopen("biosfont.bin", "wb");
if (f == nullptr) {
perror("biosfont.bin");
return 1;
}
fwrite(biosfont, 1, sizeof(biosfont), f);
fclose(f);
return 0;
}

285
fonts/biosfontpng.cpp Normal file
View File

@ -0,0 +1,285 @@
//
// Make PNGs of the BIOS font for half- and full-width glyphs.
// Compare with http://submarine.org.uk/info/biosfont/
//
#include <stdio.h>
#include <stdint.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb/stb_image_write.h>
#include <vector>
// BIOS table
// 288 12x24 characters (unicode encoding)
const unsigned charcodes12[] = {
// from, to
// overbar
1,
// ASCII 33-126
94,
// Yen
1,
// ISO-8859-1 (chars 160-255)
96,
// JIS X0201 (chars 160-255)
96,
};
// 7078 24x24 characters (JIS X0208 encoding)
const unsigned charcodes24[] = {
// JIS X0208 row 33, Symbols
94,
// JIS X0208 row 34, Symbols
94,
// JIS X0208 row 35, Roman alphabet
94,
// JIS X0208 row 36, Hiragana
94,
// JIS X0208 row 37, Katakana
94,
// JIS X0208 row 38, Greek
94,
// JIS X0208 row 39, Cyrillic
94,
// JIS X0208 row 48
94,
// JIS X0208 row 49
94,
// JIS X0208 row 50
94,
// JIS X0208 row 51
94,
// JIS X0208 row 52
94,
// JIS X0208 row 53
94,
// JIS X0208 row 54
94,
// JIS X0208 row 55
94,
// JIS X0208 row 56
94,
// JIS X0208 row 57
94,
// JIS X0208 row 58
94,
// JIS X0208 row 59
94,
// JIS X0208 row 60
94,
// JIS X0208 row 61
94,
// JIS X0208 row 62
94,
// JIS X0208 row 63
94,
// JIS X0208 row 64
94,
// JIS X0208 row 65
94,
// JIS X0208 row 66
94,
// JIS X0208 row 67
94,
// JIS X0208 row 68
94,
// JIS X0208 row 69
94,
// JIS X0208 row 70
94,
// JIS X0208 row 71
94,
// JIS X0208 row 72
94,
// JIS X0208 row 73
94,
// JIS X0208 row 74
94,
// JIS X0208 row 75
94,
// JIS X0208 row 76
94,
// JIS X0208 row 77
94,
// JIS X0208 row 78
94,
// JIS X0208 row 79
94,
// JIS X0208 row 80
94,
// JIS X0208 row 81
94,
// JIS X0208 row 82
94,
// JIS X0208 row 83
94,
// JIS X0208 row 84
94,
// JIS X0208 row 85
94,
// JIS X0208 row 86
94,
// JIS X0208 row 87
94,
// JIS X0208 row 88
94,
// JIS X0208 row 89
94,
// JIS X0208 row 90
94,
// JIS X0208 row 91
94,
// JIS X0208 row 92
94,
// JIS X0208 row 93
94,
// JIS X0208 row 94
94,
// JIS X0208 row 95
94,
// JIS X0208 row 96
94,
// JIS X0208 row 97
94,
// JIS X0208 row 98
94,
// JIS X0208 row 99
94,
// JIS X0208 row 100
94,
// JIS X0208 row 101
94,
// JIS X0208 row 102
94,
// JIS X0208 row 103
94,
// JIS X0208 row 104
94,
// JIS X0208 row 105
94,
// JIS X0208 row 106
94,
// JIS X0208 row 107
94,
// JIS X0208 row 108
94,
// JIS X0208 row 109
94,
// JIS X0208 row 110
94,
// JIS X0208 row 111
94,
// JIS X0208 row 112
94,
// JIS X0208 row 113
94,
// JIS X0208 row 114
94,
// JIS X0208 row 115
94,
// JIS X0208 row 116
6,
// Dreamcast symbols
22
};
static uint8_t biosfont[288 * 36 + 7078 * 72];
void make12x24()
{
std::vector<uint8_t> bitmap;
constexpr int WIDTH = 16 * 32;
bitmap.resize(WIDTH * 20 * 32); // 16 cols and 20 rows of 32x32 pix
int c = 0;
int y = 0;
for (int pg : charcodes12)
{
while (pg > 0)
{
for (int x = 0; x < 16 && pg > 0; x++, pg--, c++)
{
uint8_t *src = &biosfont[c * 3 * 12];
for (int row = 0; row < 24; row += 2)
{
uint8_t *dst = &bitmap[y * 32 * WIDTH + x * 32 + WIDTH * row];
for (int i = 0; i < 8; i++, dst++)
if (src[0] & (0x80 >> i))
*dst = 0xff;
for (int i = 0; i < 4; i++, dst++)
if (src[1] & (0x80 >> i))
*dst = 0xff;
dst += WIDTH - 12;
for (int i = 0; i < 4; i++, dst++)
if (src[1] & (0x8 >> i))
*dst = 0xff;
for (int i = 0; i < 8; i++, dst++)
if (src[2] & (0x80 >> i))
*dst = 0xff;
src += 3;
}
}
y++;
}
}
stbi_write_png("bios12x24.png", WIDTH, 20 * 32, 1, &bitmap[0], WIDTH);
}
void make24x24()
{
int lines = 0;
for (int pg : charcodes24)
lines += (pg + 15) / 16;
std::vector<uint8_t> bitmap;
constexpr int WIDTH = 16 * 32;
bitmap.resize(WIDTH * lines * 32); // 16 cols and n rows of 32x32 pix
uint8_t *fontbase = &biosfont[288 * 3 * 12];
int c = 0;
int y = 0;
for (int pg : charcodes24)
{
while (pg > 0)
{
for (int x = 0; x < 16 && pg > 0; x++, pg--, c++)
{
uint8_t *src = &fontbase[c * 3 * 24];
for (int row = 0; row < 24; row++)
{
uint8_t *dst = &bitmap[y * 32 * WIDTH + x * 32 + WIDTH * row];
for (int i = 0; i < 8; i++, dst++)
if (src[0] & (0x80 >> i))
*dst = 0xff;
for (int i = 0; i < 8; i++, dst++)
if (src[1] & (0x80 >> i))
*dst = 0xff;
for (int i = 0; i < 8; i++, dst++)
if (src[2] & (0x80 >> i))
*dst = 0xff;
src += 3;
}
}
y++;
}
}
stbi_write_png("bios24x24.png", WIDTH, lines * 32, 1, &bitmap[0], WIDTH);
}
int main(int argc, char *argv[])
{
FILE *f = fopen("biosfont.bin", "rb");
if (f == nullptr) {
perror("biosfont.bin");
return 1;
}
if (fread(biosfont, 1, sizeof(biosfont), f) != sizeof(biosfont))
{
fprintf(stderr, "Invalid bios font file: truncated read");
fclose(f);
return 1;
}
fclose(f);
make12x24();
make24x24();
}

61
fonts/fontutil.cpp Normal file
View File

@ -0,0 +1,61 @@
#include "fontutil.h"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdint.h>
wchar_t jisx208[65536];
void loadjisx208Table()
{
FILE *f = fopen("jisx0213-2004-8bit-std.txt" ,"rb");
if (f == nullptr) {
perror("jisx0213-2004-8bit-std.txt");
return;
}
char buf[512];
while (fgets(buf, sizeof(buf), f) != nullptr)
{
if (buf[0] == '#')
continue;
char *p;
uint16_t jis = strtoul(buf, &p, 16);
p += 3; // "\tU+"
wchar_t utf = strtoul(p, &p, 16);
if (!isspace((int8_t)*p))
continue;
jisx208[jis] = utf;
}
fclose(f);
}
bool loadGlyph(FT_Face face, unsigned glyph, unsigned w, unsigned h)
{
unsigned glyph_index = FT_Get_Char_Index(face, glyph);
if (glyph_index == 0) {
//fprintf(stderr, "Missing glyph(%d) code %x char %lc\n", w, glyph, glyph);
return false;
}
int error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
if (error) {
fprintf(stderr, "Can't load glyph(%d) code %x char %lc\n", w, glyph, glyph);
return false;
}
if (face->glyph->format != FT_GLYPH_FORMAT_BITMAP)
{
error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO);
if (error) {
fprintf(stderr, "Render glyph failed: glyph(%d) code %x char %lc\n", w, glyph, glyph);
return false;
}
}
else if (face->glyph->bitmap.width != w || face->glyph->bitmap.rows != h) {
//fprintf(stderr, "glyph(%d) code %x char %lc wrong size %d x %d\n", w, glyph, glyph, face->glyph->bitmap.width, face->glyph->bitmap.rows);
return false;
}
// printf("%lc bitmap %d x %d pitch %d px mode %d left %d top %d\n", glyph < 0x80 || glyph >= 0xA0 ? glyph : '?',
// face->glyph->bitmap.width, face->glyph->bitmap.rows, face->glyph->bitmap.pitch, face->glyph->bitmap.pixel_mode,
// face->glyph->bitmap_left, face->glyph->bitmap_top);
return true;
}

7
fonts/fontutil.h Normal file
View File

@ -0,0 +1,7 @@
#include <ft2build.h>
#include FT_FREETYPE_H
extern wchar_t jisx208[65536];
void loadjisx208Table();
bool loadGlyph(FT_Face face, unsigned glyph, unsigned w, unsigned h);

75
fonts/maketestpages.cpp Normal file
View File

@ -0,0 +1,75 @@
#include <stdio.h>
void printAsciiTestPage(bool largeFont)
{
const char *fname = largeFont ? "test12x24.dump" : "test8x16.dump";
FILE *f = fopen(fname, "wb");
if (!f) {
perror(fname);
return;
}
fprintf(f, "\33H\22F%c\n", largeFont); // disable Kanji, select font
fprintf(f, " \33-\2TEST PAGE %s\33-%c\n\n", largeFont ? "12x24" : "8x16", 0);
for (int i = 0x20; i < 0x100; i += 0x10)
{
fprintf(f, "%02X", i);
for (int j = 0; j < 0x10; j++)
{
if (i + j == 0xa0 || i + j == 0xff)
fprintf(f, " ");
else
fprintf(f, " %c", i + j);
}
fputc('\n', f);
}
fputc('\n', f);
fputc('\n', f);
fprintf(f, "\33i"); // full cut
fclose(f);
printf("%s created\n", fname);
}
void printKanjiTestPage(bool largeFont)
{
const char *fname = largeFont ? "test24x24.dump" : "test16x16.dump";
FILE *f = fopen(fname, "wb");
if (!f) {
perror(fname);
return;
}
fprintf(f, "\33H\22F%c\n", largeFont); // disable Kanji, select font
fprintf(f, " \33-\2KANJI TEST PAGE %s\33-%c\n\n", largeFont ? "24x24" : "16x16", 0);
for (int plane = 0x21; plane <= 0x7e; plane++)
{
fprintf(f, " Plane %02X\n\n", plane);
for (int c = 0x21; c <= 0x7e; c++)
{
if (c == 0x21 || (c & 0xf) == 0)
fprintf(f, " %02X ", (c & 0xf0));
if (c == 0x21)
fprintf(f, " ");
fprintf(f, "\33K%c%c\33H ", plane, c);
if ((c & 0xf) == 0xf)
fputc('\n', f);
}
fputc('\n', f);
fputc('\n', f);
}
fputc('\n', f);
fputc('\n', f);
fprintf(f, "\33i"); // full cut
fclose(f);
printf("%s created\n", fname);
}
int main(int argc, char *argv[])
{
printAsciiTestPage(false);
printAsciiTestPage(true);
printKanjiTestPage(false);
printKanjiTestPage(true);
return 0;
}

Binary file not shown.

BIN
fonts/printer_ascii8x16.bin Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

255
fonts/printerfont.cpp Normal file
View File

@ -0,0 +1,255 @@
//
// printerfont 12x24rk.pcf jiskan24.pcf jiskan16.pcf ter-u24b.bdf unifont-15.0.01.bdf
//
// TODO:
// 8x16 ascii: missing F1-FD
// 12x24 ascii: missing 80-9F, E0-FF
// 16x16 kanji:
// plane 22 additional chars line 30
// plane 23-28 some additional chars
// plane 2D missing some line 30, line 70 missing all
// kanji planes ok, some additional chars
// 24x24 kanji: ok
//
// 12x24rk.pcf uses JISX0201.1976
// jiskan24 and jiskan16 kanji only
//
// Copyright
// 12x24rk.pcf: Copyright 1989 by Sony Corp.
// Attrib license
// jiskan16.pcf, jiskan24.pcf: Licensed under Public Domain
// GNU unifont: GPL
// Terminus: SIL Open Font License, Version 1.1
//
#include "fontutil.h"
#include <ft2build.h>
#include FT_FREETYPE_H
#include <freetype/ftbdf.h>
#include <locale.h>
#include <stdio.h>
#include <errno.h>
#include <wchar.h>
#include <stdint.h>
#include <vector>
#include <cassert>
static FT_Library library;
static FT_Face face;
// L"▁▂▃▄▅▆▇█▏▎▍▌▋▊▉┼┴┬┤├▔─│▕┌┐└┘╭╮╰╯"
const wchar_t katakana80[33] = L"\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588\u258F\u258E\u258D\u258C\u258B\u258A\u2589\u253C"
"\u2534\u252C\u2524\u251C\u2594\u2500\u2502\u2595\u250C\u2510\u2514\u2518\u256D\u256E\u2570\u256F";
// L" 。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソ"
const wchar_t katakanaA0[33] = L" \uFF61\uFF62\uFF63\uFF64\uFF65\uFF66\uFF67\uFF68\uFF69\uFF6A\uFF6B\uFF6C\uFF6D\uFF6E\uFF6F"
"\uFF70\uFF71\uFF72\uFF73\uFF74\uFF75\uFF76\uFF77\uFF78\uFF79\uFF7A\uFF7B\uFF7C\uFF7D\uFF7E\uFF7F";
// L"タチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙゚"
const wchar_t katakanaC0[33] = L"\uFF80\uFF81\uFF82\uFF83\uFF84\uFF85\uFF86\uFF87\uFF88\uFF89\uFF8A\uFF8B\uFF8C\uFF8D\uFF8E\uFF8F"
"\uFF90\uFF91\uFF92\uFF93\uFF94\uFF95\uFF96\uFF97\uFF98\uFF99\uFF9A\uFF9B\uFF9C\uFF9D\uFF9E\uFF9F";
// L"゠ᅣ‡ᅧ◢◣◤◥♠♥♦♣●○╱╲╳円年月日時分秒〒市区町村人▒ "
const wchar_t katakanaE0[33] = L"\u30A0\uFFC4\u2021\uFFCA\u25E2\u25E3\u25E4\u25E5\u2660\u2665\u2666\u2663\u25CF\u25CB\u2571\u2572"
"\u2573\u5186\u5E74\u6708\u65E5\u6642\u5206\u79D2\u3012\u5E02\u533A\u753A\u6751\u4EBA\u2592 ";
constexpr int ASCII_CHARS = 256 - 32;
constexpr int KANJI_CHARS = 1 + 94 * 94;
static uint8_t ascii8x16[ASCII_CHARS * 16];
static uint8_t kanji16x16[KANJI_CHARS * 2 * 16];
static uint8_t ascii12x24[ASCII_CHARS * 2 * 24];
static uint8_t kanji24x24[KANJI_CHARS * 3 * 24];
bool save(const char *name, void *data, size_t size)
{
FILE *f = fopen(name, "wb");
if (f == nullptr) {
perror(name);
return false;
}
bool status = fwrite(data, size, 1, f) == 1;
fclose(f);
return status;
}
wchar_t convertKatakana(wchar_t c)
{
if (c >= 0xe0)
return katakanaE0[c - 0xe0];
if (c >= 0xc0)
return katakanaC0[c - 0xc0];
if (c >= 0xa0)
return katakanaA0[c - 0xa0];
if (c >= 0x80)
return katakana80[c - 0x80];
return c;
}
int main(int argc, char *argv[])
{
setlocale(LC_ALL, "");
loadjisx208Table();
if (argc < 2)
{
fprintf(stderr, "Usage: %s <font>\n", argv[0]);
return 1;
}
int error = FT_Init_FreeType(&library);
if (error)
{
fprintf(stderr, "FreeType init failed\n");
return 1;
}
for (int font = 1; font < argc; font++)
{
const char *fontname = argv[font];
long index = 0;
if (strlen(fontname) >= 4 && !strcmp(fontname + strlen(fontname) - 4, ".ttc"))
// 5: NotoSans Mono CJK.ttc
index = strtoul(argv[++font], nullptr, 10);
error = FT_New_Face(library, fontname, index, &face);
if (error)
{
fprintf(stderr, "Can't load %s\n", fontname);
return 1;
}
const char *registry;
const char *encoding;
int fontHeight = 0;
FT_Get_BDF_Charset_ID(face, &encoding, &registry);
printf("%s: %s %s\n%ld glyphs\n%d fixed sizes\n%d charmaps\n", fontname, encoding, registry, face->num_glyphs, face->num_fixed_sizes, face->num_charmaps);
FT_Bitmap_Size *size = face->available_sizes;
for (int i = 0; i < face->num_fixed_sizes; i++, size++)
{
printf("%d: %d x %d\n", i + 1, size->width, size->height);
fontHeight = size->height;
}
if (face->num_fixed_sizes == 0)
FT_Set_Pixel_Sizes(face, 0, 24);
bool jisx0201Encoding = false;
bool jisx0208Encoding = false;
if (registry != nullptr)
{
if (!strcmp(registry, "JISX0208.1983"))
{
jisx0208Encoding = true;
FT_Set_Charmap(face, face->charmaps[0]);
}
else if (!strcmp(registry, "JISX0201.1976"))
{
jisx0201Encoding = true;
FT_Set_Charmap(face, face->charmaps[0]);
}
}
if (fontHeight == 16)
{
for (wchar_t c = 32; c < 256; c++)
{
wchar_t uni = convertKatakana(c);
if (!loadGlyph(face, uni, 8, 16))
continue;
uint8_t *src = face->glyph->bitmap.buffer;
uint8_t *dst = &ascii8x16[(c - 32) * 16];
memcpy(dst, src, 16);
}
for (int plane = 0x21; plane <= 0x7e; plane++)
{
for (int c = 0x21; c <= 0x7e; c++)
{
wchar_t code = (plane << 8) | c;
if (!jisx0208Encoding)
code = jisx208[code];
if (!loadGlyph(face, code, 16, 16))
continue;
uint8_t *src = face->glyph->bitmap.buffer;
uint8_t *dst = &kanji16x16[(1 + (plane - 0x21) * 94 + (c - 0x21)) * 2 * 16];
if (face->glyph->bitmap.pitch == 2)
{
memcpy(dst, src, 2 * 16);
}
else
{
for (int y = 0; y < 16; y++)
{
*dst++ = src[0];
*dst++ = src[1];
src += face->glyph->bitmap.pitch;
}
}
}
}
}
else if (fontHeight == 24)
{
for (wchar_t c = 32; c < 256; c++)
{
wchar_t uni;
if (jisx0201Encoding)
{
if ((c >= 0x80 && c <= 0x9F) || (c >= 0xE0 && c <= 0xFF))
continue;
uni = c;
}
else
{
if (c == 0x7f)
continue;
uni = convertKatakana(c);
}
if (!loadGlyph(face, uni, 12, 24))
continue;
uint8_t *src = face->glyph->bitmap.buffer;
uint8_t *dst = &ascii12x24[(c - 32) * 2 * 24];
if (face->glyph->bitmap.pitch == 2)
memcpy(dst, src, 2 * 24);
else
{
for (int y= 0; y < 24; y++)
{
*dst++ = src[0];
*dst++ = src[1];
src += face->glyph->bitmap.pitch;
}
}
}
for (int plane = 0x21; plane <= 0x7e; plane++)
{
for (int c = 0x21; c <= 0x7e; c++)
{
wchar_t code = (plane << 8) | c;
if (!jisx0208Encoding)
code = jisx208[code];
if (!loadGlyph(face, code, 24, 24))
continue;
uint8_t *src = face->glyph->bitmap.buffer;
uint8_t *dst = &kanji24x24[(1 + (plane - 0x21) * 94 + (c - 0x21)) * 3 * 24];
if (face->glyph->bitmap.pitch == 3)
{
memcpy(dst, src, 3 * 24);
}
else
{
for (int y = 0; y < 24; y++)
{
*dst++ = *src++;
*dst++ = *src++;
*dst++ = *src++;
src += face->glyph->bitmap.pitch - 3;
}
}
}
}
}
FT_Done_Face(face);
}
FT_Done_FreeType(library);
save("printer_ascii8x16.bin", ascii8x16, sizeof(ascii8x16));
save("printer_ascii12x24.bin", ascii12x24, sizeof(ascii12x24));
save("printer_kanji16x16.bin", kanji16x16, sizeof(kanji16x16));
save("printer_kanji24x24.bin", kanji24x24, sizeof(kanji24x24));
return 0;
}