diff --git a/.github/workflows/engine-build.yml b/.github/workflows/engine-build.yml index 37713eed7..fd00186f0 100644 --- a/.github/workflows/engine-build.yml +++ b/.github/workflows/engine-build.yml @@ -82,6 +82,7 @@ jobs: cp thirdparty/bgfx.cmake/bgfx/3rdparty/directx-headers/LICENSE artifact/OpenTS_THIRD_PARTY_LICENSES/directx-headers.txt cp thirdparty/bgfx.cmake/bx/include/tinystl/LICENSE artifact/OpenTS_THIRD_PARTY_LICENSES/tinystl.txt cp thirdparty/bgfx.cmake/bimg/3rdparty/astc-encoder/LICENSE.txt artifact/OpenTS_THIRD_PARTY_LICENSES/astc-encoder.txt + cp thirdparty/libbase64/LICENSE artifact/OpenTS_THIRD_PARTY_LICENSES/libbase64.txt cp thirdparty/licenses/khronos-opengl.txt artifact/OpenTS_THIRD_PARTY_LICENSES/khronos-opengl.txt cat thirdparty/licenses/khronos-vulkan-notice.txt \ thirdparty/bgfx.cmake/bimg/3rdparty/astc-encoder/LICENSE.txt \ diff --git a/.gitmodules b/.gitmodules index df46107fd..fb83783c8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "thirdparty/bgfx.cmake"] path = thirdparty/bgfx.cmake url = https://github.com/bkaradzic/bgfx.cmake.git +[submodule "thirdparty/libbase64"] + path = thirdparty/libbase64 + url = https://github.com/aklomp/base64.git diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 6cb4b838c..05f96cf2e 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -11,6 +11,7 @@ remains under its own license and copyright notices. | [DirectX-Headers](https://github.com/microsoft/DirectX-Headers) | Direct3D API headers used by bgfx | MIT | | [tinystl](https://github.com/mendsley/tinystl) | Containers used internally by bgfx | BSD 2-Clause | | [astc-encoder](https://github.com/ARM-software/astc-encoder) | ASTC texture processing used by bimg | Apache-2.0 | +| [libbase64](https://github.com/aklomp/base64) | Base64 encoding for INI data | BSD 2-Clause | | [OpenGL Registry](https://github.com/KhronosGroup/OpenGL-Registry) | OpenGL API headers used by bgfx | MIT | | [Vulkan Headers](https://github.com/KhronosGroup/Vulkan-Headers) | Vulkan API headers used by bgfx | Apache-2.0 | diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 94876c529..8afd798a7 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -213,6 +213,7 @@ message(STATUS "${PROJECT_NAME}: Applying linker options...") add_dependencies(OpenTS bgfx bx bimg) target_link_libraries(OpenTS PRIVATE VQALib + base64 $ $ diff --git a/code/b64pipe.cpp b/code/b64pipe.cpp deleted file mode 100644 index 234e7ceab..000000000 --- a/code/b64pipe.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/******************************************************************************* - * O P E N T S - ******************************************************************************* - * SPDX-License-Identifier: GPL-3.0-or-later - * Copyright 2025 Electronic Arts Inc. - * Copyright 2026 OpenTS contributors - * - * Contains material derived from Electronic Arts source code. - * Modified by OpenTS contributors, 2026. - * EA's GPLv3 Section 7 additional terms and supplemental warranty - * disclaimers apply; see LICENSE.md. - ******************************************************************************/ - -/*********************************************************************************************** - *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** - *********************************************************************************************** - * * - * Project Name : Command & Conquer * - * * - * $Archive:: /Commando/Library/B64PIPE.CPP $* - * * - * $Author:: Greg_h $* - * * - * $Modtime:: 7/22/97 11:37a $* - * * - * $Revision:: 1 $* - * * - *---------------------------------------------------------------------------------------------* - * Functions: * - * Base64Pipe::Flush -- Flushes the final pending data through the pipe. * - * Base64Pipe::Put -- Processes a block of data through the pipe. * - * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - -#include "always.h" - -#include "b64pipe.h" - -#include "base64.h" - -#include - - -/*********************************************************************************************** - * Base64Pipe::Put -- Processes a block of data through the pipe. * - * * - * This will take the data submitted and either Base64 encode or decode it (as specified * - * in the pipe's constructor). The nature of Base64 encoding means that the data will * - * grow 30% in size when encoding and decrease by a like amount when decoding. * - * * - * INPUT: source -- Pointer to the data to be translated. * - * * - * length -- The number of bytes to translate. * - * * - * OUTPUT: Returns with the actual number of bytes output at the far distant final end of * - * the pipe chain. * - * * - * WARNINGS: none * - * * - * HISTORY: * - * 07/03/1996 JLB : Created. * - *=============================================================================================*/ -int Base64Pipe::Put(void const * source, int slen) -{ - if (source == NULL || slen < 1) { - return(BASECLASS::Put(source, slen)); - } - - int total = 0; - - char * from; - int fromsize; - char * to; - int tosize; - - if (Control == ENCODE) { - from = PBuffer; - fromsize = sizeof(PBuffer); - to = CBuffer; - tosize = sizeof(CBuffer); - } else { - from = CBuffer; - fromsize = sizeof(CBuffer); - to = PBuffer; - tosize = sizeof(PBuffer); - } - - if (Counter > 0) { - int len = (slen < (fromsize-Counter)) ? slen : (fromsize-Counter); - memmove(&from[Counter], source, len); - Counter += len; - slen -= len; - source = ((char *)source) + len; - - if (Counter == fromsize) { - int outcount; - if (Control == ENCODE) { - outcount = Base64_Encode(from, fromsize, to, tosize); - } else { - outcount = Base64_Decode(from, fromsize, to, tosize); - } - total += BASECLASS::Put(to, outcount); - Counter = 0; - } - } - - while (slen >= fromsize) { - int outcount; - if (Control == ENCODE) { - outcount = Base64_Encode(source, fromsize, to, tosize); - } else { - outcount = Base64_Decode(source, fromsize, to, tosize); - } - source = ((char *)source) + fromsize; - total += BASECLASS::Put(to, outcount); - slen -= fromsize; - } - - if (slen > 0) { - memmove(from, source, slen); - Counter = slen; - } - - return(total); -} - - -/*********************************************************************************************** - * Base64Pipe::Flush -- Flushes the final pending data through the pipe. * - * * - * If there is any non-processed data accumulated in the holding buffer (quite likely when * - * encoding), then it will be processed and flushed out the end of the pipe. * - * * - * INPUT: none * - * * - * OUTPUT: Returns with the number of bytes output at the far distant final end of the pipe * - * chain. * - * * - * WARNINGS: none * - * * - * HISTORY: * - * 07/03/1996 JLB : Created. * - *=============================================================================================*/ -int Base64Pipe::Flush(void) -{ - int len = 0; - - if (Counter) { - if (Control == ENCODE) { - int chars = Base64_Encode(PBuffer, Counter, CBuffer, sizeof(CBuffer)); - len += BASECLASS::Put(CBuffer, chars); - } else { - int chars = Base64_Decode(CBuffer, Counter, PBuffer, sizeof(PBuffer)); - len += BASECLASS::Put(PBuffer, chars); - } - Counter = 0; - } - len += BASECLASS::Flush(); - return(len); -} diff --git a/code/b64pipe.h b/code/b64pipe.h deleted file mode 100644 index 46cb92a44..000000000 --- a/code/b64pipe.h +++ /dev/null @@ -1,84 +0,0 @@ -/******************************************************************************* - * O P E N T S - ******************************************************************************* - * SPDX-License-Identifier: GPL-3.0-or-later - * Copyright 2025 Electronic Arts Inc. - * Copyright 2026 OpenTS contributors - * - * Contains material derived from Electronic Arts source code. - * Modified by OpenTS contributors, 2026. - * EA's GPLv3 Section 7 additional terms and supplemental warranty - * disclaimers apply; see LICENSE.md. - ******************************************************************************/ - -/*********************************************************************************************** - *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** - *********************************************************************************************** - * * - * Project Name : Command & Conquer * - * * - * $Archive:: /G/wwlib/b64pipe.h $* - * * - * $Author:: Eric_c $* - * * - * $Modtime:: 4/02/99 11:58a $* - * * - * $Revision:: 2 $* - * * - *---------------------------------------------------------------------------------------------* - * Functions: * - * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - -#pragma once - -#include "pipe.h" - -/* -** This class performs Base64 encoding/decoding to the data that is piped through. Note that -** encoded data will grow in size by about 30%. The reverse occurs when decoding. -*/ -class Base64Pipe : public Pipe -{ - typedef Pipe BASECLASS; - - public: - enum CodeControl { - ENCODE, - DECODE - }; - - Base64Pipe(CodeControl control) : Control(control), Counter(0) {} - - virtual int Flush(void) override; - virtual int Put(void const * source, int slen) override; - - private: - - /* - ** Indicates if this is for encoding or decoding of Base64 data. - */ - CodeControl Control; - - /* - ** The counter of the number of accumulated bytes pending for processing. - */ - int Counter; - - /* - ** Buffer that holds the Base64 coded bytes. This will be the staging buffer if - ** this is for a decoding process. Otherwise, it will be used as a scratch buffer. - */ - char CBuffer[4]; - - /* - ** Buffer that holds the plain bytes. This will be the staging buffer if this - ** is for an encoding process. Otherwise, it will be used as a scratch buffer. - */ - char PBuffer[3]; - - /* - ** Explicitly disable the copy constructor and the assignment operator. - */ - Base64Pipe(Base64Pipe & rvalue); - Base64Pipe & operator = (Base64Pipe const & pipe); -}; diff --git a/code/b64straw.cpp b/code/b64straw.cpp deleted file mode 100644 index 560a6a024..000000000 --- a/code/b64straw.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/******************************************************************************* - * O P E N T S - ******************************************************************************* - * SPDX-License-Identifier: GPL-3.0-or-later - * Copyright 2025 Electronic Arts Inc. - * Copyright 2026 OpenTS contributors - * - * Contains material derived from Electronic Arts source code. - * Modified by OpenTS contributors, 2026. - * EA's GPLv3 Section 7 additional terms and supplemental warranty - * disclaimers apply; see LICENSE.md. - ******************************************************************************/ - -/*********************************************************************************************** - *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** - *********************************************************************************************** - * * - * Project Name : Command & Conquer * - * * - * $Archive:: /Commando/Library/B64STRAW.CPP $* - * * - * $Author:: Greg_h $* - * * - * $Modtime:: 7/22/97 11:37a $* - * * - * $Revision:: 1 $* - * * - *---------------------------------------------------------------------------------------------* - * Functions: * - * Base64Straw::Get -- Fetch data and convert it to/from base 64 encoding. * - * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - -#include "always.h" - -#include "b64straw.h" - -#include "base64.h" - -#include - - -/*********************************************************************************************** - * Base64Straw::Get -- Fetch data and convert it to/from base 64 encoding. * - * * - * This routine will fetch the number of bytes requested and perform any conversion as * - * necessary upon the data. The nature of Base 64 encoding means that the data will * - * increase in size by 30% when encoding and decrease in like manner when decoding. * - * * - * INPUT: source -- The buffer to hold the processed data. * - * * - * length -- The number of bytes requested. * - * * - * OUTPUT: Returns with the number of bytes stored into the buffer. If the number is less * - * than requested, then this indicates that the data stream has been exhausted. * - * * - * WARNINGS: none * - * * - * HISTORY: * - * 07/03/1996 JLB : Created. * - *=============================================================================================*/ -int Base64Straw::Get(void * source, int slen) -{ - int total = 0; - - char * from; - int fromsize; - char * to; - int tosize; - - if (Control == ENCODE) { - from = PBuffer; - fromsize = sizeof(PBuffer); - to = CBuffer; - tosize = sizeof(CBuffer); - } else { - from = CBuffer; - fromsize = sizeof(CBuffer); - to = PBuffer; - tosize = sizeof(PBuffer); - } - - /* - ** Process the byte request in code blocks until there are either - ** no more source bytes available or the request has been fulfilled. - */ - while (slen > 0) { - - /* - ** Transfer any processed bytes available to the request buffer. - */ - if (Counter > 0) { - int len = (slen < Counter) ? slen : Counter; - memmove(source, &to[tosize-Counter], len); - Counter -= len; - slen -= len; - source = ((char *)source) + len; - total += len; - } - if (slen == 0) break; - - /* - ** More bytes are needed, so fetch and process another base 64 block. - */ - int incount = BASECLASS::Get(from, fromsize); - if (Control == ENCODE) { - Counter = Base64_Encode(from, incount, to, tosize); - } else { - Counter = Base64_Decode(from, incount, to, tosize); - } - if (Counter == 0) break; - } - - return(total); -} diff --git a/code/b64straw.h b/code/b64straw.h deleted file mode 100644 index 42e0ac715..000000000 --- a/code/b64straw.h +++ /dev/null @@ -1,82 +0,0 @@ -/******************************************************************************* - * O P E N T S - ******************************************************************************* - * SPDX-License-Identifier: GPL-3.0-or-later - * Copyright 2025 Electronic Arts Inc. - * Copyright 2026 OpenTS contributors - * - * Contains material derived from Electronic Arts source code. - * Modified by OpenTS contributors, 2026. - * EA's GPLv3 Section 7 additional terms and supplemental warranty - * disclaimers apply; see LICENSE.md. - ******************************************************************************/ - -/*********************************************************************************************** - *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** - *********************************************************************************************** - * * - * Project Name : Command & Conquer * - * * - * $Archive:: /G/wwlib/b64straw.h $* - * * - * $Author:: Eric_c $* - * * - * $Modtime:: 4/02/99 11:58a $* - * * - * $Revision:: 2 $* - * * - *---------------------------------------------------------------------------------------------* - * Functions: * - * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - -#pragma once - -#include "straw.h" - -/* -** Performs Base 64 encoding/decoding on the data that is drawn through the straw. Note that -** encoding increases the data size by about 30%. The reverse occurs when decoding. -*/ -class Base64Straw : public Straw -{ - typedef Straw BASECLASS; - - public: - enum CodeControl { - ENCODE, - DECODE - }; - - Base64Straw(CodeControl control) : Control(control), Counter(0) {} - virtual int Get(void * source, int slen) override; - - private: - - /* - ** Indicates if this is for encoding or decoding of Base64 data. - */ - CodeControl Control; - - /* - ** The counter of the number of accumulated bytes pending for processing. - */ - int Counter; - - /* - ** Buffer that holds the Base64 coded bytes. This will be the staging buffer if - ** this is for a decoding process. Otherwise, it will be used as a scratch buffer. - */ - char CBuffer[4]; - - /* - ** Buffer that holds the plain bytes. This will be the staging buffer if this - ** is for an encoding process. Otherwise, it will be used as a scratch buffer. - */ - char PBuffer[3]; - - /* - ** Explicitly disable the copy constructor and the assignment operator. - */ - Base64Straw(Base64Straw & rvalue); - Base64Straw & operator = (Base64Straw const & pipe); -}; diff --git a/code/base64.cpp b/code/base64.cpp deleted file mode 100644 index 1ed217aff..000000000 --- a/code/base64.cpp +++ /dev/null @@ -1,433 +0,0 @@ -/******************************************************************************* - * O P E N T S - ******************************************************************************* - * SPDX-License-Identifier: GPL-3.0-or-later - * Copyright 2025 Electronic Arts Inc. - * Copyright 2026 OpenTS contributors - * - * Contains material derived from Electronic Arts source code. - * Modified by OpenTS contributors, 2026. - * EA's GPLv3 Section 7 additional terms and supplemental warranty - * disclaimers apply; see LICENSE.md. - ******************************************************************************/ - -/*********************************************************************************************** - *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** - *********************************************************************************************** - * * - * Project Name : Command & Conquer * - * * - * $Archive:: /Commando/Code/wwlib/base64.cpp $* - * * - * $Author:: Jani_p $* - * * - * $Modtime:: 5/04/01 8:08p $* - * * - * $Revision:: 3 $* - * * - *---------------------------------------------------------------------------------------------* - * Functions: * - * Base64_Decode -- Decodes Base 64 data into its original data form. * - * Base64_Encode -- Encode data into Base 64 format. * - * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - -#include "always.h" - -#include "base64.h" - -#include - -/* -** This is the magic padding character used to fill out the encoded data to a multiple of -** 4 characters even though the source data is less than necessary to accomplish this. -** The pad character lets the decoder know of this condition and it will compensate -** accordingly. -*/ -static char const * const _pad = "="; - -/* -** This encoder translation table will convert a 6 bit number into an ASCII character. -*/ -static char const * const _encoder = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -/* -** The decoder translation table takes an ASCII character and converts it into a -** 6 bit number. -*/ -#define BAD 0xFE // Ignore this character in source data. -#define END 0xFF // Signifies premature end of input data. -static unsigned char const _decoder[256] = { - BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, - BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, - BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,62,BAD,BAD,BAD,63, - 52,53,54,55,56,57,58,59,60,61,BAD,BAD,BAD,END,BAD,BAD, - BAD,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14, - 15,16,17,18,19,20,21,22,23,24,25,BAD,BAD,BAD,BAD,BAD, - BAD,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, - 41,42,43,44,45,46,47,48,49,50,51,BAD,BAD,BAD,BAD,BAD, - BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, - BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, - BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, - BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, - BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, - BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, - BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, - BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD -}; - -int const PacketChars = 4; - - -/* -** The packet type is used to construct and disect the Base64 data blocks. The data -** consists of three source data bytes mapped onto four 6 bit Base64 code elements. -*/ -typedef union { - struct { -#ifdef BIG_ENDIAN - unsigned char C1; - unsigned char C2; - unsigned char C3; -#else - unsigned char C3; - unsigned char C2; - unsigned char C1; -#endif - unsigned char pad; - } Char; - struct { -#ifdef BIG_ENDIAN - unsigned O1:6; - unsigned O2:6; - unsigned O3:6; - unsigned O4:6; -#else - unsigned O4:6; - unsigned O3:6; - unsigned O2:6; - unsigned O1:6; -#endif - unsigned pad:8; - } SubCode; - unsigned int Raw; -} PacketType; - - -/*********************************************************************************************** - * Base64_Encode -- Encode data into Base 64 format. * - * * - * This will take an arbitrary length of source data and transform it into base 64 format * - * data. Base 64 format has the property of being very portable across text editors and * - * country character encoding schemes. As such it is ideal for e-mail. Note that the output * - * data will be about 33% larger than the source. * - * * - * INPUT: source -- Pointer to the source data to convert. * - * * - * slen -- The number of bytes to encode. * - * * - * dest -- Pointer to the destination buffer that will hold the encoded data. * - * * - * dlen -- The size of the destination buffer. * - * * - * OUTPUT: Returns with the number of bytes stored into the destination buffer. * - * * - * WARNINGS: Be sure that the destination buffer is big enough to hold the encoded output. * - * * - * HISTORY: * - * 07/06/1996 JLB : Created. * - *=============================================================================================*/ -int Base64_Encode(void const * source, int slen, void * dest, int dlen) -{ - /* - ** Check the parameters for legality. - */ - if (source == NULL || slen == 0 || dest == NULL || dlen == 0) { - return(0); - } - - /* - ** Process the source data in blocks of three bytes. Fewer than three bytes - ** results in special padding output characters (automatically discarded - ** during the decode process). - */ - int total = 0; - unsigned char const * sptr = (unsigned char const *)source; - unsigned char * dptr = (unsigned char *)dest; - while (slen > 0 && dlen >= PacketChars) { - - /* - ** Fetch 24 bits of source data. - */ - PacketType packet; - - int pad = 0; - packet.Raw = 0; - packet.Char.C1 = *sptr++; - slen--; - if (slen) { - packet.Char.C2 = *sptr++; - slen--; - } else { - pad++; - } - if (slen) { - packet.Char.C3 = *sptr++; - slen--; - } else { - pad++; - } - - /* - ** Translate and write 4 characters of Base64 data. Pad with pad - ** characters if there is insufficient source data for a full packet. - */ - *dptr++ = _encoder[packet.SubCode.O1]; - *dptr++ = _encoder[packet.SubCode.O2]; - if (pad < 2) { - *dptr++ = _encoder[packet.SubCode.O3]; - } else { - *dptr++ = _pad[0]; - } - if (pad < 1) { - *dptr++ = _encoder[packet.SubCode.O4]; - } else { - *dptr++ = _pad[0]; - } - - dlen -= PacketChars; - total += PacketChars; - } - - /* - ** Add a trailing null as a courtesy measure. - */ - if (dlen > 0) { - *dptr = '\0'; - } - - /* - ** Return with the total number of characters in the output buffer. - */ - return(total); -} - - -/*********************************************************************************************** - * Base64_Decode -- Decodes Base 64 data into its original data form. * - * * - * Use this routine to decode base 64 data back into the original data. A property of this * - * decode process is that unrecognized input characters are ignored. This allows mangled * - * source (filled with line breaks or spaces) to be correctly decoded. The decode process * - * terminates when the end of the source data has been reached or the special end of data * - * marker is encountered. * - * * - * INPUT: source -- Pointer to the source data to decode. * - * * - * slen -- The number of bytes in the source data buffer. * - * * - * dest -- Pointer to the destination buffer to be filled with the decoded data. * - * * - * dlen -- The maximum size of the destination buffer. * - * * - * OUTPUT: Returns with the number of bytes stored into the destination buffer. This will * - * always be less than the number of source bytes (usually by about 33%). * - * * - * WARNINGS: none * - * * - * HISTORY: * - * 07/06/1996 JLB : Created. * - *=============================================================================================*/ -int Base64_Decode(void const * source, int slen, void * dest, int dlen) -{ - /* - ** Check the parameters for legality. - */ - if (source == NULL || slen == 0 || dest == NULL || dlen == 0) { - return(0); - } - - int total = 0; - unsigned char const * sptr = (unsigned char const *)source; - unsigned char * dptr = (unsigned char *)dest; - while (slen > 0 && dlen > 0) { - - PacketType packet; - packet.Raw = 0; - - /* - ** Process input until a full packet has been accumulated or the - ** source is exhausted. - */ - int pcount = 0; - while (pcount < PacketChars && slen > 0) { - unsigned char c = *sptr++; - slen--; - - unsigned char code = _decoder[c]; - - /* - ** An unrecognized character is skipped. - */ - if (code == BAD) continue; - - /* - ** The "=" character signifies the end of data regardless of what - ** the source buffer length value may be. - */ - if (code == END) { - slen = 0; - break; - } - - /* - ** A valid Base64 character was found so add it to the packet - ** data. - */ - switch (pcount) { - case 0: - packet.SubCode.O1 = code; - break; - case 1: - packet.SubCode.O2 = code; - break; - case 2: - packet.SubCode.O3 = code; - break; - case 3: - packet.SubCode.O4 = code; - break; - } - pcount++; - } - - /* - ** A packet block is ready for output into the destination buffer. - */ - *dptr++ = packet.Char.C1; - dlen--; - total++; - if (dlen > 0 && pcount > 2) { - *dptr++ = packet.Char.C2; - dlen--; - total++; - } - if (dlen > 0 && pcount > 3) { - *dptr++ = packet.Char.C3; - dlen--; - total++; - } - } - - /* - ** Return with the total number of characters decoded into the - ** output buffer. - */ - return(total); -} - - -/* -Base64 Content-Transfer-Encoding - -The Base64 Content-Transfer-Encoding is designed to represent arbitrary -sequences of octets in a form that need not be humanly readable. The encoding -and decoding algorithms are simple, but the encoded data are consistently -only about 33 percent larger than the unencoded data. This encoding is -virtually identical to the one used in Privacy Enhanced Mail (PEM) -applications, as defined in RFC 1421. The base64 encoding is adapted from -RFC 1421, with one change: base64 eliminates the "*" mechanism for embedded -clear text. - -A 65-character subset of US-ASCII is used, enabling 6 bits to be represented -per printable character. (The extra 65th character, "=", is used to signify a -special processing function.) - -NOTE: - This subset has the important property that it is represented identically - in all versions of ISO 646, including US ASCII, and all characters in the - subset are also represented identically in all versions of EBCDIC. Other - popular encodings, such as the encoding used by the uuencode utility and - the base85 encoding specified as part of Level 2 PostScript, do not share - these properties, and thus do not fulfill the portability requirements a - binary transport encoding for mail must meet. - -The encoding process represents 24-bit groups of input bits as output strings -of 4 encoded characters. Proceeding from left to right, a 24-bit input group is -formed by concatenating 3 8-bit input groups. These 24 bits are then treated as -4 concatenated 6-bit groups, each of which is translated into a single digit in -the base64 alphabet. When encoding a bit stream via the base64 encoding, the -bit stream must be presumed to be ordered with the most-significant-bit first. -That is, the first bit in the stream will be the high-order bit in the first -byte, and the eighth bit will be the low-order bit in the first byte, and so on. - -Each 6-bit group is used as an index into an array of 64 printable characters. -The character referenced by the index is placed in the output string. These -characters, identified in Table 1, below, are selected so as to be universally -representable, and the set excludes characters with particular significance to -SMTP (e.g., ".", CR, LF) and to the encapsulation boundaries defined in this -document (e.g., "-"). - -Table 1: The Base64 Alphabet - - Value Encoding Value Encoding Value Encoding Value Encoding - 0 A 17 R 34 i 51 z - 1 B 18 S 35 j 52 0 - 2 C 19 T 36 k 53 1 - 3 D 20 U 37 l 54 2 - 4 E 21 V 38 m 55 3 - 5 F 22 W 39 n 56 4 - 6 G 23 X 40 o 57 5 - 7 H 24 Y 41 p 58 6 - 8 I 25 Z 42 q 59 7 - 9 J 26 a 43 r 60 8 - 10 K 27 b 44 s 61 9 - 11 L 28 c 45 t 62 + - 12 M 29 d 46 u 63 / - 13 N 30 e 47 v - 14 O 31 f 48 w (pad) = - 15 P 32 g 49 x - 16 Q 33 h 50 y - -The output stream (encoded bytes) must be represented in lines of no more than -76 characters each. All line breaks or other characters not found in Table 1 -must be ignored by decoding software. In base64 data, characters other than -those in Table 1, line breaks, and other white space probably indicate a -transmission error, about which a warning message or even a message rejection -might be appropriate under some circumstances. - -Special processing is performed if fewer than 24 bits are available at the end -of the data being encoded. A full encoding quantum is always completed at the -end of a body. When fewer than 24 input bits are available in an input group, -zero bits are added (on the right) to form an integral number of 6-bit groups. -Padding at the end of the data is performed using the '=' character. Since all -base64 input is an integral number of octets, only the following cases can -arise: (1) the final quantum of encoding input is an integral multiple of 24 -bits; here, the final unit of encoded output will be an integral multiple of 4 -characters with no "=" padding, (2) the final quantum of encoding input is -exactly 8 bits; here, the final unit of encoded output will be two characters -followed by two "=" padding characters, or (3) the final quantum of encoding -input is exactly 16 bits; here, the final unit of encoded output will be three -characters followed by one "=" padding character. - -Because it is used only for padding at the end of the data, the occurrence of -any '=' characters may be taken as evidence that the end of the data has been -reached (without truncation in transit). No such assurance is possible, -however, when the number of octets transmitted was a multiple of three. - -Any characters outside of the base64 alphabet are to be ignored in -base64-encoded data. The same applies to any illegal sequence of characters in -the base64 encoding, such as "=====" - -Care must be taken to use the proper octets for line breaks if base64 encoding -is applied directly to text material that has not been converted to canonical -form. In particular, text line breaks must be converted into CRLF sequences -prior to base64 encoding. The important thing to note is that this may be done -directly by the encoder rather than in a prior canonicalization step in some -implementations. - -NOTE: - There is no need to worry about quoting apparent encapsulation boundaries - within base64-encoded parts of multipart entities because no hyphen - characters are used in the base64 encoding. - -*/ diff --git a/code/base64.h b/code/base64.h deleted file mode 100644 index dd3bd3c59..000000000 --- a/code/base64.h +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************* - * O P E N T S - ******************************************************************************* - * SPDX-License-Identifier: GPL-3.0-or-later - * Copyright 2025 Electronic Arts Inc. - * Copyright 2026 OpenTS contributors - * - * Contains material derived from Electronic Arts source code. - * Modified by OpenTS contributors, 2026. - * EA's GPLv3 Section 7 additional terms and supplemental warranty - * disclaimers apply; see LICENSE.md. - ******************************************************************************/ - -/*********************************************************************************************** - *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** - *********************************************************************************************** - * * - * Project Name : Command & Conquer * - * * - * $Archive:: /G/wwlib/base64.h $* - * * - * $Author:: Eric_c $* - * * - * $Modtime:: 4/02/99 11:58a $* - * * - * $Revision:: 2 $* - * * - *---------------------------------------------------------------------------------------------* - * Functions: * - * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - -#pragma once - - -int Base64_Encode(void const * source, int slen, void * dest, int dlen); -int Base64_Decode(void const * source, int slen, void * dest, int dlen); diff --git a/code/ini.cpp b/code/ini.cpp index da6759aed..624985ac4 100644 --- a/code/ini.cpp +++ b/code/ini.cpp @@ -70,8 +70,6 @@ #include "ini.h" -#include "b64pipe.h" -#include "b64straw.h" #include "cstraw.h" #include "dbgprint.h" #include "pk.h" @@ -80,12 +78,15 @@ #include "xpipe.h" #include "xstraw.h" +#include + #include #include #include #include #include #include +#include /*********************************************************************************************** * INIClass::Clear -- Clears out a section (or all sections) of the INI data. * @@ -651,19 +652,20 @@ bool INIClass::Put_UUBlock(char const * section, void const * block, int len) Clear(section); - BufferStraw straw(block, len); - Base64Straw bstraw(Base64Straw::ENCODE); - bstraw.Get_From(straw); + size_t encoded_size = ((static_cast(len) + 2) / 3) * 4; + std::vector encoded(encoded_size); + size_t output_size = 0; + base64_encode(static_cast(block), static_cast(len), + encoded.data(), &output_size, 0); int counter = 1; - - for (;;) { + for (size_t offset = 0; offset < output_size; offset += 70) { char buffer[71]; char sbuffer[32]; - int length = bstraw.Get(buffer, sizeof(buffer)-1); + size_t length = std::min(70, output_size - offset); + std::memcpy(buffer, encoded.data() + offset, length); buffer[length] = '\0'; - if (length == 0) break; sprintf(sbuffer, "%d", counter); Put_String(section, sbuffer, buffer); @@ -699,23 +701,47 @@ bool INIClass::Put_UUBlock(char const * section, void const * block, int len) *=============================================================================================*/ int INIClass::Get_UUBlock(char const * section, void * block, int len) const { - if (section == NULL) return(0); - - Base64Pipe b64pipe(Base64Pipe::DECODE); - BufferPipe bpipe(block, len); + if (section == NULL || block == NULL || len < 1) return(0); - b64pipe.Put_To(&bpipe); + base64_state state; + base64_stream_decode_init(&state, 0); int total = 0; int counter = Entry_Count(section); + bool finished = false; for (int index = 0; index < counter; index++) { char buffer[128]; + char filtered[128]; + char decoded[sizeof(filtered) * 3 / 4]; int length = Get_String(section, Get_Entry(section, index), "=", buffer, sizeof(buffer)); - int outcount = b64pipe.Put(buffer, length); - total += outcount; + int filtered_count = 0; + for (int offset = 0; offset < length; offset++) { + char value = buffer[offset]; + bool valid = (value >= 'A' && value <= 'Z') || (value >= 'a' && value <= 'z') + || (value >= '0' && value <= '9') || value == '+' || value == '/'; + if (valid) { + filtered[filtered_count++] = value; + } else if (value == '=') { + filtered[filtered_count++] = value; + finished = true; + break; + } + } + + size_t decoded_count = 0; + if (filtered_count > 0 + && !base64_stream_decode(&state, filtered, filtered_count, decoded, &decoded_count)) { + break; + } + + int copy_count = std::min(len - total, static_cast(decoded_count)); + std::memcpy(static_cast(block) + total, decoded, copy_count); + total += copy_count; + if (finished || total == len) { + break; + } } - total += b64pipe.End(); return(total); } diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 312797ca7..842b25f74 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -27,9 +27,15 @@ source tree. ## Dependencies -The renderer uses [bgfx](https://github.com/bkaradzic/bgfx), vendored through -`thirdparty/bgfx.cmake` at a tested tag. That submodule contains bgfx, bx, and -bimg as nested submodules, so initialize it recursively: +The source tree provides these dependencies as pinned submodules: + +- [bgfx](https://github.com/bkaradzic/bgfx), vendored through + `thirdparty/bgfx.cmake` at a tested tag. That submodule contains bgfx, bx, + and bimg as nested submodules. +- [libbase64](https://github.com/aklomp/base64), used for Base64 encoding and + decoding. + +Initialize the dependencies recursively: ```powershell git submodule update --init --recursive diff --git a/tests/gamedirs/CMakeLists.txt b/tests/gamedirs/CMakeLists.txt index 964458a82..46e348039 100644 --- a/tests/gamedirs/CMakeLists.txt +++ b/tests/gamedirs/CMakeLists.txt @@ -9,9 +9,6 @@ add_executable(GameDirs "${CMAKE_SOURCE_DIR}/code/rawfile.cpp" "${CMAKE_SOURCE_DIR}/code/dbgprint.cpp" "${CMAKE_SOURCE_DIR}/code/ini.cpp" - "${CMAKE_SOURCE_DIR}/code/b64pipe.cpp" - "${CMAKE_SOURCE_DIR}/code/b64straw.cpp" - "${CMAKE_SOURCE_DIR}/code/base64.cpp" "${CMAKE_SOURCE_DIR}/code/buff.cpp" "${CMAKE_SOURCE_DIR}/code/crc.cpp" "${CMAKE_SOURCE_DIR}/code/cstraw.cpp" @@ -42,7 +39,7 @@ target_compile_options(GameDirs PRIVATE $<$:/MT /EHsc /Zc:__cplusplus> ) -target_link_libraries(GameDirs PRIVATE kernel32 user32 shell32) +target_link_libraries(GameDirs PRIVATE base64 kernel32 user32 shell32) set_target_properties(GameDirs PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" diff --git a/tests/ini/CMakeLists.txt b/tests/ini/CMakeLists.txt index 08e29da31..d915a41f1 100644 --- a/tests/ini/CMakeLists.txt +++ b/tests/ini/CMakeLists.txt @@ -2,11 +2,9 @@ # recursive glob building the engine cannot pick this target's entry point up. add_executable(IniContract "${CMAKE_CURRENT_SOURCE_DIR}/inicontract.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/uublockcontract.cpp" "${CMAKE_SOURCE_DIR}/code/ini.cpp" "${CMAKE_SOURCE_DIR}/code/dbgprint.cpp" - "${CMAKE_SOURCE_DIR}/code/b64pipe.cpp" - "${CMAKE_SOURCE_DIR}/code/b64straw.cpp" - "${CMAKE_SOURCE_DIR}/code/base64.cpp" "${CMAKE_SOURCE_DIR}/code/buff.cpp" "${CMAKE_SOURCE_DIR}/code/crc.cpp" "${CMAKE_SOURCE_DIR}/code/cstraw.cpp" @@ -37,7 +35,7 @@ target_compile_options(IniContract PRIVATE $<$:/MT /EHsc /Zc:__cplusplus> ) -target_link_libraries(IniContract PRIVATE kernel32 user32 shell32) +target_link_libraries(IniContract PRIVATE base64 kernel32 user32 shell32) set_target_properties(IniContract PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" diff --git a/tests/ini/inicontract.cpp b/tests/ini/inicontract.cpp index 2798e7a7e..b4f9785b8 100644 --- a/tests/ini/inicontract.cpp +++ b/tests/ini/inicontract.cpp @@ -27,6 +27,8 @@ #include "xpipe.h" #include "xstraw.h" +int Test_UUBlock(void); + namespace { int Failures = 0; @@ -542,6 +544,8 @@ int main(void) Check(count == 250, "a long list tokenizes to every name it holds"); } + Failures += Test_UUBlock(); + std::printf("\n%s\n", Failures == 0 ? "PASSED" : "FAILED"); return(Failures == 0 ? 0 : 1); } diff --git a/tests/ini/uublockcontract.cpp b/tests/ini/uublockcontract.cpp new file mode 100644 index 000000000..70f1d05fb --- /dev/null +++ b/tests/ini/uublockcontract.cpp @@ -0,0 +1,166 @@ +/******************************************************************************* + * O P E N T S + ******************************************************************************* + * SPDX-License-Identifier: GPL-3.0-or-later + * Copyright 2026 OpenTS contributors + * + * See LICENSE.md for applicable additional terms and warranty disclaimers. + ******************************************************************************/ + +#include "ini.h" + +#include +#include +#include +#include +#include +#include + +namespace { + +int Failures = 0; + + +void Check(bool condition, char const * what) +{ + std::printf("%-64s %s\n", what, condition ? "ok" : "FAILED"); + + if (!condition) { + Failures++; + } +} + + +std::string Entry(INIClass const & ini, char const * section, char const * name) +{ + char value[128]; + ini.Get_String(section, name, "", value, sizeof(value)); + return(value); +} + + +bool Round_Trips(std::size_t size) +{ + std::vector source(size); + for (std::size_t index = 0; index < source.size(); index++) { + source[index] = static_cast((index * 37 + 11) & 0xff); + } + + INIClass ini; + if (!ini.Put_UUBlock("Binary", source.data(), static_cast(source.size()))) { + return(false); + } + + std::vector decoded(size, 0xff); + int count = ini.Get_UUBlock("Binary", decoded.data(), static_cast(decoded.size())); + return(count == static_cast(source.size()) && decoded == source); +} + +} + + +int Test_UUBlock(void) +{ + { + INIClass ini; + std::array one{'M'}; + std::array two{'M', 'a'}; + std::array three{'M', 'a', 'n'}; + + ini.Put_UUBlock("One", one.data(), static_cast(one.size())); + ini.Put_UUBlock("Two", two.data(), static_cast(two.size())); + ini.Put_UUBlock("Three", three.data(), static_cast(three.size())); + + Check(Entry(ini, "One", "1") == "TQ==", "one input byte receives two padding characters"); + Check(Entry(ini, "Two", "1") == "TWE=", "two input bytes receive one padding character"); + Check(Entry(ini, "Three", "1") == "TWFu", "three input bytes form one complete Base64 group"); + } + + { + INIClass ini; + std::array source{}; + ini.Put_String("Binary", "stale", "value"); + bool stored = ini.Put_UUBlock("Binary", source.data(), static_cast(source.size())); + + Check(stored && ini.Entry_Count("Binary") == 2, + "writing a block replaces its section with numbered entries"); + Check(Entry(ini, "Binary", "1") == std::string(70, 'A'), + "encoded data is split after 70 characters"); + Check(Entry(ini, "Binary", "2") == "AA", "data after the line boundary is retained"); + } + + { + std::array sizes{1, 2, 3, 51, 52, 53, 54, 55, 4095, 4096, 8194}; + bool valid = std::all_of(sizes.begin(), sizes.end(), Round_Trips); + Check(valid, "binary blocks round-trip across Base64 and INI boundaries"); + } + + { + INIClass ini; + ini.Put_String("Binary", "1", std::string(70, 'A').c_str()); + ini.Put_String("Binary", "2", "AA"); + + std::array decoded; + decoded.fill(0xff); + int count = ini.Get_UUBlock("Binary", decoded.data(), static_cast(decoded.size())); + Check(count == static_cast(decoded.size()) + && std::all_of(decoded.begin(), decoded.end(), [](unsigned char value) { return(value == 0); }), + "decoding carries an incomplete group across INI entries"); + } + + { + INIClass ini; + ini.Put_String("Binary", "1", "T Q#"); + + unsigned char decoded = 0; + int count = ini.Get_UUBlock("Binary", &decoded, 1); + Check(count == 1 && decoded == 'M', + "non-Base64 separators are ignored while decoding"); + } + + { + INIClass ini; + ini.Put_String("Binary", "1", "TQ=="); + + unsigned char decoded = 0; + int count = ini.Get_UUBlock("Binary", &decoded, 1); + Check(count == 1 && decoded == 'M', "padding completes the final encoded block"); + } + + { + INIClass ini; + ini.Put_String("Binary", "1", "TQ"); + + unsigned char decoded = 0xa5; + int count = ini.Get_UUBlock("Binary", &decoded, 1); + Check(count == 1 && decoded == 'M', "missing Base64 padding remains accepted"); + } + + { + INIClass ini; + ini.Put_String("Binary", "1", std::string(70, 'A').c_str()); + ini.Put_String("Binary", "2", "AA"); + + std::array decoded; + decoded.fill(0xa5); + int count = ini.Get_UUBlock("Binary", decoded.data(), 17); + Check(count == 17 && std::all_of(decoded.begin(), decoded.begin() + 17, + [](unsigned char value) { return(value == 0); }) && decoded.back() == 0xa5, + "decoding stops at the destination buffer boundary"); + } + + { + INIClass ini; + unsigned char value = 0; + Check(!ini.Put_UUBlock(nullptr, &value, 1) + && !ini.Put_UUBlock("Binary", nullptr, 1) + && !ini.Put_UUBlock("Binary", &value, 0), + "invalid writes are rejected"); + Check(ini.Get_UUBlock(nullptr, &value, 1) == 0 + && ini.Get_UUBlock("Binary", nullptr, 1) == 0 + && ini.Get_UUBlock("Binary", &value, 0) == 0, + "invalid reads produce no output"); + } + + return(Failures); +} diff --git a/tests/spawner/CMakeLists.txt b/tests/spawner/CMakeLists.txt index 8a4c7ca2f..e86af87f2 100644 --- a/tests/spawner/CMakeLists.txt +++ b/tests/spawner/CMakeLists.txt @@ -13,9 +13,6 @@ add_executable(SpawnContract "${CMAKE_SOURCE_DIR}/code/cstraw.cpp" "${CMAKE_SOURCE_DIR}/code/pipe.cpp" "${CMAKE_SOURCE_DIR}/code/xpipe.cpp" - "${CMAKE_SOURCE_DIR}/code/b64straw.cpp" - "${CMAKE_SOURCE_DIR}/code/b64pipe.cpp" - "${CMAKE_SOURCE_DIR}/code/base64.cpp" "${CMAKE_SOURCE_DIR}/code/crc.cpp" "${CMAKE_SOURCE_DIR}/code/pk.cpp" "${CMAKE_SOURCE_DIR}/code/int.cpp" @@ -39,7 +36,7 @@ target_compile_options(SpawnContract PRIVATE $<$:/MT /EHsc /Zc:__cplusplus> ) -target_link_libraries(SpawnContract PRIVATE kernel32 user32 shell32) +target_link_libraries(SpawnContract PRIVATE base64 kernel32 user32 shell32) set_target_properties(SpawnContract PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index f16e34ec8..49a733c71 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -20,6 +20,17 @@ set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") add_subdirectory(bgfx.cmake) +# libbase64 is used as an embedded library, without its command-line tool. +set(BASE64_BUILD_CLI OFF CACHE BOOL "" FORCE) +set(BASE64_BUILD_TESTS OFF CACHE BOOL "" FORCE) +set(BASE64_WERROR OFF CACHE BOOL "" FORCE) +if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/libbase64/CMakeLists.txt") + message(FATAL_ERROR + "thirdparty/libbase64 is empty. Fetch the submodules with:\n" + " git submodule update --init --recursive") +endif() +add_subdirectory(libbase64) + # These libraries support the disabled texture tools and are not linked into OpenTS. set_target_properties(bimg_decode bimg_encode PROPERTIES EXCLUDE_FROM_ALL TRUE) diff --git a/thirdparty/libbase64 b/thirdparty/libbase64 new file mode 160000 index 000000000..8bdda2d47 --- /dev/null +++ b/thirdparty/libbase64 @@ -0,0 +1 @@ +Subproject commit 8bdda2d47caf8b066999c5bd01069e55bcd0d396