From 179169967c9e2250751d83c22b33c54684a4ac30 Mon Sep 17 00:00:00 2001 From: DCurrent Date: Tue, 9 Jun 2026 13:46:22 -0400 Subject: [PATCH 1/3] scriptlib: add atan2 for quadrant-aware direction angles Expose atan2(y, x) to script so authors can calculate direction angles from vector components without losing quadrant information. Return the result in decimal degrees to match the existing inverse trig helpers. --- engine/source/openborscript/config.h | 1 + engine/source/openborscript/index.c | 6 ++++++ engine/source/openborscript/math.c | 25 +++++++++++++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/engine/source/openborscript/config.h b/engine/source/openborscript/config.h index 32d12c846..47244fed4 100644 --- a/engine/source/openborscript/config.h +++ b/engine/source/openborscript/config.h @@ -134,6 +134,7 @@ HRESULT math_pow(ScriptVariant **varlist , ScriptVariant **pretvar, int paramCou HRESULT math_asin(ScriptVariant **varlist , ScriptVariant **pretvar, int paramCount); HRESULT math_acos(ScriptVariant **varlist , ScriptVariant **pretvar, int paramCount); HRESULT math_atan(ScriptVariant **varlist , ScriptVariant **pretvar, int paramCount); +HRESULT math_atan2(ScriptVariant **varlist , ScriptVariant **pretvar, int paramCount); HRESULT math_trunc(ScriptVariant **varlist , ScriptVariant **pretvar, int paramCount); HRESULT math_round(ScriptVariant **varlist , ScriptVariant **pretvar, int paramCount); diff --git a/engine/source/openborscript/index.c b/engine/source/openborscript/index.c index 9ccf83147..f59e1e8a4 100644 --- a/engine/source/openborscript/index.c +++ b/engine/source/openborscript/index.c @@ -108,6 +108,10 @@ const char *Script_GetFunctionName(void *functionRef) { return "atan"; } + else if (functionRef == ((void *)math_atan2)) + { + return "atan2"; + } else if (functionRef == ((void *)math_trunc)) { return "trunc"; @@ -1218,6 +1222,8 @@ void Script_LoadSystemFunctions() (void *)math_acos, "acos"); List_InsertAfter(&theFunctionList, (void *)math_atan, "atan"); + List_InsertAfter(&theFunctionList, + (void *)math_atan2, "atan2"); List_InsertAfter(&theFunctionList, (void *)math_trunc, "trunc"); List_InsertAfter(&theFunctionList, diff --git a/engine/source/openborscript/math.c b/engine/source/openborscript/math.c index bddf5fe8f..4952f3afb 100644 --- a/engine/source/openborscript/math.c +++ b/engine/source/openborscript/math.c @@ -470,6 +470,31 @@ HRESULT math_atan(ScriptVariant **varlist , ScriptVariant **pretvar, int paramCo return E_FAIL; } +/* +* Caskey, Damon V. +* 2026-06-08 +* +* Get arctangent of Y/X as a decimal degree. +*/ +HRESULT math_atan2(ScriptVariant **varlist, ScriptVariant **pretvar, int paramCount) +{ + DOUBLE y_temp; + DOUBLE x_temp; + + if (ScriptVariant_DecimalValue(varlist[0], &y_temp) == S_OK && + ScriptVariant_DecimalValue(varlist[1], &x_temp) == S_OK) { + + ScriptVariant_ChangeType(*pretvar, VT_DECIMAL); + + (*pretvar)->dblVal = (DOUBLE)(atan2((double)y_temp, (double)x_temp) * 180.0 / MATH_PI); + + return S_OK; + } + + *pretvar = NULL; + return E_FAIL; +} + /* * Caskey, Damon V. * 2026-06-08 From 4e3c62a032f40862025257741159bf763eca2235 Mon Sep 17 00:00:00 2001 From: DCurrent Date: Tue, 9 Jun 2026 23:06:04 -0400 Subject: [PATCH 2/3] scriptlib: add angle and integer rounding helpers Add angle(y, x) to return a normalized 0-359 direction from vector components, making it easier for scripts to work with gameplay-facing direction values. Add floor() and ceil() for decimal values, returning integer variants with the same 64-bit promotion behavior as other integer-producing math helpers. --- engine/source/openborscript/config.h | 3 + engine/source/openborscript/index.c | 6 ++ engine/source/openborscript/math.c | 95 ++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+) diff --git a/engine/source/openborscript/config.h b/engine/source/openborscript/config.h index 47244fed4..77eb75dcf 100644 --- a/engine/source/openborscript/config.h +++ b/engine/source/openborscript/config.h @@ -135,6 +135,9 @@ HRESULT math_asin(ScriptVariant **varlist , ScriptVariant **pretvar, int paramCo HRESULT math_acos(ScriptVariant **varlist , ScriptVariant **pretvar, int paramCount); HRESULT math_atan(ScriptVariant **varlist , ScriptVariant **pretvar, int paramCount); HRESULT math_atan2(ScriptVariant **varlist , ScriptVariant **pretvar, int paramCount); +HRESULT math_angle(ScriptVariant **varlist , ScriptVariant **pretvar, int paramCount); +HRESULT math_ceil(ScriptVariant **varlist , ScriptVariant **pretvar, int paramCount); +HRESULT math_floor(ScriptVariant **varlist , ScriptVariant **pretvar, int paramCount); HRESULT math_trunc(ScriptVariant **varlist , ScriptVariant **pretvar, int paramCount); HRESULT math_round(ScriptVariant **varlist , ScriptVariant **pretvar, int paramCount); diff --git a/engine/source/openborscript/index.c b/engine/source/openborscript/index.c index f59e1e8a4..cd2358079 100644 --- a/engine/source/openborscript/index.c +++ b/engine/source/openborscript/index.c @@ -1224,6 +1224,12 @@ void Script_LoadSystemFunctions() (void *)math_atan, "atan"); List_InsertAfter(&theFunctionList, (void *)math_atan2, "atan2"); + List_InsertAfter(&theFunctionList, + (void *)math_angle, "angle"); + List_InsertAfter(&theFunctionList, + (void *)math_ceil, "ceil"); + List_InsertAfter(&theFunctionList, + (void *)math_floor, "floor"); List_InsertAfter(&theFunctionList, (void *)math_trunc, "trunc"); List_InsertAfter(&theFunctionList, diff --git a/engine/source/openborscript/math.c b/engine/source/openborscript/math.c index 4952f3afb..54181d6a5 100644 --- a/engine/source/openborscript/math.c +++ b/engine/source/openborscript/math.c @@ -495,6 +495,101 @@ HRESULT math_atan2(ScriptVariant **varlist, ScriptVariant **pretvar, int paramCo return E_FAIL; } +/* +* Caskey, Damon V. +* 2026-06-08 +* +* Get direction angle of Y/X as a decimal degree +* normalized to [0, 360). +*/ +HRESULT math_angle(ScriptVariant **varlist, ScriptVariant **pretvar, int paramCount) +{ + DOUBLE y; + DOUBLE x; + DOUBLE degrees; + + if (ScriptVariant_DecimalValue(varlist[0], &y) == S_OK && + ScriptVariant_DecimalValue(varlist[1], &x) == S_OK) + { + degrees = (DOUBLE)(atan2((double)y, (double)x) * 180.0 / MATH_PI); + + if (degrees < 0.0) { + degrees += 360.0; + } + + ScriptVariant_ChangeType(*pretvar, VT_DECIMAL); + (*pretvar)->dblVal = degrees; + return S_OK; + } + + *pretvar = NULL; + return E_FAIL; +} + +/* +* Caskey, Damon V. +* 2026-06-08 +* +* Get floor value of a variant as an integer. +* Preserves integer carriers and promotes decimal +* results to 64-bit integer carriers when needed. +*/ +HRESULT math_floor(ScriptVariant **varlist, ScriptVariant **pretvar, int paramCount) +{ + DOUBLE dbltemp; + + switch (varlist[0]->vt) { + case VT_INTEGER: + case VT_INTEGER64: + case VT_UINTEGER64: + ScriptVariant_Copy(*pretvar, varlist[0]); + return S_OK; + + default: + break; + } + + if (ScriptVariant_DecimalValue(varlist[0], &dbltemp) == S_OK && + math_set_truncated_integer(*pretvar, (DOUBLE)floor((double)dbltemp)) == S_OK) { + return S_OK; + } + + *pretvar = NULL; + return E_FAIL; +} + +/* +* Caskey, Damon V. +* 2026-06-08 +* +* Get ceiling value of a variant as an integer. +* Preserves integer carriers and promotes decimal +* results to 64-bit integer carriers when needed. +*/ +HRESULT math_ceil(ScriptVariant **varlist, ScriptVariant **pretvar, int paramCount) +{ + DOUBLE dbltemp; + + switch (varlist[0]->vt) { + case VT_INTEGER: + case VT_INTEGER64: + case VT_UINTEGER64: + ScriptVariant_Copy(*pretvar, varlist[0]); + return S_OK; + + default: + break; + } + + if (ScriptVariant_DecimalValue(varlist[0], &dbltemp) == S_OK && + math_set_truncated_integer(*pretvar, (DOUBLE)ceil((double)dbltemp)) == S_OK) { + return S_OK; + } + + *pretvar = NULL; + return E_FAIL; +} + /* * Caskey, Damon V. * 2026-06-08 From 8ace18eb9952eee8e46e81406ce63524266ccaec Mon Sep 17 00:00:00 2001 From: DCurrent Date: Wed, 10 Jun 2026 00:39:26 -0400 Subject: [PATCH 3/3] Fix OpenWav2Bor WAV parsing and buffer safety Allocate enough space for WAV sample padding, replace unsafe header string copies, validate WAV chunk sizes and alignment, and harden cleanup paths for malformed input. --- tools/openwav2bor/source/wav2bor.c | 244 ++++++++++++++++++++++------- 1 file changed, 190 insertions(+), 54 deletions(-) diff --git a/tools/openwav2bor/source/wav2bor.c b/tools/openwav2bor/source/wav2bor.c index 59ff517e8..613612070 100644 --- a/tools/openwav2bor/source/wav2bor.c +++ b/tools/openwav2bor/source/wav2bor.c @@ -1,5 +1,5 @@ /* - * OpenWav2Bor 1.1 - https://www.chronocrash.com + * OpenWav2Bor 1.2 - https://www.chronocrash.com * ----------------------------------------------------------------------- * Licensed under a BSD-style license, see LICENSE file for details. * @@ -10,6 +10,9 @@ * OpenBOR. It is a replacement for the closed-source Wav2Bor, which was * originally written by Roel van Mastbergen of Senile Team, and updated to v1.1 * with stereo support by SX. + * + * Updated to v1.2 by Damon V. Caskey (Caskey, Damon V.) to add more robust error + * handling, and shore up overflow vulnerabilities. */ #include @@ -18,6 +21,7 @@ #include #include #include +#include #include "adpcm.h" #include "common.h" @@ -48,7 +52,7 @@ bool loadwave(char *filename, samplestruct *buf) uint16_t samplebits; // 8, 12, 16 } fmt; - FILE *fd; + FILE *fd = NULL; fd = fopen(filename, "rb"); if(fd == NULL) ioerror(strerror(errno)); @@ -63,14 +67,33 @@ bool loadwave(char *filename, samplestruct *buf) rifftag.tag = 0; // Search for format tag - while(rifftag.tag!=HEX_fmt) - { - if(fread(&rifftag, sizeof(rifftag), 1, fd)!=1) ioerror(strerror(errno)); + while(rifftag.tag!=HEX_fmt) { + + if(fread(&rifftag, sizeof(rifftag), 1, fd)!=1) { + ioerror(strerror(errno)); + } + rifftag.tag = SwapLSB32(rifftag.tag); rifftag.size = SwapLSB32(rifftag.size); - if(rifftag.tag!=HEX_fmt) fseek(fd,rifftag.size,SEEK_CUR); + + if(rifftag.tag!=HEX_fmt) { + if(rifftag.size > LONG_MAX - 1) { + ioerror("invalid WAV chunk size"); + } + + if(fseek(fd, (long)(rifftag.size + (rifftag.size & 1U)), SEEK_CUR) != 0) { + ioerror("invalid WAV chunk size"); + } + } + } + + if(rifftag.size < sizeof(fmt)) { + ioerror("invalid WAV format chunk"); + } + + if(fread(&fmt, sizeof(fmt), 1, fd)!=1){ + ioerror(strerror(errno)); } - if(fread(&fmt, sizeof(fmt), 1, fd)!=1) ioerror(strerror(errno)); fmt.format = SwapLSB16(fmt.format); fmt.channels = SwapLSB16(fmt.channels); @@ -79,33 +102,87 @@ bool loadwave(char *filename, samplestruct *buf) fmt.samplerate = SwapLSB32(fmt.samplerate); fmt.bps = SwapLSB32(fmt.bps); - if(rifftag.size>sizeof(fmt)) fseek(fd,rifftag.size-sizeof(fmt),SEEK_CUR); + if(rifftag.size > sizeof(fmt)) { + uint32_t extra = rifftag.size - (uint32_t)sizeof(fmt); - if(fmt.format!=FMT_PCM || (fmt.channels!=1 && fmt.channels!=2) || fmt.samplebits!=16) + if(extra > LONG_MAX - (rifftag.size & 1U)) { + ioerror("invalid WAV format chunk size"); + } + + if(fseek(fd, (long)(extra + (rifftag.size & 1U)), SEEK_CUR) != 0) { + ioerror("invalid WAV format chunk size"); + } + } + else if(rifftag.size & 1U) { + if(fseek(fd, 1, SEEK_CUR) != 0) { + ioerror("invalid WAV format chunk padding"); + } + } + + if(fmt.format!=FMT_PCM || (fmt.channels!=1 && fmt.channels!=2) || fmt.samplebits!=16) { ioerror("only 16-bit mono or stereo PCM WAV files are supported"); + } + + + if(fmt.blockalign != fmt.channels * (fmt.samplebits / 8)) { + ioerror("invalid WAV block alignment"); + } + + if(fmt.samplerate == 0) { + ioerror("invalid WAV sample rate"); + } + + if(fmt.bps != fmt.samplerate * fmt.blockalign) { + ioerror("invalid WAV byte rate"); + } // Search for data tag while(rifftag.tag!=HEX_data) { - if(fread(&rifftag, sizeof(rifftag), 1, fd)!=1) ioerror(strerror(errno)); + if(fread(&rifftag, sizeof(rifftag), 1, fd)!=1) { + ioerror(strerror(errno)); + } + rifftag.tag = SwapLSB32(rifftag.tag); rifftag.size = SwapLSB32(rifftag.size); - if(rifftag.tag!=HEX_data) fseek(fd,rifftag.size,SEEK_CUR); + + if(rifftag.tag!=HEX_data) { + if(rifftag.size > LONG_MAX - 1) { + ioerror("invalid WAV chunk size"); + } + + if(fseek(fd, (long)(rifftag.size + (rifftag.size & 1U)), SEEK_CUR) != 0) { + ioerror("invalid WAV chunk size"); + } + } } - buf->sampleptr = malloc(rifftag.size); - - if(fmt.samplebits==8) memset(buf->sampleptr, 0x80, rifftag.size+8); - else memset(buf->sampleptr, 0, rifftag.size+8); + if (rifftag.size == 0) { + ioerror("empty WAV data chunk"); + } - if(fread(buf->sampleptr, rifftag.size, 1, fd) != 1) - { - if(buf->sampleptr != NULL) - { - free(buf->sampleptr); - buf->sampleptr = NULL; - } - ioerror(strerror(errno)); + if (rifftag.size % fmt.blockalign != 0) { + ioerror("malformed WAV data alignment"); + } + + if (rifftag.size % 4 != 0) { + ioerror("WAV data size must be divisible by 4"); + } + + if (rifftag.size > SIZE_MAX - 8) { + ioerror("WAV data chunk too large"); + } + + buf->sampleptr = malloc((size_t)rifftag.size + 8); + + if (buf->sampleptr == NULL) { + ioerror("out of memory"); + } + + memset(buf->sampleptr, 0, (size_t)rifftag.size + 8); + + if (fread(buf->sampleptr, 1, rifftag.size, fd) != rifftag.size) { + ioerror("can't read WAV data chunk"); } buf->soundlen = rifftag.size; @@ -143,17 +220,45 @@ bool writebor(char *filename, samplestruct *buf, char *artist, char *title) int32_t datastart; } borheader; - int i=0, len = buf->soundlen; + FILE *fd = NULL; + size_t i = 0; + size_t len = buf->soundlen; + size_t encoded_size; short *pcm = (short*)buf->sampleptr; - char *adpcmbuf = malloc(len/4), *adpcm = adpcmbuf; - FILE *fd = fopen(filename, "wb"); - - if(fd == NULL) ioerror(strerror(errno)); + char *adpcmbuf = NULL; + char *adpcm = NULL; + + if (buf->sampleptr == NULL) { + ioerror("no sample data"); + } + + if (buf->soundlen > SIZE_MAX - 3){ + ioerror("sample data too large"); + } + + encoded_size = ((size_t)buf->soundlen + 3) / 4; + adpcmbuf = malloc(encoded_size); + + if (adpcmbuf == NULL) { + ioerror("out of memory"); + } + + adpcm = adpcmbuf; + + fd = fopen(filename, "wb"); + if (fd == NULL) { + ioerror(strerror(errno)); + } // write header - strcpy(borheader.identifier, BOR_IDENTIFIER); - strcpy(borheader.artist, artist); - strcpy(borheader.title, title); + memset(&borheader, 0, sizeof(borheader)); + + if(snprintf(borheader.identifier, sizeof(borheader.identifier), "%s", BOR_IDENTIFIER) >= (int)sizeof(borheader.identifier)) { + ioerror("BOR identifier too long"); + } + + snprintf(borheader.artist, sizeof(borheader.artist), "%s", artist ? artist : ""); + snprintf(borheader.title, sizeof(borheader.title), "%s", title ? title : ""); borheader.version = SwapLSB32(NEW_BOR_VERSION); borheader.frequency = SwapLSB32(buf->frequency); borheader.channels = SwapLSB32(buf->channels); @@ -161,34 +266,57 @@ bool writebor(char *filename, samplestruct *buf, char *artist, char *title) if(fwrite(&borheader, sizeof(borheader), 1, fd) != 1) ioerror(strerror(errno)); i += sizeof(borheader); - printf("\rConverting... %09d", i); + printf("\rConverting... %09zu", i); // encode raw PCM from the WAV as IMA ADPCM while(len>2048) { - if(adpcm_encode(pcm, adpcm, 2048, buf->channels) != 512) ioerror("can't encode data as ADPCM"); + if(adpcm_encode(pcm, adpcm, 2048, buf->channels) != 512){ + ioerror("can't encode data as ADPCM"); + } + pcm += 1024; - if(fwrite(adpcm, 1, 512, fd) != 512) ioerror(strerror(errno)); + + if(fwrite(adpcm, 1, 512, fd) != 512){ + ioerror(strerror(errno)); + } + adpcm += 512; i += 512; - printf("\rConverting... %09d", i); + printf("\rConverting... %09zu", i); len -= 2048; } - if(adpcm_encode(pcm, adpcm, len, buf->channels) != (len/4)) ioerror("can't encode data as ADPCM"); + if(adpcm_encode(pcm, adpcm, len, buf->channels) != (len/4)) { + ioerror("can't encode data as ADPCM"); + } + i += len/4; - if(fwrite(adpcm, 1, len/4, fd) != (len/4)) ioerror(strerror(errno)); - printf("\rConverting... %09d\n", i); - fclose(fd); - free(buf->sampleptr); - free(adpcmbuf); + if(fwrite(adpcm, 1, len/4, fd) != (len/4)) { + ioerror(strerror(errno)); + } + + printf("\rConverting... %09zu\n", i); + + if(fclose(fd) != 0) { + fd = NULL; + ioerror(strerror(errno)); + } + fd = NULL; + + if(buf->sampleptr) { free(buf->sampleptr); buf->sampleptr = NULL; } + if(adpcmbuf) { free(adpcmbuf); adpcmbuf = NULL; } return true; error: - if(fd) fclose(fd); - free(buf->sampleptr); - free(adpcmbuf); + if(fd){ + fclose(fd); + } + fd = NULL; + + if(buf->sampleptr) { free(buf->sampleptr); buf->sampleptr = NULL; } + if(adpcmbuf) { free(adpcmbuf); adpcmbuf = NULL; } return false; } @@ -198,26 +326,34 @@ bool writebor(char *filename, samplestruct *buf, char *artist, char *title) * @param name the string * @return the modified string */ -char * fixname(char *name) +char *fixname(char *name) { - int i; + size_t i; + + if (name == NULL) { + return NULL; + } + + for (i = 0; name[i] != '\0' && i < 63; i++) { + if (name[i] == '_'){ + name[i] = ' '; + } + } + + if (name[i] != '\0') { + name[i] = '\0'; + } - for(i=0; i 6) + if(argc < 3 || argc > 5) { printf("OpenWav2Bor 1.1, compile date " __DATE__ "\n"); printf("Written by Plombo\n\n"); @@ -228,7 +364,7 @@ int main(int argc, char *argv[]) printf("Replace spaces in the artist name and title with underscores (_).\n\n"); printf("Example:\n" "%s cooltune.wav cooltune.bor John_Smith Cool_Tune\n\n", argv[0]); - printf("Contact and support at LavaLit: www.LavaLit.com\n"); + printf("Contact and support at ChronoCrash: www.ChronoCrash.com\n"); success = false; } else