diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/bin2c/.gitignore | 1 | ||||
-rw-r--r-- | tools/bin2c/LICENSE | 19 | ||||
-rw-r--r-- | tools/bin2c/Makefile | 13 | ||||
-rw-r--r-- | tools/bin2c/bin2c.c | 201 | ||||
-rw-r--r-- | tools/preproc/c_file.cpp | 14 | ||||
-rw-r--r-- | tools/rsfont/.gitignore | 1 | ||||
-rw-r--r-- | tools/rsfont/LICENSE | 19 | ||||
-rw-r--r-- | tools/rsfont/Makefile | 15 | ||||
-rw-r--r-- | tools/rsfont/convert_png.c | 169 | ||||
-rw-r--r-- | tools/rsfont/convert_png.h | 11 | ||||
-rw-r--r-- | tools/rsfont/font.c | 457 | ||||
-rw-r--r-- | tools/rsfont/font.h | 30 | ||||
-rw-r--r-- | tools/rsfont/gfx.h | 50 | ||||
-rw-r--r-- | tools/rsfont/global.h | 31 | ||||
-rw-r--r-- | tools/rsfont/main.c | 93 | ||||
-rw-r--r-- | tools/rsfont/util.c | 124 | ||||
-rw-r--r-- | tools/rsfont/util.h | 14 |
17 files changed, 1259 insertions, 3 deletions
diff --git a/tools/bin2c/.gitignore b/tools/bin2c/.gitignore new file mode 100644 index 000000000..366f3d3e9 --- /dev/null +++ b/tools/bin2c/.gitignore @@ -0,0 +1 @@ +bin2c diff --git a/tools/bin2c/LICENSE b/tools/bin2c/LICENSE new file mode 100644 index 000000000..534d15349 --- /dev/null +++ b/tools/bin2c/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016 YamaArashi + +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. diff --git a/tools/bin2c/Makefile b/tools/bin2c/Makefile new file mode 100644 index 000000000..eee19af22 --- /dev/null +++ b/tools/bin2c/Makefile @@ -0,0 +1,13 @@ +CC = gcc + +CFLAGS = -Wall -Wextra -std=c11 -O2 + +.PHONY: clean + +SRCS = bin2c.c + +bin2c: $(SRCS) + $(CC) $(CFLAGS) $(SRCS) -o $@ + +clean: + $(RM) bin2c bin2c.exe diff --git a/tools/bin2c/bin2c.c b/tools/bin2c/bin2c.c new file mode 100644 index 000000000..b4bd437f0 --- /dev/null +++ b/tools/bin2c/bin2c.c @@ -0,0 +1,201 @@ +// Copyright(c) 2015-2016 YamaArashi +// +// 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. + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> + +#ifdef _MSC_VER + +#define FATAL_ERROR(format, ...) \ +do \ +{ \ + fprintf(stderr, format, __VA_ARGS__); \ + exit(1); \ +} while (0) + +#else + +#define FATAL_ERROR(format, ...) \ +do \ +{ \ + fprintf(stderr, format, ##__VA_ARGS__); \ + exit(1); \ +} while (0) + +#endif // _MSC_VER + +unsigned char *ReadWholeFile(char *path, int *size) +{ + FILE *fp = fopen(path, "rb"); + + if (fp == NULL) + FATAL_ERROR("Failed to open \"%s\" for reading.\n", path); + + fseek(fp, 0, SEEK_END); + + *size = ftell(fp); + + unsigned char *buffer = malloc(*size); + + if (buffer == NULL) + FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path); + + rewind(fp); + + if (fread(buffer, *size, 1, fp) != 1) + FATAL_ERROR("Failed to read \"%s\".\n", path); + + fclose(fp); + + return buffer; +} + +int ExtractData(unsigned char *buffer, int offset, int size) +{ + switch (size) + { + case 1: + return buffer[offset]; + case 2: + return (buffer[offset + 1] << 8) + | buffer[offset]; + case 4: + return (buffer[offset + 3] << 24) + | (buffer[offset + 2] << 16) + | (buffer[offset + 1] << 8) + | buffer[offset]; + default: + FATAL_ERROR("Invalid size passed to ExtractData.\n"); + } +} + +int main(int argc, char **argv) +{ + if (argc < 3) + FATAL_ERROR("Usage: bin2c INPUT_FILE VAR_NAME [OPTIONS...]\n"); + + int fileSize; + unsigned char *buffer = ReadWholeFile(argv[1], &fileSize); + char *var_name = argv[2]; + int col = 1; + int pad = 0; + int size = 1; + bool isSigned = false; + bool isStatic = false; + bool isDecimal = false; + + for (int i = 3; i < argc; i++) + { + if (!strcmp(argv[i], "-col")) + { + i++; + + if (i >= argc) + FATAL_ERROR("Missing argument after '-col'.\n"); + + col = atoi(argv[i]); + } + else if (!strcmp(argv[i], "-pad")) + { + i++; + + if (i >= argc) + FATAL_ERROR("Missing argument after '-pad'.\n"); + + pad = atoi(argv[i]); + } + else if (!strcmp(argv[i], "-size")) + { + i++; + + if (i >= argc) + FATAL_ERROR("Missing argument after '-size'.\n"); + + size = atoi(argv[i]); + + if (size != 1 && size != 2 && size != 4) + FATAL_ERROR("Size must be 1, 2, or 4.\n"); + } + else if (!strcmp(argv[i], "-signed")) + { + isSigned = true; + isDecimal = true; + } + else if (!strcmp(argv[i], "-static")) + { + isStatic = true; + } + else if (!strcmp(argv[i], "-decimal")) + { + isDecimal = true; + } + else + { + FATAL_ERROR("Unrecognized option '%s'.\n", argv[i]); + } + } + + if ((fileSize & (size - 1)) != 0) + FATAL_ERROR("Size %d doesn't evenly divide file size %d.\n", size, fileSize); + + printf("// Generated file. Do not edit.\n\n"); + + if (isStatic) + printf("static "); + + printf("const "); + + if (isSigned) + printf("s%d ", 8 * size); + else + printf("u%d ", 8 * size); + + printf("%s[] =\n{", var_name); + + int count = fileSize / size; + int offset = 0; + + for (int i = 0; i < count; i++) + { + if (i % col == 0) + printf("\n "); + + int data = ExtractData(buffer, offset, size); + offset += size; + + if (isDecimal) + { + if (isSigned) + printf("%*d, ", pad, data); + else + printf("%*uu, ", pad, data); + } + else + { + printf("%#*xu, ", pad, data); + } + } + + printf("\n};\n"); + + return 0; +} diff --git a/tools/preproc/c_file.cpp b/tools/preproc/c_file.cpp index 1e4dea359..aed53d44b 100644 --- a/tools/preproc/c_file.cpp +++ b/tools/preproc/c_file.cpp @@ -73,6 +73,7 @@ CFile::~CFile() void CFile::Preproc() { bool inConcatMode = false; + bool noTerminator = false; char stringChar = 0; while (m_pos < m_size) @@ -100,10 +101,13 @@ void CFile::Preproc() else { if (inConcatMode ? m_buffer[m_pos] == '"' - : m_buffer[m_pos] == '_' && m_buffer[m_pos + 1] == '"') + : (m_buffer[m_pos] == '_' || m_buffer[m_pos] == '@') && m_buffer[m_pos + 1] == '"') { if (!inConcatMode) - m_pos++; // skip past underscore + { + noTerminator = (m_buffer[m_pos] == '@'); + m_pos++; // skip past underscore or at-sign + } unsigned char s[kMaxStringLength]; int length; @@ -144,7 +148,11 @@ void CFile::Preproc() if ((c != ' ' && c != '\t' && c != '\n') && inConcatMode) { - std::printf("0xFF }"); + if (noTerminator) + std::printf(" }"); + else + std::printf("0xFF }"); + inConcatMode = false; } diff --git a/tools/rsfont/.gitignore b/tools/rsfont/.gitignore new file mode 100644 index 000000000..3140ececc --- /dev/null +++ b/tools/rsfont/.gitignore @@ -0,0 +1 @@ +rsfont diff --git a/tools/rsfont/LICENSE b/tools/rsfont/LICENSE new file mode 100644 index 000000000..b497950c1 --- /dev/null +++ b/tools/rsfont/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015-2016 YamaArashi + +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. diff --git a/tools/rsfont/Makefile b/tools/rsfont/Makefile new file mode 100644 index 000000000..78e0cab3a --- /dev/null +++ b/tools/rsfont/Makefile @@ -0,0 +1,15 @@ +CC = gcc + +CFLAGS = -Wall -Wextra -std=c11 -O2 + +LIBS = -lz -lpng + +SRCS = main.c convert_png.c util.c font.c + +.PHONY: clean + +rsfont: $(SRCS) convert_png.h gfx.h global.h util.h font.h + $(CC) $(CFLAGS) $(SRCS) -o $@ $(LIBS) + +clean: + $(RM) rsfont rsfont.exe diff --git a/tools/rsfont/convert_png.c b/tools/rsfont/convert_png.c new file mode 100644 index 000000000..f6a30804a --- /dev/null +++ b/tools/rsfont/convert_png.c @@ -0,0 +1,169 @@ +// Copyright (c) 2015 YamaArashi + +#include <stdio.h> +#include <setjmp.h> +#include <png.h> +#include "global.h" +#include "convert_png.h" +#include "gfx.h" + +void ReadPng(char *path, struct Image *image) +{ + FILE *fp = fopen(path, "rb"); + + if (fp == NULL) + FATAL_ERROR("Failed to open \"%s\" for reading.\n", path); + + unsigned char sig[8]; + + if (fread(sig, 8, 1, fp) != 1) + FATAL_ERROR("Failed to read PNG signature from \"%s\".\n", path); + + if (png_sig_cmp(sig, 0, 8)) + FATAL_ERROR("\"%s\" does not have a valid PNG signature.\n", path); + + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (!png_ptr) + FATAL_ERROR("Failed to create PNG read struct.\n"); + + png_infop info_ptr = png_create_info_struct(png_ptr); + + if (!info_ptr) + FATAL_ERROR("Failed to create PNG info struct.\n"); + + if (setjmp(png_jmpbuf(png_ptr))) + FATAL_ERROR("Failed to init I/O for reading \"%s\".\n", path); + + png_init_io(png_ptr, fp); + png_set_sig_bytes(png_ptr, 8); + png_read_info(png_ptr, info_ptr); + + int bit_depth = png_get_bit_depth(png_ptr, info_ptr); + + if (bit_depth != image->bitDepth) + FATAL_ERROR("\"%s\" has a bit depth of %d, but the expected bit depth is %d.\n", path, bit_depth, image->bitDepth); + + int color_type = png_get_color_type(png_ptr, info_ptr); + + if (color_type != PNG_COLOR_TYPE_GRAY && color_type != PNG_COLOR_TYPE_PALETTE) + FATAL_ERROR("\"%s\" has an unsupported color type.\n", path); + + // Check if the image has a palette so that we can tell if the colors need to be inverted later. + // Don't read the palette because it's not needed for now. + image->hasPalette = (color_type == PNG_COLOR_TYPE_PALETTE); + + image->width = png_get_image_width(png_ptr, info_ptr); + image->height = png_get_image_height(png_ptr, info_ptr); + + int rowbytes = png_get_rowbytes(png_ptr, info_ptr); + + image->pixels = malloc(image->height * rowbytes); + + if (image->pixels == NULL) + FATAL_ERROR("Failed to allocate pixel buffer.\n"); + + png_bytepp row_pointers = malloc(image->height * sizeof(png_bytep)); + + if (row_pointers == NULL) + FATAL_ERROR("Failed to allocate row pointers.\n"); + + for (int i = 0; i < image->height; i++) + row_pointers[i] = (png_bytep)(image->pixels + (i * rowbytes)); + + if (setjmp(png_jmpbuf(png_ptr))) + FATAL_ERROR("Error reading from \"%s\".\n", path); + + png_read_image(png_ptr, row_pointers); + + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + + free(row_pointers); + fclose(fp); +} + +void SetPngPalette(png_structp png_ptr, png_infop info_ptr, struct Palette *palette) +{ + png_colorp colors = malloc(palette->numColors * sizeof(png_color)); + + if (colors == NULL) + FATAL_ERROR("Failed to allocate PNG palette.\n"); + + for (int i = 0; i < palette->numColors; i++) { + colors[i].red = palette->colors[i].red; + colors[i].green = palette->colors[i].green; + colors[i].blue = palette->colors[i].blue; + } + + png_set_PLTE(png_ptr, info_ptr, colors, palette->numColors); + + free(colors); +} + +void WritePng(char *path, struct Image *image) +{ + FILE *fp = fopen(path, "wb"); + + if (fp == NULL) + FATAL_ERROR("Failed to open \"%s\" for writing.\n", path); + + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (!png_ptr) + FATAL_ERROR("Failed to create PNG write struct.\n"); + + png_infop info_ptr = png_create_info_struct(png_ptr); + + if (!info_ptr) + FATAL_ERROR("Failed to create PNG info struct.\n"); + + if (setjmp(png_jmpbuf(png_ptr))) + FATAL_ERROR("Failed to init I/O for writing \"%s\".\n", path); + + png_init_io(png_ptr, fp); + + if (setjmp(png_jmpbuf(png_ptr))) + FATAL_ERROR("Error writing header for \"%s\".\n", path); + + int color_type = image->hasPalette ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_GRAY; + + png_set_IHDR(png_ptr, info_ptr, image->width, image->height, + image->bitDepth, color_type, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + if (image->hasPalette) { + SetPngPalette(png_ptr, info_ptr, &image->palette); + + if (image->hasTransparency) { + png_byte trans = 0; + png_set_tRNS(png_ptr, info_ptr, &trans, 1, 0); + } + } + + png_write_info(png_ptr, info_ptr); + + png_bytepp row_pointers = malloc(image->height * sizeof(png_bytep)); + + if (row_pointers == NULL) + FATAL_ERROR("Failed to allocate row pointers.\n"); + + int rowbytes = png_get_rowbytes(png_ptr, info_ptr); + + for (int i = 0; i < image->height; i++) + row_pointers[i] = (png_bytep)(image->pixels + (i * rowbytes)); + + if (setjmp(png_jmpbuf(png_ptr))) + FATAL_ERROR("Error writing \"%s\".\n", path); + + png_write_image(png_ptr, row_pointers); + + if (setjmp(png_jmpbuf(png_ptr))) + FATAL_ERROR("Error ending write of \"%s\".\n", path); + + png_write_end(png_ptr, NULL); + + fclose(fp); + + png_destroy_write_struct(&png_ptr, &info_ptr); + free(row_pointers); +} diff --git a/tools/rsfont/convert_png.h b/tools/rsfont/convert_png.h new file mode 100644 index 000000000..55d3d6942 --- /dev/null +++ b/tools/rsfont/convert_png.h @@ -0,0 +1,11 @@ +// Copyright (c) 2015 YamaArashi + +#ifndef CONVERT_PNG_H +#define CONVERT_PNG_H + +#include "gfx.h" + +void ReadPng(char *path, struct Image *image); +void WritePng(char *path, struct Image *image); + +#endif // CONVERT_PNG_H diff --git a/tools/rsfont/font.c b/tools/rsfont/font.c new file mode 100644 index 000000000..ba0e69cd2 --- /dev/null +++ b/tools/rsfont/font.c @@ -0,0 +1,457 @@ +// Copyright(c) 2015-2016 YamaArashi +// +// 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. + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include "global.h" +#include "font.h" +#include "gfx.h" +#include "util.h" + +unsigned char gFontPalette[][3] = +{ + {0xFF, 0xFF, 0xFF}, // bg (white) + {0x38, 0x38, 0x38}, // fg (dark grey) + {0xD8, 0xD8, 0xD8}, // shadow (light grey) +}; + +void ConvertFromTiles1Bpp(unsigned char *src, unsigned char *dest, int numGlyphs, int layout) +{ + for (int glyph = 0; glyph < numGlyphs; glyph++) + { + if (layout == 0) + { + for (int i = 0; i < 8; i++) + { + unsigned char srcRow = src[(glyph * 8) + i]; + + for (int j = 0; j < 8; j++) + { + int x = ((glyph % 16) * 8) + j; + int y = ((glyph / 16) * 8) + i; + dest[(y * 128) + x] = (srcRow >> (7 - j)) & 1; + } + } + } + else + { + // layout type 1 + + int tile1Offset = glyph * 16; + int tile2Offset = tile1Offset + 8; + + for (int i = 0; i < 8; i++) + { + unsigned char srcRow = src[tile1Offset + i]; + + for (int j = 0; j < 8; j++) + { + int x = ((glyph % 16) * 8) + j; + int y = ((glyph / 16) * 16) + i; + dest[(y * 128) + x] = (srcRow >> (7 - j)) & 1; + } + } + + for (int i = 0; i < 8; i++) + { + unsigned char srcRow = src[tile2Offset + i]; + + for (int j = 0; j < 8; j++) + { + int x = ((glyph % 16) * 8) + j; + int y = ((glyph / 16) * 16) + 8 + i; + dest[(y * 128) + x] = (srcRow >> (7 - j)) & 1; + } + } + } + } +} + +void ConvertToTiles1Bpp(unsigned char *src, unsigned char *dest, int numGlyphs, int layout) +{ + for (int glyph = 0; glyph < numGlyphs; glyph++) + { + if (layout == 0) + { + for (int i = 0; i < 8; i++) + { + unsigned char destRow = 0; + + for (int j = 0; j < 8; j++) + { + int x = ((glyph % 16) * 8) + j; + int y = ((glyph / 16) * 8) + i; + unsigned char color = src[(y * 128) + x]; + + if (color > 1) + FATAL_ERROR("More than 2 colors in 1 BPP font.\n"); + + destRow <<= 1; + destRow |= color; + } + + dest[(glyph * 8) + i] = destRow; + } + } + else + { + // layout type 1 + + int tile1Offset = glyph * 16; + int tile2Offset = tile1Offset + 8; + + for (int i = 0; i < 8; i++) + { + unsigned char destRow = 0; + + for (int j = 0; j < 8; j++) + { + int x = ((glyph % 16) * 8) + j; + int y = ((glyph / 16) * 16) + i; + unsigned char color = src[(y * 128) + x]; + + if (color > 1) + FATAL_ERROR("More than 2 colors in 1 BPP font.\n"); + + destRow <<= 1; + destRow |= color; + } + + dest[tile1Offset + i] = destRow; + } + + for (int i = 0; i < 8; i++) + { + unsigned char destRow = 0; + + for (int j = 0; j < 8; j++) + { + int x = ((glyph % 16) * 8) + j; + int y = ((glyph / 16) * 16) + 8 + i; + unsigned char color = src[(y * 128) + x]; + + if (color > 1) + FATAL_ERROR("More than 2 colors in 1 BPP font.\n"); + + destRow <<= 1; + destRow |= color; + } + + dest[tile2Offset + i] = destRow; + } + } + } +} + +void ConvertFromTiles4Bpp(unsigned char *src, unsigned char *dest, int numGlyphs, int layout) +{ + static unsigned char table[16] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, + }; + + for (int glyph = 0; glyph < numGlyphs; glyph++) + { + if (layout == 0) + { + int offset = glyph * 32; + + for (int i = 0; i < 8; i++) + { + unsigned long srcRow = (src[offset + 3] << 24) + | (src[offset + 2] << 16) + | (src[offset + 1] << 8) + | src[offset]; + + for (int j = 0; j < 8; j++) + { + int x = ((glyph % 16) * 8) + j; + int y = ((glyph / 16) * 8) + i; + dest[(y * 128) + x] = table[srcRow & 0xF]; + srcRow >>= 4; + } + + offset += 4; + } + } + else + { + int tile1Offset; + int tile2Offset; + + if (layout == 1) + { + tile1Offset = glyph * 64; + tile2Offset = tile1Offset + 32; + } + else + { + tile1Offset = ((glyph / 16) * 1024) + ((glyph % 16) * 32); + tile2Offset = tile1Offset + 512; + } + + for (int i = 0; i < 8; i++) + { + unsigned long srcRow = (src[tile1Offset + 3] << 24) + | (src[tile1Offset + 2] << 16) + | (src[tile1Offset + 1] << 8) + | src[tile1Offset]; + + for (int j = 0; j < 8; j++) + { + int x = ((glyph % 16) * 8) + j; + int y = ((glyph / 16) * 16) + i; + dest[(y * 128) + x] = table[srcRow & 0xF]; + srcRow >>= 4; + } + + tile1Offset += 4; + } + + for (int i = 0; i < 8; i++) + { + unsigned long srcRow = (src[tile2Offset + 3] << 24) + | (src[tile2Offset + 2] << 16) + | (src[tile2Offset + 1] << 8) + | src[tile2Offset]; + + for (int j = 0; j < 8; j++) + { + int x = ((glyph % 16) * 8) + j; + int y = ((glyph / 16) * 16) + 8 + i; + dest[(y * 128) + x] = table[srcRow & 0xF]; + srcRow >>= 4; + } + + tile2Offset += 4; + } + } + } +} + +void ConvertToTiles4Bpp(unsigned char *src, unsigned char *dest, int numGlyphs, int layout) +{ + static unsigned char table[3] = + { + 0, 15, 14, + }; + + for (int glyph = 0; glyph < numGlyphs; glyph++) + { + if (layout == 0) + { + int offset = glyph * 32; + + for (int i = 0; i < 8; i++) + { + unsigned long destRow = 0; + + for (int j = 0; j < 8; j++) + { + int x = ((glyph % 16) * 8) + j; + int y = ((glyph / 16) * 8) + i; + unsigned char color = src[(y * 128) + x]; + + if (color > 2) + FATAL_ERROR("More than 3 colors in 4 BPP font.\n"); + + destRow >>= 4; + destRow |= (table[color] << 28); + } + + dest[offset] = destRow & 0xFF; + dest[offset + 1] = (destRow >> 8) & 0xFF; + dest[offset + 2] = (destRow >> 16) & 0xFF; + dest[offset + 3] = (destRow >> 24) & 0xFF; + + offset += 4; + } + } + else + { + int tile1Offset; + int tile2Offset; + + if (layout == 1) + { + tile1Offset = glyph * 64; + tile2Offset = tile1Offset + 32; + } + else + { + tile1Offset = ((glyph / 16) * 1024) + ((glyph % 16) * 32); + tile2Offset = tile1Offset + 512; + } + + for (int i = 0; i < 8; i++) + { + unsigned long destRow = 0; + + for (int j = 0; j < 8; j++) + { + int x = ((glyph % 16) * 8) + j; + int y = ((glyph / 16) * 16) + i; + unsigned char color = src[(y * 128) + x]; + + if (color > 2) + FATAL_ERROR("More than 3 colors in 4 BPP font.\n"); + + destRow >>= 4; + destRow |= (table[color] << 28); + } + + dest[tile1Offset] = destRow & 0xFF; + dest[tile1Offset + 1] = (destRow >> 8) & 0xFF; + dest[tile1Offset + 2] = (destRow >> 16) & 0xFF; + dest[tile1Offset + 3] = (destRow >> 24) & 0xFF; + + tile1Offset += 4; + } + + for (int i = 0; i < 8; i++) + { + unsigned long destRow = 0; + + for (int j = 0; j < 8; j++) + { + int x = ((glyph % 16) * 8) + j; + int y = ((glyph / 16) * 16) + 8 + i; + unsigned char color = src[(y * 128) + x]; + + if (color > 2) + FATAL_ERROR("More than 3 colors in 4 BPP font.\n"); + + destRow >>= 4; + destRow |= (table[color] << 28); + } + + dest[tile2Offset] = destRow & 0xFF; + dest[tile2Offset + 1] = (destRow >> 8) & 0xFF; + dest[tile2Offset + 2] = (destRow >> 16) & 0xFF; + dest[tile2Offset + 3] = (destRow >> 24) & 0xFF; + + tile2Offset += 4; + } + } + } +} + +static void SetFontPalette(struct Image *image) +{ + image->hasPalette = true; + + image->palette.numColors = 3; + + for (int i = 0; i < image->palette.numColors; i++) + { + image->palette.colors[i].red = gFontPalette[i][0]; + image->palette.colors[i].green = gFontPalette[i][1]; + image->palette.colors[i].blue = gFontPalette[i][2]; + } + + image->hasTransparency = false; +} + +int CalcFileSize(int numGlyphs, int bpp, int layout) +{ + if (layout == 2) + { + // assume 4 BPP + int numFullRows = numGlyphs / 16; + int remainder = numGlyphs % 16; + int fullRowsSize = numFullRows * 1024; + int remainderSize = 0; + + if (remainder != 0) + remainderSize = 1024 - (16 - remainder) * 32; + + return fullRowsSize + remainderSize; + } + else + { + int tilesPerGlyph = layout > 0 ? 2 : 1; + int bytesPerTile = 8 * bpp; + return numGlyphs * tilesPerGlyph * bytesPerTile; + } +} + +void ReadFont(char *path, struct Image *image, int numGlyphs, int bpp, int layout) +{ + int fileSize; + unsigned char *buffer = ReadWholeFile(path, &fileSize); + + int tilesPerGlyph = layout > 0 ? 2 : 1; + + int expectedFileSize = CalcFileSize(numGlyphs, bpp, layout); + + if (fileSize != expectedFileSize) + FATAL_ERROR("The file size is %d but should be %d.\n", fileSize, expectedFileSize); + + int numRows = (numGlyphs + 15) / 16; + int rowHeight = layout > 0 ? 16 : 8; + + image->width = 128; + image->height = numRows * rowHeight; + image->bitDepth = 8; + image->pixels = calloc(image->width * image->height, 1); + + if (image->pixels == NULL) + FATAL_ERROR("Failed to allocate memory for font.\n"); + + if (bpp == 1) + ConvertFromTiles1Bpp(buffer, image->pixels, numGlyphs, layout); + else + ConvertFromTiles4Bpp(buffer, image->pixels, numGlyphs, layout); + + free(buffer); + + SetFontPalette(image); +} + +void WriteFont(char *path, struct Image *image, int numGlyphs, int bpp, int layout) +{ + if (image->width != 128) + FATAL_ERROR("The width of the font image (%d) is not 128.\n", image->width); + + int numRows = (numGlyphs + 15) / 16; + int rowHeight = layout > 0 ? 16 : 8; + int expectedHeight = numRows * rowHeight; + + if (image->height < expectedHeight) + FATAL_ERROR("The height of the font image (%d) is less than %d.\n", image->height, expectedHeight); + + int fileSize = CalcFileSize(numGlyphs, bpp, layout); + + unsigned char *buffer = calloc(fileSize, 1); + + if (buffer == NULL) + FATAL_ERROR("Failed to allocate memory for font.\n"); + + if (bpp == 1) + ConvertToTiles1Bpp(image->pixels, buffer, numGlyphs, layout); + else + ConvertToTiles4Bpp(image->pixels, buffer, numGlyphs, layout); + + WriteWholeFile(path, buffer, fileSize); + + free(buffer); +} diff --git a/tools/rsfont/font.h b/tools/rsfont/font.h new file mode 100644 index 000000000..1cd48757f --- /dev/null +++ b/tools/rsfont/font.h @@ -0,0 +1,30 @@ +// Copyright(c) 2015-2016 YamaArashi +// +// 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. + +#ifndef FONT_H +#define FONT_H + +#include <stdbool.h> +#include "gfx.h" + +void ReadFont(char *path, struct Image *image, int numGlyphs, int bpp, int layout); +void WriteFont(char *path, struct Image *image, int numGlyphs, int bpp, int layout); + +#endif // FONT_H diff --git a/tools/rsfont/gfx.h b/tools/rsfont/gfx.h new file mode 100644 index 000000000..04a3d80c7 --- /dev/null +++ b/tools/rsfont/gfx.h @@ -0,0 +1,50 @@ +// Copyright(c) 2015-2016 YamaArashi +// +// 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. + +#ifndef GFX_H +#define GFX_H + +#include <stdint.h> +#include <stdbool.h> + +struct Color +{ + unsigned char red; + unsigned char green; + unsigned char blue; +}; + +struct Palette +{ + struct Color colors[256]; + int numColors; +}; + +struct Image { + int width; + int height; + int bitDepth; + unsigned char *pixels; + bool hasPalette; + struct Palette palette; + bool hasTransparency; +}; + +#endif // GFX_H diff --git a/tools/rsfont/global.h b/tools/rsfont/global.h new file mode 100644 index 000000000..65dd351d2 --- /dev/null +++ b/tools/rsfont/global.h @@ -0,0 +1,31 @@ +// Copyright (c) 2015 YamaArashi + +#ifndef GLOBAL_H +#define GLOBAL_H + +#include <stdio.h> +#include <stdlib.h> + +#ifdef _MSC_VER + +#define FATAL_ERROR(format, ...) \ +do { \ + fprintf(stderr, format, __VA_ARGS__); \ + exit(1); \ +} while (0) + +#define UNUSED + +#else + +#define FATAL_ERROR(format, ...) \ +do { \ + fprintf(stderr, format, ##__VA_ARGS__); \ + exit(1); \ +} while (0) + +#define UNUSED __attribute__((__unused__)) + +#endif // _MSC_VER + +#endif // GLOBAL_H diff --git a/tools/rsfont/main.c b/tools/rsfont/main.c new file mode 100644 index 000000000..2f5d9d971 --- /dev/null +++ b/tools/rsfont/main.c @@ -0,0 +1,93 @@ +// Copyright(c) 2015-2016 YamaArashi +// +// 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. + +#include <stdio.h> +#include <string.h> +#include <stdbool.h> +#include "global.h" +#include "util.h" +#include "gfx.h" +#include "convert_png.h" +#include "font.h" + +int ExtensionToBpp(const char *extension) +{ + if (!strcmp(extension, "1bpp")) + return 1; + else if (!strcmp(extension, "4bpp")) + return 4; + return 0; +} + +int main(int argc, char **argv) +{ + if (argc < 5) + FATAL_ERROR("Usage: rsfont INPUT_FILE OUTPUT_FILE NUM_GLYPHS LAYOUT_TYPE\n"); + + char *inputPath = argv[1]; + char *outputPath = argv[2]; + char *inputFileExtension = GetFileExtension(inputPath); + char *outputFileExtension = GetFileExtension(outputPath); + + if (inputFileExtension == NULL) + FATAL_ERROR("Input file \"%s\" has no extension.\n", inputPath); + + if (outputFileExtension == NULL) + FATAL_ERROR("Output file \"%s\" has no extension.\n", outputPath); + + int numGlyphs; + int bpp; + int layout; + + if (!ParseNumber(argv[3], NULL, 10, &numGlyphs)) + FATAL_ERROR("Failed to parse number of glyphs.\n"); + + if (!ParseNumber(argv[4], NULL, 10, &layout)) + FATAL_ERROR("Failed to parse layout type.\n"); + + if (layout < 0 || layout > 2) + FATAL_ERROR("Layout type %d is invalid. Layout type must be 0, 1, or 2.\n", layout); + + bool toPng; + + if (!strcmp(inputFileExtension, "png") && (bpp = ExtensionToBpp(outputFileExtension)) != 0) + toPng = false; + else if ((bpp = ExtensionToBpp(inputFileExtension)) != 0 && !strcmp(outputFileExtension, "png")) + toPng = true; + else + FATAL_ERROR("Don't know how to convert \"%s\" to \"%s\".\n", inputPath, outputPath); + + if (bpp == 1 && layout == 2) + FATAL_ERROR("Layout type 2 is not supported with 1 BPP fonts.\n"); + + struct Image image; + + if (toPng) + { + ReadFont(inputPath, &image, numGlyphs, bpp, layout); + WritePng(outputPath, &image); + } + else + { + image.bitDepth = 8; + ReadPng(inputPath, &image); + WriteFont(outputPath, &image, numGlyphs, bpp, layout); + } +} diff --git a/tools/rsfont/util.c b/tools/rsfont/util.c new file mode 100644 index 000000000..87abeb31c --- /dev/null +++ b/tools/rsfont/util.c @@ -0,0 +1,124 @@ +// Copyright (c) 2015 YamaArashi + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> +#include <errno.h> +#include <limits.h> +#include "global.h" +#include "util.h" + +bool ParseNumber(char *s, char **end, int radix, int *intValue) +{ + char *localEnd; + + if (end == NULL) + end = &localEnd; + + errno = 0; + + const long longValue = strtol(s, end, radix); + + if (*end == s) + return false; // not a number + + if ((longValue == LONG_MIN || longValue == LONG_MAX) && errno == ERANGE) + return false; + + if (longValue > INT_MAX) + return false; + + if (longValue < INT_MIN) + return false; + + *intValue = (int)longValue; + + return true; +} + +char *GetFileExtension(char *path) +{ + char *extension = path; + + while (*extension != 0) + extension++; + + while (extension > path && *extension != '.') + extension--; + + if (extension == path) + return NULL; + + extension++; + + if (*extension == 0) + return NULL; + + return extension; +} + +unsigned char *ReadWholeFile(char *path, int *size) +{ + FILE *fp = fopen(path, "rb"); + + if (fp == NULL) + FATAL_ERROR("Failed to open \"%s\" for reading.\n", path); + + fseek(fp, 0, SEEK_END); + + *size = ftell(fp); + + unsigned char *buffer = malloc(*size); + + if (buffer == NULL) + FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path); + + rewind(fp); + + if (fread(buffer, *size, 1, fp) != 1) + FATAL_ERROR("Failed to read \"%s\".\n", path); + + fclose(fp); + + return buffer; +} + +unsigned char *ReadWholeFileZeroPadded(char *path, int *size, int padAmount) +{ + FILE *fp = fopen(path, "rb"); + + if (fp == NULL) + FATAL_ERROR("Failed to open \"%s\" for reading.\n", path); + + fseek(fp, 0, SEEK_END); + + *size = ftell(fp); + + unsigned char *buffer = calloc(*size + padAmount, 1); + + if (buffer == NULL) + FATAL_ERROR("Failed to allocate memory for reading \"%s\".\n", path); + + rewind(fp); + + if (fread(buffer, *size, 1, fp) != 1) + FATAL_ERROR("Failed to read \"%s\".\n", path); + + fclose(fp); + + return buffer; +} + +void WriteWholeFile(char *path, void *buffer, int bufferSize) +{ + FILE *fp = fopen(path, "wb"); + + if (fp == NULL) + FATAL_ERROR("Failed to open \"%s\" for writing.\n", path); + + if (fwrite(buffer, bufferSize, 1, fp) != 1) + FATAL_ERROR("Failed to write to \"%s\".\n", path); + + fclose(fp); +} diff --git a/tools/rsfont/util.h b/tools/rsfont/util.h new file mode 100644 index 000000000..6d7a9c21e --- /dev/null +++ b/tools/rsfont/util.h @@ -0,0 +1,14 @@ +// Copyright (c) 2015 YamaArashi + +#ifndef UTIL_H +#define UTIL_H + +#include <stdbool.h> + +bool ParseNumber(char *s, char **end, int radix, int *intValue); +char *GetFileExtension(char *path); +unsigned char *ReadWholeFile(char *path, int *size); +unsigned char *ReadWholeFileZeroPadded(char *path, int *size, int padAmount); +void WriteWholeFile(char *path, void *buffer, int bufferSize); + +#endif // UTIL_H |