added initial code for Base64 encoding and decoding
[rocksndiamonds.git] / src / libgame / base64.c
1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2021 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // base64.c
10 // ============================================================================
11
12 /*
13
14   https://github.com/superwills/NibbleAndAHalf
15   base64.h -- Fast base64 encoding and decoding.
16   version 1.0.0, April 17, 2013 143a
17
18   Copyright (C) 2013 William Sherif
19
20   This software is provided 'as-is', without any express or implied
21   warranty.  In no event will the authors be held liable for any damages
22   arising from the use of this software.
23
24   Permission is granted to anyone to use this software for any purpose,
25   including commercial applications, and to alter it and redistribute it
26   freely, subject to the following restrictions:
27
28   1. The origin of this software must not be misrepresented; you must not
29      claim that you wrote the original software. If you use this software
30      in a product, an acknowledgment in the product documentation would be
31      appreciated but is not required.
32   2. Altered source versions must be plainly marked as such, and must not be
33      misrepresented as being the original software.
34   3. This notice may not be removed or altered from any source distribution.
35
36   William Sherif
37   will.sherif@gmail.com
38
39   YWxsIHlvdXIgYmFzZSBhcmUgYmVsb25nIHRvIHVz
40
41 */
42
43 #include <stdio.h>
44 #include <stdlib.h>
45
46 #include "base64.h"
47
48
49 const static char* b64="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ;
50
51 // maps A=>0,B=>1..
52 const static unsigned char unb64[]={
53   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //10 
54   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //20 
55   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //30 
56   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //40 
57   0,   0,   0,  62,   0,   0,   0,  63,  52,  53, //50 
58  54,  55,  56,  57,  58,  59,  60,  61,   0,   0, //60 
59   0,   0,   0,   0,   0,   0,   1,   2,   3,   4, //70 
60   5,   6,   7,   8,   9,  10,  11,  12,  13,  14, //80 
61  15,  16,  17,  18,  19,  20,  21,  22,  23,  24, //90 
62  25,   0,   0,   0,   0,   0,   0,  26,  27,  28, //100 
63  29,  30,  31,  32,  33,  34,  35,  36,  37,  38, //110 
64  39,  40,  41,  42,  43,  44,  45,  46,  47,  48, //120 
65  49,  50,  51,   0,   0,   0,   0,   0,   0,   0, //130 
66   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //140 
67   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //150 
68   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //160 
69   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //170 
70   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //180 
71   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //190 
72   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //200 
73   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //210 
74   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //220 
75   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //230 
76   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //240 
77   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, //250 
78   0,   0,   0,   0,   0,   0, 
79 }; // This array has 256 elements
80
81 // Converts binary data of length=len to base64 characters.
82 // Length of the resultant string is stored in flen
83 // (you must pass pointer flen).
84 char* base64( const void* binaryData, int len, int *flen )
85 {
86   const unsigned char* bin = (const unsigned char*) binaryData ;
87   char* res ;
88   
89   int rc = 0 ; // result counter
90   int byteNo ; // I need this after the loop
91   
92   int modulusLen = len % 3 ;
93   int pad = ((modulusLen&1)<<1) + ((modulusLen&2)>>1) ; // 2 gives 1 and 1 gives 2, but 0 gives 0.
94   
95   *flen = 4*(len + pad)/3 ;
96   res = (char*) malloc( *flen + 1 ) ; // and one for the null
97   if( !res )
98   {
99     puts( "ERROR: base64 could not allocate enough memory." ) ;
100     puts( "I must stop because I could not get enough" ) ;
101     return 0;
102   }
103   
104   for( byteNo = 0 ; byteNo <= len-3 ; byteNo+=3 )
105   {
106     unsigned char BYTE0=bin[byteNo];
107     unsigned char BYTE1=bin[byteNo+1];
108     unsigned char BYTE2=bin[byteNo+2];
109     res[rc++]  = b64[ BYTE0 >> 2 ] ;
110     res[rc++]  = b64[ ((0x3&BYTE0)<<4) + (BYTE1 >> 4) ] ;
111     res[rc++]  = b64[ ((0x0f&BYTE1)<<2) + (BYTE2>>6) ] ;
112     res[rc++]  = b64[ 0x3f&BYTE2 ] ;
113   }
114   
115   if( pad==2 )
116   {
117     res[rc++] = b64[ bin[byteNo] >> 2 ] ;
118     res[rc++] = b64[ (0x3&bin[byteNo])<<4 ] ;
119     res[rc++] = '=';
120     res[rc++] = '=';
121   }
122   else if( pad==1 )
123   {
124     res[rc++]  = b64[ bin[byteNo] >> 2 ] ;
125     res[rc++]  = b64[ ((0x3&bin[byteNo])<<4)   +   (bin[byteNo+1] >> 4) ] ;
126     res[rc++]  = b64[ (0x0f&bin[byteNo+1])<<2 ] ;
127     res[rc++] = '=';
128   }
129   
130   res[rc]=0; // NULL TERMINATOR! ;)
131   return res ;
132 }
133
134 unsigned char* unbase64( const char* ascii, int len, int *flen )
135 {
136   const unsigned char *safeAsciiPtr = (const unsigned char*)ascii ;
137   unsigned char *bin ;
138   int cb=0;
139   int charNo;
140   int pad = 0 ;
141
142   if( len < 2 ) { // 2 accesses below would be OOB.
143     // catch empty string, return NULL as result.
144     puts( "ERROR: You passed an invalid base64 string (too short). You get NULL back." ) ;
145     *flen=0;
146     return 0 ;
147   }
148   if( safeAsciiPtr[ len-1 ]=='=' )  ++pad ;
149   if( safeAsciiPtr[ len-2 ]=='=' )  ++pad ;
150   
151   *flen = 3*len/4 - pad ;
152   bin = (unsigned char*)malloc( *flen ) ;
153   if( !bin )
154   {
155     puts( "ERROR: unbase64 could not allocate enough memory." ) ;
156     puts( "I must stop because I could not get enough" ) ;
157     return 0;
158   }
159   
160   for( charNo=0; charNo <= len - 4 - pad ; charNo+=4 )
161   {
162     int A=unb64[safeAsciiPtr[charNo]];
163     int B=unb64[safeAsciiPtr[charNo+1]];
164     int C=unb64[safeAsciiPtr[charNo+2]];
165     int D=unb64[safeAsciiPtr[charNo+3]];
166     
167     bin[cb++] = (A<<2) | (B>>4) ;
168     bin[cb++] = (B<<4) | (C>>2) ;
169     bin[cb++] = (C<<6) | (D) ;
170   }
171   
172   if( pad==1 )
173   {
174     int A=unb64[safeAsciiPtr[charNo]];
175     int B=unb64[safeAsciiPtr[charNo+1]];
176     int C=unb64[safeAsciiPtr[charNo+2]];
177     
178     bin[cb++] = (A<<2) | (B>>4) ;
179     bin[cb++] = (B<<4) | (C>>2) ;
180   }
181   else if( pad==2 )
182   {
183     int A=unb64[safeAsciiPtr[charNo]];
184     int B=unb64[safeAsciiPtr[charNo+1]];
185     
186     bin[cb++] = (A<<2) | (B>>4) ;
187   }
188   
189   return bin ;
190 }