diff --git a/PSID/config.h b/PSID/config.h new file mode 100644 index 00000000..5233bc7d --- /dev/null +++ b/PSID/config.h @@ -0,0 +1,192 @@ +/* src/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +#undef AC_APPLE_UNIVERSAL_BUILD + +/* Define if the C++ compiler supports BOOL */ +#undef HAVE_BOOL + +/* Define if your C++ compiler implements exception-handling. */ +#undef HAVE_EXCEPTIONS + +/* Define to 1 if you have the header file. */ +#undef HAVE_GETOPT_H + +/* Define to 1 if you have the `getopt_long' function. */ +#undef HAVE_GETOPT_LONG + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define if standard member ``ios::binary'' is called ``ios::bin''. */ +#undef HAVE_IOS_BIN + +/* Define if ``ios::openmode'' is supported. */ +#undef HAVE_IOS_OPENMODE + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIMITS_H + +/* Define to 1 if you have the `lstat' function. */ +#undef HAVE_LSTAT + +/* Define to 1 if you have the `memmove' function. */ +#undef HAVE_MEMMOVE + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `memset' function. */ +#undef HAVE_MEMSET + +/* Define to 1 if you have the `mkdir' function. */ +#undef HAVE_MKDIR + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + +/* Define if you have the header file. */ +#undef HAVE_SSTREAM + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDBOOL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strcasecmp' function. */ +//#undef HAVE_STRCASECMP + +/* Define to 1 if you have the `strchr' function. */ +#undef HAVE_STRCHR + +/* Define to 1 if you have the `strdup' function. */ +#undef HAVE_STRDUP + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strncasecmp' function. */ +//#undef HAVE_STRNCASECMP + +/* Define to 1 if you have the `strrchr' function. */ +#undef HAVE_STRRCHR + +/* Define to 1 if you have the `strstr' function. */ +#undef HAVE_STRSTR + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if the system has the type `_Bool'. */ +#undef HAVE__BOOL + +/* Define to 1 if you have the `_mkdir' function. */ +#undef HAVE__MKDIR + +/* Define to 1 if `lstat' dereferences a symlink specified with a trailing + slash. */ +#undef LSTAT_FOLLOWS_SLASHED_SYMLINK + +/* Define if mkdir takes only one argument. */ +#undef MKDIR_TAKES_ONE_ARG + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* The size of `char', as computed by sizeof. */ +#undef SIZEOF_CHAR + +/* The size of `int', as computed by sizeof. */ +#undef SIZEOF_INT + +/* The number of bytes in type long */ +#undef SIZEOF_LONG + +/* The size of `long int', as computed by sizeof. */ +#undef SIZEOF_LONG_INT + +/* The number of bytes in type short */ +#undef SIZEOF_SHORT + +/* The size of `short int', as computed by sizeof. */ +#undef SIZEOF_SHORT_INT + +/* The number of bytes in type void* */ +#undef SIZEOF_VOIDP + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +# undef WORDS_BIGENDIAN +# endif +#endif + +/* Define for Solaris 2.5.1 so the uint8_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT8_T + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif + +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* Define to the type of an unsigned integer type of width exactly 16 bits if + such a type exists and the standard includes do not define it. */ +#undef uint16_t + +/* Define to the type of an unsigned integer type of width exactly 8 bits if + such a type exists and the standard includes do not define it. */ +#undef uint8_t diff --git a/PSID/libpsid64/exomizer/chunkpool.c b/PSID/libpsid64/exomizer/chunkpool.c new file mode 100644 index 00000000..1a8c6b01 --- /dev/null +++ b/PSID/libpsid64/exomizer/chunkpool.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include +#include "chunkpool.h" +#include "log.h" + +void +chunkpool_init(struct chunkpool *ctx, int size) +{ + ctx->chunk_size = size; + ctx->chunk = -1; + ctx->chunk_pos = 0; + ctx->chunk_max = (0x1fffff / size) * size; +} + +void +chunkpool_free(struct chunkpool *ctx) +{ + while(ctx->chunk >= 0) + { + free(ctx->chunks[ctx->chunk]); + ctx->chunk -= 1; + } + ctx->chunk_pos = 0; +} + +void * +chunkpool_malloc(struct chunkpool *ctx) +{ + void *p; + if(ctx->chunk_pos == 0) + { + void *m; + if(ctx->chunk == 31) + { + LOG(LOG_ERROR, ("out of chunks in file %s, line %d\n", + __FILE__, __LINE__)); + LOG(LOG_BRIEF, ("chunk_size %d\n", ctx->chunk_size)); + LOG(LOG_BRIEF, ("chunk_max %d\n", ctx->chunk_max)); + LOG(LOG_BRIEF, ("chunk %d\n", ctx->chunk)); + //exit(1); + return; + } + m = malloc(ctx->chunk_max); + if (m == NULL) + { + LOG(LOG_ERROR, ("out of memory error in file %s, line %d\n", + __FILE__, __LINE__)); + //exit(1); + return; + } + ctx->chunk += 1; + ctx->chunks[ctx->chunk] = m; + } + p = (char*)ctx->chunks[ctx->chunk] + ctx->chunk_pos; + ctx->chunk_pos += ctx->chunk_size; + if(ctx->chunk_pos >= ctx->chunk_max) + { + ctx->chunk_pos = 0; + } + return p; +} + +void * +chunkpool_calloc(struct chunkpool *ctx) +{ + void *p = chunkpool_malloc(ctx); + memset(p, 0, ctx->chunk_size); + return p; +} diff --git a/PSID/libpsid64/exomizer/chunkpool.h b/PSID/libpsid64/exomizer/chunkpool.h new file mode 100644 index 00000000..f6b665fe --- /dev/null +++ b/PSID/libpsid64/exomizer/chunkpool.h @@ -0,0 +1,51 @@ +#ifndef ALREADY_INCLUDED_CHUNKPOOL_H +#define ALREADY_INCLUDED_CHUNKPOOL_H + +/* + * Copyright (c) 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +struct chunkpool { + int chunk_size; + int chunk; + int chunk_pos; + int chunk_max; + void *chunks[32]; +}; + +void +chunkpool_init(struct chunkpool *ctx, int size); + +void +chunkpool_free(struct chunkpool *ctx); + +void * +chunkpool_malloc(struct chunkpool *ctx); + +void * +chunkpool_calloc(struct chunkpool *ctx); + +#endif diff --git a/PSID/libpsid64/exomizer/exomizer.c b/PSID/libpsid64/exomizer/exomizer.c new file mode 100644 index 00000000..12d85bce --- /dev/null +++ b/PSID/libpsid64/exomizer/exomizer.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2002, 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include +#include +#include +#include +#include "log.h" +#include "search.h" +#include "optimal.h" +#include "output.h" +#include "sfx.h" + +static +int +generate_output(match_ctx ctx, + search_nodep snp, + struct sfx_decruncher *decr, + encode_match_f * f, + encode_match_data emd, + int load, int len, int start, unsigned char *buf) +{ + int pos; + int pos_diff; + int max_diff; + int diff; + static output_ctx out; + output_ctxp old; + + output_ctx_init(out); + + old = emd->out; + emd->out = out; + + pos = output_get_pos(out); + + pos_diff = pos; + max_diff = 0; + + output_gamma_code(out, 16); + output_bits(out, 1, 0); /* 1 bit out */ + + diff = output_get_pos(out) - pos_diff; + if(diff > max_diff) + { + max_diff = diff; + } + + while (snp != NULL) + { + const_matchp mp; + + mp = snp->match; + if (mp != NULL && mp->len > 0) + { + if (mp->offset == 0) + { + /* literal */ + output_byte(out, ctx->buf[snp->index]); + output_bits(out, 1, 1); + } else + { + f(mp, emd); + output_bits(out, 1, 0); + } + + pos_diff += mp->len; + diff = output_get_pos(out) - pos_diff; + if(diff > max_diff) + { + max_diff = diff; + } + } + snp = snp->prev; + } + + /* output header here */ + optimal_out(out, emd); + + output_bits_flush(out); + + output_word(out, (unsigned short int) (load + len)); + + len = output_get_pos(out); + decr->load(out, (unsigned short int) load - max_diff); + output_copy_bytes(out, 0, len); + + /* second stage of decruncher */ + decr->stages(out, (unsigned short int) start); + + /*len = output_ctx_close(out, of);*/ + len = out->pos - out->start; + memcpy(buf, out->buf + out->start, len); + + emd->out = old; + + return len; +} + +static +search_nodep +do_compress(match_ctx ctx, encode_match_data emd, int max_passes) +{ + matchp_cache_enum mpce; + matchp_snp_enum snpe; + search_nodep snp; + search_nodep best_snp; + int pass; + float old_size; + + pass = 1; + + matchp_cache_get_enum(ctx, mpce); + optimal_optimize(emd, matchp_cache_enum_get_next, mpce); + + best_snp = NULL; + old_size = 1000000.0; + + for (;;) + { + snp = search_buffer(ctx, optimal_encode, emd); + if (snp == NULL) + { + //fprintf(stderr, "error: search_buffer() returned NULL\n"); + //exit(-1); + return; + } + + float size = snp->total_score; + if (size >= old_size) + { +#if 0 /* RH */ + search_node_free(snp); +#endif /* RH */ + break; + } + +#if 0 /* RH */ + if (best_snp != NULL) + { + search_node_free(best_snp); + } +#endif /* RH */ + best_snp = snp; + old_size = size; + ++pass; + + if (pass > max_passes) + { + break; + } + + optimal_free(emd); + optimal_init(emd); + + matchp_snp_get_enum(snp, snpe); + optimal_optimize(emd, matchp_snp_enum_get_next, snpe); + } + + return best_snp; +} + + +int exomizer(unsigned char *srcbuf, int len, int load, int start, unsigned char *destbuf) +{ + int destlen; + int max_offset = 65536; + int max_passes = 65536; + static match_ctx ctx; + encode_match_data emd; + encode_match_priv optimal_priv; + search_nodep snp; + + match_ctx_init(ctx, srcbuf, len, max_offset); + + emd->out = NULL; + emd->priv = optimal_priv; + + optimal_init(emd); + + snp = do_compress(ctx, emd, max_passes); + + destlen = generate_output(ctx, snp, sfx_c64ne, optimal_encode, emd, + load, len, start, destbuf); + optimal_free(emd); + +#if 0 /* RH */ + search_node_free(snp); +#endif /* RH */ + match_ctx_free(ctx); + + return destlen; +} diff --git a/PSID/libpsid64/exomizer/exomizer.h b/PSID/libpsid64/exomizer/exomizer.h new file mode 100644 index 00000000..5b6f3110 --- /dev/null +++ b/PSID/libpsid64/exomizer/exomizer.h @@ -0,0 +1,41 @@ +#ifndef ALREADY_INCLUDED_EXOMIZER_H +#define ALREADY_INCLUDED_EXOMIZER_H +/* + * Copyright (c) 2002 - 2004 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +int exomizer(unsigned char *srcbuf, int len, int load, int start, unsigned char *destbuf); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/PSID/libpsid64/exomizer/log.h b/PSID/libpsid64/exomizer/log.h new file mode 100644 index 00000000..f4c34cf8 --- /dev/null +++ b/PSID/libpsid64/exomizer/log.h @@ -0,0 +1,138 @@ +#ifndef ALREADY_INCLUDED_LOG_H +#define ALREADY_INCLUDED_LOG_H +/* + * Copyright (c) 2002, 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#if 0 /* RH */ +#include +#include +#endif /* RH */ + +enum log_level { + LOG_MIN = -99, + LOG_FATAL = -40, + LOG_ERROR = -30, + LOG_WARNING = -20, + LOG_BRIEF = -10, + LOG_NORMAL = 0, + LOG_VERBOSE = 10, + LOG_TRACE = 20, + LOG_DEBUG = 30, + LOG_DUMP = 40, + LOG_MAX = 99 +}; + +#if 0 /* RH */ +typedef +void log_formatter_f(FILE * out, /* IN */ + enum log_level level, /* IN */ + const char *context, /* IN */ + const char *); /* IN */ + +/* + * this log output function adds nothing + */ +void raw_log_formatter(FILE * out, /* IN */ + enum log_level level, /* IN */ + const char *context, /* IN */ + const char *log); /* IN */ + + +struct log_output; + +struct log_ctx; + +struct log_ctx *log_new(void); + +/* log_delete closes all added output streams + * and files except for stdout and stderr + */ +void log_delete(struct log_ctx *ctx); + +void log_set_level(struct log_ctx *ctx, /* IN/OUT */ + enum log_level level); /* IN */ + +void log_add_output_stream(struct log_ctx *ctx, /* IN/OUT */ + enum log_level min, /* IN */ + enum log_level max, /* IN */ + log_formatter_f * default_f, /* IN */ + FILE * out_stream); /* IN */ + +void log_vlog(struct log_ctx *ctx, /* IN */ + enum log_level level, /* IN */ + const char *context, /* IN */ + log_formatter_f * f, /* IN */ + const char *printf_str, /* IN */ + va_list argp); + + +void log_log_default(const char *printf_str, /* IN */ + ...); + +/* some helper macros */ + +extern struct log_ctx *G_log_ctx; +extern enum log_level G_log_level; +extern enum log_level G_log_log_level; + +#define LOG_SET_LEVEL(L) \ +do { \ + log_set_level(G_log_ctx, (L)); \ + G_log_level = (L); \ +} while(0) + +#define LOG_INIT(L) \ +do { \ + G_log_ctx = log_new(); \ + log_set_level(G_log_ctx, (L)); \ + G_log_level = (L); \ +} while(0) + +#define LOG_INIT_CONSOLE(X) \ +do { \ + G_log_ctx = log_new(); \ + log_set_level(G_log_ctx, (X)); \ + G_log_level = (X); \ + log_add_output_stream(G_log_ctx, LOG_WARNING, LOG_MAX, NULL, stdout); \ + log_add_output_stream(G_log_ctx, LOG_MIN, LOG_WARNING - 1, NULL, stderr); \ +} while(0) + +#define LOG_FREE log_delete(G_log_ctx) + +#define LOG(L, M) \ +do { \ + if(G_log_level >= (L)) { \ + G_log_log_level = (L); \ + log_log_default M; \ + } \ +} while(0) + +#endif +#endif /* RH */ + +/* RH: disable all logging */ +#define LOG(L, M) diff --git a/PSID/libpsid64/exomizer/match.c b/PSID/libpsid64/exomizer/match.c new file mode 100644 index 00000000..349365b4 --- /dev/null +++ b/PSID/libpsid64/exomizer/match.c @@ -0,0 +1,522 @@ +/* + * Copyright (c) 2002 - 2004 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include +#include +#include "log.h" +#include "match.h" +#include "chunkpool.h" +#include "radix.h" + + +struct match_node { + int index; + struct match_node *next; +}; + +static +const_matchp matches_calc(match_ctx ctx, /* IN/OUT */ + unsigned short int index); /* IN */ + +matchp match_new(match_ctx ctx, /* IN/OUT */ + matchp *mpp, + unsigned short int len, + unsigned short int offset) +{ + matchp m = chunkpool_malloc(ctx->m_pool); + m->len = len; + m->offset = offset; + + /* insert new node in list */ + m->next = *mpp; + *mpp = m; + + return m; +} + + +void match_ctx_init(match_ctx ctx, /* IN/OUT */ + const unsigned char *buf, /* IN */ + int buf_len, /* IN */ + int max_offset) +{ + struct match_node *np; + struct chunkpool map_pool[1]; + + int c, i; + int val; + + memset(ctx->info, 0, sizeof(ctx->info)); + memset(ctx->rle, 0, sizeof(ctx->rle)); + memset(ctx->rle_r, 0, sizeof(ctx->rle_r)); + + chunkpool_init(ctx->m_pool, sizeof(match)); + chunkpool_init(map_pool, sizeof(match)); + + ctx->max_offset = max_offset; + + ctx->buf = buf; + ctx->len = buf_len; + + val = buf[0]; + for (i = 1; i < buf_len; ++i) + { + if (buf[i] == val) + { + ctx->rle[i] = ctx->rle[i - 1] + 1; + } else + { + ctx->rle[i] = 0; + } + val = buf[i]; + } + + val = buf[buf_len - 1]; + for (i = buf_len - 2; i >= 0; --i) + { + if (buf[i] == val) + { + ctx->rle_r[i] = ctx->rle_r[i + 1] + 1; + } else + { + ctx->rle_r[i] = 0; + } + val = buf[i]; + } + + /* add extra nodes to rle sequences */ + for(c = 0; c < 256; ++c) + { + static char rle_map[65536]; + struct match_node *prev_np; + int rle_len; + + /* for each possible rle char */ + memset(rle_map, 0, sizeof(rle_map)); + prev_np = NULL; + for (i = 0; i < buf_len; ++i) + { + /* must be the correct char */ + if(buf[i] != c) + { + continue; + } + + rle_len = ctx->rle[i]; + if(!rle_map[rle_len] && ctx->rle_r[i] > 16) + { + /* no previous lengths and not our primary length*/ + continue; + } + + np = chunkpool_malloc(ctx->m_pool); + np->index = i; + np->next = NULL; + rle_map[rle_len] = 1; + + LOG(LOG_DEBUG, ("0) c = %d, added np idx %d -> %d\n", c, i, 0)); + + /* if we have a previous entry, let's chain it together */ + if(prev_np != NULL) + { + LOG(LOG_DEBUG, ("1) c = %d, pointed np idx %d -> %d\n", + c, prev_np->index, i)); + prev_np->next = np; + } + + ctx->info[i]->single = np; + prev_np = np; + } + + memset(rle_map, 0, sizeof(rle_map)); + prev_np = NULL; + for (i = buf_len - 1; i >= 0; --i) + { + /* must be the correct char */ + if(buf[i] != c) + { + continue; + } + + rle_len = ctx->rle_r[i]; + np = ctx->info[i]->single; + if(np == NULL) + { + if(rle_map[rle_len] && prev_np != NULL && rle_len > 0) + { + np = chunkpool_malloc(ctx->m_pool); + np->index = i; + np->next = prev_np; + ctx->info[i]->single = np; + + LOG(LOG_DEBUG, ("2) c = %d, added np idx %d -> %d\n", + c, i, prev_np->index)); + } + } + else + { + prev_np = np; + } + + if(ctx->rle_r[i] > 0) + { + continue; + } + rle_len = ctx->rle[i] + 1; + rle_map[rle_len] = 1; + } + } + + for (i = buf_len - 1; i >= 0; --i) + { + const_matchp matches; + /* let's populate the cache */ + matches = matches_calc(ctx, (unsigned short) i); + + /* add to cache */ + ctx->info[i]->cache = matches; + + if (!(i & 0xFF)) + { + LOG(LOG_NORMAL, (".")); + } + + } + + LOG(LOG_NORMAL, ("\n")); + + chunkpool_free(map_pool); +} + +void match_ctx_free(match_ctx ctx) /* IN/OUT */ +{ + chunkpool_free(ctx->m_pool); +} + +void dump_matches(matchp mp) +{ + if (mp == NULL) + { + LOG(LOG_DEBUG, (" (NULL)\n")); + } else + { + if(mp->offset > 0) + { + LOG(LOG_DEBUG, (" offset %d, len %d\n", mp->offset, mp->len)); + } + if (mp->next != NULL) + { + dump_matches(mp->next); + } + } +} + +const_matchp matches_get(match_ctx ctx, /* IN/OUT */ + unsigned short int index) /* IN */ +{ + return ctx->info[index]->cache; + +} + +/* this needs to be called with the indexes in + * reverse order */ +const_matchp matches_calc(match_ctx ctx, /* IN/OUT */ + unsigned short int index) /* IN */ +{ + const unsigned char *buf; + + matchp matches; + matchp mp; + struct match_node *np; + + buf = ctx->buf; + matches = NULL; + + LOG(LOG_DUMP, ("index %d, char '%c', rle %d, rle_r %d\n", + index, buf[index], ctx->rle[index], + ctx->rle_r[index])); + + /* proces the literal match and add it to matches */ + mp = match_new(ctx, &matches, 1, 0); + + /* get possible match */ + np = ctx->info[index]->single; + if(np != NULL) + { + np = np->next; + } + for (; np != NULL; np = np->next) + { + int mp_len; + int len; + int pos; + int offset; + + /* limit according to max offset */ + if(np->index > index + ctx->max_offset) + { + break; + } + + LOG(LOG_DEBUG, ("find lengths for index %d to index %d\n", + index, np->index)); + + /* get match len */ + mp_len = mp->offset > 0 ? mp->len : 0; + LOG(LOG_DEBUG, ("0) comparing with current best [%d] off %d len %d\n", + index, mp->offset, mp_len)); + + offset = np->index - index; + len = mp_len; + pos = index + 1 - len; + /* Compare the first bytes backwards. We can + * skip some comparisons by increasing by the rle count. We + * don't need to compare the first byte, hence > 1 instead of + * > 0 */ + while(len > 1 && buf[pos] == buf[pos + offset]) + { + int offset1 = ctx->rle_r[pos]; + int offset2 = ctx->rle_r[pos + offset]; + int offset = offset1 < offset2 ? offset1 : offset2; + + LOG(LOG_DEBUG, ("1) compared sucesssfully [%d] %d %d\n", + index, pos, pos + offset)); + + len -= 1 + offset; + pos += 1 + offset; + } + if(len > 1) + { + /* sequence length too short, skip this match */ + continue; + } + + if(offset < 17) + { + /* allocate match struct and add it to matches */ + mp = match_new(ctx, &matches, 1, offset); + } + + /* Here we know that the current match is atleast as long as + * the previuos one. let's compare further. */ + len = mp_len; + pos = index - len; + while(pos >= 0 && buf[pos] == buf[pos + offset]) + { + LOG(LOG_DEBUG, ("2) compared sucesssfully [%d] %d %d\n", + index, pos, pos + offset)); + ++len; + --pos; + } + if(len > mp_len) + { + /* allocate match struct and add it to matches */ + mp = match_new(ctx, &matches, index - pos, offset); + } + if(pos < 0) + { + /* we have reached the eof, no better matches can be found */ + break; + } + } + LOG(LOG_DEBUG, ("adding matches for index %d to cache\n", index)); + dump_matches(matches); + + return matches; +} + +static +int +matchp_keep_this(const_matchp mp) +{ + int val = 1; + /* if we want to ignore this matchp then return true else false */ + if(mp->len == 1) + { + if(mp->offset > 32) + { + val = 0; + } + } + return val; +} + +static +void +matchp_cache_peek(struct match_ctx *ctx, int pos, + const_matchp *litpp, const_matchp *seqpp, + matchp lit_tmp, matchp val_tmp) +{ + const_matchp litp, seqp, val; + + seqp = NULL; + litp = NULL; + if(pos >= 0) + { + val = matches_get(ctx, pos); + litp = val; + while(litp->offset != 0) + { + litp = litp->next; + } + + /* inject extra rle match */ + if(ctx->rle_r[pos] > 0) + { + val_tmp->offset = 1; + val_tmp->len = ctx->rle[pos] + 1; + val_tmp->next = (matchp)val; + val = val_tmp; + LOG(LOG_DEBUG, ("injecting rle val(%d,%d)\n", + val->len, val->offset)); + } + + while(val != NULL) + { + if(val->offset != 0) + { + if(matchp_keep_this(val)) + { + if(seqp == NULL || val->len > seqp->len || + (val->len == seqp->len && val->offset < seqp->offset)) + { + seqp = val; + } + } + if(litp->offset == 0 || litp->offset > val->offset) + { + LOG(LOG_DEBUG, ("val(%d,%d)", val->len, val->offset)); + if(lit_tmp != NULL) + { + int diff; + match tmp2; + *tmp2 = *val; + tmp2->len = 1; + diff = ctx->rle[pos + val->offset]; + if(tmp2->offset > diff) + { + tmp2->offset -= diff; + } + else + { + tmp2->offset = 1; + } + LOG(LOG_DEBUG, ("=> litp(%d,%d)", + tmp2->len, tmp2->offset)); + if(matchp_keep_this(tmp2)) + { + LOG(LOG_DEBUG, (", keeping")); + *lit_tmp = *tmp2; + litp = lit_tmp; + } + } + LOG(LOG_DEBUG, ("\n")); + } + } + val = val->next; + } + + } + +#if 0 + LOG(LOG_NORMAL, ("[%05d]: ", pos)); + if(litp == NULL) + LOG(LOG_NORMAL, ("litp(NULL)")); + else + LOG(LOG_NORMAL, ("litp(%d,%d)", litp->len, litp->offset)); + + if(seqp == NULL) + LOG(LOG_NORMAL, ("seqp(NULL)")); + else + LOG(LOG_NORMAL, ("seqp(%d,%d)", seqp->len, seqp->offset)); + + LOG(LOG_NORMAL, ("\n")); +#endif + + if(litpp != NULL) *litpp = litp; + if(seqpp != NULL) *seqpp = seqp; +} + +void matchp_cache_get_enum(match_ctx ctx, /* IN */ + matchp_cache_enum mpce) /* IN/OUT */ +{ + mpce->ctx = ctx; + mpce->pos = ctx->len - 1; + /*mpce->next = NULL;*/ /* two iterations */ + mpce->next = (void*)mpce; /* just one */ +} + +const_matchp matchp_cache_enum_get_next(void *matchp_cache_enum) +{ + const_matchp val, lit, seq; + matchp_cache_enump mpce; + + mpce = matchp_cache_enum; + + restart: + matchp_cache_peek(mpce->ctx, mpce->pos, &lit, &seq, + mpce->tmp1, mpce->tmp2); + + val = lit; + if(lit == NULL) + { + /* the end, reset enum and return NULL */ + mpce->pos = mpce->ctx->len - 1; + if(mpce->next == NULL) + { + mpce->next = (void*)mpce; + goto restart; + } + else + { + mpce->next = NULL; + } + } + else + { + if(seq != NULL) + { + match t1; + match t2; + const_matchp next; + matchp_cache_peek(mpce->ctx, mpce->pos - 1, NULL, &next, t1 ,t2); + if(next == NULL || + (next->len + (mpce->next != NULL && next->len < 3) <= seq->len)) + { + /* nope, next is not better, use this sequence */ + val = seq; + } + } + } + if(val != NULL) + { + LOG(LOG_DEBUG, ("Using len %05d, offset, %05d\n", val->len, val->offset)); + mpce->pos -= val->len; + } + return val; +} diff --git a/PSID/libpsid64/exomizer/match.h b/PSID/libpsid64/exomizer/match.h new file mode 100644 index 00000000..1e79740c --- /dev/null +++ b/PSID/libpsid64/exomizer/match.h @@ -0,0 +1,95 @@ +#ifndef ALREADY_INCLUDED_MATCH_H +#define ALREADY_INCLUDED_MATCH_H +/* + * Copyright (c) 2002 - 2004 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "chunkpool.h" + +struct match { + unsigned short int offset; + unsigned short int len; + struct match *next; +}; + +typedef struct match match[1]; +typedef struct match *matchp; +typedef const struct match *const_matchp; + +struct pre_calc { + struct match_node *single; + const struct match *cache; +}; + +typedef struct pre_calc pre_calc[1]; + +struct match_ctx { + struct chunkpool m_pool[1]; + pre_calc info[65536]; + unsigned short int rle[65536]; + unsigned short int rle_r[65536]; + const unsigned char *buf; + int len; + int max_offset; +}; + +typedef struct match_ctx match_ctx[1]; +typedef struct match_ctx *match_ctxp; + +void match_ctx_init(match_ctx ctx, /* IN/OUT */ + const unsigned char *buf, /* IN */ + int buf_len, /* IN */ + int max_offset); /* IN */ + +void match_ctx_free(match_ctx ctx); /* IN/OUT */ + +/* this needs to be called with the indexes in + * reverse order */ +const_matchp matches_get(match_ctx ctx, /* IN/OUT */ + unsigned short int index); /* IN */ + +void match_delete(match_ctx ctx, /* IN/OUT */ + matchp mp); /* IN */ + +struct matchp_cache_enum { + match_ctxp ctx; + const_matchp next; + match tmp1; + match tmp2; + int pos; +}; + +typedef struct matchp_cache_enum matchp_cache_enum[1]; +typedef struct matchp_cache_enum *matchp_cache_enump; + +void matchp_cache_get_enum(match_ctx ctx, /* IN */ + matchp_cache_enum mpce); /* IN/OUT */ + +typedef const_matchp matchp_enum_get_next_f(void *matchp_enum); /* IN/OUT */ + +const_matchp matchp_cache_enum_get_next(void *matchp_cache_enum); /* IN */ + +#endif diff --git a/PSID/libpsid64/exomizer/optimal.c b/PSID/libpsid64/exomizer/optimal.c new file mode 100644 index 00000000..1a4e748e --- /dev/null +++ b/PSID/libpsid64/exomizer/optimal.c @@ -0,0 +1,707 @@ +/* + * Copyright (c) 2002, 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include +#include +#include "log.h" +#include "search.h" +#include "radix.h" +#include "chunkpool.h" + +#define DOUBLE_OFFSET_TABLES + +struct _interval_node { + int start; + int score; + struct _interval_node *next; + signed char prefix; /* RH: signed */ + signed char bits; /* RH: signed */ + signed char depth; /* RH: signed */ + signed char flags; /* RH: signed */ +}; + +typedef struct _interval_node interval_node[1]; +typedef struct _interval_node *interval_nodep; + +static +void +interval_node_init(interval_nodep inp, int start, int depth, int flags) +{ + inp->start = start; + inp->flags = flags; + inp->depth = depth; + inp->bits = 0; + inp->prefix = flags >= 0 ? flags : depth + 1; + inp->score = -1; + inp->next = NULL; +} + +static +interval_nodep interval_node_clone(interval_nodep inp) +{ + interval_nodep inp2 = NULL; + + if(inp != NULL) + { + inp2 = malloc(sizeof(interval_node)); + if (inp2 == NULL) + { + LOG(LOG_ERROR, ("out of memory error in file %s, line %d\n", + __FILE__, __LINE__)); + //exit(0); + return; + } + /* copy contents */ + *inp2 = *inp; + inp2->next = interval_node_clone(inp->next); + } + + return inp2; +} + +static +void interval_node_delete(interval_nodep inp) +{ + while (inp != NULL) + { + interval_nodep inp2 = inp; + inp = inp->next; + free(inp2); + } +} + +#if 0 /* RH */ +static +void interval_node_dump(interval_nodep inp) +{ + int end; + + end = 0; + while (inp != NULL) + { + end = inp->start + (1 << inp->bits); + LOG(LOG_NORMAL, ("%X", inp->bits)); + /*LOG(LOG_NORMAL, ("%d[1�%d], ", inp->start, inp->bits));*/ + inp = inp->next; + } + LOG(LOG_NORMAL, ("[eol@%d]\n", end)); +} +#endif /* RH */ + +float optimal_encode_int(int arg, void *priv, output_ctxp out) +{ + interval_nodep inp; + int end; + + float val; + + inp = (interval_nodep) priv; + val = 1000000.0; + end = 0; + while (inp != NULL) + { + end = inp->start + (1 << inp->bits); + if (arg >= inp->start && arg < end) + { + break; + } + inp = inp->next; + } + if (inp != NULL) + { + val = (float) (inp->prefix + inp->bits); + } + /*LOG(LOG_DUMP, ("encoding %d to %0.1f bits\n", arg, val)); */ + if (out) + { + output_bits(out, inp->bits, arg - inp->start); + if (inp->flags < 0) + { + /*LOG(LOG_DUMP, ("gamma prefix code = %d", inp->depth)); */ + output_gamma_code(out, inp->depth); + } else + { + /*LOG(LOG_DUMP, ("flat prefix %d bits ", inp->depth)); */ + output_bits(out, inp->prefix, inp->depth); + } + } + + return val; +} + +float optimal_encode(const_matchp mp, encode_match_data emd) +{ + interval_nodep *offset; + float bits; + encode_match_privp data; + + data = emd->priv; + offset = data->offset_f_priv; + /*LOG(LOG_DUMP, ("judging offset %d len %d", offset, len)); */ + + bits = 0.0; + if (mp->offset == 0) + { + bits += 9.0f * mp->len; + data->lit_num += mp->len; + data->lit_bits += bits; + } else + { + bits += 1.0; + switch (mp->len) + { + case 0: + LOG(LOG_ERROR, ("bad len\n")); + // exit(1); + return; + break; + case 1: +#if 1 + bits += data->offset_f(mp->offset, offset[0], emd->out); +#else + bits += 4.0; + if (mp->offset > (1 << 4)) + { + bits += 1000000.0; + } + if (emd->out) + { + output_bits(emd->out, 4, mp->offset - 1); + } +#endif + break; +#ifdef DOUBLE_OFFSET_TABLES + case 2: + bits += data->offset_f(mp->offset, offset[1], emd->out); + break; +#if 0 + case 3: + case 4: + bits += data->offset_f(mp->offset, offset[2], emd->out); + break; +#endif +#endif + default: + bits += data->offset_f(mp->offset, offset[7], emd->out); + break; + } + bits += data->len_f(mp->len, data->len_f_priv, emd->out); + if (bits > (9.0 * mp->len)) + { + /* lets make literals out of it */ + data->lit_num += 1; + data->lit_bits += bits; + } else + { + if (mp->offset == 1) + { + data->rle_num += 1; + data->rle_bits += bits; + } else + { + data->seq_num += 1; + data->seq_bits += bits; + } + } + } + return bits; +} + +struct _optimize_arg { + radix_root cache; + int *stats; + int *stats2; + int max_depth; + int flags; + struct chunkpool in_pool[1]; +}; + +#define CACHE_KEY(START, DEPTH, MAXDEPTH) ((int)((START)*(MAXDEPTH)|DEPTH)) + +typedef struct _optimize_arg optimize_arg[1]; +typedef struct _optimize_arg optimize_argp; + +static interval_nodep +optimize1(optimize_arg arg, int start, int depth) +{ + interval_node inp; + interval_nodep best_inp; + int end, i; + int start_count, end_count; + + /*LOG(LOG_DUMP, ("IN start %d, depth %d\n", start, depth)); */ + + do + { + best_inp = NULL; + if (arg->stats[start] == 0) + { + break; + } + int key = CACHE_KEY(start, depth, arg->max_depth); + best_inp = radix_node_get(arg->cache, key); + if (best_inp != NULL) + { + break; + } + + interval_node_init(inp, start, depth, arg->flags); + + for (i = 0; i < 16; ++i) + { + inp->next = NULL; + inp->bits = i; + end = start + (1 << i); + + start_count = end_count = 0; + if (start < 65536) + { + start_count = arg->stats[start]; + if (end < 65536) + { + end_count = arg->stats[end]; + } + } + + inp->score = (start_count - end_count) * + (inp->prefix + inp->bits); + + /* one index below */ + /*LOG(LOG_DUMP, ("interval score: [%d�%d[%d\n", + start, i, inp->score)); */ + if (end_count > 0) + { + int penalty; + /* we're not done, now choose between using + * more bits, go deeper or skip the rest */ + if (depth + 1 < arg->max_depth) + { + /* we can go deeper, let's try that */ + inp->next = optimize1(arg, end, depth + 1); + } + /* get the penalty for skipping */ + penalty = 1000000; + if (arg->stats2 != NULL) + { + penalty = arg->stats2[end]; + } + if (inp->next != NULL && inp->next->score < penalty) + { + penalty = inp->next->score; + } + inp->score += penalty; + } + if (best_inp == NULL || inp->score < best_inp->score) + { + /* it's the new best in town, use it */ + if (best_inp == NULL) + { + /* allocate if null */ + best_inp = chunkpool_malloc(arg->in_pool); + } + *best_inp = *inp; + } + } + if (best_inp != NULL) + { + radix_node_set(arg->cache, key, best_inp); + } + } + while (0); + /*LOG(LOG_DUMP, ("OUT depth %d: ", depth)); */ + /*interval_node_dump(best_inp); */ + return best_inp; +} + +static interval_nodep +optimize(int stats[65536], int stats2[65536], int max_depth, int flags) +{ + optimize_arg arg; + + interval_nodep inp; + + arg->stats = stats; + arg->stats2 = stats2; + + arg->max_depth = max_depth; + arg->flags = flags; + + chunkpool_init(arg->in_pool, sizeof(interval_node)); + + radix_tree_init(arg->cache); + + inp = optimize1(arg, 1, 0); + + /* use normal malloc for the winner */ + inp = interval_node_clone(inp); + + /* cleanup */ + radix_tree_free(arg->cache, NULL, NULL); + chunkpool_free(arg->in_pool); + + return inp; +} + + +/*static interval_nodep optimal_offset[16] = {NULL, NULL}; + static interval_nodep optimal_len = NULL;*/ + +void optimal_init(encode_match_data emd) /* OUT */ +{ + encode_match_privp data; + interval_nodep *inpp; + + data = emd->priv; + + memset(data, 0, sizeof(encode_match_priv)); + + data->offset_f = optimal_encode_int; + data->len_f = optimal_encode_int; + inpp = malloc(sizeof(interval_nodep[8])); + inpp[0] = NULL; + inpp[1] = NULL; + inpp[2] = NULL; + inpp[3] = NULL; + inpp[4] = NULL; + inpp[5] = NULL; + inpp[6] = NULL; + inpp[7] = NULL; + data->offset_f_priv = inpp; + data->len_f_priv = NULL; +} + +void optimal_free(encode_match_data emd) /* IN */ +{ + encode_match_privp data; + interval_nodep *inpp; + interval_nodep inp; + + data = emd->priv; + + inpp = data->offset_f_priv; + if (inpp != NULL) + { + interval_node_delete(inpp[0]); + interval_node_delete(inpp[1]); + interval_node_delete(inpp[2]); + interval_node_delete(inpp[3]); + interval_node_delete(inpp[4]); + interval_node_delete(inpp[5]); + interval_node_delete(inpp[6]); + interval_node_delete(inpp[7]); + } + free(inpp); + + inp = data->len_f_priv; + interval_node_delete(inp); + + data->offset_f_priv = NULL; + data->len_f_priv = NULL; +} + +#if 0 /* RH */ +void freq_stats_dump(int arr[65536]) +{ + int i; + for (i = 0; i < 32; ++i) + { + LOG(LOG_NORMAL, ("%d, ", arr[i] - arr[i + 1])); + } + LOG(LOG_NORMAL, ("\n")); +} + +void freq_stats_dump_raw(int arr[65536]) +{ + int i; + for (i = 0; i < 32; ++i) + { + LOG(LOG_NORMAL, ("%d, ", arr[i])); + } + LOG(LOG_NORMAL, ("\n")); +} +#endif /* RH */ + +void optimal_optimize(encode_match_data emd, /* IN/OUT */ + matchp_enum_get_next_f * f, /* IN */ + void *matchp_enum) /* IN */ +{ + encode_match_privp data; + const_matchp mp; + interval_nodep *offset; + static int offset_arr[8][65536]; + static int offset_parr[8][65536]; + static int len_arr[65536]; + int treshold; + + int i, j; + void *priv1; + + data = emd->priv; + + memset(offset_arr, 0, sizeof(offset_arr)); + memset(offset_parr, 0, sizeof(offset_parr)); + memset(len_arr, 0, sizeof(len_arr)); + + offset = data->offset_f_priv; + + /* first the lens */ + priv1 = matchp_enum; + while ((mp = f(priv1)) != NULL && mp->len > 0) + { + if (mp->offset > 0) + { + len_arr[mp->len] += 1; + } + } + + for (i = 65534; i >= 0; --i) + { + len_arr[i] += len_arr[i + 1]; + } + + data->len_f_priv = optimize(len_arr, NULL, 16, -1); + + /* then the offsets */ + priv1 = matchp_enum; + while ((mp = f(priv1)) != NULL && mp->len > 0) + { + if (mp->offset > 0) + { + treshold = mp->len * 9; + treshold -= 1 + (int) optimal_encode_int(mp->len, + data->len_f_priv, + NULL); + switch (mp->len) + { + case 0: + LOG(LOG_NORMAL, ("bad len\n")); + //exit(0); + return; + break; + case 1: +#if 1 + offset_parr[0][mp->offset] += treshold; + offset_arr[0][mp->offset] += 1; +#endif + break; +#ifdef DOUBLE_OFFSET_TABLES + case 2: + offset_parr[1][mp->offset] += treshold; + offset_arr[1][mp->offset] += 1; + break; +#if 0 + case 3: + case 4: + offset_parr[2][mp->offset] += treshold; + offset_arr[2][mp->offset] += 1; + break; +#endif +#endif + default: + offset_parr[7][mp->offset] += treshold; + offset_arr[7][mp->offset] += 1; + break; + } + } + } + + for (i = 65534; i >= 0; --i) + { + for (j = 0; j < 8; ++j) + { + offset_arr[j][i] += offset_arr[j][i + 1]; + offset_parr[j][i] += offset_parr[j][i + 1]; + } + } + + offset[0] = optimize(offset_arr[0], offset_parr[0], 1 << 2, 2); + offset[1] = optimize(offset_arr[1], offset_parr[1], 1 << 4, 4); + offset[2] = optimize(offset_arr[2], offset_parr[2], 1 << 4, 4); + offset[3] = optimize(offset_arr[3], offset_parr[3], 1 << 4, 4); + offset[4] = optimize(offset_arr[4], offset_parr[4], 1 << 4, 4); + offset[5] = optimize(offset_arr[5], offset_parr[5], 1 << 4, 4); + offset[6] = optimize(offset_arr[6], offset_parr[6], 1 << 4, 4); + offset[7] = optimize(offset_arr[7], offset_parr[7], 1 << 4, 4); +} + +#if 0 /* RH */ +static int optimal_fixup1(interval_nodep *npp, + int start, int depth, int flags, int max) +{ + int size = max - start; + if (*npp == NULL) + { + int bits = 0; + LOG(LOG_DEBUG, ("max %d, size %d\n", max, size)); + if(depth < 15) + { + interval_node in; + while (size > 0) + { + size >>= 1; + ++bits; + } + in->start = start; + in->depth = depth; + in->flags = flags; + in->bits = bits > 15 ? 15 : bits; + in->next = NULL; + + *npp = interval_node_clone(in); + } + } + if(*npp != NULL) + { + int len = (1 << (*npp)->bits); + size = optimal_fixup1(&((*npp)->next), + start + len, depth + 1, flags, max); + if(size > 0) + { + int bits = (*npp)->bits; + int diff = 32768 - (1 << bits); + if(diff > 0) + { + interval_nodep np; + (*npp)->bits = 15; + + np = (*npp)->next; + while(np != NULL) + { + np->start += diff; + np = np->next; + } + size -= diff; + } + } + } + return size; +} + +void optimal_fixup(encode_match_data emd, int max_len, int max_offset) +{ + encode_match_privp data; + interval_nodep *offset; + interval_nodep len; + + data = emd->priv; + + offset = data->offset_f_priv; + len = data->len_f_priv; + + optimal_fixup1(&len, 1, 0, -1, max_len); + + /*optimal_fixup1(&(offset[7]), 1, 0, -4, max_offset);*/ +} + +void optimal_dump(encode_match_data emd) +{ + encode_match_privp data; + interval_nodep *offset; + interval_nodep len; + + data = emd->priv; + + offset = data->offset_f_priv; + len = data->len_f_priv; + + LOG(LOG_NORMAL, ("lens: ")); + interval_node_dump(len); + + LOG(LOG_NORMAL, ("offsets (len =1): ")); + interval_node_dump(offset[0]); + + LOG(LOG_NORMAL, ("offsets (len =2): ")); + interval_node_dump(offset[1]); +#if 0 + LOG(LOG_NORMAL, ("offsets (len =3): ")); + interval_node_dump(offset[2]); + + LOG(LOG_NORMAL, ("offsets (len =4): ")); + interval_node_dump(offset[3]); + + LOG(LOG_NORMAL, ("offsets (len =5): ")); + interval_node_dump(offset[4]); + + LOG(LOG_NORMAL, ("offsets (len =6): ")); + interval_node_dump(offset[5]); + + LOG(LOG_NORMAL, ("offsets (len =7): ")); + interval_node_dump(offset[6]); +#endif + LOG(LOG_NORMAL, ("offsets (len =8): ")); + interval_node_dump(offset[7]); +} +#endif /* RH */ + +static +void interval_out(output_ctx out, interval_nodep inp1, int size) +{ + unsigned char buffer[256]; + unsigned char count; + interval_nodep inp; + + count = 0; + + memset(buffer, 15, sizeof(buffer)); + inp = inp1; + while (inp != NULL) + { + ++count; + /*LOG(LOG_DUMP, ("bits %d, lo %d, hi %d\n", + inp->bits, inp->start & 0xFF, inp->start >> 8)); */ + buffer[sizeof(buffer) - count] = inp->bits; + inp = inp->next; + } + + while (size > 0) + { + int b; + b = buffer[sizeof(buffer) - size]; + /*LOG(LOG_DUMP, ("outputting nibble %d\n", b)); */ + output_bits(out, 4, b); + size--; + } +} + +void optimal_out(output_ctx out, /* IN/OUT */ + encode_match_data emd) /* IN */ +{ + encode_match_privp data; + interval_nodep *offset; + interval_nodep len; + + data = emd->priv; + + offset = data->offset_f_priv; + len = data->len_f_priv; + + interval_out(out, offset[0], 4); + interval_out(out, offset[1], 16); + interval_out(out, offset[7], 16); + interval_out(out, len, 16); +} diff --git a/PSID/libpsid64/exomizer/optimal.h b/PSID/libpsid64/exomizer/optimal.h new file mode 100644 index 00000000..82dfca2f --- /dev/null +++ b/PSID/libpsid64/exomizer/optimal.h @@ -0,0 +1,54 @@ +#ifndef ALREADY_INCLUDED_OPTIMAL_H +#define ALREADY_INCLUDED_OPTIMAL_H + +/* + * Copyright (c) 2002, 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "search.h" +#include "output.h" + +float optimal_encode(const_matchp mp, /* IN */ + encode_match_data emp); /* IN */ + +void optimal_init(encode_match_data emp); /* IN/OUT */ + +void optimal_free(encode_match_data emd); /* IN */ + +void optimal_optimize(encode_match_data emd, /* IN/OUT */ + matchp_enum_get_next_f * f, /* IN */ + void *priv); /* IN */ + +void optimal_fixup(encode_match_data emd, /* IN/OUT */ + int max_len, /* IN */ + int max_offset); /* IN */ + +void optimal_dump(encode_match_data emp); /* IN */ + +void optimal_out(output_ctx out, /* IN/OUT */ + encode_match_data emd); /* IN */ + +#endif diff --git a/PSID/libpsid64/exomizer/output.c b/PSID/libpsid64/exomizer/output.c new file mode 100644 index 00000000..372976bb --- /dev/null +++ b/PSID/libpsid64/exomizer/output.c @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2002, 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include "log.h" +#include "output.h" + +#define OUTPUT_FLAG_REVERSE 1 + +void output_ctx_init(output_ctx ctx) /* IN/OUT */ +{ + ctx->bitbuf = 1; + ctx->pos = 0; + ctx->start = 0; + ctx->flags = 0; +} + +void output_ctx_set_start(output_ctx ctx, /* IN/OUT */ + unsigned int pos) /* IN */ +{ + ctx->start = pos; +} + +#if 0 /* RH */ +void output_ctx_set_reverse(output_ctx ctx) /* IN/OUT */ +{ + ctx->flags |= OUTPUT_FLAG_REVERSE; +} + + +static void reverse(unsigned char *start, int len) +{ + unsigned char *end = start + len - 1; + unsigned char tmp; + + while (start < end) + { + tmp = *start; + *start = *end; + *end = tmp; + + ++start; + --end; + } +} + +unsigned int output_ctx_close(output_ctx ctx, /* IN */ + FILE * out) /* OUT */ +{ + int rval; + int len; + + rval = 0; + /* flush the buffer */ + len = ctx->pos - ctx->start; + + if (ctx->start + len > sizeof(ctx->buf)) + { + LOG(LOG_ERROR, ("error: out of range in output_ctx_close()\n")); + exit(1); + } + + if (ctx->flags & OUTPUT_FLAG_REVERSE) + { + reverse(ctx->buf + ctx->start, len); + ctx->flags &= ~OUTPUT_FLAG_REVERSE; + } + + fwrite(ctx->buf + ctx->start, 1, len, out); + return len; +} +#endif /* RH */ + +unsigned int output_get_pos(output_ctx ctx) /* IN */ +{ + return ctx->pos; +} + +void output_set_pos(output_ctx ctx, /* IN */ + unsigned int pos) /* IN */ +{ + ctx->pos = pos; +} + +void output_byte(output_ctx ctx, /* IN/OUT */ + unsigned char byte) /* IN */ +{ + /*LOG(LOG_DUMP, ("output_byte: $%02X\n", byte)); */ + ctx->buf[ctx->pos] = byte; + ++(ctx->pos); +} + +void output_copy_bytes(output_ctx ctx, /* IN */ + unsigned int src_pos, /* IN */ + unsigned int len) /* IN */ +{ + int i; + if(src_pos > ctx->pos) + { + for (i = 0; (unsigned int)i < len; ++i) + { + ctx->buf[ctx->pos + i] = ctx->buf[src_pos + i]; + } + } + else if(src_pos < ctx->pos) + { + for (i = len - 1; i >= 0; --i) + { + ctx->buf[ctx->pos + i] = ctx->buf[src_pos + i]; + } + } + ctx->pos += len; +} + +void output_word(output_ctx ctx, /* IN/OUT */ + unsigned short int word) /* IN */ +{ + output_byte(ctx, (unsigned char) (word & 0xff)); + output_byte(ctx, (unsigned char) (word >> 8)); +} + + +void output_bits_flush(output_ctx ctx) /* IN/OUT */ +{ + /* flush the bitbuf including + * the extra 1 bit acting as eob flag */ + output_byte(ctx, (unsigned char) (ctx->bitbuf & 0xFF)); + if (ctx->bitbuf & 0x100) + { + output_byte(ctx, 1); + } + /*LOG(LOG_DUMP, ("bitstream flushed 0x%02X\n", ctx->bitbuf & 0xFF)); */ + + /* reset it */ + ctx->bitbuf = 1; +} + +#if 0 /* RH */ +void bits_dump(int count, int val) +{ + static char buf[1024]; + char *pek; + pek = buf; + if (count > 0) + { + pek += sprintf(pek, "0x%04X, % 2d: ", val, count); + } + while (count-- > 0) + { + *(pek++) = val & (1 << count) ? '1' : '0'; + } + *(pek++) = '\0'; + LOG(LOG_NORMAL, ("%s\n", buf)); +} +#endif /* RH */ + +void output_bits(output_ctx ctx, /* IN/OUT */ + int count, /* IN */ + int val) /* IN */ +{ + /*LOG(LOG_DUMP, ("output_bits: count = %d, val = %d\n", count, val)); */ + /* this makes the bits appear in reversed + * big endian order in the output stream */ + while (count-- > 0) + { + ctx->bitbuf <<= 1; + ctx->bitbuf |= val & 0x1; + val >>= 1; + if (ctx->bitbuf & 0x100) + { + /* full byte, flush it */ + output_byte(ctx, (unsigned char) (ctx->bitbuf & 0xFF)); + /*LOG(LOG_DUMP, + ("bitstream byte 0x%02X\n", ctx->bitbuf & 0xFF)); */ + ctx->bitbuf = 1; + } + } +} + +void output_gamma_code(output_ctx ctx, /* IN/OUT */ + int code) /* IN */ +{ + output_bits(ctx, 1, 1); + while (code-- > 0) + { + output_bits(ctx, 1, 0); + } +} diff --git a/PSID/libpsid64/exomizer/output.h b/PSID/libpsid64/exomizer/output.h new file mode 100644 index 00000000..fa68b767 --- /dev/null +++ b/PSID/libpsid64/exomizer/output.h @@ -0,0 +1,77 @@ +#ifndef ALREADY_INCLUDED_OUTPUT_H +#define ALREADY_INCLUDED_OUTPUT_H + +/* + * Copyright (c) 2002, 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include + +struct _output_ctx { + unsigned int bitbuf; + unsigned short int pos; + unsigned short int start; + unsigned char flags; + unsigned char buf[65536]; +}; + +typedef struct _output_ctx output_ctx[1]; +typedef struct _output_ctx *output_ctxp; + +void output_ctx_init(output_ctx ctx); /* IN/OUT */ + +void output_ctx_set_start(output_ctx ctx, /* IN/OUT */ + unsigned int pos); /* IN */ + +void output_ctx_set_reverse(output_ctx ctx); /* IN/OUT */ + +unsigned int output_ctx_close(output_ctx ctx, /* IN */ + FILE * out); /* OUT */ + +unsigned int output_get_pos(output_ctx ctx); /* IN */ + +void output_set_pos(output_ctx ctx, /* IN */ + unsigned int pos); /* IN */ + +void output_copy_bytes(output_ctx ctx, /* IN */ + unsigned int src_pos, /* IN */ + unsigned int len); /* IN */ + +void output_byte(output_ctx ctx, /* IN/OUT */ + unsigned char byte); /* IN */ + +void output_word(output_ctx ctx, /* IN/OUT */ + unsigned short int word); /* IN */ + +void output_bits_flush(output_ctx ctx); /* IN/OUT */ + +void output_bits(output_ctx ctx, /* IN/OUT */ + int count, /* IN */ + int val); /* IN */ + +void output_gamma_code(output_ctx ctx, /* IN/OUT */ + int code); /* IN */ +#endif diff --git a/PSID/libpsid64/exomizer/radix.c b/PSID/libpsid64/exomizer/radix.c new file mode 100644 index 00000000..2e741c18 --- /dev/null +++ b/PSID/libpsid64/exomizer/radix.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2002, 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include +#include +#include "log.h" +#include "radix.h" +#include "chunkpool.h" + +#define RADIX_TREE_NODE_RADIX 11U +#define RADIX_TREE_NODE_MASK ((1U << RADIX_TREE_NODE_RADIX) - 1U) + +struct _radix_node { + struct _radix_node *rn; +}; + +void radix_tree_init(radix_root rr) /* IN */ +{ + rr->depth = 0; + rr->root = NULL; + + chunkpool_init(rr->mem, (1 << RADIX_TREE_NODE_RADIX) * sizeof(void *)); +} + +static +void radix_tree_free_helper(int depth, radix_nodep rnp, free_callback * f, /* IN */ + void *priv) /* IN */ +{ + int i; + do + { + if (depth == 0) + { + /* do something to the data pointer? */ + if (f != NULL) + { + f(rnp, priv); + } + break; + } + if (rnp == NULL) + { + /* tree not grown here */ + break; + } + + for (i = RADIX_TREE_NODE_MASK; i >= 0; --i) + { + radix_tree_free_helper(depth - 1, rnp[i].rn, f, priv); + rnp[i].rn = NULL; + } + } + while (0); +} + +void radix_tree_free(radix_root rr, /* IN */ + free_callback * f, /* IN */ + void *priv) /* IN */ +{ + radix_tree_free_helper(rr->depth, rr->root, f, priv); + rr->depth = 0; + rr->root = NULL; + chunkpool_free(rr->mem); +} + +void radix_node_set(radix_rootp rrp, /* IN */ + unsigned int index, /* IN */ + void *data) /* IN */ +{ + radix_nodep rnp; + radix_nodep *rnpp; + unsigned int mask; + int depth; + + mask = ~0U << (RADIX_TREE_NODE_RADIX * rrp->depth); + while (index & mask) + { + /*LOG(LOG_DUMP, ("calloc called\n")); */ + /* not deep enough, let's deepen the tree */ + rnp = chunkpool_calloc(rrp->mem); + + rnp[0].rn = rrp->root; + rrp->root = rnp; + rrp->depth += 1; + + mask = ~0U << (RADIX_TREE_NODE_RADIX * rrp->depth); + } + + /* go down */ + rnpp = &rrp->root; + for (depth = rrp->depth - 1; depth >= 0; --depth) + { + unsigned int node_index; + + if (*rnpp == NULL) + { + /*LOG(LOG_DUMP, ("calloc called\n")); */ + /* tree is not grown in this interval */ + *rnpp = chunkpool_calloc(rrp->mem); + } + node_index = ((index >> (RADIX_TREE_NODE_RADIX * depth)) & + RADIX_TREE_NODE_MASK); + + rnpp = &((*rnpp)[node_index].rn); + } + *rnpp = data; +} + +void *radix_node_get(radix_root rr, /* IN */ + unsigned int index) /* IN */ +{ + radix_nodep rnp; + unsigned short int depth; + + /* go down */ + rnp = rr->root; + for (depth = rr->depth - 1; depth < 0xffff; --depth) + { + unsigned short int node_index; + + if (rnp == NULL) + { + /* tree is not grown in this interval */ + break; + } + node_index = ((index >> (RADIX_TREE_NODE_RADIX * depth)) & + RADIX_TREE_NODE_MASK); + + rnp = rnp[node_index].rn; + } + return rnp; +} diff --git a/PSID/libpsid64/exomizer/radix.h b/PSID/libpsid64/exomizer/radix.h new file mode 100644 index 00000000..efe86e51 --- /dev/null +++ b/PSID/libpsid64/exomizer/radix.h @@ -0,0 +1,60 @@ +#ifndef ALREADY_INCLUDED_RADIX_H +#define ALREADY_INCLUDED_RADIX_H +/* + * Copyright (c) 2002, 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "chunkpool.h" + +typedef struct _radix_node *radix_nodep; + +struct _radix_root { + int depth; + radix_nodep root; + struct chunkpool mem[1]; +}; + +typedef struct _radix_root radix_root[1]; +typedef struct _radix_root *radix_rootp; + + +typedef void free_callback(void *data, void *priv); + +/* *f will be called even for null pointers */ +void radix_tree_free(radix_root rr, /* IN */ + free_callback * f, /* IN */ + void *priv); /* IN */ + +void radix_tree_init(radix_root rr); /* IN */ + +void radix_node_set(radix_root rr, /* IN */ + unsigned int index, /* IN */ + void *data); /* IN */ + +void *radix_node_get(radix_root rr, /* IN */ + unsigned int index); /* IN */ + +#endif diff --git a/PSID/libpsid64/exomizer/search.c b/PSID/libpsid64/exomizer/search.c new file mode 100644 index 00000000..e79f5a40 --- /dev/null +++ b/PSID/libpsid64/exomizer/search.c @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2002 - 2004 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include +#include +#include "log.h" +#include "search.h" + +#if 0 /* RH */ +void search_node_free(search_nodep snp) /* IN */ +{ + /* emty now since snp:s are stored in an array */ +} + +void search_node_dump(search_nodep snp) /* IN */ +{ + while (snp != NULL) + { + LOG(LOG_DEBUG, ("index %d, mp ", snp->index)); + if (snp->match == NULL) + { + LOG(LOG_DEBUG, ("(NULL)")); + } else + { + LOG(LOG_DEBUG, + ("(of %d, le %d)", snp->match->offset, snp->match->len)); + } + LOG(LOG_DEBUG, + (", score %0.1f, total %0.1f\n", + snp->match_score, snp->total_score)); + + snp = snp->prev; + } +} +#endif /* RH */ + +search_nodep search_buffer(match_ctx ctx, /* IN */ + encode_match_f * f, /* IN */ + encode_match_data emd) /* IN */ +{ + static search_node snp_arr[65536]; + const_matchp mp; + search_nodep snp; +#if 0 /* RH */ + search_nodep best_copy_snp; +#endif /* RH */ + int best_copy_len; + + search_nodep best_rle_snp; + + int len = ctx->len; + + memset(snp_arr, 0, sizeof(snp_arr)); + + snp = snp_arr[len]; + snp->index = len; + snp->match->offset = 0; + snp->match->len = 0; + snp->match_score = 0; + snp->total_score = 0; + snp->prev = NULL; + +#if 0 /* RH */ + best_copy_snp = snp; +#endif /* RH */ + best_copy_len = 0.0; + + best_rle_snp = NULL; + + /* think twice about changing this code, + * it works the way it is. The last time + * I examined this code I was certain it was + * broken and broke it myself, trying to fix it. */ + while (len >= 0 && + (mp = matches_get(ctx, (unsigned short) (len - 1))) != NULL) + { + float prev_score; + + /* check if we can do rle */ + snp = snp_arr[len]; + if(best_rle_snp == NULL || + snp->index + ctx->rle_r[snp->index] < best_rle_snp->index) + { + /* best_rle_snp can't be reached by rle from snp, reset it*/ + if(ctx->rle[snp->index] > 0) + { + best_rle_snp = snp; + LOG(LOG_DEBUG, ("resetting best_rle at index %d, len %d\n", + snp->index, ctx->rle[snp->index])); + } + else + { + best_rle_snp = NULL; + } + } + else if(ctx->rle[snp->index] > 0 && + snp->index + ctx->rle_r[snp->index] >= best_rle_snp->index) + { + float best_rle_score; + float total_best_rle_score; + float snp_rle_score; + float total_snp_rle_score; + match rle_mp; + + LOG(LOG_DEBUG, ("challenger len %d, index %d, " + "ruling len %d, index %d\n", + ctx->rle_r[snp->index], snp->index, + ctx->rle_r[best_rle_snp->index], + best_rle_snp->index)); + + /* snp and best_rle_snp is the same rle area, + * let's see which is best */ +#undef NEW_STYLE +#ifdef NEW_STYLE + rle_mp->len = best_rle_snp->index - snp->index; +#else + rle_mp->len = ctx->rle[best_rle_snp->index]; +#endif + rle_mp->offset = 1; + best_rle_score = f(rle_mp, emd); + total_best_rle_score = best_rle_snp->total_score + + best_rle_score; + +#ifdef NEW_STYLE + snp_rle_score = 0.0; +#else + rle_mp->len = ctx->rle[snp->index]; + rle_mp->offset = 1; + snp_rle_score = f(rle_mp, emd); +#endif + total_snp_rle_score = snp->total_score + snp_rle_score; + + if(total_snp_rle_score <= total_best_rle_score) + { + /* yes, the snp is a better rle than best_rle_snp */ + LOG(LOG_DEBUG, ("prospect len %d, index %d, (%0.1f+%0.1f) " + "ruling len %d, index %d (%0.1f+%0.1f)\n", + ctx->rle[snp->index], snp->index, + snp->total_score, snp_rle_score, + ctx->rle[best_rle_snp->index], + best_rle_snp->index, + best_rle_snp->total_score, best_rle_score)); + best_rle_snp = snp; + LOG(LOG_DEBUG, ("setting current best_rle: " + "index %d, len %d\n", + snp->index, rle_mp->len)); + } + } + if(best_rle_snp != NULL && best_rle_snp != snp) + { + float rle_score; + float total_rle_score; + /* check if rle is better */ + match local_mp; + local_mp->len = best_rle_snp->index - snp->index; + local_mp->offset = 1; + rle_score = f(local_mp, emd); + total_rle_score = best_rle_snp->total_score + rle_score; + + LOG(LOG_DEBUG, ("comparing index %d (%0.1f) with " + "rle index %d, len %d, total score %0.1f %0.1f\n", + snp->index, snp->total_score, + best_rle_snp->index, local_mp->len, + best_rle_snp->total_score, rle_score)); + + if(snp->total_score > total_rle_score) + { + /*here it is good to do rle instead of crunch */ + LOG(LOG_DEBUG, + ("rle index %d, len %d, total %0.1f, rle %0.1f\n", + snp->index, local_mp->len, + snp->total_score, total_rle_score)); + + snp->total_score = total_rle_score; + snp->match_score = rle_score; + snp->prev = best_rle_snp; + *snp->match = *local_mp; + } + } + /* end of rle optimization */ + + LOG(LOG_DUMP, + ("matches for index %d with total score %0.1f\n", + len - 1, snp->total_score)); + + prev_score = snp_arr[len]->total_score; + while (mp != NULL) + { + matchp next; + int end_len; + + match tmp; + + next = mp->next; + end_len = 1; +#if 0 + if(next != NULL) + { + end_len = next->len + (next->offset > 0); + } +#endif + *tmp = *mp; + for(tmp->len = mp->len; tmp->len >= end_len; --(tmp->len)) + { + float score; + float total_score; + + LOG(LOG_DUMP, ("mp[%d, %d], tmp[%d, %d]\n", + mp->offset, mp->len, + tmp->offset, tmp->len)); + + score = f(tmp, emd); + total_score = prev_score + score; + + snp = snp_arr[len - tmp->len]; + + LOG(LOG_DUMP, + ("[%05d] cmp [%05d, %05d score %.1f + %.1f] with %.1f", + len, tmp->offset, tmp->len, + prev_score, score, snp->total_score)); + + if ((total_score < 1000000.0) && + (snp->match->len == 0 || + total_score < snp->total_score || + (total_score == snp->total_score && + (tmp->offset == 0 || + (snp->match->len == tmp->len && + snp->match->offset > tmp->offset))))) + { + LOG(LOG_DUMP, (", replaced")); + snp->index = len - tmp->len; + *snp->match = *tmp; + snp->match_score = score; + snp->total_score = total_score; + snp->prev = snp_arr[len]; + } + LOG(LOG_DUMP, ("\n")); + } + LOG(LOG_DUMP, ("tmp->len %d, ctx->rle[%d] %d\n", + tmp->len, len - tmp->len, + ctx->rle[len - tmp->len])); + + mp = next; + } + + /* slow way to get to the next node for cur */ +/* commented out do/while loop below to prevent the following llvm error: + * error: comparison of array 'snp_arr[len]->match' equal to a null pointer + * is always false [-Werror,-Wtautological-pointer-compare] + */ +#if 0 /* RH */ + do + { +#endif /* RH */ + --len; + ++best_copy_len; + if (!(len & 0xFF)) + { + LOG(LOG_NORMAL, (".")); + } +#if 0 /* RH */ + } + while (snp_arr[len]->match == NULL); +#endif /* RH */ + } + LOG(LOG_NORMAL, ("\n")); + + return snp_arr[0]; +} + +void matchp_snp_get_enum(const_search_nodep snp, /* IN */ + matchp_snp_enum snpe) /* IN/OUT */ +{ + snpe->startp = snp; + snpe->currp = snp; +} + +const_matchp matchp_snp_enum_get_next(void *matchp_snp_enum) +{ + matchp_snp_enump snpe; + const_matchp val; + + snpe = matchp_snp_enum; + + val = NULL; + while (snpe->currp != NULL && val == NULL) + { + val = snpe->currp->match; + snpe->currp = snpe->currp->prev; + } + + if (snpe->currp == NULL) + { + snpe->currp = snpe->startp; + } + return val; +} + + +#if 0 /* RH */ +void matchp_snp_rle_get_enum(const_search_nodep snp, /* IN */ + matchp_snp_rle_enum snpe) /* IN/OUT */ +{ + snpe->startp = snp; + snpe->currp = snp; +} + +const_matchp matchp_snp_rle_enum_get_next(void *matchp_snp_enum) +{ + int c, p; + matchp val; + const_matchp mp; + matchp_snp_rle_enump snpe; + + val = NULL; + snpe = matchp_snp_enum; + if (snpe->currp == NULL) + { + snpe->currp = snpe->startp; + } else + { + val = snpe->m; + val->len = 1; + val->offset = 1; + mp = snpe->currp->match; + p = mp ? mp->offset == 0 : -2; + + while ((snpe->currp = snpe->currp->prev) != NULL) + { + mp = snpe->currp->match; + if (mp == NULL || mp->len == 0) + { + continue; + } + c = p; + p = mp->offset == 0; + if (p != c) + { + break; + } + val->len += 1; + } + } + LOG(LOG_DUMP, ("rle: len = %d\n", val ? val->len : -1)); + return val; +} +#endif /* RH */ diff --git a/PSID/libpsid64/exomizer/search.h b/PSID/libpsid64/exomizer/search.h new file mode 100644 index 00000000..88bd7ed8 --- /dev/null +++ b/PSID/libpsid64/exomizer/search.h @@ -0,0 +1,119 @@ +#ifndef ALREADY_INCLUDED_SEARCH_H +#define ALREADY_INCLUDED_SEARCH_H + +/* + * Copyright (c) 2002, 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "match.h" +#include "output.h" + +struct _search_node { + int index; + match match; + float match_score; + float total_score; + struct _search_node *prev; +}; + +typedef struct _search_node search_node[1]; +typedef struct _search_node *search_nodep; +typedef const struct _search_node *const_search_nodep; + +struct _encode_match_data { + output_ctxp out; + void *priv; +}; + +typedef struct _encode_match_data encode_match_data[1]; +typedef struct _encode_match_data *encode_match_datap; + +/* example of what may be used for priv data + * field in the encode_match_data struct */ +typedef +float encode_int_f(int val, void *priv, output_ctxp out); /* IN */ + +struct _encode_match_priv { + int lit_num; + int seq_num; + int rle_num; + float lit_bits; + float seq_bits; + float rle_bits; + + encode_int_f *offset_f; + encode_int_f *len_f; + void *offset_f_priv; + void *len_f_priv; + + output_ctxp out; +}; + +typedef struct _encode_match_priv encode_match_priv[1]; +typedef struct _encode_match_priv *encode_match_privp; +/* end of example */ + +typedef +float encode_match_f(const_matchp mp, encode_match_data emd); /* IN */ + +void search_node_dump(search_nodep snp); /* IN */ + +void search_node_free(search_nodep snp); /* IN/OUT */ + +search_nodep search_buffer(match_ctx ctx, /* IN */ + encode_match_f * f, /* IN */ + encode_match_data emd); /* IN */ + +struct _matchp_snp_enum { + const_search_nodep startp; + const_search_nodep currp; +}; + +typedef struct _matchp_snp_enum matchp_snp_enum[1]; +typedef struct _matchp_snp_enum *matchp_snp_enump; + +void matchp_snp_get_enum(const_search_nodep snp, /* IN */ + matchp_snp_enum snpe); /* IN/OUT */ + +const_matchp matchp_snp_enum_get_next(void *matchp_snp_enum); + + +struct _matchp_snp_rle_enum { + const_search_nodep startp; + const_search_nodep currp; + const_search_nodep lastp; + match m; +}; + +typedef struct _matchp_snp_rle_enum matchp_snp_rle_enum[1]; +typedef struct _matchp_snp_rle_enum *matchp_snp_rle_enump; + +void matchp_snp_rle_get_enum(const_search_nodep snp, /* IN */ + matchp_snp_rle_enum snpe); /* IN/OUT */ + +const_matchp matchp_snp_rle_enum_get_next(void *matchp_snp_enum); + +#endif diff --git a/PSID/libpsid64/exomizer/sfx.h b/PSID/libpsid64/exomizer/sfx.h new file mode 100644 index 00000000..9ca3ea8e --- /dev/null +++ b/PSID/libpsid64/exomizer/sfx.h @@ -0,0 +1,49 @@ +#ifndef ALREADY_INCLUDED_SFX_H +#define ALREADY_INCLUDED_SFX_H + +/* + * Copyright (c) 2002, 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include "output.h" +typedef +void sfx1_set_new_load_f(output_ctx out, /* IN/OUT */ + unsigned short int load); /* IN */ + +typedef +void sfx2_add_stages_f(output_ctx out, /* IN/OUT */ + unsigned short int start); /* IN */ +struct sfx_decruncher { + sfx1_set_new_load_f *load; + sfx2_add_stages_f *stages; + const char *text; +}; +extern struct sfx_decruncher sfx_c64[]; +extern struct sfx_decruncher sfx_c64ne[]; +extern struct sfx_decruncher sfx_c264[]; +extern struct sfx_decruncher sfx_c264ne[]; + +#endif diff --git a/PSID/libpsid64/exomizer/sfx64ne.c b/PSID/libpsid64/exomizer/sfx64ne.c new file mode 100644 index 00000000..1e027c5c --- /dev/null +++ b/PSID/libpsid64/exomizer/sfx64ne.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2002, 2003 Magnus Lind. + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software, alter it and re- + * distribute it freely for any non-commercial, non-profit purpose subject to + * the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in a + * product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any distribution. + * + * 4. The names of this software and/or it's copyright holders may not be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + */ + +#include +#include "sfx.h" +#include "log.h" +#include "output.h" + +#define DECOMP_MIN_ADDR 0x03d0 +#define DECOMP_LEN 0xb3 + +static const unsigned char stage1[] = { + 0x01, 0x08, 0x0B, 0x08, 0xD3, 0x07, 0x9E, 0x32, + 0x30, 0x35, 0x39, 0x00, 0xA0, 0x00, 0x78, 0xE6, + 0x01, 0xBA, 0xBD, 0x00, 0x00, 0x9D, 0xFC, 0x00, + 0xCA, 0xD0, 0xF7, 0x4C, 0x00, 0x00 +}; +#define STAGE1_COPY_SRC 19 +#define STAGE1_JMP_STAGE2 28 + +#define STAGE1_BEGIN 0x07ff +#define STAGE1_END (STAGE1_BEGIN + sizeof(stage1)) + +static unsigned char stage2[] = { + 0xE8, 0xA9, 0x00, 0x85, 0xFC, 0x85, 0xFB, 0xE0, + 0x01, 0x90, 0x21, 0xA5, 0xFD, 0x4A, 0xD0, 0x11, + 0xAD, 0x1C, 0x01, 0xD0, 0x03, + 0xCE, 0x1d, 0x01, 0xCE, + 0x1C, 0x01, 0xAD, 0x1B, 0x08, 0x90, 0x1B, 0x6A, + 0x26, 0xFC, 0x26, 0xFB, 0xCA, 0xD0, 0xE5, 0x85, + 0xFD, 0xA5, 0xFC, 0x60, 0xC6, 0x01, 0x58, 0x4C, + 0x00, 0xC6, 0xCA, 0xC6, 0xFF, 0xC6, 0xAF, 0x88, + 0xB1, 0xAE, 0x91, 0xFE, 0x98, 0xD0, 0xF8, 0x8A, + 0xD0, 0xF0, 0x20, 0x00, 0x01, 0xF0, 0x0A, 0xA5, + 0xFE, 0xD0, 0x02, 0xC6, 0xFF, 0xC6, 0xFE, 0x90, + 0xBE, 0xC8, 0x20, 0x00, 0x01, 0xF0, 0xFA, 0xC0, + 0x11, 0xB0, 0xD1, 0xBE, 0x33, 0x03, 0x20, 0x01, + 0x01, 0x79, 0x67, 0x03, 0x85, 0xA7, 0xA5, 0xFB, + 0x79, 0x9B, 0x03, 0x48, 0xD0, 0x06, 0xA4, 0xA7, + 0xC0, 0x04, 0x90, 0x02, 0xA0, 0x03, 0xBE, 0xAC, + 0x01, 0x20, 0x01, 0x01, 0x79, 0xAF, 0x01, 0xA8, + 0x38, 0xA5, 0xFE, 0xE5, 0xA7, 0x85, 0xFE, 0xB0, + 0x02, 0xC6, 0xFF, 0xBE, 0x34, 0x03, 0x20, 0x01, + 0x01, 0x79, 0x68, 0x03, 0x90, 0x03, 0xE6, 0xFB, + 0x18, 0x65, 0xFE, 0x85, 0xAE, 0xA5, 0xFB, 0x79, + 0x9C, 0x03, 0x65, 0xFF, 0x85, 0xAF, 0xA4, 0xA7, + 0x68, 0xAA, 0x90, 0x90, 0x02, 0x04, 0x04, 0x30, + 0x20, 0x10, 0xE8, 0x98, 0x29, 0x0F, 0xF0, 0x13, + 0x8A, 0x4A, 0xA6, 0xFC, 0x2A, 0x26, 0xFB, 0xCA, + 0x10, 0xFA, 0x79, 0x67, 0x03, 0xAA, 0xA5, 0xFB, + 0x79, 0x9B, 0x03, 0x99, 0x9C, 0x03, 0x8A, 0x99, + 0x68, 0x03, 0xA2, 0x04, 0x20, 0x01, 0x01, 0x99, + 0x34, 0x03, 0xC8, 0xC0, 0x34, 0xD0, 0xD3, 0xA0, + 0x00, 0x4C, 0x43, 0x01 +}; +#define STAGE2_GET_BYTE 28 +#define STAGE2_START 49 +#define STAGE2_COPY_LEN_LO 225 + +static unsigned char stage3s[] = { + 0xB9, 0x00, 0x00, 0x99, 0x00, 0x00, 0x88, 0xD0, + 0xF7, 0x4C, 0x43, 0x01 +}; +#define STAGE3S_COPY_SRC 1 +#define STAGE3S_COPY_DEST 4 + +static unsigned char stage3l[] = { + 0xA2, 0x00, 0xB0, 0x0E, 0xCA, 0xCE, 0x1A, 0x09, + 0xCE, 0x1D, 0x09, 0x88, 0xB9, 0x00, 0x00, 0x99, + 0x00, 0x00, 0x98, 0xD0, 0xF6, 0x8A, 0xD0, 0xEC, + 0x4C, 0x43, 0x01 +}; +#define STAGE3L_COPY_LEN_HI 1 +#define STAGE3L_COPY_DEC_SRC_HI 6 +#define STAGE3L_COPY_DEC_DEST_HI 9 +#define STAGE3L_COPY_SRC 13 +#define STAGE3L_COPY_DEST 16 + +static unsigned int L_copy_len; +static +void load(output_ctx out, /* IN/OUT */ + unsigned short int load) /* IN */ +{ + unsigned short int new_load; + if (load < DECOMP_MIN_ADDR) + { + LOG(LOG_ERROR, + ("error: cant handle load address < $%04X\n", + DECOMP_MIN_ADDR)); + //exit(1); + return; + } + + output_ctx_set_start(out, STAGE1_BEGIN); + + /* set L_load to just after stage1 code */ + new_load = STAGE1_END; + + /* do we have enough decompression buffer safety? */ + if (load < STAGE1_END) + { + /* no, we need to transfer bytes */ + new_load = load; + } + output_set_pos(out, new_load); + + L_copy_len = STAGE1_END - new_load; + + LOG(LOG_DUMP, ("copy_len $%04X\n", L_copy_len)); + LOG(LOG_DUMP, ("new_load $%04X\n", new_load)); + +} + +static +void stages(output_ctx out, /* IN/OUT */ + unsigned short int start) /* IN */ +{ + unsigned int i; + int stage2_begin; + int stage3_begin = 0; + int stage3_end = 0; + int stages_end; + + stage2_begin = output_get_pos(out); + /*LOG(LOG_DUMP, ("stage2_begin $%04X\n", stage2_begin)); */ + + for (i = 0; i < sizeof(stage2); ++i) + { + output_byte(out, stage2[i]); + } + if (L_copy_len > 0) + { + /* add stage 3 */ + LOG(LOG_DUMP, + ("adding extra copy stage, copy_len %d\n", L_copy_len)); + + /* clobber the jmp last in stage 2 */ + + stage3_begin = output_get_pos(out) - 3; + /*LOG(LOG_DUMP, ("stage3_begin $%04X\n", stage3_begin)); */ + output_set_pos(out, stage3_begin); + + /* copy stage 3 */ + if (L_copy_len > 256) + { + for (i = 0; i < sizeof(stage3l); ++i) + { + output_byte(out, stage3l[i]); + } + } else + { + for (i = 0; i < sizeof(stage3s); ++i) + { + output_byte(out, stage3s[i]); + } + } + + stage3_end = output_get_pos(out); + + /* copy the actual bytes */ + output_copy_bytes(out, STAGE1_END - L_copy_len, L_copy_len); + } + + stages_end = output_get_pos(out); + /*LOG(LOG_DUMP, ("stages_end $%04X\n", stages_end)); */ + + /* add stage 1 */ + output_set_pos(out, STAGE1_BEGIN); + for (i = 0; i < sizeof(stage1); ++i) + { + output_byte(out, stage1[i]); + } + + /* fixup addresses */ + output_set_pos(out, STAGE1_BEGIN + STAGE1_COPY_SRC); + output_word(out, (unsigned short int) (stage2_begin - 4)); + + output_set_pos(out, STAGE1_BEGIN + STAGE1_JMP_STAGE2); + output_word(out, (unsigned short int) (stage2_begin + DECOMP_LEN)); + + output_set_pos(out, stage2_begin + STAGE2_GET_BYTE); + output_word(out, (unsigned short int) (stage2_begin - 3)); + + output_set_pos(out, stage2_begin + STAGE2_START); + output_word(out, (unsigned short int) start); + + if (L_copy_len > 256) + { + /* fixup additional stage 3 stuff */ + output_set_pos(out, stage2_begin + STAGE2_COPY_LEN_LO); + output_byte(out, (unsigned char) (L_copy_len & 0xff)); + + output_set_pos(out, stage3_begin + STAGE3L_COPY_LEN_HI); + output_byte(out, (unsigned char) ((L_copy_len >> 8) & 0xff)); + + output_set_pos(out, stage3_begin + STAGE3L_COPY_DEC_SRC_HI); + output_word(out, + (unsigned short int) (stage3_begin + STAGE3L_COPY_SRC + + 1)); + + output_set_pos(out, stage3_begin + STAGE3L_COPY_DEC_DEST_HI); + output_word(out, + (unsigned short int) (stage3_begin + + STAGE3L_COPY_DEST + 1)); + + output_set_pos(out, stage3_begin + STAGE3L_COPY_SRC); + output_word(out, + (unsigned short int) (stage3_end + + (L_copy_len & 0xff00))); + + output_set_pos(out, stage3_begin + STAGE3L_COPY_DEST); + output_word(out, + (unsigned short int) (STAGE1_END - + (L_copy_len & 0x00ff))); + + } else if (L_copy_len > 0) + { + int adjust = (L_copy_len != 256); + /* fixup additional stage 3 stuff */ + output_set_pos(out, stage2_begin + STAGE2_COPY_LEN_LO); + output_byte(out, (unsigned char) L_copy_len); + + output_set_pos(out, stage3_begin + STAGE3S_COPY_SRC); + output_word(out, (unsigned short int) (stage3_end - adjust)); + + output_set_pos(out, stage3_begin + STAGE3S_COPY_DEST); + output_word(out, + (unsigned short int) (STAGE1_END - L_copy_len - + adjust)); + + } + + /* set the pos behind everything */ + output_set_pos(out, stages_end); +} +struct sfx_decruncher sfx_c64ne[1] = {{&load, &stages, "c64 (no effect)"}}; diff --git a/PSID/libpsid64/psid64.cpp b/PSID/libpsid64/psid64.cpp new file mode 100644 index 00000000..1bcb9414 --- /dev/null +++ b/PSID/libpsid64/psid64.cpp @@ -0,0 +1,1789 @@ +/* + $Id$ + + psid64 - create a C64 executable from a PSID file + Copyright (C) 2001-2014 Roland Hermans + + this code has been modified for integration into the Sidekick64 software + (the modifications are mostly removing everything that does not compile with vanilla Circle, + I really feel sorry for having disfigured this code, please refer to the original repository + if you want to get the real and decent psid64 version) + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +////////////////////////////////////////////////////////////////////////////// +// I N C L U D E S +////////////////////////////////////////////////////////////////////////////// + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../psid64/psid64.h" + +#include +#include +#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include + +#include "reloc65.h" +#include "screen.h" +//#include "sidid.h" +//#include "theme.h" +//#include "stilview/stil.h" +#include "exomizer/exomizer.h" + +//using std::cerr; +//using std::dec; +//using std::endl; +//using std::hex; +//using std::ofstream; +//using std::ostream; +//using std::ostringstream; +//using std::replace; +//using std::setfill; +//using std::setw; +//using std::string; +//using std::uppercase; +//using std::vector; + +////////////////////////////////////////////////////////////////////////////// +// L O C A L D E F I N I T I O N S +////////////////////////////////////////////////////////////////////////////// +static char* emptyString = ""; + +#if defined(HAVE_IOS_OPENMODE) + typedef std::ios::openmode openmode; +#else + typedef int openmode; +#endif + +/** + * Structure to describe a memory block in the C64's memory map. + */ +struct block_t +{ + uint_least16_t load; /**< start address */ + uint_least16_t size; /**< size of the memory block in bytes */ + const uint_least8_t* data; /**< data to be stored */ + //string description; /**< a short description */ +}; + +static inline unsigned int min(unsigned int a, unsigned int b); +static bool block_cmp(const block_t& a, const block_t& b); +//static void setThemeGlobals(globals_t& globals, Psid64::Theme theme); + + +////////////////////////////////////////////////////////////////////////////// +// L O C A L F U N C T I O N S +////////////////////////////////////////////////////////////////////////////// + +static inline unsigned int +min(unsigned int a, unsigned int b) +{ + if (a <= b) + { + return a; + } + return b; +} + + +static bool +block_cmp(const block_t& a, const block_t& b) +{ + return a.load < b.load; +} + + +/*static void +setThemeGlobals(globals_t& globals, Psid64::Theme theme) +{ + DriverTheme *driverTheme; + switch (theme) + { + case Psid64::THEME_BLUE: + driverTheme = DriverTheme::createBlueTheme(); + break; + case Psid64::THEME_C1541_ULTIMATE: + driverTheme = DriverTheme::createC1541UltimateTheme(); + break; + case Psid64::THEME_COAL: + driverTheme = DriverTheme::createCoalTheme(); + break; + case Psid64::THEME_DUTCH: + driverTheme = DriverTheme::createDutchTheme(); + break; + case Psid64::THEME_KERNAL: + driverTheme = DriverTheme::createKernalTheme(); + break; + case Psid64::THEME_LIGHT: + driverTheme = DriverTheme::createLightTheme(); + break; + case Psid64::THEME_MONDRIAAN: + driverTheme = DriverTheme::createMondriaanTheme(); + break; + case Psid64::THEME_OCEAN: + driverTheme = DriverTheme::createOceanTheme(); + break; + case Psid64::THEME_PENCIL: + driverTheme = DriverTheme::createPencilTheme(); + break; + case Psid64::THEME_RAINBOW: + driverTheme = DriverTheme::createRainbowTheme(); + break; + case Psid64::THEME_DEFAULT: + default: + driverTheme = DriverTheme::createSidekickTheme(); + //driverTheme = DriverTheme::createDefaultTheme(); + break; + } + + globals["COL_BORDER"] = driverTheme->getBorderColor(); + globals["COL_BACKGROUND"] = driverTheme->getBackgroundColor(); + globals["COL_RASTER_TIME"] = driverTheme->getRasterTimeColor(); + globals["COL_TITLE"] = driverTheme->getTitleColor(); + const int *lineColors = driverTheme->getLineColors(); + for (int i = 0; i < NUM_LINE_COLORS; ++i) + { + ostringstream oss; + oss << "COL_LINE_" << i; + globals[oss.str()] = lineColors[i]; + } + globals["COL_PARAMETER"] = driverTheme->getFieldNameColor(); + globals["COL_COLON"] = driverTheme->getFieldColonColor(); + globals["COL_VALUE"] = driverTheme->getFieldValueColor(); + globals["COL_LEGEND"] = driverTheme->getLegendColor(); + globals["COL_BAR_FG"] = driverTheme->getProgressBarFillColor(); + globals["COL_BAR_BG"] = driverTheme->getProgressBarBackgroundColor(); + globals["COL_SCROLLER"] = driverTheme->getScrollerColor(); + const int *scrollerColors = driverTheme->getScrollerColors(); + for (int i = 0; i < NUM_SCROLLER_COLORS; ++i) + { + ostringstream oss; + oss << "COL_SCROLLER_" << i; + globals[oss.str()] = scrollerColors[i]; + } + const int *footerColors = driverTheme->getFooterColors(); + for (int i = 0; i < NUM_FOOTER_COLORS; ++i) + { + ostringstream oss; + oss << "COL_FOOTER_" << i; + globals[oss.str()] = footerColors[i]; + } + + delete driverTheme; +}*/ + + +////////////////////////////////////////////////////////////////////////////// +// P R I V A T E M E M B E R D A T A +////////////////////////////////////////////////////////////////////////////// + +const char* Psid64::txt_relocOverlapsImage = "PSID64: relocation information overlaps the load image"; +const char* Psid64::txt_notEnoughC64Memory = "PSID64: C64 memory has no space for driver code"; +const char* Psid64::txt_fileIoError = "PSID64: File I/O error"; +const char* Psid64::txt_noSidTuneLoaded = "PSID64: No SID tune loaded"; +const char* Psid64::txt_noSidTuneConverted = "PSID64: No SID tune converted"; +//const char* Psid64::txt_sidIdConfigError = "PSID64: Cannot read SID ID configuration file"; + + +////////////////////////////////////////////////////////////////////////////// +// P U B L I C M E M B E R F U N C T I O N S +////////////////////////////////////////////////////////////////////////////// + +// constructor + +Psid64::Psid64() : + m_noDriver(false), + m_blankScreen(false), + m_compress(false), + m_initialSong(0), + m_useGlobalComment(false), + m_verbose(false), + //m_hvscRoot(), +// m_databaseFileName(), + //m_sidIdConfigFileName(), + //m_theme(THEME_DEFAULT), + m_status(false), + m_statusString(NULL), + //m_fileName(), + m_tune(0), + m_tuneInfo(), +// m_database(), + //m_stil(new STIL), + //m_sidId(new SidId), + m_screen(new Screen), + //m_stilText(), + m_songlengthsData(), + m_songlengthsSize(0), + m_driverPage(0), + m_screenPage(0), + m_charPage(0), + m_stilPage(0), + m_songlengthsPage(0), + //m_playerId(), + m_programData(NULL), + m_programSize(0) +{ +} + +// destructor + +Psid64::~Psid64() +{ + //delete m_stil; + //delete m_sidId; + //delete m_screen; + //delete[] m_programData; +} + + +/*bool Psid64::setDatabaseFileName(string &databaseFileName) +{ + m_databaseFileName = databaseFileName; + if (!m_databaseFileName.empty()) + { + if (m_database.open(m_databaseFileName.c_str()) < 0) + { + m_statusString = m_database.error(); + return false; + } + } + + return true; +}*/ + + +/*bool Psid64::setSidIdConfigFileName(string &sidIdConfigFileName) +{ + m_sidIdConfigFileName = sidIdConfigFileName; + if (!m_sidIdConfigFileName.empty()) + { + if (!m_sidId->readConfigFile(m_sidIdConfigFileName)) + { + m_statusString = txt_sidIdConfigError; + return false; + } + } + + return true; +}*/ + + + +bool Psid64::load( unsigned char* sidData, int sidLength ) +{ + if ( !m_tune.load( sidData, sidLength ) ) + { + m_statusString = m_tune.getInfo().statusString; + return false; + } + + m_tune.getInfo( m_tuneInfo ); + + return true; +} + +/* +bool +Psid64::load(const char* fileName) +{ + if (!m_tune.load(fileName)) + { + //m_fileName.clear(); + m_statusString = m_tune.getInfo().statusString; + return false; + } + + m_tune.getInfo(m_tuneInfo); + + //m_fileName = fileName; + + return true; +} +*/ + +bool +Psid64::convert() +{ + static const uint_least8_t psid_boot_obj[] = { +#include "psidboot.h" + }; + static const uint_least8_t psid_extboot_obj[] = { +#include "psidextboot.h" + }; + uint_least8_t* psid_mem; + uint_least8_t* psid_driver; + int driver_size; + uint_least16_t size; + + // ensure valid sidtune object + if (!m_tune) + { + m_statusString = txt_noSidTuneLoaded; + return false; + } + + // handle special treatment of conversion without driver code + if (m_noDriver) + { + return convertNoDriver(); + } + + // handle special treatment of tunes programmed in BASIC + if (m_tuneInfo.compatibility == SIDTUNE_COMPATIBILITY_BASIC) { + return convertBASIC(); + } + + // retrieve STIL entry for this SID tune + if (!formatStilText()) + { + return false; + } + + // retrieve song length data for this SID tune + if (!getSongLengths()) + { + return false; + } + + // find space for driver and screen (optional) + findFreeSpace(); + if (m_driverPage == 0x00) + { + m_statusString = txt_notEnoughC64Memory; + return false; + } + + // use minimal driver if screen blanking is enabled + if (m_blankScreen) + { + m_screenPage = (uint_least8_t) 0x00; + m_charPage = (uint_least8_t) 0x00; + m_stilPage = (uint_least8_t) 0x00; + } + + // relocate and initialize the driver + initDriver (&psid_mem, &psid_driver, &driver_size); + + // copy SID data + uint_least8_t c64buf[65536]; + m_tune.placeSidTuneInC64mem(c64buf); + + // identify player routine + const uint_least8_t* p_start = c64buf + m_tuneInfo.loadAddr; + const uint_least8_t* p_end = p_start + m_tuneInfo.c64dataLen; + //vector buffer(p_start, p_end); + //m_playerId = m_sidId->identify(buffer); + + // fill the blocks structure + //vector blocks; + block_t blocks[ MAX_BLOCKS ]; + int nBlocks = 0; + //blocks.reserve(MAX_BLOCKS); + block_t driver_block; + driver_block.load = m_driverPage << 8; + driver_block.size = driver_size; + driver_block.data = psid_driver; + //driver_block.description = "Driver code"; + //blocks.push_back(driver_block); + blocks[ nBlocks++ ] = driver_block; + + block_t music_data_block; + music_data_block.load = m_tuneInfo.loadAddr; + music_data_block.size = m_tuneInfo.c64dataLen; + music_data_block.data = &(c64buf[m_tuneInfo.loadAddr]); + //music_data_block.description = "Music data"; + //blocks.push_back(music_data_block); + blocks[ nBlocks++ ] = music_data_block; + + if (m_screenPage != 0x00) + { + drawScreen(); + block_t screen_block; + screen_block.load = m_screenPage << 8; + screen_block.size = m_screen->getDataSize(); + screen_block.data = m_screen->getData(); + //screen_block.description = "Screen"; + //blocks.push_back(screen_block); + blocks[ nBlocks++ ] = screen_block; + } + + if (m_stilPage != 0x00) + { + block_t stil_text_block; + stil_text_block.load = m_stilPage << 8; + stil_text_block.size = 0; + stil_text_block.data = (uint_least8_t*)emptyString;//(uint_least8_t*) m_stilText.c_str(); + //stil_text_block.description = "STIL text"; + //blocks.push_back(stil_text_block); + blocks[ nBlocks++ ] = stil_text_block; + } + + if (m_songlengthsPage != 0x00) + { + block_t song_length_data_block; + song_length_data_block.load = m_songlengthsPage << 8; + song_length_data_block.size = m_songlengthsSize; + song_length_data_block.data = m_songlengthsData; + //song_length_data_block.description = "Song length data"; + //blocks.push_back(song_length_data_block); + blocks[ nBlocks++ ] = song_length_data_block; + } + + //std::sort(blocks.begin(), blocks.end(), block_cmp); + + // print memory map + if (m_verbose) + { + uint_least16_t charset = m_charPage << 8; + + //cerr << "C64 memory map:" << endl; + /*for (vector::const_iterator block_iter = blocks.begin(); + block_iter != blocks.end(); + ++block_iter)*/ + for ( int i = 0; i < nBlocks; i++ ) + { + block_t *block_iter = &blocks[ i ]; + + if ((charset != 0) && (block_iter->load > charset)) + { + /*cerr << " $" << toHexWord(charset) << "-$" + << toHexWord(charset + (256 * NUM_CHAR_PAGES)) + << " Character set" << endl;*/ + charset = 0; + } + /*cerr << " $" << toHexWord(block_iter->load) << "-$" + << toHexWord(block_iter->load + block_iter->size) << " " + << block_iter->description << endl;*/ + } + if (charset != 0) + { + /*cerr << " $" << toHexWord(charset) << "-$" + << toHexWord(charset + (256 * NUM_CHAR_PAGES)) + << " Character set" << endl;*/ + } + } + + // calculate total size of the blocks + size = 0; +/* for (vector::const_iterator block_iter = blocks.begin(); + block_iter != blocks.end(); + ++block_iter)*/ + for ( int i = 0; i < nBlocks; i++ ) + { + block_t *block_iter = &blocks[ i ]; + size = size + block_iter->size; + } + + // determine initial song number (passed in at boot time) + int initialSong; + if ((1 <= m_initialSong) && (m_initialSong <= m_tuneInfo.songs)) + { + initialSong = m_initialSong; + } + else + { + initialSong = m_tuneInfo.startSong; + } + + // little gimmick: BASIC line number will be set to the preferred SID model + int lineNumber; + switch (m_tuneInfo.sidModel) + { + case SIDTUNE_SIDMODEL_6581: + lineNumber = 6581; + break; + case SIDTUNE_SIDMODEL_8580: + lineNumber = 8580; + break; + default: + lineNumber = 1103; + break; + } + + // select boot code object + const uint_least8_t* boot_obj; + int boot_size; + if (m_screenPage == 0x00) + { + boot_obj = psid_boot_obj; + boot_size = sizeof(psid_boot_obj); + } + else + { + boot_obj = psid_extboot_obj; + boot_size = sizeof(psid_extboot_obj); + } + + // relocate boot code + uint_least8_t* boot_mem; + uint_least8_t* boot_reloc; + boot_mem = boot_reloc = new uint_least8_t[boot_size]; + if (boot_mem == NULL) + { + return false; + } + memcpy(boot_reloc, boot_obj, boot_size); + + GLOBALS_BLOCK globals; + + //setThemeGlobals(globals, m_theme); + globals.song = (initialSong - 1) & 0xff; + uint_least16_t jmpAddr = m_driverPage << 8; + // start address of driver + globals.player = jmpAddr; + // address of new stop vector for tunes that call $a7ae during init + globals.stopvec = jmpAddr+3; + const uint_least16_t load_addr = 0x0801; + int screen = m_screenPage << 8; + globals.screen = screen; + globals.barsprptr = ((screen + BAR_SPRITE_SCREEN_OFFSET) & 0x3fc0) >> 6; + globals.dd00 = ((((m_screenPage & 0xc0) >> 6) ^ 3) | 0x04); + uint_least8_t vsa; // video screen address + uint_least8_t cba; // character memory base address + vsa = (uint_least8_t) ((m_screenPage & 0x3c) << 2); + cba = (uint_least8_t) (m_charPage ? (m_charPage >> 2) & 0x0e : 0x06); + globals.d018 = vsa | cba; + + // the additional BASIC starter code is not needed when compressing file + uint_least16_t basic_size = (m_compress ? 0 : 12); + uint_least16_t boot_addr = load_addr + basic_size; + if (!reloc65 ((char **) &boot_reloc, &boot_size, boot_addr, &globals)) + { + //cerr << ": Relocation error." << endl; + return false; + } + + uint_least16_t file_size = basic_size + boot_size + size; + m_programSize = 2 + file_size; + delete[] m_programData; + m_programData = new uint_least8_t[m_programSize]; + uint_least8_t *dest = m_programData; + *(dest++) = (uint_least8_t) (load_addr & 0xff); + *(dest++) = (uint_least8_t) (load_addr >> 8); + if (basic_size > 0) + { + // pointer to next BASIC line + *(dest++) = (uint_least8_t) ((load_addr + 10) & 0xff); + *(dest++) = (uint_least8_t) ((load_addr + 10) >> 8); + // line number + *(dest++) = (uint_least8_t) (lineNumber & 0xff); + *(dest++) = (uint_least8_t) (lineNumber >> 8); + // SYS token + *(dest++) = (uint_least8_t) 0x9e; + // "2061" + *(dest++) = (uint_least8_t) 0x32; + *(dest++) = (uint_least8_t) 0x30; + *(dest++) = (uint_least8_t) 0x36; + *(dest++) = (uint_least8_t) 0x31; + // end of BASIC line + *(dest++) = (uint_least8_t) 0x00; + // pointer to next BASIC line + *(dest++) = (uint_least8_t) 0x00; + *(dest++) = (uint_least8_t) 0x00; + } + memcpy(dest, boot_reloc, boot_size); + + // free memory of relocated boot code + delete[] boot_mem; + + uint_least16_t addr = 5; // parameter offset in psidboot.a65 + uint_least16_t eof = load_addr + file_size; + if (m_screenPage != 0x00) + { + dest[addr++] = (uint_least8_t) (m_charPage); // page for character set, or 0 + } + dest[addr++] = (uint_least8_t) (eof & 0xff); // end of C64 file + dest[addr++] = (uint_least8_t) (eof >> 8); + dest[addr++] = (uint_least8_t) (0x10000 & 0xff); // end of high memory + dest[addr++] = (uint_least8_t) (0x10000 >> 8); + dest[addr++] = (uint_least8_t) ((size + 0xff) >> 8); // number of pages to copy + dest[addr++] = (uint_least8_t) ((0x10000 - size) & 0xff); // start of blocks after moving + dest[addr++] = (uint_least8_t) ((0x10000 - size) >> 8); + dest[addr++] = (uint_least8_t) (nBlocks - 1); // number of blocks - 1 + + // copy block data to psidboot.a65 parameters + for ( int i = 0; i < nBlocks; i++ ) + { + block_t *block_iter = &blocks[ i ]; +/* for (vector::const_iterator block_iter = blocks.begin(); + block_iter != blocks.end(); + ++block_iter) + {*/ + int block_index = i;//block_iter - blocks.begin(); + uint_least16_t offs = addr + nBlocks - 1 - block_index; + dest[offs] = (uint_least8_t) (block_iter->load & 0xff); + dest[offs + MAX_BLOCKS] = (uint_least8_t) (block_iter->load >> 8); + dest[offs + 2 * MAX_BLOCKS] = (uint_least8_t) (block_iter->size & 0xff); + dest[offs + 3 * MAX_BLOCKS] = (uint_least8_t) (block_iter->size >> 8); + } + // addr = addr + 4 * MAX_BLOCKS; + dest += boot_size; + + // copy blocks to c64 program file +/* for (vector::const_iterator block_iter = blocks.begin(); + block_iter != blocks.end(); + ++block_iter) + {*/ + for ( int i = 0; i < nBlocks; i++ ) + { + block_t *block_iter = &blocks[ i ]; + memcpy(dest, block_iter->data, block_iter->size); + dest += block_iter->size; + } + + // free memory of relocated driver + delete[] psid_mem; + + /*if ( m_programSize > 37 * 1024) + { + // Use Exomizer to compress the program data. The first two bytes + // of m_programData are skipped as these contain the load address. + uint_least8_t* compressedData = new uint_least8_t[0x10000]; + m_programSize = exomizer(m_programData + 2, m_programSize - 2, + load_addr, boot_addr, compressedData); + // set BASIC line number + compressedData[4] = (uint_least8_t) (lineNumber & 0xff); + compressedData[5] = (uint_least8_t) (lineNumber >> 8); + delete[] m_programData; + m_programData = compressedData; + }*/ + + return true; +} + + +/*bool +Psid64::save(const char* fileName) +{ + // Open binary output file stream. + openmode createAttr = std::ios::out; +#if defined(HAVE_IOS_BIN) + createAttr |= std::ios::bin; +#else + createAttr |= std::ios::binary; +#endif + + ofstream outfile(fileName, createAttr); + return write(outfile); +} + + +bool +Psid64::write(ostream& out) +{ + if (!m_programData) + { + m_statusString = txt_noSidTuneConverted; + return false; + } + + out.write((const char*) m_programData, m_programSize); + if (!out) + { + m_statusString = txt_fileIoError; + return false; + } + + return true; +}*/ + + +////////////////////////////////////////////////////////////////////////////// +// P R O T E C T E D M E M B E R F U N C T I O N S +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// P R I V A T E M E M B E R F U N C T I O N S +////////////////////////////////////////////////////////////////////////////// + +int_least32_t +Psid64::roundDiv(int_least32_t dividend, int_least32_t divisor) +{ + return (dividend + (divisor / 2)) / divisor; +} + + +bool +Psid64::convertNoDriver() +{ + const uint_least16_t load = m_tuneInfo.loadAddr; + const uint_least16_t end = load + m_tuneInfo.c64dataLen; + + // allocate space for C64 program + m_programSize = 2 + m_tuneInfo.c64dataLen; + delete[] m_programData; + m_programData = new uint_least8_t[m_programSize]; + + // first the load address + m_programData[0] = (uint_least8_t) (load & 0xff); + m_programData[1] = (uint_least8_t) (load >> 8); + + // then copy the music data + uint_least8_t c64buf[65536]; + m_tune.placeSidTuneInC64mem(c64buf); + memcpy(m_programData + 2, &(c64buf[load]), m_tuneInfo.c64dataLen); + + // print memory map + if (m_verbose) + { + /*cerr << "C64 memory map:" << endl; + cerr << " $" << toHexWord(load) << "-$" << toHexWord(end) + << " Music data" << endl;*/ + } + + return true; +} + + +bool +Psid64::convertBASIC() +{ + const uint_least16_t load = m_tuneInfo.loadAddr; + const uint_least16_t end = load + m_tuneInfo.c64dataLen; + uint_least16_t bootCodeSize = m_compress ? 27 : 0; + + // allocate space for BASIC program and boot code (optional) + m_programSize = 2 + m_tuneInfo.c64dataLen + bootCodeSize; + delete[] m_programData; + m_programData = new uint_least8_t[m_programSize]; + + // first the load address + m_programData[0] = (uint_least8_t) (load & 0xff); + m_programData[1] = (uint_least8_t) (load >> 8); + + // then copy the BASIC program + uint_least8_t c64buf[65536]; + m_tune.placeSidTuneInC64mem(c64buf); + memcpy(m_programData + 2, &(c64buf[load]), m_tuneInfo.c64dataLen); + + /*if (m_compress) + { + uint_least16_t offs = 2 + m_tuneInfo.c64dataLen; + // lda #0 + m_programData[offs++] = 0xa9; + m_programData[offs++] = 0x00; + // sta load-1 + m_programData[offs++] = 0x8d; + m_programData[offs++] = (uint_least8_t) ((load - 1) & 0xff); + m_programData[offs++] = (uint_least8_t) ((load - 1) >> 8); + // lda #load + m_programData[offs++] = 0xa9; + m_programData[offs++] = (uint_least8_t) (load >> 8); + // sta $2c + m_programData[offs++] = 0x85; + m_programData[offs++] = 0x2c; + // lda #end + m_programData[offs++] = 0xa9; + m_programData[offs++] = (uint_least8_t) (end >> 8); + // sta $2e + m_programData[offs++] = 0x85; + m_programData[offs++] = 0x2e; + // jsr $a659 + m_programData[offs++] = 0x20; + m_programData[offs++] = 0x59; + m_programData[offs++] = 0xa6; + // jmp $a7ae + m_programData[offs++] = 0x4c; + m_programData[offs++] = 0xae; + m_programData[offs++] = 0xa7; + + // Use Exomizer to compress the program data. The first two bytes + // of m_programData are skipped as these contain the load address. + int start = end; + uint_least8_t* compressedData = new uint_least8_t[0x10000]; + m_programSize = exomizer(m_programData + 2, m_programSize - 2, load, start, compressedData); + delete[] m_programData; + m_programData = compressedData; + }*/ + + // print memory map + if (m_verbose) + { + /*cerr << "C64 memory map:" << endl; + cerr << " $" << toHexWord(load) << "-$" << toHexWord(end) + << " BASIC program" << endl;*/ + if (m_compress) + { + /*cerr << " $" << toHexWord(end) << "-$" + << toHexWord(end + bootCodeSize) + << " Post decompression boot code" << endl;*/ + } + } + + return true; +} + + +bool +Psid64::formatStilText() +{ +/* m_stilText.clear(); + + if (m_hvscRoot.empty()) + { + return true; + } + + // strip hvsc path from the file name + string hvscFileName = m_fileName; + size_t index = hvscFileName.find(m_hvscRoot); + if (index != string::npos) + { + hvscFileName.erase(0, index + m_hvscRoot.length()); + } + + // convert backslashes to slashes (for DOS and Windows filenames) + replace(hvscFileName.begin(), hvscFileName.end(), '\\', '/'); + + string str; + if (!m_stil->hasCriticalError() && m_useGlobalComment) + { + char* globalComment = m_stil->getGlobalComment(hvscFileName.c_str()); + if (globalComment != NULL) + { + str += globalComment; + } + } + if (!m_stil->hasCriticalError()) + { + char* stilEntry = m_stil->getEntry(hvscFileName.c_str(), 0); + if (stilEntry != NULL) + { + str += stilEntry; + } + } + if (!m_stil->hasCriticalError()) + { + char* bugEntry = m_stil->getBug(hvscFileName.c_str(), 0); + if (bugEntry != NULL) + { + str += bugEntry; + } + } + if (m_stil->hasCriticalError()) + { + m_statusString = m_stil->getErrorStr(); + return false; + } + + // convert the stil text and remove all double whitespace characters + unsigned int n = str.length(); + m_stilText.reserve(n); + + // start the scroll text with some space characters (to separate end + // from beginning and to make sure the color effect has reached the end + // of the line before the first character is visible) + for (unsigned int i = 0; i < (STIL_EOT_SPACES-1); ++i) + { + m_stilText += Screen::iso2scr(' '); + } + + bool space = true; + bool realText = false; + for (unsigned int i = 0; i < n; ++i) + { + if (isspace(str[i])) + { + space = true; + } + else + { + if (space) { + m_stilText += Screen::iso2scr(' '); + space = false; + } + m_stilText += Screen::iso2scr(str[i]); + realText = true; + } + } + + // check if the message contained at least one graphical character + if (realText) + { + // end-of-text marker + m_stilText += 0xff; + } + else + { + // no STIL text at all + m_stilText.clear(); + } +*/ + return true; +} + + +bool +Psid64::getSongLengths() +{ + bool have_songlengths = false; + for (int i = 0; i < m_tuneInfo.songs; ++i) + { + // retrieve song length database information + m_tune.selectSong(i + 1); + + int_least32_t length = 0; + + #if 0 + int_least32_t length = m_database.length (m_tune); + if (length > 0) + { + // maximum representable length is 99:59 + if (length > 5999) + { + length = 5999; + } + + // store song length as binary-coded decimal minutes and seconds + uint_least8_t min = length / 60; + uint_least8_t sec = length % 60; + m_songlengthsData[i] = ((min / 10) << 4) | (min % 10); + m_songlengthsData[i + m_tuneInfo.songs] = ((sec / 10) << 4) | (sec % 10); +#if 0 + if (m_verbose) + { + cerr << "Length of song " << i + 1 << ": " + << setfill('0') << setw(2) << static_cast(min) << ":" + << setfill('0') << setw(2) << static_cast(sec) << endl; + } +#endif + + // As floating point divisions are quite expensive on a 6502 instead + // a counter is used in the driver code to determine when to update + // the progress bar. The maximum error introduced by this method is: + // bar_pixels / ticks_per_sec / 2, which is 0.253333s for a 19 chars + // wide progress bar and 5 (NTSC) or 6 (PAL) ticks per frame. + const int_least32_t bar_pixels = BAR_WIDTH * 8; + const int_least32_t ticks_per_sec = 300; // PAL: 50 Hz * 6, NTSC: 60 Hz * 5 + int_least32_t bartpi = roundDiv(length * ticks_per_sec, bar_pixels); + m_songlengthsData[i + (2 * m_tuneInfo.songs)] = bartpi & 0xff; + m_songlengthsData[i + (3 * m_tuneInfo.songs)] = (bartpi >> 8) & 0xff; + have_songlengths = true; + } + else + #endif + { + // no song length data for this song + m_songlengthsData[i] = 0x00; + m_songlengthsData[i + m_tuneInfo.songs] = 0x00; + m_songlengthsData[i + (2 * m_tuneInfo.songs)] = 0x00; + m_songlengthsData[i + (3 * m_tuneInfo.songs)] = 0x00; + } + } + + // Only need a block with song length data if at least one song has song + // length data. + if (have_songlengths) + { + m_songlengthsSize = 4 * m_tuneInfo.songs; + } + else + { + m_songlengthsSize = 0; + } + + return true; +} + + +uint_least8_t +Psid64::findSonglengthsSpace(bool* pages, uint_least8_t scr, + uint_least8_t chars, uint_least8_t driver, + uint_least8_t stil, uint_least8_t stil_pages, + uint_least8_t size) const +{ + uint_least8_t firstPage = 0; + for (unsigned int i = 0; i < MAX_PAGES; ++i) + { + if (pages[i] || (scr && (scr <= i) && (i < (scr + NUM_SCREEN_PAGES))) + || (chars && (chars <= i) && (i < (chars + NUM_CHAR_PAGES))) + || ((driver <= i) && (i < (driver + NUM_EXTDRV_PAGES))) + || (stil && (stil <= i) && (i < (stil + stil_pages)))) + { + if ((i - firstPage) >= size) + { + return firstPage; + } + firstPage = i + 1; + } + } + + return 0; +} + + + +uint_least8_t +Psid64::findStilSpace(bool* pages, uint_least8_t scr, + uint_least8_t chars, uint_least8_t driver, + uint_least8_t size) const +{ + uint_least8_t firstPage = 0; + for (unsigned int i = 0; i < MAX_PAGES; ++i) + { + if (pages[i] || (scr && (scr <= i) && (i < (scr + NUM_SCREEN_PAGES))) + || (chars && (chars <= i) && (i < (chars + NUM_CHAR_PAGES))) + || ((driver <= i) && (i < (driver + NUM_EXTDRV_PAGES)))) + { + if ((i - firstPage) >= size) + { + return firstPage; + } + firstPage = i + 1; + } + } + + return 0; +} + + +uint_least8_t +Psid64::findDriverSpace(bool* pages, uint_least8_t scr, + uint_least8_t chars, uint_least8_t size) const +{ + uint_least8_t firstPage = 0; + for (unsigned int i = 0; i < MAX_PAGES; ++i) + { + if (pages[i] || (scr && (scr <= i) && (i < (scr + NUM_SCREEN_PAGES))) + || (chars && (chars <= i) && (i < (chars + NUM_CHAR_PAGES)))) + { + if ((i - firstPage) >= size) + { + return firstPage; + } + firstPage = i + 1; + } + } + + return 0; +} + +static bool hasCustomCharset = false; + +void +Psid64::findFreeSpace() +/*--------------------------------------------------------------------------* + In : - + Out : m_driverPage startpage of driver, 0 means no driver + m_screenPage startpage of screen, 0 means no screen + m_charPage startpage of chars, 0 means no chars + m_stilPage startpage of stil, 0 means no stil + m_songlengthsPage startpage of song lengths, 0 means N/A + Return value: - + Description : Find free space in the C64 memory map for the screen and the + driver code. Of course the driver code takes priority over + the screen. + Globals : psid PSID header and data + History : 15-AUG-2001 RH Initial version + 21-SEP-2001 RH Added support for screens located in the + memory ranges $4000-$8000 and $c000-$d000. + *--------------------------------------------------------------------------*/ +{ + bool pages[MAX_PAGES]; + unsigned int startp = m_tuneInfo.relocStartPage; + unsigned int maxp = m_tuneInfo.relocPages; + unsigned int i; + unsigned int j; + unsigned int k; + uint_least8_t scr; + uint_least8_t chars; + uint_least8_t driver; + + // calculate size of the STIL text in pages + uint_least8_t stilSize = 0;//(m_stilText.length() + 255) >> 8; + uint_least8_t songlengthsSize = (m_songlengthsSize + 255) >> 8; + + hasCustomCharset = true; +restartBuild: + stilSize = 0;//(m_stilText.length() + 255) >> 8; + songlengthsSize = (m_songlengthsSize + 255) >> 8; + startp = m_tuneInfo.relocStartPage; + maxp = m_tuneInfo.relocPages; + + m_screenPage = (uint_least8_t) (0x00); + m_driverPage = (uint_least8_t) (0x00); + m_charPage = (uint_least8_t) (0x00); + m_stilPage = (uint_least8_t) (0x00); + m_songlengthsPage = (uint_least8_t) (0x00); + + if (startp == 0x00) + { + // Used memory ranges. + unsigned int used[] = { + 0x00, 0x03, + 0xa0, 0xbf, + 0xd0, 0xff, + 0x00, 0x00 // calculated below + }; + + // Finish initialization by setting start and end pages. + used[6] = m_tuneInfo.loadAddr >> 8; + used[7] = (m_tuneInfo.loadAddr + m_tuneInfo.c64dataLen - 1) >> 8; + + // Mark used pages in table. + for (i = 0; i < MAX_PAGES; ++i) + { + pages[i] = false; + } + for (i = 0; i < sizeof(used) / sizeof(*used); i += 2) + { + for (j = used[i]; j <= used[i + 1]; ++j) + { + pages[j] = true; + } + } + } + else if ((startp != 0xff) && (maxp != 0)) + { + // the available pages have been specified in the PSID file + unsigned int endp = min((startp + maxp), MAX_PAGES); + + // check that the relocation information does not use the following + // memory areas: 0x0000-0x03FF, 0xA000-0xBFFF and 0xD000-0xFFFF + if ((startp < 0x04) + || ((0xa0 <= startp) && (startp <= 0xbf)) + || (startp >= 0xd0) + || ((endp - 1) < 0x04) + || ((0xa0 <= (endp - 1)) && ((endp - 1) <= 0xbf)) + || ((endp - 1) >= 0xd0)) + { + return; + } + + for (i = 0; i < MAX_PAGES; ++i) + { + pages[i] = ((startp <= i) && (i < endp)) ? false : true; + } + } + else + { + // not a single page is available + return; + } + + driver = 0; + for (i = 0; i < 4; ++i) + { + // Calculate the VIC bank offset. Screens located inside banks 1 and 3 + // require a copy the character rom in ram. The code below uses a + // little trick to swap bank 1 and 2 so that bank 0 and 2 are checked + // before 1 and 3. + uint_least8_t bank = (((i & 1) ^ (i >> 1)) ? i ^ 3 : i) << 6; + + for (j = 0; j < 0x40; j += 4) + { + // screen may not reside within the char rom mirror areas + if (!(bank & 0x40) && (0x10 <= j) && (j < 0x20)) + continue; + + // check if screen area is available + scr = bank + j; + if (pages[scr] || pages[scr + 1] || pages[scr + 2] + || pages[scr + 3]) + continue; + + // hack: try space for a custom charset! + if ( hasCustomCharset || bank & 0x40 ) + { + // The char rom needs to be copied to RAM so let's try to find + // a suitable location. + for ( k = 0; k < 0x40; k += 8 ) + { + // char rom area may not overlap with screen area + if ( k == ( j & 0x38 ) ) + continue; + + // check if character rom area is available + chars = bank + k; + if ( pages[ chars ] || pages[ chars + 1 ] + || pages[ chars + 2 ] || pages[ chars + 3 ] ) + continue; + + driver = + findDriverSpace( pages, scr, chars, NUM_EXTDRV_PAGES ); + if ( driver ) + { + m_driverPage = driver; + m_screenPage = scr; + m_charPage = chars; + if ( stilSize ) + { + m_stilPage = findStilSpace( pages, scr, chars, + driver, stilSize ); + } + if ( songlengthsSize ) + { + m_songlengthsPage = findSonglengthsSpace( + pages, scr, chars, driver, m_stilPage, + stilSize, songlengthsSize ); + } + return; + } + } + } + else + { + driver = findDriverSpace(pages, scr, 0, NUM_EXTDRV_PAGES); + if (driver) + { + m_driverPage = driver; + m_screenPage = scr; + if (stilSize) + { + m_stilPage = findStilSpace(pages, scr, 0, driver, + stilSize); + } + if (songlengthsSize) + { + m_songlengthsPage = findSonglengthsSpace( + pages, scr, 0, driver, m_stilPage, stilSize, + songlengthsSize); + } + return; + } + } + } + } + + if ( !driver && hasCustomCharset ) + { + hasCustomCharset = false; + goto restartBuild; + } + + if (!driver) + { + driver = findDriverSpace(pages, 0, 0, NUM_MINDRV_PAGES); + m_driverPage = driver; + } +} + + +//------------------------------------------------------------------------- +// Temporary hack till real bank switching code added + +// Input: A 16-bit effective address +// Output: A default bank-select value for $01. +uint8_t Psid64::iomap(uint_least16_t addr) +{ + // Force Real C64 Compatibility + if (m_tuneInfo.compatibility == SIDTUNE_COMPATIBILITY_R64) + return 0; // Special case, converted to 0x37 later + + if (addr == 0) + return 0; // Special case, converted to 0x37 later + if (addr < 0xa000) + return 0x37; // Basic-ROM, Kernal-ROM, I/O + if (addr < 0xd000) + return 0x36; // Kernal-ROM, I/O + if (addr >= 0xe000) + return 0x35; // I/O only + return 0x34; // RAM only +} + + +void +Psid64::initDriver(uint_least8_t** mem, uint_least8_t** ptr, int* n) +{ + static const uint_least8_t psid_driver[] = { +#include "psiddrv.h" + }; + static const uint_least8_t psid_extdriver[] = { +#include "psidextdrv.h" + }; + const uint_least8_t* driver; + uint_least8_t* psid_mem; + uint_least8_t* psid_reloc; + int psid_size; + uint_least16_t reloc_addr; + uint_least16_t addr; + + *ptr = NULL; + *n = 0; + + // select driver + if (m_screenPage == 0x00) + { + psid_size = sizeof(psid_driver); + driver = psid_driver; + } + else + { + psid_size = sizeof(psid_extdriver); + driver = psid_extdriver; + } + + // Relocation of C64 PSID driver code. + psid_mem = psid_reloc = new uint_least8_t[psid_size]; + if (psid_mem == NULL) + { + return; + } + memcpy(psid_reloc, driver, psid_size); + reloc_addr = m_driverPage << 8; + + // undefined references in the driver code need to be added to globals + GLOBALS_BLOCK globals; + //setThemeGlobals(globals, m_theme); + int screen = m_screenPage << 8; + globals.screen = screen; + int screen_songnum = 0; + if (m_tuneInfo.songs > 1) + { + screen_songnum = screen + (10*40) + 24; + if (m_tuneInfo.songs >= 100) ++screen_songnum; + if (m_tuneInfo.songs >= 10) ++screen_songnum; + } + globals.screen_songnum = screen_songnum; + int sid2base; + if (((m_tuneInfo.secondSIDAddress & 1) == 0) + && (((0x42 <= m_tuneInfo.secondSIDAddress) && (m_tuneInfo.secondSIDAddress <= 0x7e)) + || ((0xe0 <= m_tuneInfo.secondSIDAddress) && (m_tuneInfo.secondSIDAddress <= 0xfe)))) + { + sid2base = (m_tuneInfo.secondSIDAddress * 0x10) + 0xd000; + } + else + { + sid2base = 0xd400; + } + globals.sid2base = sid2base; + int sid3base; + if (((m_tuneInfo.thirdSIDAddress & 1) == 0) + && (((0x42 <= m_tuneInfo.thirdSIDAddress) && (m_tuneInfo.thirdSIDAddress <= 0x7e)) + || ((0xe0 <= m_tuneInfo.thirdSIDAddress) && (m_tuneInfo.thirdSIDAddress <= 0xfe))) + && (m_tuneInfo.thirdSIDAddress != m_tuneInfo.secondSIDAddress)) + { + sid3base = (m_tuneInfo.thirdSIDAddress * 0x10) + 0xd000; + } + else + { + sid3base = 0xd400; + } + globals.sid3base = sid3base; + globals.stil = m_stilPage * 0x100; + if (m_songlengthsPage != 0x00) + { + globals.songlengths_min = m_songlengthsPage * 0x100; + globals.songlengths_sec = (m_songlengthsPage * 0x100) + m_tuneInfo.songs; + globals.songtpi_lo = (m_songlengthsPage * 0x100) + (2 * m_tuneInfo.songs); + globals.songtpi_hi = (m_songlengthsPage * 0x100) + (3 * m_tuneInfo.songs); + } + else + { + globals.songlengths_min = 0x0000; + globals.songlengths_sec = 0x0000; + globals.songtpi_lo = 0x0000; + globals.songtpi_hi = 0x0000; + } + + if (!reloc65 ((char **) &psid_reloc, &psid_size, reloc_addr, &globals)) + { + //cerr << ": Relocation error." << endl; + return; + } + + // Skip JMP table + addr = 6; + + // Store parameters for PSID player. + psid_reloc[addr++] = (uint_least8_t) (m_tuneInfo.initAddr ? 0x4c : 0x60); + psid_reloc[addr++] = (uint_least8_t) (m_tuneInfo.initAddr & 0xff); + psid_reloc[addr++] = (uint_least8_t) (m_tuneInfo.initAddr >> 8); + psid_reloc[addr++] = (uint_least8_t) (m_tuneInfo.playAddr ? 0x4c : 0x60); + psid_reloc[addr++] = (uint_least8_t) (m_tuneInfo.playAddr & 0xff); + psid_reloc[addr++] = (uint_least8_t) (m_tuneInfo.playAddr >> 8); + psid_reloc[addr++] = (uint_least8_t) (m_tuneInfo.songs); + + // get the speed bits (the driver only has space for the first 32 songs) + uint_least32_t speed = 0; + unsigned int songs = min(m_tuneInfo.songs, 32); + for (unsigned int i = 0; i < songs; ++i) + { + if (m_tune[i + 1].songSpeed != SIDTUNE_SPEED_VBI) + { + speed |= (1 << i); + } + } + psid_reloc[addr++] = (uint_least8_t) (speed & 0xff); + psid_reloc[addr++] = (uint_least8_t) ((speed >> 8) & 0xff); + psid_reloc[addr++] = (uint_least8_t) ((speed >> 16) & 0xff); + psid_reloc[addr++] = (uint_least8_t) (speed >> 24); + + psid_reloc[addr++] = (uint_least8_t) ((m_tuneInfo.loadAddr < 0x31a) ? 0xff : 0x05); + psid_reloc[addr++] = iomap (m_tuneInfo.initAddr); + psid_reloc[addr++] = iomap (m_tuneInfo.playAddr); + + *mem = psid_mem; + *ptr = psid_reloc; + *n = psid_size; +} + + +/*void +Psid64::addFlag(bool &hasFlags, const string &flagName) +{ + if (hasFlags) + { + m_screen->write(", "); + } + else + { + hasFlags = true; + } + m_screen->write(flagName); +}*/ + +void +Psid64::addFlag(bool &hasFlags, const char *flagName) +{ + if (hasFlags) + { + m_screen->write(", "); + } + else + { + hasFlags = true; + } + m_screen->write(flagName); +} + +static char tmpStr[512]; + +char* +Psid64::toHexWord(uint16_t value) const +{ + sprintf( tmpStr, "%X", value ); + return tmpStr; +} + +char* +Psid64::toNumStr(int value) const +{ + sprintf( tmpStr, "%d", value ); + return tmpStr; +} + +/*string +Psid64::toHexWord(uint16_t value) const +{ + ostringstream oss; + + oss << hex << uppercase << setfill('0') << setw(4) << value; + + return string(oss.str()); +} + + +string +Psid64::toNumStr(int value) const +{ + ostringstream oss; + + oss << dec << value; + + return string(oss.str()); +}*/ + + +void +Psid64::drawScreen() +{ + m_screen->clear(); + + // set title + m_screen->move(2,1); +// m_screen->write("PSID64 v" VERSION " by Roland Hermans!"); + m_screen->write("PSID64 v1.2 by Roland Hermans"); + + // characters for color line effect +/* m_screen->poke( 4-3, 0, 0x70); + m_screen->poke(35-4, 0, 0x6e); + m_screen->poke( 4-3, 1, 0x5d); + m_screen->poke(35-4, 1, 0x5d); + m_screen->poke( 4-3, 2, 0x6d); + m_screen->poke(35-4, 2, 0x7d);*/ + for (unsigned int i = 0; i < 29; ++i) + { + m_screen->poke(5 + i-3, 0, 0x40); + m_screen->poke(5 + i-3, 2, 0x40); + } + + // information lines + m_screen->move(2, 4); + m_screen->write("Name : "); +// m_screen->write(string(m_tuneInfo.infoString[0]).substr(0,31)); + m_tuneInfo.infoString[0][32] = 0; + m_screen->write(m_tuneInfo.infoString[0]); + + m_screen->write("\n Author : "); + m_tuneInfo.infoString[1][32] = 0; + m_screen->write(m_tuneInfo.infoString[1]); + + m_screen->write("\n Release: "); + m_tuneInfo.infoString[2][32] = 0; + m_screen->write(m_tuneInfo.infoString[2]); + + m_screen->write("\n Load : $"); + m_screen->write(toHexWord(m_tuneInfo.loadAddr)); + m_screen->write("-$"); + m_screen->write(toHexWord(m_tuneInfo.loadAddr + m_tuneInfo.c64dataLen)); + + m_screen->write("\n Init : $"); + m_screen->write(toHexWord(m_tuneInfo.initAddr)); + + m_screen->write("\n Play : "); + if (m_tuneInfo.playAddr) + { + m_screen->write("$"); + m_screen->write(toHexWord(m_tuneInfo.playAddr)); + } + else + { + m_screen->write("N/A"); + } + + m_screen->write("\n Songs : "); + m_screen->write(toNumStr(m_tuneInfo.songs)); + if (m_tuneInfo.songs > 1) + { + m_screen->write(" (now playing"); + } + + bool hasFlags = false; + m_screen->write("\n Flags : "); + if (m_tuneInfo.compatibility == SIDTUNE_COMPATIBILITY_PSID) + { + addFlag(hasFlags, "PlaySID"); + } + switch (m_tuneInfo.clockSpeed) + { + case SIDTUNE_CLOCK_PAL: + addFlag(hasFlags, "PAL"); + break; + case SIDTUNE_CLOCK_NTSC: + addFlag(hasFlags, "NTSC"); + break; + case SIDTUNE_CLOCK_ANY: + addFlag(hasFlags, "PAL/NTSC"); + break; + default: + break; + } + switch (m_tuneInfo.sidModel) + { + case SIDTUNE_SIDMODEL_6581: + addFlag(hasFlags, "6581"); + break; + case SIDTUNE_SIDMODEL_8580: + addFlag(hasFlags, "8580"); + break; + case SIDTUNE_SIDMODEL_ANY: + addFlag(hasFlags, "6581/8580"); + break; + default: + break; + } + int sid2base; + if (((m_tuneInfo.secondSIDAddress & 1) == 0) + && (((0x42 <= m_tuneInfo.secondSIDAddress) && (m_tuneInfo.secondSIDAddress <= 0x7e)) + || ((0xe0 <= m_tuneInfo.secondSIDAddress) && (m_tuneInfo.secondSIDAddress <= 0xfe)))) + { + sid2base = (m_tuneInfo.secondSIDAddress * 0x10) + 0xd000; + } + else + { + sid2base = 0; + } + int sid3base; + if (((m_tuneInfo.thirdSIDAddress & 1) == 0) + && (((0x42 <= m_tuneInfo.thirdSIDAddress) && (m_tuneInfo.thirdSIDAddress <= 0x7e)) + || ((0xe0 <= m_tuneInfo.thirdSIDAddress) && (m_tuneInfo.thirdSIDAddress <= 0xfe))) + && (m_tuneInfo.thirdSIDAddress != m_tuneInfo.secondSIDAddress)) + { + sid3base = (m_tuneInfo.thirdSIDAddress * 0x10) + 0xd000; + } + else + { + sid3base = 0; + } + if (sid2base != 0) + { + // todo + char b[512]; +// ostringstream oss; + //oss << ((sid3base == 0) ? " at $" : "@") << toHexWord(sid2base); + switch (m_tuneInfo.sid2Model) + { + case SIDTUNE_SIDMODEL_6581: + sprintf( b, "6581%s%X",((sid3base == 0) ? " at $" : "@"), sid2base ); + addFlag(hasFlags, b ); + break; + case SIDTUNE_SIDMODEL_8580: + sprintf( b, "8580%s%X",((sid3base == 0) ? " at $" : "@"), sid2base ); + addFlag(hasFlags, b ); +//addFlag(hasFlags, "8580" + oss.str()); + break; + case SIDTUNE_SIDMODEL_ANY: + sprintf( b, "6581/8580%s%X",((sid3base == 0) ? " at $" : "@"), sid2base ); + addFlag(hasFlags, b ); + //addFlag(hasFlags, "6581/8580" + oss.str()); + break; + default: + sprintf( b, "SID%s%X",((sid3base == 0) ? " at $" : "@"), sid2base ); + addFlag(hasFlags, b ); +// addFlag(hasFlags, "SID" + oss.str()); + break; + } + } + if (sid3base != 0) + { + char b[64]; + // ostringstream oss; + //oss << "@" << toHexWord(sid3base); + switch (m_tuneInfo.sid3Model) + { + case SIDTUNE_SIDMODEL_6581: + sprintf( b, "6581@%X", sid3base ); + addFlag(hasFlags, b ); +// addFlag(hasFlags, "6581" + oss.str()); + break; + case SIDTUNE_SIDMODEL_8580: + sprintf( b, "8580@%X", sid3base ); + addFlag(hasFlags, b ); +// addFlag(hasFlags, "8580" + oss.str()); + break; + case SIDTUNE_SIDMODEL_ANY: + sprintf( b, "6581/8580@%X", sid3base ); + addFlag(hasFlags, b ); + //addFlag(hasFlags, "6581/8580" + oss.str()); + break; + default: + sprintf( b, "SID@%X", sid3base ); + addFlag(hasFlags, b ); +// addFlag(hasFlags, "SID" + oss.str()); + break; + } + } + if (!hasFlags) + { + m_screen->write("-"); + } + + //m_screen->write("\n"); +/* m_screen->write("\n Player : "); + string playerName = m_playerId; + if (playerName.empty()) + { + playerName = ""; + } + else + { + std::replace(playerName.begin(), playerName.end(), '_', ' '); + } + m_screen->write(playerName);*/ + + m_screen->write("\n Clock : : "); + if (m_songlengthsPage != 0) + { + uint_least8_t bar_char = (m_charPage == 0) ? 0xa0 : 0x7f; + for (unsigned int i = 0; i < BAR_WIDTH; ++i) + { + m_screen->poke(BAR_X + i, 13, bar_char); + } + m_screen->poke(BAR_X + BAR_WIDTH + 3, 13, 0x3a); + } + + // some additional text + m_screen->write("\n\n "); + if (m_tuneInfo.songs <= 1) + { + m_screen->write(" [1"); + } + else if (m_tuneInfo.songs <= 10) + { + m_screen->write(" [1-"); + m_screen->putchar((m_tuneInfo.songs % 10) + '0'); + } + else if (m_tuneInfo.songs <= 11) + { + m_screen->write(" [1-0, A"); + } + else + { + m_screen->write("[1-0, A-"); + m_screen->putchar(m_tuneInfo.songs <= 36 ? m_tuneInfo.songs - 11 + 'A' : 'Z'); + } + m_screen->write("] Select song [+] Next song\n"); + m_screen->write(" [-] Previous song [DEL] Blank screen\n"); + if (m_tuneInfo.playAddr) + { + m_screen->write(" [~] Fast forward [\x5e] raster time\n"); +/* for ( int i = 0; i < 40; i++ ) + { + char b[2]; + b[0] = i+80; b[1]=0; + m_screen->write(b); + } + m_screen->write("\n");*/ + } + m_screen->write(" [RUN/STOP] Stop [CBM] Go to Sidekick64\n"); + + // flashing bottom line (should be exactly 38 characters) + m_screen->move(1,24); + m_screen->write("Website: http://psid64.sourceforge.net"); + + if ( hasCustomCharset ) + { + int a = 91; + + for ( int j = 0; j < 4; j++ ) + for ( int i = 0; i < 7; i++ ) + { +// m_screen->poke( 16 + i + (j+20-5) * 40, (a++) ); + m_screen->poke( 33 + i + j * 40, (a++) ); + //c64screen[ i + 33 + j * 40 ] = (a++); + //c64color[ i + 33 + j * 40 ] = j < 2 ? skinValues.SKIN_MENU_TEXT_ITEM : skinValues.SKIN_MENU_TEXT_KEY; + } + } + + // initialize sprite for progress bar + for (unsigned int i = 0; i < 63; ++i) + { + m_screen->poke(BAR_SPRITE_SCREEN_OFFSET + i, 0x00); + } +} diff --git a/PSID/libpsid64/psidboot.a65 b/PSID/libpsid64/psidboot.a65 new file mode 100644 index 00000000..812ef6f4 --- /dev/null +++ b/PSID/libpsid64/psidboot.a65 @@ -0,0 +1,288 @@ +; psid64 - create a C64 executable from a PSID file +; Copyright (C) 2001-2014 Roland Hermans + +; modified for integration into the Sidekick64 software + +; +; This program 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. +; +; This program 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 this program; if not, write to the Free Software +; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + .fopt 0, "psidboot.o65", 0 + .fopt 3, "Roland Hermans ", 0 + +#define MAX_BLOCKS 5 + +#ifdef SCREEN +r1_org = $00f7 +#else /* SCREEN */ +r1_org = $00f8 +#endif /* SCREEN */ + + + lda #stopvec + sta $0329 + + ldy #r1_len +copyr1 lda r1_src-1,y + sta r1_org-1,y + dey + bne copyr1 + + jmp memmove + + +#ifdef SCREEN +vicdata .byte $00,$9a,$00,$00,$00,$00,$00,$00 + .byte $00,$00,$00,$00,$00,$00,$00,$00 + .byte $00,$6b,$37,$00,$00,$01,$08,$00 + .byte + +; modified for integration into the Sidekick64 software + +; +; This program 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. +; +; This program 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 this program; if not, write to the Free Software +; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +; +; +; The relocating PSID driver is based on a reference implementation written +; by Dag Lem, using Andre Fachat's relocating cross assembler, xa. The +; original driver code was introduced in VICE 1.7. +; +; Please note that this driver code is optimized to squeeze the minimal +; driver (without screen support) in just two memory pages. For this reason +; it contains some strange branches to gain a few bytes. Look out for side +; effects when updating this code! + + .fopt 0, "psiddrv.o65", 0 + .fopt 3, "Dag Lem ", 0 + + .align 256 + +#ifdef SCREEN +#define CLOCK_OFFSET (531-40) +#define BAR_X 15 +#define BAR_OFFSET (520 + BAR_X) +#define BAR_WIDTH 19 +#define BAR_PIXELS (BAR_WIDTH * 8) +#define LENGTH_OFFSET 555 +#define BAR_SPRITE_SCREEN_OFFSET $0300 +#define SCROLLER_OFFSET 840 +#endif + jmp cold + jmp setiomap + + ; Parameters +init rts + .word 0 +play rts + .word 0 +playmax .byte 0 +speed .word 0, 0 +irqvec .byte 0 +initiomap .byte 0 +playiomap .byte 0 + + ; Variables +playnum .byte 0 +video .byte 0 +random .byte 0 +#ifdef SCREEN +d011 .byte $1b +prevjoy .byte 0 +clkcounter .byte 0 +barcounter .word 0 ; progress bar increment counter +bartpi .word 0 ; progress bar ticks per increment +barpos .byte 0 ; progress bar position +#ifdef SMOOTH_SCROLL +d016 .byte $08 +#endif /* SMOOTH_SCROLL */ +#endif /* SCREEN */ + + ;Constants +#ifdef SCREEN +clkoffs .byte 0,1,3,4 +#endif /* SCREEN */ + + ; User interrupt vectors +irqusr .word irqret +brkusr .word brkjob +nmiusr .word nmijob + + ; IRQ handler +irqjob lda $d020 ; save values + pha + lda $dc00 + pha +#ifdef SCREEN + lda #$fd ; test for shift lock or shift left + sta $dc00 + lda $dc01 + bmi noshift + lda #screen_songnum+2 + beq nosongnum + ldx playnum + inx + txa + ldy #0 + ldx #$30 ; '0' + cmp #100 + bcc lt100 +ldiv100 sbc #100 + inx + cmp #100 + bcs ldiv100 + stx screen_songnum+2 ; here always y == 0 + iny + ldx #$30 + bne adiv10 ; bra +lt100 cmp #10 + bcc lt10 +ldiv10 sbc #10 + inx +adiv10 cmp #10 + bcs ldiv10 + pha + txa + sta screen_songnum+2,y + iny + pla +lt10 ora #$30 + sta screen_songnum+2,y + lda #$29 ; ')' +spfill sta screen_songnum+1+2,y + lda #$20 ; ' ' (used to erase old song number) + iny + cpy #3 + bcc spfill +nosongnum + + ldx #4 ; reset clock + lda #$30 ; '0' +rstclk ldy clkoffs-1,x + sta screen+CLOCK_OFFSET,y + dex + bne rstclk + stx clkcounter + stx barcounter ; reset playback progress bar + stx barcounter+1 + stx barpos + jsr drawbar + +#endif /* SCREEN */ + + ; Set interrupt vectors + ldx irqvec + bmi vicras +store03 lda irqusr,x + sta $0314,x + dex + bpl store03 + + ; Set VICII raster to line 0 +vicras +#ifdef SCREEN + lda d011 +#else /* !SCREEN */ + lda #$0b ; blank screen +#endif /* SCREEN */ + ldx #$00 + sta $d011 + stx $d012 + + ; Set CIA 1 Timer A to 60Hz + lda video + beq ntsc +pal lda #$25 + ldx #$40 + bne timer +ntsc lda #$95 + ldx #$42 +timer sta $dc04 + stx $dc05 + + ; Get song number + ldy playnum + cpy playmax + bcc songset + ldy #$00 +songset tya + pha + +#ifdef SCREEN + lda #>songlengths_min + beq nosonglen + lda songlengths_min,y ; minutes part of song length as BCD + ldx #0 + jsr wrsonglen + lda songlengths_sec,y ; seconds part of song length as BCD + inx + jsr wrsonglen + lda songtpi_lo,y ; ticks per increment of progress bar + sta bartpi ; to simplify pal/ntsc support there are + lda songtpi_hi,y ; always 300 ticks per second + sta bartpi+1 +nosonglen +#endif /* SCREEN */ + + + ; RSIDs use default environment of CIA1 only + lda initiomap + beq ciainit + + ; Get shift number for speed bit + cpy #32 + bcc shftset + ldy #31 + + ; Find speed for current song number +shftset lda #$00 + tax + sec +shift rol + bcc nxtshft + rol + inx +nxtshft dey + bpl shift + and speed,x + bne ciainit + + ; Enable VICII raster interrupt +vicinit lda #$81 + sta $d01a + bne doinit + + ; Enable CIA 1 timer A interrupt +ciainit lda #$81 + sta $dc0d + + ; always enable timer A for random numbers +doinit lda #$01 + sta $dc0e + + ; If play address, override default irq vector so + ; we reach our routine to handle play routine + lda playiomap + beq noplay + lda # $80 +l2 +#ifdef SMOOTH_SCROLL + ldx d016 ; smooth scroll + sec + sbc #$cb + cmp #$18 + bcc l3 + ldx #8 +l3 stx $d016 +#endif /* SMOOTH_SCROLL */ + lda $d012 + bmi l2 ; waits until raster < $80 or > $100 +#ifdef SMOOTH_SCROLL + lda #8 + sta $d016 +#endif /* SMOOTH_SCROLL */ + rts +#else /* !SCREEN */ + ldx #14 ; wait approx. 1 frame. accuracy is not +l1 dey ; important as we only have to deal with + bne l1 ; keyboard handling + dex + bne l1 + rts +#endif /* SCREEN */ + .) + +keyboard .( + ldx key + bmi nopress + lda keyrow,x ; check if the key is still pressed + sta $dc00 + lda $dc01 + and keycol,x + beq exit ; wait until key has been released +nopress lda playmax +#ifdef SCREEN + clc + adc #3 ; inst/del, + and - key +#endif /* SCREEN */ + cmp #numkeys + bcc maxnum + lda #numkeys-1 +maxnum tax +loop lda keyrow,x + sta $dc00 + lda $dc01 + and keycol,x + beq found + dex + bpl loop + stx key +exit rts +found stx key + txa + bne tglscr + jmp stop ; run/stop key pressed +tglscr +#ifdef SCREEN + dex + bne incsong + lda d011 ; inst/del key pressed + eor #$10 + sta d011 + sta $d011 + rts +incsong + dex + bne decsong + ldx playnum ; + key pressed + inx + cpx playmax + bcc xstart + ldx #0 + beq xstart ; bra +decsong + dex + bne newsong + ldx playnum ; - key pressed + bne maxsong + ldx playmax +maxsong dex + jmp xstart +newsong +#endif /* SCREEN */ + dex +xstart txa + jmp start ; start new song + +key .byte $ff + +keyrow .byte $7f ;run/stop + +#ifdef SCREEN + .byte $fe ;inst/del + .byte $df ;+ + .byte $df ;- +#endif /* SCREEN */ + + .byte $7f ;1 + .byte $7f ;2 + .byte $fd ;3 + .byte $fd ;4 + .byte $fb ;5 + .byte $fb ;6 + .byte $f7 ;7 + .byte $f7 ;8 + .byte $ef ;9 + .byte $ef ;0 + .byte $fd ;a + .byte $f7 ;b + .byte $fb ;c + .byte $fb ;d + .byte $fd ;e + .byte $fb ;f + .byte $f7 ;g + .byte $f7 ;h + .byte $ef ;i + .byte $ef ;j + .byte $ef ;k + .byte $df ;l + .byte $ef ;m + .byte $ef ;n + .byte $ef ;o + .byte $df ;p + .byte $7f ;q + .byte $fb ;r + .byte $fd ;s + .byte $fb ;t + .byte $f7 ;u + .byte $f7 ;v + .byte $fd ;w + .byte $fb ;x + .byte $f7 ;y + .byte $fd ;z +numkeys = * - keyrow + +keycol .byte $80 ;run/stop + +#ifdef SCREEN + .byte $01 ;inst/del + .byte $01 ;+ + .byte $08 ;- +#endif /* SCREEN */ + + .byte $01 ;1 + .byte $08 ;2 + .byte $01 ;3 + .byte $08 ;4 + .byte $01 ;5 + .byte $08 ;6 + .byte $01 ;7 + .byte $08 ;8 + .byte $01 ;9 + .byte $08 ;0 + .byte $04 ;a + .byte $10 ;b + .byte $10 ;c + .byte $04 ;d + .byte $40 ;e + .byte $20 ;f + .byte $04 ;g + .byte $20 ;h + .byte $02 ;i + .byte $04 ;j + .byte $20 ;k + .byte $04 ;l + .byte $10 ;m + .byte $80 ;n + .byte $40 ;o + .byte $02 ;p + .byte $40 ;q + .byte $02 ;r + .byte $20 ;s + .byte $40 ;t + .byte $40 ;u + .byte $80 ;v + .byte $02 ;w + .byte $80 ;x + .byte $02 ;y + .byte $10 ;z + .) + + +#ifdef SCREEN + +; A bit of CIA register $DC00 (Data Port A) is zero when the joystick connected +; to port two is moved in a certain direction or the fire button is pressed. By +; using the previous state of the joystick it is easy to determine the beginning +; of a new move. In the table below, A is the previous state of $DC00 and B is +; the current state of $DC00. +; +; A B (A xor B) and A +; - - --------------- +; 0 0 0 +; 0 1 0 +; 1 0 1 +; 1 1 0 + +joystick .( + lda #$ff + sta $dc00 + lda $dc00 + tax + eor prevjoy + and prevjoy ; set bits indicate start of new move + stx prevjoy + lsr + bcc tdown + ldx playnum ; joystick moved up + inx + cpx playmax + bcc xstart + ldx #0 + beq xstart ; bra +tdown + lsr + bcc tleft + ldx playnum ; joystick moved down + bne maxsong + ldx playmax +maxsong dex +xstart txa + jmp start ; start new song +tleft + lsr + bcc tright + jmp stop ; joystick moved left +tright + lsr + bcc exit + jmp restart ; joystick moved right +exit + rts + .) + + +clock .( ; update the clock + ldx clkcounter + bmi exit ; clock is not running + inx + txa + ldy video + cmp clkspd,y + bcc cl1 + ldx #4 +cl2 ldy clkoffs-1,x + lda screen+CLOCK_OFFSET,y + adc #0 ; c=1 + cmp clkcmp-1,x + bcc cl3 + lda #$30 ; '0' + .byte $2c ; bit $xxxxx +cl3 ldx #1 ; no wraparound, so break the loop + sta screen+CLOCK_OFFSET,y + dex + bne cl2 ; c=1 +cl1 stx clkcounter + + ldy video ; update playback progress bar + ldx barspd,y + ldy bartpi + tya + ora bartpi+1 + beq exit ; no songlength info for this song +bc3 inc barcounter + bne bc1 + inc barcounter+1 +bc1 cpy barcounter + bne bc2 + lda bartpi+1 ; c=1 + sbc barcounter+1 + bne bc2 + sta barcounter + sta barcounter+1 + lda barpos + cmp #BAR_PIXELS + bcs bc2 + inc barpos +bc2 dex + bne bc3 + jsr drawbar +exit + rts + +clkcmp .byte $3a,$3a,$36,$3a +clkspd .byte 60,50 ; ntsc/pal frames per second +barspd .byte 5,6 ; ntsc/pal ticks per frame + .) + + +wrsonglen .( ; write BCD coded song length part + pha + lsr + lsr + lsr + lsr + jsr wrdigit + pla + and #$0f +wrdigit ora #$30 + sta screen+LENGTH_OFFSET,x + inx + rts + .) + + +drawbar .( ; draw playback progress bar + lda barpos + tax + and #$f8 ; position sprite for color overlapping + clc + adc #(BAR_X*8)+24 + sta $d000 + lda #0 + rol + sta $d010 + txa + and #7 ; draw sprite + tay + lda dbspr,y + ldy #24 +db3 sta screen+BAR_SPRITE_SCREEN_OFFSET-3,y + dey + dey + dey + bne db3 + txa + lsr ; set progress bar character colors + lsr + lsr + tax + lda #stil + beq exit ; no stil text +#ifdef SMOOTH_SCROLL + ldx #38 +colscr1 lda $d800+SCROLLER_OFFSET,x + sta $d801+SCROLLER_OFFSET,x + dex + bpl colscr1 + dec d016 + dec d016 +#else /* SMOOTH_SCROLL */ + dec counter +#endif /* SMOOTH_SCROLL */ + bpl exit +#ifdef SMOOTH_SCROLL + lda #6 + sta d016 + inc counter + lda counter + and #7 + tax + lda scrcol,x + sta $d800+SCROLLER_OFFSET +#else /* SMOOTH_SCROLL */ + lda #5 + sta counter +#endif /* SMOOTH_SCROLL */ + + ldx #0 +scroll lda screen+SCROLLER_OFFSET+1,x + sta screen+SCROLLER_OFFSET,x + inx + cpx #39 + bcc scroll + +msgpos = *+1 +newchar ldx eot +eot = *+1 + cpx #$ff + bne okchar + inx ; restart scroll text + stx msgpos + sty msgpos+1 + beq newchar ; bra +okchar stx screen+SCROLLER_OFFSET+39 + inc msgpos + bne exit + inc msgpos+1 +exit rts + +counter .byte 0 + +#ifdef SMOOTH_SCROLL +scrcol .byte + Relocate and extract text segment from memory buffer instead of file. + For use with VICE VSID. +*/ + +#include +#include +#include +//#include +//#include +//#include + +#include "reloc65.h" + +extern int atoi( char* str ); + +#define BUF (9*2+8) /* 16 bit header */ + +struct file65 { + char *fname; + size_t fsize; + unsigned char *buf; + int tbase, tlen, dbase, dlen, bbase, blen, zbase, zlen; + int tdiff, ddiff, bdiff, zdiff; + int nundef; + char **ud; + unsigned char *segt; + unsigned char *segd; + unsigned char *utab; + unsigned char *rttab; + unsigned char *rdtab; + unsigned char *extab; + GLOBALS_BLOCK *globals; +}; + + +int read_options(unsigned char *f); +int read_undef(unsigned char *f, file65 *fp); +unsigned char *reloc_seg(unsigned char *f, int len, unsigned char *rtab, file65 *fp); +unsigned char *reloc_globals(unsigned char *, file65 *fp); + +file65 file; +unsigned char cmp[] = { 1, 0, 'o', '6', '5' }; + +int reloc65(char** buf, int* fsize, int addr, GLOBALS_BLOCK* globals) +{ + int mode, hlen; + + int tflag=0, dflag=0, bflag=0, zflag=0; + int tbase=0, dbase=0, bbase=0, zbase=0; + int extract = 0; + + file.globals = globals; + + file.buf = (unsigned char *) *buf; + file.fsize = *fsize; + tflag= 1; + tbase = addr; + extract = 1; + + if (memcmp(file.buf, cmp, 5) != 0) { + return 0; + } + + mode=file.buf[7]*256+file.buf[6]; + if(mode & 0x2000) { + return 0; + } else if(mode & 0x4000) { + return 0; + } + + hlen = BUF+read_options(file.buf+BUF); + + file.tbase = file.buf[ 9]*256+file.buf[ 8]; + file.tlen = file.buf[11]*256+file.buf[10]; + file.tdiff = tflag? tbase - file.tbase : 0; + file.dbase = file.buf[13]*256+file.buf[12]; + file.dlen = file.buf[15]*256+file.buf[14]; + file.ddiff = dflag? dbase - file.dbase : 0; + file.bbase = file.buf[17]*256+file.buf[16]; + file.blen = file.buf[19]*256+file.buf[18]; + file.bdiff = bflag? bbase - file.bbase : 0; + file.zbase = file.buf[21]*256+file.buf[20]; + file.zlen = file.buf[23]*256+file.buf[21]; + file.zdiff = zflag? zbase - file.zbase : 0; + + file.segt = file.buf + hlen; + file.segd = file.segt + file.tlen; + + file.utab = file.segd + file.dlen; + + file.rttab = file.utab + read_undef(file.utab, &file); + + file.rdtab = reloc_seg(file.segt, file.tlen, file.rttab, &file); + file.extab = reloc_seg(file.segd, file.dlen, file.rdtab, &file); + + reloc_globals(file.extab, &file); + + if(tflag) { + file.buf[ 9]= (tbase>>8)&255; + file.buf[ 8]= tbase & 255; + } + if(dflag) { + file.buf[13]= (dbase>>8)&255; + file.buf[12]= dbase & 255; + } + if(bflag) { + file.buf[17]= (bbase>>8)&255; + file.buf[16]= bbase & 255; + } + if(zflag) { + file.buf[21]= (zbase>>8)&255; + file.buf[20]= zbase & 255; + } + + /* free array with names of undefined labels */ + free(file.ud); + + switch(extract) { + case 0: /* whole file */ + return 1; + case 1: /* text segment */ + *buf = (char *) file.segt; + *fsize = file.tlen; + return 1; + case 2: + *buf = (char *) file.segd; + *fsize = file.dlen; + return 1; + default: + return 0; + } +} + + +int read_options(unsigned char *buf) { + int c, l=0; + + c=buf[0]; + while(c && c!=EOF) { + c&=255; + l+=c; + c=buf[l]; + } + return ++l; +} + +int read_undef(unsigned char *buf, file65 *fp) { + int i, n, l = 2; + + n = buf[0] + 256*buf[1]; + + fp->nundef = n; + fp->ud = (char **) calloc(n, sizeof(char *)); + +/*printf("number of undefined labels = %d\n", fp->nundef);*/ + i=0; + while(iud[i] = (char*) buf+l; +/*printf("undefined label %d = '%s'\n", i, fp->ud[i]);*/ + while(buf[l++]); + i++; + } + return l; +} + +static int find_global(unsigned char *bp, file65 *fp) { + char *name; + int nl = bp[0]+256*bp[1]; + + name = fp->ud[nl]; + + if ( strcmp( name, "song") == 0 ) return fp->globals->song; + if ( strcmp( name, "player") == 0 ) return fp->globals->player; + if ( strcmp( name, "stopvec") == 0 ) return fp->globals->stopvec; + if ( strcmp( name, "screen") == 0 ) return fp->globals->screen; + if ( strcmp( name, "barsprptr") == 0 ) return fp->globals->barsprptr; + if ( strcmp( name, "dd00") == 0 ) return fp->globals->dd00; + if ( strcmp( name, "d018") == 0 ) return fp->globals->d018; + if ( strcmp( name, "screen_songnum") == 0 ) return fp->globals->screen_songnum; + if ( strcmp( name, "sid2base") == 0 ) return fp->globals->sid2base; + if ( strcmp( name, "sid3base") == 0 ) return fp->globals->sid3base; + if ( strcmp( name, "stil") == 0 ) return fp->globals->stil; + if ( strcmp( name, "songlengths_min") == 0 ) return fp->globals->songlengths_min; + if ( strcmp( name, "songlengths_sec") == 0 ) return fp->globals->songlengths_sec; + if ( strcmp( name, "songtpi_lo") == 0 ) return fp->globals->songtpi_lo; + if ( strcmp( name, "songtpi_hi") == 0 ) return fp->globals->songtpi_hi; + + if ( strcmp( name, "COL_BORDER") == 0 ) return 0x0b; + if ( strcmp( name, "COL_BACKGROUND") == 0 ) return 0x00; + if ( strcmp( name, "COL_RASTER_TIME") == 0 ) return 0x0c; + if ( strcmp( name, "COL_TITLE") == 0 ) return 0x01; + if ( strcmp( name, "COL_PARAMETER") == 0 ) return 0x0d; + if ( strcmp( name, "COL_COLON") == 0 ) return 0x0d; + if ( strcmp( name, "COL_VALUE") == 0 ) return 0x03; + if ( strcmp( name, "COL_LEGEND") == 0 ) return 0x0c; + if ( strcmp( name, "COL_BAR_FG") == 0 ) return 0x06; + if ( strcmp( name, "COL_BAR_BG") == 0 ) return 0x03; + if ( strcmp( name, "COL_SCROLLER") == 0 ) return 0x01; + if ( strcmp( name, "COL_BORDER") == 0 ) return 0x0b; + + + const int lineColors[] = +// {0x09, 0x0b, 0x08, 0x0c, 0x0a, 0x0f, 0x07, 0x01, + // 0x0d, 0x07, 0x03, 0x0c, 0x0e, 0x04, 0x06}; + {0x0b, 0x05, 0x0d, 0x01, 0x0d, 0x05, 0x0b, 0x00, + 0x06, 0x0e, 0x03, 0x01, 0x03, 0x0e, 0x06}; + const int scrollerColors[] = + {0x05, 0x05, 0x05, 0x03, 0x0d, 0x01, 0x0d, 0x03}; + const int footerColors[] = + // {0x0b, 0x05, 0x0d, 0x01, 0x0d, 0x05, 0x0b, 0x00, + //0x06, 0x0e, 0x03, 0x01, 0x03, 0x0e, 0x06, 0x00 }; + {0x0b, 0x0b, 0x0e, 0x05, 0x03, 0x0d, 0x01, 0x01, 0x0d, 0x03, 0x05, 0x0e, 0x0b, 0x0b, 0x00, 0x00 }; + //{0x09, 0x02, 0x04, 0x0a, 0x07, 0x0d, 0x01, 0x0d, + //0x07, 0x0a, 0x04, 0x02, 0x09, 0x00, 0x00, 0x00}; + + if ( strstr( name, "COL_LINE_") ) + { + int i = atoi( &name[9] ); + return lineColors[ i ]; + } + + if ( strstr( name, "COL_SCROLLER_") ) + { + int i = atoi( &name[13] ); + return scrollerColors[ i ]; + } + + if ( strstr( name, "COL_FOOTER_") ) + { + int i = atoi( &name[11] ); + return footerColors[ i ]; + } + + /* + const int *lineColors = driverTheme->getLineColors(); + for (int i = 0; i < NUM_LINE_COLORS; ++i) + { + ostringstream oss; + oss << "COL_LINE_" << i; + globals[oss.str()] = lineColors[i]; + } + const int *scrollerColors = driverTheme->getScrollerColors(); + for (int i = 0; i < NUM_SCROLLER_COLORS; ++i) + { + ostringstream oss; + oss << "COL_SCROLLER_" << i; + globals[oss.str()] = scrollerColors[i]; + } + const int *footerColors = driverTheme->getFooterColors(); + for (int i = 0; i < NUM_FOOTER_COLORS; ++i) + { + ostringstream oss; + oss << "COL_FOOTER_" << i; + globals[oss.str()] = footerColors[i]; +*/ + + return 0; + exit(123); + +/* globals_t::iterator iter = fp->globals->find(name); + if (iter != fp->globals->end()) + { + return iter->second; + } + fprintf(stderr,"Warning: undefined label '%s'\n", name);*/ + return 0; +} + +#define reldiff(s) (((s)==2)?fp->tdiff:(((s)==3)?fp->ddiff:(((s)==4)?fp->bdiff:(((s)==5)?fp->zdiff:0)))) + +unsigned char *reloc_seg(unsigned char *buf, int len, unsigned char *rtab, file65 *fp) { + int adr = -1; + int type, seg, old, n_new; +/*printf("tdiff=%04x, ddiff=%04x, bdiff=%04x, zdiff=%04x\n", + fp->tdiff, fp->ddiff, fp->bdiff, fp->zdiff);*/ + while(*rtab) { + if((*rtab & 255) == 255) { + adr += 254; + rtab++; + } else { + adr += *rtab & 255; + rtab++; + type = *rtab & 0xe0; + seg = *rtab & 0x07; +/*printf("reloc entry @ rtab=%p (offset=%d), adr=%04x, type=%02x, seg=%d\n",rtab-1, *(rtab-1), adr, type, seg);*/ + rtab++; + switch(type) { + case 0x80: + // two byte address + old = buf[adr] + 256*buf[adr+1]; + if (seg) n_new = old + reldiff(seg); + else n_new = old + find_global(rtab, fp); + buf[adr] = n_new & 255; + buf[adr+1] = (n_new>>8)&255; + break; + case 0x40: + // high byte of an address + old = buf[adr]*256 + *rtab; + if (seg) n_new = old + reldiff(seg); + else n_new = old + find_global(rtab, fp); + buf[adr] = (n_new>>8)&255; +// FIXME: I don't understand the line below. Why should we write data do the +// relocation table? +// *rtab = n_new & 255; + rtab++; + break; + case 0x20: + // low byte of an address + old = buf[adr]; + if (seg) n_new = old + reldiff(seg); + else n_new = old + find_global(rtab, fp); + buf[adr] = n_new & 255; + break; + } + if(seg==0) rtab+=2; + } + } + if(adr > len) { +/* + fprintf(stderr,"reloc65: %s: Warning: relocation table entries past segment end!\n", + fp->fname); +*/ + } + return ++rtab; +} + +unsigned char *reloc_globals(unsigned char *buf, file65 *fp) { + int n, n_new; + + n = buf[0] + 256*buf[1]; + buf +=2; + + while(n) { +/*printf("relocating %s, ", buf);*/ + while(*(buf++)); + int seg = *buf; + int old = buf[1] + 256*buf[2]; + + if (seg) n_new = old + reldiff(seg); + else n_new = old + find_global(buf+1, fp); +/*printf("old=%04x, seg=%d, rel=%04x, n_new=%04x\n", old, seg, reldiff(seg), n_new);*/ + buf[1] = n_new & 255; + buf[2] = (n_new>>8) & 255; + buf +=3; + n--; + } + return buf; +} diff --git a/PSID/libpsid64/reloc65.h b/PSID/libpsid64/reloc65.h new file mode 100644 index 00000000..a994b7e1 --- /dev/null +++ b/PSID/libpsid64/reloc65.h @@ -0,0 +1,77 @@ +/* + $Id$ + + psid64 - PlaySID player for a real C64 environment + Copyright (C) 2001-2003 Roland Hermans + + this code has been modified for integration into the Sidekick64 software + (the modifications are mostly removing everything that does not compile with vanilla Circle, + I really feel sorry for having disfigured this code, please refer to the original repository + if you want to get the real and decent psid64 version) + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef RELOC65_H +#define RELOC65_H + +////////////////////////////////////////////////////////////////////////////// +// I N C L U D E S +////////////////////////////////////////////////////////////////////////////// + +//#include +//#include + + +////////////////////////////////////////////////////////////////////////////// +// F O R W A R D D E C L A R A T O R S +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// D A T A D E C L A R A T O R S +////////////////////////////////////////////////////////////////////////////// + +//typedef std::map globals_t; + +#define uint_least32_t unsigned int + +typedef struct +{ + uint_least32_t song; + uint_least32_t player; + uint_least32_t stopvec; + uint_least32_t screen; + uint_least32_t barsprptr; + uint_least32_t dd00; + uint_least32_t d018; + uint_least32_t screen_songnum; + uint_least32_t sid2base; + uint_least32_t sid3base; + uint_least32_t stil; + uint_least32_t songlengths_min; + uint_least32_t songlengths_sec; + uint_least32_t songtpi_lo; + uint_least32_t songtpi_hi; +}GLOBALS_BLOCK; + + +////////////////////////////////////////////////////////////////////////////// +// F U N C T I O N D E C L A R A T O R S +////////////////////////////////////////////////////////////////////////////// + +extern int reloc65 (char **buf, int *fsize, int addr, GLOBALS_BLOCK *globals); + +#endif // RELOC65_H diff --git a/PSID/libpsid64/screen.cpp b/PSID/libpsid64/screen.cpp new file mode 100644 index 00000000..67e0b197 --- /dev/null +++ b/PSID/libpsid64/screen.cpp @@ -0,0 +1,155 @@ +/* + $Id$ + + psid64 - create a C64 executable from a PSID file + Copyright (C) 2001-2003 Roland Hermans + + this code has been modified for integration into the Sidekick64 software + (the modifications are mostly removing everything that does not compile with vanilla Circle, + I really feel sorry for having disfigured this code, please refer to the original repository + if you want to get the real and decent psid64 version) + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +////////////////////////////////////////////////////////////////////////////// +// I N C L U D E S +////////////////////////////////////////////////////////////////////////////// + +/*#include +#include +#include +#include */ +#include +#include +#include + +#include "screen.h" + + +////////////////////////////////////////////////////////////////////////////// +// G L O B A L D A T A +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// L O C A L D E F I N I T I O N S +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// L O C A L D A T A +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// L O C A L F U N C T I O N S +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// G L O B A L F U N C T I O N S +////////////////////////////////////////////////////////////////////////////// + +// constructor + +Screen::Screen() : + m_x(0), + m_y(0) +{ + clear(); + home(); +} + + +// destructor + +Screen::~Screen() +{ +} + + +// public member functions + +void Screen::clear() +{ + uint_least8_t c = iso2scr (' '); + for (unsigned int i = 0; i < m_screenSize; ++i) + { + m_screen[i] = c; + } +} + + +void Screen::home() +{ + m_x = 0; + m_y = 0; +} + + +void Screen::move(unsigned int x, unsigned int y) +{ + if ((x < m_width) && (y < m_height)) + { + m_x = x; + m_y = y; + } +} + + +void Screen::putchar(char c) +{ + if (c == '\n') + { + m_x = 0; + moveDown(); + } + else + { + unsigned int offs = offset(m_x, m_y); + m_screen[offs] = iso2scr ((uint_least8_t) c); + moveRight(); + } +} + + +void Screen::write(const char *str) +{ + while (*str) + { + putchar(*str); + ++str; + } +} + + +void Screen::poke(unsigned int x, unsigned int y, uint_least8_t value) +{ + if ((x < m_width) && (y < m_height)) + { + unsigned int offs = offset (x, y); + m_screen[offs] = value; + } +} + + +void Screen::poke(unsigned int offs, uint_least8_t value) +{ + if (offs < m_screenSize) + { + m_screen[offs] = value; + } +} diff --git a/PSID/libpsid64/screen.h b/PSID/libpsid64/screen.h new file mode 100644 index 00000000..d443fadc --- /dev/null +++ b/PSID/libpsid64/screen.h @@ -0,0 +1,154 @@ +/* + $Id$ + + psid64 - create a C64 executable from a PSID file + Copyright (C) 2001-2003 Roland Hermans + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef SCREEN_H +#define SCREEN_H + +////////////////////////////////////////////////////////////////////////////// +// I N C L U D E S +////////////////////////////////////////////////////////////////////////////// + +//#include + +#include "../sidplay/sidint.h" + + +////////////////////////////////////////////////////////////////////////////// +// F O R W A R D D E C L A R A T O R S +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// D A T A D E C L A R A T O R S +////////////////////////////////////////////////////////////////////////////// + +class Screen +{ +public: + Screen(); + ~Screen(); + + void clear(); + void home(); + void move(unsigned int x, unsigned int y); + void putchar(char c); + void write(const char *str); + + /*inline void write(const std::string& str) + { + write(str.c_str()); + }*/ + + void poke(unsigned int x, unsigned int y, uint_least8_t value); + void poke(unsigned int offs, uint_least8_t value); + + inline const uint_least8_t *getData() const + { + return m_screen; + } + + inline unsigned int getDataSize() const + { + return m_screenSize; + } + + inline unsigned int getX() const + { + return m_x; + } + + inline unsigned int getY() const + { + return m_y; + } + + inline void moveRight() + { + if (m_x < (m_width - 1)) + { + ++m_x; + } + } + + inline void moveDown() + { + if (m_y < (m_height - 1)) + { + ++m_y; + } + } + + static inline uint_least8_t iso2scr(uint_least8_t c) + { + static const uint_least8_t scrtab[] = { + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, // 0x00 + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, // 0x08 + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, // 0x10 + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, // 0x18 + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, // 0x20 !"#$%&' + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, // 0x28 ()*+,-./ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, // 0x30 01234567 + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, // 0x38 89:;<=>? + 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, // 0x40 @ABCDEFG + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, // 0x48 HIJKLMNO + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, // 0x50 PQRSTUVW + 0x58, 0x59, 0x5a, 0x1b, 0xbf, 0x1d, 0x1e, 0x64, // 0x58 XYZ[\]^_ + 0x27, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 0x60 `abcdefg + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // 0x68 hijklmno + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, // 0x70 pqrstuvw + 0x18, 0x19, 0x1a, 0x1b, 0x5d, 0x1d, 0x1f, 0x20, // 0x78 xyz{|}~ + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 0x80 + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 0x88 + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 0x90 + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 0x98 + 0x20, 0x21, 0x03, 0x1c, 0xbf, 0x59, 0x5d, 0xbf, // 0xa0  ¡¢£¤¥¦§ + 0x22, 0x43, 0x01, 0x3c, 0xbf, 0x2d, 0x52, 0x63, // 0xa8 ¨©ª«¬­®¯ + 0x0f, 0xbf, 0x32, 0x33, 0x27, 0x15, 0xbf, 0xbf, // 0xb0 °±²³´µ¶· + 0x2c, 0x31, 0x0f, 0x3e, 0xbf, 0xbf, 0xbf, 0x3f, // 0xb8 ¸¹º»¼½¾¿ + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x43, // 0xc0 ÀÁÂÃÄÅÆÇ + 0x45, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49, // 0xc8 ÈÉÊËÌÍÎÏ + 0xbf, 0x4e, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x18, // 0xd0 ÐÑÒÓÔÕÖ× + 0x4f, 0x55, 0x55, 0x55, 0x55, 0x59, 0xbf, 0xbf, // 0xd8 ØÙÚÛÜÝÞß + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, // 0xe0 àáâãäåæç + 0x05, 0x05, 0x05, 0x05, 0x09, 0x09, 0x09, 0x09, // 0xe8 èéêëìíîï + 0xbf, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0xbf, // 0xf0 ðñòóôõö÷ + 0x0f, 0x15, 0x15, 0x15, 0x15, 0x19, 0xbf, 0x19 // 0xf8 øùúûüýþÿ + }; + + return scrtab[c]; + } + +private: + static const unsigned int m_width = 40; + static const unsigned int m_height = 25; + static const unsigned int m_screenSize = m_width * m_height; + + uint_least8_t m_screen[m_screenSize]; + unsigned int m_x; + unsigned int m_y; + + inline unsigned int offset(unsigned int x, unsigned int y) + { + return x + (m_width * y); + } +}; + +#endif // SCREEN_H diff --git a/PSID/libpsid64/sidid.cpp b/PSID/libpsid64/sidid.cpp new file mode 100644 index 00000000..94b4f890 --- /dev/null +++ b/PSID/libpsid64/sidid.cpp @@ -0,0 +1,239 @@ +/* + $Id$ + + psid64 - create a C64 executable from a PSID file + Copyright (C) 2015 Roland Hermans + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +////////////////////////////////////////////////////////////////////////////// +// I N C L U D E S +////////////////////////////////////////////////////////////////////////////// + +#include "sidid.h" + +#include +#include + + +////////////////////////////////////////////////////////////////////////////// +// G L O B A L D A T A +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// L O C A L D E F I N I T I O N S +////////////////////////////////////////////////////////////////////////////// + + + +////////////////////////////////////////////////////////////////////////////// +// L O C A L D A T A +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// L O C A L F U N C T I O N S +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// G L O B A L F U N C T I O N S +////////////////////////////////////////////////////////////////////////////// + +void SidId::Pattern::clear() +{ + m_values.clear(); +} + + +void SidId::Pattern::pushValue(uint_least16_t value) +{ + m_values.push_back(value); +} + + +bool SidId::Pattern::match(const std::vector& buffer) const +{ + const uint_least8_t* p_buffer = &buffer.front(); + const uint_least16_t* p_values = &m_values.front(); + size_t buffer_size = buffer.size(); + size_t i = 0; + size_t j = 0; + + while (true) + { + // search for first matching byte + while ((i < buffer_size) && (p_buffer[i] != p_values[j])) + { + ++i; + } + if (i == buffer_size) + { + break; + } + size_t saved_i = i; + size_t saved_j = j; + + // check if subsequent bytes match as well + while ((i < buffer_size) + && ((p_buffer[i] == p_values[j]) + || (p_values[j] == MATCH_WILDCARD_ONE))) + { + ++i; + ++j; + } + + if (p_values[j] == MATCH_END) + { + // all parts matched + break; + } + else if (p_values[j] == MATCH_WILDCARD_MULTIPLE) + { + // this part did match, continue with next part + ++j; + } + else + { + // retry matching after first byte of current match attempt + i = saved_i + 1; + j = saved_j; + } + } + + return (p_values[j] == MATCH_END); +} + + +SidId::Player::Player(const std::string& name) : m_name(name) +{ + // do nothing +} + + +void SidId::Player::addPattern(const Pattern& pattern) +{ + m_patterns.push_back(pattern); +} + + +const std::string& SidId::Player::name() const +{ + return m_name; +} + + + bool SidId::Player::match(const std::vector& buffer) const + { + for (std::vector::const_iterator iter = m_patterns.begin(); + iter != m_patterns.end(); ++iter) + { + if (iter->match(buffer)) + { + return true; + } + } + return false; + } + + +std::string SidId::trim(const std::string& str, const std::string& whitespace) +{ + const size_t begin_pos = str.find_first_not_of(whitespace); + if (begin_pos == std::string::npos) + { + return ""; + } + const size_t end_pos = str.find_last_not_of(whitespace); + return str.substr(begin_pos, end_pos - begin_pos + 1); +} + + +bool SidId::readConfigFile(const std::string& filename) +{ + m_players.clear(); + std::ifstream f; + f.open(filename.c_str()); + if (!f) + { + return false; + } + std::string line; + while (std::getline(f, line)) + { + line = trim(line); + if (!line.empty()) + { + Pattern pattern; + std::istringstream line_ss(line); + std::string token; + while (line_ss >> token) + { + if (token == "??") + { + pattern.pushValue(MATCH_WILDCARD_ONE); + } + else if (token == "AND") + { + pattern.pushValue(MATCH_WILDCARD_MULTIPLE); + } + else if (token == "END") + { + pattern.pushValue(MATCH_END); + if (m_players.empty()) + { + // first player definition did not start with a name + return false; + } + m_players.back().addPattern(pattern); + pattern.clear(); + } + else if ((token.size() == 2) + && isxdigit(token[0]) && isxdigit(token[1])) + { + uint_least16_t i; + std::stringstream token_ss(token); + token_ss >> std::hex >> i; + + pattern.pushValue(i); + } + else + { + // start of new player definition + m_players.push_back(Player(token)); + } + } + } + } + + return true; +} + + +std::string SidId::identify(const std::vector& buffer) +{ + for (std::vector::const_iterator iter = m_players.begin(); + iter != m_players.end(); ++iter) + { + if (iter->match(buffer)) + { + return iter->name(); + } + } + + return ""; +} diff --git a/PSID/libpsid64/sidid.h b/PSID/libpsid64/sidid.h new file mode 100644 index 00000000..1f75690a --- /dev/null +++ b/PSID/libpsid64/sidid.h @@ -0,0 +1,79 @@ +/* + $Id$ + + psid64 - create a C64 executable from a PSID file + Copyright (C) 2015 Roland Hermans + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef SIDID_H +#define SIDID_H + +////////////////////////////////////////////////////////////////////////////// +// I N C L U D E S +////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "../sidplay/sidint.h" + + +////////////////////////////////////////////////////////////////////////////// +// F O R W A R D D E C L A R A T O R S +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// D A T A D E C L A R A T O R S +////////////////////////////////////////////////////////////////////////////// + +class SidId +{ + class Pattern + { + std::vector m_values; + public: + void clear(); + void pushValue(uint_least16_t value); + bool match(const std::vector& buffer) const; + }; + + class Player + { + std::string m_name; + std::vector m_patterns; + public: + explicit Player(const std::string& name); + void addPattern(const Pattern& pattern); + const std::string& name() const; + bool match(const std::vector& buffer) const; + }; + + std::vector m_players; + + static const uint_least16_t MATCH_WILDCARD_ONE = 0x100; + static const uint_least16_t MATCH_WILDCARD_MULTIPLE = 0x101; + static const uint_least16_t MATCH_END = 0x102; + + static std::string trim(const std::string& str, + const std::string& whitespace = " \t\n\r"); +public: + bool readConfigFile(const std::string& filename); + std::string identify(const std::vector& buffer); +}; + +#endif // SIDID_H diff --git a/PSID/libpsid64/stilview/README.txt b/PSID/libpsid64/stilview/README.txt new file mode 100644 index 00000000..32977145 --- /dev/null +++ b/PSID/libpsid64/stilview/README.txt @@ -0,0 +1,508 @@ +STILVIEW v2.17 +============== + +(C) 1998, 2002 by LaLa + +URL: http://lala.c64.org/ + + +WHAT IS IT? +~~~~~~~~~~~ + +If you don't have a clue about what STIL is, read the STIL.faq file in the +DOCUMENTS subdirectory of your HVSC. + +If you don't even know what HVSC is, head over to http://www.hvsc.c64.org. + +STILView (or more precisely, the STIL class written in C++) is intended to be +compiled with the various SID emulators available on many platforms to provide +the capability of showing STIL and BUG information along with the given SID +that is currently being played in the emulator. It requires HVSC v2.6 +(post-update #12) or later to function correctly, but it will work with +earlier versions to a limited extent. + +Also included in the ZIP is 'stilview.cpp', which demonstrates the current +features of STILView and provides an example on usage for SID emulator +developers, but it also makes STILView a standalone application. + + +IMPORTANT NOTES +~~~~~~~~~~~~~~~ + +You should have an ISO C++ Standards compliant compiler to compile this code. + +In addition in stildefs.h you should define: + +- what character is used on your system as a pathname separator (an attempt is +made to determine the value for SLASH automatically, though), + +- whether your system uses strcasecmp/strancasecmp or stricmp/strncimp for +case-insensitive string comparisons. + + +DEBUG OUTPUT +~~~~~~~~~~~~ + +If you run STILVIEW with the -d option, it will spew out debugging stuff to +STDERR. This provides helpful output for me to find problems with the code. If +you can include the output acquired in this way in your bug-report, please do +so. See the BUG REPORTS section below. + + +STILView_vX.XX.zip CONTAINS: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- *.cpp, *.h - The required source files. However, your application has to + #include only "stil.h", as demonstrated by 'stilview.cpp'. + +- Makefile - This should build the command-line version of STILView and all + the required object files. + +- README.txt - This text. + +- USAGE.txt - How to use the standalone STILVIEW program (which is the result + of compiling 'stilview.cpp', ie. the STILVIEW.EXE under WinDOS). + +- TODO.txt - Ignore this file. ;) + + +STILVIEW FEATURES: +~~~~~~~~~~~~~~~~~~ + +- It's implemented as an object-oriented C++ class. + +- Fast searching of both STIL.txt and BUGlist.txt with an internal directory +cache list. + +- The last requested entry is internally buffered (cached), so that subsequent +requests on the same entry/directory are significantly faster (no disk I/O +involved at all). + +- Strings returned contain newlines as '\n' is defined for the given OS the +code was compiled under. Yet, it doesn't matter what is used as an +end-of-line (EOL) char (or chars) in STIL.txt: (0x0d), (0x0a), (0x0d 0x0a) or +even (0x0a 0x0d) is all properly taken care of. One important note, though: +both STIL.txt and BUGlist.txt have to use the same EOL chars!!! (The actual +EOL used is determined only from STIL.txt!) + +- Ability to get the section-global comment for an HVSC directory (even if you +specify the full path+filename!). + +- Ability to get the BUG entry for a given tune number (0 = get it for all +subtunes). + +- Ability to get the STIL entry for a given tune number. + +- Ability to get a specific STIL field for a given tune number. + +- Partially compatible with STIL v2.5 or earlier: section-global comments +and BUG entries (if HVSC:/DOCUMENTS/BUGlist.txt exists) are retrieved, and +full STIL entries are retrieved, too, but specific tune number and field +requests are ignored (ie. doesn't matter what you ask for, you'll receive the +full STIL entry as a result). + +- If setBaseDir() fails, it will *not* have an impact on the private data +already there. In other words, if you did a previous setBaseDir() that +succeeded, then you do one again that fails, you should still be able to +safely use the STIL class, because it'll have the private data populated from +the first setBaseDir() operation intact. + +- Also included is a standalone version of STILView that can be used as a +command-line tool to print STIL entries, but is also good for testing +STILView's capabilities. See USAGE.txt for details. + + +USAGE: +~~~~~~ + +I admit, the public interface of the STIL class can be somewhat confusing for +newcomers, so here's the bottom line on how to make good use of it. + +In your program, #include "stil.h" and then instantiate a STIL object. Then +ask the user about the location of HVSC, and pass the pathname string entered +by the user to STIL::setBaseDir(). This initializes the internal structures of +the STIL object. (Of course, you should check with STIL::getError() to make +sure this step succeeded, i.e. that STILView found the STIL.txt document and +it was able to read it in.) + +Then you have essentially two choices: + +A) The procedure to extract everything from STIL relating to a specific tune + in one specific SID file is: + + 1) Retrieve the section-global comment with: + getGlobalComment() + 2) Retrieve the file-global comment with: + getEntry(..., tuneNo=0, field=comment) + 3) Retrieve all of the STIL entry for tune #x: + getEntry(..., tuneNo=x) + or retrieve one field from the STIL entry of tune #x: + getEntry(..., tuneNo=x, field=) + 4) Retrieve the BUG entry for tune #x with: + getBug(..., tuneNo=x) + +B) If you would like to extract everything from STIL relating to a specific + SID file (i.e. including everything related to all of its subtunes), the + procedure is: + + 1) Retrieve the section-global comment with: + getGlobalComment() + 2) Retrieve all of the STIL entry with: + getEntry(..., tuneNo=0, field=all) + 3) Retrieve all of the BUG entry with: + getBug(..., tuneNo=0) + +These steps may or may not return something to you, but it is guaranteed that +there will be no duplicate info if they all return something. + +See also 'stil.h' or 'USAGE.txt' for details and 'stilview.cpp' for more +practical usage ideas. + + +BUG REPORTS: +~~~~~~~~~~~~ + +If for whatever reason you cannot get STILView compiled under your OS, +or it compiles but gives you warnings, or it runs but crashes on you, or it +doesn't behave as expected (please, do read 'stil.h' before complaining, +though!), send your *DETAILED* description of the encountered problem along +with the STIL.txt and BUGlist.txt files found in your HVSC:/DOCUMENTS/ +subdirectory to: + +LaLa + + +GREETZ TO: +~~~~~~~~~~ + +The Shark - for HVSC and for his relentless support of STILView. + +Michael Schwendt - for giving me excellent feedback and great bug reports that +forced me to completely rewrite STILView v1.0... ;) + +Andreas Varga - for giving me great feedback on Mac stuff and for giving me an +idea on how to speed up searching the STIL by indexing the sections (subdirs). + +The rest of the HVSC Crew - for giving me many-many ideas! + + +HISTORY +~~~~~~~ + +v2.17: +- BUGFIX: The get*() member functions used to bomb when any of them was + called after setBaseDir() failed the very first time. Frankly, people should + check the return value of setBaseDir() to prevent this from happening, but + what the heck - it was an easy fix. +- BUGFIX: The internal buffers (caches) were not flushed in setBaseDir() which + might've resulted in incorrect entries being returned if the same entry was + asked for right before and right after setting the baseDir to something + different with setBaseDir(). +- NEW: Minor fixes in I/O stream handling to make the code ISO C++ standards + (and thus, GCC3) compliant. (Thanks, Michael!) +- NEW: Replaced insecure library functions with more secure ones. Namely, + replaced strcpy() with strncpy(), strcat() with strncat(), sprintf() with + snprintf(), and an sscanf() with an atof() call. (Thanks, Andreas!) +- NEW: A slightly different way of #define's is introduced in stildefs.h that + will hopefully make them easier to define for developers. +- NEW: The Win32 executable version of STILView is no longer available. + +v2.16: +- BUGFIX: getEntry(tune=0,field=comment) used to retrieve all of the entry for + single-tune entries like /Hubbard_Rob/Chicken_Song.sid instead of just + the file-global comment. This is now fixed, so the behavior of getEntry() + should be consistent once again. +- NEW: Added a practical usage guideline to the README file. + +v2.15: +- NEW: Added support for the new AUTHOR field. + +v2.14: +- BUGFIX: If the BUGlist.txt file had no entries in it (which is a perfectly + valid, normal, in fact, desired situation), STILView refused to continue. + This is now fixed, and an empty BUGlist.txt will be accepted, too. +- NEW: Restored debug output to go to STDERR again. Screw Microsoft. + +v2.13: +- BUGFIX OF BUGFIX: The previous "fix" prevented multitune entries from + showing up if a specific tune number was asked for. Fixed it (fingers + crossed). +- NEW: Debug output is now dumped to STDOUT, because I don't know how to dump + STDERR to a file under MS-DOS/Windows (really stupid). + +v2.12: +- BUGFIX: If a COMMENT had the string "(#" in it, STILView didn't always show + it when a tune-specific entry was asked for. Fixed it. + +v2.11: +- BUGFIX: Char arrays are now deleted by delete[] (thanks, Michael!). +- BUGFIX: Version info acquired from a new STIL.txt file is now also updated + when setBaseDir() is called with a different HVSC base directory. The + command-line STILView with the "-d -m" options will now test this, too. + +v2.10: +All of the changes in this version are strictly internal enhancements. +- NEW: The directory list structures are replaced with dynamically allocated + linked lists. This gets rid of a previous limitation, which allowed only a max + number of dirs in STIL.txt before STILView crapped out. +- NEW: Also got rid of the STIL_MAX_DIRS #define, which is not needed any more: + theoretically, STILView is now able to handle as many dirs present in STIL.txt + as the available memory in your machine allows. +- NEW: Internal structures for holding absolute pathnames are now dynamically + allocated, not statically. +- NEW: Also got rid of the STIL_MAX_PATH_SIZE #define, which is not needed any + more: theoretically, STILView is now able to handle an arbitrary long + pathname for the absolute path of the HVSC base directory. +- NEW: Increased the STIL_MAX_ENTRY_SIZE #define to 100 lines. Analysis of STIL + v3.5 shows that the largest STIL entry is /DEMOS/Synth_Sample.sid, which is + about 3087 bytes large, which is still well within the old max entry size + limit, but better be safe than sorry. It was easier to do this than to change + all internal buffers to dynamically allocated ones... (I'm lacking time to rip + out the guts of the code to change this.) + +v2.00: +- BUGFIX: Some for loops used inline declared integers. Moved the declarations + to the beginning of the functions. +- Please, note that for the get*Entry() methods saying tuneNo != 0, field = all + will return stuff for single-tune entries only if tuneNo = 1! For all other + numbers, nothing is returned. +- BUGFIX: In the get*Entry() methods tuneNo = 0, field = comment returned + nothing if the SID file had more than one tune, but the STIL entry had nothing + else but a COMMENT in it (ie. there's no "(#x)" tune designation in it). This + was because STILView assumed that the STIL entry was for a single-tune SID. + (And asking for tuneNo=1, field=comment/all did return the COMMENT!) Fixed it + so that if a STIL entry has nothing but a single COMMENT in it, tuneNo=0, + field=comment will always return it, but tuneNo != 0, field= will + not. + Essentially, single-tune entries that have nothing but a COMMENT field in + them, that COMMENT is assumed to be a file-global comment from now on. + Here's a full and complete explanation of how the two parameters work now + (it is exactly the same as in the beta, except that now this bug is fixed): + - tuneNo = 0, field = all : all of the STIL entry is returned. + - tuneNo = 0, field = comment : the file-global comment is returned. + For single-tune entries that have nothing but a COMMENT field in them, + this returns that COMMENT. For single-tune entries that have other fields + in them, this returns nothing. + - tuneNo = 0, field = : INVALID! (NULL is returned) + - tuneNo != 0, field = all : all fields of the STIL entry for the given + tune number are returned. For single-tune entries that have nothing but a + COMMENT in them, this returns nothing. For single-tune entries that have + other fields in them, this returns the whole entry, but only if tuneNo=1 + (otherwise it returns nothing). + - tuneNo != 0, field = : the specific field of the specific + tune number is returned. If the tune number doesn't exist (eg. if + tuneNo=2 for single-tune entries, or if tuneNo=2 when there's no + STIL entry for tune #2 in a multitune entry), returns NULL. For + single-tune entries that have nothing but a COMMENT in them, this returns + nothing. + + The procedure to extract everything from STIL relating to a specific tune + in one specific SID file is unchanged: + a) Retrieve the section-global comment with: + get*GlobalComment() + b) Retrieve the file-global comment with: + get*Entry(..., tuneNo=0, field=comment) + c) Retrieve all of the STIL entry for tune #x: + get*Entry(..., tuneNo=x) + or retrieve one field from the STIL entry of tune #x: + get*Entry(..., tuneNo=x, field=) + d) Retrieve the BUG entry for tune #x with: + get*Bug(..., tuneNo=x) + + If you would like to extract everything from STIL relating to a specific + SID file (including all its subtunes), the procedure is: + a) Retrieve the section-global comment with: + get*GlobalComment() + c) Retrieve all of the STIL entry with: + get*Entry(..., tuneNo=0, field=all) + d) Retrieve all of the BUG entry with: + get*Bug(..., tuneNo=0) + + Any of these steps may or may not return something to you, but it is + guaranteed that there will be no duplicate info if they all return + something. +- BUGFIX: Increased the STIL_MAX_DIR #define to 500 (STIL v3.5 has entries for + more than 300 dirs in it!). + +v2.00beta: +- NEW: Added support for the new "NAME" field in STIL (see the new enum for + STILField). + Previous versions of STILView will *NOT* break because of the presence of + this new field in STIL, but you won't be able to retrieve just this specific + field with them with the get*Entry() methods. +- NEW: The Makefile is back again, and hopefully, this time it works on every + platform (at least GCC likes it both under Solaris and Win98. :) +- NEW: Totally got rid of all references to timestamps. (I will just never have + the time to implement it, plus nobody screamed to me that they want to have + it implemented.) This also means that the public methods of get*Entry() + have one less parameter now! You might have to change your code where these + methods are called. +- NEW: Since a few users of the command line STILView complained about it (and + rightly so), I have modified how the tuneNo and field parameters are + interpreted by get*Entry(). Below is full and complete (!) explanation: + - tuneNo = 0, field = all : all of the STIL entry is returned. + - tuneNo = 0, field = comment : the file-global comment is returned. + (For single-tune entries, this returns nothing! This is NEW!) + - tuneNo = 0, field = : INVALID! (NULL is returned) (This is NEW!) + - tuneNo != 0, field = all : all fields of the STIL entry for the + given tune number are returned. (For single-tune entries, this is + equivalent to saying tuneNo = 0, field = all.) + However, the file-global comment is *NOT* returned with it any + more! (Unlike in versions before v2.00.) It led to confusions: + eg. when a comment was asked for tune #3, it returned the + file-global comment even if there was no specific entry for tune #3! + - tuneNo != 0, field = : the specific field of the specific + tune number is returned. If the tune number doesn't exist (eg. if + tuneNo=2 for single-tune entries, or if tuneNo=2 when there's no + STIL entry for tune #2 in a multitune entry), returns NULL. + What does all of this really mean to you? Two things: + 1) tuneNo != 0, field = all will no longer return the file-global comment + (along with the actual entry) any more! In other words, the *ONLY* way + that a file-global comment will show up in a result is if you explicitly + ask for it (tuneNo=0, field=comment), or if you ask for the whole entry + (tuneNo=0, field=all). (Provided it exists at all, of course.) + 2) Subsequently, tuneNo=0, field=name/title/artist will no longer return + all of the STIL entry for single-tune entries! If you relied on this + before, change your calling routine(s). The *ONLY* way to retrieve + specific fields in a single-tune entry (other than a comment) is to say + tuneNo=1, field=name/title/artist/comment. + 3) So, if you want to retrieve all pieces of info from STIL that are + related to one specific tune in one specific SID file, you'll need to do + the following steps: + a) Retrieve the section-global comment with: + get*GlobalComment() + b) Retrieve the file-global comment with: + get*Entry(..., tuneNo=0, field=comment) + c) Retrieve all of the STIL entry for tune #x: + get*Entry(..., tuneNo=x) + or retrieve one field from the STIL entry of tune #x: + get*Entry(..., tuneNo=x, field=) + d) Retrieve the BUG entry for tune #x with: + get*Bug(..., tuneNo=x) + Any of these steps may or may not return something to you, but it is + guaranteed that there will be no duplicate info if they all return + something (ie. step c. will *NOT* return the file-global comment, even + if there exists a COMMENT field in a single-tune entry!). + I hope this all sounds and looks more logical now. Strangely enough, my + modifications for this resulted in a leaner and more logical code and + algorithm in stil.cpp... :-) +- NEW: Also, in the get*Entry() and get*Bug() methods the default value of + tuneNo is now 0, and the default value of field is 'all'. +- NEW: Minor enhancements in the formatted output of the command-line STILView. +- BUGFIX: Previous versions of STILView used to have a "hidden feature": they + were able to retrieve STIL entries even if only a partial pathname was + specified, eg. /Hubbard_Rob/C retrieved /Hubbard_Rob/Chain_Reaction.sid (the + first entry in Hubbard's section starting with a C). This was all nice, but + if you asked for /Hubbard_Rob/Comm (which retrieved the Commando entry) then + for /Hubbard_Rob/C again, this still returned the Commando entry, since the + internal buffer now had a Hubbard entry in it that was starting with a C! + This behavior was inconsistent, so now the pathname have to be a fully + qualified STIL entry in order for the search to be successful. In other + words, asking for partial pathnames (eg. just /Hubbard_Rob/C) will now fail. +- BUGFIX: If a section-global comment was asked for with get*Entry() (eg. + /Hubbard_Rob/, with the trailing slash!), it returned it. This is not + desirable, as those entries should be asked for only with + get*GlobalComment(). Fixed it. Also added a new non-critical error to note + when get*Entry() is wrongly used to ask for section-global comments. +- NEW: Made STIL entry search under UNIX totally case-insensitive (strcasecmp() + instead of strcmp() ). +- NEW: If setBaseDir() fails, it will *not* have an impact on the private data + already there. In other words, if you did a previous setBaseDir() that + succeeded, then you do one again that fails, you should still be able to + safely use the STIL class, because it'll have the private data populated from + the first setBaseDir() operation intact. + This new functionality slightly increases the time it takes to do + setBaseDir(), but it should not be too noticeable. +- NEW: Separated the demo mode from the interactive mode, and added a new + option (-m) for the command-line STILView to ask for just demo mode. + Specifying just -m will print some things demonstrating the capabilities of + STILView (which also tests some of its most important features) then exits. + Specifying just -i enters interactive mode. Specifying both -m and -i will + work like previous versions: the demo is printed, then interactive mode is + entered. + +v1.30: +- Never-released internal version to test the new NAME field support. + +v1.25: +- Version number is now defined as a float constant, not with a #define. +- ios::bad() calls were changed to the more compatible ios::fail(). +- ios::clear() is now called every time before an ios::open() to clear the + potential error bits left over from a previous open(). +- the standalone STILView now returns a 0 after normal operation (it is still + returning 0's and 1's with exit() in some cases, though). +- Added a sentence the (C) statements everywhere to reflect that I don't mind + if other people modify and/or extend this code, as long as they still give + credit to the original author (ie. me). +- Updated the USAGE.txt here and there, did a spell check on it and other + small cosmetic changes. + +v1.24: +- getGlobalComment() was not safeguarded: if an incorrectly formatted + path+filename was passed to it, it crapped out. Fixed it. +- Previous versions are no longer available from the website. I am tight on + space on the server (I have to pay for the extra space occupied), plus I see + no need to keep earlier versions around. The Win32 executable is also gone + for the same reason. + +v1.23: +- Naturally, I screwed up the error strings with the reshuffled error codes. + This could've caused having garbage printed out instead of the actual error + description. This has been fixed. + +v1.22: +- Added a new method called hasCriticalError() that tells whether the last + encountered error was a critical show-stopper error or not. The STILerror + enum values have also been shuffled around to support this, so if you did + not use them by their symbolic names, take a look at them again. + +v1.21: +- Changed the demo entry to /Galway_Martin/Green_Beret.sid + +v1.20: +- Got rid of the Makefile in the source distribution - it was too specific. +- Added #defines to support the Amiga OS. +- Renamed everything concerning DEBUG to be more STILView specific. +- Added a new public method called getError(), which returns the specific + error number for the error that happened during the last execution of a STIL + method. +- Added a new public method called getErrorStr(), which returns a string + containing the description of the error that happened during the last + execution of a STIL method. +- Changed all "char *" to "const char *" in the public methods to get rid of + the warnings they generated (the strings passed in do not get modified at + all in the STIL class). +- The included stilview.cpp file now compiles a standalone command-line + version of STILView, mainly for demonstration purposes, but it can be used + as a full-fledged command-line tool. See USAGE.TXT for details about its + usage. +- STILView now has a webpage from where all versions can be downloaded. + +v1.12: +- Fixed a bug that happened with the latest STIL v2.6 and was due to the fact + that the STIL got resorted case-insensitively. Made the positionToEntry() + function independent of how STIL is sorted. +- At the same time, also made a small change that speeds up the entry search + within a section in positionToEntry() significantly (a friendly advice: use + tellg() only when really necessary, otherwise it slows things down + considerably, like it did here...) + +v1.11: +- Fixed a bug with the getAbs*() functions that was introduced with the + previous bugfix. ;) +- STIL::setBaseDir() now will complain when an empty string is passed to it + as a path. +- Fixed a minor compile-time warning (getDirs() was hiding stilFile). +- The source files in this package now have the .cpp extension instead of .C. + +v1.10: +- Fixed bug when an entry was passed in with an empty string instead of the + actual relative path (caused a crash in SIDPlay/Win when playing SIDs + outside of the HVSC subdir). +- Fixed problem with doing a case-sensitive pathname match under Win instead + of a case-insensitive one. +- Significantly sped up the initial parsing of STIL.txt when building up the + list of directories. + +v1.00: +- First public release. diff --git a/PSID/libpsid64/stilview/STILView-config.patch b/PSID/libpsid64/stilview/STILView-config.patch new file mode 100644 index 00000000..7a9d7cd8 --- /dev/null +++ b/PSID/libpsid64/stilview/STILView-config.patch @@ -0,0 +1,88 @@ +*** stildefs.h.orig Sat Nov 17 13:29:03 2001 +--- stildefs.h Sat Nov 17 13:41:13 2001 +*************** +*** 5,47 **** + #ifndef _STILDEFS_H + #define _STILDEFS_H + +! #if defined(__linux__) || defined(__FreeBSD__) || defined(solaris2) || defined(sun) || defined(sparc) || defined(sgi) +! #define UNIX +! #endif +! +! #if defined(__MACOS__) +! #define MAC +! #endif +! +! #if defined(__amigaos__) +! #define AMIGA +! #endif + + // On a per OS basis: + // - Define what the pathname separator is. + // - Define what the function is to compare strings case-insensitively. + // - Define what the function is to compare portions of strings case-insensitively. + // +! #ifdef UNIX +! #define SLASH '/' +! #define MYSTRCMP strcasecmp +! #define MYSTRNCMP strncasecmp +! #else +! #ifdef MAC +! #define SLASH ':' +! #define MYSTRCMP stricmp +! #define MYSTRNCMP strnicmp + #else +! #ifdef AMIGA +! #define SLASH '/' +! #define MYSTRCMP stricmp +! #define MYSTRNCMP strnicmp +! #else // WinDoze +! #define SLASH '\\' +! #define MYSTRCMP stricmp +! #define MYSTRNCMP strnicmp +! #endif + #endif + #endif + + // Common definition for boolean +--- 5,27 ---- + #ifndef _STILDEFS_H + #define _STILDEFS_H + +! #include "config.h" + + // On a per OS basis: + // - Define what the pathname separator is. + // - Define what the function is to compare strings case-insensitively. + // - Define what the function is to compare portions of strings case-insensitively. + // +! #define SLASH '/' +! #ifdef HAVE_STRCASECMP +! #define MYSTRCMP strcasecmp + #else +! #define MYSTRCMP stricmp + #endif ++ #ifdef HAVE_STRNCASECMP ++ #define MYSTRNCMP strncasecmp ++ #else ++ #define MYSTRNCMP strnicmp + #endif + + // Common definition for boolean +*************** +*** 60,66 **** + + // Maximum size of a single line in STIL - also accounts for some extra + // padding, just in case. +! #define STIL_MAX_LINE_SIZE 91 + + // Maximum size of a single STIL entry (in bytes). + #define STIL_MAX_ENTRY_SIZE STIL_MAX_LINE_SIZE*100 +--- 40,46 ---- + + // Maximum size of a single line in STIL - also accounts for some extra + // padding, just in case. +! #define STIL_MAX_LINE_SIZE 129 + + // Maximum size of a single STIL entry (in bytes). + #define STIL_MAX_ENTRY_SIZE STIL_MAX_LINE_SIZE*100 diff --git a/PSID/libpsid64/stilview/stil.cpp b/PSID/libpsid64/stilview/stil.cpp new file mode 100644 index 00000000..915868e4 --- /dev/null +++ b/PSID/libpsid64/stilview/stil.cpp @@ -0,0 +1,1402 @@ +// +// STIL class - Implementation file +// +// AUTHOR: LaLa +// Email : LaLa@C64.org +// Copyright (C) 1998, 2002 by LaLa +// + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef _STIL +#define _STIL + +#include +#include +#include +#include +#include // For snprintf() and NULL +using namespace std; +#include "stil.h" + +#define STILopenFlags (ios::in | ios::binary) + +const float VERSION_NO = 2.17; + +#define CERR_STIL_DEBUG if (STIL_DEBUG) cerr << "Line #" << __LINE__ << " STIL::" + +// CONSTRUCTOR +STIL::STIL() +{ +#ifdef HAVE_SNPRINTF + snprintf(versionString, 2*STIL_MAX_LINE_SIZE-1, "STILView v%4.2f, (C) 1998, 2002 by LaLa (LaLa@C64.org)\n", VERSION_NO); +#else + sprintf(versionString, "STILView v%4.2f, (C) 1998, 2002 by LaLa (LaLa@C64.org)\n", VERSION_NO); +#endif + versionString[2*STIL_MAX_LINE_SIZE-1] = '\0'; + + STILVersion = 0.0; + baseDir = NULL; + baseDirLength = 0; + memset((void *)entrybuf, 0, sizeof(entrybuf)); + memset((void *)globalbuf, 0, sizeof(globalbuf)); + memset((void *)bugbuf, 0, sizeof(bugbuf)); + memset((void *)resultEntry, 0, sizeof(resultEntry)); + memset((void *)resultBug, 0, sizeof(resultBug)); + + // Just so that we have the first one in here. + stilDirs = createOneDir(); + bugDirs = createOneDir(); + + STIL_EOL = '\n'; + STIL_EOL2 = '\0'; + + STIL_DEBUG = false; + lastError = NO_STIL_ERROR; +} + +// DESTRUCTOR +STIL::~STIL() +{ + CERR_STIL_DEBUG << "Destructor called" << endl; + + deleteDirList(stilDirs); + deleteDirList(bugDirs); + + if (baseDir != NULL) { + delete[] baseDir; + } + + CERR_STIL_DEBUG << "Destructor finished" << endl; +} + +char * +STIL::getVersion() +{ + lastError = NO_STIL_ERROR; + return versionString; +} + +float +STIL::getVersionNo() +{ + lastError = NO_STIL_ERROR; + return VERSION_NO; +} + +float +STIL::getSTILVersionNo() +{ + lastError = NO_STIL_ERROR; + return STILVersion; +} + +bool +STIL::setBaseDir(const char *pathToHVSC) +{ + char *temp; // Just a pointer to help out. + char *tempName; // Points to full pathname to STIL.txt. + char *tempBaseDir; + size_t tempBaseDirLength; + size_t pathToHVSCLength = strlen(pathToHVSC); + size_t tempNameLength; + + // Temporary version number/copyright string. + char tempVersionString[2*STIL_MAX_LINE_SIZE]; + + // Temporary placeholder for STIL.txt's version number. + float tempSTILVersion = STILVersion; + + // Temporary placeholders for lists of sections. + dirList *tempStilDirs, *tempBugDirs; + + + lastError = NO_STIL_ERROR; + + CERR_STIL_DEBUG << "setBaseDir() called, pathToHVSC=" << pathToHVSC << endl; + + // Sanity check the length. + if (strlen(pathToHVSC) < 1) { + CERR_STIL_DEBUG << "setBaseDir() has problem with the size of pathToHVSC" << endl; + lastError = BASE_DIR_LENGTH; + return false; + } + + tempBaseDir = new char [pathToHVSCLength+1]; + strncpy(tempBaseDir, pathToHVSC, pathToHVSCLength); + tempBaseDir[pathToHVSCLength] = '\0'; + + // Chop the trailing slash + temp = tempBaseDir+strlen(tempBaseDir)-1; + if (*temp == SLASH) { + *temp = '\0'; + } + tempBaseDirLength = strlen(tempBaseDir); + + // Attempt to open STIL + + // Create the full path+filename + tempNameLength = tempBaseDirLength+strlen(PATH_TO_STIL); + tempName = new char [tempNameLength+1]; + strncpy(tempName, tempBaseDir, tempNameLength); + strncat(tempName, PATH_TO_STIL, tempNameLength-tempBaseDirLength); + tempName[tempNameLength] = '\0'; + convertSlashes(tempName); + + stilFile.clear(); + stilFile.open(tempName, STILopenFlags); + + if (stilFile.fail()) { + stilFile.close(); + CERR_STIL_DEBUG << "setBaseDir() open failed for " << tempName << endl; + lastError = STIL_OPEN; + return false; + } + + CERR_STIL_DEBUG << "setBaseDir(): open succeeded for " << tempName << endl; + + // Attempt to open BUGlist + + // Create the full path+filename + delete[] tempName; + tempNameLength = tempBaseDirLength+strlen(PATH_TO_BUGLIST); + tempName = new char [tempNameLength+1]; + strncpy(tempName, tempBaseDir, tempNameLength); + strncat(tempName, PATH_TO_BUGLIST, tempNameLength-tempBaseDirLength); + tempName[tempNameLength] = '\0'; + convertSlashes(tempName); + + bugFile.clear(); + bugFile.open(tempName, STILopenFlags); + + if (bugFile.fail()) { + + // This is not a critical error - some earlier versions of HVSC did + // not have a BUGlist.txt file at all. + + bugFile.close(); + CERR_STIL_DEBUG << "setBaseDir() open failed for " << tempName << endl; + lastError = BUG_OPEN; + } + + CERR_STIL_DEBUG << "setBaseDir(): open succeeded for " << tempName << endl; + + delete[] tempName; + + // Find out what the EOL really is + if (determineEOL() != true) { + stilFile.close(); + bugFile.close(); + CERR_STIL_DEBUG << "determinEOL() failed" << endl; + lastError = NO_EOL; + return false; + } + + // Save away the current string so we can restore it if needed. + strncpy(tempVersionString, versionString, 2*STIL_MAX_LINE_SIZE-1); + tempVersionString[2*STIL_MAX_LINE_SIZE-1] = '\0'; +#ifdef HAVE_SNPRINTF + snprintf(versionString, 2*STIL_MAX_LINE_SIZE-1, "STILView v%4.2f, (C) 1998, 2002 by LaLa (LaLa@C64.org)\n", VERSION_NO); +#else + sprintf(versionString, "STILView v%4.2f, (C) 1998, 2002 by LaLa (LaLa@C64.org)\n", VERSION_NO); +#endif + versionString[2*STIL_MAX_LINE_SIZE-1] = '\0'; + + // This is necessary so the version number gets scanned in from the new + // file, too. + STILVersion = 0.0; + + // These will populate the tempStilDirs and tempBugDirs arrays (or not :) + + tempStilDirs = createOneDir(); + tempBugDirs = createOneDir(); + + if (getDirs(stilFile, tempStilDirs, true) != true) { + stilFile.close(); + bugFile.close(); + CERR_STIL_DEBUG << "getDirs() failed for stilFile" << endl; + lastError = NO_STIL_DIRS; + + // Clean up and restore things. + deleteDirList(tempStilDirs); + deleteDirList(tempBugDirs); + STILVersion = tempSTILVersion; + strncpy(versionString, tempVersionString, 2*STIL_MAX_LINE_SIZE-1); + versionString[2*STIL_MAX_LINE_SIZE-1] = '\0'; + return false; + } + + if (bugFile.good()) { + if (getDirs(bugFile, tempBugDirs, false) != true) { + + // This is not a critical error - it is possible that the + // BUGlist.txt file has no entries in it at all (in fact, that's + // good!). + + CERR_STIL_DEBUG << "getDirs() failed for bugFile" << endl; + lastError = BUG_OPEN; + } + } + + stilFile.close(); + bugFile.close(); + + // Now we can copy the stuff into private data. + // NOTE: At this point, STILVersion and the versionString should contain + // the new info! + + // First, delete what may have been there previously. + if (baseDir != NULL) { + delete[] baseDir; + } + + // Copy. + baseDir = new char [tempBaseDirLength+1]; + strncpy(baseDir, tempBaseDir, tempBaseDirLength); + baseDir[tempBaseDirLength] = '\0'; + baseDirLength = tempBaseDirLength; + + // First, delete whatever may have been there previously. + deleteDirList(stilDirs); + deleteDirList(bugDirs); + + stilDirs = createOneDir(); + bugDirs = createOneDir(); + + // Now proceed with copy. + + copyDirList(stilDirs, tempStilDirs); + copyDirList(bugDirs, tempBugDirs); + + // Clear the buffers (caches). + memset((void *)entrybuf, 0, sizeof(entrybuf)); + memset((void *)globalbuf, 0, sizeof(globalbuf)); + memset((void *)bugbuf, 0, sizeof(bugbuf)); + + // Cleanup. + deleteDirList(tempStilDirs); + deleteDirList(tempBugDirs); + delete[] tempBaseDir; + + CERR_STIL_DEBUG << "setBaseDir() succeeded" << endl; + + return true; +} + +char * +STIL::getAbsEntry(const char *absPathToEntry, int tuneNo, STILField field) +{ + char *tempDir; + char *returnPtr; + size_t tempDirLength; + + lastError = NO_STIL_ERROR; + + CERR_STIL_DEBUG << "getAbsEntry() called, absPathToEntry=" << absPathToEntry << endl; + + if (baseDir == NULL) { + CERR_STIL_DEBUG << "HVSC baseDir is not yet set!" << endl; + lastError = STIL_OPEN; + return NULL; + } + + // Determine if the baseDir is in the given pathname. + + if (MYSTRNICMP(absPathToEntry, baseDir, baseDirLength) != 0) { + CERR_STIL_DEBUG << "getAbsEntry() failed: baseDir=" << baseDir << ", absPath=" << absPathToEntry << endl; + lastError = WRONG_DIR; + return NULL; + } + + tempDirLength = strlen(absPathToEntry)-baseDirLength; + tempDir = new char [tempDirLength+1]; + strncpy(tempDir, absPathToEntry+baseDirLength, tempDirLength); + tempDir[tempDirLength] = '\0'; + convertToSlashes(tempDir); + + returnPtr = getEntry(tempDir, tuneNo, field); + delete[] tempDir; + return returnPtr; +} + +char * +STIL::getEntry(const char *relPathToEntry, int tuneNo, STILField field) +{ + lastError = NO_STIL_ERROR; + + CERR_STIL_DEBUG << "getEntry() called, relPath=" << relPathToEntry << ", rest=" << tuneNo << "," << field << endl; + + if (baseDir == NULL) { + CERR_STIL_DEBUG << "HVSC baseDir is not yet set!" << endl; + lastError = STIL_OPEN; + return NULL; + } + + // Fail if a section-global comment was asked for. + + if (*(relPathToEntry+strlen(relPathToEntry)-1) == '/') { + CERR_STIL_DEBUG << "getEntry() section-global comment was asked for - failed" << endl; + lastError = WRONG_ENTRY; + return NULL; + } + + if (STILVersion < 2.59) { + + // Older version of STIL is detected. + + tuneNo = 0; + field = all; + } + + // Find out whether we have this entry in the buffer. + + if ((MYSTRNICMP(entrybuf, relPathToEntry, strlen(relPathToEntry)) != 0) || + ((( (size_t) (strchr(entrybuf, '\n')-entrybuf)) != strlen(relPathToEntry)) + && (STILVersion > 2.59))) { + + // The relative pathnames don't match or they're not the same length: + // we don't have it in the buffer, so pull it in. + + CERR_STIL_DEBUG << "getEntry(): entry not in buffer" << endl; + + // Create the full path+filename + size_t tempNameLength = baseDirLength+strlen(PATH_TO_STIL); + char *tempName = new char [tempNameLength+1]; + tempName[tempNameLength] = '\0'; + strncpy(tempName, baseDir, tempNameLength); + strncat(tempName, PATH_TO_STIL, tempNameLength-baseDirLength); + convertSlashes(tempName); + + stilFile.clear(); + stilFile.open(tempName, STILopenFlags); + + if (stilFile.fail()) { + stilFile.close(); + CERR_STIL_DEBUG << "getEntry() open failed for stilFile" << endl; + lastError = STIL_OPEN; + delete[] tempName; + return NULL; + } + + CERR_STIL_DEBUG << "getEntry() open succeeded for stilFile" << endl; + + if (positionToEntry(relPathToEntry, stilFile, stilDirs) == false) { + // Copy the entry's name to the buffer. + strncpy(entrybuf, relPathToEntry, STIL_MAX_ENTRY_SIZE-1); + strncat(entrybuf, "\n", 2); + entrybuf[STIL_MAX_ENTRY_SIZE-1] = '\0'; + CERR_STIL_DEBUG << "getEntry() posToEntry() failed" << endl; + lastError = NOT_IN_STIL; + } + else { + *entrybuf= '\0'; + readEntry(stilFile, entrybuf); + CERR_STIL_DEBUG << "getEntry() entry read" << endl; + } + + stilFile.close(); + delete[] tempName; + } + + // Put the requested field into the result string. + + if (getField(resultEntry, entrybuf, tuneNo, field) != true) { + return NULL; + } + else { + return resultEntry; + } +} + +char * +STIL::getAbsBug(const char *absPathToEntry, int tuneNo) +{ + char *tempDir; + char *returnPtr; + size_t tempDirLength; + + lastError = NO_STIL_ERROR; + + CERR_STIL_DEBUG << "getAbsBug() called, absPathToEntry=" << absPathToEntry << endl; + + if (baseDir == NULL) { + CERR_STIL_DEBUG << "HVSC baseDir is not yet set!" << endl; + lastError = BUG_OPEN; + return NULL; + } + + // Determine if the baseDir is in the given pathname. + + if (MYSTRNICMP(absPathToEntry, baseDir, baseDirLength) != 0) { + CERR_STIL_DEBUG << "getAbsBug() failed: baseDir=" << baseDir << ", absPath=" << absPathToEntry << endl; + lastError = WRONG_DIR; + return NULL; + } + + tempDirLength = strlen(absPathToEntry)-baseDirLength; + tempDir = new char [tempDirLength+1]; + strncpy(tempDir, absPathToEntry+baseDirLength, tempDirLength); + tempDir[tempDirLength] = '\0'; + convertToSlashes(tempDir); + + returnPtr = getBug(tempDir, tuneNo); + delete[] tempDir; + return returnPtr; +} + +char * +STIL::getBug(const char *relPathToEntry, int tuneNo) +{ + lastError = NO_STIL_ERROR; + + CERR_STIL_DEBUG << "getBug() called, relPath=" << relPathToEntry << ", rest=" << tuneNo << endl; + + if (baseDir == NULL) { + CERR_STIL_DEBUG << "HVSC baseDir is not yet set!" << endl; + lastError = BUG_OPEN; + return NULL; + } + + // Older version of STIL is detected. + + if (STILVersion < 2.59) { + tuneNo = 0; + } + + // Find out whether we have this bug entry in the buffer. + // If the baseDir was changed, we'll have to read it in again, + // even if it might be in the buffer already. + + if ((MYSTRNICMP(bugbuf, relPathToEntry, strlen(relPathToEntry)) != 0) || + ((( (size_t) (strchr(bugbuf, '\n')-bugbuf)) != strlen(relPathToEntry)) && + (STILVersion > 2.59))) { + + // The relative pathnames don't match or they're not the same length: + // we don't have it in the buffer, so pull it in. + + CERR_STIL_DEBUG << "getBug(): entry not in buffer" << endl; + + // Create the full path+filename + size_t tempNameLength = baseDirLength+strlen(PATH_TO_BUGLIST); + char *tempName = new char [tempNameLength+1]; + tempName[tempNameLength] = '\0'; + strncpy(tempName, baseDir, tempNameLength); + strncat(tempName, PATH_TO_BUGLIST, tempNameLength-baseDirLength); + convertSlashes(tempName); + + bugFile.clear(); + bugFile.open(tempName, STILopenFlags); + + if (bugFile.fail()) { + bugFile.close(); + CERR_STIL_DEBUG << "getBug() open failed for bugFile" << endl; + lastError = BUG_OPEN; + delete[] tempName; + return NULL; + } + + CERR_STIL_DEBUG << "getBug() open succeeded for bugFile" << endl; + + if (positionToEntry(relPathToEntry, bugFile, bugDirs) == false) { + // Copy the entry's name to the buffer. + strncpy(bugbuf, relPathToEntry, STIL_MAX_ENTRY_SIZE-1); + strcat(bugbuf, "\n"); + bugbuf[STIL_MAX_ENTRY_SIZE-1] = '\0'; + CERR_STIL_DEBUG << "getBug() posToEntry() failed" << endl; + lastError = NOT_IN_BUG; + } + else { + *bugbuf = '\0'; + readEntry(bugFile, bugbuf); + CERR_STIL_DEBUG << "getBug() entry read" << endl; + } + + bugFile.close(); + delete[] tempName; + } + + // Put the requested field into the result string. + + if (getField(resultBug, bugbuf, tuneNo) != true) { + return NULL; + } + else { + return resultBug; + } +} + +char * +STIL::getAbsGlobalComment(const char *absPathToEntry) +{ + char *tempDir; + char *returnPtr; + size_t tempDirLength; + + lastError = NO_STIL_ERROR; + + CERR_STIL_DEBUG << "getAbsGC() called, absPathToEntry=" << absPathToEntry << endl; + + if (baseDir == NULL) { + CERR_STIL_DEBUG << "HVSC baseDir is not yet set!" << endl; + lastError = STIL_OPEN; + return NULL; + } + + // Determine if the baseDir is in the given pathname. + + if (MYSTRNICMP(absPathToEntry, baseDir, baseDirLength) != 0) { + CERR_STIL_DEBUG << "getAbsGC() failed: baseDir=" << baseDir << ", absPath=" << absPathToEntry << endl; + lastError = WRONG_DIR; + return NULL; + } + + tempDirLength = strlen(absPathToEntry)-baseDirLength; + tempDir = new char [tempDirLength+1]; + strncpy(tempDir, absPathToEntry+baseDirLength, tempDirLength); + tempDir[tempDirLength] = '\0'; + convertToSlashes(tempDir); + + returnPtr = getGlobalComment(tempDir); + delete[] tempDir; + return returnPtr; +} + +char * +STIL::getGlobalComment(const char *relPathToEntry) +{ + char *dir; + size_t pathLen; + char *temp; + char *lastSlash; + + lastError = NO_STIL_ERROR; + + CERR_STIL_DEBUG << "getGC() called, relPath=" << relPathToEntry << endl; + + if (baseDir == NULL) { + CERR_STIL_DEBUG << "HVSC baseDir is not yet set!" << endl; + lastError = STIL_OPEN; + return NULL; + } + + // Save the dirpath. + + lastSlash = (char *)strrchr(relPathToEntry, '/'); + + if (lastSlash == NULL) { + lastError = WRONG_DIR; + return NULL; + } + + pathLen = lastSlash-relPathToEntry+1; + dir = new char [pathLen+1]; + strncpy(dir, relPathToEntry, pathLen); + *(dir+pathLen) = '\0'; + + // Find out whether we have this global comment in the buffer. + // If the baseDir was changed, we'll have to read it in again, + // even if it might be in the buffer already. + + if ((MYSTRNICMP(globalbuf, dir, pathLen) != 0) || + ((( (size_t) (strchr(globalbuf, '\n')-globalbuf)) != pathLen) && + (STILVersion > 2.59))) { + + // The relative pathnames don't match or they're not the same length: + // we don't have it in the buffer, so pull it in. + + CERR_STIL_DEBUG << "getGC(): entry not in buffer" << endl; + + // Create the full path+filename + size_t tempNameLength = baseDirLength+strlen(PATH_TO_STIL); + char *tempName = new char [tempNameLength+1]; + tempName[tempNameLength] = '\0'; + strncpy(tempName, baseDir, tempNameLength); + strncat(tempName, PATH_TO_STIL, tempNameLength-baseDirLength); + convertSlashes(tempName); + + stilFile.clear(); + stilFile.open(tempName, STILopenFlags); + + if (stilFile.fail()) { + stilFile.close(); + CERR_STIL_DEBUG << "getGC() open failed for stilFile" << endl; + lastError = STIL_OPEN; + delete[] dir; + delete[] tempName; + return NULL; + } + + if (positionToEntry(dir, stilFile, stilDirs) == false) { + // Copy the dirname to the buffer. + strncpy(globalbuf, dir, STIL_MAX_ENTRY_SIZE-1); + strcat(globalbuf, "\n"); + globalbuf[STIL_MAX_ENTRY_SIZE-1] = '\0'; + CERR_STIL_DEBUG << "getGC() posToEntry() failed" << endl; + lastError = NOT_IN_STIL; + } + else { + *globalbuf = '\0'; + readEntry(stilFile, globalbuf); + CERR_STIL_DEBUG << "getGC() entry read" << endl; + } + + stilFile.close(); + delete[] tempName; + } + + CERR_STIL_DEBUG << "getGC() globalbuf=" << globalbuf << endl; + CERR_STIL_DEBUG << "-=END=-" << endl; + + // Position pointer to the global comment field. + + temp = strchr(globalbuf, '\n'); + temp++; + + delete[] dir; + + // Check whether this is a NULL entry or not. + + if (*temp == '\0') { + return NULL; + } + else { + return temp; + } +} + +//////// PRIVATE + +bool +STIL::determineEOL() +{ + char line[STIL_MAX_LINE_SIZE+5]; + int i=0; + + CERR_STIL_DEBUG << "detEOL() called" << endl; + + if (stilFile.fail()) { + CERR_STIL_DEBUG << "detEOL() open failed" << endl; + return false; + } + + stilFile.seekg(0); + + // Read in the first line from stilFile to determine what the + // EOL character is (it can be different from OS to OS). + + stilFile.read(line, sizeof(line)-1); + line[sizeof(line)-1] = '\0'; + + CERR_STIL_DEBUG << "detEOL() line=" << line << endl; + + // Now find out what the EOL char is (or are). + + STIL_EOL = '\0'; + STIL_EOL2 = '\0'; + + while (line[i] != '\0') { + if ((line[i] == 0x0d) || (line[i] == 0x0a)) { + if (STIL_EOL == '\0') { + STIL_EOL = line[i]; + } + else { + if (line[i] != STIL_EOL) { + STIL_EOL2 = line[i]; + } + } + } + i++; + } + + if (STIL_EOL == '\0') { + // Something is wrong - no EOL-like char was found. + CERR_STIL_DEBUG << "detEOL() no EOL found" << endl; + return false; + } + + CERR_STIL_DEBUG << "detEOL() EOL1=0x" << hex << (int) STIL_EOL << " EOL2=0x" << hex << (int) STIL_EOL2 << dec << endl; + + return true; +} + +bool +STIL::getDirs(ifstream& inFile, dirList *dirs, bool isSTILFile) +{ + char line[STIL_MAX_LINE_SIZE]; + int i=0; + size_t j; + bool newDir; + dirList *prevDir; + + if (isSTILFile) { + newDir = false; + } + else { + newDir = true; + } + + prevDir = dirs; + + CERR_STIL_DEBUG << "getDirs() called" << endl; + + inFile.seekg(0); + + while (inFile.good()) { + getStilLine(inFile, line); + + if (!isSTILFile) CERR_STIL_DEBUG << line << '\n'; + + // Try to extract STIL's version number if it's not done, yet. + + if (isSTILFile && (STILVersion == 0.0)) { + if (strncmp(line, "# STIL v", 9) == 0) { + + // Get the version number + STILVersion = atof(line+9); + + // Put it into the string, too. +#ifdef HAVE_SNPRINTF + snprintf(line, STIL_MAX_LINE_SIZE-1, "SID Tune Information List (STIL) v%4.2f\n", STILVersion); +#else + sprintf(line, "SID Tune Information List (STIL) v%4.2f\n", STILVersion); +#endif + line[STIL_MAX_LINE_SIZE-1] = '\0'; + strncat(versionString, line, 2*STIL_MAX_LINE_SIZE-1); + versionString[2*STIL_MAX_LINE_SIZE-1] = '\0'; + + CERR_STIL_DEBUG << "getDirs() STILVersion=" << STILVersion << endl; + + continue; + } + } + + // Search for the start of a dir separator first. + + if (isSTILFile && !newDir && (MYSTRNICMP(line, "### ", 4) == 0)) { + newDir = true; + continue; + } + + // Is this the start of an entry immediately following a dir separator? + + if (newDir && (*line == '/')) { + + // Get the directory only + j = strrchr(line,'/')-line+1; + + if (!isSTILFile) { + // Compare it to the last stored dirname + if (i==0) { + newDir = true; + } + else if (MYSTRNICMP(prevDir->dirName, line, j) != 0) { + newDir = true; + } + else { + newDir = false; + } + } + + // Store the info + if (newDir) { + + dirs->dirName = new char [j+1]; + strncpy(dirs->dirName, line, j); + *((dirs->dirName)+j) = '\0'; + + dirs->position = inFile.tellg()-(streampos)strlen(line)-1L; + + CERR_STIL_DEBUG << "getDirs() i=" << i << ", dirName=" << dirs->dirName << ", pos=" << dirs->position << endl; + + prevDir = dirs; + + // We create the entries one ahead. This way we also assure that + // the last entry will always be a NULL (empty) entry. + dirs->next = createOneDir(); + dirs = dirs->next; + + i++; + } + + if (isSTILFile) { + newDir = false; + } + else { + newDir = true; + } + } + } + + if (i == 0) { + // No entries found - something is wrong. + // NOTE: It's perfectly valid to have a BUGlist.txt file with no + // entries in it! + CERR_STIL_DEBUG << "getDirs() no dirs found" << endl; + return false; + } + + CERR_STIL_DEBUG << "getDirs() successful" << endl; + + return true; +} + +bool +STIL::positionToEntry(const char *entryStr, ifstream& inFile, dirList *dirs) +{ + size_t pathLen; + size_t entryStrLen; + char line[STIL_MAX_LINE_SIZE]; + int temp; + bool foundIt = false; + bool globComm = false; + char *chrptr; + + CERR_STIL_DEBUG << "pos2Entry() called, entryStr=" << entryStr << endl; + + inFile.seekg(0); + + // Get the dirpath. + + chrptr = strrchr((char *)entryStr, '/'); + + // If no slash was found, something is screwed up in the entryStr. + + if (chrptr == NULL) { + return false; + } + + pathLen = chrptr-entryStr+1; + + // Determine whether a section-global comment is asked for. + + entryStrLen = strlen(entryStr); + if (pathLen == entryStrLen) { + globComm = true; + } + + // Find it in the table. + + while (dirs->dirName) { + if ((MYSTRNICMP(dirs->dirName, entryStr, pathLen) == 0) && + (strlen(dirs->dirName) == pathLen)) { + CERR_STIL_DEBUG << "pos2Entry() found dir, dirName=" << dirs->dirName << endl; + foundIt = true; + break; + } + dirs = dirs->next; + } + + if (!foundIt) { + // The directory was not found. + CERR_STIL_DEBUG << "pos2Entry() did not find the dir" << endl; + return false; + } + + // Jump to the first entry of this section. + inFile.seekg(dirs->position); + foundIt = false; + + // Now find the desired entry + + do { + getStilLine(inFile, line); + if (inFile.eof()) { + break; + } + + // Check if it is the start of an entry + + if (*line == '/') { + + if (MYSTRNICMP(dirs->dirName, line, pathLen) != 0) { + // We are outside the section - get out of the loop, + // which will fail the search. + break; + } + + // Check whether we need to find a section-global comment or + // a specific entry. + + if (globComm || (STILVersion > 2.59)) { + temp = MYSTRICMP(line, entryStr); + } + else { + // To be compatible with older versions of STIL, which may have + // the tune designation on the first line of a STIL entry + // together with the pathname. + temp = MYSTRNICMP(line, entryStr, entryStrLen); + } + + CERR_STIL_DEBUG << "pos2Entry() line=" << line << endl; + + if (temp == 0) { + // Found it! + foundIt = true; + } + + } + } while (!foundIt); + + if (foundIt) { + // Reposition the file pointer back to the start of the entry. + inFile.seekg(inFile.tellg()-(streampos)strlen(line)-1L); + CERR_STIL_DEBUG << "pos2Entry() entry found" << endl; + return true; + } + else { + CERR_STIL_DEBUG << "pos2Entry() entry not found" << endl; + return false; + } +} + +void +STIL::readEntry(ifstream& inFile, char *buffer) +{ + char line[STIL_MAX_LINE_SIZE]; + + do { + getStilLine(inFile, line); + strcat(buffer, line); + if (*line != '\0') { + strncat(buffer, "\n", 2); + } + } while (*line != '\0'); +} + +bool +STIL::getField(char *result, char *buffer, int tuneNo, STILField field) +{ + CERR_STIL_DEBUG << "getField() called, buffer=" << buffer << ", rest=" << tuneNo << "," << field << endl; + + // Clean out the result buffer first. + *result = '\0'; + + // Position pointer to the first char beyond the file designation. + + char *start = strchr(buffer, '\n'); + start++; + + // Check whether this is a NULL entry or not. + + if (*start == '\0') { + CERR_STIL_DEBUG << "getField() null entry" << endl; + return false; + } + + // Is this a multitune entry? + char *firstTuneNo = strstr(start, "(#"); + + // This is a tune designation only if the previous char was + // a newline (ie. if the "(#" is on the beginning of a line). + if ((firstTuneNo != NULL) && (*(firstTuneNo-1) != '\n')) { + firstTuneNo = NULL; + } + + if (firstTuneNo == NULL) { + + //-------------------// + // SINGLE TUNE ENTRY // + //-------------------// + + // Is the first thing in this STIL entry the COMMENT? + + char *temp = strstr(start, _COMMENT_STR); + char *temp2 = NULL; + + // Search for other potential fields beyond the COMMENT. + if (temp == start) { + temp2 = strstr(start, _NAME_STR); + if (temp2 == NULL) { + temp2 = strstr(start, _AUTHOR_STR); + if (temp2 == NULL) { + temp2 = strstr(start, _TITLE_STR); + if (temp2 == NULL) { + temp2 = strstr(start, _ARTIST_STR); + } + } + } + } + + if (temp == start) { + + // Yes. So it's assumed to be a file-global comment. + + CERR_STIL_DEBUG << "getField() single-tune entry, COMMENT only" << endl; + + if ((tuneNo == 0) && ((field == all) || ((field == comment) && (temp2 == NULL))) ) { + + // Simply copy the stuff in. + + strncpy(result, start, STIL_MAX_ENTRY_SIZE-1); + result[STIL_MAX_ENTRY_SIZE-1] = '\0'; + CERR_STIL_DEBUG << "getField() copied to resultbuf" << endl; + return true; + } + + else if ((tuneNo == 0) && (field == comment)) { + + // Copy just the comment. + + strncpy(result, start, temp2-start); + *(result+(temp2-start)) = '\0'; + CERR_STIL_DEBUG << "getField() copied to just the COMMENT to resultbuf" << endl; + return true; + } + + else if ((tuneNo == 1) && (temp2 != NULL)) { + + // A specific field was asked for. + + CERR_STIL_DEBUG << "getField() copying COMMENT to resultbuf" << endl; + return getOneField(result, temp2, temp2+strlen(temp2), field); + } + + else { + + // Anything else is invalid as of v2.00. + + CERR_STIL_DEBUG << "getField() invalid parameter combo: single tune, tuneNo=" << tuneNo << ", field=" << field << endl; + return false; + } + } + else { + + // No. Handle it as a regular entry. + + CERR_STIL_DEBUG << "getField() single-tune regular entry" << endl; + + if ((field == all) && ((tuneNo == 0) || (tuneNo == 1))) { + + // The complete entry was asked for. Simply copy the stuff in. + + strncpy(result, start, STIL_MAX_ENTRY_SIZE-1); + result[STIL_MAX_ENTRY_SIZE-1] = '\0'; + CERR_STIL_DEBUG << "getField() copied to resultbuf" << endl; + return true; + } + + else if (tuneNo == 1) { + + // A specific field was asked for. + + CERR_STIL_DEBUG << "getField() copying COMMENT to resultbuf" << endl; + return getOneField(result, start, start+strlen(start), field); + } + + else { + + // Anything else is invalid as of v2.00. + + CERR_STIL_DEBUG << "getField() invalid parameter combo: single tune, tuneNo=" << tuneNo << ", field=" << field << endl; + return false; + } + } + } + else { + + //-------------------// + // MULTITUNE ENTRY + //-------------------// + + CERR_STIL_DEBUG << "getField() multitune entry" << endl; + + // Was the complete entry asked for? + + if (tuneNo == 0) { + + switch (field) { + + case all: + + // Yes. Simply copy the stuff in. + + strncpy(result, start, STIL_MAX_ENTRY_SIZE-1); + result[STIL_MAX_ENTRY_SIZE-1] = '\0'; + CERR_STIL_DEBUG << "getField() copied all to resultbuf" << endl; + return true; + break; + + case comment: + + // Only the file-global comment field was asked for. + + if (firstTuneNo != start) { + CERR_STIL_DEBUG << "getField() copying file-global comment to resultbuf" << endl; + return getOneField(result, start, firstTuneNo, comment); + } + else { + CERR_STIL_DEBUG << "getField() no file-global comment" << endl; + return false; + } + break; + + default: + + // If a specific field other than a comment is + // asked for tuneNo=0, this is illegal. + + CERR_STIL_DEBUG << "getField() invalid parameter combo: multitune, tuneNo=" << tuneNo << ", field=" << field << endl; + return false; + break; + } + } + + char *myTuneNo; + char tuneNoStr[8]; + + // Search for the requested tune number. + +#ifdef HAVE_SNPRINTF + snprintf(tuneNoStr, 7, "(#%d)", tuneNo); +#else + sprintf(tuneNoStr, "(#%d)", tuneNo); +#endif + tuneNoStr[7] = '\0'; + myTuneNo = strstr(start, tuneNoStr); + + if (myTuneNo != NULL) { + + // We found the requested tune number. + // Set the pointer beyond it. + myTuneNo = strchr(myTuneNo, '\n') + 1; + + // Where is the next one? + + char *nextTuneNo = strstr(myTuneNo, "\n(#"); + if (nextTuneNo == NULL) { + // There is no next one - set pointer to end of entry. + nextTuneNo = start+strlen(start); + } + else { + // The search included the \n - go beyond it. + nextTuneNo++; + } + + // Put the desired fields into the result (which may be 'all'). + + CERR_STIL_DEBUG << "getField() myTuneNo=" << myTuneNo << ", nextTuneNo=" << nextTuneNo << endl; + return getOneField(result+strlen(result), myTuneNo, nextTuneNo, field); + } + + else { + CERR_STIL_DEBUG << "getField() nothing found" << endl; + return false; + } + } +} + +bool +STIL::getOneField(char *result, char *start, char *end, STILField field) +{ + char *temp = NULL; + + // Sanity checking + + if ((end < start) || (*(end-1) != '\n')) { + *result = '\0'; + CERR_STIL_DEBUG << "getOneField() illegal parameters" << endl; + return false; + } + + CERR_STIL_DEBUG << "getOneField() called, start=" << start << ", rest=" << field << endl; + + switch (field) { + + case all: + + strncat(result, start, end-start); + return true; + break; + + case name: + + temp = strstr(start, _NAME_STR); + break; + + case author: + + temp = strstr(start, _AUTHOR_STR); + break; + + case title: + + temp = strstr(start, _TITLE_STR); + break; + + case artist: + + temp = strstr(start, _ARTIST_STR); + break; + + case comment: + + temp = strstr(start, _COMMENT_STR); + break; + + default: + + break; + } + + // If the field was not found or it is not in between 'start' + // and 'end', it is declared a failure. + + if ((temp == NULL) || (temp < start) || (temp > end)) { + *result = '\0'; + return false; + } + + // Search for the end of this field. This is done by finding + // where the next field starts. + + char *nextName, *nextAuthor, *nextTitle, *nextArtist, *nextComment, *nextField; + + nextName = strstr(temp+1, _NAME_STR); + nextAuthor = strstr(temp+1, _AUTHOR_STR); + nextTitle = strstr(temp+1, _TITLE_STR); + nextArtist = strstr(temp+1, _ARTIST_STR); + nextComment = strstr(temp+1, _COMMENT_STR); + + // If any of these fields is beyond 'end', they are ignored. + + if ((nextName != NULL) && (nextName >= end)) { + nextName = NULL; + } + + if ((nextAuthor != NULL) && (nextAuthor >= end)) { + nextAuthor = NULL; + } + + if ((nextTitle != NULL) && (nextTitle >= end)) { + nextTitle = NULL; + } + + if ((nextArtist != NULL) && (nextArtist >= end)) { + nextArtist = NULL; + } + + if ((nextComment != NULL) && (nextComment >= end)) { + nextComment = NULL; + } + + // Now determine which one is the closest to our field - that one + // will mark the end of the required field. + + nextField = nextName; + + if (nextField == NULL) { + nextField = nextAuthor; + } + else if ((nextAuthor != NULL) && (nextAuthor < nextField)) { + nextField = nextAuthor; + } + + if (nextField == NULL) { + nextField = nextTitle; + } + else if ((nextTitle != NULL) && (nextTitle < nextField)) { + nextField = nextTitle; + } + + if (nextField == NULL) { + nextField = nextArtist; + } + else if ((nextArtist != NULL) && (nextArtist < nextField)) { + nextField = nextArtist; + } + + if (nextField == NULL) { + nextField = nextComment; + } + else if ((nextComment != NULL) && (nextComment < nextField)) { + nextField = nextComment; + } + + if (nextField == NULL) { + nextField = end; + } + + // Now nextField points to the last+1 char that should be copied to + // result. Do that. + + strncat(result, temp, nextField-temp); + return true; +} + +void +STIL::getStilLine(ifstream& infile, char *line) +{ + if (STIL_EOL2 != '\0') { + + // If there was a remaining EOL char from the previous read, eat it up. + + char temp = infile.peek(); + + if ((temp == 0x0d) || (temp == 0x0a)) { + infile.get(temp); + } + } + + infile.getline(line, STIL_MAX_LINE_SIZE, STIL_EOL); +} + +void +STIL::deleteDirList(dirList *dirs) +{ + dirList *ptr; + + do { + ptr = dirs->next; + if (dirs->dirName) { + delete[] dirs->dirName; + } + delete dirs; + dirs = ptr; + } while (ptr); +} + +void +STIL::copyDirList(dirList *toPtr, dirList *fromPtr) +{ + // Big assumption: the first element exists on both linked lists! + + do { + toPtr->position = fromPtr->position; + + if (fromPtr->dirName) { + toPtr->dirName = new char [strlen(fromPtr->dirName)+1]; + strcpy(toPtr->dirName, fromPtr->dirName); + } + else { + toPtr->dirName = NULL; + } + + if (fromPtr->next) { + toPtr->next = createOneDir(); + } + else { + toPtr->next = NULL; + } + + fromPtr = fromPtr->next; + toPtr = toPtr->next; + + } while (fromPtr); +} + +STIL::dirList * +STIL::createOneDir(void) +{ + dirList *ptr; + + ptr = new dirList; + ptr->dirName = NULL; + ptr->position = 0; + ptr->next = NULL; + + return ptr; +} + +#endif // _STIL diff --git a/PSID/libpsid64/stilview/stil.h b/PSID/libpsid64/stilview/stil.h new file mode 100644 index 00000000..db068979 --- /dev/null +++ b/PSID/libpsid64/stilview/stil.h @@ -0,0 +1,468 @@ +// +// STIL class +// +// AUTHOR: LaLa +// Email : LaLa@C64.org +// Copyright (C) 1998, 2002 by LaLa +// + +// +// Given the location of HVSC this class can extract STIL information for a +// given tune of a given SID file. (Sounds simple, huh?) +// +// PLEASE, READ THE ACCOMPANYING README.TXT FILE BEFORE PROCEEDING!!!! +// + +#ifndef _STIL_H +#define _STIL_H + +#include +#include // For atof() and size_t +#include "stilcomm.h" + +class STIL { + + public: + + // Enum to use for asking for specific fields. + enum STILField { + all, + name, + author, + title, + artist, + comment + }; + + // Enum that describes the possible errors this class may encounter. + enum STILerror { + NO_STIL_ERROR = 0, + BUG_OPEN, // INFO ONLY: failed to open BUGlist.txt. + WRONG_DIR, // INFO ONLY: path was not within HVSC base dir. + NOT_IN_STIL, // INFO ONLY: requested entry was not found in STIL.txt. + NOT_IN_BUG, // INFO ONLY: requested entry was not found in BUGlist.txt. + WRONG_ENTRY, // INFO ONLY: section-global comment was asked for with get*Entry(). + CRITICAL_STIL_ERROR = 10, + BASE_DIR_LENGTH, // The length of the HVSC base dir was wrong (empty string?) + STIL_OPEN, // Failed to open STIL.txt. + NO_EOL, // Failed to determine EOL char(s). + NO_STIL_DIRS, // Failed to get sections (subdirs) when parsing STIL.txt. + NO_BUG_DIRS // Failed to get sections (subdirs) when parsing BUGlist.txt. + }; + + // To turn debug output on + bool STIL_DEBUG; + + //----// + + // CONSTRUCTOR + // Allocates necessary memory. + STIL(); + + // DESTRUCTOR + // Deallocates the necessary memory. + ~STIL(); + + // + // getVersion() + // + // FUNCTION: Returns a formatted string telling what the version + // number is for the STIL class and other info. + // If it is called after setBaseDir(), the string also + // has the STIL.txt file's version number in it. + // ARGUMENTS: + // NONE + // RETURNS: + // char * - printable formatted string with version and copyright + // info + // (It's kinda dangerous to return a pointer that points + // to an internal structure, but I trust you. :) + // + char *getVersion(); + + // + // getVersionNo() + // + // FUNCTION: Returns a floating number telling what the version + // number is of this STIL class. + // ARGUMENTS: + // NONE + // RETURNS: + // float - version number + float getVersionNo(); + + // + // setBaseDir() + // + // FUNCTION: Tell the object where the HVSC base directory is - it + // figures that the STIL should be in /DOCUMENTS/STIL.txt + // and that the BUGlist should be in /DOCUMENTS/BUGlist.txt. + // It should not matter whether the path is given in UNIX, + // WinDOS, or Mac format (ie. '\' vs. '/' vs. ':') + // ARGUMENTS: + // pathToHVSC = HVSC base directory in your machine's format + // RETURNS: + // false - Problem opening or parsing STIL/BUGlist + // true - All okay + bool setBaseDir(const char *pathToHVSC); + + // + // getSTILVersionNo() + // + // FUNCTION: Returns a floating number telling what the version + // number is of the STIL.txt file. + // To be called only after setBaseDir()! + // ARGUMENTS: + // NONE + // RETURNS: + // float - version number (0.0 if setBaseDir() was not called, yet) + // + float getSTILVersionNo(); + + // + // getEntry() + // + // FUNCTION: Given an HVSC pathname, a tune number and a + // field designation, it returns a formatted string that + // contains the STIL field for the tune number (if exists). + // If it doesn't exist, returns a NULL. + // ARGUMENTS: + // relPathToEntry = relative to the HVSC base dir, starting with + // a slash + // tuneNo = song number within the song (default=0). + // field = which field to retrieve (default=all). + // + // What the possible combinations of tuneNo and field represent: + // + // - tuneNo = 0, field = all : all of the STIL entry is returned. + // - tuneNo = 0, field = comment : the file-global comment is returned. + // (For single-tune entries, this returns nothing!) + // - tuneNo = 0, field = : INVALID! (NULL is returned) + // - tuneNo != 0, field = all : all fields of the STIL entry for the + // given tune number are returned. (For single-tune entries, this is + // equivalent to saying tuneNo = 0, field = all.) + // However, the file-global comment is *NOT* returned with it any + // more! (Unlike in versions before v2.00.) It led to confusions: + // eg. when a comment was asked for tune #3, it returned the + // file-global comment even if there was no specific entry for tune #3! + // - tuneNo != 0, field = : the specific field of the specific + // tune number is returned. If the tune number doesn't exist (eg. if + // tuneNo=2 for single-tune entries, or if tuneNo=2 when there's no + // STIL entry for tune #2 in a multitune entry), returns NULL. + // + // NOTE: For older versions of STIL (older than v2.59) the tuneNo and + // field parameters are ignored and are assumed to be tuneNo=0 and + // field=all to maintain backwards compatibility. + // + // RETURNS: + // NULL - if there's absolutely no STIL entry for the tune + // char * - pointer to a printable formatted string containing + // the STIL entry + // (It's kinda dangerous to return a pointer that points + // to an internal structure, but I trust you. :) + // + char *getEntry(const char *relPathToEntry, int tuneNo=0, STILField field=all); + + // Same as above, but with an absolute path given + // given in your machine's format. + // + char *getAbsEntry(const char *absPathToEntry, int tuneNo=0, STILField field=all); + + // + // getGlobalComment() + // + // FUNCTION: Given an HVSC pathname and tune number it returns a + // formatted string that contains the section-global + // comment for the tune number (if it exists). If it + // doesn't exist, returns a NULL. + // ARGUMENTS: + // relPathToEntry = relative to the HVSC base dir starting with + // a slash + // RETURNS: + // NULL - if there's absolutely no section-global comment + // for the tune + // char * - pointer to a printable formatted string containing + // the section-global comment + // (It's kinda dangerous to return a pointer that points + // to an internal structure, but I trust you. :) + // + char *getGlobalComment(const char *relPathToEntry); + + // Same as above, but with an absolute path + // given in your machine's format. + // + char *getAbsGlobalComment(const char *absPathToEntry); + + // + // getBug() + // + // FUNCTION: Given an HVSC pathname and tune number it returns a + // formatted string that contains the BUG entry for the + // tune number (if exists). If it doesn't exist, returns + // a NULL. + // ARGUMENTS: + // relPathToEntry = relative to the HVSC base dir starting with + // a slash + // tuneNo = song number within the song (default=0) + // If tuneNo=0, returns all of the BUG entry. + // + // NOTE: For older versions of STIL (older than v2.59) tuneNo is + // ignored and is assumed to be 0 to maintain backwards + // compatibility. + // + // RETURNS: + // NULL - if there's absolutely no BUG entry for the tune + // char * - pointer to a printable formatted string containing + // the BUG entry + // (It's kinda dangerous to return a pointer that points + // to an internal structure, but I trust you. :) + // + char *getBug(const char *relPathToEntry, int tuneNo=0); + + // Same as above, but with an absolute path + // given in your machine's format. + // + char *getAbsBug(const char *absPathToEntry, int tuneNo=0); + + // + // getError() + // + // FUNCTION: Returns a specific error number identifying the problem + // that happened at the last invoked public method. + // ARGUMENTS: + // NONE + // RETURNS: + // STILerror - an enumerated error value + // + inline STILerror getError() {return (lastError);} + + // + // hasCriticalError() + // + // FUNCTION: Returns true if the last error encountered was critical + // (ie. not one that the STIL class can recover from). + // ARGUMENTS: + // NONE + // RETURNS: + // bool - true if the last error encountered was critical + // + inline bool hasCriticalError() { + return ( (lastError >= CRITICAL_STIL_ERROR) ? true : false); + } + + // + // getErrorStr() + // + // FUNCTION: Returns an ASCII error string containing the + // description of the error that happened at the last + // invoked public method. + // ARGUMENTS: + // NONE + // RETURNS: + // char * - pointer to string with the error description + // + inline const char *getErrorStr() {return (STIL_ERROR_STR[lastError]);} + + private: + + // Version number/copyright string + char versionString[2*STIL_MAX_LINE_SIZE]; + + // STIL.txt's version number + float STILVersion; + + // Base dir + char *baseDir; + size_t baseDirLength; + + // File handles + std::ifstream stilFile; + std::ifstream bugFile; + + // Linked list of sections (subdirs) for easier positioning. + // NOTE: There's always at least one (empty) member on these + // lists! (The pointers will never be NULL.) + struct dirList { + char *dirName; + std::streampos position; + struct dirList *next; + } *stilDirs, *bugDirs; + + // This tells us what the line delimiter is in STIL.txt. + // (It may be two chars!) + char STIL_EOL; + char STIL_EOL2; + + // Error number of the last error that happened. + STILerror lastError; + + // Error strings containing the description of the possible errors in STIL. + static const char *STIL_ERROR_STR[]; + + //////////////// + + // The last retrieved entry + char entrybuf[STIL_MAX_ENTRY_SIZE]; + + // The last retrieved section-global comment + char globalbuf[STIL_MAX_ENTRY_SIZE]; + + // The last retrieved BUGentry + char bugbuf[STIL_MAX_ENTRY_SIZE]; + + // Buffers to hold the resulting strings + char resultEntry[STIL_MAX_ENTRY_SIZE]; + char resultBug[STIL_MAX_ENTRY_SIZE]; + + //////////////// + + // + // determineEOL() + // + // FUNCTION: Determines what the EOL char is (or are) from STIL.txt. + // It is assumed that BUGlist.txt will use the same EOL. + // ARGUMENTS: + // NONE + // RETURNS: + // false - something went wrong + // true - everything is okay + // + bool determineEOL(); + + // + // getDirs() + // + // FUNCTION: Populates the given dirList array with the directories + // obtained from 'inFile' for faster positioning within + // 'inFile'. + // ARGUMENTS: + // inFile - where to read the directories from + // dirs - the dirList array that should be populated with the + // directory list + // isSTILFile - is this the STIL or the BUGlist we are parsing + // RETURNS: + // false - No entries were found or otherwise failed to process + // inFile + // true - everything is okay + // + bool getDirs(std::ifstream& inFile, dirList *dirs, bool isSTILFile); + + // + // positionToEntry() + // + // FUNCTION: Positions the file pointer to the given entry in 'inFile' + // using the 'dirs' dirList for faster positioning. + // ARGUMENTS: + // entryStr - the entry to position to + // inFile - position the file pointer in this file + // dirs - the list of dirs in inFile for easier positioning + // RETURNS: + // true - if successful + // false - otherwise + bool positionToEntry(const char *entryStr, std::ifstream& inFile, dirList *dirs); + + // + // readEntry() + // + // FUNCTION: Reads the entry from 'inFile' into 'buffer'. 'inFile' should + // already be positioned to the entry to be read. + // ARGUMENTS: + // inFile - filehandle of file to read from + // entryStr - the entry needed to be read + // buffer - where to put the result to + // RETURNS: + // NONE + void readEntry(std::ifstream& inFile, char *buffer); + + // + // getField() + // + // FUNCTION: Given a STIL formatted entry in 'buffer', a tune number, + // and a field designation, it returns the requested + // STIL field into 'result'. + // If field=all, it also puts the file-global comment (if it exists) + // as the first field into 'result'. + // ARGUMENTS: + // result - where to put the resulting string to (if any) + // buffer - pointer to the first char of what to search for + // the field. Should be a buffer in standard STIL + // format. + // tuneNo - song number within the song (default=0) + // field - which field to retrieve (default=all). + // RETURNS: + // false - if nothing was put into 'result' + // true - 'result' has the resulting field + bool getField(char *result, char *buffer, int tuneNo=0, STILField field=all); + + // + // getOneField() + // + // FUNCTION: + // ARGUMENTS: + // result - where to put the resulting string to (if any) + // start - pointer to the first char of what to search for + // the field. Should be a buffer in standard STIL + // format. + // end - pointer to the last+1 char of what to search for + // the field. ('end-1' should be a '\n'!) + // field - which specific field to retrieve + // RETURNS: + // false - if nothing was put into 'result' + // true - 'result' has the resulting field + bool getOneField(char *result, char *start, char *end, STILField field); + + // + // getStilLine() + // + // FUNCTION: Extracts one line from 'infile' to 'line[]'. The end of + // the line is marked by endOfLineChar. Also eats up + // additional EOL-like chars. + // ARGUMENTS: + // infile - filehandle (streampos should already be positioned + // to the start of the desired line) + // line - char array to put the line into + // RETURNS: + // NONE + void getStilLine(std::ifstream& infile, char *line); + + // + // deleteDirList() + // + // FUNCTION: Deletes *all* of the linked list of dirnames. Assumes that + // there is at least one element on the linked list! + // ARGUMENTS: + // dirs - pointer to the head of the linked list to be deleted. + // RETURNS: + // NONE (Maybe it should return a bool for error-checking purposes?) + // + void deleteDirList(dirList *dirs); + + // + // copyDirList() + // + // FUNCTION: Copies the linked list of dirnames from one linked list + // to another. It creates new dirlist entries in the + // destination list as needed. Assumes that there is at least + // one element on the source *and* destination linked lists! + // ARGUMENTS: + // toPtr - pointer to the head of the destination linked list + // fromPtr - pointer to the head of the source linked list + // RETURNS: + // NONE (Maybe it should return a bool for error-checking purposes?) + // + void copyDirList(dirList *toPtr, dirList *fromPtr); + + // + // createOneDir() + // + // FUNCTION: Creates a new dirlist entry (allocates memory for it), and + // returns a pointer to this new entry. + // ARGUMENTS: + // NONE + // RETURNS: + // dirList * - pointer to the newly created entry + // + inline dirList *createOneDir(void); +}; + +#endif // _STIL_H diff --git a/PSID/libpsid64/stilview/stilcomm.cpp b/PSID/libpsid64/stilview/stilcomm.cpp new file mode 100644 index 00000000..a23cfe9b --- /dev/null +++ b/PSID/libpsid64/stilview/stilcomm.cpp @@ -0,0 +1,59 @@ +// +// STIL - Common stuff +// + +// +// Common functions used for STIL handling. +// See stilcomm.h for prologs. +// + +#ifndef _STILCOMM +#define _STILCOMM + +#include "stildefs.h" +#include "stil.h" + +const char *STIL::STIL_ERROR_STR[] = { + "No error.", + "Failed to open BUGlist.txt.", + "Base dir path is not the HVSC base dir path.", + "The entry was not found in STIL.txt.", + "The entry was not found in BUGlist.txt.", + "A section-global comment was asked for in the wrong way.", + "", + "", + "", + "", + "CRITICAL ERROR", + "Incorrect HVSC base dir length!", + "Failed to open STIL.txt!", + "Failed to determine EOL from STIL.txt!", + "No STIL sections were found in STIL.txt!", + "No STIL sections were found in BUGlist.txt!" +}; + +void convertSlashes(char *str); +void convertToSlashes(char *str); + +void convertSlashes(char *str) +{ + while (*str) { + if (*str == '/') { + *str = SLASH; + } + str++; + } +} + +void convertToSlashes(char *str) +{ + while (*str) { + if (*str == SLASH) { + *str = '/' ; + } + str++; + } +} + + +#endif //_STILCOMM diff --git a/PSID/libpsid64/stilview/stilcomm.h b/PSID/libpsid64/stilview/stilcomm.h new file mode 100644 index 00000000..4ae9de6e --- /dev/null +++ b/PSID/libpsid64/stilview/stilcomm.h @@ -0,0 +1,34 @@ +// +// STIL Common header +// + +// +// Contains some definitions common to STIL +// + +#ifndef _STILCOMM_H +#define _STILCOMM_H + +#include "stildefs.h" + +// convertSlashes() +// +// FUNCTION: Converts slashes to the one the OS uses to access files. +// ARGUMENTS: +// str - what to convert +// RETURNS: +// NONE +// +extern void convertSlashes(char *str); + +// convertToSlashes() +// +// FUNCTION: Converts OS specific dir separators to slashes. +// ARGUMENTS: +// str - what to convert +// RETURNS: +// NONE +// +extern void convertToSlashes(char *str); + +#endif // _STILCOMM_H diff --git a/PSID/libpsid64/stilview/stildefs.h b/PSID/libpsid64/stilview/stildefs.h new file mode 100644 index 00000000..90c7d686 --- /dev/null +++ b/PSID/libpsid64/stilview/stildefs.h @@ -0,0 +1,85 @@ +// +// STIL - Common defines +// + +#ifndef _STILDEFS_H +#define _STILDEFS_H + +#if defined(__linux__) || defined(__FreeBSD__) || defined(solaris2) || defined(sun) || defined(sparc) || defined(sgi) || defined(__APPLE__) +#define UNIX +#endif + +#if defined(__MACOS__) +#define MAC +#endif + +#if defined(__amigaos__) +#define AMIGA +#endif + +// +// Here you should define: +// - what the pathname separator is on your system (attempted to be defined +// automatically), +// - what function compares strings case-insensitively, +// - what function compares portions of strings case-insensitively. +// + +#ifdef UNIX + #define SLASH '/' +#elif MAC + #define SLASH ':' +#elif AMIGA + #define SLASH '/' +#else // WinDoze + #define SLASH '\\' +#endif + +// Define which one of the following two is supported on your system. +//#define HAVE_STRCASECMP 1 + #define HAVE_STRICMP 1 + +// Define which one of the following two is supported on your system. +//#define HAVE_STRNCASECMP 1 + #define HAVE_STRNICMP 1 + +// Now let's do the actual definitions. + +#ifdef HAVE_STRCASECMP +#define MYSTRICMP strcasecmp +#elif HAVE_STRICMP +#define MYSTRICMP stricmp +#else +#error Neither strcasecmp nor stricmp is available. +#endif + +#ifdef HAVE_STRNCASECMP +#define MYSTRNICMP strncasecmp +#elif HAVE_STRNICMP +#define MYSTRNICMP strnicmp +#else +#error Neither strncasecmp nor strnicmp is available. +#endif + +// These are the hardcoded STIL/BUG field names. +const char _NAME_STR[]=" NAME: "; +const char _AUTHOR_STR[]=" AUTHOR: "; +const char _TITLE_STR[]=" TITLE: "; +const char _ARTIST_STR[]=" ARTIST: "; +const char _COMMENT_STR[]="COMMENT: "; +const char _BUG_STR[]="BUG: "; + +// Maximum size of a single line in STIL - also accounts for some extra +// padding, just in case. +#define STIL_MAX_LINE_SIZE 91 + +// Maximum size of a single STIL entry (in bytes). +#define STIL_MAX_ENTRY_SIZE STIL_MAX_LINE_SIZE*100 + +// HVSC path to STIL. +const char PATH_TO_STIL[]="/DOCUMENTS/STIL.txt"; + +// HVSC path to BUGlist. +const char PATH_TO_BUGLIST[]="/DOCUMENTS/BUGlist.txt"; + +#endif // _STILDEFS_H diff --git a/PSID/libpsid64/theme.cpp b/PSID/libpsid64/theme.cpp new file mode 100644 index 00000000..1b4711b8 --- /dev/null +++ b/PSID/libpsid64/theme.cpp @@ -0,0 +1,447 @@ +/* + $Id$ + + psid64 - create a C64 executable from a PSID file + Copyright (C) 2014 Roland Hermans + + this code has been modified for integration into the Sidekick64 software + (the modifications are mostly removing everything that does not compile with vanilla Circle, + I really feel sorry for having disfigured this code, please refer to the original repository + if you want to get the real and decent psid64 version) + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +////////////////////////////////////////////////////////////////////////////// +// I N C L U D E S +////////////////////////////////////////////////////////////////////////////// + +#include "theme.h" + + +////////////////////////////////////////////////////////////////////////////// +// G L O B A L D A T A +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// L O C A L D E F I N I T I O N S +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// L O C A L D A T A +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// L O C A L F U N C T I O N S +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// G L O B A L F U N C T I O N S +////////////////////////////////////////////////////////////////////////////// + +DriverTheme::DriverTheme( + int borderColor, int backgroundColor, int rasterTimeColor, + int titleColor, const int* lineColors, + int fieldNameColor, int fieldColonColor, int fieldValueColor, + int legendColor, + int progressBarFillColor, int progressBarBackgroundColor, + int scrollerColor, const int* scrollerColors, + const int* footerColors) : + m_borderColor(borderColor), + m_backgroundColor(backgroundColor), + m_rasterTimeColor(rasterTimeColor), + m_titleColor(titleColor), + m_fieldNameColor(fieldNameColor), + m_fieldColonColor(fieldColonColor), + m_fieldValueColor(fieldValueColor), + m_legendColor(legendColor), + m_progressBarFillColor(progressBarFillColor), + m_progressBarBackgroundColor(progressBarBackgroundColor), + m_scrollerColor(scrollerColor) +{ + for (int i = 0; i < NUM_LINE_COLORS; ++i) + { + m_lineColors[i] = lineColors[i]; + } + for (int i = 0; i < NUM_SCROLLER_COLORS; ++i) + { + m_scrollerColors[i] = scrollerColors[i]; + } + for (int i = 0; i < NUM_FOOTER_COLORS; ++i) + { + m_footerColors[i] = footerColors[i]; + } +} + + +DriverTheme::~DriverTheme() +{ + // empty +} + + +DriverTheme* DriverTheme::createBlueTheme() +{ + static const int lineColors[NUM_LINE_COLORS] = + {0x09, 0x0b, 0x08, 0x0c, 0x0a, 0x0f, 0x07, 0x01, + 0x0d, 0x07, 0x03, 0x0c, 0x0e, 0x04, 0x06}; + static const int scrollerColors[NUM_SCROLLER_COLORS] = + {0x05, 0x05, 0x05, 0x03, 0x0d, 0x01, 0x0d, 0x03}; + static const int footerColors[NUM_FOOTER_COLORS] = + {0x09, 0x02, 0x04, 0x0a, 0x07, 0x0d, 0x01, 0x0d, + 0x07, 0x0a, 0x04, 0x02, 0x09, 0x06, 0x06, 0x06}; + + return new DriverTheme( + 0x06, // border color + 0x06, // background color + 0x0e, // raster time color + 0x0e, // title color + lineColors, // line colors + 0x0d, // field name color + 0x07, // field colon color + 0x01, // field value color + 0x07, // legend color + 0x0d, // progress bar fill color + 0x01, // progress bar background color + 0x01, // scroller color + scrollerColors, // scroller colors + footerColors); // footer colors +} + + +DriverTheme* DriverTheme::createC1541UltimateTheme() +{ + static const int lineColors[NUM_LINE_COLORS] = + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0}; + static const int scrollerColors[NUM_SCROLLER_COLORS] = + {0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f}; + static const int footerColors[NUM_FOOTER_COLORS] = + {0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f}; + + return new DriverTheme( + 0x00, // border color + 0x00, // background color + 0x07, // raster time color + 0x0f, // title color + lineColors, // line colors + 0x07, // field name color + 0x07, // field colon color + 0x0f, // field value color + 0x0f, // legend color + 0x07, // progress bar fill color + 0x0f, // progress bar background color + 0x0f, // scroller color + scrollerColors, // scroller colors + footerColors); // footer colors +} + + +DriverTheme* DriverTheme::createCoalTheme() +{ + static const int lineColors[NUM_LINE_COLORS] = + {0x0b, 0x0b, 0x0c, 0x0c, 0x0f, 0x0f, 0x01, 0x01, + 0x01, 0x0f, 0x0f, 0x0c, 0x0c, 0x0b, 0x0b}; + static const int scrollerColors[NUM_SCROLLER_COLORS] = + {0x0b, 0x0b, 0x0b, 0x0c, 0x0f, 1, 0x0f, 0x0c}; + static const int footerColors[NUM_FOOTER_COLORS] = + {0x0b, 0x0b, 0x0c, 0x0c, 0x0f, 0x0f, 0x01, 0x01, + 0x0f, 0x0f, 0x0c, 0x0c, 0x0b, 0x0b, 0x00, 0x00}; + + return new DriverTheme( + 0x00, // border color + 0x00, // background color + 0x0b, // raster time color + 0x0c, // title color + lineColors, // line colors + 0x0b, // field name color + 0x0c, // field colon color + 0x0f, // field value color + 0x0c, // legend color + 0x0b, // progress bar fill color + 0x0c, // progress bar background color + 0x0b, // scroller color + scrollerColors, // scroller colors + footerColors); // footer colors +} + +DriverTheme* DriverTheme::createSidekickTheme() +{ + static const int lineColors[NUM_LINE_COLORS] = +// {0x09, 0x0b, 0x08, 0x0c, 0x0a, 0x0f, 0x07, 0x01, + // 0x0d, 0x07, 0x03, 0x0c, 0x0e, 0x04, 0x06}; + {0x0b, 0x05, 0x0d, 0x01, 0x0d, 0x05, 0x0b, 0x00, + 0x06, 0x0e, 0x03, 0x01, 0x03, 0x0e, 0x06}; + static const int scrollerColors[NUM_SCROLLER_COLORS] = + {0x05, 0x05, 0x05, 0x03, 0x0d, 0x01, 0x0d, 0x03}; + static const int footerColors[NUM_FOOTER_COLORS] = + // {0x0b, 0x05, 0x0d, 0x01, 0x0d, 0x05, 0x0b, 0x00, + //0x06, 0x0e, 0x03, 0x01, 0x03, 0x0e, 0x06, 0x00 }; + {0x0b, 0x0b, 0x0e, 0x05, 0x03, 0x0d, 0x01, 0x01, 0x0d, 0x03, 0x05, 0x0e, 0x0b, 0x0b, 0x00, 0x00 }; + //{0x09, 0x02, 0x04, 0x0a, 0x07, 0x0d, 0x01, 0x0d, + //0x07, 0x0a, 0x04, 0x02, 0x09, 0x00, 0x00, 0x00}; + + return new DriverTheme( + 0x0b, // border color + 0x00, // background color + 0x0c, // raster time color + 0x01, // title color + lineColors, // line colors + 0x0d, // field name color + 0x0d, // field colon color + 0x03, // field value color + 0x0c, // legend color + 0x06, // progress bar fill color + 0x03, // progress bar background color + 0x01, // scroller color + scrollerColors, // scroller colors + footerColors); // footer colors +} + +DriverTheme* DriverTheme::createDefaultTheme() +{ + static const int lineColors[NUM_LINE_COLORS] = + {0x09, 0x0b, 0x08, 0x0c, 0x0a, 0x0f, 0x07, 0x01, + 0x0d, 0x07, 0x03, 0x0c, 0x0e, 0x04, 0x06}; + static const int scrollerColors[NUM_SCROLLER_COLORS] = + {0x05, 0x05, 0x05, 0x03, 0x0d, 0x01, 0x0d, 0x03}; + static const int footerColors[NUM_FOOTER_COLORS] = + {0x09, 0x02, 0x04, 0x0a, 0x07, 0x0d, 0x01, 0x0d, + 0x07, 0x0a, 0x04, 0x02, 0x09, 0x00, 0x00, 0x00}; + + return new DriverTheme( + 0x00, // border color + 0x00, // background color + 0x06, // raster time color + 0x0f, // title color + lineColors, // line colors + 0x0d, // field name color + 0x07, // field colon color + 0x01, // field value color + 0x0c, // legend color + 0x06, // progress bar fill color + 0x0e, // progress bar background color + 0x01, // scroller color + scrollerColors, // scroller colors + footerColors); // footer colors +} + + +DriverTheme* DriverTheme::createDutchTheme() +{ + static const int lineColors[NUM_LINE_COLORS] = + {0x06, 0x06, 0x06, 0x06, 0x0e, 0x0e, 0x01, 0x01, + 0x01, 0x0a, 0x0a, 0x02, 0x02, 0x02, 0x02}; + static const int scrollerColors[NUM_SCROLLER_COLORS] = + {0x06, 0x06, 0x06, 0x0e, 0x03, 0x01, 0x03, 0x0e}; + static const int footerColors[NUM_FOOTER_COLORS] = + {0x09, 0x02, 0x08, 0x0a, 0x0f, 0x07, 0x01, 0x07, + 0x0f, 0x0a, 0x08, 0x02, 0x09, 0x00, 0x00, 0x00}; + + return new DriverTheme( + 0x00, // border color + 0x00, // background color + 0x08, // raster time color + 0x0f, // title color + lineColors, // line colors + 0x0f, // field name color + 0x0f, // field colon color + 0x01, // field value color + 0x0a, // legend color + 0x02, // progress bar fill color + 0x0b, // progress bar background color + 0x01, // scroller color + scrollerColors, // scroller colors + footerColors); // footer colors +} + + +DriverTheme* DriverTheme::createKernalTheme() +{ + static const int lineColors[NUM_LINE_COLORS] = + {0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e}; + static const int scrollerColors[NUM_SCROLLER_COLORS] = + {0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e}; + static const int footerColors[NUM_FOOTER_COLORS] = + {0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06}; + + return new DriverTheme( + 0x0e, // border color + 0x06, // background color + 0x06, // raster time color + 0x0e, // title color + lineColors, // line colors + 0x0e, // field name color + 0x0e, // field colon color + 0x0e, // field value color + 0x0e, // legend color + 0x0e, // progress bar fill color + 0x06, // progress bar background color + 0x0e, // scroller color + scrollerColors, // scroller colors + footerColors); // footer colors +} + + +DriverTheme* DriverTheme::createLightTheme() +{ + static const int lineColors[NUM_LINE_COLORS] = + {0x07, 0x0f, 0x0a, 0x0c, 0x08, 0x0b, 0x09, 0x00, + 0x06, 0x04, 0x0e, 0x0c, 0x03, 0x07, 0x0d}; + static const int scrollerColors[NUM_SCROLLER_COLORS] = + {0x05, 0x05, 0x05, 0x03, 0x0d, 0x01, 0x0d, 0x03}; + static const int footerColors[NUM_FOOTER_COLORS] = + {0x0d, 0x07, 0x0a, 0x04, 0x02, 0x09, 0x00, 0x09, + 0x02, 0x04, 0x0a, 0x07, 0x0d, 0x01, 0x01, 0x01}; + + return new DriverTheme( + 0x01, // border color + 0x01, // background color + 0x0d, // raster time color + 0x0b, // title color + lineColors, // line colors + 0x05, // field name color + 0x0d, // field colon color + 0x00, // field value color + 0x0c, // legend color + 0x0e, // progress bar fill color + 0x06, // progress bar background color + 0x0b, // scroller color + scrollerColors, // scroller colors + footerColors); // footer colors +} + + +DriverTheme* DriverTheme::createMondriaanTheme() +{ + static const int lineColors[NUM_LINE_COLORS] = + {0x06, 0x06, 0x06, 0x06, 0x0e, 0x0e, 0x07, 0x07, + 0x07, 0x0a, 0x0a, 0x02, 0x02, 0x02, 0x02}; + static const int scrollerColors[NUM_SCROLLER_COLORS] = + {0x02, 0x02, 0x02, 0x0a, 0x0f, 0x01, 0x0f, 0x0a}; + static const int footerColors[NUM_FOOTER_COLORS] = + {0x02, 0x02, 0x0a, 0x0a, 0x07, 0x07, 0x01, 0x01, + 0x07, 0x07, 0x0e, 0x0e, 0x06, 0x06, 0x00, 0x00}; + + return new DriverTheme( + 0x00, // border color + 0x00, // background color + 0x02, // raster time color + 0x0f, // title color + lineColors, // line colors + 0x0c, // field name color + 0x0e, // field colon color + 0x0f, // field value color + 0x07, // legend color + 0x06, // progress bar fill color + 0x0e, // progress bar background color + 0x01, // scroller color + scrollerColors, // scroller colors + footerColors); // footer colors +} + + +DriverTheme* DriverTheme::createOceanTheme() +{ + static const int lineColors[NUM_LINE_COLORS] = + {0x0d, 0x03, 0x0c, 0x0e, 0x04, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x04, 0x0e, 0x0c, 0x03, 0x0d}; + static const int scrollerColors[NUM_SCROLLER_COLORS] = + {0x03, 0x03, 0x03, 0x0e, 0x06, 0x00, 0x06, 0x0e}; + static const int footerColors[NUM_FOOTER_COLORS] = + {0x0f, 0x0e, 0x04, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x04, 0x0e, 0x0f, 0x01}; + + return new DriverTheme( + 0x01, // border color + 0x01, // background color + 0x0e, // raster time color + 0x06, // title color + lineColors, // line colors + 0x03, // field name color + 0x0e, // field colon color + 0x06, // field value color + 0x0e, // legend color + 0x06, // progress bar fill color + 0x0e, // progress bar background color + 0x0b, // scroller color + scrollerColors, // scroller colors + footerColors); // footer colors +} + + +DriverTheme* DriverTheme::createPencilTheme() +{ + static const int lineColors[NUM_LINE_COLORS] = + {0x0f, 0x0c, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x0c, 0x0f}; + static const int scrollerColors[NUM_SCROLLER_COLORS] = + {0x0f, 0x0f, 0x0f, 0x0c, 0x0b, 0x00, 0x0b, 0x0c}; + static const int footerColors[NUM_FOOTER_COLORS] = + {0x0f, 0x0c, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x0c, 0x0f, 0x01}; + + return new DriverTheme( + 0x01, // border color + 0x01, // background color + 0x0f, // raster time color + 0x00, // title color + lineColors, // line colors + 0x0c, // field name color + 0x0f, // field colon color + 0x0b, // field value color + 0x0c, // legend color + 0x0b, // progress bar fill color + 0x0c, // progress bar background color + 0x0b, // scroller color + scrollerColors, // scroller colors + footerColors); // footer colors +} + + +DriverTheme* DriverTheme::createRainbowTheme() +{ + static const int lineColors[NUM_LINE_COLORS] = + {0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x07, 0x07, + 0x07, 0x08, 0x08, 0x08, 0x02, 0x02, 0x02}; + static const int scrollerColors[NUM_SCROLLER_COLORS] = + {0x02, 0x08, 0x07, 0x05, 0x06, 0x00, 0x00, 0x00}; + static const int footerColors[NUM_FOOTER_COLORS] = + {0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f}; + + return new DriverTheme( + 0x00, // border color + 0x00, // background color + 0x0b, // raster time color + 0x0f, // title color + lineColors, // line colors + 0x0f, // field name color + 0x0c, // field colon color + 0x01, // field value color + 0x0c, // legend color + 0x0b, // progress bar fill color + 0x0f, // progress bar background color + 0x01, // scroller color + scrollerColors, // scroller colors + footerColors); // footer colors +} diff --git a/PSID/libpsid64/theme.h b/PSID/libpsid64/theme.h new file mode 100644 index 00000000..bc2ea38b --- /dev/null +++ b/PSID/libpsid64/theme.h @@ -0,0 +1,156 @@ +/* + $Id$ + + psid64 - create a C64 executable from a PSID file + Copyright (C) 2014 Roland Hermans + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef THEME_H +#define THEME_H + +////////////////////////////////////////////////////////////////////////////// +// I N C L U D E S +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// F O R W A R D D E C L A R A T O R S +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// D A T A D E C L A R A T O R S +////////////////////////////////////////////////////////////////////////////// + +#define NUM_LINE_COLORS 15 +#define NUM_SCROLLER_COLORS 8 +#define NUM_FOOTER_COLORS 16 + +class DriverTheme +{ +public: + DriverTheme(int borderColor, int backgroundColor, int rasterTimeColor, + int titleColor, const int* lineColors, + int fieldNameColor, int fieldColonColor, int fieldValueColor, + int legendColor, + int progressBarFillColor, int progressBarBackgroundColor, + int scrollerColor, const int* scrollerColors, + const int* footerColors); + virtual ~DriverTheme(); + + // factory methods + static DriverTheme* createBlueTheme(); + static DriverTheme* createC1541UltimateTheme(); + static DriverTheme* createCoalTheme(); + static DriverTheme* createDefaultTheme(); + static DriverTheme* createSidekickTheme(); + static DriverTheme* createDutchTheme(); + static DriverTheme* createKernalTheme(); + static DriverTheme* createLightTheme(); + static DriverTheme* createMondriaanTheme(); + static DriverTheme* createOceanTheme(); + static DriverTheme* createPencilTheme(); + static DriverTheme* createRainbowTheme(); + + int getBorderColor() const + { + return m_borderColor; + } + + int getBackgroundColor() const + { + return m_backgroundColor; + } + + int getRasterTimeColor() const + { + return m_rasterTimeColor; + } + + int getTitleColor() const + { + return m_titleColor; + } + + const int* getLineColors() const + { + return m_lineColors; + } + + int getFieldNameColor() const + { + return m_fieldNameColor; + } + + int getFieldColonColor() const + { + return m_fieldColonColor; + } + + int getFieldValueColor() const + { + return m_fieldValueColor; + } + + int getLegendColor() const + { + return m_legendColor; + } + + int getProgressBarFillColor() const + { + return m_progressBarFillColor; + } + + int getProgressBarBackgroundColor() const + { + return m_progressBarBackgroundColor; + } + + int getScrollerColor() const + { + return m_scrollerColor; + } + + const int* getScrollerColors() const + { + return m_scrollerColors; + } + + const int* getFooterColors() const + { + return m_footerColors; + } + +private: + int m_borderColor; + int m_backgroundColor; + int m_rasterTimeColor; + int m_titleColor; + int m_lineColors[NUM_LINE_COLORS]; + int m_fieldNameColor; + int m_fieldColonColor; + int m_fieldValueColor; + int m_legendColor; + int m_progressBarFillColor; + int m_progressBarBackgroundColor; + int m_scrollerColor; + int m_scrollerColors[NUM_SCROLLER_COLORS]; + int m_footerColors[NUM_FOOTER_COLORS]; +}; + +#endif // THEME_H diff --git a/PSID/psid64/psid64.h b/PSID/psid64/psid64.h new file mode 100644 index 00000000..0c3afb9e --- /dev/null +++ b/PSID/psid64/psid64.h @@ -0,0 +1,376 @@ +/* + $Id$ + + psid64 - create a C64 executable from a PSID file + Copyright (C) 2001-2003 Roland Hermans + + this code has been modified for integration into the Sidekick64 software + (the modifications are mostly removing everything that does not compile with vanilla Circle, + I really feel sorry for having disfigured this code, please refer to the original repository + if you want to get the real and decent psid64 version) + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +////////////////////////////////////////////////////////////////////////////// +// I N C L U D E S +////////////////////////////////////////////////////////////////////////////// + +#ifndef PSID64_H +#define PSID64_H + +//#include +//#include + +//#include "../sidplay/utils/SidDatabase.h" +#include "../sidplay/utils/SidTuneMod.h" + +#define size_t int + + +////////////////////////////////////////////////////////////////////////////// +// F O R W A R D D E C L A R A T O R S +////////////////////////////////////////////////////////////////////////////// + +class Screen; +//class SidId; +class STIL; + + +////////////////////////////////////////////////////////////////////////////// +// D E F I N I T I O N S +////////////////////////////////////////////////////////////////////////////// + +/** + * Class to generate a C64 self extracting executable from a PSID file. + */ +class Psid64 +{ +public: + enum Theme { + THEME_DEFAULT, + THEME_BLUE, + THEME_C1541_ULTIMATE, + THEME_COAL, + THEME_DUTCH, + THEME_KERNAL, + THEME_LIGHT, + THEME_MONDRIAAN, + THEME_OCEAN, + THEME_PENCIL, + THEME_RAINBOW + }; + + /** + * Constructor. + */ + Psid64(); + + /** + * Destructor. + */ + ~Psid64(); + + /** + * Set the path to the HVSC. This path is e.g. used to retrieve the STIL + * information for a PSID file. + */ + //bool setHvscRoot(std::string &hvscRoot); + + /** + * Get the path to the HVSC. + */ + /*inline const std::string getHvscRoot() const + { + return m_hvscRoot; + }*/ + + /** + * Set the path to the HVSC song length database. + */ + //bool setDatabaseFileName(std::string &databaseFileName); + + /** + * Get the path to the HVSC song length database. + */ + /*inline const std::string getDatabaseFileName() const + { + return m_databaseFileName; + }*/ + + /** + * Set the path to the SID ID player identification configuration file. + */ + //bool setSidIdConfigFileName(std::string &sidIdConfigFileName); + + /** + * Get the path to the SID ID player identification configuration file. + */ + /*inline const std::string getSidIdConfigFileName() const + { + return m_sidIdConfigFileName; + }*/ + + /** + * Set the no driver option. When true, no driver code is added to the C64 + * executable and PSID64 only acts as a .sid to .prg converter. + */ + inline void setNoDriver(bool noDriver) + { + m_noDriver = noDriver; + } + + /** + * Get the no driver option. + */ + inline bool getNoDriver() const + { + return m_noDriver; + } + + /** + * Set the blank screen option. When true, the screen is forced to be + * blank, even when there is enough free memory available to display + * a screen with information about the PSID. A C64 executable that blanks + * the screen will be several pages smaller as it does not require display + * data and code. + */ + inline void setBlankScreen(bool blankScreen) + { + m_blankScreen = blankScreen; + } + + /** + * Get the blank screen option. + */ + inline bool getBlankScreen() const + { + return m_blankScreen; + } + + /** + * Set the compress option. When true, the output file is compressed with + * Exomizer. + */ + inline void setCompress(bool compress) + { + m_compress = compress; + } + + /** + * Get the compress option. + */ + inline bool getCompress() const + { + return m_compress; + } + + /** + * Set the initial song number. When 0 or larger than the total number of + * songs, the initial song as specified in the SID file header is used. + */ + inline void setInitialSong(int initialSong) + { + m_initialSong = initialSong; + } + + /** + * Get the initial song. + */ + inline int getInitialSong() + { + return m_initialSong; + } + + /** + * Set the use global comment flag. When set, PSID64 tries to extract + * the global comment field of a PSID from the STIL database. + */ + inline void setUseGlobalComment(bool useGlobalComment) + { + m_useGlobalComment = useGlobalComment; + } + + /** + * Get the use global comment flag. + */ + inline bool getUseGlobalComment() const + { + return m_useGlobalComment; + } + + /** + * Set the verbose flag. When set, PSID64 will be more verbose about the + * generation of the C64 executable. + */ + inline void setVerbose(bool verbose) + { + m_verbose = verbose; + } + + /** + * Get the verbose flag. + */ + inline bool getVerbose() const + { + return m_verbose; + } + + /** + * Set the theme. + */ + /*inline void setTheme(Theme theme) + { + m_theme = theme; + }*/ + + /** + * Get the theme. + */ + /*inline Theme getTheme() + { + return m_theme; + }*/ + + /** + * Get the status string. After an error has occurred, the status string + * contains a description of the error. + */ + inline const char* getStatus() const + { + return m_statusString; + } + + /** + * Load a PSID file. + */ + //bool load(const char* fileName); + bool load(unsigned char* sidData, int sidLength); + + /** + * Convert the currently loaded PSID file. + */ + bool convert(); + + /** + * Save the most recently generated C64 executable. + */ + //bool save(const char* fileName); + + /** + * Write the C64 executable to an output stream. + */ + //bool write(std::ostream& out = std::cout); + + // converted file + uint_least8_t *m_programData; + unsigned int m_programSize; + +private: + Psid64(const Psid64&); + Psid64 operator=(const Psid64&); + + static const unsigned int MAX_BLOCKS = 5; + static const unsigned int MAX_PAGES = 256; // number of memory pages + static const unsigned int NUM_MINDRV_PAGES = 2; // driver without screen display + static const unsigned int NUM_EXTDRV_PAGES = 5; // driver with screen display + static const unsigned int NUM_SCREEN_PAGES = 4; // size of screen in pages + static const unsigned int NUM_CHAR_PAGES = 4; // size of charset in pages + static const unsigned int STIL_EOT_SPACES = 10; // number of spaces before EOT + static const unsigned int BAR_X = 15; + static const unsigned int BAR_WIDTH = 19; + static const unsigned int BAR_SPRITE_SCREEN_OFFSET = 0x300; + + // error and status message strings + static const char* txt_relocOverlapsImage; + static const char* txt_notEnoughC64Memory; + static const char* txt_fileIoError; + static const char* txt_noSidTuneLoaded; + static const char* txt_noSidTuneConverted; + //static const char* txt_sidIdConfigError; + + // configuration options + bool m_noDriver; + bool m_blankScreen; + bool m_compress; + int m_initialSong; + bool m_useGlobalComment; + bool m_verbose; + //std::string m_hvscRoot; + //std::string m_databaseFileName; + //std::string m_sidIdConfigFileName; + //Theme m_theme; + + // state data + bool m_status; + const char* m_statusString; // error/status message of last operation + + // other internal data + //std::string m_fileName; + SidTuneMod m_tune; + SidTuneInfo m_tuneInfo; + //SidDatabase m_database; + //STIL *m_stil; + //SidId *m_sidId; + + // conversion data + Screen *m_screen; + //std::string m_stilText; + uint_least8_t m_songlengthsData[4 * SIDTUNE_MAX_SONGS]; + size_t m_songlengthsSize; + uint_least8_t m_driverPage; // startpage of driver, 0 means no driver + uint_least8_t m_screenPage; // startpage of screen, 0 means no screen + uint_least8_t m_charPage; // startpage of chars, 0 means no chars + uint_least8_t m_stilPage; // startpage of stil, 0 means no stil + uint_least8_t m_songlengthsPage; // startpage of song length data, 0 means no song lengths + //std::string m_playerId; + + // member functions + int_least32_t roundDiv(int_least32_t dividend, int_least32_t divisor); + bool convertNoDriver(); + bool convertBASIC(); + bool formatStilText(); + bool getSongLengths(); + uint_least8_t findSonglengthsSpace(bool* pages, uint_least8_t scr, + uint_least8_t chars, + uint_least8_t driver, + uint_least8_t stil, + uint_least8_t stil_pages, + uint_least8_t size) const; + uint_least8_t findStilSpace(bool* pages, uint_least8_t scr, + uint_least8_t chars, + uint_least8_t driver, + uint_least8_t size) const; + uint_least8_t findDriverSpace(bool* pages, uint_least8_t scr, + uint_least8_t chars, + uint_least8_t size) const; + void findFreeSpace(); + uint8_t iomap(uint_least16_t addr); + void initDriver(uint_least8_t** mem, uint_least8_t** ptr, int* n); + void addFlag(bool &hasFlags, const char *flagName); + char *toHexWord(uint_least16_t value) const; + char *toNumStr(int value) const; + void drawScreen(); + bool compress(); +}; + + +/*inline std::ostream& operator << (std::ostream& out, Psid64 &psid64) +{ + psid64.write(out); + return out; +}*/ + +#endif // PSID64_H diff --git a/PSID/readme.txt b/PSID/readme.txt new file mode 100644 index 00000000..744a7ab6 --- /dev/null +++ b/PSID/readme.txt @@ -0,0 +1,6 @@ +The psid64 code has been modified for integration into the Sidekick64 software. + +The modifications are mostly removing everything that does not compile with vanilla Circle. +I really feel sorry for having disfigured this code, please refer to the original repository +if you want to get the real and decent psid64 source code version. + diff --git a/PSID/sidplay/Buffer.h b/PSID/sidplay/Buffer.h new file mode 100644 index 00000000..94fec72c --- /dev/null +++ b/PSID/sidplay/Buffer.h @@ -0,0 +1,114 @@ +/* + * /home/ms/files/source/libsidtune/RCS/Buffer.h,v + * + * Copyright (C) Michael Schwendt + * + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef BUFFER_H +#define BUFFER_H + +#include +#include "sidtypes.h" + +template class Buffer_sidtt +{ + public: + Buffer_sidtt(void) : dummy(0) + { + kill(); + } + + Buffer_sidtt(T* inBuf, uint_least32_t inLen) : dummy(0) + { + kill(); + if (inBuf!=0 && inLen!=0) + { + buf = inBuf; + bufLen = inLen; + } + } + + bool assign(T* newBuf, uint_least32_t newLen) + { + erase(); + buf = newBuf; + bufLen = newLen; + return (buf!=0); + } + + T* get(void) const { return buf; } + uint_least32_t len(void) const { return bufLen; } + + T* xferPtr(void) + { + T* tmpBuf = buf; + buf = 0; + return tmpBuf; + } + + uint_least32_t xferLen(void) + { + uint_least32_t tmpBufLen = bufLen; + bufLen = 0; + return tmpBufLen; + } + + T& operator[](uint_least32_t index) + { + if (index < bufLen) + return buf[index]; + else + return dummy; + } + + bool isEmpty(void) const { return (buf==0); } + + void erase(void) + { + if (buf!=0 && bufLen!=0) + { +#ifndef SID_HAVE_BAD_COMPILER + delete[] buf; +#else + delete[] (void *) buf; +#endif + } + kill(); + } + + ~Buffer_sidtt(void) + { + erase(); + } + + private: + T* buf; + uint_least32_t bufLen; + T dummy; + + void kill(void) + { + buf = 0; + bufLen = 0; + } + + private: // prevent copying + Buffer_sidtt(const Buffer_sidtt&); + Buffer_sidtt& operator=(Buffer_sidtt& b); +}; + +#endif /* BUFFER_H */ diff --git a/PSID/sidplay/SidTune.h b/PSID/sidplay/SidTune.h new file mode 100644 index 00000000..0e0eb540 --- /dev/null +++ b/PSID/sidplay/SidTune.h @@ -0,0 +1,379 @@ +/* + * /home/ms/files/source/libsidtune/RCS/SidTune.h,v + * + this code has been modified for integration into the Sidekick64 software + (the modifications are mostly removing everything that does not compile with vanilla Circle, + I really feel sorry for having disfigured this code, please refer to the original repository + if you want to get the real and decent psid64 version) + + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef SIDTUNE_H +#define SIDTUNE_H + + +#include "sidtypes.h" +#include "Buffer.h" +#include "SmartPtr.h" + +//#include + + +const uint_least16_t SIDTUNE_MAX_SONGS = 256; +// Also PSID file format limit. + +const uint_least16_t SIDTUNE_MAX_CREDIT_STRINGS = 10; +const uint_least16_t SIDTUNE_MAX_CREDIT_STRLEN = 80+1; +// 80 characters plus terminating zero. + +const uint_least32_t SIDTUNE_MAX_MEMORY = 65536; +const uint_least32_t SIDTUNE_MAX_FILELEN = 65536+2+0x7C; +// C64KB+LOAD+PSID + +const int SIDTUNE_SPEED_VBI = 0; // Vertical-Blanking-Interrupt +const int SIDTUNE_SPEED_CIA_1A = 60; // CIA 1 Timer A + +const int SIDTUNE_CLOCK_UNKNOWN = 0x00; +const int SIDTUNE_CLOCK_PAL = 0x01; // These are also used in the +const int SIDTUNE_CLOCK_NTSC = 0x02; // emulator engine! +const int SIDTUNE_CLOCK_ANY = (SIDTUNE_CLOCK_PAL | SIDTUNE_CLOCK_NTSC); + +const int SIDTUNE_SIDMODEL_UNKNOWN = 0x00; +const int SIDTUNE_SIDMODEL_6581 = 0x01; // These are also used in the +const int SIDTUNE_SIDMODEL_8580 = 0x02; // emulator engine! +const int SIDTUNE_SIDMODEL_ANY = (SIDTUNE_SIDMODEL_6581 | SIDTUNE_SIDMODEL_8580); + +const int SIDTUNE_COMPATIBILITY_C64 = 0x00; // File is C64 compatible +const int SIDTUNE_COMPATIBILITY_PSID = 0x01; // File is PSID specific +const int SIDTUNE_COMPATIBILITY_R64 = 0x02; // File is Real C64 only +const int SIDTUNE_COMPATIBILITY_BASIC = 0x03; // File requires C64 Basic + + +// Required to export template +#ifndef _SidTune_cpp_ +extern +#endif +template class SID_EXTERN Buffer_sidtt; + +struct SidTuneInfo +{ + // An instance of this structure is used to transport values to + // and from SidTune objects. + + // You must read (i.e. activate) sub-song specific information + // via: + // const SidTuneInfo& tuneInfo = SidTune[songNumber]; + // const SidTuneInfo& tuneInfo = SidTune.getInfo(); + // void SidTune.getInfo(tuneInfo&); + + // Consider the following fields as read-only, because the SidTune class + // does not provide an implementation of: bool setInfo(const SidTuneInfo&). + // Currently, the only way to get the class to accept values which + // are written to these fields is by creating a derived class. + + const char* formatString; // the name of the identified file format + const char* statusString; // error/status message of last operation + + const char* speedString; // describing the speed a song is running at + + uint_least16_t loadAddr; + uint_least16_t initAddr; + uint_least16_t playAddr; + + uint_least16_t songs; + uint_least16_t startSong; + + // The SID chip base address(es) used by the sidtune. + uint_least16_t sidChipBase1; // 0xD400 (normal, 1st SID) + uint_least16_t sidChipBase2; // 0xD?00 (2nd SID) or 0 (no 2nd SID) + uint_least16_t sidChipBase3; // 0xD?00 (3rd SID) or 0 (no 3rd SID) + + // Available after song initialization. + // + uint_least16_t currentSong; // the one that has been initialized + uint_least8_t songSpeed; // intended speed, see top + uint_least8_t clockSpeed; // -"- + uint_least8_t relocStartPage; // First available page for relocation + uint_least8_t relocPages; // Number of pages available for relocation + uint_least8_t secondSIDAddress;// Indicator for address of the second SID + uint_least8_t thirdSIDAddress; // Indicator for address of the third SID + bool musPlayer; // whether Sidplayer routine has been installed + int sidModel; // Sid Model required for this sid + int sid2Model; // Sid Model required for 2nd sid of this sid + int sid3Model; // Sid Model required for 3rd sid of this sid + int compatibility; // compatibility requirements + bool fixLoad; // whether load address might be duplicate + uint_least16_t songLength; // --- not yet supported --- + // + // Song title, credits, ... + // 0 = Title, 1 = Author, 2 = Copyright/Publisher + // + uint_least8_t numberOfInfoStrings; // the number of available text info lines + char* infoString[SIDTUNE_MAX_CREDIT_STRINGS]; + // + uint_least16_t numberOfCommentStrings; // --- not yet supported --- + char ** commentString; // --- not yet supported --- + // + uint_least32_t dataFileLen; // length of single-file sidtune file + uint_least32_t c64dataLen; // length of raw C64 data without load address + char* path; // path to sidtune files; "", if cwd + char* dataFileName; // a first file: e.g. "foo.c64"; "", if none + char* infoFileName; // a second file: e.g. "foo.sid"; "", if none + // +}; + + +class SID_EXTERN SidTune +{ + private: + typedef enum + { + LOAD_NOT_MINE = 0, + LOAD_OK, + LOAD_ERROR + } LoadStatus; + + public: // ---------------------------------------------------------------- + + // If your opendir() and readdir()->d_name return path names + // that contain the forward slash (/) as file separator, but + // your operating system uses a different character, there are + // extra functions that can deal with this special case. Set + // separatorIsSlash to true if you like path names to be split + // correctly. + // You do not need these extra functions if your systems file + // separator is the forward slash. + // + // Load a sidtune from a file. + // + // To retrieve data from standard input pass in filename "-". + // If you want to override the default filename extensions use this + // contructor. Please note, that if the specified ``sidTuneFileName'' + // does exist and the loader is able to determine its file format, + // this function does not try to append any file name extension. + // See ``sidtune.cpp'' for the default list of file name extensions. + // You can specific ``sidTuneFileName = 0'', if you do not want to + // load a sidtune. You can later load one with open(). + SidTune(const char* fileName, const char **fileNameExt = 0, + const bool separatorIsSlash = false); + + // Load a single-file sidtune from a memory buffer. + // Currently supported: PSID format + SidTune(const uint_least8_t* oneFileFormatSidtune, const uint_least32_t sidtuneLength); + + virtual ~SidTune(); + + // The sidTune class does not copy the list of file name extensions, + // so make sure you keep it. If the provided pointer is 0, the + // default list will be activated. This is a static list which + // is used by all SidTune objects. + void setFileNameExtensions(const char **fileNameExt); + + // Load a sidtune into an existing object. + // From a file. + //bool load(const char* fileName, const bool separatorIsSlash = false); + bool load( unsigned char* sidData, int sidLength, const bool separatorIsSlash = false); + + // From a buffer. + bool read(const uint_least8_t* sourceBuffer, const uint_least32_t bufferLen); + + // Select sub-song (0 = default starting song) + // and retrieve active song information. + const SidTuneInfo& operator[](const uint_least16_t songNum); + + // Select sub-song (0 = default starting song) + // and return active song number out of [1,2,..,SIDTUNE_MAX_SONGS]. + uint_least16_t selectSong(const uint_least16_t songNum); + + // Retrieve sub-song specific information. + // Beware! Still member-wise copy! + const SidTuneInfo& getInfo(); + + // Get a copy of sub-song specific information. + // Beware! Still member-wise copy! + void getInfo(SidTuneInfo&); + + // Determine current state of object (true = okay, false = error). + // Upon error condition use ``getInfo'' to get a descriptive + // text string in ``SidTuneInfo.statusString''. + operator bool() { return status; } + bool getStatus() { return status; } + + // Whether sidtune uses two SID chips. + bool isStereo() + { + return (info.sidChipBase1!=0 && info.sidChipBase2!=0); + } + + // Copy sidtune into C64 memory (64 KB). + bool placeSidTuneInC64mem(uint_least8_t* c64buf); + + // --- file save & format conversion --- + + // These functions work for any successfully created object. + // overWriteFlag: true = Overwrite existing file. + // false = Default, return error when file already + // exists. + // One could imagine an "Are you sure ?"-checkbox before overwriting + // any file. + // returns: true = Successful, false = Error condition. + //bool saveC64dataFile( const char* destFileName, const bool overWriteFlag = false ); + //bool saveSIDfile( const char* destFileName, const bool overWriteFlag = false ); + //bool savePSIDfile( const char* destFileName, const bool overWriteFlag = false ); + + // This function can be used to remove a duplicate C64 load address in + // the C64 data (example: FE 0F 00 10 4C ...). A duplicate load address + // of offset 0x02 is indicated by the ``fixLoad'' flag in the SidTuneInfo + // structure. + // + // The ``force'' flag here can be used to remove the first load address + // and set new INIT/PLAY addresses regardless of whether a duplicate + // load address has been detected and indicated by ``fixLoad''. + // For instance, some position independent sidtunes contain a load address + // of 0xE000, but are loaded to 0x0FFE and call the player code at 0x1000. + // + // Do not forget to save the sidtune file. + void fixLoadAddress(const bool force = false, uint_least16_t initAddr = 0, + uint_least16_t playAddr = 0); + + // Does not affect status of object, and therefore can be used + // to load files. Error string is put into info.statusString, though. + //bool loadFile(const char* fileName, Buffer_sidtt& bufferRef); + + //bool saveToOpenFile( std::ofstream& toFile, const uint_least8_t* buffer, uint_least32_t bufLen ); + + protected: // ------------------------------------------------------------- + + SidTuneInfo info; + bool status; + + uint_least8_t songSpeed[SIDTUNE_MAX_SONGS]; + uint_least8_t clockSpeed[SIDTUNE_MAX_SONGS]; + uint_least16_t songLength[SIDTUNE_MAX_SONGS]; + + // holds text info from the format headers etc. + char infoString[SIDTUNE_MAX_CREDIT_STRINGS][SIDTUNE_MAX_CREDIT_STRLEN]; + + // See instructions at top. + bool isSlashedFileName; + + // For files with header: offset to real data + uint_least32_t fileOffset; + + // Needed for MUS/STR player installation. + uint_least16_t musDataLen; + + Buffer_sidtt cache; + + // Filename extensions to append for various file types. + static const char** fileNameExtensions; + + // --- protected member functions --- + + // Convert 32-bit PSID-style speed word to internal tables. + void convertOldStyleSpeedToTables(uint_least32_t speed, + int clock = SIDTUNE_CLOCK_PAL); + + virtual int convertPetsciiToAscii (SmartPtr_sidtt&, char*); + + // Check compatibility details are sensible + bool checkCompatibility(void); + // Check for valid relocation information + bool checkRelocInfo(void); + // Common address resolution procedure + bool resolveAddrs(const uint_least8_t* c64data); + + // Support for various file formats. + + virtual LoadStatus PSID_fileSupport ( unsigned char *dataBuf, int dataLen ); + //virtual bool PSID_fileSupportSave(std::ofstream& toFile, const uint_least8_t* dataBuffer); + + //virtual LoadStatus SID_fileSupport (Buffer_sidtt& dataBuf, + // Buffer_sidtt& sidBuf); + /*virtual bool SID_fileSupportSave (std::ofstream& toFile); + + virtual LoadStatus MUS_fileSupport (Buffer_sidtt& musBuf, + Buffer_sidtt& strBuf); + LoadStatus MUS_load (Buffer_sidtt& musBuf, + bool init = false); + LoadStatus MUS_load (Buffer_sidtt& musBuf, + Buffer_sidtt& strBuf, + bool init = false); + virtual bool MUS_detect (const void* buffer, const uint_least32_t bufLen, + uint_least32_t& voice3Index); + virtual bool MUS_mergeParts (Buffer_sidtt& musBuf, + Buffer_sidtt& strBuf); + virtual void MUS_setPlayerAddress(); + virtual void MUS_installPlayer (uint_least8_t *c64buf); + + virtual LoadStatus INFO_fileSupport (Buffer_sidtt& dataBuf, + Buffer_sidtt& infoBuf); + virtual LoadStatus PRG_fileSupport (const char* fileName, + Buffer_sidtt& dataBuf); + virtual LoadStatus X00_fileSupport (const char* fileName, + Buffer_sidtt& dataBuf);*/ + + // Error and status message strings. + static const char* txt_songNumberExceed; + static const char* txt_empty; + static const char* txt_unrecognizedFormat; + static const char* txt_noDataFile; + static const char* txt_notEnoughMemory; + static const char* txt_cantLoadFile; + static const char* txt_cantOpenFile; + static const char* txt_fileTooLong; + static const char* txt_dataTooLong; + static const char* txt_cantCreateFile; + static const char* txt_fileIoError; + static const char* txt_VBI; + static const char* txt_CIA; + static const char* txt_noErrors; + static const char* txt_na; + static const char* txt_badAddr; + static const char* txt_badReloc; + static const char* txt_corrupt; + + private: // --------------------------------------------------------------- + + void init(); + void cleanup(); +#if !defined(SIDTUNE_NO_STDIN_LOADER) + void getFromStdIn(); +#endif + //void getFromFiles(const char* name); + void getFromRaw( unsigned char* sidData, int sidLength ); + + void deleteFileNameCopies(); + + // Try to retrieve single-file sidtune from specified buffer. + //void getFromBuffer(const uint_least8_t* const buffer, const uint_least32_t bufferLen); + + // Cache the data of a single-file or two-file sidtune and its + // corresponding file names. + bool acceptSidTune(const char* dataFileName, const char* infoFileName, + Buffer_sidtt& buf); + + bool createNewFileName(Buffer_sidtt& destString, + const char* sourceName, const char* sourceExt); + + int decompressPP20(Buffer_sidtt& buf); + + private: // prevent copying + SidTune(const SidTune&); + SidTune& operator=(SidTune&); +}; + +#endif /* SIDTUNE_H */ diff --git a/PSID/sidplay/SmartPtr.h b/PSID/sidplay/SmartPtr.h new file mode 100644 index 00000000..bddb4b5e --- /dev/null +++ b/PSID/sidplay/SmartPtr.h @@ -0,0 +1,228 @@ +/* Simple smart pointer class. */ + +#ifndef SMARTPTR_H +#define SMARTPTR_H + +typedef unsigned long int ulint_smartpt; + +template +class SmartPtrBase_sidtt +{ + public: + + /* --- constructors --- */ + + SmartPtrBase_sidtt(T* buffer, ulint_smartpt bufferLen, bool bufOwner = false) : dummy(0) + { + doFree = bufOwner; + if ( bufferLen >= 1 ) + { + pBufCurrent = ( bufBegin = buffer ); + bufEnd = bufBegin + bufferLen; + bufLen = bufferLen; + status = true; + } + else + { + pBufCurrent = ( bufBegin = ( bufEnd = 0 )); + bufLen = 0; + status = false; + } + } + + /* --- destructor --- */ + + virtual ~SmartPtrBase_sidtt() + { + if ( doFree && (bufBegin != 0) ) + { +#ifndef SID_HAVE_BAD_COMPILER + delete[] bufBegin; +#else + delete[] (void*)bufBegin; +#endif + } + } + + /* --- public member functions --- */ + + virtual T* tellBegin() { return bufBegin; } + virtual ulint_smartpt tellLength() { return bufLen; } + virtual ulint_smartpt tellPos() { return (ulint_smartpt)(pBufCurrent-bufBegin); } + + virtual bool checkIndex(ulint_smartpt index) + { + return ((pBufCurrent+index)= 1 ) + { + pBufCurrent = bufBegin; + return (status = true); + } + else + { + return (status = false); + } + } + + virtual bool good() + { + return (pBufCurrent= bufBegin) + { + pBufCurrent -= offset; + } + else + { + status = false; + } + } + + virtual T operator*() + { + if ( good() ) + { + return *pBufCurrent; + } + else + { + status = false; + return dummy; + } + } + + virtual T& operator [](ulint_smartpt index) + { + if (checkIndex(index)) + { + return pBufCurrent[index]; + } + else + { + status = false; + return dummy; + } + } + + virtual operator bool() { return status; } + + protected: + + T* bufBegin; + T* bufEnd; + T* pBufCurrent; + ulint_smartpt bufLen; + bool status; + bool doFree; + T dummy; +}; + + +template +class SmartPtr_sidtt : public SmartPtrBase_sidtt +{ + public: + + /* --- constructors --- */ + + SmartPtr_sidtt(T* buffer, ulint_smartpt bufferLen, bool bufOwner = false) + : SmartPtrBase_sidtt(buffer, bufferLen, bufOwner) + { + } + + SmartPtr_sidtt() + : SmartPtrBase_sidtt(0,0) + { + } + + void setBuffer(T* buffer, ulint_smartpt bufferLen) + { + if ( bufferLen >= 1 ) + { + this->pBufCurrent = ( this->bufBegin = buffer ); + this->bufEnd = buffer + bufferLen; + this->bufLen = bufferLen; + this->status = true; + } + else + { + this->pBufCurrent = this->bufBegin = this->bufEnd = 0; + this->bufLen = 0; + this->status = false; + } + } +}; + +#endif /* SMARTPTR_H */ diff --git a/PSID/sidplay/sidconfig.h b/PSID/sidplay/sidconfig.h new file mode 100644 index 00000000..d38dfd9c --- /dev/null +++ b/PSID/sidplay/sidconfig.h @@ -0,0 +1,51 @@ +/* sidconfig.h (template) */ +#ifndef _sidconfig_h_ +#define _sidconfig_h_ + +/* Define the sizeof of types in bytes */ +#define SID_SIZEOF_CHAR 1 +#define SID_SIZEOF_SHORT_INT 2 +#define SID_SIZEOF_INT 4 +#define SID_SIZEOF_LONG_INT 8 + +/* Define SID_WORDS_BIGENDIAN if your processor stores words + with the most significant byte first (like Motorola and SPARC, + unlike Intel and VAX). */ +/* Define SID_WORDS_LITTLEENDIAN if your processor stores words + with the least significant byte first (like Intel and VAX). */ +#define SID_WORDS_LITTLEENDIAN + +/* Define if your compiler supports type "bool". + If not, a user-defined signed integral type will be used. */ +#define SID_HAVE_BOOL + +/* Define if your compiler supports AC99 header "stdbool.h" */ +#define SID_HAVE_STDBOOL_H + +/* DLL building support on win32 hosts */ +#ifndef SID_EXTERN +# ifdef DLL_EXPORT /* defined by libtool (if required) */ +# define SID_EXTERN __declspec(dllexport) +# endif +# ifdef SID_DLL_IMPORT /* define if linking with this dll */ +# define SID_EXTERN __declspec(dllimport) +# endif +# ifndef SID_EXTERN /* static linking or !_WIN32 */ +# define SID_EXTERN +# endif +#endif + +/* Namespace support */ +#define SIDPLAY2_NAMESPACE __sidplay2__ +#ifdef SIDPLAY2_NAMESPACE +# define SIDPLAY2_NAMESPACE_START \ + namespace SIDPLAY2_NAMESPACE \ + { +# define SIDPLAY2_NAMESPACE_STOP \ + } +#else +# define SIDPLAY2_NAMESPACE_START +# define SIDPLAY2_NAMESPACE_STOP +#endif + +#endif /* _sidconfig_h_ */ diff --git a/PSID/sidplay/sidconfig.h.in b/PSID/sidplay/sidconfig.h.in new file mode 100644 index 00000000..b9c82441 --- /dev/null +++ b/PSID/sidplay/sidconfig.h.in @@ -0,0 +1,51 @@ +/* sidconfig.h (template) */ +#ifndef _sidconfig_h_ +#define _sidconfig_h_ + +/* Define the sizeof of types in bytes */ +#define SID_SIZEOF_CHAR @SID_SIZEOF_CHAR@ +#define SID_SIZEOF_SHORT_INT @SID_SIZEOF_SHORT_INT@ +#define SID_SIZEOF_INT @SID_SIZEOF_INT@ +#define SID_SIZEOF_LONG_INT @SID_SIZEOF_LONG_INT@ + +/* Define SID_WORDS_BIGENDIAN if your processor stores words + with the most significant byte first (like Motorola and SPARC, + unlike Intel and VAX). */ +/* Define SID_WORDS_LITTLEENDIAN if your processor stores words + with the least significant byte first (like Intel and VAX). */ +#define @SID_WORDS_ENDIANESS@ + +/* Define if your compiler supports type "bool". + If not, a user-defined signed integral type will be used. */ +@SID_HAVE_BOOL@ + +/* Define if your compiler supports AC99 header "stdbool.h" */ +@SID_HAVE_STDBOOL_H@ + +/* DLL building support on win32 hosts */ +#ifndef SID_EXTERN +# ifdef DLL_EXPORT /* defined by libtool (if required) */ +# define SID_EXTERN __declspec(dllexport) +# endif +# ifdef SID_DLL_IMPORT /* define if linking with this dll */ +# define SID_EXTERN __declspec(dllimport) +# endif +# ifndef SID_EXTERN /* static linking or !_WIN32 */ +# define SID_EXTERN +# endif +#endif + +/* Namespace support */ +#define SIDPLAY2_NAMESPACE __sidplay2__ +#ifdef SIDPLAY2_NAMESPACE +# define SIDPLAY2_NAMESPACE_START \ + namespace SIDPLAY2_NAMESPACE \ + { +# define SIDPLAY2_NAMESPACE_STOP \ + } +#else +# define SIDPLAY2_NAMESPACE_START +# define SIDPLAY2_NAMESPACE_STOP +#endif + +#endif /* _sidconfig_h_ */ diff --git a/PSID/sidplay/sidendian.h b/PSID/sidplay/sidendian.h new file mode 100644 index 00000000..548129af --- /dev/null +++ b/PSID/sidplay/sidendian.h @@ -0,0 +1,424 @@ +/*************************************************************************** + sidendian.h - Improtant endian functions + ------------------- + begin : Mon Jul 3 2000 + copyright : (C) 2000 by Simon White + email : s_a_white@email.com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program 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. * + * * + ***************************************************************************/ +/*************************************************************************** + * $Log$ + * Revision 1.2 2014/05/29 21:37:01 rolandh + * Prevent a compiler warning on platforms where type uint_least16_t is larger than 16 bits, or uint_least32_t is larger than 32 bits. + * + * Revision 1.2 2014/05/29 21:31:42 rolandh + * Prevent a compiler warning on platforms where type uint_least16_t is larger than 16 bits. + * + * Revision 1.1 2003/04/13 10:09:50 rolandh + * switched to C++ + * + * Revision 1.5 2001/07/03 22:44:13 s_a_white + * Added endian_16 to convert a 16 bit value to an array of 8s. + * + * Revision 1.4 2001/05/07 16:27:24 s_a_white + * Fix optimisation issues with gcc 2.96. + * + * Revision 1.3 2001/03/25 19:46:12 s_a_white + * _endian_h_ changed to _sidendian_h_. + * + * Revision 1.2 2001/03/10 19:49:32 s_a_white + * Removed bad include. + * + * Revision 1.1 2001/03/02 19:04:38 s_a_white + * Include structure modified for better compatibility + * + * Revision 1.5 2001/01/07 16:01:33 s_a_white + * sidendian.h is now installed, therefore endian defines updated. + * + * Revision 1.4 2000/12/13 17:53:01 s_a_white + * Fixes some of the endian calls. + * + * Revision 1.3 2000/12/12 19:39:15 s_a_white + * Removed bad const. + * + * Revision 1.2 2000/12/11 19:10:59 s_a_white + * AC99 Update. + * + ***************************************************************************/ + +#ifndef _sidendian_h_ +#define _sidendian_h_ + +// NOTE: The optimisations in this file rely on the structure of memory +// e.g. 2 shorts being contained in 1 long. Although these sizes are +// checked to make sure the optimisation is ok, gcc 2.96 (and above) +// introduced better optimisations. This results in caching of values +// in internal registers and therefore writes to ram through the aliases +// not being reflected in the CPU regs. The use of the volatile keyword +// fixes this. + +#include "sidtypes.h" + +#if defined(SID_WORDS_BIGENDIAN) +/* byte-order: HIHI..3210..LO */ +#elif defined(SID_WORDS_LITTLEENDIAN) +/* byte-order: LO..0123..HIHI */ +#else + #error Please check source code configuration! +#endif + +/* +Labeling: +0 - LO +1 - HI +2 - HILO +3 - HIHI +*/ + +/////////////////////////////////////////////////////////////////// +// INT16 FUNCTIONS +/////////////////////////////////////////////////////////////////// +// Set the lo byte (8 bit) in a word (16 bit) +inline void endian_16lo8 (uint_least16_t &word, uint8_t byte) +{ +#if defined(SID_OPTIMISE_MEMORY_ACCESS) +# if defined(SID_WORDS_LITTLEENDIAN) + ((volatile uint8_t *) &word)[0] = byte; +# else + ((volatile uint8_t *) &word)[1] = byte; +# endif + word = *((volatile uint_least16_t *) &word); +#else + word &= 0xff00; + word |= byte; +#endif +} + +// Get the lo byte (8 bit) in a word (16 bit) +inline uint8_t endian_16lo8 (uint_least16_t word) +{ + return (uint8_t) word; +} + +// Set the hi byte (8 bit) in a word (16 bit) +inline void endian_16hi8 (uint_least16_t &word, uint8_t byte) +{ +#if defined(SID_OPTIMISE_MEMORY_ACCESS) +# if defined(SID_WORDS_LITTLEENDIAN) + ((volatile uint8_t *) &word)[1] = byte; +# else + ((volatile uint8_t *) &word)[0] = byte; +# endif + word = *((volatile uint_least16_t *) &word); +#else + word &= 0x00ff; + word |= (uint_least16_t) byte << 8; +#endif +} + +// Set the hi byte (8 bit) in a word (16 bit) +inline uint8_t endian_16hi8 (uint_least16_t word) +{ +#if defined(SID_OPTIMISE_MEMORY_ACCESS) +# if defined(SID_WORDS_LITTLEENDIAN) + return ((uint8_t *) &word)[1]; +# else + return ((uint8_t *) &word)[0]; +# endif +#else + return (uint8_t) (word >> 8); +#endif +} + +// Swap word endian. +inline void endian_16swap8 (uint_least16_t &word) +{ + uint8_t lo = endian_16lo8 (word); + uint8_t hi = endian_16hi8 (word); + endian_16lo8 (word, hi); + endian_16hi8 (word, lo); +} + +// Convert high-byte and low-byte to 16-bit word. +inline uint_least16_t endian_16 (uint8_t hi, uint8_t lo) +{ + uint_least16_t word = 0x0000; + endian_16lo8 (word, lo); + endian_16hi8 (word, hi); + return word; +} + +// Convert high-byte and low-byte to 16-bit little endian word. +inline void endian_16 (uint8_t ptr[2], uint_least16_t word) +{ +#if defined(SID_OPTIMISE_MEMORY_ACCESS) + *((uint_least16_t *) ptr) = word; +#else +# if defined(SID_WORDS_BIGENDIAN) + ptr[0] = endian_16hi8 (word); + ptr[1] = endian_16lo8 (word); +# else + ptr[0] = endian_16lo8 (word); + ptr[1] = endian_16hi8 (word); +# endif +#endif +} + +inline void endian_16 (char ptr[2], uint_least16_t word) +{ + endian_16 ((uint8_t *) ptr, word); +} + +// Convert high-byte and low-byte to 16-bit little endian word. +inline uint_least16_t endian_little16 (const uint8_t ptr[2]) +{ +#if defined(SID_OPTIMISE_MEMORY_ACCESS) && \ + defined(SID_WORDS_LITTLEENDIAN) + return *((uint_least16_t *) ptr); +#else + return endian_16 (ptr[1], ptr[0]); +#endif +} + +// Write a little-endian 16-bit word to two bytes in memory. +inline void endian_little16 (uint8_t ptr[2], uint_least16_t word) +{ +#if defined(SID_OPTIMISE_MEMORY_ACCESS) && \ + defined(SID_WORDS_LITTLEENDIAN) + *((uint_least16_t *) ptr) = word; +#else + ptr[0] = endian_16lo8 (word); + ptr[1] = endian_16hi8 (word); +#endif +} + +// Convert high-byte and low-byte to 16-bit big endian word. +inline uint_least16_t endian_big16 (const uint8_t ptr[2]) +{ +#if defined(SID_OPTIMISE_MEMORY_ACCESS) && \ + defined(SID_WORDS_BIGENDIAN) + return *((uint_least16_t *) ptr); +#else + return endian_16 (ptr[0], ptr[1]); +#endif +} + +// Write a little-big 16-bit word to two bytes in memory. +inline void endian_big16 (uint8_t ptr[2], uint_least16_t word) +{ +#if defined(SID_OPTIMISE_MEMORY_ACCESS) && \ + defined(SID_WORDS_BIGENDIAN) + *((uint_least16_t *) ptr) = word; +#else + ptr[0] = endian_16hi8 (word); + ptr[1] = endian_16lo8 (word); +#endif +} + + +/////////////////////////////////////////////////////////////////// +// INT32 FUNCTIONS +/////////////////////////////////////////////////////////////////// +// Set the lo word (16bit) in a dword (32 bit) +inline void endian_32lo16 (uint_least32_t &dword, uint_least16_t word) +{ +#if defined(SID_OPTIMISE_MEMORY_ACCESS) +# if defined(SID_WORDS_LITTLEENDIAN) + ((volatile uint_least16_t *) &dword)[0] = word; +# else + ((volatile uint_least16_t *) &dword)[1] = word; +# endif + dword = *((volatile uint_least32_t *) &dword); +#else + dword &= (uint_least32_t) 0xffff0000; + dword |= word; +#endif +} + +// Get the lo word (16bit) in a dword (32 bit) +inline uint_least16_t endian_32lo16 (uint_least32_t dword) +{ + return (uint_least16_t) dword & 0xffff; +} + +// Set the hi word (16bit) in a dword (32 bit) +inline void endian_32hi16 (uint_least32_t &dword, uint_least16_t word) +{ +#if defined(SID_OPTIMISE_MEMORY_ACCESS) +# if defined(SID_WORDS_LITTLEENDIAN) + ((volatile uint_least16_t *) &dword)[1] = word; +# else + ((volatile uint_least16_t *) &dword)[0] = word; +# endif + dword = *((volatile uint_least32_t *) &dword); +#else + dword &= (uint_least32_t) 0x0000ffff; + dword |= (uint_least32_t) word << 16; +#endif +} + +// Get the hi word (16bit) in a dword (32 bit) +inline uint_least16_t endian_32hi16 (uint_least32_t dword) +{ +#if defined(SID_OPTIMISE_MEMORY_ACCESS) +# if defined(SID_WORDS_LITTLEENDIAN) + return ((uint_least16_t *) &dword)[1]; +# else + return ((uint_least16_t *) &dword)[0]; +# endif +#else + return (uint_least16_t) (dword >> 16); +#endif +} + +// Set the lo byte (8 bit) in a dword (32 bit) +inline void endian_32lo8 (uint_least32_t &dword, uint8_t byte) +{ +#if defined(SID_OPTIMISE_MEMORY_ACCESS) +# if defined(SID_WORDS_LITTLEENDIAN) + ((volatile uint8_t *) &dword)[0] = byte; +# else + ((volatile uint8_t *) &dword)[3] = byte; +# endif + dword = *((volatile uint_least32_t *) &dword); +#else + dword &= (uint_least32_t) 0xffffff00; + dword |= (uint_least32_t) byte; +#endif +} + +// Get the lo byte (8 bit) in a dword (32 bit) +inline uint8_t endian_32lo8 (uint_least32_t dword) +{ + return (uint8_t) dword; +} + +// Set the hi byte (8 bit) in a dword (32 bit) +inline void endian_32hi8 (uint_least32_t &dword, uint8_t byte) +{ +#if defined(SID_OPTIMISE_MEMORY_ACCESS) +# if defined(SID_WORDS_LITTLEENDIAN) + ((volatile uint8_t *) &dword)[1] = byte; +# else + ((volatile uint8_t *) &dword)[2] = byte; +# endif + dword = *((volatile uint_least32_t *) &dword); +#else + dword &= (uint_least32_t) 0xffff00ff; + dword |= (uint_least32_t) byte << 8; +#endif +} + +// Get the hi byte (8 bit) in a dword (32 bit) +inline uint8_t endian_32hi8 (uint_least32_t dword) +{ +#if defined(SID_OPTIMISE_MEMORY_ACCESS) +# if defined(SID_WORDS_LITTLEENDIAN) + return ((uint8_t *) &dword)[1]; +# else + return ((uint8_t *) &dword)[2]; +# endif +#else + return (uint8_t) (dword >> 8); +#endif +} + +// Swap hi and lo words endian in 32 bit dword. +inline void endian_32swap16 (uint_least32_t &dword) +{ + uint_least16_t lo = endian_32lo16 (dword); + uint_least16_t hi = endian_32hi16 (dword); + endian_32lo16 (dword, hi); + endian_32hi16 (dword, lo); +} + +// Swap word endian. +inline void endian_32swap8 (uint_least32_t &dword) +{ + uint_least16_t lo, hi; + lo = endian_32lo16 (dword); + hi = endian_32hi16 (dword); + endian_16swap8 (lo); + endian_16swap8 (hi); + endian_32lo16 (dword, hi); + endian_32hi16 (dword, lo); +} + +// Convert high-byte and low-byte to 32-bit word. +inline uint_least32_t endian_32 (uint8_t hihi, uint8_t hilo, uint8_t hi, uint8_t lo) +{ + uint_least32_t dword = 0x00000000; + uint_least16_t word = 0x0000; + endian_32lo8 (dword, lo); + endian_32hi8 (dword, hi); + endian_16lo8 (word, hilo); + endian_16hi8 (word, hihi); + endian_32hi16 (dword, word); + return dword; +} + +// Convert high-byte and low-byte to 32-bit little endian word. +inline uint_least32_t endian_little32 (const uint8_t ptr[4]) +{ +#if defined(SID_OPTIMISE_MEMORY_ACCESS) && \ + defined(SID_WORDS_LITTLEENDIAN) + return *((uint_least32_t *) ptr); +#else + return endian_32 (ptr[3], ptr[2], ptr[1], ptr[0]); +#endif +} + +// Write a little-endian 32-bit word to four bytes in memory. +inline void endian_little32 (uint8_t ptr[4], uint_least32_t dword) +{ +#if defined(SID_OPTIMISE_MEMORY_ACCESS) && \ + defined(SID_WORDS_LITTLEENDIAN) + *((uint_least32_t *) ptr) = dword; +#else + uint_least16_t word; + ptr[0] = endian_32lo8 (dword); + ptr[1] = endian_32hi8 (dword); + word = endian_32hi16 (dword); + ptr[2] = endian_16lo8 (word); + ptr[3] = endian_16hi8 (word); +#endif +} + +// Convert high-byte and low-byte to 32-bit big endian word. +inline uint_least32_t endian_big32 (const uint8_t ptr[4]) +{ +#if defined(SID_OPTIMISE_MEMORY_ACCESS) && \ + defined(SID_WORDS_BIGENDIAN) + return *((uint_least32_t *) ptr); +#else + return endian_32 (ptr[0], ptr[1], ptr[2], ptr[3]); +#endif +} + +// Write a big-endian 32-bit word to four bytes in memory. +inline void endian_big32 (uint8_t ptr[4], uint_least32_t dword) +{ +#if defined(SID_OPTIMISE_MEMORY_ACCESS) && \ + defined(SID_WORDS_BIGENDIAN) + *((uint_least32_t *) ptr) = dword; +#else + uint_least16_t word; + word = endian_32hi16 (dword); + ptr[1] = endian_16lo8 (word); + ptr[0] = endian_16hi8 (word); + ptr[2] = endian_32hi8 (dword); + ptr[3] = endian_32lo8 (dword); +#endif +} + +#endif // _sidendian_h_ + + diff --git a/PSID/sidplay/sidint.h b/PSID/sidplay/sidint.h new file mode 100644 index 00000000..c9f5034b --- /dev/null +++ b/PSID/sidplay/sidint.h @@ -0,0 +1,17 @@ +#ifndef _INCLUDE_SIDPLAY_SIDINT_H +#define _INCLUDE_SIDPLAY_SIDINT_H 1 +#ifndef _GENERATED_STDINT_H +#define _GENERATED_STDINT_H "psid64 1.2" +/* generated using a gnu compiler version gcc (GCC) 5.3.1 20151207 (Red Hat 5.3.1-2) Copyright (C) 2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ + +#include + + +/* system headers have good uint64_t */ +#ifndef _HAVE_UINT64_T +#define _HAVE_UINT64_T +#endif + + /* once */ +#endif +#endif diff --git a/PSID/sidplay/sidtypes.h b/PSID/sidplay/sidtypes.h new file mode 100644 index 00000000..788c21ab --- /dev/null +++ b/PSID/sidplay/sidtypes.h @@ -0,0 +1,62 @@ +/*************************************************************************** + sidtypes.h - type definition file + ------------------- + begin : Mon Jul 3 2000 + copyright : (C) 2000 by Simon White + email : s_a_white@email.com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program 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. * + * * + ***************************************************************************/ + +#ifndef _sidtypes_h_ +#define _sidtypes_h_ + +#include "sidint.h" +#include "sidconfig.h" + +#if SID_SIZEOF_CHAR == 1 +# if (SID_SIZEOF_SHORT_INT == 2) || (SID_SIZEOF_INT == 2) +# if (SID_SIZEOF_INT == 4) || (SID_SIZEOF_LONG_INT == 4) +//# define SID_OPTIMISE_MEMORY_ACCESS +# endif +# endif +#endif + +#if SID_SIZEOF_CHAR != 1 +# error Code cannot work correctly on this platform as no real 8 bit data type supported! +#endif + +#ifndef SID_HAVE_BOOL +# ifdef SID_HAVE_STDBOOL_H +# include +# else + typedef int bool; +# define true 1 +# define false 0 +# endif /* SID_HAVE_STDBOOL_H */ +#endif /* HAVE_BOOL */ + +/* Custom types */ +typedef int sid_fc_t[2]; +typedef struct +{ + sid_fc_t cutoff[0x800]; + uint_least16_t points; +} sid_filter_t; +#define sid_filter_t sid_filter_t + +typedef unsigned int uint; +typedef float float32_t; +typedef double float64_t; + +#define SID_FOREVER for(;;) +#define SID_SWAP(x,y) ((x)^=(y)^=(x)^=(y)) + +#endif /* _sidtypes_h_ */ diff --git a/PSID/sidplay/utils/SidDatabase.h b/PSID/sidplay/utils/SidDatabase.h new file mode 100644 index 00000000..36803da7 --- /dev/null +++ b/PSID/sidplay/utils/SidDatabase.h @@ -0,0 +1,50 @@ +/*************************************************************************** + SidDatabase.h - songlength database support + ------------------- + begin : Sun Mar 11 2001 + copyright : (C) 2001 by Simon White + email : s_a_white@email.com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program 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. * + * * + ***************************************************************************/ + +#ifndef _siddatabase_h_ +#define _siddatabase_h_ + +#include "SidTuneMod.h" +#include "libini.h" + +class SID_EXTERN SidDatabase +{ +private: + static const char *ERR_DATABASE_CORRUPT; + static const char *ERR_NO_DATABASE_LOADED; + static const char *ERR_NO_SELECTED_SONG; + static const char *ERR_MEM_ALLOC; + static const char *ERR_UNABLE_TO_LOAD_DATABASE; + + ini_fd_t database; + const char *errorString; + + int_least32_t parseTimeStamp (const char* arg); + uint_least8_t timesFound (char *str); + +public: + SidDatabase () : database (0), errorString(NULL) {;} + ~SidDatabase (); + + int open (const char *filename); + void close (); + int_least32_t length (SidTuneMod &tune); + int_least32_t length (const char *md5, uint_least16_t song); + const char * error (void) { return errorString; } +}; + +#endif // _siddatabase_h_ diff --git a/PSID/sidplay/utils/libini.h b/PSID/sidplay/utils/libini.h new file mode 100644 index 00000000..b1de0b67 --- /dev/null +++ b/PSID/sidplay/utils/libini.h @@ -0,0 +1,219 @@ +/*************************************************************************** + libini.h - Header file of functions to + manipulate an ini file. + ------------------- + begin : Fri Apr 21 2000 + copyright : (C) 2000 by Simon White + email : s_a_white@email.com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program 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. * + * * + ***************************************************************************/ + +#ifndef _libini_h_ +#define _libini_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define INI_ADD_EXTRAS +#define INI_ADD_LIST_SUPPORT + +#ifdef SWIG +%ignore ini_readString; +%include typemaps.i +%apply int *INOUT { int *value }; +%apply long *INOUT { long *value }; +%apply double *INOUT { double *value }; +%rename (ini_readString) ini_readFileToBuffer; +#define INI_EXTERN +#define INI_STATIC +#endif /* SWIG */ + +/*#ifdef _WINDOWS +# define INI_LINKAGE __stdcall +#else*/ +# define INI_LINKAGE +/*#endif*/ + +/* DLL building support on win32 hosts */ +#ifndef INI_EXTERN +# ifdef DLL_EXPORT /* defined by libtool (if required) */ +# define INI_EXTERN __declspec(dllexport) +# endif +# ifdef LIBINI_DLL_IMPORT /* define if linking with this dll */ +# define INI_EXTERN extern __declspec(dllimport) +# endif +# ifndef INI_EXTERN /* static linking or !_WIN32 */ +# define INI_EXTERN extern +# endif +#endif + +#ifndef INI_ADD_EXTRAS +#undef INI_ADD_LIST_SUPPORT +#endif + +/* Compatibility with future C++ code */ +#ifndef ini_fd_t +#define ini_fd_t ini_fd_t +typedef void* ini_fd_t; +#endif + + +/* Rev 1.2 Added new fuction */ +INI_EXTERN ini_fd_t INI_LINKAGE ini_open (const char *name, const char *mode, + const char *comment); +INI_EXTERN int INI_LINKAGE ini_close (ini_fd_t fd); +INI_EXTERN int INI_LINKAGE ini_flush (ini_fd_t fd); +INI_EXTERN int INI_LINKAGE ini_delete (ini_fd_t fd); + +/* Rev 1.2 Added these functions to make life a bit easier, can still be implemented + * through ini_writeString though. */ +INI_EXTERN int INI_LINKAGE ini_locateKey (ini_fd_t fd, const char *key); +INI_EXTERN int INI_LINKAGE ini_locateHeading (ini_fd_t fd, const char *heading); +INI_EXTERN int INI_LINKAGE ini_deleteKey (ini_fd_t fd); +INI_EXTERN int INI_LINKAGE ini_deleteHeading (ini_fd_t fd); + +INI_EXTERN const char * INI_LINKAGE ini_currentKey (ini_fd_t fd); +INI_EXTERN const char * INI_LINKAGE ini_currentHeading (ini_fd_t fd); + +/* Returns the number of bytes required to be able to read the key as a + * string from the file. (1 should be added to this length to account + * for a NULL character). If delimiters are used, returns the length + * of the next data element in the key to be read */ +INI_EXTERN int INI_LINKAGE ini_dataLength (ini_fd_t fd); + +/* Default Data Type Operations + * Arrays implemented to help with reading, for writing you should format the + * complete array as a string and perform an ini_writeString. */ +INI_EXTERN int INI_LINKAGE ini_readString (ini_fd_t fd, char *str, size_t size); +INI_EXTERN int INI_LINKAGE ini_writeString (ini_fd_t fd, const char *str); +INI_EXTERN int INI_LINKAGE ini_readInt (ini_fd_t fd, int *value); + + +#ifdef INI_ADD_EXTRAS + /* Read Operations */ + INI_EXTERN int INI_LINKAGE ini_readLong (ini_fd_t fd, long *value); + INI_EXTERN int INI_LINKAGE ini_readDouble (ini_fd_t fd, double *value); + INI_EXTERN int INI_LINKAGE ini_readBool (ini_fd_t fd, int *value); + + /* Write Operations */ + INI_EXTERN int INI_LINKAGE ini_writeInt (ini_fd_t fd, int value); + INI_EXTERN int INI_LINKAGE ini_writeLong (ini_fd_t fd, long value); + INI_EXTERN int INI_LINKAGE ini_writeDouble (ini_fd_t fd, double value); + INI_EXTERN int INI_LINKAGE ini_writeBool (ini_fd_t fd, int value); + + /* Extra Functions */ + INI_EXTERN int INI_LINKAGE ini_append (ini_fd_t fddst, ini_fd_t fdsrc); +#endif /* INI_ADD_EXTRAS */ + + +#ifdef INI_ADD_LIST_SUPPORT + /* Rev 1.1 Added - List Operations (Used for read operations only) + * Be warned, once delimiters are set, every key that is read will be checked for the + * presence of sub strings. This will incure a speed hit and therefore once a line + * has been read and list/array functionality is not required, set delimiters + * back to NULL. + */ + + /* Returns the number of elements in an list being seperated by the provided delimiters */ + INI_EXTERN int INI_LINKAGE ini_listLength (ini_fd_t fd); + /* Change delimiters, default "" */ + INI_EXTERN int INI_LINKAGE ini_listDelims (ini_fd_t fd, const char *delims); + /* Set index to access in a list. When read the index will automatically increment */ + INI_EXTERN int INI_LINKAGE ini_listIndex (ini_fd_t fd, unsigned long index); +#endif /* INI_ADD_LIST_SUPPORT */ + + +#ifdef SWIG +%{ +#include +#include +#define INI_STATIC static +typedef struct +{ + char *buffer; + size_t size; +} ini_buffer_t; +%} + +%inline %{ +/************************************************************* + * SWIG helper functions to create C compatible string buffers + *************************************************************/ +INI_STATIC ini_buffer_t *ini_createBuffer (unsigned int size) +{ + ini_buffer_t *b; + /* Allocate memory to structure */ + if (size == ( ((unsigned) -1 << 1) >> 1 )) + return 0; /* Size is too big */ + b = malloc (sizeof (ini_buffer_t)); + if (!b) + return 0; + + /* Allocate memory to buffer */ + b->buffer = malloc (sizeof (char) * (size + 1)); + if (!b->buffer) + { + free (b); + return 0; + } + b->size = size; + b->buffer[0] = '\0'; + + /* Returns address to tcl */ + return b; +} + +INI_STATIC void ini_deleteBuffer (ini_buffer_t *buffer) +{ + if (!buffer) + return; + free (buffer->buffer); + free (buffer); +} + +INI_STATIC int ini_readFileToBuffer (ini_fd_t fd, ini_buffer_t *buffer) +{ + if (!buffer) + return -1; + return ini_readString (fd, buffer->buffer, buffer->size + 1); +} + +INI_STATIC char *ini_getBuffer (ini_buffer_t *buffer) +{ + if (!buffer) + return ""; + return buffer->buffer; +} + +INI_STATIC int ini_setBuffer (ini_buffer_t *buffer, const char *str) +{ + size_t len; + if (!buffer) + return -1; + len = strlen (str); + if (len > buffer->size) + len = buffer->size; + + memcpy (buffer->buffer, str, len); + buffer->buffer[len] = '\0'; + return len; +} +%} +#endif /* SWIG */ + +#ifdef __cplusplus +} +#endif + +#endif /* _libini_h_ */ diff --git a/PSID/sidplay/utils/sidtunemod.h b/PSID/sidplay/utils/sidtunemod.h new file mode 100644 index 00000000..1e611739 --- /dev/null +++ b/PSID/sidplay/utils/sidtunemod.h @@ -0,0 +1,39 @@ +/* + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef SIDTUNEMOD_H +#define SIDTUNEMOD_H + +#include "../SidTune.h" + +#define SIDTUNE_MD5_LENGTH 32 + + +class SID_EXTERN SidTuneMod : public SidTune +{ + private: + char m_md5[SIDTUNE_MD5_LENGTH+1]; + + public: // --------------------------------------------------------- public + + explicit SidTuneMod(const char* fileName) : SidTune(fileName) + { m_md5[0] = '\0'; } + + // Not providing an md5 buffer will cause the internal one to be used + const char *createMD5(char *md5 = 0); // Buffer must be SIDTUNE_MD5_LENGTH + 1 +}; + +#endif /* SIDTUNEMOD_H */ diff --git a/PSID/sidtune/IconInfo.cpp b/PSID/sidtune/IconInfo.cpp new file mode 100644 index 00000000..5acaf3b7 --- /dev/null +++ b/PSID/sidtune/IconInfo.cpp @@ -0,0 +1,575 @@ +/* + * /home/ms/files/source/libsidtune/RCS/IconInfo.cpp,v + * + * Amiga icon tooltype PlaySID file format (.info) support. + * + * This is a derived work, courtesy of Peter Kunath, who has + * provided an examplary source code to examine an Amiga icon file. + * + * It has been ported and heavily modified to suit certain + * requirements. This replaces the old code, which was simply + * scanning input data for a first, presumedly constant, Id string. + * This code does not require the default tool to serve as a + * constant Id by containing "SID:PlaySID". + * + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "../config.h" +#include "../SidPlay/SidTune.h" +#include "../SidPlay/SmartPtr.h" +#include "SidTuneTools.h" +#include "../SidPlay/sidendian.h" + +#ifdef HAVE_EXCEPTIONS +# include +#endif +#include +#if defined(HAVE_SSTREAM) +# include +#else +# include +#endif + +// Amiga Workbench specific structures. + +struct Border +{ + uint8_t LeftEdge[2]; // uint_least16_t; initial offsets from the origin + uint8_t TopEdge[2]; // uint_least16_t + uint8_t FrontPen, BackPen; // pens numbers for rendering + uint8_t DrawMode; // mode for rendering + uint8_t Count; // number of XY pairs + uint8_t pXY[4]; // int_least16_t *XY; vector coordinate pairs rel to LeftTop + uint8_t pNextBorder[4]; // Border *NextBorder; pointer to any other Border too +}; + +struct Image +{ + uint8_t LeftEdge[2]; // uint_least16_t; starting offset relative to some origin + uint8_t TopEdge[2]; // uint_least16_t; starting offsets relative to some origin + uint8_t Width[2]; // uint_least16_t; pixel size (though data is word-aligned) + uint8_t Height[2]; // uint_least16_t + uint8_t Depth[2]; // uint_least16_t; >= 0, for images you create + uint8_t pImageData[4]; // uint_least16_t *ImageData; pointer to the actual word-aligned bits + uint8_t PlanePick, PlaneOnOff; + uint8_t pNextImage[4]; // Image *NextImage; +}; + +struct Gadget +{ + // uint8_t pNextGadget[4]; // Gadget *NextGadget; next gadget in the list + // uint8_t LeftEdge[2]; // uint_least16_t; "hit box" of gadget + // uint8_t TopEdge[2]; // uint_least16_t + uint8_t Width[2]; // uint_least16_t; "hit box" of gadget + uint8_t Height[2]; // uint_least16_t + uint8_t Flags[2]; // uint_least16_t; see below for list of defines + // uint8_t Activation[2]; // uint_least16_t + // uint8_t GadgetType[2]; // uint_least16_t; see below for defines + uint8_t pGadgetRender[4]; // Image *GadgetRender; + uint8_t pSelectRender[4]; // Image *SelectRender; + // uint8_t pGadgetText[4]; // void *GadgetText; + // uint8_t MutualExclude[4]; // udword + // uint8_t pSpecialInfo[4]; // void *SpecialInfo; + // uint8_t GadgetID[2]; // uint_least16_t + // uint8_t UserData[4]; // udword; ptr to general purpose User data +}; + +struct DiskObject +{ + uint8_t Magic[2]; // uint_least16_t; a magic num at the start of the file + uint8_t Version[2]; // uint_least16_t; a version number, so we can change it + struct Gadget Gadget; // a copy of in core gadget + uint8_t Type; + uint8_t PAD_BYTE; // Pad it out to the next word boundry + uint8_t pDefaultTool[4]; // char *DefaultTool; + uint8_t ppToolTypes[4]; // char **ToolTypes; + uint8_t CurrentX[4]; // udword + uint8_t CurrentY[4]; // udword + uint8_t pDrawerData[4]; // char *DrawerData; + uint8_t pToolWindow[4]; // char *ToolWindow; only applies to tools + uint8_t StackSize[4]; // udword; only applies to tools +}; + + +// A magic number, not easily impersonated. +#define WB_DISKMAGIC 0xE310 +// Our current version number. +#define WB_DISKVERSION 1 +// Our current revision number. +#define WB_DISKREVISION 1 +// I only use the lower 8 bits of Gadget.UserData for the revision #. +#define WB_DISKREVISIONMASK 0xFF + +// The Workbench object types. +#define WB_DISK 1 +#define WB_DRAWER 2 +#define WB_TOOL 3 +#define WB_PROJECT 4 +#define WB_GARBAGE 5 +#define WB_DEVICE 6 +#define WB_KICK 7 +#define WB_APPICON 8 + +// --- Gadget.Flags values --- +// Combinations in these bits describe the highlight technique to be used. +#define GFLG_GADGHIGHBITS 0x0003 +// Complement the select box. +#define GFLG_GADGHCOMP 0x0000 +// Draw a box around the image. +#define GFLG_GADGHBOX 0x0001 +// Blast in this alternate image. +#define GFLG_GADGHIMAGE 0x0002 +// Don't highlight. +#define GFLG_GADGHNONE 0x0003 +// Set if GadgetRender and SelectRender point to an Image structure, +// clear if they point to Border structures. +#define GFLG_GADGIMAGE 0x0004 + +static const char _sidtune_txt_format[] = "Raw plus PlaySID icon tooltype file (INFO)"; + +static const char _sidtune_keyword_id[] = "SID:PLAYSID"; +static const char _sidtune_keyword_address[] = "ADDRESS="; +static const char _sidtune_keyword_songs[] = "SONGS="; +static const char _sidtune_keyword_speed[] = "SPEED="; +static const char _sidtune_keyword_name[] = "NAME="; +static const char _sidtune_keyword_author[] = "AUTHOR="; +static const char _sidtune_keyword_copyright[] = "COPYRIGHT="; // Depreicated +static const char _sidtune_keyword_released[] = "RELEASED="; +static const char _sidtune_keyword_musPlayer[] = "SIDSONG=YES"; +static const char _sidtune_keyword_reloc[] = "RELOC="; +static const char _sidtune_keyword_clock[] = "CLOCK="; +static const char _sidtune_keyword_sidModel[] = "SIDMODEL="; +static const char _sidtune_keyword_compatibility[] = "COMPATIBILITY="; + +static const char _sidtune_txt_noMemError[] = "ERROR: Not enough free memory"; +static const char _sidtune_txt_corruptError[] = "ERROR: Info file is incomplete or corrupt"; +static const char _sidtune_txt_noStringsError[] = "ERROR: Info file does not contain required strings"; +static const char _sidtune_txt_invalidError[] = "SIDTUNE ERROR: File contains invalid data"; +#if defined(SIDTUNE_REJECT_UNKNOWN_FIELDS) +const char _sidtune_txt_chunkError[] = "ERROR: Invalid tooltype information in icon file"; +#endif + +static const uint_least16_t safeBufferSize = 64; // for string comparison, stream parsing + + +SidTune::LoadStatus SidTune::INFO_fileSupport(Buffer_sidtt& dataBuf, + Buffer_sidtt& infoBuf) +{ + uint_least32_t infoLength = infoBuf.len(); + // Require a first minimum safety size. + uint_least32_t minSize = 1+sizeof(struct DiskObject); + if (infoLength < minSize) + return( LOAD_NOT_MINE ); + + const DiskObject *dobject = reinterpret_cast(infoBuf.get()); + + // Require Magic_Id in the first two bytes of the file. + if ( endian_16(dobject->Magic[0],dobject->Magic[1]) != WB_DISKMAGIC ) + return LOAD_NOT_MINE; + + // Only version 1.x supported. + if ( endian_16(dobject->Version[0],dobject->Version[1]) != WB_DISKVERSION ) + return LOAD_NOT_MINE; + + // A PlaySID icon must be of type project. + if ( dobject->Type != WB_PROJECT ) + return LOAD_NOT_MINE; + + uint i; // general purpose index variable + + // We want to skip a possible Gadget Image item. + const char *icon = (const char*)infoBuf.get() + sizeof(DiskObject); + + if ( (endian_16(dobject->Gadget.Flags[0],dobject->Gadget.Flags[1]) & GFLG_GADGIMAGE) == 0) + { + // Calculate size of gadget borders (vector image). + + if (dobject->Gadget.pGadgetRender[0] | + dobject->Gadget.pGadgetRender[1] | + dobject->Gadget.pGadgetRender[2] | + dobject->Gadget.pGadgetRender[3]) // border present? + { + // Require another minimum safety size. + minSize += sizeof(struct Border); + if (infoLength < minSize) + return( LOAD_NOT_MINE ); + + const Border *brd = reinterpret_cast(icon); + icon += sizeof(Border); + icon += brd->Count * (2+2); // pair of uint_least16_t + } + + if (dobject->Gadget.pSelectRender[0] | + dobject->Gadget.pSelectRender[1] | + dobject->Gadget.pSelectRender[2] | + dobject->Gadget.pSelectRender[3]) // alternate border present? + { + // Require another minimum safety size. + minSize += sizeof(Border); + if (infoLength < minSize) + return( LOAD_NOT_MINE ); + + const Border *brd = reinterpret_cast(icon); + icon += sizeof(Border); + icon += brd->Count * (2+2); // pair of uint_least16_t + } + } + else + { + // Calculate size of gadget images (bitmap image). + + if (dobject->Gadget.pGadgetRender[0] | + dobject->Gadget.pGadgetRender[1] | + dobject->Gadget.pGadgetRender[2] | + dobject->Gadget.pGadgetRender[3]) // image present? + { + // Require another minimum safety size. + minSize += sizeof(Image); + if (infoLength < minSize) + return( LOAD_NOT_MINE ); + + const Image *img = reinterpret_cast(icon); + icon += sizeof(Image); + + uint_least32_t imgsize = 0; + for(i=0;iDepth[0],img->Depth[1]);i++) + { + if ( (img->PlanePick & (1<Width[0],img->Width[1])+15)/16)*2; // bytes per line + imgsize *= endian_16(img->Height[0],img->Height[1]); // bytes per plane + + icon += imgsize; + } + + if (dobject->Gadget.pSelectRender[0] | + dobject->Gadget.pSelectRender[1] | + dobject->Gadget.pSelectRender[2] | + dobject->Gadget.pSelectRender[3]) // alternate image present? + { + // Require another minimum safety size. + minSize += sizeof(Image); + if (infoLength < minSize) + return( LOAD_NOT_MINE ); + + const Image *img = reinterpret_cast(icon); + icon += sizeof(Image); + + uint_least32_t imgsize = 0; + for(i=0;iDepth[0],img->Depth[1]);i++) + { + if ( (img->PlanePick & (1<Width[0],img->Width[1])+15)/16)*2; // bytes per line + imgsize *= endian_16(img->Height[0],img->Height[1]); // bytes per plane + icon += imgsize; + } + } + + // Here use a smart pointer to prevent access violation errors. + SmartPtr_sidtt spTool((const char*)icon,infoLength-(uint_least32_t)(icon-(const char*)infoBuf.get())); + if ( !spTool ) + { + info.formatString = _sidtune_txt_corruptError; + return LOAD_ERROR; + } + + // A separate safe buffer is used for each tooltype string. +#ifdef HAVE_EXCEPTIONS + SmartPtr_sidtt spCmpBuf(new(std::nothrow) char[safeBufferSize],safeBufferSize,true); +#else + SmartPtr_sidtt spCmpBuf(new char[safeBufferSize],safeBufferSize,true); +#endif + if ( !spCmpBuf ) + { + info.formatString = _sidtune_txt_noMemError; + return LOAD_ERROR; + } + +#ifndef SID_HAVE_BAD_COMPILER + char* cmpBuf = spCmpBuf.tellBegin(); +#else + // This should not be necessary, but for some reason + // Microsoft Visual C++ says spCmpBuf is const... + char* cmpBuf = (char*) spCmpBuf.tellBegin(); +#endif + + // Skip default tool. + spTool += endian_32(spTool[0],spTool[1],spTool[2],spTool[3]) + 4; + + // Defaults. + fileOffset = 0; // no header in separate data file + uint_least32_t oldStyleSpeed = 0; + + // Flags for required entries. + bool hasAddress = false, + hasName = false, + hasAuthor = false, + hasReleased = false, + hasSongs = false, + hasSpeed = false, + hasInitAddr = false; + + // Calculate number of tooltype strings. + i = (endian_32(spTool[0],spTool[1],spTool[2],spTool[3])/4) - 1; + spTool += 4; // skip size info + + while( i-- > 0 ) + { + // Get length of this tool. + uint_least32_t toolLen = endian_32(spTool[0],spTool[1],spTool[2],spTool[3]); + spTool += 4; // skip tool length + // Copy item to safe buffer. + for ( uint ci = 0; ci < toolLen; ci++ ) + { +#ifndef SID_HAVE_BAD_COMPILER + spCmpBuf[ci] = spTool[ci]; +#else + // This should not be necessary, but for some reason + // Microsoft Visual C++ says spCmpBuf is const... + (*((char*) (&spCmpBuf[ci]))) = (char) spTool[ci]; +#endif + } + if ( !(spTool&&spCmpBuf) ) + { + return LOAD_ERROR; + } + + // Now check all possible keywords. + if ( SidTuneTools::myStrNcaseCmp(cmpBuf,_sidtune_keyword_address) == 0 ) + { +#ifdef HAVE_SSTREAM + std::string sAddrIn( cmpBuf + strlen(_sidtune_keyword_address), + toolLen - strlen(_sidtune_keyword_address) ); + std::istringstream addrIn( sAddrIn ); +#else + std::istrstream addrIn( cmpBuf + strlen(_sidtune_keyword_address), + toolLen - strlen(_sidtune_keyword_address) ); +#endif + info.loadAddr = (uint_least16_t)SidTuneTools::readHex( addrIn ); + info.initAddr = info.loadAddr; + hasInitAddr = true; + info.initAddr = (uint_least16_t)SidTuneTools::readHex( addrIn ); + if ( addrIn ) + { + info.playAddr = (uint_least16_t)SidTuneTools::readHex( addrIn ); + if ( !addrIn ) + { + return LOAD_ERROR; + } + hasAddress = true; + } + } + else if ( SidTuneTools::myStrNcaseCmp(cmpBuf,_sidtune_keyword_songs) == 0 ) + { +#ifdef HAVE_SSTREAM + std::string sNumIn( cmpBuf + strlen(_sidtune_keyword_songs), + toolLen - strlen(_sidtune_keyword_songs) ); + std::istringstream numIn( sNumIn ); +#else + std::istrstream numIn( cmpBuf + strlen(_sidtune_keyword_songs), + toolLen - strlen(_sidtune_keyword_songs) ); +#endif + if ( !numIn ) + { + return LOAD_ERROR; + } + info.songs = (uint_least16_t)SidTuneTools::readDec( numIn ); + info.startSong = (uint_least16_t)SidTuneTools::readDec( numIn ); + hasSongs = true; + } + else if ( SidTuneTools::myStrNcaseCmp(cmpBuf,_sidtune_keyword_speed) == 0 ) + { +#ifdef HAVE_SSTREAM + std::string sSpeedIn( cmpBuf + strlen(_sidtune_keyword_speed), + toolLen - strlen(_sidtune_keyword_speed) ); + std::istringstream speedIn( sSpeedIn ); +#else + std::istrstream speedIn( cmpBuf + strlen(_sidtune_keyword_speed), + toolLen - strlen(_sidtune_keyword_speed) ); +#endif + if ( !speedIn ) + { + return LOAD_ERROR; + } + oldStyleSpeed = SidTuneTools::readHex(speedIn); + hasSpeed = true; + } + else if ( SidTuneTools::myStrNcaseCmp(cmpBuf,_sidtune_keyword_name) == 0 ) + { + SidTuneTools::copyStringValueToEOL(cmpBuf,&infoString[2][0],SIDTUNE_MAX_CREDIT_STRLEN); + info.infoString[0] = &infoString[0][0]; + hasName = true; + } + else if ( SidTuneTools::myStrNcaseCmp(cmpBuf,_sidtune_keyword_author) == 0 ) + { + SidTuneTools::copyStringValueToEOL(cmpBuf,&infoString[2][0],SIDTUNE_MAX_CREDIT_STRLEN); + info.infoString[1] = &infoString[1][0]; + hasAuthor = true; + } + else if ( SidTuneTools::myStrNcaseCmp(cmpBuf,_sidtune_keyword_copyright) == 0 ) + { + SidTuneTools::copyStringValueToEOL(cmpBuf,&infoString[2][0],SIDTUNE_MAX_CREDIT_STRLEN); + info.infoString[2] = &infoString[2][0]; + hasReleased = true; + } + else if ( SidTuneTools::myStrNcaseCmp(cmpBuf,_sidtune_keyword_musPlayer) == 0 ) + { + info.musPlayer = true; + } + + + // New extensions from here on! + else if ( SidTuneTools::myStrNcaseCmp(cmpBuf, _sidtune_keyword_released) == 0 ) + { + SidTuneTools::copyStringValueToEOL(cmpBuf,&infoString[2][0],SIDTUNE_MAX_CREDIT_STRLEN); + info.infoString[2] = &infoString[2][0]; + hasReleased = true; + } + else if ( SidTuneTools::myStrNcaseCmp(cmpBuf,_sidtune_keyword_reloc) == 0 ) + { +#ifdef HAVE_SSTREAM + std::string sRelocIn( cmpBuf + strlen(_sidtune_keyword_reloc), + toolLen - strlen(_sidtune_keyword_reloc) ); + std::istringstream relocIn( sRelocIn ); +#else + std::istrstream relocIn( cmpBuf + strlen(_sidtune_keyword_reloc), + toolLen - strlen(_sidtune_keyword_reloc) ); +#endif + info.relocStartPage = (uint_least8_t)SidTuneTools::readHex( relocIn ); + if ( !relocIn ) + break; + info.relocPages = (uint_least8_t)SidTuneTools::readHex( relocIn ); + } + else if ( SidTuneTools::myStrNcaseCmp(cmpBuf,_sidtune_keyword_clock) == 0 ) + { + char clock[8]; + SidTuneTools::copyStringValueToEOL(cmpBuf,clock,sizeof(clock)); + if ( SidTuneTools::myStrNcaseCmp( clock, "UNKNOWN" ) == 0 ) + info.clockSpeed = SIDTUNE_CLOCK_UNKNOWN; + else if ( SidTuneTools::myStrNcaseCmp( clock, "PAL" ) == 0 ) + info.clockSpeed = SIDTUNE_CLOCK_PAL; + else if ( SidTuneTools::myStrNcaseCmp( clock, "NTSC" ) == 0 ) + info.clockSpeed = SIDTUNE_CLOCK_NTSC; + else if ( SidTuneTools::myStrNcaseCmp( clock, "ANY" ) == 0 ) + info.clockSpeed = SIDTUNE_CLOCK_ANY; + } + else if ( SidTuneTools::myStrNcaseCmp(cmpBuf,_sidtune_keyword_sidModel) == 0 ) + { + char model[8]; + SidTuneTools::copyStringValueToEOL(cmpBuf,model,sizeof(model)); + if ( SidTuneTools::myStrNcaseCmp( model, "UNKNOWN" ) == 0 ) + info.sidModel = SIDTUNE_SIDMODEL_UNKNOWN; + else if ( SidTuneTools::myStrNcaseCmp( model, "6581" ) == 0 ) + info.sidModel = SIDTUNE_SIDMODEL_6581; + else if ( SidTuneTools::myStrNcaseCmp( model, "8580" ) == 0 ) + info.sidModel = SIDTUNE_SIDMODEL_8580; + else if ( SidTuneTools::myStrNcaseCmp( model, "ANY" ) == 0 ) + info.sidModel = SIDTUNE_SIDMODEL_ANY; + } + else if ( SidTuneTools::myStrNcaseCmp(cmpBuf,_sidtune_keyword_compatibility) == 0 ) + { + char comp[6]; + SidTuneTools::copyStringValueToEOL(cmpBuf,comp,sizeof(comp)); + if ( SidTuneTools::myStrNcaseCmp( comp, "C64" ) == 0 ) + info.compatibility = SIDTUNE_COMPATIBILITY_C64; + else if ( SidTuneTools::myStrNcaseCmp( comp, "PSID" ) == 0 ) + info.compatibility = SIDTUNE_COMPATIBILITY_PSID; + else if ( SidTuneTools::myStrNcaseCmp( comp, "R64" ) == 0 ) + info.compatibility = SIDTUNE_COMPATIBILITY_R64; + else if ( SidTuneTools::myStrNcaseCmp( comp, "BASIC" ) == 0 ) + info.compatibility = SIDTUNE_COMPATIBILITY_BASIC; + } + + else + { +#if defined(SIDTUNE_REJECT_UNKNOWN_FIELDS) + info.formatString = _sidtune_txt_chunkError; + return LOAD_ERROR; +#endif + } + // Skip to next tool. + spTool += toolLen; + } + + if ( !(hasName && hasAuthor && hasReleased && hasSongs) ) + { // Something is missing (or damaged ?). + // Error string set above. + if ( hasName || hasAuthor || hasReleased || hasSongs ) + { // Something is missing (or damaged?). + info.formatString = _sidtune_txt_corruptError; + } + else + { // No PlaySID conform info strings. + info.formatString = _sidtune_txt_noStringsError; + } + return LOAD_ERROR; + } + + switch ( info.compatibility ) + { + case SIDTUNE_COMPATIBILITY_PSID: + case SIDTUNE_COMPATIBILITY_C64: + if ( ! (hasAddress && hasSpeed) ) + { + info.formatString = _sidtune_txt_corruptError; + return LOAD_ERROR; + } + break; + + case SIDTUNE_COMPATIBILITY_R64: + if ( !(hasInitAddr || hasAddress) ) + { + info.formatString = _sidtune_txt_corruptError; + return LOAD_ERROR; + } + // Allow user to provide single address + if ( !hasAddress ) + info.loadAddr = 0; + else if ( info.loadAddr || info.playAddr ) + { + info.formatString = _sidtune_txt_invalidError; + return LOAD_ERROR; + } + // Deliberate run on + + case SIDTUNE_COMPATIBILITY_BASIC: + oldStyleSpeed = ~0; + } + + // Create the speed/clock setting table. + convertOldStyleSpeedToTables(oldStyleSpeed, info.clockSpeed); + info.numberOfInfoStrings = 3; + // We finally accept the input data. + info.formatString = _sidtune_txt_format; + if ( info.musPlayer && !dataBuf.isEmpty() ) + return MUS_load (dataBuf); + return LOAD_OK; +} diff --git a/PSID/sidtune/InfoFile.cpp b/PSID/sidtune/InfoFile.cpp new file mode 100644 index 00000000..9832370a --- /dev/null +++ b/PSID/sidtune/InfoFile.cpp @@ -0,0 +1,468 @@ +/* + * /home/ms/files/source/libsidtune/RCS/InfoFile.cpp,v + * + * SIDPLAY INFOFILE format support. + * + + this code has been modified for integration into the Sidekick64 software + (the modifications are mostly removing everything that does not compile with vanilla Circle, + I really feel sorry for having disfigured this code, please refer to the original repository + if you want to get the real and decent psid64 version) + + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "../config.h" + +#ifdef HAVE_EXCEPTIONS +# include +#endif +#include +#include +#include +#include + +#if defined(HAVE_SSTREAM) +# include +#else +# include +#endif + +#include "../SidPlay/SidTune.h" +#include "SidTuneTools.h" +#include "../SidPlay/sidendian.h" + + +static const char text_format[] = "Raw plus SIDPLAY ASCII text file (SID)"; + +static const char text_truncatedError[] = "SIDTUNE ERROR: SID file is truncated"; +static const char text_noMemError[] = "SIDTUNE ERROR: Not enough free memory"; +static const char text_invalidError[] = "SIDTUNE ERROR: File contains invalid data"; + +static const char keyword_id[] = "SIDPLAY INFOFILE"; + +static const char keyword_name[] = "NAME="; // No white-space characters +static const char keyword_author[] = "AUTHOR="; // in these keywords, because +static const char keyword_copyright[] = "COPYRIGHT="; // we want to use a white-space (depreciated) +static const char keyword_released[] = "RELEASED="; // we want to use a white-space +static const char keyword_address[] = "ADDRESS="; // eating string stream to +static const char keyword_songs[] = "SONGS="; // parse most of the header. +static const char keyword_speed[] = "SPEED="; +static const char keyword_musPlayer[] = "SIDSONG=YES"; +static const char keyword_reloc[] = "RELOC="; +static const char keyword_clock[] = "CLOCK="; +static const char keyword_sidModel[] = "SIDMODEL="; +static const char keyword_compatibility[] = "COMPATIBILITY="; + +static const uint_least16_t sidMinFileSize = 1+sizeof(keyword_id); // Just to avoid a first segm.fault. +static const uint_least16_t parseChunkLen = 80; // Enough for all keywords incl. their values. + + +SidTune::LoadStatus SidTune::SID_fileSupport(Buffer_sidtt& dataBuf, + Buffer_sidtt& sidBuf) +{ + uint_least32_t parseLen = sidBuf.len(); + // Make sure SID buffer pointer is not zero. + // Check for a minimum file size. If it is smaller, we will not proceed. + if (parseLen> c; + pParseChunk[i] = c; + } + pParseChunk[parseChunkLen]=0; + // Now check for the possible keywords. + // ADDRESS + if ( SidTuneTools::myStrNcaseCmp( pParseChunk, keyword_address ) == 0 ) + { + SidTuneTools::skipToEqu( parseStream ); + info.loadAddr = (uint_least16_t)SidTuneTools::readHex( parseStream ); + info.initAddr = info.loadAddr; + hasInitAddr = true; + if ( parseStream ) + { + info.initAddr = (uint_least16_t)SidTuneTools::readHex( parseStream ); + if ( !parseStream ) + break; + info.playAddr = (uint_least16_t)SidTuneTools::readHex( parseStream ); + hasAddress = true; + } + } + // NAME + else if ( SidTuneTools::myStrNcaseCmp( pParseChunk, keyword_name ) == 0 ) + { + SidTuneTools::copyStringValueToEOL(pParseBuf,&infoString[0][0],SIDTUNE_MAX_CREDIT_STRLEN); + info.infoString[0] = &infoString[0][0]; + hasName = true; + } + // AUTHOR + else if ( SidTuneTools::myStrNcaseCmp( pParseChunk, keyword_author ) == 0 ) + { + SidTuneTools::copyStringValueToEOL(pParseBuf,&infoString[1][0],SIDTUNE_MAX_CREDIT_STRLEN); + info.infoString[1] = &infoString[1][0]; + hasAuthor = true; + } + // COPYRIGHT + else if ( SidTuneTools::myStrNcaseCmp( pParseChunk, keyword_copyright ) == 0 ) + { + SidTuneTools::copyStringValueToEOL(pParseBuf,&infoString[2][0],SIDTUNE_MAX_CREDIT_STRLEN); + info.infoString[2] = &infoString[2][0]; + hasReleased = true; + } + // RELEASED + else if ( SidTuneTools::myStrNcaseCmp( pParseChunk, keyword_released ) == 0 ) + { + SidTuneTools::copyStringValueToEOL(pParseBuf,&infoString[2][0],SIDTUNE_MAX_CREDIT_STRLEN); + info.infoString[2] = &infoString[2][0]; + hasReleased = true; + } + // SONGS + else if ( SidTuneTools::myStrNcaseCmp( pParseChunk, keyword_songs ) == 0 ) + { + SidTuneTools::skipToEqu( parseStream ); + info.songs = (uint_least16_t)SidTuneTools::readDec( parseStream ); + info.startSong = (uint_least16_t)SidTuneTools::readDec( parseStream ); + hasSongs = true; + } + // SPEED + else if ( SidTuneTools::myStrNcaseCmp( pParseChunk, keyword_speed ) == 0 ) + { + SidTuneTools::skipToEqu( parseStream ); + oldStyleSpeed = SidTuneTools::readHex(parseStream); + hasSpeed = true; + } + // SIDSONG + else if ( SidTuneTools::myStrNcaseCmp( pParseChunk, keyword_musPlayer ) == 0 ) + { + info.musPlayer = true; + } + // RELOC + else if ( SidTuneTools::myStrNcaseCmp( pParseChunk, keyword_reloc ) == 0 ) + { + info.relocStartPage = (uint_least8_t)SidTuneTools::readHex( parseStream ); + if ( !parseStream ) + break; + info.relocPages = (uint_least8_t)SidTuneTools::readHex( parseStream ); + } + // CLOCK + else if ( SidTuneTools::myStrNcaseCmp( pParseChunk, keyword_clock ) == 0 ) + { + char clock[8]; + SidTuneTools::copyStringValueToEOL(pParseBuf,clock,sizeof(clock)); + if ( SidTuneTools::myStrNcaseCmp( clock, "UNKNOWN" ) == 0 ) + info.clockSpeed = SIDTUNE_CLOCK_UNKNOWN; + else if ( SidTuneTools::myStrNcaseCmp( clock, "PAL" ) == 0 ) + info.clockSpeed = SIDTUNE_CLOCK_PAL; + else if ( SidTuneTools::myStrNcaseCmp( clock, "NTSC" ) == 0 ) + info.clockSpeed = SIDTUNE_CLOCK_NTSC; + else if ( SidTuneTools::myStrNcaseCmp( clock, "ANY" ) == 0 ) + info.clockSpeed = SIDTUNE_CLOCK_ANY; + } + // SIDMODEL + else if ( SidTuneTools::myStrNcaseCmp( pParseChunk, keyword_sidModel ) == 0 ) + { + char model[8]; + SidTuneTools::copyStringValueToEOL(pParseBuf,model,sizeof(model)); + if ( SidTuneTools::myStrNcaseCmp( model, "UNKNOWN" ) == 0 ) + info.sidModel = SIDTUNE_SIDMODEL_UNKNOWN; + else if ( SidTuneTools::myStrNcaseCmp( model, "6581" ) == 0 ) + info.sidModel = SIDTUNE_SIDMODEL_6581; + else if ( SidTuneTools::myStrNcaseCmp( model, "8580" ) == 0 ) + info.sidModel = SIDTUNE_SIDMODEL_8580; + else if ( SidTuneTools::myStrNcaseCmp( model, "ANY" ) == 0 ) + info.sidModel = SIDTUNE_SIDMODEL_ANY; + } + // COMPATIBILITY + else if ( SidTuneTools::myStrNcaseCmp( pParseChunk, keyword_compatibility ) == 0 ) + { + char comp[6]; + SidTuneTools::copyStringValueToEOL(pParseBuf,comp,sizeof(comp)); + if ( SidTuneTools::myStrNcaseCmp( comp, "C64" ) == 0 ) + info.compatibility = SIDTUNE_COMPATIBILITY_C64; + else if ( SidTuneTools::myStrNcaseCmp( comp, "PSID" ) == 0 ) + info.compatibility = SIDTUNE_COMPATIBILITY_PSID; + else if ( SidTuneTools::myStrNcaseCmp( comp, "R64" ) == 0 ) + info.compatibility = SIDTUNE_COMPATIBILITY_R64; + else if ( SidTuneTools::myStrNcaseCmp( comp, "BASIC" ) == 0 ) + info.compatibility = SIDTUNE_COMPATIBILITY_BASIC; + } + parseLen -= restLen; + pParseBuf += restLen; + // Skip to next line. Leave loop, if none. + } while (parseLen != 0); + + delete[] pParseChunk; + + if ( !(hasName && hasAuthor && hasReleased && hasSongs) ) + { // Something is missing (or damaged ?). + // Error string set above. + return LOAD_ERROR; + } + + switch ( info.compatibility ) + { + case SIDTUNE_COMPATIBILITY_PSID: + case SIDTUNE_COMPATIBILITY_C64: + if ( !(hasAddress && hasSpeed) ) + return LOAD_ERROR; // Error set above + break; + + case SIDTUNE_COMPATIBILITY_R64: + if ( !(hasInitAddr || hasAddress) ) + return LOAD_ERROR; // Error set above + // Allow user to provide single address + if ( !hasAddress ) + info.loadAddr = 0; + else if ( info.loadAddr || info.playAddr ) + { + info.formatString = text_invalidError; + return LOAD_ERROR; + } + // Deliberate run on + + case SIDTUNE_COMPATIBILITY_BASIC: + oldStyleSpeed = ~0; + } + + // Create the speed/clock setting table. + convertOldStyleSpeedToTables(oldStyleSpeed, info.clockSpeed); + info.numberOfInfoStrings = 3; + // We finally accept the input data. + info.formatString = text_format; + if ( info.musPlayer && !dataBuf.isEmpty() ) + return LOAD_NOT_MINE; + // return MUS_load (dataBuf); + return LOAD_OK; + } + return LOAD_NOT_MINE; +} + + +/*bool SidTune::SID_fileSupportSave( std::ofstream& toFile ) +{ + toFile << keyword_id << std::endl; + int compatibility = info.compatibility; + + if ( info.musPlayer ) + { + compatibility = SIDTUNE_COMPATIBILITY_C64; + } + + switch ( compatibility ) + { + case SIDTUNE_COMPATIBILITY_PSID: + case SIDTUNE_COMPATIBILITY_C64: + toFile << keyword_address << std::setfill('0') + << std::hex << std::setw(4) << 0 << ','; + + if ( info.musPlayer ) + { + toFile << std::setw(4) << 0 << ',' + << std::setw(4) << 0 << std::endl; + } + else + { + toFile << std::hex << std::setw(4) << info.initAddr << ',' + << std::hex << std::setw(4) << info.playAddr << std::endl; + } + + { + uint_least32_t oldStyleSpeed = 0; + int maxBugSongs = ((info.songs <= 32) ? info.songs : 32); + for (int s = 0; s < maxBugSongs; s++) + { + if (songSpeed[s] == SIDTUNE_SPEED_CIA_1A) + { + oldStyleSpeed |= (1< + +#include "../config.h" +#include "SidTuneCfg.h" +#include "../SidPlay/SidTune.h" +#include "../SidPlay/sidendian.h" + +#ifdef HAVE_EXCEPTIONS +# include +#endif + +static const char _sidtune_txt_invalid[] = "ERROR: File contains invalid data"; +static const char _sidtune_txt_format_mus[] = "C64 Sidplayer format (MUS)"; +static const char _sidtune_txt_format_str[] = "C64 Stereo Sidplayer format (MUS+STR)"; +static const char _sidtune_txt_notEnoughMemory[] = "ERROR: Not enough free memory"; +static const char _sidtune_txt_sizeExceeded[] = "ERROR: Total file size too large"; + +static const uint_least16_t SIDTUNE_MUS_HLT_CMD = 0x14F; + +static const uint_least16_t SIDTUNE_MUS_DATA_ADDR = 0x0900; +static const uint_least16_t SIDTUNE_SID1_BASE_ADDR = 0xd400; +static const uint_least16_t SIDTUNE_SID2_BASE_ADDR = 0xd500; + +SidTune::LoadStatus SidTune::MUS_fileSupport(Buffer_sidtt& musBuf, + Buffer_sidtt& strBuf) +{ + return MUS_load (musBuf, strBuf, true); +} + +bool SidTune::MUS_detect(const void* buffer, const uint_least32_t bufLen, + uint_least32_t& voice3Index) +{ + SmartPtr_sidtt spMus((const uint8_t*)buffer,bufLen); + // Skip load address and 3x length entry. + uint_least32_t voice1Index = (2+3*2); + // Add length of voice 1 data. + voice1Index += endian_16(spMus[3],spMus[2]); + // Add length of voice 2 data. + uint_least32_t voice2Index = voice1Index + endian_16(spMus[5],spMus[4]); + // Add length of voice 3 data. + voice3Index = voice2Index + endian_16(spMus[7],spMus[6]); + return ((endian_16(spMus[voice1Index-2],spMus[voice1Index+1-2]) == SIDTUNE_MUS_HLT_CMD) + && (endian_16(spMus[voice2Index-2],spMus[voice2Index+1-2]) == SIDTUNE_MUS_HLT_CMD) + && (endian_16(spMus[voice3Index-2],spMus[voice3Index+1-2]) == SIDTUNE_MUS_HLT_CMD) + && spMus); +} + +void SidTune::MUS_setPlayerAddress() +{ + if (info.sidChipBase2 == 0) + { + // Player #1. + info.initAddr = 0xec60; + info.playAddr = 0xec80; + } + else + { + // Player #1 + #2. + info.initAddr = 0xfc90; + info.playAddr = 0xfc96; + } +} + +static const uint8_t _sidtune_sidplayer1[] = +{ + 0x00, 0xe0, 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, 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, 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, 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, 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, + 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, 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, 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, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x61, 0xe1, 0x60, 0x01, 0x02, 0x04, 0x00, 0x07, 0x0e, 0x02, 0x02, 0xfe, 0x02, 0x02, 0xfe, + 0xfe, 0x00, 0x01, 0x00, 0xff, 0x00, 0x02, 0x04, 0x05, 0x07, 0x09, 0x0b, 0x1e, 0x18, 0x8b, 0x7e, + 0xfa, 0x06, 0xac, 0xf3, 0xe6, 0x8f, 0xf8, 0x2e, 0x86, 0x8e, 0x96, 0x9f, 0xa8, 0xb3, 0xbd, 0xc8, + 0xd4, 0xe1, 0xee, 0xfd, 0x8c, 0x78, 0x64, 0x50, 0x3c, 0x28, 0x14, 0x00, 0x00, 0x02, 0x03, 0x05, + 0x07, 0x08, 0x0a, 0x0c, 0x0d, 0x0f, 0x11, 0x12, 0x00, 0xe0, 0x00, 0x05, 0x0a, 0x0f, 0xf9, 0x00, + 0xf5, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x50, 0x00, 0x00, 0x60, 0x00, 0x00, 0x70, 0x00, 0x00, 0x80, 0x00, 0x00, 0x90, 0x00, 0x00, 0xa0, + 0x00, 0xa9, 0x00, 0x8d, 0x00, 0xe0, 0xa2, 0x95, 0xa0, 0x42, 0xad, 0xa6, 0x02, 0xf0, 0x04, 0xa2, + 0x25, 0xa0, 0x40, 0x8e, 0x5b, 0xe1, 0x8c, 0x5c, 0xe1, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, + 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, + 0xea, 0x60, 0xa9, 0x00, 0x8d, 0x00, 0xe0, 0x86, 0x61, 0x84, 0x62, 0xa0, 0xbc, 0x99, 0x00, 0xe0, + 0x88, 0xd0, 0xfa, 0xa0, 0x72, 0x99, 0xbc, 0xe0, 0x88, 0xd0, 0xfa, 0x8d, 0x15, 0xd4, 0x8d, 0x16, + 0xd4, 0xa9, 0x08, 0x8d, 0x25, 0xe0, 0x8d, 0x17, 0xd4, 0x8d, 0x26, 0xe0, 0x8d, 0x18, 0xd4, 0xa9, + 0x90, 0x8d, 0x27, 0xe0, 0xa9, 0x60, 0x8d, 0x28, 0xe0, 0xa9, 0x0c, 0x8d, 0x29, 0xe0, 0xad, 0x5b, + 0xe1, 0x8d, 0x2d, 0xe0, 0xad, 0x5c, 0xe1, 0x8d, 0x2e, 0xe0, 0xa9, 0xff, 0x8d, 0xcc, 0xe0, 0xa9, + 0xd4, 0x85, 0x64, 0xa2, 0x02, 0xa9, 0xff, 0x9d, 0x0b, 0xe0, 0xa9, 0x01, 0x9d, 0x30, 0xe0, 0x9d, + 0x2a, 0xe0, 0x8a, 0x9d, 0x33, 0xe0, 0x9d, 0xae, 0xe0, 0xa9, 0x04, 0x9d, 0x39, 0xe0, 0xbd, 0xa8, + 0xe1, 0x9d, 0xba, 0xe0, 0xa9, 0x5b, 0x9d, 0x7e, 0xe0, 0xbd, 0x65, 0xe1, 0x85, 0x63, 0xa9, 0x00, + 0xa8, 0x91, 0x63, 0xc8, 0x91, 0x63, 0xc8, 0x91, 0x63, 0xa9, 0x08, 0x9d, 0x17, 0xe0, 0x9d, 0x9c, + 0xe0, 0xc8, 0x91, 0x63, 0xc8, 0x91, 0x63, 0xa9, 0x40, 0x9d, 0x1a, 0xe0, 0x91, 0x63, 0xa9, 0x20, + 0x9d, 0x1d, 0xe0, 0xc8, 0x91, 0x63, 0xa9, 0xf5, 0x9d, 0x20, 0xe0, 0xc8, 0x91, 0x63, 0xca, 0x10, + 0xa4, 0x8a, 0xa2, 0x17, 0x9d, 0x3e, 0xe1, 0xca, 0x10, 0xfa, 0xa5, 0x61, 0x18, 0x69, 0x06, 0x85, + 0x63, 0xa9, 0x00, 0xaa, 0xa8, 0x65, 0x62, 0x85, 0x64, 0x9d, 0xab, 0xe0, 0x9d, 0xb4, 0xe0, 0xa5, + 0x63, 0x9d, 0xa8, 0xe0, 0x9d, 0xb1, 0xe0, 0x18, 0x71, 0x61, 0x85, 0x63, 0xa5, 0x64, 0xc8, 0x71, + 0x61, 0xc8, 0xe8, 0xe0, 0x03, 0xd0, 0xe0, 0xa6, 0x63, 0xa8, 0x60, 0xa9, 0x00, 0x8d, 0x04, 0xd4, + 0x8d, 0x0b, 0xd4, 0x8d, 0x12, 0xd4, 0x8d, 0x01, 0xd4, 0x8d, 0x08, 0xd4, 0x8d, 0x0f, 0xd4, 0xa9, + 0x08, 0x8d, 0x17, 0xd4, 0xad, 0x5b, 0xe1, 0x8d, 0x04, 0xdc, 0xad, 0x5c, 0xe1, 0x8d, 0x05, 0xdc, + 0x60, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0x60, + 0xa9, 0x08, 0x8d, 0x00, 0xe0, 0x6c, 0x5d, 0xe1, 0xea, 0xea, 0xea, 0xad, 0x00, 0xe0, 0x30, 0xf0, + 0x09, 0x80, 0xa8, 0x29, 0x07, 0xf0, 0xee, 0xd8, 0x8c, 0x00, 0xe0, 0xea, 0xa5, 0xfb, 0x8d, 0x56, + 0xe1, 0xa5, 0xfc, 0x8d, 0x57, 0xe1, 0xa5, 0xfd, 0x8d, 0x58, 0xe1, 0xa5, 0xfe, 0x8d, 0x59, 0xe1, + 0xa5, 0xff, 0x8d, 0x5a, 0xe1, 0xad, 0x23, 0xe0, 0x18, 0x6d, 0xd9, 0xe0, 0x48, 0x29, 0x07, 0xa8, + 0xad, 0xdc, 0xe0, 0x69, 0x00, 0x85, 0xff, 0x68, 0x46, 0xff, 0x6a, 0x46, 0xff, 0x6a, 0x46, 0xff, + 0x6a, 0x18, 0x6d, 0x24, 0xe0, 0x8c, 0x15, 0xd4, 0x8d, 0x16, 0xd4, 0xad, 0x25, 0xe0, 0x8d, 0x17, + 0xd4, 0xad, 0x26, 0xe0, 0x8d, 0x18, 0xd4, 0xa9, 0xd4, 0x85, 0xfc, 0xa2, 0x00, 0xad, 0x00, 0xe0, + 0x3d, 0x62, 0xe1, 0xf0, 0x51, 0xbd, 0x65, 0xe1, 0x85, 0xfb, 0xbd, 0x0e, 0xe0, 0x18, 0x7d, 0x51, + 0xe0, 0xa8, 0xbd, 0x11, 0xe0, 0x7d, 0x54, 0xe0, 0x48, 0x98, 0x18, 0x7d, 0xcd, 0xe0, 0xa0, 0x00, + 0x91, 0xfb, 0x68, 0x7d, 0xd0, 0xe0, 0xc8, 0x91, 0xfb, 0xbd, 0x14, 0xe0, 0x18, 0x7d, 0x69, 0xe0, + 0x85, 0xff, 0xbd, 0x17, 0xe0, 0x7d, 0x6c, 0xe0, 0x48, 0xa5, 0xff, 0x18, 0x7d, 0xd3, 0xe0, 0xc8, + 0x91, 0xfb, 0x68, 0x7d, 0xd6, 0xe0, 0xc8, 0x91, 0xfb, 0xbd, 0x1d, 0xe0, 0xc8, 0xc8, 0x91, 0xfb, + 0xbd, 0x20, 0xe0, 0xc8, 0x91, 0xfb, 0xe8, 0xe0, 0x03, 0xd0, 0xa2, 0xac, 0x1a, 0xe0, 0xae, 0x1b, + 0xe0, 0xad, 0x1c, 0xe0, 0x8c, 0x04, 0xd4, 0x8e, 0x0b, 0xd4, 0x8d, 0x12, 0xd4, 0xae, 0x2d, 0xe0, + 0xac, 0x2e, 0xe0, 0x8e, 0x04, 0xdc, 0x8c, 0x05, 0xdc, 0xad, 0x1b, 0xd4, 0x8d, 0xbe, 0xe0, 0xad, + 0x1c, 0xd4, 0x8d, 0xbf, 0xe0, 0xa2, 0x00, 0xad, 0x00, 0xe0, 0x3d, 0x62, 0xe1, 0xf0, 0x10, 0x8e, + 0x2f, 0xe0, 0x20, 0x36, 0xe5, 0xad, 0x00, 0xe0, 0x29, 0x78, 0xf0, 0x03, 0x4c, 0x0c, 0xe5, 0xe8, + 0xe0, 0x03, 0xd0, 0xe3, 0xad, 0xc9, 0xe0, 0xd0, 0x52, 0xad, 0xca, 0xe0, 0x0d, 0xcb, 0xe0, 0xf0, + 0x78, 0xad, 0xdf, 0xe0, 0xd0, 0x28, 0xad, 0xca, 0xe0, 0xf0, 0x28, 0x18, 0x6d, 0xbd, 0xe0, 0xb0, + 0x07, 0xcd, 0xcc, 0xe0, 0x90, 0x60, 0xf0, 0x5e, 0xa9, 0x00, 0x8d, 0xdf, 0xe0, 0xad, 0xcb, 0xe0, + 0xf0, 0x54, 0xee, 0xdf, 0xe0, 0xad, 0xbd, 0xe0, 0xed, 0xcb, 0xe0, 0x4c, 0xb4, 0xe4, 0xad, 0xcb, + 0xe0, 0xf0, 0xd3, 0xad, 0xbd, 0xe0, 0x38, 0xed, 0xcb, 0xe0, 0xb0, 0x3a, 0xa9, 0x00, 0x8d, 0xdf, + 0xe0, 0xad, 0xca, 0xe0, 0xd0, 0x30, 0xee, 0xdf, 0xe0, 0xd0, 0x28, 0xce, 0xe0, 0xe0, 0xd0, 0x29, + 0xad, 0xdf, 0xe0, 0xd0, 0x11, 0xee, 0xdf, 0xe0, 0xad, 0xcb, 0xe0, 0xd0, 0x02, 0xa9, 0x20, 0x8d, + 0xe0, 0xe0, 0xa9, 0x00, 0xf0, 0x10, 0xce, 0xdf, 0xe0, 0xad, 0xca, 0xe0, 0xd0, 0x02, 0xa9, 0x20, + 0x8d, 0xe0, 0xe0, 0xad, 0xcc, 0xe0, 0x8d, 0xbd, 0xe0, 0xa2, 0x00, 0xbd, 0xc3, 0xe0, 0xf0, 0x44, + 0xa9, 0x00, 0x85, 0xff, 0xbc, 0xc0, 0xe0, 0xb9, 0xbd, 0xe0, 0xbc, 0xc6, 0xe0, 0xf0, 0x0e, 0x30, + 0x08, 0x0a, 0x26, 0xff, 0x88, 0xd0, 0xfa, 0xf0, 0x04, 0x4a, 0xc8, 0xd0, 0xfc, 0xbc, 0xc3, 0xe0, + 0x88, 0xd0, 0x0b, 0x9d, 0xcd, 0xe0, 0xa5, 0xff, 0x9d, 0xd0, 0xe0, 0x4c, 0x02, 0xe5, 0x88, 0xd0, + 0x0b, 0x9d, 0xd3, 0xe0, 0xa5, 0xff, 0x9d, 0xd6, 0xe0, 0x4c, 0x02, 0xe5, 0x8d, 0xd9, 0xe0, 0xa5, + 0xff, 0x8d, 0xdc, 0xe0, 0xe8, 0xe0, 0x03, 0xd0, 0xb2, 0xad, 0x00, 0xe0, 0x29, 0x7f, 0x8d, 0x00, + 0xe0, 0xad, 0x56, 0xe1, 0x85, 0xfb, 0xad, 0x57, 0xe1, 0x85, 0xfc, 0xad, 0x58, 0xe1, 0x85, 0xfd, + 0xad, 0x59, 0xe1, 0x85, 0xfe, 0xad, 0x5a, 0xe1, 0x85, 0xff, 0x6c, 0x5d, 0xe1, 0xbd, 0x60, 0xe0, + 0xd0, 0x03, 0x4c, 0x9f, 0xe6, 0x4c, 0xba, 0xe5, 0xde, 0x30, 0xe0, 0xd0, 0x03, 0x4c, 0xa0, 0xe6, + 0xbd, 0x36, 0xe0, 0x30, 0xe8, 0xd0, 0x1a, 0xbd, 0x3f, 0xe0, 0xf0, 0x05, 0xde, 0x3f, 0xe0, 0xd0, + 0x10, 0xbd, 0x39, 0xe0, 0xdd, 0x30, 0xe0, 0x90, 0x08, 0xbd, 0x1a, 0xe0, 0x29, 0xfe, 0x9d, 0x1a, + 0xe0, 0xbd, 0x42, 0xe0, 0xf0, 0x56, 0x0a, 0xbd, 0x0e, 0xe0, 0xb0, 0x1d, 0x7d, 0x45, 0xe0, 0x9d, + 0x0e, 0xe0, 0xa8, 0xbd, 0x11, 0xe0, 0x7d, 0x48, 0xe0, 0x9d, 0x11, 0xe0, 0x48, 0x98, 0xdd, 0x8d, + 0xe0, 0x68, 0xfd, 0x90, 0xe0, 0xb0, 0x1f, 0x90, 0x2e, 0xfd, 0x45, 0xe0, 0x9d, 0x0e, 0xe0, 0xbd, + 0x11, 0xe0, 0xfd, 0x48, 0xe0, 0x9d, 0x11, 0xe0, 0xbd, 0x8d, 0xe0, 0xdd, 0x0e, 0xe0, 0xbd, 0x90, + 0xe0, 0xfd, 0x11, 0xe0, 0x90, 0x11, 0xbd, 0x8d, 0xe0, 0x9d, 0x0e, 0xe0, 0xbd, 0x90, 0xe0, 0x9d, + 0x11, 0xe0, 0xa9, 0x00, 0x9d, 0x42, 0xe0, 0xbd, 0x60, 0xe0, 0xf0, 0x55, 0xbd, 0x4b, 0xe0, 0xf0, + 0x4b, 0xa0, 0x00, 0xde, 0x4e, 0xe0, 0xd0, 0x31, 0xbd, 0x51, 0xe0, 0x1d, 0x54, 0xe0, 0xd0, 0x1b, + 0xbd, 0x5d, 0xe0, 0x9d, 0x57, 0xe0, 0x9d, 0x4e, 0xe0, 0xbd, 0x4b, 0xe0, 0x0a, 0xbd, 0x5a, 0xe0, + 0x90, 0x04, 0x49, 0xff, 0x69, 0x00, 0x9d, 0x4b, 0xe0, 0xd0, 0x10, 0xbd, 0x57, 0xe0, 0x9d, 0x4e, + 0xe0, 0x98, 0x38, 0xfd, 0x4b, 0xe0, 0x9d, 0x4b, 0xe0, 0xc9, 0x00, 0x10, 0x01, 0x88, 0x18, 0x7d, + 0x51, 0xe0, 0x9d, 0x51, 0xe0, 0x98, 0x7d, 0x54, 0xe0, 0x9d, 0x54, 0xe0, 0xbd, 0x36, 0xe0, 0x30, + 0x15, 0xbd, 0x93, 0xe0, 0xf0, 0x10, 0x18, 0x7d, 0x14, 0xe0, 0x9d, 0x14, 0xe0, 0xbd, 0x96, 0xe0, + 0x7d, 0x17, 0xe0, 0x9d, 0x17, 0xe0, 0xbd, 0x63, 0xe0, 0xf0, 0x4b, 0xa0, 0x00, 0xde, 0x66, 0xe0, + 0xd0, 0x31, 0xbd, 0x69, 0xe0, 0x1d, 0x6c, 0xe0, 0xd0, 0x1b, 0xbd, 0x72, 0xe0, 0x9d, 0x6f, 0xe0, + 0x9d, 0x66, 0xe0, 0xbd, 0x63, 0xe0, 0x0a, 0xbd, 0x75, 0xe0, 0x90, 0x04, 0x49, 0xff, 0x69, 0x00, + 0x9d, 0x63, 0xe0, 0xd0, 0x10, 0xbd, 0x6f, 0xe0, 0x9d, 0x66, 0xe0, 0x98, 0x38, 0xfd, 0x63, 0xe0, + 0x9d, 0x63, 0xe0, 0xc9, 0x00, 0x10, 0x01, 0x88, 0x18, 0x7d, 0x69, 0xe0, 0x9d, 0x69, 0xe0, 0x98, + 0x7d, 0x6c, 0xe0, 0x9d, 0x6c, 0xe0, 0xbd, 0x36, 0xe0, 0x10, 0x03, 0x4c, 0x9f, 0xe6, 0xa0, 0x00, + 0xbd, 0xa2, 0xe0, 0xf0, 0x1c, 0x10, 0x01, 0xc8, 0x18, 0x6d, 0x23, 0xe0, 0x48, 0x29, 0x07, 0x8d, + 0x23, 0xe0, 0x68, 0x6a, 0x4a, 0x4a, 0x18, 0x79, 0xa6, 0xe1, 0x18, 0x6d, 0x24, 0xe0, 0x8d, 0x24, + 0xe0, 0x60, 0xbd, 0xa8, 0xe0, 0x85, 0xfd, 0xbd, 0xab, 0xe0, 0x85, 0xfe, 0xd0, 0x04, 0x60, 0x20, + 0x98, 0xe8, 0xad, 0x00, 0xe0, 0x3d, 0x62, 0xe1, 0xf0, 0xf4, 0xa0, 0x00, 0xb1, 0xfd, 0x85, 0xff, + 0xc8, 0xb1, 0xfd, 0xa8, 0xa5, 0xfd, 0x18, 0x69, 0x02, 0x85, 0xfd, 0x9d, 0xa8, 0xe0, 0xa5, 0xfe, + 0x69, 0x00, 0x85, 0xfe, 0x9d, 0xab, 0xe0, 0xa5, 0xff, 0x29, 0x03, 0xd0, 0xd2, 0xbd, 0x8d, 0xe0, + 0x9d, 0x0e, 0xe0, 0xbd, 0x90, 0xe0, 0x9d, 0x11, 0xe0, 0xa5, 0xff, 0x9d, 0x05, 0xe0, 0x98, 0x9d, + 0x02, 0xe0, 0x29, 0x07, 0xa8, 0xb9, 0x67, 0xe1, 0x8d, 0x6f, 0xe1, 0xbd, 0x02, 0xe0, 0x29, 0x38, + 0x4a, 0x4a, 0x4a, 0x7d, 0x81, 0xe0, 0x85, 0xfd, 0xbd, 0x02, 0xe0, 0x29, 0xc0, 0x0a, 0x2a, 0x2a, + 0xa8, 0xb9, 0x6f, 0xe1, 0x85, 0xfe, 0xbd, 0x02, 0xe0, 0x29, 0x07, 0xf0, 0x62, 0xa8, 0xb9, 0x72, + 0xe1, 0x65, 0xfe, 0x18, 0x7d, 0x84, 0xe0, 0x10, 0x05, 0x18, 0x69, 0x0c, 0xe6, 0xfd, 0xc9, 0x0c, + 0x90, 0x04, 0xe9, 0x0c, 0xc6, 0xfd, 0x85, 0xfe, 0xa8, 0xb9, 0x86, 0xe1, 0x85, 0xff, 0xb9, 0x7a, + 0xe1, 0xa4, 0xfd, 0x88, 0x30, 0x06, 0x46, 0xff, 0x6a, 0x88, 0x10, 0xfa, 0x18, 0x7d, 0x87, 0xe0, + 0x9d, 0x8d, 0xe0, 0xa5, 0xff, 0x7d, 0x8a, 0xe0, 0x9d, 0x90, 0xe0, 0xbd, 0x05, 0xe0, 0xd0, 0x03, + 0x4c, 0xa0, 0xe6, 0xbd, 0x45, 0xe0, 0x1d, 0x48, 0xe0, 0xf0, 0x16, 0xbd, 0x0e, 0xe0, 0xdd, 0x8d, + 0xe0, 0xbd, 0x11, 0xe0, 0xfd, 0x90, 0xe0, 0xa9, 0xfe, 0x6a, 0x9d, 0x42, 0xe0, 0x90, 0x11, 0xf0, + 0x4a, 0x9d, 0x42, 0xe0, 0xbd, 0x8d, 0xe0, 0x9d, 0x0e, 0xe0, 0xbd, 0x90, 0xe0, 0x9d, 0x11, 0xe0, + 0xbd, 0x36, 0xe0, 0x0a, 0xd0, 0x35, 0xbd, 0x93, 0xe0, 0xf0, 0x0c, 0xbd, 0x99, 0xe0, 0x9d, 0x14, + 0xe0, 0xbd, 0x9c, 0xe0, 0x9d, 0x17, 0xe0, 0xbd, 0x9f, 0xe0, 0xf0, 0x0f, 0xa4, 0xfd, 0x18, 0x79, + 0x92, 0xe1, 0xa4, 0xfe, 0x18, 0x79, 0x9a, 0xe1, 0x18, 0x90, 0x08, 0xbd, 0xa2, 0xe0, 0xf0, 0x0b, + 0xbd, 0xa5, 0xe0, 0x8d, 0x24, 0xe0, 0xa9, 0x00, 0x8d, 0x23, 0xe0, 0xbd, 0x3c, 0xe0, 0x9d, 0x3f, + 0xe0, 0xbd, 0x05, 0xe0, 0x29, 0x40, 0x9d, 0x36, 0xe0, 0xbd, 0x05, 0xe0, 0x4a, 0x4a, 0x29, 0x07, + 0xd0, 0x30, 0xbd, 0x05, 0xe0, 0x30, 0x14, 0xad, 0x27, 0xe0, 0x29, 0x3c, 0xd0, 0x1e, 0xad, 0x27, + 0xe0, 0x0a, 0x2a, 0x2a, 0xd0, 0x02, 0xa9, 0x04, 0x4c, 0x70, 0xe8, 0xad, 0x28, 0xe0, 0xf0, 0x0c, + 0x29, 0x3f, 0xd0, 0x08, 0xad, 0x28, 0xe0, 0x0a, 0x2a, 0x2a, 0xd0, 0x66, 0xa9, 0x10, 0x8d, 0x00, + 0xe0, 0x60, 0xc9, 0x01, 0xd0, 0x13, 0xbd, 0x05, 0xe0, 0x29, 0x20, 0xd0, 0x06, 0xad, 0x29, 0xe0, + 0x4c, 0x70, 0xe8, 0xbd, 0x2a, 0xe0, 0x4c, 0x70, 0xe8, 0xa8, 0xbd, 0x05, 0xe0, 0x29, 0xa0, 0xc9, + 0x80, 0xf0, 0x30, 0x85, 0xff, 0x18, 0xad, 0x27, 0xe0, 0xd0, 0x01, 0x38, 0x88, 0x88, 0xf0, 0x06, + 0x6a, 0xb0, 0x4e, 0x88, 0xd0, 0xfa, 0xa4, 0xff, 0x85, 0xff, 0xf0, 0x26, 0x46, 0xff, 0xb0, 0x41, + 0xf0, 0x42, 0x65, 0xff, 0xb0, 0x3e, 0xc8, 0x10, 0x19, 0x46, 0xff, 0xb0, 0x34, 0x65, 0xff, 0x90, + 0x11, 0xb0, 0x31, 0xad, 0x28, 0xe0, 0xf0, 0x29, 0x88, 0x88, 0xf0, 0x06, 0x4a, 0xb0, 0x22, 0x88, + 0xd0, 0xfa, 0x9d, 0x30, 0xe0, 0xbd, 0x1a, 0xe0, 0x29, 0xf6, 0x9d, 0x1a, 0xe0, 0x38, 0xbd, 0x02, + 0xe0, 0x29, 0x07, 0xd0, 0x03, 0x7e, 0x36, 0xe0, 0xbd, 0x1a, 0xe0, 0x69, 0x00, 0x9d, 0x1a, 0xe0, + 0x60, 0xa9, 0x10, 0x2c, 0xa9, 0x18, 0x8d, 0x00, 0xe0, 0x60, 0x98, 0x48, 0xa5, 0xff, 0x4a, 0x90, + 0x03, 0x4c, 0x42, 0xea, 0x4a, 0x4a, 0xb0, 0x1e, 0x4a, 0xb0, 0x0e, 0x9d, 0x9c, 0xe0, 0x9d, 0x17, + 0xe0, 0x68, 0x9d, 0x99, 0xe0, 0x9d, 0x14, 0xe0, 0x60, 0x4a, 0x90, 0x02, 0x09, 0xf8, 0x9d, 0x8a, + 0xe0, 0x68, 0x9d, 0x87, 0xe0, 0x60, 0x4a, 0xb0, 0x03, 0x4c, 0x4a, 0xe9, 0x4a, 0xb0, 0x61, 0x4a, + 0xb0, 0x0f, 0xd0, 0x08, 0x68, 0x9d, 0xa5, 0xe0, 0x8d, 0x24, 0xe0, 0x60, 0x68, 0x9d, 0x3c, 0xe0, + 0x60, 0xd0, 0x48, 0x68, 0x9d, 0x7e, 0xe0, 0xc9, 0x5b, 0xf0, 0x33, 0xa8, 0x4a, 0x4a, 0x4a, 0x38, + 0xe9, 0x0b, 0x18, 0x7d, 0x84, 0xe0, 0x30, 0x0c, 0xc9, 0x0c, 0x90, 0x11, 0xe9, 0x0c, 0xde, 0x81, + 0xe0, 0x4c, 0x0b, 0xe9, 0xc9, 0xf5, 0xb0, 0x05, 0x69, 0x0c, 0xfe, 0x81, 0xe0, 0x9d, 0x84, 0xe0, + 0x98, 0x29, 0x07, 0x38, 0xe9, 0x03, 0x18, 0x7d, 0x81, 0xe0, 0x9d, 0x81, 0xe0, 0x60, 0xbd, 0x78, + 0xe0, 0x9d, 0x81, 0xe0, 0xbd, 0x7b, 0xe0, 0x9d, 0x84, 0xe0, 0x60, 0x68, 0x9d, 0xc6, 0xe0, 0x60, + 0x4a, 0xb0, 0x08, 0x9d, 0x0b, 0xe0, 0x68, 0x9d, 0x08, 0xe0, 0x60, 0x4a, 0x6a, 0x6a, 0x6d, 0x5b, + 0xe1, 0x8d, 0x2d, 0xe0, 0x68, 0x6d, 0x5c, 0xe1, 0x8d, 0x2e, 0xe0, 0x60, 0x4a, 0x90, 0x03, 0x4c, + 0xd3, 0xe9, 0x4a, 0xb0, 0x40, 0x4a, 0xb0, 0x17, 0x4a, 0xb0, 0x0f, 0x68, 0x8d, 0x27, 0xe0, 0x4a, + 0x4a, 0x4a, 0xa8, 0xb9, 0xaf, 0xe1, 0x8d, 0x28, 0xe0, 0x60, 0x68, 0x9d, 0x5d, 0xe0, 0x60, 0x4a, + 0xb0, 0x05, 0x68, 0x8d, 0x01, 0xe0, 0x60, 0x68, 0xf0, 0x11, 0x9d, 0x75, 0xe0, 0xbc, 0x63, 0xe0, + 0xd0, 0x08, 0x9d, 0x63, 0xe0, 0xa9, 0x01, 0x9d, 0x66, 0xe0, 0x60, 0x9d, 0x63, 0xe0, 0x9d, 0x69, + 0xe0, 0x9d, 0x6c, 0xe0, 0x60, 0x4a, 0xb0, 0x30, 0x4a, 0xb0, 0x05, 0x68, 0x9d, 0x39, 0xe0, 0x60, + 0x68, 0xa0, 0x00, 0x4a, 0x90, 0x02, 0xc8, 0x18, 0x48, 0x29, 0x07, 0x79, 0xac, 0xe1, 0x9d, 0x78, + 0xe0, 0x9d, 0x81, 0xe0, 0x68, 0x4a, 0x4a, 0x4a, 0x18, 0x79, 0xad, 0xe1, 0x9d, 0x7b, 0xe0, 0x9d, + 0x84, 0xe0, 0xa9, 0x5b, 0x9d, 0x7e, 0xe0, 0x60, 0x4a, 0xb0, 0x05, 0x68, 0x9d, 0xa2, 0xe0, 0x60, + 0x68, 0x8d, 0xcc, 0xe0, 0x60, 0x4a, 0xb0, 0x27, 0x4a, 0xb0, 0x0d, 0x4a, 0xb0, 0x05, 0x68, 0x8d, + 0x29, 0xe0, 0x60, 0x68, 0x9d, 0x9f, 0xe0, 0x60, 0x4a, 0xb0, 0x0f, 0x68, 0x9d, 0x93, 0xe0, 0xa0, + 0x00, 0x0a, 0x90, 0x01, 0x88, 0x98, 0x9d, 0x96, 0xe0, 0x60, 0x68, 0x9d, 0x72, 0xe0, 0x60, 0x4a, + 0xb0, 0x1c, 0x4a, 0xb0, 0x15, 0x68, 0x9d, 0xb7, 0xe0, 0xa5, 0xfd, 0x9d, 0xb1, 0xe0, 0xa5, 0xfe, + 0x9d, 0xb4, 0xe0, 0xbd, 0x33, 0xe0, 0x9d, 0xae, 0xe0, 0x60, 0x68, 0x6c, 0x5f, 0xe1, 0x4a, 0xb0, + 0x1e, 0x68, 0xd0, 0x0a, 0x9d, 0x4b, 0xe0, 0x9d, 0x51, 0xe0, 0x9d, 0x54, 0xe0, 0x60, 0x9d, 0x5a, + 0xe0, 0xbc, 0x4b, 0xe0, 0xd0, 0x08, 0x9d, 0x4b, 0xe0, 0xa9, 0x01, 0x9d, 0x4e, 0xe0, 0x60, 0x68, + 0x9d, 0x2a, 0xe0, 0x60, 0x4a, 0x90, 0x08, 0x9d, 0x48, 0xe0, 0x68, 0x9d, 0x45, 0xe0, 0x60, 0x68, + 0x4a, 0xb0, 0x61, 0x4a, 0xb0, 0x25, 0x4a, 0xb0, 0x05, 0x4a, 0xa0, 0xf0, 0xd0, 0x06, 0x0a, 0x0a, + 0x0a, 0x0a, 0xa0, 0x0f, 0x85, 0xff, 0x98, 0xb0, 0x09, 0x3d, 0x1d, 0xe0, 0x05, 0xff, 0x9d, 0x1d, + 0xe0, 0x60, 0x3d, 0x20, 0xe0, 0x05, 0xff, 0x9d, 0x20, 0xe0, 0x60, 0x4a, 0xb0, 0x38, 0x4a, 0xb0, + 0x64, 0x85, 0xff, 0xbd, 0xba, 0xe0, 0xdd, 0xa9, 0xe1, 0xf0, 0x54, 0xfe, 0xba, 0xe0, 0xa8, 0xa5, + 0xfd, 0x99, 0xe1, 0xe0, 0xa5, 0xfe, 0x99, 0xf0, 0xe0, 0xbd, 0x33, 0xe0, 0x99, 0x2f, 0xe1, 0xa4, + 0xff, 0xb9, 0x17, 0xe1, 0xf0, 0x36, 0x85, 0xfe, 0xb9, 0xff, 0xe0, 0x85, 0xfd, 0xb9, 0x3e, 0xe1, + 0x9d, 0x33, 0xe0, 0x60, 0xb0, 0x4b, 0x4a, 0xb0, 0x3c, 0xa8, 0xa5, 0xfd, 0x99, 0xff, 0xe0, 0xa5, + 0xfe, 0x99, 0x17, 0xe1, 0xbd, 0x33, 0xe0, 0x99, 0x3e, 0xe1, 0xbd, 0xba, 0xe0, 0xdd, 0xa9, 0xe1, + 0xf0, 0x0d, 0xfe, 0xba, 0xe0, 0xa8, 0xa9, 0x00, 0x99, 0xf0, 0xe0, 0x60, 0xa9, 0x30, 0x2c, 0xa9, + 0x28, 0x8d, 0x00, 0xe0, 0x60, 0x0a, 0x0a, 0x0a, 0x0a, 0x4d, 0x25, 0xe0, 0x29, 0xf0, 0x4d, 0x25, + 0xe0, 0x8d, 0x25, 0xe0, 0x60, 0x4d, 0x26, 0xe0, 0x29, 0x0f, 0x4d, 0x26, 0xe0, 0x8d, 0x26, 0xe0, + 0x60, 0x4a, 0xb0, 0x0b, 0x4a, 0xb0, 0x04, 0x8d, 0xca, 0xe0, 0x60, 0x8d, 0xcb, 0xe0, 0x60, 0x4a, + 0x90, 0x03, 0x4c, 0xa5, 0xeb, 0x4a, 0xa8, 0xf0, 0x21, 0x88, 0xf0, 0x34, 0x88, 0xf0, 0x42, 0x88, + 0xf0, 0x4a, 0x88, 0xf0, 0x52, 0x88, 0xf0, 0x5c, 0x88, 0xf0, 0x66, 0x88, 0xf0, 0x73, 0x29, 0x07, + 0x09, 0x10, 0xb0, 0x03, 0x4c, 0xb7, 0xea, 0x4c, 0x7f, 0xea, 0xac, 0x26, 0xe0, 0xb0, 0x07, 0xc8, + 0x98, 0x29, 0x0f, 0xd0, 0x07, 0x60, 0x98, 0x29, 0x0f, 0xf0, 0x04, 0x88, 0x8c, 0x26, 0xe0, 0x60, + 0xbd, 0x62, 0xe1, 0x49, 0xff, 0x2d, 0x25, 0xe0, 0x90, 0x03, 0x1d, 0x62, 0xe1, 0x8d, 0x25, 0xe0, + 0x60, 0xbd, 0x1a, 0xe0, 0x29, 0xfb, 0x90, 0x55, 0x09, 0x04, 0xb0, 0x51, 0xbd, 0x1a, 0xe0, 0x29, + 0xfd, 0x90, 0x4a, 0x09, 0x02, 0xb0, 0x46, 0xad, 0x25, 0xe0, 0x29, 0xf7, 0x90, 0x02, 0x09, 0x08, + 0x8d, 0x25, 0xe0, 0x60, 0xad, 0x26, 0xe0, 0x29, 0x7f, 0x90, 0x02, 0x09, 0x80, 0x8d, 0x26, 0xe0, + 0x60, 0x98, 0x8d, 0xbd, 0xe0, 0x8d, 0xdf, 0xe0, 0xc8, 0x8c, 0xe0, 0xe0, 0x2a, 0x8d, 0xc9, 0xe0, + 0x60, 0x98, 0x2a, 0x9d, 0x60, 0xe0, 0x60, 0x4a, 0xb0, 0x27, 0x4a, 0xb0, 0x14, 0xd0, 0x02, 0xa9, + 0x08, 0x0a, 0x0a, 0x0a, 0x0a, 0x5d, 0x1a, 0xe0, 0x29, 0xf0, 0x5d, 0x1a, 0xe0, 0x9d, 0x1a, 0xe0, + 0x60, 0x0a, 0x0a, 0x0a, 0x0a, 0x4d, 0x26, 0xe0, 0x29, 0x70, 0x4d, 0x26, 0xe0, 0x8d, 0x26, 0xe0, + 0x60, 0x4a, 0x90, 0x04, 0x9d, 0xc0, 0xe0, 0x60, 0xa8, 0xf0, 0x20, 0x88, 0xf0, 0x40, 0x88, 0xf0, + 0x63, 0x29, 0x03, 0x9d, 0xc3, 0xe0, 0xa9, 0x00, 0x9d, 0xcd, 0xe0, 0x9d, 0xd0, 0xe0, 0x9d, 0xd3, + 0xe0, 0x9d, 0xd6, 0xe0, 0x8d, 0xd9, 0xe0, 0x8d, 0xdc, 0xe0, 0x60, 0xbd, 0xb7, 0xe0, 0xf0, 0x05, + 0xde, 0xb7, 0xe0, 0xf0, 0x12, 0xbd, 0x33, 0xe0, 0xdd, 0xae, 0xe0, 0xd0, 0x0b, 0xbd, 0xb1, 0xe0, + 0x85, 0xfd, 0xbd, 0xb4, 0xe0, 0x85, 0xfe, 0x60, 0xa9, 0x38, 0x8d, 0x00, 0xe0, 0x60, 0xbd, 0xba, + 0xe0, 0xdd, 0xa8, 0xe1, 0xf0, 0x18, 0xde, 0xba, 0xe0, 0xa8, 0x88, 0xb9, 0xf0, 0xe0, 0xf0, 0x0d, + 0x85, 0xfe, 0xb9, 0xe1, 0xe0, 0x85, 0xfd, 0xb9, 0x2f, 0xe1, 0x9d, 0x33, 0xe0, 0x60, 0xa9, 0x20, + 0x8d, 0x00, 0xe0, 0x60, 0xad, 0x00, 0xe0, 0x5d, 0x62, 0xe1, 0x8d, 0x00, 0xe0, 0xa9, 0x01, 0x9d, + 0x30, 0xe0, 0x60, 0xad, 0x00, 0xe0, 0x29, 0x07, 0x8d, 0x81, 0xec, 0xd0, 0x03, 0x20, 0xe9, 0xe2, + 0x60, 0x00, 0xa2, 0x51, 0xa0, 0xec, 0x8e, 0x5d, 0xe1, 0x8c, 0x5e, 0xe1, 0x20, 0xcf, 0xe1, 0xa2, + 0x00, 0xa0, 0x09, 0x20, 0x00, 0xe2, 0xa9, 0x07, 0x8d, 0x00, 0xe0, 0x8d, 0x81, 0xec, 0x60, 0x00, + 0x00, 0x00, 0xa9, 0x00, 0x29, 0xff, 0xf0, 0xf6, 0x4c, 0x29, 0xe3, 0xa9, 0x07, 0x8d, 0x00, 0xe0, + 0x60 +}; + +static const uint8_t _sidtune_sidplayer2[] = +{ + 0x00, 0xf0, 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, 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, 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, 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, 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, + 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, 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, 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, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x61, 0xf1, 0x60, 0x01, 0x02, 0x04, 0x00, 0x07, 0x0e, 0x02, 0x02, 0xfe, 0x02, 0x02, 0xfe, + 0xfe, 0x00, 0x01, 0x00, 0xff, 0x00, 0x02, 0x04, 0x05, 0x07, 0x09, 0x0b, 0x1e, 0x18, 0x8b, 0x7e, + 0xfa, 0x06, 0xac, 0xf3, 0xe6, 0x8f, 0xf8, 0x2e, 0x86, 0x8e, 0x96, 0x9f, 0xa8, 0xb3, 0xbd, 0xc8, + 0xd4, 0xe1, 0xee, 0xfd, 0x8c, 0x78, 0x64, 0x50, 0x3c, 0x28, 0x14, 0x00, 0x00, 0x02, 0x03, 0x05, + 0x07, 0x08, 0x0a, 0x0c, 0x0d, 0x0f, 0x11, 0x12, 0x00, 0xe0, 0x00, 0x05, 0x0a, 0x0f, 0xf9, 0x00, + 0xf5, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x30, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x50, 0x00, 0x00, 0x60, 0x00, 0x00, 0x70, 0x00, 0x00, 0x80, 0x00, 0x00, 0x90, 0x00, 0x00, 0xa0, + 0x00, 0xa9, 0x00, 0x8d, 0x00, 0xf0, 0xa2, 0x95, 0xa0, 0x42, 0xad, 0xa6, 0x02, 0xf0, 0x04, 0xa2, + 0x25, 0xa0, 0x40, 0x8e, 0x5b, 0xf1, 0x8c, 0x5c, 0xf1, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, + 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, + 0xea, 0x60, 0xa9, 0x00, 0x8d, 0x00, 0xf0, 0x86, 0x61, 0x84, 0x62, 0xa0, 0xbc, 0x99, 0x00, 0xf0, + 0x88, 0xd0, 0xfa, 0xa0, 0x72, 0x99, 0xbc, 0xf0, 0x88, 0xd0, 0xfa, 0x8d, 0x15, 0xd5, 0x8d, 0x16, + 0xd5, 0xa9, 0x08, 0x8d, 0x25, 0xf0, 0x8d, 0x17, 0xd5, 0x8d, 0x26, 0xf0, 0x8d, 0x18, 0xd5, 0xa9, + 0x90, 0x8d, 0x27, 0xf0, 0xa9, 0x60, 0x8d, 0x28, 0xf0, 0xa9, 0x0c, 0x8d, 0x29, 0xf0, 0xad, 0x5b, + 0xf1, 0x8d, 0x2d, 0xf0, 0xad, 0x5c, 0xf1, 0x8d, 0x2e, 0xf0, 0xa9, 0xff, 0x8d, 0xcc, 0xf0, 0xa9, + 0xd5, 0x85, 0x64, 0xa2, 0x02, 0xa9, 0xff, 0x9d, 0x0b, 0xf0, 0xa9, 0x01, 0x9d, 0x30, 0xf0, 0x9d, + 0x2a, 0xf0, 0x8a, 0x9d, 0x33, 0xf0, 0x9d, 0xae, 0xf0, 0xa9, 0x04, 0x9d, 0x39, 0xf0, 0xbd, 0xa8, + 0xf1, 0x9d, 0xba, 0xf0, 0xa9, 0x5b, 0x9d, 0x7e, 0xf0, 0xbd, 0x65, 0xf1, 0x85, 0x63, 0xa9, 0x00, + 0xa8, 0x91, 0x63, 0xc8, 0x91, 0x63, 0xc8, 0x91, 0x63, 0xa9, 0x08, 0x9d, 0x17, 0xf0, 0x9d, 0x9c, + 0xf0, 0xc8, 0x91, 0x63, 0xc8, 0x91, 0x63, 0xa9, 0x40, 0x9d, 0x1a, 0xf0, 0x91, 0x63, 0xa9, 0x20, + 0x9d, 0x1d, 0xf0, 0xc8, 0x91, 0x63, 0xa9, 0xf5, 0x9d, 0x20, 0xf0, 0xc8, 0x91, 0x63, 0xca, 0x10, + 0xa4, 0x8a, 0xa2, 0x17, 0x9d, 0x3e, 0xf1, 0xca, 0x10, 0xfa, 0xa5, 0x61, 0x18, 0x69, 0x06, 0x85, + 0x63, 0xa9, 0x00, 0xaa, 0xa8, 0x65, 0x62, 0x85, 0x64, 0x9d, 0xab, 0xf0, 0x9d, 0xb4, 0xf0, 0xa5, + 0x63, 0x9d, 0xa8, 0xf0, 0x9d, 0xb1, 0xf0, 0x18, 0x71, 0x61, 0x85, 0x63, 0xa5, 0x64, 0xc8, 0x71, + 0x61, 0xc8, 0xe8, 0xe0, 0x03, 0xd0, 0xe0, 0xa6, 0x63, 0xa8, 0x60, 0xa9, 0x00, 0x8d, 0x04, 0xd5, + 0x8d, 0x0b, 0xd5, 0x8d, 0x12, 0xd5, 0x8d, 0x01, 0xd5, 0x8d, 0x08, 0xd5, 0x8d, 0x0f, 0xd5, 0xa9, + 0x08, 0x8d, 0x17, 0xd5, 0xad, 0x5b, 0xf1, 0x8d, 0x04, 0xdc, 0xad, 0x5c, 0xf1, 0x8d, 0x05, 0xdc, + 0x60, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0xea, 0x60, + 0xa9, 0x08, 0x8d, 0x00, 0xf0, 0x6c, 0x5d, 0xf1, 0xea, 0xea, 0xea, 0xad, 0x00, 0xf0, 0x30, 0xf0, + 0x09, 0x80, 0xa8, 0x29, 0x07, 0xf0, 0xee, 0xd8, 0x8c, 0x00, 0xf0, 0xea, 0xa5, 0xfb, 0x8d, 0x56, + 0xf1, 0xa5, 0xfc, 0x8d, 0x57, 0xf1, 0xa5, 0xfd, 0x8d, 0x58, 0xf1, 0xa5, 0xfe, 0x8d, 0x59, 0xf1, + 0xa5, 0xff, 0x8d, 0x5a, 0xf1, 0xad, 0x23, 0xf0, 0x18, 0x6d, 0xd9, 0xf0, 0x48, 0x29, 0x07, 0xa8, + 0xad, 0xdc, 0xf0, 0x69, 0x00, 0x85, 0xff, 0x68, 0x46, 0xff, 0x6a, 0x46, 0xff, 0x6a, 0x46, 0xff, + 0x6a, 0x18, 0x6d, 0x24, 0xf0, 0x8c, 0x15, 0xd5, 0x8d, 0x16, 0xd5, 0xad, 0x25, 0xf0, 0x8d, 0x17, + 0xd5, 0xad, 0x26, 0xf0, 0x8d, 0x18, 0xd5, 0xa9, 0xd5, 0x85, 0xfc, 0xa2, 0x00, 0xad, 0x00, 0xf0, + 0x3d, 0x62, 0xf1, 0xf0, 0x51, 0xbd, 0x65, 0xf1, 0x85, 0xfb, 0xbd, 0x0e, 0xf0, 0x18, 0x7d, 0x51, + 0xf0, 0xa8, 0xbd, 0x11, 0xf0, 0x7d, 0x54, 0xf0, 0x48, 0x98, 0x18, 0x7d, 0xcd, 0xf0, 0xa0, 0x00, + 0x91, 0xfb, 0x68, 0x7d, 0xd0, 0xf0, 0xc8, 0x91, 0xfb, 0xbd, 0x14, 0xf0, 0x18, 0x7d, 0x69, 0xf0, + 0x85, 0xff, 0xbd, 0x17, 0xf0, 0x7d, 0x6c, 0xf0, 0x48, 0xa5, 0xff, 0x18, 0x7d, 0xd3, 0xf0, 0xc8, + 0x91, 0xfb, 0x68, 0x7d, 0xd6, 0xf0, 0xc8, 0x91, 0xfb, 0xbd, 0x1d, 0xf0, 0xc8, 0xc8, 0x91, 0xfb, + 0xbd, 0x20, 0xf0, 0xc8, 0x91, 0xfb, 0xe8, 0xe0, 0x03, 0xd0, 0xa2, 0xac, 0x1a, 0xf0, 0xae, 0x1b, + 0xf0, 0xad, 0x1c, 0xf0, 0x8c, 0x04, 0xd5, 0x8e, 0x0b, 0xd5, 0x8d, 0x12, 0xd5, 0xae, 0x2d, 0xf0, + 0xac, 0x2e, 0xf0, 0x8e, 0x04, 0xdc, 0x8c, 0x05, 0xdc, 0xad, 0x1b, 0xd5, 0x8d, 0xbe, 0xf0, 0xad, + 0x1c, 0xd5, 0x8d, 0xbf, 0xf0, 0xa2, 0x00, 0xad, 0x00, 0xf0, 0x3d, 0x62, 0xf1, 0xf0, 0x10, 0x8e, + 0x2f, 0xf0, 0x20, 0x36, 0xf5, 0xad, 0x00, 0xf0, 0x29, 0x78, 0xf0, 0x03, 0x4c, 0x0c, 0xf5, 0xe8, + 0xe0, 0x03, 0xd0, 0xe3, 0xad, 0xc9, 0xf0, 0xd0, 0x52, 0xad, 0xca, 0xf0, 0x0d, 0xcb, 0xf0, 0xf0, + 0x78, 0xad, 0xdf, 0xf0, 0xd0, 0x28, 0xad, 0xca, 0xf0, 0xf0, 0x28, 0x18, 0x6d, 0xbd, 0xf0, 0xb0, + 0x07, 0xcd, 0xcc, 0xf0, 0x90, 0x60, 0xf0, 0x5e, 0xa9, 0x00, 0x8d, 0xdf, 0xf0, 0xad, 0xcb, 0xf0, + 0xf0, 0x54, 0xee, 0xdf, 0xf0, 0xad, 0xbd, 0xf0, 0xed, 0xcb, 0xf0, 0x4c, 0xb4, 0xf4, 0xad, 0xcb, + 0xf0, 0xf0, 0xd3, 0xad, 0xbd, 0xf0, 0x38, 0xed, 0xcb, 0xf0, 0xb0, 0x3a, 0xa9, 0x00, 0x8d, 0xdf, + 0xf0, 0xad, 0xca, 0xf0, 0xd0, 0x30, 0xee, 0xdf, 0xf0, 0xd0, 0x28, 0xce, 0xe0, 0xf0, 0xd0, 0x29, + 0xad, 0xdf, 0xf0, 0xd0, 0x11, 0xee, 0xdf, 0xf0, 0xad, 0xcb, 0xf0, 0xd0, 0x02, 0xa9, 0x20, 0x8d, + 0xe0, 0xf0, 0xa9, 0x00, 0xf0, 0x10, 0xce, 0xdf, 0xf0, 0xad, 0xca, 0xf0, 0xd0, 0x02, 0xa9, 0x20, + 0x8d, 0xe0, 0xf0, 0xad, 0xcc, 0xf0, 0x8d, 0xbd, 0xf0, 0xa2, 0x00, 0xbd, 0xc3, 0xf0, 0xf0, 0x44, + 0xa9, 0x00, 0x85, 0xff, 0xbc, 0xc0, 0xf0, 0xb9, 0xbd, 0xf0, 0xbc, 0xc6, 0xf0, 0xf0, 0x0e, 0x30, + 0x08, 0x0a, 0x26, 0xff, 0x88, 0xd0, 0xfa, 0xf0, 0x04, 0x4a, 0xc8, 0xd0, 0xfc, 0xbc, 0xc3, 0xf0, + 0x88, 0xd0, 0x0b, 0x9d, 0xcd, 0xf0, 0xa5, 0xff, 0x9d, 0xd0, 0xf0, 0x4c, 0x02, 0xf5, 0x88, 0xd0, + 0x0b, 0x9d, 0xd3, 0xf0, 0xa5, 0xff, 0x9d, 0xd6, 0xf0, 0x4c, 0x02, 0xf5, 0x8d, 0xd9, 0xf0, 0xa5, + 0xff, 0x8d, 0xdc, 0xf0, 0xe8, 0xe0, 0x03, 0xd0, 0xb2, 0xad, 0x00, 0xf0, 0x29, 0x7f, 0x8d, 0x00, + 0xf0, 0xad, 0x56, 0xf1, 0x85, 0xfb, 0xad, 0x57, 0xf1, 0x85, 0xfc, 0xad, 0x58, 0xf1, 0x85, 0xfd, + 0xad, 0x59, 0xf1, 0x85, 0xfe, 0xad, 0x5a, 0xf1, 0x85, 0xff, 0x6c, 0x5d, 0xf1, 0xbd, 0x60, 0xf0, + 0xd0, 0x03, 0x4c, 0x9f, 0xf6, 0x4c, 0xba, 0xf5, 0xde, 0x30, 0xf0, 0xd0, 0x03, 0x4c, 0xa0, 0xf6, + 0xbd, 0x36, 0xf0, 0x30, 0xe8, 0xd0, 0x1a, 0xbd, 0x3f, 0xf0, 0xf0, 0x05, 0xde, 0x3f, 0xf0, 0xd0, + 0x10, 0xbd, 0x39, 0xf0, 0xdd, 0x30, 0xf0, 0x90, 0x08, 0xbd, 0x1a, 0xf0, 0x29, 0xfe, 0x9d, 0x1a, + 0xf0, 0xbd, 0x42, 0xf0, 0xf0, 0x56, 0x0a, 0xbd, 0x0e, 0xf0, 0xb0, 0x1d, 0x7d, 0x45, 0xf0, 0x9d, + 0x0e, 0xf0, 0xa8, 0xbd, 0x11, 0xf0, 0x7d, 0x48, 0xf0, 0x9d, 0x11, 0xf0, 0x48, 0x98, 0xdd, 0x8d, + 0xf0, 0x68, 0xfd, 0x90, 0xf0, 0xb0, 0x1f, 0x90, 0x2e, 0xfd, 0x45, 0xf0, 0x9d, 0x0e, 0xf0, 0xbd, + 0x11, 0xf0, 0xfd, 0x48, 0xf0, 0x9d, 0x11, 0xf0, 0xbd, 0x8d, 0xf0, 0xdd, 0x0e, 0xf0, 0xbd, 0x90, + 0xf0, 0xfd, 0x11, 0xf0, 0x90, 0x11, 0xbd, 0x8d, 0xf0, 0x9d, 0x0e, 0xf0, 0xbd, 0x90, 0xf0, 0x9d, + 0x11, 0xf0, 0xa9, 0x00, 0x9d, 0x42, 0xf0, 0xbd, 0x60, 0xf0, 0xf0, 0x55, 0xbd, 0x4b, 0xf0, 0xf0, + 0x4b, 0xa0, 0x00, 0xde, 0x4e, 0xf0, 0xd0, 0x31, 0xbd, 0x51, 0xf0, 0x1d, 0x54, 0xf0, 0xd0, 0x1b, + 0xbd, 0x5d, 0xf0, 0x9d, 0x57, 0xf0, 0x9d, 0x4e, 0xf0, 0xbd, 0x4b, 0xf0, 0x0a, 0xbd, 0x5a, 0xf0, + 0x90, 0x04, 0x49, 0xff, 0x69, 0x00, 0x9d, 0x4b, 0xf0, 0xd0, 0x10, 0xbd, 0x57, 0xf0, 0x9d, 0x4e, + 0xf0, 0x98, 0x38, 0xfd, 0x4b, 0xf0, 0x9d, 0x4b, 0xf0, 0xc9, 0x00, 0x10, 0x01, 0x88, 0x18, 0x7d, + 0x51, 0xf0, 0x9d, 0x51, 0xf0, 0x98, 0x7d, 0x54, 0xf0, 0x9d, 0x54, 0xf0, 0xbd, 0x36, 0xf0, 0x30, + 0x15, 0xbd, 0x93, 0xf0, 0xf0, 0x10, 0x18, 0x7d, 0x14, 0xf0, 0x9d, 0x14, 0xf0, 0xbd, 0x96, 0xf0, + 0x7d, 0x17, 0xf0, 0x9d, 0x17, 0xf0, 0xbd, 0x63, 0xf0, 0xf0, 0x4b, 0xa0, 0x00, 0xde, 0x66, 0xf0, + 0xd0, 0x31, 0xbd, 0x69, 0xf0, 0x1d, 0x6c, 0xf0, 0xd0, 0x1b, 0xbd, 0x72, 0xf0, 0x9d, 0x6f, 0xf0, + 0x9d, 0x66, 0xf0, 0xbd, 0x63, 0xf0, 0x0a, 0xbd, 0x75, 0xf0, 0x90, 0x04, 0x49, 0xff, 0x69, 0x00, + 0x9d, 0x63, 0xf0, 0xd0, 0x10, 0xbd, 0x6f, 0xf0, 0x9d, 0x66, 0xf0, 0x98, 0x38, 0xfd, 0x63, 0xf0, + 0x9d, 0x63, 0xf0, 0xc9, 0x00, 0x10, 0x01, 0x88, 0x18, 0x7d, 0x69, 0xf0, 0x9d, 0x69, 0xf0, 0x98, + 0x7d, 0x6c, 0xf0, 0x9d, 0x6c, 0xf0, 0xbd, 0x36, 0xf0, 0x10, 0x03, 0x4c, 0x9f, 0xf6, 0xa0, 0x00, + 0xbd, 0xa2, 0xf0, 0xf0, 0x1c, 0x10, 0x01, 0xc8, 0x18, 0x6d, 0x23, 0xf0, 0x48, 0x29, 0x07, 0x8d, + 0x23, 0xf0, 0x68, 0x6a, 0x4a, 0x4a, 0x18, 0x79, 0xa6, 0xf1, 0x18, 0x6d, 0x24, 0xf0, 0x8d, 0x24, + 0xf0, 0x60, 0xbd, 0xa8, 0xf0, 0x85, 0xfd, 0xbd, 0xab, 0xf0, 0x85, 0xfe, 0xd0, 0x04, 0x60, 0x20, + 0x98, 0xf8, 0xad, 0x00, 0xf0, 0x3d, 0x62, 0xf1, 0xf0, 0xf4, 0xa0, 0x00, 0xb1, 0xfd, 0x85, 0xff, + 0xc8, 0xb1, 0xfd, 0xa8, 0xa5, 0xfd, 0x18, 0x69, 0x02, 0x85, 0xfd, 0x9d, 0xa8, 0xf0, 0xa5, 0xfe, + 0x69, 0x00, 0x85, 0xfe, 0x9d, 0xab, 0xf0, 0xa5, 0xff, 0x29, 0x03, 0xd0, 0xd2, 0xbd, 0x8d, 0xf0, + 0x9d, 0x0e, 0xf0, 0xbd, 0x90, 0xf0, 0x9d, 0x11, 0xf0, 0xa5, 0xff, 0x9d, 0x05, 0xf0, 0x98, 0x9d, + 0x02, 0xf0, 0x29, 0x07, 0xa8, 0xb9, 0x67, 0xf1, 0x8d, 0x6f, 0xf1, 0xbd, 0x02, 0xf0, 0x29, 0x38, + 0x4a, 0x4a, 0x4a, 0x7d, 0x81, 0xf0, 0x85, 0xfd, 0xbd, 0x02, 0xf0, 0x29, 0xc0, 0x0a, 0x2a, 0x2a, + 0xa8, 0xb9, 0x6f, 0xf1, 0x85, 0xfe, 0xbd, 0x02, 0xf0, 0x29, 0x07, 0xf0, 0x62, 0xa8, 0xb9, 0x72, + 0xf1, 0x65, 0xfe, 0x18, 0x7d, 0x84, 0xf0, 0x10, 0x05, 0x18, 0x69, 0x0c, 0xe6, 0xfd, 0xc9, 0x0c, + 0x90, 0x04, 0xe9, 0x0c, 0xc6, 0xfd, 0x85, 0xfe, 0xa8, 0xb9, 0x86, 0xf1, 0x85, 0xff, 0xb9, 0x7a, + 0xf1, 0xa4, 0xfd, 0x88, 0x30, 0x06, 0x46, 0xff, 0x6a, 0x88, 0x10, 0xfa, 0x18, 0x7d, 0x87, 0xf0, + 0x9d, 0x8d, 0xf0, 0xa5, 0xff, 0x7d, 0x8a, 0xf0, 0x9d, 0x90, 0xf0, 0xbd, 0x05, 0xf0, 0xd0, 0x03, + 0x4c, 0xa0, 0xf6, 0xbd, 0x45, 0xf0, 0x1d, 0x48, 0xf0, 0xf0, 0x16, 0xbd, 0x0e, 0xf0, 0xdd, 0x8d, + 0xf0, 0xbd, 0x11, 0xf0, 0xfd, 0x90, 0xf0, 0xa9, 0xfe, 0x6a, 0x9d, 0x42, 0xf0, 0x90, 0x11, 0xf0, + 0x4a, 0x9d, 0x42, 0xf0, 0xbd, 0x8d, 0xf0, 0x9d, 0x0e, 0xf0, 0xbd, 0x90, 0xf0, 0x9d, 0x11, 0xf0, + 0xbd, 0x36, 0xf0, 0x0a, 0xd0, 0x35, 0xbd, 0x93, 0xf0, 0xf0, 0x0c, 0xbd, 0x99, 0xf0, 0x9d, 0x14, + 0xf0, 0xbd, 0x9c, 0xf0, 0x9d, 0x17, 0xf0, 0xbd, 0x9f, 0xf0, 0xf0, 0x0f, 0xa4, 0xfd, 0x18, 0x79, + 0x92, 0xf1, 0xa4, 0xfe, 0x18, 0x79, 0x9a, 0xf1, 0x18, 0x90, 0x08, 0xbd, 0xa2, 0xf0, 0xf0, 0x0b, + 0xbd, 0xa5, 0xf0, 0x8d, 0x24, 0xf0, 0xa9, 0x00, 0x8d, 0x23, 0xf0, 0xbd, 0x3c, 0xf0, 0x9d, 0x3f, + 0xf0, 0xbd, 0x05, 0xf0, 0x29, 0x40, 0x9d, 0x36, 0xf0, 0xbd, 0x05, 0xf0, 0x4a, 0x4a, 0x29, 0x07, + 0xd0, 0x30, 0xbd, 0x05, 0xf0, 0x30, 0x14, 0xad, 0x27, 0xf0, 0x29, 0x3c, 0xd0, 0x1e, 0xad, 0x27, + 0xf0, 0x0a, 0x2a, 0x2a, 0xd0, 0x02, 0xa9, 0x04, 0x4c, 0x70, 0xf8, 0xad, 0x28, 0xf0, 0xf0, 0x0c, + 0x29, 0x3f, 0xd0, 0x08, 0xad, 0x28, 0xf0, 0x0a, 0x2a, 0x2a, 0xd0, 0x66, 0xa9, 0x10, 0x8d, 0x00, + 0xf0, 0x60, 0xc9, 0x01, 0xd0, 0x13, 0xbd, 0x05, 0xf0, 0x29, 0x20, 0xd0, 0x06, 0xad, 0x29, 0xf0, + 0x4c, 0x70, 0xf8, 0xbd, 0x2a, 0xf0, 0x4c, 0x70, 0xf8, 0xa8, 0xbd, 0x05, 0xf0, 0x29, 0xa0, 0xc9, + 0x80, 0xf0, 0x30, 0x85, 0xff, 0x18, 0xad, 0x27, 0xf0, 0xd0, 0x01, 0x38, 0x88, 0x88, 0xf0, 0x06, + 0x6a, 0xb0, 0x4e, 0x88, 0xd0, 0xfa, 0xa4, 0xff, 0x85, 0xff, 0xf0, 0x26, 0x46, 0xff, 0xb0, 0x41, + 0xf0, 0x42, 0x65, 0xff, 0xb0, 0x3e, 0xc8, 0x10, 0x19, 0x46, 0xff, 0xb0, 0x34, 0x65, 0xff, 0x90, + 0x11, 0xb0, 0x31, 0xad, 0x28, 0xf0, 0xf0, 0x29, 0x88, 0x88, 0xf0, 0x06, 0x4a, 0xb0, 0x22, 0x88, + 0xd0, 0xfa, 0x9d, 0x30, 0xf0, 0xbd, 0x1a, 0xf0, 0x29, 0xf6, 0x9d, 0x1a, 0xf0, 0x38, 0xbd, 0x02, + 0xf0, 0x29, 0x07, 0xd0, 0x03, 0x7e, 0x36, 0xf0, 0xbd, 0x1a, 0xf0, 0x69, 0x00, 0x9d, 0x1a, 0xf0, + 0x60, 0xa9, 0x10, 0x2c, 0xa9, 0x18, 0x8d, 0x00, 0xf0, 0x60, 0x98, 0x48, 0xa5, 0xff, 0x4a, 0x90, + 0x03, 0x4c, 0x42, 0xfa, 0x4a, 0x4a, 0xb0, 0x1e, 0x4a, 0xb0, 0x0e, 0x9d, 0x9c, 0xf0, 0x9d, 0x17, + 0xf0, 0x68, 0x9d, 0x99, 0xf0, 0x9d, 0x14, 0xf0, 0x60, 0x4a, 0x90, 0x02, 0x09, 0xf8, 0x9d, 0x8a, + 0xf0, 0x68, 0x9d, 0x87, 0xf0, 0x60, 0x4a, 0xb0, 0x03, 0x4c, 0x4a, 0xf9, 0x4a, 0xb0, 0x61, 0x4a, + 0xb0, 0x0f, 0xd0, 0x08, 0x68, 0x9d, 0xa5, 0xf0, 0x8d, 0x24, 0xf0, 0x60, 0x68, 0x9d, 0x3c, 0xf0, + 0x60, 0xd0, 0x48, 0x68, 0x9d, 0x7e, 0xf0, 0xc9, 0x5b, 0xf0, 0x33, 0xa8, 0x4a, 0x4a, 0x4a, 0x38, + 0xe9, 0x0b, 0x18, 0x7d, 0x84, 0xf0, 0x30, 0x0c, 0xc9, 0x0c, 0x90, 0x11, 0xe9, 0x0c, 0xde, 0x81, + 0xf0, 0x4c, 0x0b, 0xf9, 0xc9, 0xf5, 0xb0, 0x05, 0x69, 0x0c, 0xfe, 0x81, 0xf0, 0x9d, 0x84, 0xf0, + 0x98, 0x29, 0x07, 0x38, 0xe9, 0x03, 0x18, 0x7d, 0x81, 0xf0, 0x9d, 0x81, 0xf0, 0x60, 0xbd, 0x78, + 0xf0, 0x9d, 0x81, 0xf0, 0xbd, 0x7b, 0xf0, 0x9d, 0x84, 0xf0, 0x60, 0x68, 0x9d, 0xc6, 0xf0, 0x60, + 0x4a, 0xb0, 0x08, 0x9d, 0x0b, 0xf0, 0x68, 0x9d, 0x08, 0xf0, 0x60, 0x4a, 0x6a, 0x6a, 0x6d, 0x5b, + 0xf1, 0x8d, 0x2d, 0xf0, 0x68, 0x6d, 0x5c, 0xf1, 0x8d, 0x2e, 0xf0, 0x60, 0x4a, 0x90, 0x03, 0x4c, + 0xd3, 0xf9, 0x4a, 0xb0, 0x40, 0x4a, 0xb0, 0x17, 0x4a, 0xb0, 0x0f, 0x68, 0x8d, 0x27, 0xf0, 0x4a, + 0x4a, 0x4a, 0xa8, 0xb9, 0xaf, 0xf1, 0x8d, 0x28, 0xf0, 0x60, 0x68, 0x9d, 0x5d, 0xf0, 0x60, 0x4a, + 0xb0, 0x05, 0x68, 0x8d, 0x01, 0xf0, 0x60, 0x68, 0xf0, 0x11, 0x9d, 0x75, 0xf0, 0xbc, 0x63, 0xf0, + 0xd0, 0x08, 0x9d, 0x63, 0xf0, 0xa9, 0x01, 0x9d, 0x66, 0xf0, 0x60, 0x9d, 0x63, 0xf0, 0x9d, 0x69, + 0xf0, 0x9d, 0x6c, 0xf0, 0x60, 0x4a, 0xb0, 0x30, 0x4a, 0xb0, 0x05, 0x68, 0x9d, 0x39, 0xf0, 0x60, + 0x68, 0xa0, 0x00, 0x4a, 0x90, 0x02, 0xc8, 0x18, 0x48, 0x29, 0x07, 0x79, 0xac, 0xf1, 0x9d, 0x78, + 0xf0, 0x9d, 0x81, 0xf0, 0x68, 0x4a, 0x4a, 0x4a, 0x18, 0x79, 0xad, 0xf1, 0x9d, 0x7b, 0xf0, 0x9d, + 0x84, 0xf0, 0xa9, 0x5b, 0x9d, 0x7e, 0xf0, 0x60, 0x4a, 0xb0, 0x05, 0x68, 0x9d, 0xa2, 0xf0, 0x60, + 0x68, 0x8d, 0xcc, 0xf0, 0x60, 0x4a, 0xb0, 0x27, 0x4a, 0xb0, 0x0d, 0x4a, 0xb0, 0x05, 0x68, 0x8d, + 0x29, 0xf0, 0x60, 0x68, 0x9d, 0x9f, 0xf0, 0x60, 0x4a, 0xb0, 0x0f, 0x68, 0x9d, 0x93, 0xf0, 0xa0, + 0x00, 0x0a, 0x90, 0x01, 0x88, 0x98, 0x9d, 0x96, 0xf0, 0x60, 0x68, 0x9d, 0x72, 0xf0, 0x60, 0x4a, + 0xb0, 0x1c, 0x4a, 0xb0, 0x15, 0x68, 0x9d, 0xb7, 0xf0, 0xa5, 0xfd, 0x9d, 0xb1, 0xf0, 0xa5, 0xfe, + 0x9d, 0xb4, 0xf0, 0xbd, 0x33, 0xf0, 0x9d, 0xae, 0xf0, 0x60, 0x68, 0x6c, 0x5f, 0xf1, 0x4a, 0xb0, + 0x1e, 0x68, 0xd0, 0x0a, 0x9d, 0x4b, 0xf0, 0x9d, 0x51, 0xf0, 0x9d, 0x54, 0xf0, 0x60, 0x9d, 0x5a, + 0xf0, 0xbc, 0x4b, 0xf0, 0xd0, 0x08, 0x9d, 0x4b, 0xf0, 0xa9, 0x01, 0x9d, 0x4e, 0xf0, 0x60, 0x68, + 0x9d, 0x2a, 0xf0, 0x60, 0x4a, 0x90, 0x08, 0x9d, 0x48, 0xf0, 0x68, 0x9d, 0x45, 0xf0, 0x60, 0x68, + 0x4a, 0xb0, 0x61, 0x4a, 0xb0, 0x25, 0x4a, 0xb0, 0x05, 0x4a, 0xa0, 0xf0, 0xd0, 0x06, 0x0a, 0x0a, + 0x0a, 0x0a, 0xa0, 0x0f, 0x85, 0xff, 0x98, 0xb0, 0x09, 0x3d, 0x1d, 0xf0, 0x05, 0xff, 0x9d, 0x1d, + 0xf0, 0x60, 0x3d, 0x20, 0xf0, 0x05, 0xff, 0x9d, 0x20, 0xf0, 0x60, 0x4a, 0xb0, 0x38, 0x4a, 0xb0, + 0x64, 0x85, 0xff, 0xbd, 0xba, 0xf0, 0xdd, 0xa9, 0xf1, 0xf0, 0x54, 0xfe, 0xba, 0xf0, 0xa8, 0xa5, + 0xfd, 0x99, 0xe1, 0xf0, 0xa5, 0xfe, 0x99, 0xf0, 0xf0, 0xbd, 0x33, 0xf0, 0x99, 0x2f, 0xf1, 0xa4, + 0xff, 0xb9, 0x17, 0xf1, 0xf0, 0x36, 0x85, 0xfe, 0xb9, 0xff, 0xf0, 0x85, 0xfd, 0xb9, 0x3e, 0xf1, + 0x9d, 0x33, 0xf0, 0x60, 0xb0, 0x4b, 0x4a, 0xb0, 0x3c, 0xa8, 0xa5, 0xfd, 0x99, 0xff, 0xf0, 0xa5, + 0xfe, 0x99, 0x17, 0xf1, 0xbd, 0x33, 0xf0, 0x99, 0x3e, 0xf1, 0xbd, 0xba, 0xf0, 0xdd, 0xa9, 0xf1, + 0xf0, 0x0d, 0xfe, 0xba, 0xf0, 0xa8, 0xa9, 0x00, 0x99, 0xf0, 0xf0, 0x60, 0xa9, 0x30, 0x2c, 0xa9, + 0x28, 0x8d, 0x00, 0xf0, 0x60, 0x0a, 0x0a, 0x0a, 0x0a, 0x4d, 0x25, 0xf0, 0x29, 0xf0, 0x4d, 0x25, + 0xf0, 0x8d, 0x25, 0xf0, 0x60, 0x4d, 0x26, 0xf0, 0x29, 0x0f, 0x4d, 0x26, 0xf0, 0x8d, 0x26, 0xf0, + 0x60, 0x4a, 0xb0, 0x0b, 0x4a, 0xb0, 0x04, 0x8d, 0xca, 0xf0, 0x60, 0x8d, 0xcb, 0xf0, 0x60, 0x4a, + 0x90, 0x03, 0x4c, 0xa5, 0xfb, 0x4a, 0xa8, 0xf0, 0x21, 0x88, 0xf0, 0x34, 0x88, 0xf0, 0x42, 0x88, + 0xf0, 0x4a, 0x88, 0xf0, 0x52, 0x88, 0xf0, 0x5c, 0x88, 0xf0, 0x66, 0x88, 0xf0, 0x73, 0x29, 0x07, + 0x09, 0x10, 0xb0, 0x03, 0x4c, 0xb7, 0xfa, 0x4c, 0x7f, 0xfa, 0xac, 0x26, 0xf0, 0xb0, 0x07, 0xc8, + 0x98, 0x29, 0x0f, 0xd0, 0x07, 0x60, 0x98, 0x29, 0x0f, 0xf0, 0x04, 0x88, 0x8c, 0x26, 0xf0, 0x60, + 0xbd, 0x62, 0xf1, 0x49, 0xff, 0x2d, 0x25, 0xf0, 0x90, 0x03, 0x1d, 0x62, 0xf1, 0x8d, 0x25, 0xf0, + 0x60, 0xbd, 0x1a, 0xf0, 0x29, 0xfb, 0x90, 0x55, 0x09, 0x04, 0xb0, 0x51, 0xbd, 0x1a, 0xf0, 0x29, + 0xfd, 0x90, 0x4a, 0x09, 0x02, 0xb0, 0x46, 0xad, 0x25, 0xf0, 0x29, 0xf7, 0x90, 0x02, 0x09, 0x08, + 0x8d, 0x25, 0xf0, 0x60, 0xad, 0x26, 0xf0, 0x29, 0x7f, 0x90, 0x02, 0x09, 0x80, 0x8d, 0x26, 0xf0, + 0x60, 0x98, 0x8d, 0xbd, 0xf0, 0x8d, 0xdf, 0xf0, 0xc8, 0x8c, 0xe0, 0xf0, 0x2a, 0x8d, 0xc9, 0xf0, + 0x60, 0x98, 0x2a, 0x9d, 0x60, 0xf0, 0x60, 0x4a, 0xb0, 0x27, 0x4a, 0xb0, 0x14, 0xd0, 0x02, 0xa9, + 0x08, 0x0a, 0x0a, 0x0a, 0x0a, 0x5d, 0x1a, 0xf0, 0x29, 0xf0, 0x5d, 0x1a, 0xf0, 0x9d, 0x1a, 0xf0, + 0x60, 0x0a, 0x0a, 0x0a, 0x0a, 0x4d, 0x26, 0xf0, 0x29, 0x70, 0x4d, 0x26, 0xf0, 0x8d, 0x26, 0xf0, + 0x60, 0x4a, 0x90, 0x04, 0x9d, 0xc0, 0xf0, 0x60, 0xa8, 0xf0, 0x20, 0x88, 0xf0, 0x40, 0x88, 0xf0, + 0x63, 0x29, 0x03, 0x9d, 0xc3, 0xf0, 0xa9, 0x00, 0x9d, 0xcd, 0xf0, 0x9d, 0xd0, 0xf0, 0x9d, 0xd3, + 0xf0, 0x9d, 0xd6, 0xf0, 0x8d, 0xd9, 0xf0, 0x8d, 0xdc, 0xf0, 0x60, 0xbd, 0xb7, 0xf0, 0xf0, 0x05, + 0xde, 0xb7, 0xf0, 0xf0, 0x12, 0xbd, 0x33, 0xf0, 0xdd, 0xae, 0xf0, 0xd0, 0x0b, 0xbd, 0xb1, 0xf0, + 0x85, 0xfd, 0xbd, 0xb4, 0xf0, 0x85, 0xfe, 0x60, 0xa9, 0x38, 0x8d, 0x00, 0xf0, 0x60, 0xbd, 0xba, + 0xf0, 0xdd, 0xa8, 0xf1, 0xf0, 0x18, 0xde, 0xba, 0xf0, 0xa8, 0x88, 0xb9, 0xf0, 0xf0, 0xf0, 0x0d, + 0x85, 0xfe, 0xb9, 0xe1, 0xf0, 0x85, 0xfd, 0xb9, 0x2f, 0xf1, 0x9d, 0x33, 0xf0, 0x60, 0xa9, 0x20, + 0x8d, 0x00, 0xf0, 0x60, 0xad, 0x00, 0xf0, 0x5d, 0x62, 0xf1, 0x8d, 0x00, 0xf0, 0xa9, 0x01, 0x9d, + 0x30, 0xf0, 0x60, 0xad, 0x00, 0xf0, 0x29, 0x07, 0x8d, 0x81, 0xfc, 0xd0, 0x03, 0x20, 0xe9, 0xf2, + 0x60, 0x00, 0xa2, 0x51, 0xa0, 0xfc, 0x8e, 0x5d, 0xf1, 0x8c, 0x5e, 0xf1, 0x20, 0xcf, 0xf1, 0xa2, + 0x00, 0xa0, 0x09, 0x20, 0x00, 0xf2, 0xa9, 0x07, 0x8d, 0x00, 0xf0, 0x8d, 0x81, 0xfc, 0x60, 0x00, + 0x00, 0x00, 0xa9, 0x00, 0x29, 0xff, 0xf0, 0xf6, 0x4c, 0x29, 0xf3, 0xa9, 0x07, 0x8d, 0x00, 0xf0, + 0x60, 0x00, 0x20, 0x60, 0xec, 0x4c, 0x60, 0xfc, 0x20, 0x80, 0xec, 0x4c, 0x80, 0xfc +}; + +bool SidTune::MUS_mergeParts(Buffer_sidtt& musBuf, + Buffer_sidtt& strBuf) +{ + Buffer_sidtt mergeBuf; + + uint_least32_t mergeLen = musBuf.len()+strBuf.len(); + + // Sanity check. I do not trust those MUS/STR files around. + uint_least32_t freeSpace = endian_16(_sidtune_sidplayer1[1],_sidtune_sidplayer1[0]) + - SIDTUNE_MUS_DATA_ADDR; + if ( (musBuf.len()+strBuf.len()-4) > freeSpace) + { + info.statusString = _sidtune_txt_sizeExceeded; + return false; + } + +#ifdef HAVE_EXCEPTIONS + if ( !mergeBuf.assign(new(std::nothrow) uint8_t[mergeLen],mergeLen) ) +#else + if ( !mergeBuf.assign(new uint8_t[mergeLen],mergeLen) ) +#endif + { + info.statusString = _sidtune_txt_notEnoughMemory; + return false; + } + + // Install MUS data #1 including load address. +#ifndef SID_HAVE_BAD_COMPILER + memcpy(mergeBuf.get(),musBuf.get(),musBuf.len()); +#else + memcpy((void*)mergeBuf.get(),musBuf.get(),musBuf.len()); +#endif + + if ( !strBuf.isEmpty() && info.sidChipBase2!=0 ) + { + // Install MUS data #2 _NOT_ including load address. +#ifndef SID_HAVE_BAD_COMPILER + memcpy(mergeBuf.get()+musBuf.len(),strBuf.get(),strBuf.len()); +#else + memcpy((void*)(mergeBuf.get()+musBuf.len()),strBuf.get(),strBuf.len()); +#endif + } + + musBuf.assign(mergeBuf.xferPtr(),mergeBuf.xferLen()); + strBuf.erase(); + + return true; +} + +void SidTune::MUS_installPlayer(uint_least8_t *c64buf) +{ + if (status && (c64buf != 0)) + { + // Install MUS player #1. + uint_least16_t dest = endian_16(_sidtune_sidplayer1[1], + _sidtune_sidplayer1[0]); + memcpy(c64buf+dest,_sidtune_sidplayer1+2,sizeof(_sidtune_sidplayer1)-2); + // Point player #1 to data #1. + c64buf[dest+0xc6e] = (SIDTUNE_MUS_DATA_ADDR+2)&0xFF; + c64buf[dest+0xc70] = (SIDTUNE_MUS_DATA_ADDR+2)>>8; + + if (info.sidChipBase2 != 0) + { + // Install MUS player #2. + dest = endian_16(_sidtune_sidplayer2[1], + _sidtune_sidplayer2[0]); + memcpy(c64buf+dest,_sidtune_sidplayer2+2,sizeof(_sidtune_sidplayer2)-2); + // Point player #2 to data #2. + c64buf[dest+0xc6e] = (SIDTUNE_MUS_DATA_ADDR+musDataLen+2)&0xFF; + c64buf[dest+0xc70] = (SIDTUNE_MUS_DATA_ADDR+musDataLen+2)>>8; + } + } +} + +SidTune::LoadStatus SidTune::MUS_load (Buffer_sidtt& musBuf, bool init) +{ + Buffer_sidtt empty; + return MUS_load (musBuf, empty, init); +} + +SidTune::LoadStatus SidTune::MUS_load (Buffer_sidtt& musBuf, + Buffer_sidtt& strBuf, + bool init) +{ + uint_least32_t voice3Index; + SmartPtr_sidtt spPet(musBuf.get()+fileOffset,musBuf.len()-fileOffset); + if ( !MUS_detect(&spPet[0],spPet.tellLength(),voice3Index) ) + return LOAD_NOT_MINE; + + if (init) + { + info.songs = (info.startSong = 1); + info.musPlayer = true; + + songSpeed[0] = SIDTUNE_SPEED_CIA_1A; +#ifdef SIDTUNE_PSID2NG + clockSpeed[0] = SIDTUNE_CLOCK_ANY; +#endif + } + + // Check setting compatibility for MUS playback + if ((info.compatibility != SIDTUNE_COMPATIBILITY_C64) || + (info.relocStartPage != 0) || (info.relocPages != 0)) + { + info.formatString = _sidtune_txt_invalid; + return LOAD_ERROR; + } + + { // All subtunes should be CIA + for (uint_least16_t i = 0; i < info.songs; i++) + { + if (songSpeed[i] != SIDTUNE_SPEED_CIA_1A) + { + info.formatString = _sidtune_txt_invalid; + return LOAD_ERROR; + } + } + } + + musDataLen = musBuf.len(); + info.loadAddr = SIDTUNE_MUS_DATA_ADDR; + info.sidChipBase1 = SIDTUNE_SID1_BASE_ADDR; + + // No credits so extract them from the MUS files + bool credits = (infoString[0][0] | infoString[1][0] | infoString[2][0]) != 0; + + // Voice3Index now is offset to text lines (uppercase Pet-strings). + spPet += voice3Index; + + // Already have credits just skip over the ones in the MUS + if (credits) + { + while (spPet[0]) + convertPetsciiToAscii(spPet,0); + } + // Extract credits + else + { + for (int line = info.numberOfInfoStrings = 0; spPet[0]; line = + ++info.numberOfInfoStrings) + { + if (line < 10) + { + convertPetsciiToAscii(spPet,infoString[line]); + info.infoString[line] = infoString[line]; + } + else + convertPetsciiToAscii(spPet,0); + } + } + ++spPet; + + // If we appear to have additional data at the end, check is it's + // another mus file (but only if a second file isn't supplied) + bool stereo = false; + if ( !strBuf.isEmpty() ) + { + if ( !MUS_detect(strBuf.get(),strBuf.len(),voice3Index) ) + return LOAD_ERROR; + spPet.setBuffer (strBuf.get(),strBuf.len()); + stereo = true; + } + else + { // For MUS + STR via stdin the files come combined + if ( spPet.good() ) + { + uint_least16_t pos = (uint_least16_t) spPet.tellPos(); + if ( MUS_detect(&spPet[0],spPet.tellLength()-pos,voice3Index) ) + { + musDataLen = pos; + stereo = true; + } + } + } + + if ( stereo ) + { // Voice3Index now is offset to text lines (uppercase Pet-strings). + spPet += voice3Index; + + // Already have credits just skip over the ones in the MUS + if (credits) + { + while (spPet[0]) + convertPetsciiToAscii(spPet,0); + } + // Extract credits + else + { + for (int line = info.numberOfInfoStrings; spPet[0]; line = + ++info.numberOfInfoStrings) + { + if (line < 10) + { + convertPetsciiToAscii(spPet,infoString[line]); + info.infoString[line] = infoString[line]; + } + else + convertPetsciiToAscii(spPet,0); + } + } + + info.sidChipBase2 = SIDTUNE_SID2_BASE_ADDR; + info.formatString = _sidtune_txt_format_str; + } + else + { + info.sidChipBase2 = 0; + info.formatString = _sidtune_txt_format_mus; + } + MUS_setPlayerAddress(); + + if (!credits) + { // Remove trailing empty lines. + const int lines = info.numberOfInfoStrings; + { + for ( int line = lines-1; line >= 0; line-- ) + { + if (strlen(info.infoString[line]) == 0) + --info.numberOfInfoStrings; + else + break; + } + } + + // Three strings are assumed to be credits in + // the format title, author and released, which + // these are not + if (info.numberOfInfoStrings == 3) + { + info.infoString[3] = &infoString[3][0]; + info.numberOfInfoStrings++; + } + } + return LOAD_OK; +} diff --git a/PSID/sidtune/Makefile.am b/PSID/sidtune/Makefile.am new file mode 100644 index 00000000..2e8ca251 --- /dev/null +++ b/PSID/sidtune/Makefile.am @@ -0,0 +1,20 @@ +noinst_LIBRARIES = libsidtune.a + +# enable warnings and turn them into errors to enforce warning free code +AM_CFLAGS = -Wall -Werror + +libsidtune_a_CFLAGS = $(AM_CFLAGS) + +libsidtune_a_SOURCES = IconInfo.cpp \ +InfoFile.cpp \ +MUS.cpp \ +p00.cpp \ +PP20.cpp \ +PP20.h \ +PP20_Defs.h \ +prg.cpp \ +PSID.cpp \ +SidTune.cpp \ +SidTuneCfg.h \ +SidTuneTools.cpp \ +SidTuneTools.h diff --git a/PSID/sidtune/Makefile.in b/PSID/sidtune/Makefile.in new file mode 100644 index 00000000..1e2f96a3 --- /dev/null +++ b/PSID/sidtune/Makefile.in @@ -0,0 +1,584 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = src/sidtune +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/macros/ac_compile_check_sizeof.m4 \ + $(top_srcdir)/macros/ac_create_stdint_h.m4 \ + $(top_srcdir)/macros/ax_func_mkdir.m4 \ + $(top_srcdir)/macros/sidtune.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/src/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +am__v_AR_1 = +libsidtune_a_AR = $(AR) $(ARFLAGS) +libsidtune_a_LIBADD = +am_libsidtune_a_OBJECTS = IconInfo.$(OBJEXT) InfoFile.$(OBJEXT) \ + MUS.$(OBJEXT) p00.$(OBJEXT) PP20.$(OBJEXT) prg.$(OBJEXT) \ + PSID.$(OBJEXT) SidTune.$(OBJEXT) SidTuneTools.$(OBJEXT) +libsidtune_a_OBJECTS = $(am_libsidtune_a_OBJECTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/src +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libsidtune_a_SOURCES) +DIST_SOURCES = $(libsidtune_a_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SID_HAVE_BOOL = @SID_HAVE_BOOL@ +SID_HAVE_STDBOOL_H = @SID_HAVE_STDBOOL_H@ +SID_SIZEOF_CHAR = @SID_SIZEOF_CHAR@ +SID_SIZEOF_INT = @SID_SIZEOF_INT@ +SID_SIZEOF_LONG_INT = @SID_SIZEOF_LONG_INT@ +SID_SIZEOF_SHORT_INT = @SID_SIZEOF_SHORT_INT@ +SID_WORDS_ENDIANESS = @SID_WORDS_ENDIANESS@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +noinst_LIBRARIES = libsidtune.a + +# enable warnings and turn them into errors to enforce warning free code +AM_CFLAGS = -Wall -Werror +libsidtune_a_CFLAGS = $(AM_CFLAGS) +libsidtune_a_SOURCES = IconInfo.cpp \ +InfoFile.cpp \ +MUS.cpp \ +p00.cpp \ +PP20.cpp \ +PP20.h \ +PP20_Defs.h \ +prg.cpp \ +PSID.cpp \ +SidTune.cpp \ +SidTuneCfg.h \ +SidTuneTools.cpp \ +SidTuneTools.h + +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/sidtune/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/sidtune/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +libsidtune.a: $(libsidtune_a_OBJECTS) $(libsidtune_a_DEPENDENCIES) $(EXTRA_libsidtune_a_DEPENDENCIES) + $(AM_V_at)-rm -f libsidtune.a + $(AM_V_AR)$(libsidtune_a_AR) libsidtune.a $(libsidtune_a_OBJECTS) $(libsidtune_a_LIBADD) + $(AM_V_at)$(RANLIB) libsidtune.a + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/IconInfo.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/InfoFile.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/MUS.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PP20.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PSID.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SidTune.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SidTuneTools.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/p00.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prg.Po@am__quote@ + +.cpp.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-noinstLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-noinstLIBRARIES cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/PSID/sidtune/PP20.cpp b/PSID/sidtune/PP20.cpp new file mode 100644 index 00000000..0a93f3a9 --- /dev/null +++ b/PSID/sidtune/PP20.cpp @@ -0,0 +1,297 @@ +/* + * /home/ms/files/source/libsidtune/RCS/PP20.cpp,v + * + * PowerPacker (AMIGA) "PP20" format decompressor. + * Copyright (C) Michael Schwendt + * + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "PP20.h" + +#include +#ifdef PP20_HAVE_EXCEPTIONS +#include +#endif + +/* Read a big-endian 32-bit word from four bytes in memory. + No endian-specific optimizations applied. */ +inline udword_ppt readBEdword(const ubyte_ppt ptr[4]) +{ + return ( (((udword_ppt)ptr[0])<<24) + (((udword_ppt)ptr[1])<<16) + + (((udword_ppt)ptr[2])<<8) + ((udword_ppt)ptr[3]) ); +} + +const char _pp20_txt_packeddatacorrupt[] = "PowerPacker: Packed data is corrupt"; +const char _pp20_txt_unrecognized[] = "PowerPacker: Unrecognized compression method"; +const char _pp20_txt_uncompressed[] = "Not compressed with PowerPacker (PP20)"; +const char _pp20_txt_notenoughmemory[] = "Not enough free memory"; +const char _pp20_txt_fast[] = "PowerPacker: fast compression"; +const char _pp20_txt_mediocre[] = "PowerPacker: mediocre compression"; +const char _pp20_txt_good[] = "PowerPacker: good compression"; +const char _pp20_txt_verygood[] = "PowerPacker: very good compression"; +const char _pp20_txt_best[] = "PowerPacker: best compression"; +const char _pp20_txt_na[] = "N/A"; + +const char* PP20::PP_ID = "PP20"; + +PP20::PP20() : + efficiency(), + sourceBeg(NULL), + readPtr(NULL), + destBeg(NULL), + writePtr(NULL), + current(0), + bits(0), + globalError(false) +{ + statusString = _pp20_txt_uncompressed; +} + +bool PP20::isCompressed(const void* source, const udword_ppt size) +{ + // Check minimum input size, PP20 ID, and efficiency table. + if ( size<8 ) + { + return false; + } + // We hope that every file with a valid signature and a valid + // efficiency table is PP-compressed actually. + const char* idPtr = (const char*)source; + if ( strncmp(idPtr,PP_ID,4) != 0 ) + { + statusString = _pp20_txt_uncompressed; + return false; + } + return checkEfficiency(idPtr+4); +} + +bool PP20::checkEfficiency(const void* source) +{ + const udword_ppt PP_BITS_FAST = 0x09090909; + const udword_ppt PP_BITS_MEDIOCRE = 0x090a0a0a; + const udword_ppt PP_BITS_GOOD = 0x090a0b0b; + const udword_ppt PP_BITS_VERYGOOD = 0x090a0c0c; + const udword_ppt PP_BITS_BEST = 0x090a0c0d; + + // Copy efficiency table. + memcpy(efficiency,(const ubyte_ppt*)source,4); + udword_ppt eff = readBEdword(efficiency); + if (( eff != PP_BITS_FAST ) && + ( eff != PP_BITS_MEDIOCRE ) && + ( eff != PP_BITS_GOOD ) && + ( eff != PP_BITS_VERYGOOD ) && + ( eff != PP_BITS_BEST )) + { + statusString = _pp20_txt_unrecognized; + return false; + } + + // Define string describing compression encoding used. + switch ( eff) + { + case PP_BITS_FAST: + statusString = _pp20_txt_fast; + break; + case PP_BITS_MEDIOCRE: + statusString = _pp20_txt_mediocre; + break; + case PP_BITS_GOOD: + statusString = _pp20_txt_good; + break; + case PP_BITS_VERYGOOD: + statusString = _pp20_txt_verygood; + break; + case PP_BITS_BEST: + statusString = _pp20_txt_best; + break; + } + + return true; +} + +// Move four bytes to Motorola big-endian double-word. +inline void PP20::bytesTOdword() +{ + readPtr -= 4; + if ( readPtr < sourceBeg ) + { + statusString = _pp20_txt_packeddatacorrupt; + globalError = true; + } + else + { + current = readBEdword(readPtr); + } +} + +inline udword_ppt PP20::readBits(int count) +{ + udword_ppt data = 0; + // read 'count' bits of packed data + for (; count > 0; count--) + { + // equal to shift left + data += data; + // merge bit 0 + data |= (current&1); + current >>= 1; + if (--bits == 0) + { + bytesTOdword(); + bits = 32; + } + } + return data; +} + +inline void PP20::bytes() +{ + udword_ppt count, add; + count = (add = readBits(2)); + while (add == 3) + { + add = readBits(2); + count += add; + } + for ( ++count; count > 0 ; count--) + { + if (writePtr > destBeg) + { + *(--writePtr) = (ubyte_ppt)readBits(8); + } + else + { + statusString = _pp20_txt_packeddatacorrupt; + globalError = true; + } + } +} + +inline void PP20::sequence() +{ + udword_ppt offset; + udword_ppt length = readBits(2); // is length-2 + int offsetBitLen = (int)efficiency[length]; + length += 2; + if ( length != 5 ) + offset = readBits( offsetBitLen ); + else + { + if ( readBits(1) == 0 ) + offsetBitLen = 7; + offset = readBits( offsetBitLen ); + udword_ppt add = readBits(3); + length += add; + while ( add == 7 ) + { + add = readBits(3); + length += add; + } + } + for ( ; length > 0 ; length-- ) + { + if ( writePtr > destBeg ) + { + --writePtr; + *writePtr = *(writePtr+1+offset); + } + else + { + statusString = _pp20_txt_packeddatacorrupt; + globalError = true; + } + } +} + +udword_ppt PP20::decompress(const void* source, + udword_ppt size, + ubyte_ppt** destRef) +{ + globalError = false; // assume no error + + sourceBeg = (const ubyte_ppt*)source; + readPtr = sourceBeg; + + if ( !isCompressed(readPtr,size) ) + { + return 0; + } + + // Uncompressed size is stored at end of source file. + // Backwards decompression. + readPtr += (size-4); + + udword_ppt lastDword = readBEdword(readPtr); + // Uncompressed length in bits 31-8 of last dword. + udword_ppt outputLen = lastDword>>8; + + // Allocate memory for output data. + ubyte_ppt* dest; +#ifdef PP20_HAVE_EXCEPTIONS + if (( dest = new(std::nothrow) ubyte_ppt[outputLen]) == 0 ) +#else + if (( dest = new ubyte_ppt[outputLen]) == 0 ) +#endif + { + statusString = _pp20_txt_notenoughmemory; + return 0; + } + + // Lowest dest. address for range-checks. + destBeg = dest; + // Put destptr to end of uncompressed data. + writePtr = dest+outputLen; + + // Read number of unused bits in 1st data dword + // from lowest bits 7-0 of last dword. + bits = 32 - (lastDword&0xFF); + + // Main decompression loop. + bytesTOdword(); + if ( bits != 32 ) + current >>= (32-bits); + do + { + if ( readBits(1) == 0 ) + bytes(); + if ( writePtr > dest ) + sequence(); + if ( globalError ) + { + // statusString already set. + outputLen = 0; // unsuccessful decompression + break; + } + } while ( writePtr > dest ); + + // Finished. + + if (outputLen > 0) // successful + { + // Free any previously existing destination buffer. + if ( *destRef != 0 ) + { + delete[] *destRef; + } + *destRef = dest; + } + else + { + delete[] dest; + } + + return outputLen; +} diff --git a/PSID/sidtune/PP20.h b/PSID/sidtune/PP20.h new file mode 100644 index 00000000..74e7f5f7 --- /dev/null +++ b/PSID/sidtune/PP20.h @@ -0,0 +1,70 @@ +/* + * /home/ms/files/source/libsidtune/RCS/PP20.h,v + * + * PowerPacker (AMIGA) "PP20" format decompressor. + * Copyright (C) Michael Schwendt + * + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PP_DECOMPRESSOR_H +#define PP_DECOMPRESSOR_H + +#include "PP20_Defs.h" + +class PP20 +{ + public: + + PP20(); + + bool isCompressed(const void* source, const udword_ppt size); + + // If successful, allocates a new buffer containing the + // uncompresse data and returns the uncompressed length. + // Else, returns 0. + udword_ppt decompress(const void* source, + udword_ppt size, + ubyte_ppt** destRef); + + const char* getStatusString() { return statusString; } + + private: + bool checkEfficiency(const void* source); + + void bytesTOdword(); + udword_ppt readBits(int count); + void bytes(); + void sequence(); + + static const char* PP_ID; + + ubyte_ppt efficiency[4]; + + const ubyte_ppt* sourceBeg; + const ubyte_ppt* readPtr; + + const ubyte_ppt* destBeg; + ubyte_ppt* writePtr; + + udword_ppt current; // compressed data longword + int bits; // number of bits in 'current' to evaluate + + bool globalError; // exception-free version of code + + const char* statusString; +}; + +#endif /* PP_DECOMPRESSOR_H */ diff --git a/PSID/sidtune/PP20_Defs.h b/PSID/sidtune/PP20_Defs.h new file mode 100644 index 00000000..1dfcc7ed --- /dev/null +++ b/PSID/sidtune/PP20_Defs.h @@ -0,0 +1,39 @@ +/* + * /home/ms/files/source/libsidtune/RCS/PP20_Defs.h,v + * + * PowerPacker (AMIGA) "PP20" format decompressor. + * Copyright (C) Michael Schwendt + * + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PP_DECOMPRESSOR_DEFS_H +#define PP_DECOMPRESSOR_DEFS_H + +#include "../SidPlay/sidtypes.h" + +#ifdef HAVE_EXCEPTIONS + #define PP20_HAVE_EXCEPTIONS +#else + #undef PP20_HAVE_EXCEPTIONS +#endif + +// Wanted: 8-bit unsigned. +typedef uint_least8_t ubyte_ppt; + +// Wanted: 32-bit unsigned. +typedef uint_least32_t udword_ppt; + +#endif /* PP_DECOMPRESSOR_DEFS_H */ diff --git a/PSID/sidtune/PSID.cpp b/PSID/sidtune/PSID.cpp new file mode 100644 index 00000000..fae913cc --- /dev/null +++ b/PSID/sidtune/PSID.cpp @@ -0,0 +1,392 @@ +/* + * /home/ms/files/source/libsidtune/RCS/PSID.cpp,v + * + * PlaySID one-file format support. + + this code has been modified for integration into the Sidekick64 software + (the modifications are mostly removing everything that does not compile with vanilla Circle, + I really feel sorry for having disfigured this code, please refer to the original repository + if you want to get the real and decent psid64 version) + + * + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include "../config.h" +#include "SidTuneCfg.h" +#include "../SidPlay/SidTune.h" +#include "../SidPlay/sidendian.h" + +#define PSID_ID 0x50534944 +#define RSID_ID 0x52534944 + +// Header has been extended for 'RSID' format +// The following changes are present: +// id = 'RSID' +// version = 2 or 3 only +// play, load and speed reserved 0 +// psidspecific flag reserved 0 +// init cannot be under ROMS/IO +// load cannot be less than 0x0801 (start of basic) + +struct psidHeader // all values big-endian +{ + char id[4]; // 'PSID' (ASCII) + uint8_t version[2]; // 0x0001, 0x0002 or 0x0003 + uint8_t data[2]; // 16-bit offset to binary data in file + uint8_t load[2]; // 16-bit C64 address to load file to + uint8_t init[2]; // 16-bit C64 address of init subroutine + uint8_t play[2]; // 16-bit C64 address of play subroutine + uint8_t songs[2]; // number of songs + uint8_t start[2]; // start song out of [1..256] + uint8_t speed[4]; // 32-bit speed info + // bit: 0=50 Hz, 1=CIA 1 Timer A (default: 60 Hz) + char name[32]; // ASCII strings, 31 characters long and + char author[32]; // terminated by a trailing zero + char released[32]; // + uint8_t flags[2]; // only version 0x0002 + uint8_t relocStartPage; // only version 0x0002B + uint8_t relocPages; // only version 0x0002B + uint8_t secondSIDAddress; // only version 0x0003, reserved in version 0x0002 + uint8_t thirdSIDAddress; // only version 0x0004, reserved in version 0x0002 and 0x003 +}; + +enum +{ + PSID_MUS = 1 << 0, + PSID_SPECIFIC = 1 << 1, // These two are mutally exclusive + PSID_BASIC = 1 << 1, + PSID_CLOCK = 3 << 2, + PSID_SIDMODEL = 3 << 4, + PSID_SID2MODEL = 3 << 6, + PSID_SID3MODEL = 3 << 8 +}; + +enum +{ + PSID_CLOCK_UNKNOWN = 0, + PSID_CLOCK_PAL = 1 << 2, + PSID_CLOCK_NTSC = 1 << 3, + PSID_CLOCK_ANY = PSID_CLOCK_PAL | PSID_CLOCK_NTSC +}; + +enum +{ + PSID_SIDMODEL_UNKNOWN = 0, + PSID_SIDMODEL_6581 = 1 << 0, + PSID_SIDMODEL_8580 = 1 << 1, + PSID_SIDMODEL_ANY = PSID_SIDMODEL_6581 | PSID_SIDMODEL_8580 +}; + +static const char _sidtune_format_psid[] = "PlaySID one-file format (PSID)"; +static const char _sidtune_format_rsid[] = "Real C64 one-file format (RSID)"; +static const char _sidtune_unknown_psid[] = "Unsupported PSID version"; +static const char _sidtune_unknown_rsid[] = "Unsupported RSID version"; +static const char _sidtune_truncated[] = "ERROR: File is most likely truncated"; +static const char _sidtune_invalid[] = "ERROR: File contains invalid data"; + +static const int _sidtune_psid_maxStrLen = 31; + + +//SidTune::LoadStatus SidTune::PSID_fileSupport(Buffer_sidtt& dataBuf) +SidTune::LoadStatus SidTune::PSID_fileSupport( unsigned char *dataBuf, int dataLen ) +{ + int clock, compatibility; + uint_least32_t speed; + uint_least32_t bufLen = dataLen; +#ifdef SIDTUNE_PSID2NG + clock = SIDTUNE_CLOCK_UNKNOWN; +#else + clock = info.clockSpeed; +#endif + compatibility = SIDTUNE_COMPATIBILITY_C64; + + // Require minimum size to allow access to the first few bytes. + // Require a valid ID and version number. + const psidHeader* pHeader = reinterpret_cast(dataBuf); + + // File format check + if (bufLen<6) + return LOAD_NOT_MINE; + if (endian_big32((const uint_least8_t*)pHeader->id)==PSID_ID) + { + switch (endian_big16(pHeader->version)) + { + case 1: + compatibility = SIDTUNE_COMPATIBILITY_PSID; + // Deliberate run on + case 2: + case 3: + case 4: + break; + default: + info.formatString = _sidtune_unknown_psid; + return LOAD_ERROR; + } + info.formatString = _sidtune_format_psid; + } + else if (endian_big32((const uint_least8_t*)pHeader->id)==RSID_ID) + { + switch (endian_big16(pHeader->version)) + { + case 2: + case 3: + case 4: + break; + default: + info.formatString = _sidtune_unknown_rsid; + return LOAD_ERROR; + } + info.formatString = _sidtune_format_rsid; + compatibility = SIDTUNE_COMPATIBILITY_R64; + } + else + { + return LOAD_NOT_MINE; + } + + // Due to security concerns, input must be at least as long as version 1 + // header plus 16-bit C64 load address. That is the area which will be + // accessed. + if ( bufLen < (sizeof(psidHeader)+2) ) + { + info.formatString = _sidtune_truncated; + return LOAD_ERROR; + } + + fileOffset = endian_big16(pHeader->data); + info.loadAddr = endian_big16(pHeader->load); + info.initAddr = endian_big16(pHeader->init); + info.playAddr = endian_big16(pHeader->play); + info.songs = endian_big16(pHeader->songs); + info.startSong = endian_big16(pHeader->start); + info.sidChipBase1 = 0xd400; + info.sidChipBase2 = 0; + info.sidChipBase3 = 0; + info.compatibility = compatibility; + speed = endian_big32(pHeader->speed); + + if (info.songs > SIDTUNE_MAX_SONGS) + { + info.songs = SIDTUNE_MAX_SONGS; + } + + info.musPlayer = false; + info.sidModel = SIDTUNE_SIDMODEL_UNKNOWN; + info.sid2Model = SIDTUNE_SIDMODEL_UNKNOWN; + info.sid3Model = SIDTUNE_SIDMODEL_UNKNOWN; + info.relocPages = 0; + info.relocStartPage = 0; + info.secondSIDAddress = 0; + info.thirdSIDAddress = 0; + if ( endian_big16(pHeader->version) >= 2 ) + { + uint_least16_t flags = endian_big16(pHeader->flags); + if (flags & PSID_MUS) + { // MUS tunes run at any speed + clock = SIDTUNE_CLOCK_ANY; + info.musPlayer = true; + } + +#ifdef SIDTUNE_PSID2NG + // This flags is only available for the appropriate + // file formats + switch (compatibility) + { + case SIDTUNE_COMPATIBILITY_C64: + if (flags & PSID_SPECIFIC) + info.compatibility = SIDTUNE_COMPATIBILITY_PSID; + break; + case SIDTUNE_COMPATIBILITY_R64: + if (flags & PSID_BASIC) + info.compatibility = SIDTUNE_COMPATIBILITY_BASIC; + break; + } + + if (flags & PSID_CLOCK_PAL) + clock |= SIDTUNE_CLOCK_PAL; + if (flags & PSID_CLOCK_NTSC) + clock |= SIDTUNE_CLOCK_NTSC; + info.clockSpeed = clock; + + info.sidModel = SIDTUNE_SIDMODEL_UNKNOWN; + if ((flags >> 4) & PSID_SIDMODEL_6581) + info.sidModel |= SIDTUNE_SIDMODEL_6581; + if ((flags >> 4) & PSID_SIDMODEL_8580) + info.sidModel |= SIDTUNE_SIDMODEL_8580; + + info.sid2Model = SIDTUNE_SIDMODEL_UNKNOWN; + if ((flags >> 6) & PSID_SIDMODEL_6581) + info.sid2Model |= SIDTUNE_SIDMODEL_6581; + if ((flags >> 6) & PSID_SIDMODEL_8580) + info.sid2Model |= SIDTUNE_SIDMODEL_8580; + + info.sid3Model = SIDTUNE_SIDMODEL_UNKNOWN; + if ((flags >> 8) & PSID_SIDMODEL_6581) + info.sid3Model |= SIDTUNE_SIDMODEL_6581; + if ((flags >> 8) & PSID_SIDMODEL_8580) + info.sid3Model |= SIDTUNE_SIDMODEL_8580; + + info.relocStartPage = pHeader->relocStartPage; + info.relocPages = pHeader->relocPages; + +#endif // SIDTUNE_PSID2NG + } + + if ( endian_big16(pHeader->version) >= 3 ) + { + info.secondSIDAddress = pHeader->secondSIDAddress; + } + if ( endian_big16(pHeader->version) >= 4 ) + { + info.thirdSIDAddress = pHeader->thirdSIDAddress; + } + + // Check reserved fields to force real c64 compliance + // as required by the RSID specification + if (compatibility == SIDTUNE_COMPATIBILITY_R64) + { + if ((info.loadAddr != 0) || + (info.playAddr != 0) || + (speed != 0)) + { + info.formatString = _sidtune_invalid; + return LOAD_ERROR; + } + // Real C64 tunes appear as CIA + speed = ~0; + } + // Create the speed/clock setting table. + convertOldStyleSpeedToTables(speed, clock); + + // Copy info strings, so they will not get lost. + info.numberOfInfoStrings = 3; + // Name + strncpy(&infoString[0][0],pHeader->name,_sidtune_psid_maxStrLen); + info.infoString[0] = &infoString[0][0]; + // Author + strncpy(&infoString[1][0],pHeader->author,_sidtune_psid_maxStrLen); + info.infoString[1] = &infoString[1][0]; + // Released + strncpy(&infoString[2][0],pHeader->released,_sidtune_psid_maxStrLen); + info.infoString[2] = &infoString[2][0]; + + //if ( info.musPlayer ) + // return MUS_load (dataBuf); + return LOAD_OK; +} + + +/*bool SidTune::PSID_fileSupportSave(std::ofstream& fMyOut, const uint_least8_t* dataBuffer) +{ + psidHeader myHeader; + endian_big32((uint_least8_t*)myHeader.id,PSID_ID); + endian_big16(myHeader.version,2); + endian_big16(myHeader.data,sizeof(psidHeader)); + endian_big16(myHeader.songs,info.songs); + endian_big16(myHeader.start,info.startSong); + + uint_least32_t speed = 0; + uint_least32_t maxBugSongs = ((info.songs <= 32) ? info.songs : 32); + for (uint_least32_t s = 0; s < maxBugSongs; s++) + { + if (songSpeed[s] == SIDTUNE_SPEED_CIA_1A) + speed |= (1<> 8; + fMyOut.write( (char*)saveAddr, 2 ); // !cast! + + // Data starts at: bufferaddr + fileoffset + // Data length: datafilelen - fileoffset + fMyOut.write( (const char*)dataBuffer + fileOffset, info.dataFileLen - fileOffset ); // !cast! + } + + if ( !fMyOut ) + return false; + else + return true; +} +*/ \ No newline at end of file diff --git a/PSID/sidtune/SidTune.cpp b/PSID/sidtune/SidTune.cpp new file mode 100644 index 00000000..49708ca2 --- /dev/null +++ b/PSID/sidtune/SidTune.cpp @@ -0,0 +1,1057 @@ +/* + * /home/ms/files/source/libsidtune/RCS/SidTune.cpp,v + * + this code has been modified for integration into the Sidekick64 software + (the modifications are mostly removing everything that does not compile with vanilla Circle, + I really feel sorry for having disfigured this code, please refer to the original repository + if you want to get the real and decent psid64 version) + + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _SidTune_cpp_ + +#include "../config.h" +#include "SidTuneCfg.h" +#include "../SidPlay/SidTune.h" +#include "SidTuneTools.h" +#include "../SidPlay/sidendian.h" +#include "PP20.h" + +#ifdef HAVE_EXCEPTIONS +# include +#endif +//#include +//#include +#include +#include +#include + +// this is only a hack +static char *strdup( char *s ) +{ + char *n = new char[ strlen( s ) ]; + strcpy( n, s ); + return n; +} + +#if defined(HAVE_IOS_OPENMODE) + typedef std::ios::openmode openmode; +#else + typedef int openmode; +#endif + +static const char* savedString = "--- SAVED WITH SIDPLAY ---"; + +const char* SidTune::txt_songNumberExceed = "SIDTUNE WARNING: Selected song number was too high"; +const char* SidTune::txt_empty = "SIDTUNE ERROR: No data to load"; +const char* SidTune::txt_unrecognizedFormat = "SIDTUNE ERROR: Could not determine file format"; +const char* SidTune::txt_noDataFile = "SIDTUNE ERROR: Did not find the corresponding data file"; +const char* SidTune::txt_notEnoughMemory = "SIDTUNE ERROR: Not enough free memory"; +const char* SidTune::txt_cantLoadFile = "SIDTUNE ERROR: Could not load input file"; +const char* SidTune::txt_cantOpenFile = "SIDTUNE ERROR: Could not open file for binary input"; +const char* SidTune::txt_fileTooLong = "SIDTUNE ERROR: Input data too long"; +const char* SidTune::txt_dataTooLong = "SIDTUNE ERROR: Size of music data exceeds C64 memory"; +const char* SidTune::txt_cantCreateFile = "SIDTUNE ERROR: Could not create output file"; +const char* SidTune::txt_fileIoError = "SIDTUNE ERROR: File I/O error"; +const char* SidTune::txt_VBI = "VBI"; +const char* SidTune::txt_CIA = "CIA 1 Timer A"; +const char* SidTune::txt_noErrors = "No errors"; +const char* SidTune::txt_na = "N/A"; +const char* SidTune::txt_badAddr = "SIDTUNE ERROR: Bad address data"; +const char* SidTune::txt_badReloc = "SIDTUNE ERROR: Bad reloc data"; +const char* SidTune::txt_corrupt = "SIDTUNE ERROR: File is incomplete or corrupt"; + +// Default sidtune file name extensions. This selection can be overriden +// by specifying a custom list in the constructor. +const char* defaultFileNameExt[] = +{ + // Preferred default file extension for single-file sidtunes + // or sidtune description files in SIDPLAY INFOFILE format. + ".sid", + // Common file extension for single-file sidtunes due to SIDPLAY/DOS + // displaying files *.DAT in its file selector by default. + // Originally this was intended to be the extension of the raw data file + // of two-file sidtunes in SIDPLAY INFOFILE format. + ".dat", + // Extension of Amiga Workbench tooltype icon info files, which + // have been cut to MS-DOS file name length (8.3). + ".inf", + // No extension for the raw data file of two-file sidtunes in + // PlaySID Amiga Workbench tooltype icon info format. + "", + // Common upper-case file extensions from MS-DOS (unconverted). + ".DAT", ".SID", ".INF", + // File extensions used (and created) by various C64 emulators and + // related utilities. These extensions are recommended to be used as + // a replacement for ".dat" in conjunction with two-file sidtunes. + ".c64", ".prg", ".p00", ".C64", ".PRG", ".P00", + // Uncut extensions from Amiga. + ".info", ".INFO", ".data", ".DATA", + // Stereo Sidplayer (.mus/.MUS ought not be included because + // these must be loaded first; it sometimes contains the first + // credit lines of a MUS/STR pair). + ".str", ".STR", ".mus", ".MUS", + // End. + 0 +}; + +// Petscii to Ascii conversion table +static const char _sidtune_CHRtab[256] = // CHR$ conversion table (0x01 = no output) +{ + 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0xd, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x20,0x21, 0x1,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f, + 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f, + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x24,0x5d,0x20,0x20, + // alternative: CHR$(92=0x5c) => ISO Latin-1(0xa3) + 0x2d,0x23,0x7c,0x2d,0x2d,0x2d,0x2d,0x7c,0x7c,0x5c,0x5c,0x2f,0x5c,0x5c,0x2f,0x2f, + 0x5c,0x23,0x5f,0x23,0x7c,0x2f,0x58,0x4f,0x23,0x7c,0x23,0x2b,0x7c,0x7c,0x26,0x5c, + // 0x80-0xFF + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, + 0x20,0x7c,0x23,0x2d,0x2d,0x7c,0x23,0x7c,0x23,0x2f,0x7c,0x7c,0x2f,0x5c,0x5c,0x2d, + 0x2f,0x2d,0x2d,0x7c,0x7c,0x7c,0x7c,0x2d,0x2d,0x2d,0x2f,0x5c,0x5c,0x2f,0x2f,0x23, + 0x2d,0x23,0x7c,0x2d,0x2d,0x2d,0x2d,0x7c,0x7c,0x5c,0x5c,0x2f,0x5c,0x5c,0x2f,0x2f, + 0x5c,0x23,0x5f,0x23,0x7c,0x2f,0x58,0x4f,0x23,0x7c,0x23,0x2b,0x7c,0x7c,0x26,0x5c, + 0x20,0x7c,0x23,0x2d,0x2d,0x7c,0x23,0x7c,0x23,0x2f,0x7c,0x7c,0x2f,0x5c,0x5c,0x2d, + 0x2f,0x2d,0x2d,0x7c,0x7c,0x7c,0x7c,0x2d,0x2d,0x2d,0x2f,0x5c,0x5c,0x2f,0x2f,0x23 +}; + +const char** SidTune::fileNameExtensions = defaultFileNameExt; + +inline void SidTune::setFileNameExtensions(const char **fileNameExt) +{ + fileNameExtensions = ((fileNameExt!=0)?fileNameExt:defaultFileNameExt); +} + +SidTune::SidTune(const char* fileName, const char **fileNameExt, + const bool separatorIsSlash) +{ + init(); + isSlashedFileName = separatorIsSlash; + setFileNameExtensions(fileNameExt); +#if !defined(SIDTUNE_NO_STDIN_LOADER) +/* // Filename ``-'' is used as a synonym for standard input. + if ( fileName!=0 && (strcmp(fileName,"-")==0) ) + { + getFromStdIn(); + } + else*/ +#endif + if (fileName != 0) + { + //getFromFiles(fileName); + } +} + +/*SidTune::SidTune(const uint_least8_t* data, const uint_least32_t dataLen) +{ + init(); + getFromBuffer(data,dataLen); +}*/ + +SidTune::~SidTune() +{ + cleanup(); +} + +#if 0 +bool SidTune::load(const char* fileName, const bool separatorIsSlash) +{ +// cleanup(); + init(); + isSlashedFileName = separatorIsSlash; +#if !defined(SIDTUNE_NO_STDIN_LOADER) +/* if ( strcmp(fileName,"-")==0 ) + getFromStdIn(); + else*/ +#endif + getFromFiles(fileName); + return status; +} +#endif + +bool SidTune::load( unsigned char* sidData, int sidLength, const bool separatorIsSlash) +{ +// cleanup(); + init(); + isSlashedFileName = separatorIsSlash; + getFromRaw(sidData, sidLength); + return status; +} + +/*bool SidTune::read(const uint_least8_t* data, uint_least32_t dataLen) +{ + cleanup(); + init(); + getFromBuffer(data,dataLen); + return status; +}*/ + +const SidTuneInfo& SidTune::operator[](const uint_least16_t songNum) +{ + selectSong(songNum); + return info; +} + +void SidTune::getInfo(SidTuneInfo& outInfo) +{ + outInfo = info; // copy +} + +const SidTuneInfo& SidTune::getInfo() +{ + return info; +} + +// First check, whether a song is valid. Then copy any song-specific +// variable information such a speed/clock setting to the info structure. +uint_least16_t SidTune::selectSong(const uint_least16_t selectedSong) +{ + if ( !status ) + return 0; + else + info.statusString = SidTune::txt_noErrors; + + uint_least16_t song = selectedSong; + // Determine and set starting song number. + if (selectedSong == 0) + song = info.startSong; + if (selectedSong>info.songs || selectedSong>SIDTUNE_MAX_SONGS) + { + song = info.startSong; + info.statusString = SidTune::txt_songNumberExceed; + } + info.currentSong = song; + info.songLength = songLength[song-1]; + // Retrieve song speed definition. + if (info.compatibility == SIDTUNE_COMPATIBILITY_R64) + info.songSpeed = SIDTUNE_SPEED_CIA_1A; + else if (info.compatibility == SIDTUNE_COMPATIBILITY_PSID) + { // This does not take into account the PlaySID bug upon evaluating the + // SPEED field. It would most likely break compatibility to lots of + // sidtunes, which have been converted from .SID format and vice versa. + // The .SID format does the bit-wise/song-wise evaluation of the SPEED + // value correctly, like it is described in the PlaySID documentation. + info.songSpeed = songSpeed[(song-1)&31]; + } + else + info.songSpeed = songSpeed[song-1]; + info.clockSpeed = clockSpeed[song-1]; + // Assign song speed description string depending on clock speed. + // Final speed description is available only after song init. + if (info.songSpeed == SIDTUNE_SPEED_VBI) + info.speedString = txt_VBI; + else + info.speedString = txt_CIA; + return info.currentSong; +} + +void SidTune::fixLoadAddress(bool force, uint_least16_t init, uint_least16_t play) +{ + if (info.fixLoad || force) + { + info.fixLoad = false; + info.loadAddr += 2; + fileOffset += 2; + + if (force) + { + info.initAddr = init; + info.playAddr = play; + } + } +} + +// ------------------------------------------------- private member functions + +bool SidTune::placeSidTuneInC64mem(uint_least8_t* c64buf) +{ + if ( status && c64buf!=0 ) + { + uint_least32_t endPos = info.loadAddr + info.c64dataLen; + if (endPos <= SIDTUNE_MAX_MEMORY) + { + // Copy data from cache to the correct destination. + memcpy(c64buf+info.loadAddr,cache.get()+fileOffset,info.c64dataLen); + info.statusString = SidTune::txt_noErrors; + } + else + { + // Security - cut data which would exceed the end of the C64 + // memory. Memcpy could not detect this. + // + // NOTE: In libsidplay1 the rest gets wrapped to the beginning + // of the C64 memory. It is an undocumented hack most likely not + // used by any sidtune. Here we no longer do it like that, set + // an error message, and hope the modified behaviour will find + // a few badly ripped sids. + memcpy(c64buf+info.loadAddr,cache.get()+fileOffset,info.c64dataLen-(endPos-SIDTUNE_MAX_MEMORY)); + info.statusString = SidTune::txt_dataTooLong; + } + /*if (info.musPlayer) + { + MUS_installPlayer(c64buf); + }*/ + } + return ( status && c64buf!=0 ); +} + +/*bool SidTune::loadFile(const char* fileName, Buffer_sidtt& bufferRef) +{ + Buffer_sidtt fileBuf; + uint_least32_t fileLen = 0; + + // This sucks big time + openmode createAtrr = std::ios::in; +#ifdef HAVE_IOS_NOCREATE + createAtrr |= std::ios::nocreate; +#endif + // Open binary input file stream at end of file. +#if defined(HAVE_IOS_BIN) + createAtrr |= std::ios::bin; +#else + createAtrr |= std::ios::binary; +#endif + + std::fstream myIn(fileName,createAtrr); + // As a replacement for !is_open(), bad() and the NOT-operator don't seem + // to work on all systems. +#if defined(DONT_HAVE_IS_OPEN) + if ( !myIn ) +#else + if ( !myIn.is_open() ) +#endif + { + info.statusString = SidTune::txt_cantOpenFile; + return false; + } + else + { +#if defined(HAVE_SEEKG_OFFSET) + fileLen = (myIn.seekg(0,std::ios::end)).offset(); +#else + myIn.seekg(0,std::ios::end); + fileLen = (uint_least32_t)myIn.tellg(); +#endif +#ifdef HAVE_EXCEPTIONS + if ( !fileBuf.assign(new(std::nothrow) uint_least8_t[fileLen],fileLen) ) +#else + if ( !fileBuf.assign(new uint_least8_t[fileLen],fileLen) ) +#endif + { + info.statusString = SidTune::txt_notEnoughMemory; + return false; + } + myIn.seekg(0,std::ios::beg); + uint_least32_t restFileLen = fileLen; + // 16-bit compatible loading. Is this really necessary? + while ( restFileLen > INT_MAX ) + { + myIn.read((char*)fileBuf.get()+(fileLen-restFileLen),INT_MAX); // !cast! + restFileLen -= INT_MAX; + } + if ( restFileLen > 0 ) + { + myIn.read((char*)fileBuf.get()+(fileLen-restFileLen),restFileLen); // !cast! + } + if ( myIn.bad() ) + { + info.statusString = SidTune::txt_cantLoadFile; + return false; + } + else + { + info.statusString = SidTune::txt_noErrors; + } + } + myIn.close(); + if ( fileLen==0 ) + { + info.statusString = SidTune::txt_empty; + return false; + } + + if ( decompressPP20(fileBuf) < 0 ) + return false; + + bufferRef.assign(fileBuf.xferPtr(),fileBuf.xferLen()); + return true; +}*/ + +void SidTune::deleteFileNameCopies() +{ + // When will it be fully safe to call delete[](0) on every system? + if ( info.dataFileName != 0 ) + delete[] info.dataFileName; + if ( info.infoFileName != 0 ) + delete[] info.infoFileName; + if ( info.path != 0 ) + delete[] info.path; + info.dataFileName = 0; + info.infoFileName = 0; + info.path = 0; +} + +void SidTune::init() +{ + // Initialize the object with some safe defaults. + status = false; + + info.statusString = SidTune::txt_na; + info.path = info.infoFileName = info.dataFileName = 0; + info.dataFileLen = info.c64dataLen = 0; + info.formatString = SidTune::txt_na; + info.speedString = SidTune::txt_na; + info.loadAddr = ( info.initAddr = ( info.playAddr = 0 )); + info.songs = ( info.startSong = ( info.currentSong = 0 )); + info.sidChipBase1 = 0xd400; + info.sidChipBase2 = 0; + info.sidChipBase3 = 0; + info.musPlayer = false; + info.fixLoad = false; + info.songSpeed = SIDTUNE_SPEED_VBI; +#ifdef SIDTUNE_PSID2NG + info.clockSpeed = SIDTUNE_CLOCK_UNKNOWN; + info.sidModel = SIDTUNE_SIDMODEL_UNKNOWN; +#else + info.clockSpeed = SIDTUNE_CLOCK_PAL; + info.sidModel = SIDTUNE_SIDMODEL_6581; +#endif + info.compatibility = SIDTUNE_COMPATIBILITY_C64; + info.songLength = 0; + info.relocStartPage = 0; + info.relocPages = 0; + + for ( uint_least16_t si = 0; si < SIDTUNE_MAX_SONGS; si++ ) + { + songSpeed[si] = info.songSpeed; + clockSpeed[si] = info.clockSpeed; + songLength[si] = 0; + } + + fileOffset = 0; + musDataLen = 0; + + for ( uint_least16_t sNum = 0; sNum < SIDTUNE_MAX_CREDIT_STRINGS; sNum++ ) + { + for ( uint_least16_t sPos = 0; sPos < SIDTUNE_MAX_CREDIT_STRLEN; sPos++ ) + { + infoString[sNum][sPos] = 0; + } + } + info.numberOfInfoStrings = 0; + + // Not used!!! + info.numberOfCommentStrings = 1; +#ifdef HAVE_EXCEPTIONS + info.commentString = new(std::nothrow) char* [info.numberOfCommentStrings]; +#else + info.commentString = new char* [info.numberOfCommentStrings]; +#endif + if (info.commentString != 0) + { + info.commentString[0] = new char[ 40 ]; + strcpy( info.commentString[0], savedString );//SidTuneTools::myStrDup("--- SAVED WITH SIDPLAY ---"); + } + else + info.commentString[0] = 0; +} + +void SidTune::cleanup() +{ + return; + // Remove copy of comment field. + uint_least32_t strNum = 0; + // Check and remove every available line. + while (info.numberOfCommentStrings-- > 0) + { + if (info.commentString[strNum] != 0) + { + delete[] info.commentString[strNum]; + info.commentString[strNum] = 0; + } + strNum++; // next string + }; + delete[] info.commentString; // free the array pointer + + deleteFileNameCopies(); + + status = false; +} + +#if !defined(SIDTUNE_NO_STDIN_LOADER) + +/*void SidTune::getFromStdIn() +{ + // Assume a failure, so we can simply return. + status = false; + // Assume the memory allocation to fail. + info.statusString = SidTune::txt_notEnoughMemory; + uint_least8_t* fileBuf; +#ifdef HAVE_EXCEPTIONS + if ( 0 == (fileBuf = new(std::nothrow) uint_least8_t[SIDTUNE_MAX_FILELEN]) ) +#else + if ( 0 == (fileBuf = new uint_least8_t[SIDTUNE_MAX_FILELEN]) ) +#endif + { + return; + } + // We only read as much as fits in the buffer. + // This way we avoid choking on huge data. + uint_least32_t i = 0; + char datb; + while (std::cin.get(datb) && i SIDTUNE_MAX_FILELEN) + { + info.statusString = SidTune::txt_fileTooLong; + return; + } + + uint_least8_t* tmpBuf; +#ifdef HAVE_EXCEPTIONS + if ( 0 == (tmpBuf = new(std::nothrow) uint_least8_t[bufferLen]) ) +#else + if ( 0 == (tmpBuf = new uint_least8_t[bufferLen]) ) +#endif + { + info.statusString = SidTune::txt_notEnoughMemory; + return; + } + memcpy(tmpBuf,buffer,bufferLen); + + Buffer_sidtt buf1(tmpBuf,bufferLen); + Buffer_sidtt buf2; // empty + + if ( decompressPP20(buf1) < 0 ) + return; + + bool foundFormat = false; + LoadStatus ret; + // Here test for the possible single file formats. -------------- + ret = PSID_fileSupport( buf1 ); + if ( ret != LOAD_NOT_MINE ) + { + if ( ret == LOAD_ERROR ) + return; + foundFormat = true; + } + else + { + ret = MUS_fileSupport(buf1,buf2); + if ( ret != LOAD_NOT_MINE ) + { + if ( ret == LOAD_ERROR ) + return; + foundFormat = MUS_mergeParts(buf1,buf2); + } + else + { + // No further single-file-formats available. + info.statusString = SidTune::txt_unrecognizedFormat; + } + } + + if ( foundFormat ) + { + status = acceptSidTune("-","-",buf1); + } +} +*/ + +bool SidTune::acceptSidTune(const char* dataFileName, const char* infoFileName, + Buffer_sidtt& buf) +{ + // @FIXME@ - MUS + if ( info.numberOfInfoStrings == 3 ) + { // Add (HVSC standard) to missing title, author, release fields + for (int i = 0; i < 3; i++) + { + if (infoString[i][0] == '\0') + { + strcpy (&infoString[i][0], ""); + info.infoString[i] = &infoString[i][0]; + } + } + } + + deleteFileNameCopies(); + // Make a copy of the data file name and path, if available. +/* if ( dataFileName != 0 ) + { + info.path = SidTuneTools::myStrDup(dataFileName); + if (isSlashedFileName) + { + info.dataFileName = SidTuneTools::myStrDup(SidTuneTools::slashedFileNameWithoutPath(info.path)); + *SidTuneTools::slashedFileNameWithoutPath(info.path) = 0; // path only + } + else + { + info.dataFileName = SidTuneTools::myStrDup(SidTuneTools::fileNameWithoutPath(info.path)); + *SidTuneTools::fileNameWithoutPath(info.path) = 0; // path only + } + if ((info.path==0) || (info.dataFileName==0)) + { + info.statusString = SidTune::txt_notEnoughMemory; + return false; + } + } + else*/ + { + // Provide empty strings. + char emptyString[1] = {0}; + info.path = strdup(emptyString);//::myStrDup(""); + info.dataFileName = strdup(emptyString);//SidTuneTools::myStrDup(""); + } + // Make a copy of the info file name, if available. +/* if ( infoFileName != 0 ) + { + char* tmp = SidTuneTools::myStrDup(infoFileName); + if (isSlashedFileName) + info.infoFileName = SidTuneTools::myStrDup(SidTuneTools::slashedFileNameWithoutPath(tmp)); + else + info.infoFileName = SidTuneTools::myStrDup(SidTuneTools::fileNameWithoutPath(tmp)); + if ((tmp==0) || (info.infoFileName==0)) + { + info.statusString = SidTune::txt_notEnoughMemory; + return false; + } + delete[] tmp; + } + else*/ + { + // Provide empty string. + char emptyString[1] = {0}; + info.infoFileName = strdup(emptyString);//SidTuneTools::myStrDup(""); + } + // Fix bad sidtune set up. + if (info.songs > SIDTUNE_MAX_SONGS) + info.songs = SIDTUNE_MAX_SONGS; + else if (info.songs == 0) + info.songs++; + if (info.startSong > info.songs) + info.startSong = 1; + else if (info.startSong == 0) + info.startSong++; + + //if ( info.musPlayer ) + // MUS_setPlayerAddress(); + + info.dataFileLen = buf.len(); + info.c64dataLen = buf.len() - fileOffset; + + // Calculate any remaining addresses and then + // confirm all the file details are correct + if ( resolveAddrs(buf.get() + fileOffset) == false ) + return false; + if ( checkRelocInfo() == false ) + return false; + if ( checkCompatibility() == false ) + return false; + + if (info.dataFileLen >= 2) + { + // We only detect an offset of two. Some position independent + // sidtunes contain a load address of 0xE000, but are loaded + // to 0x0FFE and call player at 0x1000. + info.fixLoad = (endian_little16(buf.get()+fileOffset)==(info.loadAddr+2)); + } + + // Check the size of the data. + if ( info.c64dataLen > SIDTUNE_MAX_MEMORY ) + { + info.statusString = SidTune::txt_dataTooLong; + return false; + } + else if ( info.c64dataLen == 0 ) + { + info.statusString = SidTune::txt_empty; + return false; + } + + cache.assign(buf.xferPtr(),buf.xferLen()); + + info.statusString = SidTune::txt_noErrors; + return true; +} + +bool SidTune::createNewFileName(Buffer_sidtt& destString, + const char* sourceName, + const char* sourceExt) +{ + Buffer_sidtt newBuf; + uint_least32_t newLen = strlen(sourceName)+strlen(sourceExt)+1; + // Get enough memory, so we can appended the extension. +#ifdef HAVE_EXCEPTIONS + newBuf.assign(new(std::nothrow) char[newLen],newLen); +#else + newBuf.assign(new char[newLen],newLen); +#endif + if ( newBuf.isEmpty() ) + { + info.statusString = SidTune::txt_notEnoughMemory; + return (status = false); + } + strcpy(newBuf.get(),sourceName); + strcpy(SidTuneTools::fileExtOfPath(newBuf.get()),sourceExt); + destString.assign(newBuf.xferPtr(),newBuf.xferLen()); + return true; +} + +// Initializing the object based upon what we find in the specified file. + +/* +void SidTune::getFromFiles( const char* fileName ) +{ + // Assume a failure, so we can simply return. + status = false; + + Buffer_sidtt fileBuf2; + Buffer_sidtt fileName2; + + // Try to load the single specified file. The original method didn't + // quite work that well, so instead we now let the support files take + // ownership of a known file and don't assume we should just + // continue searching when an error is found. + unsigned char *b = new unsigned char[65536]; + int s = 0; + FILE *f = fopen( fileName, "rb" ); + fseek( f, 0, SEEK_END ); + s = ftell( f ); + fseek( f, 0, SEEK_SET ); + fread( b, 1, 65536, f ); + fclose( f ); + + Buffer_sidtt fileBuf1( b, s ); + + + //if ( loadFile( fileName, fileBuf1 ) ) + if ( true ) + { + LoadStatus ret; + + // File loaded. Now check if it is in a valid single-file-format. + ret = PSID_fileSupport( b, s ); + if ( ret != LOAD_NOT_MINE ) + { + if ( ret == LOAD_OK ) + status = acceptSidTune( fileName, 0, fileBuf1 ); + else + info.statusString = info.formatString; + return; + } + + // -------------------------------------- Support for multiple-files formats. + else + { + status = false; + info.statusString = SidTune::txt_unrecognizedFormat; + return; + } // end else ( = is no singlefile ) + +// ---------------------------------------------------------- File I/O error. + + } // if loaddatafile + else + { + // returned fileLen was 0 = error. The info.statusString is + // already set then. + return; + } +} +*/ + + +void SidTune::getFromRaw( unsigned char* sidData, int sidLength ) +{ + // Assume a failure, so we can simply return. + status = false; + + Buffer_sidtt fileBuf2; + Buffer_sidtt fileName2; + + // Try to load the single specified file. The original method didn't + // quite work that well, so instead we now let the support files take + // ownership of a known file and don't assume we should just + // continue searching when an error is found. + + uint_least8_t *bufCopy = new uint_least8_t[65536]; + memcpy( bufCopy, sidData, sidLength ); + Buffer_sidtt fileBuf1( (const uint_least8_t*)bufCopy, sidLength ); + + + //if ( loadFile( fileName, fileBuf1 ) ) + if ( true ) + { + LoadStatus ret; + + // File loaded. Now check if it is in a valid single-file-format. + ret = PSID_fileSupport( sidData, sidLength ); + if ( ret != LOAD_NOT_MINE ) + { + if ( ret == LOAD_OK ) + status = acceptSidTune( "dummyfileName", 0, fileBuf1 ); + else + info.statusString = info.formatString; + return; + } + + // -------------------------------------- Support for multiple-files formats. + else + { + status = false; + info.statusString = SidTune::txt_unrecognizedFormat; + return; + } // end else ( = is no singlefile ) + +// ---------------------------------------------------------- File I/O error. + + } // if loaddatafile + else + { + // returned fileLen was 0 = error. The info.statusString is + // already set then. + return; + } + + delete [] bufCopy; +} + + + + + + +void SidTune::convertOldStyleSpeedToTables(uint_least32_t speed, int clock) +{ + // Create the speed/clock setting tables. + // + // This routine implements the PSIDv2NG compliant speed conversion. All tunes + // above 32 use the same song speed as tune 32 + int toDo = ((info.songs <= SIDTUNE_MAX_SONGS) ? info.songs : SIDTUNE_MAX_SONGS); + for (int s = 0; s < toDo; s++) + { + clockSpeed[s] = clock; + if (speed & 1) + songSpeed[s] = SIDTUNE_SPEED_CIA_1A; + else + songSpeed[s] = SIDTUNE_SPEED_VBI; + if (s < 31) + speed >>= 1; + } +} + +// +// File format conversion --------------------------------------------------- +// + +bool SidTune::checkRelocInfo (void) +{ + uint_least8_t startp, endp; + + // Fix relocation information + if (info.relocStartPage == 0xFF) + { + info.relocPages = 0; + return true; + } + else if (info.relocPages == 0) + { + info.relocStartPage = 0; + return true; + } + + // Calculate start/end page + startp = info.relocStartPage; + endp = (startp + info.relocPages - 1) & 0xff; + if (endp < startp) + { + info.statusString = txt_badReloc; + return false; + } + + { // Check against load range + uint_least8_t startlp, endlp; + startlp = (uint_least8_t) (info.loadAddr >> 8); + endlp = startlp; + endlp += (uint_least8_t) ((info.c64dataLen - 1) >> 8); + + if ( ((startp <= startlp) && (endp >= startlp)) || + ((startp <= endlp) && (endp >= endlp)) ) + { + info.statusString = txt_badReloc; + return false; + } + } + + // Check that the relocation information does not use the following + // memory areas: 0x0000-0x03FF, 0xA000-0xBFFF and 0xD000-0xFFFF + if ((startp < 0x04) + || ((0xa0 <= startp) && (startp <= 0xbf)) + || (startp >= 0xd0) + || ((0xa0 <= endp) && (endp <= 0xbf)) + || (endp >= 0xd0)) + { + info.statusString = txt_badReloc; + return false; + } + return true; +} + +bool SidTune::resolveAddrs (const uint_least8_t *c64data) +{ // Originally used as a first attempt at an RSID + // style format. Now reserved for future use + if ( info.playAddr == 0xffff ) + info.playAddr = 0; + + // loadAddr = 0 means, the address is stored in front of the C64 data. + if ( info.loadAddr == 0 ) + { + if ( info.c64dataLen < 2 ) + { + info.statusString = txt_corrupt; + return false; + } + info.loadAddr = endian_16( *(c64data+1), *c64data ); + fileOffset += 2; + info.c64dataLen -= 2; + } + + if ( info.compatibility == SIDTUNE_COMPATIBILITY_BASIC ) + { + if ( info.initAddr != 0 ) + { + info.statusString = txt_badAddr; + return false; + } + } + else if ( info.initAddr == 0 ) + info.initAddr = info.loadAddr; + return true; +} + +bool SidTune::checkCompatibility (void) +{ + switch ( info.compatibility ) + { + case SIDTUNE_COMPATIBILITY_R64: + // Check valid init address + switch (info.initAddr >> 12) + { + case 0x0F: + case 0x0E: + case 0x0D: + case 0x0B: + case 0x0A: + info.statusString = txt_badAddr; + return false; + default: + if ( (info.initAddr < info.loadAddr) || + (info.initAddr > (info.loadAddr + info.c64dataLen - 1)) ) + { + info.statusString = txt_badAddr; + return false; + } + } + // deliberate run on + + case SIDTUNE_COMPATIBILITY_BASIC: + // Check tune is loadable on a real C64 + if ( info.loadAddr < SIDTUNE_R64_MIN_LOAD_ADDR ) + { + info.statusString = txt_badAddr; + return false; + } + break; + } + return true; +} + +// returns 0 for no decompression (buf unchanged), 1 for decompression and -1 for error +int SidTune::decompressPP20(Buffer_sidtt& buf) +{ + // Check for PowerPacker compression: load and decompress, if PP20 file. + PP20 myPP; + if ( myPP.isCompressed(buf.get(),buf.len()) ) + { + uint_least32_t fileLen; + uint_least8_t* destBufRef = 0; + if ( 0 == (fileLen = myPP.decompress(buf.get(),buf.len(), + &destBufRef)) ) + { + info.statusString = myPP.getStatusString(); + return -1; + } + else + { + info.statusString = myPP.getStatusString(); + // Replace compressed buffer with uncompressed buffer. + buf.assign(destBufRef,fileLen); + } + return 1; + } + return 0; +} + +int SidTune::convertPetsciiToAscii(SmartPtr_sidtt& spPet, char* dest) +{ + int count = 0; + char c; + if (dest) + { + do + { + c = _sidtune_CHRtab[*spPet]; // ASCII CHR$ conversion + if ((c>=0x20) && (count<=31)) + dest[count++] = c; // copy to info string + + // If character is 0x9d (left arrow key) then move back. + if ((*spPet==0x9d) && (count>0)) + count--; + spPet++; + } + while ( !((c==0x0D)||(c==0x00)||spPet.fail()) ); + } + else + { // Just find end of string + do + { + c = _sidtune_CHRtab[*spPet]; // ASCII CHR$ conversion + spPet++; + } + while ( !((c==0x0D)||(c==0x00)||spPet.fail()) ); + } + return count; +} diff --git a/PSID/sidtune/SidTuneCfg.h b/PSID/sidtune/SidTuneCfg.h new file mode 100644 index 00000000..c20d7b56 --- /dev/null +++ b/PSID/sidtune/SidTuneCfg.h @@ -0,0 +1,36 @@ +/* SidTuneCfg.h (template) */ + +#ifndef SIDTUNECFG_H +#define SIDTUNECFG_H + +#include "../config.h" + +/* Define to add PSID2NG support */ +#define SIDTUNE_PSID2NG + +/* Minimum load address for real c64 only tunes */ +#define SIDTUNE_R64_MIN_LOAD_ADDR 0x07e8 + + +/* -------------------------------------------------------------------------- + * Don't touch these! + * -------------------------------------------------------------------------- + */ +#undef SIDTUNE_NO_STDIN_LOADER +#undef SIDTUNE_REJECT_UNKNOWN_FIELDS + + +/* Define the file/path separator(s) that your filesystem uses: + SID_FS_IS_COLON_AND_BACKSLASH, SID_FS_IS_COLON_AND_SLASH, + SID_FS_IS_BACKSLASH, SID_FS_IS_COLON, SID_FS_IS_SLASH */ +#if defined(HAVE_MSWINDOWS) || defined(HAVE_MSDOS) || defined(HAVE_OS2) + #define SID_FS_IS_COLON_AND_BACKSLASH_AND_SLASH +#elif defined(HAVE_MACOS) + #define SID_FS_IS_COLON +#elif defined(HAVE_AMIGAOS) + #define SID_FS_IS_COLON_AND_SLASH +#else + #define SID_FS_IS_SLASH +#endif + +#endif /* SIDTUNECFG_H */ diff --git a/PSID/sidtune/SidTuneTools.cpp b/PSID/sidtune/SidTuneTools.cpp new file mode 100644 index 00000000..2584273a --- /dev/null +++ b/PSID/sidtune/SidTuneTools.cpp @@ -0,0 +1,234 @@ +/* + * /home/ms/files/source/libsidtune/RCS/SidTuneTools.cpp,v + * + * Copyright (C) Michael Schwendt + * + + this code has been modified for integration into the Sidekick64 software + (the modifications are mostly removing everything that does not compile with vanilla Circle, + I really feel sorry for having disfigured this code, please refer to the original repository + if you want to get the real and decent psid64 version) + + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "SidTuneTools.h" + +#ifdef HAVE_EXCEPTIONS +# include +#endif +#include +#include + +int strncasecmp2(const char *s1, const char *s2, size_t n) +{ + char temp[ 1024 ] = {0}; + strcpy( temp, s1 ); + temp[ n ] = 0; + return MYSTRICMP( temp, s2 ); +} + +// Own version of strdup, which uses new instead of malloc. +char* SidTuneTools::myStrDup(const char *source) +{ + char *dest; +#ifdef HAVE_EXCEPTIONS + if ( (dest = new(std::nothrow) char[strlen(source)+1]) != 0) +#else + if ( (dest = new char[strlen(source)+1]) != 0) +#endif + { + strcpy(dest,source); + } + return dest; +} + +// Return pointer to file name position in complete path. +char* SidTuneTools::fileNameWithoutPath(char* s) +{ + int last_slash_pos = -1; + for ( uint_least32_t pos = 0; pos < strlen(s); pos++ ) + { +#if defined(SID_FS_IS_COLON_AND_BACKSLASH_AND_SLASH) + if ( s[pos] == ':' || s[pos] == '\\' || + s[pos] == '/' ) +#elif defined(SID_FS_IS_COLON_AND_SLASH) + if ( s[pos] == ':' || s[pos] == '/' ) +#elif defined(SID_FS_IS_SLASH) + if ( s[pos] == '/' ) +#elif defined(SID_FS_IS_BACKSLASH) + if ( s[pos] == '\\' ) +#elif defined(SID_FS_IS_COLON) + if ( s[pos] == ':' ) +#else +#error Missing file/path separator definition. +#endif + { + last_slash_pos = pos; + } + } + return( &s[last_slash_pos +1] ); +} + +// Return pointer to file name position in complete path. +// Special version: file separator = forward slash. +char* SidTuneTools::slashedFileNameWithoutPath(char* s) +{ + int last_slash_pos = -1; + for ( uint_least32_t pos = 0; pos < strlen(s); pos++ ) + { + if ( s[pos] == '/' ) + { + last_slash_pos = pos; + } + } + return( &s[last_slash_pos +1] ); +} + +// Return pointer to file name extension in path. +// The backwards-version. +char* SidTuneTools::fileExtOfPath(char* s) +{ + uint_least32_t last_dot_pos = strlen(s); // assume no dot and append + for ( int pos = last_dot_pos; pos >= 0; --pos ) + { + if ( s[pos] == '.' ) + { + last_dot_pos = pos; + break; + } + } + return( &s[last_dot_pos] ); +} + +// Parse input string stream. Read and convert a hexa-decimal number up +// to a ``,'' or ``:'' or ``\0'' or end of stream. +/*uint_least32_t SidTuneTools::readHex( std::istringstream& hexin ) +{ + uint_least32_t hexLong = 0; + char c; + do + { + hexin >> c; + if ( !hexin ) + break; + if (( c != ',') && ( c != ':' ) && ( c != 0 )) + { + // machine independed to_upper + c &= 0xdf; + ( c < 0x3a ) ? ( c &= 0x0f ) : ( c -= ( 0x41 - 0x0a )); + hexLong <<= 4; + hexLong |= (uint_least32_t)c; + } + else + { + if ( c == 0 ) + hexin.putback(c); + break; + } + } while ( hexin ); + return hexLong; +} +*/ + +// Parse input string stream. Read and convert a decimal number up +// to a ``,'' or ``:'' or ``\0'' or end of stream. +/* +uint_least32_t SidTuneTools::readDec( std::istringstream& decin ) +{ + uint_least32_t hexLong = 0; + char c; + do + { + decin >> c; + if ( !decin ) + break; + if (( c != ',') && ( c != ':' ) && ( c != 0 )) + { + c &= 0x0f; + hexLong *= 10; + hexLong += (uint_least32_t)c; + } + else + { + if ( c == 0 ) + decin.putback(c); + break; + } + } while ( decin ); + return hexLong; +} +*/ + +// Search terminated string for next newline sequence. +// Skip it and return pointer to start of next line. +const char* SidTuneTools::returnNextLine(const char* s, uint_least32_t len) +{ + // Unix: LF = 0x0A + // Windows, DOS: CR,LF = 0x0D,0x0A + // Mac: CR = 0x0D + char c; + while (len && ((c = *s) != 0)) + { + len--; + s++; // skip read character + if (c == 0x0A) + { + break; // LF found + } + else if (c == 0x0D) + { + if (len && (*s == 0x0A)) + { + len--; + s++; // CR,LF found, skip LF + } + break; // CR or CR,LF found + } + } + if ((len == 0) || (*s == 0)) // end of string ? + { + return 0; // no next line available + } + return s; // next line available +} + +// Skip any characters in an input string stream up to '='. +/*void SidTuneTools::skipToEqu( std::istringstream& parseStream ) +{ + char c; + do + { + parseStream >> c; + } + while ( c != '=' ); +}*/ + +void SidTuneTools::copyStringValueToEOL(const char* pSourceStr, char* pDestStr, int DestMaxLen ) +{ + // Start at first character behind '='. + while ( *pSourceStr != '=' ) + { + pSourceStr++; + } + pSourceStr++; // Skip '='. + while (( DestMaxLen > 0 ) && ( *pSourceStr != 0 ) + && ( *pSourceStr != '\n' ) && ( *pSourceStr != '\r' )) + { + *pDestStr++ = *pSourceStr++; + DestMaxLen--; + } + *pDestStr++ = 0; +} diff --git a/PSID/sidtune/SidTuneTools.h b/PSID/sidtune/SidTuneTools.h new file mode 100644 index 00000000..20fb75ab --- /dev/null +++ b/PSID/sidtune/SidTuneTools.h @@ -0,0 +1,98 @@ +/* + * /home/ms/files/source/libsidtune/RCS/SidTuneTools.h,v + * + * Copyright (C) Michael Schwendt + * + this code has been modified for integration into the Sidekick64 software + (the modifications are mostly removing everything that does not compile with vanilla Circle, + I really feel sorry for having disfigured this code, please refer to the original repository + if you want to get the real and decent psid64 version) + + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TOOLS_H +#define TOOLS_H + +#include "SidTuneCfg.h" +#include "../SidPlay/sidtypes.h" + +#include "linux/kernel.h" +#include +#include +#include +#include + +//#if defined(HAVE_SSTREAM) +//# include +//#else +//# include +//# undef istringstream +//# define istringstream istrstream +//#endif + +#if defined(HAVE_STRINGS_H) +# include +#endif + +# define MYSTRICMP strcasecmp +# define MYSTRNICMP strncasecmp2 + +extern int strncasecmp2(const char *s1, const char *s2, size_t n); + +class SidTuneTools +{ + public: + + // Wrapper for ``strnicmp'' without third argument. + static int myStrNcaseCmp(const char* s1, const char* s2) + { + return MYSTRNICMP(s1,s2,strlen(s2)); + } + + // Own version of strdup, which uses new instead of malloc. + static char* myStrDup(const char *source); + + // Return pointer to file name position in complete path. + static char* fileNameWithoutPath(char* s); + + // Return pointer to file name position in complete path. + // Special version: file separator = forward slash. + static char* slashedFileNameWithoutPath(char* s); + + // Return pointer to file name extension in path. + // Searching backwards until first dot is found. + static char* fileExtOfPath(char* s); + + // Parse input string stream. Read and convert a hexa-decimal number up + // to a ``,'' or ``:'' or ``\0'' or end of stream. + //static uint_least32_t readHex(std::istringstream& parseStream); + + // Parse input string stream. Read and convert a decimal number up + // to a ``,'' or ``:'' or ``\0'' or end of stream. + //static uint_least32_t readDec(std::istringstream& parseStream); + + // Search terminated string for next newline sequence. + // Skip it and return pointer to start of next line. + static const char* returnNextLine(const char* s, uint_least32_t len); + + // Skip any characters in an input string stream up to '='. + //static void skipToEqu(std::istringstream& parseStream); + + // Start at first character behind '=' and copy rest of string. + static void copyStringValueToEOL(const char* pSourceStr, char* pDestStr, int destMaxLen); +}; + +#endif /* TOOLS_H */ diff --git a/PSID/sidtune/p00.cpp b/PSID/sidtune/p00.cpp new file mode 100644 index 00000000..54c7bc81 --- /dev/null +++ b/PSID/sidtune/p00.cpp @@ -0,0 +1,142 @@ +/* + * C64 P00 file format support. + * + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "SidTuneCfg.h" +#include "../SidPlay/SidTune.h" +#include "SidTuneTools.h" + +#define X00_ID_LEN 8 +#define X00_NAME_LEN 17 + +// File format from PC64. PC64 automatically generates +// the filename from the cbm name (16 to 8 conversion) +// but we only need to worry about that when writing files +// should we want pc64 compatibility. The extension numbers +// are just an index to try to avoid repeats. Name conversion +// works by creating an initial filename from alphanumeric +// and ' ', '-' characters only with the later two being +// converted to '_'. Then it parses the filename +// from end to start removing characters stopping as soon +// as the filename becomes <= 8. The removal of characters +// occurs in three passes, the first removes all '_', then +// vowels and finally numerics. If the filename is still +// greater than 8 it is trucated. struct X00Header +struct X00Header +{ + char id[X00_ID_LEN]; // C64File + char name[X00_NAME_LEN]; // C64 name + uint8_t length; // Rel files only (Bytes/Record), + // should be 0 for all other types +}; + +typedef enum +{ + X00_UNKNOWN, + X00_DEL, + X00_SEQ, + X00_PRG, + X00_USR, + X00_REL +} X00Format; + +static const char _sidtune_id[] = "C64File"; +static const char _sidtune_format_del[] = "Unsupported tape image file (DEL)"; +static const char _sidtune_format_seq[] = "Unsupported tape image file (SEQ)"; +static const char _sidtune_format_prg[] = "Tape image file (PRG)"; +static const char _sidtune_format_usr[] = "Unsupported USR file (USR)"; +static const char _sidtune_format_rel[] = "Unsupported tape image file (REL)"; +static const char _sidtune_truncated[] = "ERROR: File is most likely truncated"; + + +SidTune::LoadStatus SidTune::X00_fileSupport(const char *fileName, + Buffer_sidtt& dataBuf) +{ + const char *ext = SidTuneTools::fileExtOfPath(const_cast(fileName)); + const char *format = 0; + const X00Header *pHeader = reinterpret_cast(dataBuf.get()); + uint_least32_t bufLen = dataBuf.len (); + + // Combined extension & magic field identification + if (strlen (ext) != 4) + return LOAD_NOT_MINE; + if (!isdigit(ext[2]) || !isdigit(ext[3])) + return LOAD_NOT_MINE; + + X00Format type = X00_UNKNOWN; + switch (toupper(ext[1])) + { + case 'D': + type = X00_DEL; + format = _sidtune_format_del; + break; + case 'S': + type = X00_SEQ; + format = _sidtune_format_seq; + break; + case 'P': + type = X00_PRG; + format = _sidtune_format_prg; + break; + case 'U': + type = X00_USR; + format = _sidtune_format_usr; + break; + case 'R': + type = X00_REL; + format = _sidtune_format_rel; + break; + } + + if (type == X00_UNKNOWN) + return LOAD_NOT_MINE; + + // Verify the file is what we think it is + if (bufLen < X00_ID_LEN) + return LOAD_NOT_MINE; + else if (strcmp (pHeader->id, _sidtune_id)) + return LOAD_NOT_MINE; + + info.formatString = format; + + // File types current supported + if (type != X00_PRG) + return LOAD_ERROR; + + if (bufLen < sizeof(X00Header)+2) + { + info.formatString = _sidtune_truncated; + return LOAD_ERROR; + } + + { // Decode file name + SmartPtr_sidtt spPet((const uint8_t*)pHeader->name,X00_NAME_LEN); + convertPetsciiToAscii(spPet,infoString[0]); + } + + // Automatic settings + fileOffset = sizeof(X00Header); + info.songs = 1; + info.startSong = 1; + info.compatibility = SIDTUNE_COMPATIBILITY_BASIC; + info.numberOfInfoStrings = 1; + info.infoString[0] = infoString[0]; + + // Create the speed/clock setting table. + convertOldStyleSpeedToTables(~0, info.clockSpeed); + return LOAD_OK; +} diff --git a/PSID/sidtune/prg.cpp b/PSID/sidtune/prg.cpp new file mode 100644 index 00000000..dc345348 --- /dev/null +++ b/PSID/sidtune/prg.cpp @@ -0,0 +1,60 @@ +/* + * C64 PRG file format support. + * + + this code has been modified for integration into the Sidekick64 software + (the modifications are mostly removing everything that does not compile with vanilla Circle, + I really feel sorry for having disfigured this code, please refer to the original repository + if you want to get the real and decent psid64 version) + + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "SidTuneCfg.h" +#include "../SidPlay/SidTune.h" +#include "SidTuneTools.h" + +static const char _sidtune_format_prg[] = "Tape image file (PRG)"; +static const char _sidtune_truncated[] = "ERROR: File is most likely truncated"; + + +/*SidTune::LoadStatus SidTune::PRG_fileSupport(const char *fileName, + Buffer_sidtt& dataBuf) +{ + const char *ext = SidTuneTools::fileExtOfPath(const_cast(fileName)); + if ( (MYSTRICMP(ext,".prg")!=0) && + (MYSTRICMP(ext,".c64")!=0) ) + { + return LOAD_NOT_MINE; + } + + info.formatString = _sidtune_format_prg; + if (dataBuf.len() < 2) + { + info.formatString = _sidtune_truncated; + return LOAD_ERROR; + } + + // Automatic settings + info.songs = 1; + info.startSong = 1; + info.compatibility = SIDTUNE_COMPATIBILITY_BASIC; + info.numberOfInfoStrings = 0; + + // Create the speed/clock setting table. + convertOldStyleSpeedToTables(~0, info.clockSpeed); + return LOAD_OK; +} +*/ \ No newline at end of file