aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbluebear94 <[email protected]>2016-07-22 04:55:55 -0400
committerbluebear94 <[email protected]>2016-07-22 04:55:55 -0400
commit8a24caa4056ac18d8fbae84728a5ea4ee4604d32 (patch)
tree15eff6928985402e09a736706a37314d82fa3864
downloadsanstop-8a24caa4056ac18d8fbae84728a5ea4ee4604d32.tar.xz
sanstop-8a24caa4056ac18d8fbae84728a5ea4ee4604d32.zip
Initial commit (first release plus some changes)
-rw-r--r--.gitignore4
-rwxr-xr-xLICENSE20
-rwxr-xr-xMakefile16
-rwxr-xr-xREADME.md18
-rwxr-xr-xdds.h45
-rwxr-xr-xsanstop.c377
-rwxr-xr-xsanstop.h72
-rwxr-xr-xutf8.c30
-rwxr-xr-xutf8.h44
9 files changed, 626 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..45f3f5e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+sanstop
+*.o
+*.exe
+test/
diff --git a/LICENSE b/LICENSE
new file mode 100755
index 0000000..8928e2b
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+Copyright (c) 2016 kyare
+
+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/Makefile b/Makefile
new file mode 100755
index 0000000..f06606d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,16 @@
+CC=gcc
+CFLAGS=-std=c11 -Wall -Werror -O3 -I/usr/include/freetype2
+
+release: sanstop
+
+sanstop: sanstop.o utf8.o
+ $(CC) $(CFLAGS) sanstop.o utf8.o -o sanstop -static -lfreetype -lpng -lz -lbz2
+
+sanstop.o: sanstop.c sanstop.h
+ $(CC) $(CFLAGS) -c sanstop.c
+
+utf8.o: utf8.c utf8.h
+ $(CC) $(CFLAGS) -c utf8.c
+
+yukkuri:
+ @echo "ゆっくりしていってね!"
diff --git a/README.md b/README.md
new file mode 100755
index 0000000..8f25889
--- /dev/null
+++ b/README.md
@@ -0,0 +1,18 @@
+## sanstop
+
+replace Comic Sans with an even worse font
+
+more seriously, generates .dds and .xml files from a font for use in Wizard101
+
+### Dependencies
+
+[FreeType](https://www.freetype.org/), LibPNG, and ZLib.
+
+### Usage:
+
+ # to compile it:
+ make
+ # to magically convert a font:
+ ./sanstop SomeFont.ttf WizardRegular targets.txt 48
+ # targets.txt is a file with every character you want in your font
+ # in this case this uses 48 as the height
diff --git a/dds.h b/dds.h
new file mode 100755
index 0000000..b7e66d1
--- /dev/null
+++ b/dds.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#define FOUR_ZEROES 0, 0, 0, 0
+static const char DDS_HEADER[DDS_HEADER_SIZE] = {
+
+ 'D', 'D', 'S', ' ', // magic number
+ 124, 0, 0, 0, // header proper size
+ 7, 16, 0, 0, // flags
+ 0, 2, 0, 0,
+
+ 0, 2, 0, 0, // dimensions
+ FOUR_ZEROES, // pitch
+ FOUR_ZEROES, // depth
+ FOUR_ZEROES, // mipmap count
+
+ FOUR_ZEROES, // 44 unused bytes
+ FOUR_ZEROES,
+ FOUR_ZEROES,
+ FOUR_ZEROES,
+
+ FOUR_ZEROES,
+ FOUR_ZEROES,
+ FOUR_ZEROES,
+ FOUR_ZEROES,
+
+ FOUR_ZEROES,
+ FOUR_ZEROES,
+ FOUR_ZEROES,
+ 32, 0, 0, 0, // pixel format - size
+
+ 2, 0, 0, 0, // flags - DDPF_ALPHA, apparently the original file uses this
+ FOUR_ZEROES, // FourCC
+ 8, 0, 0, 0, // 8 bits per channel
+ FOUR_ZEROES, // red maxk
+
+ FOUR_ZEROES, // green mask
+ FOUR_ZEROES, // blue mask
+ 255, 0, 0, 0, // alpha mask - end of pixel format
+ 2, 16, 0, 0, // something weird; 0x1000 is required but I'm not sure about 0x2
+
+ FOUR_ZEROES, // this is a simple texture so we don't need to do anything fancy here
+ FOUR_ZEROES,
+ FOUR_ZEROES,
+ FOUR_ZEROES, // unused fields
+};
diff --git a/sanstop.c b/sanstop.c
new file mode 100755
index 0000000..922bf72
--- /dev/null
+++ b/sanstop.c
@@ -0,0 +1,377 @@
+#include "sanstop.h"
+#include "utf8.h"
+
+int main(int argc, char** argv) {
+ Config c = (Config) {NULL, NULL, NULL, -1};
+ Glyph* glyphs = NULL;
+ FT_Library ft = NULL;
+ FT_Face face = NULL;
+ Buffer b = (Buffer) {NULL, -1, 0, 512};
+ int stat = readConfig(&c, argc, argv);
+ if (stat == 0) {
+ size_t glyphCap = 256;
+ size_t glyphCount = 0;
+ glyphs = malloc(glyphCap * sizeof(Glyph));
+ if (initializeBuffer(&b) != 0) {
+ fprintf(stderr, "Could not allocate memory for buffer.\n");
+ fprintf(stderr, "Maybe you don't have enough?\n");
+ stat = -2;
+ goto end;
+ }
+ if (FT_Init_FreeType(&ft) != 0) {
+ fprintf(stderr, "Could not init FreeType library\n");
+ stat = -2;
+ goto end;
+ }
+ if (FT_New_Face(ft, c.fontFileName, 0, &face) != 0) {
+ fprintf(stderr, "Could not load font face %s\n", c.fontFileName);
+ stat = -1;
+ goto end;
+ }
+ FT_Set_Pixel_Sizes(face, 0, c.size);
+ char* here = c.targets;
+ int codepoint;
+ // Load and blit glyphs
+ while (*here != '\0') {
+ int ustat = utf8Next(&here, &codepoint);
+ //printf("[0x%x]\n", codepoint);
+ if (ustat != 0) {
+ fprintf(stderr, "Malformatted UTF-8.\n");
+ int byte = ustat & 0xFF;
+ switch (utf8DecodeErrorClass(ustat)) {
+ case ERRC_UNEXPECTED_CONTINUATION:
+ fprintf(stderr, "Unexpected continuation byte 0x%x.\n", byte);
+ break;
+ case ERRC_CONTINUATION_EXPECTED:
+ fprintf(stderr, "Expected continuation byte, but got 0x%x instead.\n", byte);
+ break;
+ case ERRC_INVALID_UTF8_BYTE:
+ fprintf(stderr, "Invalid byte 0x%x.\n", byte);
+ }
+ stat = -1;
+ goto end;
+ }
+ if (codepoint == 10 || codepoint == 13) continue;
+ if (FT_Load_Char(face, codepoint, FT_LOAD_RENDER) != 0) {
+ fprintf(stderr, "Warn: glyph %lc [0x%x] does not exist in this font.\n", codepoint, codepoint);
+ } else {
+ glyphs[glyphCount].id = codepoint;
+ stat = blitAndAdvance(&b, face, glyphs + glyphCount, &c);
+ if (stat != 0) goto end;
+ ++glyphCount;
+ if (glyphCount >= glyphCap) {
+ glyphCap <<= 1;
+ glyphs = realloc(glyphs, glyphCap * sizeof(Glyph));
+ }
+ }
+ }
+ // Write what's Left
+ writePage(&b, &c);
+ // Write XML file
+ int len = strlen(c.fontPrefix) + 5;
+ char* fname = malloc(sizeof(char) * len);
+ snprintf(fname, len, "%s.xml", c.fontPrefix);
+ FILE* f = fopen(fname, "w");
+ if (f == NULL) {
+ fprintf(stderr, "Cannot write to file %s!", fname);
+ free(fname);
+ stat = -1;
+ goto end;
+ }
+ writeXMLHeader(f, face, &c);
+ writeXMLPageData(f, &c, &b);
+ writeXMLGlyphData(f, glyphCount, glyphs);
+ writeXMLFooter(f);
+ fclose(f);
+ free(fname);
+ }
+ end:
+ cleanConfig(&c);
+ if (glyphs != NULL) free(glyphs);
+ if (face != NULL) FT_Done_Face(face);
+ if (ft != NULL) FT_Done_FreeType(ft);
+ if (b.data != NULL) free(b.data);
+ return stat;
+}
+
+int initializeBuffer(Buffer* b) {
+ b->page = 0;
+ uint8_t* data = malloc(IMAGE_PIXELS * sizeof(uint8_t));
+ if (data == NULL) return -1;
+ b->data = data;
+ return 0;
+}
+
+void flipPage(Buffer* b) {
+ b->page++;
+ memset(b->data, 0, IMAGE_PIXELS * sizeof(uint8_t));
+ b->x = 0;
+ b->y = IMAGE_DIM;
+}
+
+int shouldGoUp(Buffer* b, int width) {
+ return IMAGE_DIM - b->x < width;
+}
+
+int shouldFlip(Buffer* b, int height) {
+ return b->y < height;
+}
+
+void goUp(Buffer* b, int height) {
+ b->y -= height;
+ b->x = 0;
+}
+
+void advance(Buffer* b, int width) {
+ b->x += width;
+}
+
+#include "dds.h"
+
+int writePage(Buffer* b, Config* c) {
+ size_t len = snprintf(NULL, 0, "%s_%d.dds", c->fontPrefix, b->page) + 1;
+ char* fname = malloc(sizeof(char) * len);
+ snprintf(fname, len, "%s_%d.dds", c->fontPrefix, b->page);
+ FILE* f = fopen(fname, "wb");
+ if (f == NULL) {
+ fprintf(stderr, "File %s is unwritable\n", fname);
+ free(fname);
+ return -1;
+ }
+ fwrite(DDS_HEADER, DDS_HEADER_SIZE, 1, f);
+ fwrite(b->data, IMAGE_PIXELS * sizeof(uint8_t), 1, f);
+ free(fname);
+ fclose(f);
+ return 0;
+}
+
+void blit(Buffer* b, FT_Face face, Glyph* gp, int tw, int th, int size) {
+ Glyph g = *gp;
+ g.page = b->page;
+ g.left = b->x;
+ g.bottom = b->y;
+ g.right = b->x + tw;
+ g.top = b->y - th;
+ int sx = g.left + face->glyph->bitmap_left;
+ int base = g.bottom + size * face->descender / face->units_per_EM;
+ int sy = base - face->glyph->bitmap_top;
+ int w = face->glyph->bitmap.width;
+ int h = face->glyph->bitmap.rows;
+ uint8_t* iwl = b->data + sx + sy * IMAGE_DIM;
+ uint8_t* grl = face->glyph->bitmap.buffer;
+ // This is hardly the fastest way, but let's focus on being correct first.
+ for (int i = 0; i < h; ++i) {
+ memcpy(iwl, grl, w);
+ iwl += IMAGE_DIM;
+ grl += w;
+ }
+ *gp = g;
+}
+
+int blitAndAdvance(Buffer* b, FT_Face face, Glyph* gp, Config* c) {
+ int tw = face->glyph->advance.x >> 6;
+ int th = c->size * (face->ascender - face->descender) / face->units_per_EM;
+ if (shouldGoUp(b, tw))
+ goUp(b, th);
+ if (shouldFlip(b, th)) {
+ int stat = writePage(b, c);
+ if (stat != 0) return stat;
+ flipPage(b);
+ }
+ blit(b, face, gp, tw, th, c->size);
+ advance(b, tw);
+ return 0;
+}
+
+const char* xmlFontPropertyFormatter =
+ "<GlyphisFont Version=\"1\" "
+ "FaceName=\"%s\" "
+ "PtSize=\"%d\" "
+ "Weight=\"%d\" "
+ "Bold=\"%d\" "
+ "Italic=\"%d\" "
+ "Outline=\"0\" "
+ "OutputFormat=\"DDS_A8\" "
+ "InvalidGlyph=\"127\">"
+ ;
+void writeXMLHeader(FILE* f, FT_Face face, Config* c) {
+ fprintf(f, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
+ fprintf(f, xmlFontPropertyFormatter,
+ face->family_name,
+ c->size * 5,
+ 700,
+ face->style_flags & FT_STYLE_FLAG_BOLD,
+ face->style_flags & FT_STYLE_FLAG_ITALIC
+ );
+}
+
+void writeXMLPageData(FILE* f, Config* c, Buffer* b) {
+ int totalPages = b->page + 1;
+ fprintf(f, "<Pages Count=\"%d\">\n", totalPages);
+ for (int i = 0; i < totalPages; ++i) {
+ fprintf(f, "<Page FileName=\"%s_%d.dds\" Width=\"%d\" Height=\"%d\" />",
+ c->fontPrefix, i, IMAGE_DIM, IMAGE_DIM);
+ }
+ fprintf(f, "</Pages>");
+}
+
+const char* xmlGlyphEntryFormatter =
+ "<Glyph ID=\"%d\" "
+ "A=\"%d\" "
+ "B=\"%d\" "
+ "C=\"%d\" "
+ "Page=\"%d\" "
+ "Top=\"%d\" "
+ "Bottom=\"%d\" "
+ "Left=\"%d\" "
+ "Right=\"%d\" />"
+ ;
+void writeXMLGlyphData(FILE* f, int glyphCount, Glyph* glyphs) {
+ fprintf(f, "<Glyphs Count=\"%d\">", glyphCount);
+ for (int i = 0; i < glyphCount; ++i) {
+ Glyph g = glyphs[i];
+ fprintf(f, xmlGlyphEntryFormatter,
+ g.id,
+ 0,
+ g.right - g.left - 2,
+ 0,
+ g.page,
+ g.top,
+ g.bottom,
+ g.left,
+ g.right
+ );
+ }
+ fprintf(f, "</Glyphs>");
+}
+
+void writeXMLFooter(FILE* f) {
+ fprintf(f, "</GlyphisFont>\n");
+}
+
+int readConfig(Config* c, int argc, char** argv) {
+ int pos = 0;
+ int ignoreOpts = 0;
+ int status = 0;
+ for (int i = 1; i < argc; ++i) {
+ if (argv[i][0] == '-' && !ignoreOpts) {
+ if (argv[i][1] == '-') {
+ if (argv[i][2] == '\0') ignoreOpts = 1;
+ else if (!strcmp(argv[i] + 2, "help")) {
+ printHelp();
+ status = 1;
+ break;
+ }
+ else {
+ fprintf(stderr, "Invalid option %s.\n", argv[i]);
+ status = -1;
+ break;
+ }
+ } else {
+ for (char* f = argv[i] + 1; *f != '\0'; ++f) {
+ switch (*f) {
+ case 'h':
+ printHelp();
+ status = 1;
+ goto end;
+ default:
+ fprintf(stderr, "Invalid option -%c.\n", *f);
+ status = -1;
+ goto end;
+ }
+ }
+ }
+ }
+ else {
+ switch (pos) {
+ case 0:
+ c->fontFileName = argv[i];
+ break;
+ case 1:
+ c->fontPrefix = argv[i];
+ break;
+ case 2: {
+ FILE* f = fopen(argv[i], "r");
+ if (f == NULL) {
+ fprintf(stderr, "File %s is missing or unreadable\n", argv[i]);
+ status = -1;
+ goto end;
+ }
+ // Slurp the source
+ // Thanks http://stackoverflow.com/questions/14002954/c-programming-how-to-read-the-whole-file-contents-into-a-buffer
+ fseek(f, 0, SEEK_END);
+ long fsize = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ char* string = (char*) malloc(fsize + 1);
+ size_t br = fread(string, fsize, 1, f);
+ if (br < 1) {
+ status = -2;
+ goto end;
+ }
+ fclose(f);
+ c->targets = string;
+ break;
+ }
+ case 3:
+ c->size = atoi(argv[i]);
+ if (c->size <= 0) {
+ fprintf(stderr, "Size too small!\n");
+ status = -1;
+ goto end;
+ }
+ if (c->size > 144) {
+ fprintf(stderr, "Size too big (max is 144)!\n");
+ status = -1;
+ goto end;
+ }
+ break;
+ default:
+ status = -1;
+ goto end;
+ }
+ ++pos;
+ }
+ }
+ end:
+ if (status == 0 && (c->fontFileName == NULL || c->fontPrefix == NULL ||
+ c->targets == NULL || c->size <= 0))
+ status = -1;
+ if (status == -1) printUsage();
+ if (status == -2) fprintf(stderr, "Unexpected error; please tell kyarei.\n");
+ return status;
+}
+
+void cleanConfig(Config* c) {
+ free(c->targets);
+}
+
+static const char* DESCRIPTION =
+ "sanstop - generates xml and dds files from font files for Wizard101.\n"
+ "Version 0.1.\n"
+ ;
+
+static const char* USAGE =
+ "Usage:\n"
+ " sanstop [OPTIONS] <font-file> <font-prefix> <targets> <size>\n"
+ " font-file: the source font file (ttf / otf)\n"
+ " font-prefix: the prefix used for output files\n"
+ " targets: file containing list of characters to be used\n"
+ " size: the height to use for glyphs\n"
+ " OPTIONS -\n"
+ " -h --help display this help message\n"
+ " -- don't treat future arguments starting with - as flags\n"
+ "Return codes:\n"
+ " 0: everything is fine\n"
+ " 1: help was displayed\n"
+ " -1: usage error\n"
+ " -2: please tell kyarei!\n"
+ ;
+
+void printUsage() {
+ fprintf(stderr, USAGE);
+}
+
+void printHelp() {
+ fprintf(stderr, DESCRIPTION);
+ printUsage();
+}
diff --git a/sanstop.h b/sanstop.h
new file mode 100755
index 0000000..5132662
--- /dev/null
+++ b/sanstop.h
@@ -0,0 +1,72 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#define IMAGE_DIM 512
+#define IMAGE_PIXELS (IMAGE_DIM * IMAGE_DIM)
+#define DDS_HEADER_SIZE 128
+
+typedef struct {
+ uint8_t* data;
+ int page;
+ int x, y; // x = left edge; y = bottom edge
+} Buffer;
+
+typedef struct {
+ char* fontFileName;
+ char* fontPrefix;
+ char* targets;
+ int size;
+} Config;
+
+typedef struct {
+ int id;
+ int page;
+ int left, right, top, bottom;
+ // For future reference:
+ // B = right - left - 2
+ // A and C are unknown
+} Glyph;
+
+// Initializes a Buffer struct.
+int initializeBuffer(Buffer* b);
+// Increments the page counter, clears the data buffer, and resets coordinates.
+void flipPage(Buffer* b);
+// Returns 1 if there is not enough room in the current row to fit another
+// glyph of a given width, and thus the program should advance to the space
+// above.
+int shouldGoUp(Buffer* b, int width);
+// Returns 1 if there is not enough room in the current image to fit another
+// glyph of a given height, and thus the program should flip to a new page.
+int shouldFlip(Buffer* b, int height);
+void goUp(Buffer* b, int height);
+void advance(Buffer* b, int width);
+
+// Writes the current page to a file.
+int writePage(Buffer* b, Config* c);
+
+// Blits the character stored in face->glyph to the buffer.
+// Assumes that there is enough room.
+// Does not advance automatically.
+// Stores glyph definition in g.
+// g->id must be set.
+void blit(Buffer* b, FT_Face face, Glyph* gp, int tw, int th, int size);
+// Same as blit, but checks if there is enough room and advances
+// automatically afterwards.
+int blitAndAdvance(Buffer* b, FT_Face face, Glyph* gp, Config* c);
+
+void writeXMLHeader(FILE* f, FT_Face face, Config* c);
+void writeXMLPageData(FILE* f, Config* c, Buffer* b);
+void writeXMLGlyphData(FILE* f, int glyphCount, Glyph* glyphs);
+void writeXMLFooter(FILE* f);
+
+int readConfig(Config* c, int argc, char** argv);
+void cleanConfig(Config* c);
+void printUsage();
+void printHelp();
diff --git a/utf8.c b/utf8.c
new file mode 100755
index 0000000..20bba73
--- /dev/null
+++ b/utf8.c
@@ -0,0 +1,30 @@
+#include "utf8.h"
+#include <stdio.h>
+int utf8Next(char** strRef, int* codeRef) {
+ unsigned char* str = (unsigned char*) *strRef;
+ unsigned char first = *str;
+ if (isASCII(first)) {
+ *codeRef = *str;
+ ++*strRef;
+ return 0;
+ }
+ if (isContinuation(first))
+ return ERR_UNEXPECTED_CONTINUATION + first;
+ if (first >= 248)
+ return ERR_INVALID_UTF8_BYTE + first;
+ int expectedContinuations =
+ is2ByteStarter(first) ? 1 :
+ is3ByteStarter(first) ? 2 : 3;
+ int code = first - (
+ is2ByteStarter(first) ? 192 :
+ is3ByteStarter(first) ? 224 : 240);
+ for (int i = 1; i <= expectedContinuations; ++i) {
+ unsigned char b = str[i];
+ if (b > 248) return ERR_INVALID_UTF8_BYTE | (i << 8) | b;
+ if (!isContinuation(b)) return ERR_CONTINUATION_EXPECTED | (i << 8) | b;
+ code = (code << 6) | (b & 0x7f);
+ }
+ *codeRef = code;
+ *strRef += 1 + expectedContinuations;
+ return 0;
+}
diff --git a/utf8.h b/utf8.h
new file mode 100755
index 0000000..ec5dc71
--- /dev/null
+++ b/utf8.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <stdlib.h>
+
+inline int isASCII(unsigned char c) {
+ return c < 128;
+}
+inline int isContinuation(unsigned char c) {
+ return c >= 128 && c < 192;
+}
+inline int is2ByteStarter(unsigned char c) {
+ return c >= 192 && c < 224;
+}
+inline int is3ByteStarter(unsigned char c) {
+ return c >= 224 && c < 240;
+}
+inline int is4ByteStarter(unsigned char c) {
+ return c >= 240 && c < 248;
+}
+inline int utf8DecodeErrorClass(int err) {
+ return err >> 12;
+}
+
+#define ERR_UNEXPECTED_CONTINUATION 0x1000
+#define ERR_INVALID_UTF8_BYTE 0x2000
+#define ERR_CONTINUATION_EXPECTED 0x3000
+#define ERRC_UNEXPECTED_CONTINUATION 0x1
+#define ERRC_INVALID_UTF8_BYTE 0x2
+#define ERRC_CONTINUATION_EXPECTED 0x3
+
+/*
+ Returns the next UTF-8 character and advances the string pointer.
+ This function does not consider the case when the current byte
+ does not align with any codepoint.
+ @param strRef a pointer to a pointer to the current character
+ @param codeRef where you want the resulting codepoint to be stored
+ @return
+ 0 if everything went well
+ ERR_UNEXPECTED_CONTINUATION + 0x100 * bytes advanced + byte
+ if an unexpected continuation byte was found
+ ERR_INVALID_UTF8_BYTE + 0x100 * bytes advanced + byte
+ if an unexpected continuation byte was found
+*/
+int utf8Next(char** strRef, int* codeRef);