diff --git a/engine/source/openborscript/config.h b/engine/source/openborscript/config.h index 32d12c846..77eb75dcf 100644 --- a/engine/source/openborscript/config.h +++ b/engine/source/openborscript/config.h @@ -134,6 +134,10 @@ 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_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 9ccf83147..cd2358079 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,14 @@ 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_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 bddf5fe8f..54181d6a5 100644 --- a/engine/source/openborscript/math.c +++ b/engine/source/openborscript/math.c @@ -470,6 +470,126 @@ 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 +* +* 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 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