Initial commit
This commit is contained in:
411
engines/glk/adrift/scutils.cpp
Normal file
411
engines/glk/adrift/scutils.cpp
Normal file
@@ -0,0 +1,411 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#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::vformat(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 = nullptr;
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user