mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-23 03:22:08 +00:00
253 lines
5.0 KiB
C
253 lines
5.0 KiB
C
/*
|
|
* This file is part of RGBDS.
|
|
*
|
|
* Copyright (c) 2013-2018, stag019 and RGBDS contributors.
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "asm/asm.h"
|
|
#include "asm/charmap.h"
|
|
#include "asm/main.h"
|
|
#include "asm/output.h"
|
|
#include "asm/util.h"
|
|
#include "asm/warning.h"
|
|
|
|
#define CHARMAP_HASH_SIZE (1 << 9)
|
|
|
|
struct CharmapStackEntry {
|
|
struct Charmap *charmap;
|
|
struct CharmapStackEntry *next;
|
|
};
|
|
|
|
static struct Charmap *tHashedCharmaps[CHARMAP_HASH_SIZE];
|
|
|
|
static struct Charmap *mainCharmap;
|
|
static struct Charmap *currentCharmap;
|
|
|
|
struct CharmapStackEntry *charmapStack;
|
|
|
|
static uint32_t charmap_CalcHash(const char *s)
|
|
{
|
|
return calchash(s) % CHARMAP_HASH_SIZE;
|
|
}
|
|
|
|
static struct Charmap **charmap_Get(const char *name)
|
|
{
|
|
struct Charmap **ppCharmap = &tHashedCharmaps[charmap_CalcHash(name)];
|
|
|
|
while (*ppCharmap != NULL && strcmp((*ppCharmap)->name, name))
|
|
ppCharmap = &(*ppCharmap)->next;
|
|
|
|
return ppCharmap;
|
|
}
|
|
|
|
static void CopyNode(struct Charmap *dest,
|
|
const struct Charmap *src,
|
|
int nodeIdx)
|
|
{
|
|
dest->nodes[nodeIdx].code = src->nodes[nodeIdx].code;
|
|
dest->nodes[nodeIdx].isCode = src->nodes[nodeIdx].isCode;
|
|
for (int i = 0; i < 256; i++)
|
|
if (src->nodes[nodeIdx].next[i])
|
|
dest->nodes[nodeIdx].next[i] = dest->nodes +
|
|
(src->nodes[nodeIdx].next[i] - src->nodes);
|
|
}
|
|
|
|
struct Charmap *charmap_New(const char *name, const char *baseName)
|
|
{
|
|
struct Charmap *pBase = NULL;
|
|
|
|
if (baseName != NULL) {
|
|
struct Charmap **ppBase = charmap_Get(baseName);
|
|
|
|
if (*ppBase == NULL) {
|
|
yyerror("Base charmap '%s' doesn't exist", baseName);
|
|
return NULL;
|
|
}
|
|
|
|
pBase = *ppBase;
|
|
}
|
|
|
|
struct Charmap **ppCharmap = charmap_Get(name);
|
|
|
|
if (*ppCharmap != NULL) {
|
|
yyerror("Charmap '%s' already exists", name);
|
|
return NULL;
|
|
}
|
|
|
|
*ppCharmap = calloc(1, sizeof(struct Charmap));
|
|
|
|
if (*ppCharmap == NULL)
|
|
fatalerror("Not enough memory for charmap");
|
|
|
|
struct Charmap *pCharmap = *ppCharmap;
|
|
|
|
snprintf(pCharmap->name, sizeof(pCharmap->name), "%s", name);
|
|
|
|
if (pBase != NULL) {
|
|
pCharmap->charCount = pBase->charCount;
|
|
pCharmap->nodeCount = pBase->nodeCount;
|
|
|
|
for (int i = 0; i < MAXCHARNODES; i++)
|
|
CopyNode(pCharmap, pBase, i);
|
|
}
|
|
|
|
currentCharmap = pCharmap;
|
|
|
|
return pCharmap;
|
|
}
|
|
|
|
void charmap_Set(const char *name)
|
|
{
|
|
struct Charmap **ppCharmap = charmap_Get(name);
|
|
|
|
if (*ppCharmap == NULL) {
|
|
yyerror("Charmap '%s' doesn't exist", name);
|
|
return;
|
|
}
|
|
|
|
currentCharmap = *ppCharmap;
|
|
}
|
|
|
|
void charmap_Push(void)
|
|
{
|
|
struct CharmapStackEntry *stackEntry;
|
|
|
|
stackEntry = malloc(sizeof(struct CharmapStackEntry));
|
|
if (stackEntry == NULL)
|
|
fatalerror("No memory for charmap stack");
|
|
|
|
stackEntry->charmap = currentCharmap;
|
|
stackEntry->next = charmapStack;
|
|
|
|
charmapStack = stackEntry;
|
|
}
|
|
|
|
void charmap_Pop(void)
|
|
{
|
|
if (charmapStack == NULL)
|
|
fatalerror("No entries in the charmap stack");
|
|
|
|
struct CharmapStackEntry *top = charmapStack;
|
|
|
|
currentCharmap = top->charmap;
|
|
charmapStack = top->next;
|
|
free(top);
|
|
}
|
|
|
|
void charmap_InitMain(void)
|
|
{
|
|
mainCharmap = charmap_New("main", NULL);
|
|
}
|
|
|
|
int32_t charmap_Add(char *input, uint8_t output)
|
|
{
|
|
int32_t i;
|
|
uint8_t v;
|
|
|
|
struct Charmap *charmap = currentCharmap;
|
|
struct Charnode *curr_node, *temp_node;
|
|
|
|
if (charmap->charCount >= MAXCHARMAPS || strlen(input) > CHARMAPLENGTH)
|
|
return -1;
|
|
|
|
curr_node = &charmap->nodes[0];
|
|
|
|
for (i = 0; (v = (uint8_t)input[i]); i++) {
|
|
if (curr_node->next[v]) {
|
|
curr_node = curr_node->next[v];
|
|
} else {
|
|
temp_node = &charmap->nodes[charmap->nodeCount + 1];
|
|
|
|
curr_node->next[v] = temp_node;
|
|
curr_node = temp_node;
|
|
|
|
++charmap->nodeCount;
|
|
}
|
|
}
|
|
|
|
/* prevent duplicated keys by accepting only first key-value pair. */
|
|
if (curr_node->isCode)
|
|
return charmap->charCount;
|
|
|
|
curr_node->code = output;
|
|
curr_node->isCode = 1;
|
|
|
|
return ++charmap->charCount;
|
|
}
|
|
|
|
int32_t charmap_Convert(char **input)
|
|
{
|
|
struct Charmap *charmap = currentCharmap;
|
|
struct Charnode *charnode;
|
|
|
|
char *output;
|
|
char outchar[8];
|
|
|
|
int32_t i, match, length;
|
|
uint8_t v, foundCode;
|
|
|
|
output = malloc(strlen(*input));
|
|
if (output == NULL)
|
|
fatalerror("Not enough memory for buffer");
|
|
|
|
length = 0;
|
|
|
|
while (**input) {
|
|
charnode = &charmap->nodes[0];
|
|
|
|
/*
|
|
* Find the longest valid match which has been registered in
|
|
* charmap, possibly yielding multiple or no matches.
|
|
* The longest match is taken, meaning partial matches shorter
|
|
* than the longest one are ignored.
|
|
*/
|
|
for (i = match = 0; (v = (*input)[i]);) {
|
|
if (!charnode->next[v])
|
|
break;
|
|
|
|
charnode = charnode->next[v];
|
|
i++;
|
|
|
|
if (charnode->isCode) {
|
|
match = i;
|
|
foundCode = charnode->code;
|
|
}
|
|
}
|
|
|
|
if (match) {
|
|
output[length] = foundCode;
|
|
|
|
length += 1;
|
|
} else {
|
|
/*
|
|
* put a utf-8 character
|
|
* if failed to find a match.
|
|
*/
|
|
match = readUTF8Char(outchar, *input);
|
|
|
|
if (match) {
|
|
memcpy(output + length, *input, match);
|
|
} else {
|
|
output[length] = 0;
|
|
match = 1;
|
|
}
|
|
|
|
length += match;
|
|
}
|
|
|
|
*input += match;
|
|
}
|
|
|
|
*input = output;
|
|
|
|
return length;
|
|
}
|