From 60c860c6a6c37b95ab8265d658cbcc144d51153b Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Mon, 2 Sep 2019 20:57:19 -0700 Subject: GLK: ADRIFT: Skeleton sub-engine commit --- engines/glk/adrift/scutils.cpp | 462 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 462 insertions(+) create mode 100644 engines/glk/adrift/scutils.cpp (limited to 'engines/glk/adrift/scutils.cpp') diff --git a/engines/glk/adrift/scutils.cpp b/engines/glk/adrift/scutils.cpp new file mode 100644 index 0000000000..98e10651e7 --- /dev/null +++ b/engines/glk/adrift/scutils.cpp @@ -0,0 +1,462 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "glk/adrift/scare.h" +#include "glk/adrift/scprotos.h" +#include "glk/glk.h" +#include "glk/events.h" +#include "common/debug.h" +#include "common/str.h" +#include "common/textconsole.h" + +namespace Glk { +namespace Adrift { + +/* + * Module notes: + * + * o Implement smarter selective module tracing. + */ + +/* + * sc_trace() + * + * Debugging trace function; printf wrapper that writes to stderr. + */ +void sc_trace (const sc_char *format, ...) { + va_list ap; + assert(format); + + va_start (ap, format); + Common::String s = Common::String::format(format, ap); + va_end (ap); + debug("%s", s.c_str()); +} + + +/* + * sc_error() + * sc_fatal() + * + * Error reporting functions. sc_error() prints a message and continues. + * sc_fatal() prints a message, then calls abort(). + */ +void sc_error (const sc_char *format, ...) { + va_list ap; + assert(format); + + va_start(ap, format); + Common::String s = Common::String::format(format, ap); + va_end(ap); + warning("%s", s.c_str()); +} + +void sc_fatal (const sc_char *format, ...) { + va_list ap; + assert(format); + + va_start(ap, format); + Common::String s = Common::String::format(format, ap); + va_end(ap); + error("%s", s.c_str()); +} + + +/* Unique non-heap address for zero size malloc() and realloc() requests. */ +static void *sc_zero_allocation = &sc_zero_allocation; + +/* + * sc_malloc() + * sc_realloc() + * sc_free() + * + * Non-failing wrappers around malloc functions. Newly allocated memory is + * cleared to zero. In ANSI/ISO C, zero byte allocations are implementation- + * defined, so we have to take special care to get predictable behavior. + */ +void * +sc_malloc (size_t size) +{ + void *allocated; + + if (size == 0) + return sc_zero_allocation; + + allocated = malloc (size); + if (!allocated) + sc_fatal ("sc_malloc: requested %lu bytes\n", (sc_uint) size); + else if (allocated == sc_zero_allocation) + sc_fatal ("sc_malloc: zero-byte allocation address returned\n"); + + memset (allocated, 0, size); + return allocated; +} + +void * +sc_realloc (void *pointer, size_t size) +{ + void *allocated; + + if (size == 0) + { + sc_free (pointer); + return sc_zero_allocation; + } + + if (pointer == sc_zero_allocation) + pointer = NULL; + + allocated = realloc (pointer, size); + if (!allocated) + sc_fatal ("sc_realloc: requested %lu bytes\n", (sc_uint) size); + else if (allocated == sc_zero_allocation) + sc_fatal ("sc_realloc: zero-byte allocation address returned\n"); + + if (!pointer) + memset (allocated, 0, size); + return allocated; +} + +void +sc_free (void *pointer) +{ + if (sc_zero_allocation != &sc_zero_allocation) + sc_fatal ("sc_free: write to zero-byte allocation address detected\n"); + + if (pointer && pointer != sc_zero_allocation) + free (pointer); +} + + +/* + * sc_strncasecmp() + * sc_strcasecmp() + * + * Strncasecmp and strcasecmp are not ANSI functions, so here are local + * definitions to do the same jobs. + */ +sc_int +sc_strncasecmp (const sc_char *s1, const sc_char *s2, sc_int n) +{ + sc_int index_; + assert (s1 && s2); + + for (index_ = 0; index_ < n; index_++) + { + sc_int diff; + + diff = sc_tolower (s1[index_]) - sc_tolower (s2[index_]); + if (diff < 0 || diff > 0) + return diff < 0 ? -1 : 1; + } + + return 0; +} + +sc_int +sc_strcasecmp (const sc_char *s1, const sc_char *s2) +{ + sc_int s1len, s2len, result; + assert (s1 && s2); + + s1len = strlen (s1); + s2len = strlen (s2); + + result = sc_strncasecmp (s1, s2, s1len < s2len ? s1len : s2len); + if (result < 0 || result > 0) + return result; + else + return s1len < s2len ? -1 : s1len > s2len ? 1 : 0; +} + + +/* + * sc_platform_rand() + * sc_congruential_rand() + * sc_set_random_handler() + * + * Internal random number generation functions. We offer two: one is a self- + * seeding wrapper around the platform's rand(), which should generate good + * random numbers but with a sequence that is platform-dependent; the other + * is a linear congruential generator with a long period that is guaranteed + * to return the same sequence for all platforms. The default is the first, + * with the latter intended for predictability of game actions. + */ +static sc_int +sc_platform_rand (sc_uint new_seed) +{ + static sc_bool is_seeded = FALSE; + + /* If reseeding, seed with the value supplied, note seeded, and return 0. */ + if (new_seed > 0) { + g_vm->setRandomNumberSeed(new_seed); + is_seeded = TRUE; + return 0; + } + else + { + /* If not explicitly seeded yet, generate a seed from time(). */ + if (!is_seeded) + { + //srand ((sc_uint) time (NULL)); + is_seeded = TRUE; + } + + /* Return the next rand() number in the sequence. */ + return g_vm->getRandomNumber(0xffffff); + } +} + +static sc_int +sc_congruential_rand (sc_uint new_seed) +{ + static sc_bool is_seeded = FALSE; + static sc_uint rand_state = 1; + + /* If reseeding, seed with the value supplied, and note seeded. */ + if (new_seed > 0) + { + rand_state = new_seed; + is_seeded = TRUE; + return 0; + } + else + { + /* If not explicitly seeded yet, generate a seed from time(). */ + if (!is_seeded) + { + rand_state = (sc_uint)g_vm->_events->getTotalPlayTicks(); + is_seeded = TRUE; + } + + /* + * Advance random state, using constants from Park & Miller (1988). + * To keep the values the same for both 32 and 64 bit longs, mask out + * any bits above the bottom 32. + */ + rand_state = (rand_state * 16807 + 2147483647) & 0xffffffff; + + /* + * Discard the lowest bit as a way to map 32-bits unsigned to a 32-bit + * positive signed. + */ + return rand_state >> 1; + } +} + + +/* Function pointer for the actual random number generator in use. */ +static sc_int (*sc_rand_function) (sc_uint) = sc_platform_rand; + +/* + * sc_set_congruential_random() + * sc_set_platform_random() + * sc_is_congruential_random() + * sc_seed_random() + * sc_rand() + * sc_randomint() + * + * Public interface to random functions; control and reseed the random + * handler in use, generate a random number, and a convenience function to + * generate a random value within a given range. + */ +void +sc_set_congruential_random (void) +{ + sc_rand_function = sc_congruential_rand; +} + +void +sc_set_platform_random (void) +{ + sc_rand_function = sc_platform_rand; +} + +sc_bool +sc_is_congruential_random (void) +{ + return sc_rand_function == sc_congruential_rand; +} + +void +sc_seed_random (sc_uint new_seed) +{ + /* Ignore zero values of new_seed by simply using 1 instead. */ + sc_rand_function (new_seed > 0 ? new_seed : 1); +} + +sc_int +sc_rand (void) +{ + sc_int retval; + + /* Passing zero indicates this is not a seed operation. */ + retval = sc_rand_function (0); + return retval; +} + +sc_int +sc_randomint (sc_int low, sc_int high) +{ + /* + * If the range is invalid, just return the low value given. This mimics + * Adrift under the same conditions, and also guards against division by + * zero in the mod operation. + */ + return (high < low) ? low : low + sc_rand () % (high - low + 1); +} + + +/* Miscellaneous general ascii constants. */ +static const sc_char NUL = '\0'; +static const sc_char SPACE = ' '; + +/* + * sc_strempty() + * + * Return TRUE if a string is either zero-length or contains only whitespace. + */ +sc_bool +sc_strempty (const sc_char *string) +{ + sc_int index_; + assert (string); + + /* Scan for any non-space character. */ + for (index_ = 0; string[index_] != NUL; index_++) + { + if (!sc_isspace (string[index_])) + return FALSE; + } + + /* None found, so string is empty. */ + return TRUE; +} + + +/* + * sc_trim_string() + * + * Trim leading and trailing whitespace from a string. Modifies the string + * in place, and returns the string address for convenience. + */ +sc_char * +sc_trim_string (sc_char *string) +{ + sc_int index_; + assert (string); + + for (index_ = strlen (string) - 1; + index_ >= 0 && sc_isspace (string[index_]); index_--) + string[index_] = NUL; + + for (index_ = 0; sc_isspace (string[index_]);) + index_++; + memmove (string, string + index_, strlen (string) - index_ + 1); + + return string; +} + + +/* + * sc_normalize_string() + * + * Trim a string, and set all runs of whitespace to a single space character. + * Modifies the string in place, and returns the string address for + * convenience. + */ +sc_char * +sc_normalize_string (sc_char *string) +{ + sc_int index_; + assert (string); + + /* Trim all leading and trailing spaces. */ + string = sc_trim_string (string); + + /* Compress multiple whitespace runs into a single space character. */ + for (index_ = 0; string[index_] != NUL; index_++) + { + if (sc_isspace (string[index_])) + { + sc_int cursor; + + string[index_] = SPACE; + for (cursor = index_ + 1; sc_isspace (string[cursor]);) + cursor++; + memmove (string + index_ + 1, + string + cursor, strlen (string + cursor) + 1); + } + } + + return string; +} + + +/* + * sc_compare_word() + * + * Return TRUE if the first word in the string is word, case insensitive. + */ +sc_bool +sc_compare_word (const sc_char *string, const sc_char *word, sc_int length) +{ + assert (string && word); + + /* Return TRUE if string starts with word, then space or string end. */ + return sc_strncasecmp (string, word, length) == 0 + && (string[length] == NUL || sc_isspace (string[length])); +} + + +/* + * sc_hash() + * + * Hash a string, hashpjw algorithm, from 'Compilers, principles, techniques, + * and tools', page 436, unmodulo'ed and somewhat restyled. + */ +sc_uint +sc_hash (const sc_char *string) +{ + sc_int index_; + sc_uint hash; + assert (string); + + hash = 0; + for (index_ = 0; string[index_] != NUL; index_++) + { + sc_uint temp; + + hash = (hash << 4) + string[index_]; + temp = hash & 0xf0000000; + if (temp != 0) + { + hash = hash ^ (temp >> 24); + hash = hash ^ temp; + } + } + + return hash; +} + +} // End of namespace Adrift +} // End of namespace Glk -- cgit v1.2.3