Mercurial > hg > sidinfo
diff sidutil.c @ 313:b3d46806787d
Move a number of more or less generic helper functions into a separate sidutil.[ch] module.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sat, 11 Jan 2020 18:30:10 +0200 |
parents | |
children | b0c844b39516 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sidutil.c Sat Jan 11 18:30:10 2020 +0200 @@ -0,0 +1,280 @@ +/* + * SIDLib common utility functions + * Programmed and designed by Matti 'ccr' Hämäläinen <ccr@tnsp.org> + * (C) Copyright 2014-2020 Tecnic Software productions (TNSP) + */ +#include "sidutil.h" +#include "th_file.h" +#include "th_string.h" +#include "th_datastruct.h" + + +void sidutil_print_license(void) +{ + printf("%s - %s\n%s\n", th_prog_name, th_prog_desc, th_prog_author); + printf( + "\n" + "Redistribution and use in source and binary forms, with or without\n" + "modification, are permitted provided that the following conditions\n" + "are met:\n" + "\n" + " 1. Redistributions of source code must retain the above copyright\n" + " notice, this list of conditions and the following disclaimer.\n" + "\n" + " 2. Redistributions in binary form must reproduce the above copyright\n" + " notice, this list of conditions and the following disclaimer in\n" + " the documentation and/or other materials provided with the\n" + " distribution.\n" + "\n" + " 3. The name of the author may not be used to endorse or promote\n" + " products derived from this software without specific prior written\n" + " permission.\n" + "\n" + "THIS SOFTWARE IS PROVIDED BY THE AUTHOR \"AS IS\" AND ANY EXPRESS OR\n" + "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n" + "WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n" + "ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,\n" + "INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n" + "(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n" + "SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n" + "HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n" + "STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\n" + "IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n" + "POSSIBILITY OF SUCH DAMAGE.\n" + ); +} + + +const char *sidutil_strip_hvsc_path(const char *hvscPath, const char *filename) +{ + if (hvscPath != NULL) + { + const char *hvsc = hvscPath, *fptr = filename; + + // Compare until end of string(s) + for (; *hvsc != 0 && *fptr != 0 && *hvsc == *fptr; hvsc++, fptr++); + + // Full match? + if (*hvsc == 0) + return fptr - 1; + } + + return filename; +} + + +char *sidutil_check_hvsc_file(const char *hvscPath, const char *filebase, const char *fext) +{ + th_stat_data sdata; + char *npath = th_strdup_printf("%s%c%s%c%s%s", + hvscPath, TH_DIR_SEPARATOR_CHR, + SET_HVSC_DOCUMENTS, TH_DIR_SEPARATOR_CHR, + filebase, fext != NULL ? fext : ""); + + if (npath != NULL && + th_stat_path(npath, &sdata) && + (sdata.flags & TH_IS_READABLE) && + (sdata.flags & TH_IS_DIR) == 0) + return npath; + + th_free(npath); + return NULL; +} + + +#ifndef HAVE_ICONV + +static const uint8_t sidutil_lang_iso88591_to_cp850[16*6] = { +0xff, 0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5, 0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee, +0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa, 0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8, +0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, +0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e, 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1, +0x85, 0xa0, 0x83, 0xc6, 0x84, 0x86, 0x91, 0x87, 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b, +0xd0, 0xa4, 0x95, 0xa2, 0x93, 0xe4, 0x94, 0xf6, 0x9b, 0x97, 0xa3, 0x96, 0x81, 0xec, 0xe7, 0x98, +}; + +static const uint8_t sidutil_lang_iso88591_to_cp437[16*6] = { +0xff, 0xad, 0x9b, 0x9c, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0xa6, 0xae, 0xaa, 0x00, 0x00, 0x00, +0xf8, 0xf1, 0xfd, 0x00, 0x00, 0xe6, 0x00, 0xfa, 0x00, 0x00, 0xa7, 0xaf, 0xac, 0xab, 0x00, 0xa8, +0x00, 0x00, 0x00, 0x00, 0x8e, 0x8f, 0x92, 0x80, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0xe1, +0x85, 0xa0, 0x83, 0x00, 0x84, 0x86, 0x91, 0x87, 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b, +0x00, 0xa4, 0x95, 0xa2, 0x93, 0x00, 0x94, 0xf6, 0x00, 0x97, 0xa3, 0x96, 0x81, 0x00, 0x00, 0x98, +}; + + +static char *sidutil_chconv_internal(SIDUtilChConvCtx *ctx, const char *src) +{ + // Fallback conversion of ISO-8859-1 to X + const uint8_t *srcPtr = (const uint8_t *) src; + const uint8_t *convTable; + size_t outSize, outLen; + char *outBuf, outByte; + + if (src == NULL) + return NULL; + + outSize = strlen(src) + 1; + if ((outBuf = th_malloc(outSize)) == NULL) + return NULL; + + for (outLen = 0; *srcPtr; srcPtr++) + { + switch (ctx->outLangID) + { + case TH_LANG_UTF8: + // Not 100% correct really, but close enough + if (*srcPtr < 0x80) + { + if (!th_strbuf_putch(&outBuf, &outSize, &outLen, *srcPtr)) + goto err; + } + else + if (*srcPtr < 0xBF) + { + if (!th_strbuf_putch(&outBuf, &outSize, &outLen, 0xC2) || + !th_strbuf_putch(&outBuf, &outSize, &outLen, *srcPtr)) + goto err; + } + else + { + if (!th_strbuf_putch(&outBuf, &outSize, &outLen, 0xC3) || + !th_strbuf_putch(&outBuf, &outSize, &outLen, *srcPtr - 0x40)) + goto err; + } + break; + + case TH_LANG_ISO88591: + if (!th_strbuf_putch(&outBuf, &outSize, &outLen, *srcPtr)) + goto err; + break; + + case TH_LANG_CP850: + case TH_LANG_CP437: + // Not 100% correct either, but close enough + convTable = (ctx->outLangID == TH_LANG_CP850) ? + sidutil_lang_iso88591_to_cp850 : sidutil_lang_iso88591_to_cp437; + + if (*srcPtr < 0x7f) + outByte = *srcPtr; + else + if (*srcPtr >= 0xA0) + outByte = convTable[*srcPtr - 0xA0]; + else + outByte = '?'; + + if (!th_strbuf_putch(&outBuf, &outSize, &outLen, outByte)) + goto err; + break; + } + } + + if (!th_strbuf_putch(&outBuf, &outSize, &outLen, *srcPtr)) + goto err; + + return outBuf; + +err: + th_free(outBuf); + return NULL; +} + +#endif + + +// NOTICE! Only call this function IF ctx->enabled == TRUE +char * sidutil_chconv_convert(SIDUtilChConvCtx *ctx, const char *src) +{ +#ifdef HAVE_ICONV + size_t srcLeft = strlen(src) + 1; + size_t outLeft = srcLeft * 2; + char *srcPtr = (char *) src; + char *outBuf, *outPtr; + + if ((outBuf = outPtr = th_malloc(outLeft + 1)) == NULL) + return NULL; + + while (srcLeft > 0) + { + size_t ret = iconv(ctx->iconvCtx, &srcPtr, &srcLeft, &outPtr, &outLeft); + if (ret == (size_t) -1) + break; + } + + return (char *) outBuf; +#else + return sidutil_chconv_internal(ctx, src); +#endif +} + + +int sidutil_chconv_init(SIDUtilChConvCtx *ctx, const char *outLang) +{ + if (ctx == NULL) + return THERR_NULLPTR; + + memset(ctx, 0, sizeof(*ctx)); + + if (outLang != NULL) + { + // Get the character encoding part (e.g. "UTF-8" etc.) and + // strip out and lowercase everything (e.g. "utf8") + size_t i; + char *ptr; + + if ((ctx->outLang = th_strdup(outLang)) == NULL) + return THERR_MALLOC; + + if ((ptr = strchr(ctx->outLang, '.')) == NULL) + ptr = ctx->outLang; + else + ptr++; + + for (i = 0; *ptr; ptr++) + { + if (*ptr != '-') + ctx->outLang[i++] = th_tolower(*ptr); + } + ctx->outLang[i] = 0; + +#ifdef HAVE_ICONV + // Initialize iconv, check if we have language/charset + ctx->iconvCtx = iconv_open(ctx->outLang, "iso88591"); + ctx->enabled = (ctx->iconvCtx != (iconv_t) -1); +#else + // Check if we can use our fallback converter + if (strcmp(ctx->outLang, "utf8") == 0) + ctx->outLangID = TH_LANG_UTF8; + else + if (strcmp(ctx->outLang, "iso88591") == 0 || + strcmp(ctx->outLang, "cp819") == 0 || + strcmp(ctx->outLang, "latin1") == 0 || + strcmp(ctx->outLang, "cp28591") == 0) + ctx->outLangID = TH_LANG_ISO88591; + else + if (strcmp(ctx->outLang, "cp850") == 0) + ctx->outLangID = TH_LANG_CP850; + else + if (strcmp(ctx->outLang, "cp437") == 0) + ctx->outLangID = TH_LANG_CP437; + else + ctx->outLangID = TH_LANG_ISO88591; + + ctx->enabled = ctx->outLangID != TH_LANG_ISO88591; +#endif + } + + return THERR_OK; +} + + +void sidutil_chconv_close(SIDUtilChConvCtx *ctx) +{ +#ifdef HAVE_ICONV + if (ctx->iconvCtx != (iconv_t) -1) + iconv_close(ctx->iconvCtx); +#else +#endif + + th_free(ctx->outLang); +}