├── .gitignore ├── Manual Map Injector.sln ├── Manual Map Injector ├── DebuggerBypass.dll ├── Manual Map Injector.aps ├── Manual Map Injector.rc ├── Manual Map Injector.vcxproj ├── Manual Map Injector.vcxproj.filters ├── Manual Map Injector.vcxproj.user ├── ReleaseHelper.cpp ├── ReleaseHelper.h ├── includes │ ├── ConvertUTF.c │ ├── ConvertUTF.h │ └── SimpleIni.h ├── injector.cpp ├── injector.h ├── main.cpp └── resource.h ├── README.md └── hello-world-x64.dll /.gitignore: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # 此 .gitignore 文件已由 Microsoft(R) Visual Studio 自动创建。 3 | ################################################################################ 4 | 5 | /.vs/Manual Map Injector 6 | /Manual Map Injector/x64 7 | /x64 8 | -------------------------------------------------------------------------------- /Manual Map Injector.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30413.136 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Manual Map Injector", "Manual Map Injector\Manual Map Injector.vcxproj", "{8E30BB13-F263-4EAF-A17D-DB0906099AAC}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {8E30BB13-F263-4EAF-A17D-DB0906099AAC}.Debug|x64.ActiveCfg = Debug|x64 17 | {8E30BB13-F263-4EAF-A17D-DB0906099AAC}.Debug|x64.Build.0 = Debug|x64 18 | {8E30BB13-F263-4EAF-A17D-DB0906099AAC}.Debug|x86.ActiveCfg = Debug|Win32 19 | {8E30BB13-F263-4EAF-A17D-DB0906099AAC}.Debug|x86.Build.0 = Debug|Win32 20 | {8E30BB13-F263-4EAF-A17D-DB0906099AAC}.Release|x64.ActiveCfg = Release|x64 21 | {8E30BB13-F263-4EAF-A17D-DB0906099AAC}.Release|x64.Build.0 = Release|x64 22 | {8E30BB13-F263-4EAF-A17D-DB0906099AAC}.Release|x86.ActiveCfg = Release|Win32 23 | {8E30BB13-F263-4EAF-A17D-DB0906099AAC}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {73D095A9-52CC-4F6F-8AE0-578FD00C05D9} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /Manual Map Injector/DebuggerBypass.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RinOfficial0615/Manual-Map-Injector/143f8225cda3da8ae3735867fad1f26d59c9dc64/Manual Map Injector/DebuggerBypass.dll -------------------------------------------------------------------------------- /Manual Map Injector/Manual Map Injector.aps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RinOfficial0615/Manual-Map-Injector/143f8225cda3da8ae3735867fad1f26d59c9dc64/Manual Map Injector/Manual Map Injector.aps -------------------------------------------------------------------------------- /Manual Map Injector/Manual Map Injector.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RinOfficial0615/Manual-Map-Injector/143f8225cda3da8ae3735867fad1f26d59c9dc64/Manual Map Injector/Manual Map Injector.rc -------------------------------------------------------------------------------- /Manual Map Injector/Manual Map Injector.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {8e30bb13-f263-4eaf-a17d-db0906099aac} 25 | ManualMapInjector 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | false 75 | $(Platform)\$(Configuration)\ 76 | $(SolutionDir)$(Platform)\$(Configuration)\ 77 | Injector-x86 78 | 79 | 80 | false 81 | $(SolutionDir)$(Platform)\$(Configuration)\ 82 | $(Platform)\$(Configuration)\ 83 | Injector-x86 84 | 85 | 86 | false 87 | Injector-x64 88 | 89 | 90 | false 91 | Injector-x64 92 | 93 | 94 | 95 | Level3 96 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 97 | false 98 | ProgramDatabase 99 | false 100 | false 101 | stdcpp17 102 | 103 | 104 | Console 105 | true 106 | RequireAdministrator 107 | 108 | 109 | 110 | 111 | Level3 112 | true 113 | true 114 | true 115 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 116 | false 117 | stdcpp20 118 | 119 | 120 | Console 121 | true 122 | true 123 | true 124 | RequireAdministrator 125 | 126 | 127 | 128 | 129 | Level3 130 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 131 | false 132 | stdcpp17 133 | false 134 | false 135 | ProgramDatabase 136 | 137 | 138 | Console 139 | true 140 | RequireAdministrator 141 | 142 | 143 | 144 | 145 | Level3 146 | true 147 | true 148 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 149 | false 150 | stdcpp20 151 | false 152 | 153 | 154 | Console 155 | true 156 | true 157 | true 158 | RequireAdministrator 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /Manual Map Injector/Manual Map Injector.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 6 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 7 | 8 | 9 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 10 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 11 | 12 | 13 | 14 | 15 | 源文件 16 | 17 | 18 | 源文件 19 | 20 | 21 | 源文件 22 | 23 | 24 | 25 | 26 | 头文件 27 | 28 | 29 | 头文件 30 | 31 | 32 | 头文件 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Manual Map Injector/Manual Map Injector.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Manual Map Injector/ReleaseHelper.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RinOfficial0615/Manual-Map-Injector/143f8225cda3da8ae3735867fad1f26d59c9dc64/Manual Map Injector/ReleaseHelper.cpp -------------------------------------------------------------------------------- /Manual Map Injector/ReleaseHelper.h: -------------------------------------------------------------------------------- 1 | BOOL ReleaseLibrary(UINT uResourceId, CHAR *szResourceType, const char *szFileName); -------------------------------------------------------------------------------- /Manual Map Injector/includes/ConvertUTF.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2001-2004 Unicode, Inc. 3 | * 4 | * Disclaimer 5 | * 6 | * This source code is provided as is by Unicode, Inc. No claims are 7 | * made as to fitness for any particular purpose. No warranties of any 8 | * kind are expressed or implied. The recipient agrees to determine 9 | * applicability of information provided. If this file has been 10 | * purchased on magnetic or optical media from Unicode, Inc., the 11 | * sole remedy for any claim will be exchange of defective media 12 | * within 90 days of receipt. 13 | * 14 | * Limitations on Rights to Redistribute This Code 15 | * 16 | * Unicode, Inc. hereby grants the right to freely use the information 17 | * supplied in this file in the creation of products supporting the 18 | * Unicode Standard, and to make copies of this file in any form 19 | * for internal or external distribution as long as this notice 20 | * remains attached. 21 | */ 22 | 23 | /* --------------------------------------------------------------------- 24 | 25 | Conversions between UTF32, UTF-16, and UTF-8. Source code file. 26 | Author: Mark E. Davis, 1994. 27 | Rev History: Rick McGowan, fixes & updates May 2001. 28 | Sept 2001: fixed const & error conditions per 29 | mods suggested by S. Parent & A. Lillich. 30 | June 2002: Tim Dodd added detection and handling of incomplete 31 | source sequences, enhanced error detection, added casts 32 | to eliminate compiler warnings. 33 | July 2003: slight mods to back out aggressive FFFE detection. 34 | Jan 2004: updated switches in from-UTF8 conversions. 35 | Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions. 36 | 37 | See the header file "ConvertUTF.h" for complete documentation. 38 | 39 | ------------------------------------------------------------------------ */ 40 | 41 | 42 | #include "ConvertUTF.h" 43 | #ifdef CVTUTF_DEBUG 44 | #include 45 | #endif 46 | 47 | static const int halfShift = 10; /* used for shifting by 10 bits */ 48 | 49 | static const UTF32 halfBase = 0x0010000UL; 50 | static const UTF32 halfMask = 0x3FFUL; 51 | 52 | #define UNI_SUR_HIGH_START (UTF32)0xD800 53 | #define UNI_SUR_HIGH_END (UTF32)0xDBFF 54 | #define UNI_SUR_LOW_START (UTF32)0xDC00 55 | #define UNI_SUR_LOW_END (UTF32)0xDFFF 56 | #define false 0 57 | #define true 1 58 | 59 | /* --------------------------------------------------------------------- */ 60 | 61 | ConversionResult ConvertUTF32toUTF16 ( 62 | const UTF32** sourceStart, const UTF32* sourceEnd, 63 | UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { 64 | ConversionResult result = conversionOK; 65 | const UTF32* source = *sourceStart; 66 | UTF16* target = *targetStart; 67 | while (source < sourceEnd) { 68 | UTF32 ch; 69 | if (target >= targetEnd) { 70 | result = targetExhausted; break; 71 | } 72 | ch = *source++; 73 | if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ 74 | /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */ 75 | if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { 76 | if (flags == strictConversion) { 77 | --source; /* return to the illegal value itself */ 78 | result = sourceIllegal; 79 | break; 80 | } else { 81 | *target++ = UNI_REPLACEMENT_CHAR; 82 | } 83 | } else { 84 | *target++ = (UTF16)ch; /* normal case */ 85 | } 86 | } else if (ch > UNI_MAX_LEGAL_UTF32) { 87 | if (flags == strictConversion) { 88 | result = sourceIllegal; 89 | } else { 90 | *target++ = UNI_REPLACEMENT_CHAR; 91 | } 92 | } else { 93 | /* target is a character in range 0xFFFF - 0x10FFFF. */ 94 | if (target + 1 >= targetEnd) { 95 | --source; /* Back up source pointer! */ 96 | result = targetExhausted; break; 97 | } 98 | ch -= halfBase; 99 | *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); 100 | *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); 101 | } 102 | } 103 | *sourceStart = source; 104 | *targetStart = target; 105 | return result; 106 | } 107 | 108 | /* --------------------------------------------------------------------- */ 109 | 110 | ConversionResult ConvertUTF16toUTF32 ( 111 | const UTF16** sourceStart, const UTF16* sourceEnd, 112 | UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { 113 | ConversionResult result = conversionOK; 114 | const UTF16* source = *sourceStart; 115 | UTF32* target = *targetStart; 116 | UTF32 ch, ch2; 117 | while (source < sourceEnd) { 118 | const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ 119 | ch = *source++; 120 | /* If we have a surrogate pair, convert to UTF32 first. */ 121 | if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { 122 | /* If the 16 bits following the high surrogate are in the source buffer... */ 123 | if (source < sourceEnd) { 124 | ch2 = *source; 125 | /* If it's a low surrogate, convert to UTF32. */ 126 | if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { 127 | ch = ((ch - UNI_SUR_HIGH_START) << halfShift) 128 | + (ch2 - UNI_SUR_LOW_START) + halfBase; 129 | ++source; 130 | } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ 131 | --source; /* return to the illegal value itself */ 132 | result = sourceIllegal; 133 | break; 134 | } 135 | } else { /* We don't have the 16 bits following the high surrogate. */ 136 | --source; /* return to the high surrogate */ 137 | result = sourceExhausted; 138 | break; 139 | } 140 | } else if (flags == strictConversion) { 141 | /* UTF-16 surrogate values are illegal in UTF-32 */ 142 | if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { 143 | --source; /* return to the illegal value itself */ 144 | result = sourceIllegal; 145 | break; 146 | } 147 | } 148 | if (target >= targetEnd) { 149 | source = oldSource; /* Back up source pointer! */ 150 | result = targetExhausted; break; 151 | } 152 | *target++ = ch; 153 | } 154 | *sourceStart = source; 155 | *targetStart = target; 156 | #ifdef CVTUTF_DEBUG 157 | if (result == sourceIllegal) { 158 | fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2); 159 | fflush(stderr); 160 | } 161 | #endif 162 | return result; 163 | } 164 | 165 | /* --------------------------------------------------------------------- */ 166 | 167 | /* 168 | * Index into the table below with the first byte of a UTF-8 sequence to 169 | * get the number of trailing bytes that are supposed to follow it. 170 | * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is 171 | * left as-is for anyone who may want to do such conversion, which was 172 | * allowed in earlier algorithms. 173 | */ 174 | static const char trailingBytesForUTF8[256] = { 175 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 176 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 177 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 178 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 179 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 180 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 181 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 182 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 183 | }; 184 | 185 | /* 186 | * Magic values subtracted from a buffer value during UTF8 conversion. 187 | * This table contains as many values as there might be trailing bytes 188 | * in a UTF-8 sequence. 189 | */ 190 | static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 191 | 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; 192 | 193 | /* 194 | * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed 195 | * into the first byte, depending on how many bytes follow. There are 196 | * as many entries in this table as there are UTF-8 sequence types. 197 | * (I.e., one byte sequence, two byte... etc.). Remember that sequencs 198 | * for *legal* UTF-8 will be 4 or fewer bytes total. 199 | */ 200 | static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; 201 | 202 | /* --------------------------------------------------------------------- */ 203 | 204 | /* The interface converts a whole buffer to avoid function-call overhead. 205 | * Constants have been gathered. Loops & conditionals have been removed as 206 | * much as possible for efficiency, in favor of drop-through switches. 207 | * (See "Note A" at the bottom of the file for equivalent code.) 208 | * If your compiler supports it, the "isLegalUTF8" call can be turned 209 | * into an inline function. 210 | */ 211 | 212 | /* --------------------------------------------------------------------- */ 213 | 214 | ConversionResult ConvertUTF16toUTF8 ( 215 | const UTF16** sourceStart, const UTF16* sourceEnd, 216 | UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { 217 | ConversionResult result = conversionOK; 218 | const UTF16* source = *sourceStart; 219 | UTF8* target = *targetStart; 220 | while (source < sourceEnd) { 221 | UTF32 ch; 222 | unsigned short bytesToWrite = 0; 223 | const UTF32 byteMask = 0xBF; 224 | const UTF32 byteMark = 0x80; 225 | const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ 226 | ch = *source++; 227 | /* If we have a surrogate pair, convert to UTF32 first. */ 228 | if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { 229 | /* If the 16 bits following the high surrogate are in the source buffer... */ 230 | if (source < sourceEnd) { 231 | UTF32 ch2 = *source; 232 | /* If it's a low surrogate, convert to UTF32. */ 233 | if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { 234 | ch = ((ch - UNI_SUR_HIGH_START) << halfShift) 235 | + (ch2 - UNI_SUR_LOW_START) + halfBase; 236 | ++source; 237 | } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ 238 | --source; /* return to the illegal value itself */ 239 | result = sourceIllegal; 240 | break; 241 | } 242 | } else { /* We don't have the 16 bits following the high surrogate. */ 243 | --source; /* return to the high surrogate */ 244 | result = sourceExhausted; 245 | break; 246 | } 247 | } else if (flags == strictConversion) { 248 | /* UTF-16 surrogate values are illegal in UTF-32 */ 249 | if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { 250 | --source; /* return to the illegal value itself */ 251 | result = sourceIllegal; 252 | break; 253 | } 254 | } 255 | /* Figure out how many bytes the result will require */ 256 | if (ch < (UTF32)0x80) { bytesToWrite = 1; 257 | } else if (ch < (UTF32)0x800) { bytesToWrite = 2; 258 | } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; 259 | } else if (ch < (UTF32)0x110000) { bytesToWrite = 4; 260 | } else { bytesToWrite = 3; 261 | ch = UNI_REPLACEMENT_CHAR; 262 | } 263 | 264 | target += bytesToWrite; 265 | if (target > targetEnd) { 266 | source = oldSource; /* Back up source pointer! */ 267 | target -= bytesToWrite; result = targetExhausted; break; 268 | } 269 | switch (bytesToWrite) { /* note: everything falls through. */ 270 | case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; 271 | case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; 272 | case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; 273 | case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); 274 | } 275 | target += bytesToWrite; 276 | } 277 | *sourceStart = source; 278 | *targetStart = target; 279 | return result; 280 | } 281 | 282 | /* --------------------------------------------------------------------- */ 283 | 284 | /* 285 | * Utility routine to tell whether a sequence of bytes is legal UTF-8. 286 | * This must be called with the length pre-determined by the first byte. 287 | * If not calling this from ConvertUTF8to*, then the length can be set by: 288 | * length = trailingBytesForUTF8[*source]+1; 289 | * and the sequence is illegal right away if there aren't that many bytes 290 | * available. 291 | * If presented with a length > 4, this returns false. The Unicode 292 | * definition of UTF-8 goes up to 4-byte sequences. 293 | */ 294 | 295 | static Boolean isLegalUTF8(const UTF8 *source, int length) { 296 | UTF8 a; 297 | const UTF8 *srcptr = source+length; 298 | switch (length) { 299 | default: return false; 300 | /* Everything else falls through when "true"... */ 301 | case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; 302 | case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; 303 | case 2: if ((a = (*--srcptr)) > 0xBF) return false; 304 | 305 | switch (*source) { 306 | /* no fall-through in this inner switch */ 307 | case 0xE0: if (a < 0xA0) return false; break; 308 | case 0xED: if (a > 0x9F) return false; break; 309 | case 0xF0: if (a < 0x90) return false; break; 310 | case 0xF4: if (a > 0x8F) return false; break; 311 | default: if (a < 0x80) return false; 312 | } 313 | 314 | case 1: if (*source >= 0x80 && *source < 0xC2) return false; 315 | } 316 | if (*source > 0xF4) return false; 317 | return true; 318 | } 319 | 320 | /* --------------------------------------------------------------------- */ 321 | 322 | /* 323 | * Exported function to return whether a UTF-8 sequence is legal or not. 324 | * This is not used here; it's just exported. 325 | */ 326 | Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) { 327 | int length = trailingBytesForUTF8[*source]+1; 328 | if (source+length > sourceEnd) { 329 | return false; 330 | } 331 | return isLegalUTF8(source, length); 332 | } 333 | 334 | /* --------------------------------------------------------------------- */ 335 | 336 | ConversionResult ConvertUTF8toUTF16 ( 337 | const UTF8** sourceStart, const UTF8* sourceEnd, 338 | UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { 339 | ConversionResult result = conversionOK; 340 | const UTF8* source = *sourceStart; 341 | UTF16* target = *targetStart; 342 | while (source < sourceEnd) { 343 | UTF32 ch = 0; 344 | unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; 345 | if (source + extraBytesToRead >= sourceEnd) { 346 | result = sourceExhausted; break; 347 | } 348 | /* Do this check whether lenient or strict */ 349 | if (! isLegalUTF8(source, extraBytesToRead+1)) { 350 | result = sourceIllegal; 351 | break; 352 | } 353 | /* 354 | * The cases all fall through. See "Note A" below. 355 | */ 356 | switch (extraBytesToRead) { 357 | case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ 358 | case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ 359 | case 3: ch += *source++; ch <<= 6; 360 | case 2: ch += *source++; ch <<= 6; 361 | case 1: ch += *source++; ch <<= 6; 362 | case 0: ch += *source++; 363 | } 364 | ch -= offsetsFromUTF8[extraBytesToRead]; 365 | 366 | if (target >= targetEnd) { 367 | source -= (extraBytesToRead+1); /* Back up source pointer! */ 368 | result = targetExhausted; break; 369 | } 370 | if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ 371 | /* UTF-16 surrogate values are illegal in UTF-32 */ 372 | if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { 373 | if (flags == strictConversion) { 374 | source -= (extraBytesToRead+1); /* return to the illegal value itself */ 375 | result = sourceIllegal; 376 | break; 377 | } else { 378 | *target++ = UNI_REPLACEMENT_CHAR; 379 | } 380 | } else { 381 | *target++ = (UTF16)ch; /* normal case */ 382 | } 383 | } else if (ch > UNI_MAX_UTF16) { 384 | if (flags == strictConversion) { 385 | result = sourceIllegal; 386 | source -= (extraBytesToRead+1); /* return to the start */ 387 | break; /* Bail out; shouldn't continue */ 388 | } else { 389 | *target++ = UNI_REPLACEMENT_CHAR; 390 | } 391 | } else { 392 | /* target is a character in range 0xFFFF - 0x10FFFF. */ 393 | if (target + 1 >= targetEnd) { 394 | source -= (extraBytesToRead+1); /* Back up source pointer! */ 395 | result = targetExhausted; break; 396 | } 397 | ch -= halfBase; 398 | *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); 399 | *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); 400 | } 401 | } 402 | *sourceStart = source; 403 | *targetStart = target; 404 | return result; 405 | } 406 | 407 | /* --------------------------------------------------------------------- */ 408 | 409 | ConversionResult ConvertUTF32toUTF8 ( 410 | const UTF32** sourceStart, const UTF32* sourceEnd, 411 | UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { 412 | ConversionResult result = conversionOK; 413 | const UTF32* source = *sourceStart; 414 | UTF8* target = *targetStart; 415 | while (source < sourceEnd) { 416 | UTF32 ch; 417 | unsigned short bytesToWrite = 0; 418 | const UTF32 byteMask = 0xBF; 419 | const UTF32 byteMark = 0x80; 420 | ch = *source++; 421 | if (flags == strictConversion ) { 422 | /* UTF-16 surrogate values are illegal in UTF-32 */ 423 | if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { 424 | --source; /* return to the illegal value itself */ 425 | result = sourceIllegal; 426 | break; 427 | } 428 | } 429 | /* 430 | * Figure out how many bytes the result will require. Turn any 431 | * illegally large UTF32 things (> Plane 17) into replacement chars. 432 | */ 433 | if (ch < (UTF32)0x80) { bytesToWrite = 1; 434 | } else if (ch < (UTF32)0x800) { bytesToWrite = 2; 435 | } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; 436 | } else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4; 437 | } else { bytesToWrite = 3; 438 | ch = UNI_REPLACEMENT_CHAR; 439 | result = sourceIllegal; 440 | } 441 | 442 | target += bytesToWrite; 443 | if (target > targetEnd) { 444 | --source; /* Back up source pointer! */ 445 | target -= bytesToWrite; result = targetExhausted; break; 446 | } 447 | switch (bytesToWrite) { /* note: everything falls through. */ 448 | case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; 449 | case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; 450 | case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; 451 | case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]); 452 | } 453 | target += bytesToWrite; 454 | } 455 | *sourceStart = source; 456 | *targetStart = target; 457 | return result; 458 | } 459 | 460 | /* --------------------------------------------------------------------- */ 461 | 462 | ConversionResult ConvertUTF8toUTF32 ( 463 | const UTF8** sourceStart, const UTF8* sourceEnd, 464 | UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { 465 | ConversionResult result = conversionOK; 466 | const UTF8* source = *sourceStart; 467 | UTF32* target = *targetStart; 468 | while (source < sourceEnd) { 469 | UTF32 ch = 0; 470 | unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; 471 | if (source + extraBytesToRead >= sourceEnd) { 472 | result = sourceExhausted; break; 473 | } 474 | /* Do this check whether lenient or strict */ 475 | if (! isLegalUTF8(source, extraBytesToRead+1)) { 476 | result = sourceIllegal; 477 | break; 478 | } 479 | /* 480 | * The cases all fall through. See "Note A" below. 481 | */ 482 | switch (extraBytesToRead) { 483 | case 5: ch += *source++; ch <<= 6; 484 | case 4: ch += *source++; ch <<= 6; 485 | case 3: ch += *source++; ch <<= 6; 486 | case 2: ch += *source++; ch <<= 6; 487 | case 1: ch += *source++; ch <<= 6; 488 | case 0: ch += *source++; 489 | } 490 | ch -= offsetsFromUTF8[extraBytesToRead]; 491 | 492 | if (target >= targetEnd) { 493 | source -= (extraBytesToRead+1); /* Back up the source pointer! */ 494 | result = targetExhausted; break; 495 | } 496 | if (ch <= UNI_MAX_LEGAL_UTF32) { 497 | /* 498 | * UTF-16 surrogate values are illegal in UTF-32, and anything 499 | * over Plane 17 (> 0x10FFFF) is illegal. 500 | */ 501 | if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { 502 | if (flags == strictConversion) { 503 | source -= (extraBytesToRead+1); /* return to the illegal value itself */ 504 | result = sourceIllegal; 505 | break; 506 | } else { 507 | *target++ = UNI_REPLACEMENT_CHAR; 508 | } 509 | } else { 510 | *target++ = ch; 511 | } 512 | } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */ 513 | result = sourceIllegal; 514 | *target++ = UNI_REPLACEMENT_CHAR; 515 | } 516 | } 517 | *sourceStart = source; 518 | *targetStart = target; 519 | return result; 520 | } 521 | 522 | /* --------------------------------------------------------------------- 523 | 524 | Note A. 525 | The fall-through switches in UTF-8 reading code save a 526 | temp variable, some decrements & conditionals. The switches 527 | are equivalent to the following loop: 528 | { 529 | int tmpBytesToRead = extraBytesToRead+1; 530 | do { 531 | ch += *source++; 532 | --tmpBytesToRead; 533 | if (tmpBytesToRead) ch <<= 6; 534 | } while (tmpBytesToRead > 0); 535 | } 536 | In UTF-8 writing code, the switches on "bytesToWrite" are 537 | similarly unrolled loops. 538 | 539 | --------------------------------------------------------------------- */ 540 | -------------------------------------------------------------------------------- /Manual Map Injector/includes/ConvertUTF.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2001-2004 Unicode, Inc. 3 | * 4 | * Disclaimer 5 | * 6 | * This source code is provided as is by Unicode, Inc. No claims are 7 | * made as to fitness for any particular purpose. No warranties of any 8 | * kind are expressed or implied. The recipient agrees to determine 9 | * applicability of information provided. If this file has been 10 | * purchased on magnetic or optical media from Unicode, Inc., the 11 | * sole remedy for any claim will be exchange of defective media 12 | * within 90 days of receipt. 13 | * 14 | * Limitations on Rights to Redistribute This Code 15 | * 16 | * Unicode, Inc. hereby grants the right to freely use the information 17 | * supplied in this file in the creation of products supporting the 18 | * Unicode Standard, and to make copies of this file in any form 19 | * for internal or external distribution as long as this notice 20 | * remains attached. 21 | */ 22 | 23 | /* --------------------------------------------------------------------- 24 | 25 | Conversions between UTF32, UTF-16, and UTF-8. Header file. 26 | 27 | Several funtions are included here, forming a complete set of 28 | conversions between the three formats. UTF-7 is not included 29 | here, but is handled in a separate source file. 30 | 31 | Each of these routines takes pointers to input buffers and output 32 | buffers. The input buffers are const. 33 | 34 | Each routine converts the text between *sourceStart and sourceEnd, 35 | putting the result into the buffer between *targetStart and 36 | targetEnd. Note: the end pointers are *after* the last item: e.g. 37 | *(sourceEnd - 1) is the last item. 38 | 39 | The return result indicates whether the conversion was successful, 40 | and if not, whether the problem was in the source or target buffers. 41 | (Only the first encountered problem is indicated.) 42 | 43 | After the conversion, *sourceStart and *targetStart are both 44 | updated to point to the end of last text successfully converted in 45 | the respective buffers. 46 | 47 | Input parameters: 48 | sourceStart - pointer to a pointer to the source buffer. 49 | The contents of this are modified on return so that 50 | it points at the next thing to be converted. 51 | targetStart - similarly, pointer to pointer to the target buffer. 52 | sourceEnd, targetEnd - respectively pointers to the ends of the 53 | two buffers, for overflow checking only. 54 | 55 | These conversion functions take a ConversionFlags argument. When this 56 | flag is set to strict, both irregular sequences and isolated surrogates 57 | will cause an error. When the flag is set to lenient, both irregular 58 | sequences and isolated surrogates are converted. 59 | 60 | Whether the flag is strict or lenient, all illegal sequences will cause 61 | an error return. This includes sequences such as: , , 62 | or in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code 63 | must check for illegal sequences. 64 | 65 | When the flag is set to lenient, characters over 0x10FFFF are converted 66 | to the replacement character; otherwise (when the flag is set to strict) 67 | they constitute an error. 68 | 69 | Output parameters: 70 | The value "sourceIllegal" is returned from some routines if the input 71 | sequence is malformed. When "sourceIllegal" is returned, the source 72 | value will point to the illegal value that caused the problem. E.g., 73 | in UTF-8 when a sequence is malformed, it points to the start of the 74 | malformed sequence. 75 | 76 | Author: Mark E. Davis, 1994. 77 | Rev History: Rick McGowan, fixes & updates May 2001. 78 | Fixes & updates, Sept 2001. 79 | 80 | ------------------------------------------------------------------------ */ 81 | 82 | /* --------------------------------------------------------------------- 83 | The following 4 definitions are compiler-specific. 84 | The C standard does not guarantee that wchar_t has at least 85 | 16 bits, so wchar_t is no less portable than unsigned short! 86 | All should be unsigned values to avoid sign extension during 87 | bit mask & shift operations. 88 | ------------------------------------------------------------------------ */ 89 | 90 | typedef unsigned int UTF32; /* at least 32 bits */ 91 | typedef unsigned short UTF16; /* at least 16 bits */ 92 | typedef unsigned char UTF8; /* typically 8 bits */ 93 | typedef unsigned char Boolean; /* 0 or 1 */ 94 | 95 | /* Some fundamental constants */ 96 | #define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD 97 | #define UNI_MAX_BMP (UTF32)0x0000FFFF 98 | #define UNI_MAX_UTF16 (UTF32)0x0010FFFF 99 | #define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF 100 | #define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF 101 | 102 | typedef enum { 103 | conversionOK, /* conversion successful */ 104 | sourceExhausted, /* partial character in source, but hit end */ 105 | targetExhausted, /* insuff. room in target for conversion */ 106 | sourceIllegal /* source sequence is illegal/malformed */ 107 | } ConversionResult; 108 | 109 | typedef enum { 110 | strictConversion = 0, 111 | lenientConversion 112 | } ConversionFlags; 113 | 114 | /* This is for C++ and does no harm in C */ 115 | #ifdef __cplusplus 116 | extern "C" { 117 | #endif 118 | 119 | ConversionResult ConvertUTF8toUTF16 ( 120 | const UTF8** sourceStart, const UTF8* sourceEnd, 121 | UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); 122 | 123 | ConversionResult ConvertUTF16toUTF8 ( 124 | const UTF16** sourceStart, const UTF16* sourceEnd, 125 | UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); 126 | 127 | ConversionResult ConvertUTF8toUTF32 ( 128 | const UTF8** sourceStart, const UTF8* sourceEnd, 129 | UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); 130 | 131 | ConversionResult ConvertUTF32toUTF8 ( 132 | const UTF32** sourceStart, const UTF32* sourceEnd, 133 | UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); 134 | 135 | ConversionResult ConvertUTF16toUTF32 ( 136 | const UTF16** sourceStart, const UTF16* sourceEnd, 137 | UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); 138 | 139 | ConversionResult ConvertUTF32toUTF16 ( 140 | const UTF32** sourceStart, const UTF32* sourceEnd, 141 | UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); 142 | 143 | Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); 144 | 145 | #ifdef __cplusplus 146 | } 147 | #endif 148 | 149 | /* --------------------------------------------------------------------- */ 150 | -------------------------------------------------------------------------------- /Manual Map Injector/includes/SimpleIni.h: -------------------------------------------------------------------------------- 1 | /** @mainpage 2 | 3 | 4 |
Library SimpleIni 5 |
File SimpleIni.h 6 |
Author Brodie Thiesfield 7 |
Source https://github.com/brofield/simpleini 8 |
Version 4.19 9 |
10 | 11 | Jump to the @link CSimpleIniTempl CSimpleIni @endlink interface documentation. 12 | 13 | @section intro INTRODUCTION 14 | 15 | This component allows an INI-style configuration file to be used on both 16 | Windows and Linux/Unix. It is fast, simple and source code using this 17 | component will compile unchanged on either OS. 18 | 19 | 20 | @section features FEATURES 21 | 22 | - MIT Licence allows free use in all software (including GPL and commercial) 23 | - multi-platform (Windows CE/9x/NT..10/etc, Linux, MacOSX, Unix) 24 | - loading and saving of INI-style configuration files 25 | - configuration files can have any newline format on all platforms 26 | - liberal acceptance of file format 27 | - key/values with no section 28 | - removal of whitespace around sections, keys and values 29 | - support for multi-line values (values with embedded newline characters) 30 | - optional support for multiple keys with the same name 31 | - optional case-insensitive sections and keys (for ASCII characters only) 32 | - saves files with sections and keys in the same order as they were loaded 33 | - preserves comments on the file, section and keys where possible. 34 | - supports both char or wchar_t programming interfaces 35 | - supports both MBCS (system locale) and UTF-8 file encodings 36 | - system locale does not need to be UTF-8 on Linux/Unix to load UTF-8 file 37 | - support for non-ASCII characters in section, keys, values and comments 38 | - support for non-standard character types or file encodings 39 | via user-written converter classes 40 | - support for adding/modifying values programmatically 41 | - compiles cleanly in the following compilers: 42 | - Windows/VC6 (warning level 3) 43 | - Windows/VC.NET 2003 (warning level 4) 44 | - Windows/VC 2005 (warning level 4) 45 | - Windows/VC 2019 (warning level 4) 46 | - Linux/gcc (-Wall) 47 | 48 | 49 | @section usage USAGE SUMMARY 50 | 51 | -# Decide if you will be using utf8 or MBCS files, and working with the 52 | data in utf8, wchar_t or ICU chars. 53 | -# If you will only be using straight utf8 files and access the data via the 54 | char interface, then you do not need any conversion library and could define 55 | SI_NO_CONVERSION. Note that no conversion also means no validation of the data. 56 | If no converter is specified then the default converter is SI_CONVERT_GENERIC 57 | on Mac/Linux and SI_CONVERT_WIN32 on Windows. If you need widechar support on 58 | Mac/Linux then use either SI_CONVERT_GENERIC or SI_CONVERT_ICU. These are also 59 | supported on all platforms. 60 | -# Define the appropriate symbol for the converter you wish to use and 61 | include the SimpleIni.h header file. 62 | -# Declare an instance of the appropriate class. Note that the following 63 | definitions are just shortcuts for commonly used types. Other types 64 | (PRUnichar, unsigned short, unsigned char) are also possible. 65 | 66 |
Interface Case-sensitive Load UTF-8 Load MBCS Typedef 67 |
SI_NO_CONVERSION 68 |
char No Yes No CSimpleIniA 69 |
char Yes Yes No CSimpleIniCaseA 70 |
SI_CONVERT_GENERIC 71 |
char No Yes Yes #1 CSimpleIniA 72 |
char Yes Yes Yes CSimpleIniCaseA 73 |
wchar_t No Yes Yes CSimpleIniW 74 |
wchar_t Yes Yes Yes CSimpleIniCaseW 75 |
SI_CONVERT_WIN32 76 |
char No No #2 Yes CSimpleIniA 77 |
char Yes Yes Yes CSimpleIniCaseA 78 |
wchar_t No Yes Yes CSimpleIniW 79 |
wchar_t Yes Yes Yes CSimpleIniCaseW 80 |
SI_CONVERT_ICU 81 |
char No Yes Yes CSimpleIniA 82 |
char Yes Yes Yes CSimpleIniCaseA 83 |
UChar No Yes Yes CSimpleIniW 84 |
UChar Yes Yes Yes CSimpleIniCaseW 85 |
86 | #1 On Windows you are better to use CSimpleIniA with SI_CONVERT_WIN32.
87 | #2 Only affects Windows. On Windows this uses MBCS functions and 88 | so may fold case incorrectly leading to uncertain results. 89 | -# Call LoadData() or LoadFile() to load and parse the INI configuration file 90 | -# Access and modify the data of the file using the following functions 91 | 92 |
GetAllSections Return all section names 93 |
GetAllKeys Return all key names within a section 94 |
GetAllValues Return all values within a section & key 95 |
GetSection Return all key names and values in a section 96 |
GetSectionSize Return the number of keys in a section 97 |
GetValue Return a value for a section & key 98 |
SetValue Add or update a value for a section & key 99 |
Delete Remove a section, or a key from a section 100 |
SectionExists Does a section exist? 101 |
KeyExists Does a key exist? 102 |
103 | -# Call Save() or SaveFile() to save the INI configuration data 104 | 105 | @section iostreams IO STREAMS 106 | 107 | SimpleIni supports reading from and writing to STL IO streams. Enable this 108 | by defining SI_SUPPORT_IOSTREAMS before including the SimpleIni.h header 109 | file. Ensure that if the streams are backed by a file (e.g. ifstream or 110 | ofstream) then the flag ios_base::binary has been used when the file was 111 | opened. 112 | 113 | @section multiline MULTI-LINE VALUES 114 | 115 | Values that span multiple lines are created using the following format. 116 | 117 |
 118 |         key = <<
 122 | 
 123 |     Note the following:
 124 |     - The text used for ENDTAG can be anything and is used to find
 125 |       where the multi-line text ends.
 126 |     - The newline after ENDTAG in the start tag, and the newline
 127 |       before ENDTAG in the end tag is not included in the data value.
 128 |     - The ending tag must be on it's own line with no whitespace before
 129 |       or after it.
 130 |     - The multi-line value is modified at load so that each line in the value
 131 |       is delimited by a single '\\n' character on all platforms. At save time
 132 |       it will be converted into the newline format used by the current
 133 |       platform.
 134 | 
 135 |     @section comments COMMENTS
 136 | 
 137 |     Comments are preserved in the file within the following restrictions:
 138 |     - Every file may have a single "file comment". It must start with the
 139 |       first character in the file, and will end with the first non-comment
 140 |       line in the file.
 141 |     - Every section may have a single "section comment". It will start
 142 |       with the first comment line following the file comment, or the last
 143 |       data entry. It ends at the beginning of the section.
 144 |     - Every key may have a single "key comment". This comment will start
 145 |       with the first comment line following the section start, or the file
 146 |       comment if there is no section name.
 147 |     - Comments are set at the time that the file, section or key is first
 148 |       created. The only way to modify a comment on a section or a key is to
 149 |       delete that entry and recreate it with the new comment. There is no
 150 |       way to change the file comment.
 151 | 
 152 |     @section save SAVE ORDER
 153 | 
 154 |     The sections and keys are written out in the same order as they were
 155 |     read in from the file. Sections and keys added to the data after the
 156 |     file has been loaded will be added to the end of the file when it is
 157 |     written. There is no way to specify the location of a section or key
 158 |     other than in first-created, first-saved order.
 159 | 
 160 |     @section notes NOTES
 161 | 
 162 |     - To load UTF-8 data on Windows 95, you need to use Microsoft Layer for
 163 |       Unicode, or SI_CONVERT_GENERIC, or SI_CONVERT_ICU.
 164 |     - When using SI_CONVERT_GENERIC, ConvertUTF.c must be compiled and linked.
 165 |     - When using SI_CONVERT_ICU, ICU header files must be on the include
 166 |       path and icuuc.lib must be linked in.
 167 |     - To load a UTF-8 file on Windows AND expose it with SI_CHAR == char,
 168 |       you should use SI_CONVERT_GENERIC.
 169 |     - The collation (sorting) order used for sections and keys returned from
 170 |       iterators is NOT DEFINED. If collation order of the text is important
 171 |       then it should be done yourself by either supplying a replacement
 172 |       SI_STRLESS class, or by sorting the strings external to this library.
 173 |     - Usage of the  header on Windows can be disabled by defining
 174 |       SI_NO_MBCS. This is defined automatically on Windows CE platforms.
 175 |     - Not thread-safe so manage your own locking
 176 | 
 177 |     @section contrib CONTRIBUTIONS
 178 |     
 179 |     - 2010/05/03: Tobias Gehrig: added GetDoubleValue()
 180 | 
 181 |     @section licence MIT LICENCE
 182 | 
 183 |     The licence text below is the boilerplate "MIT Licence" used from:
 184 |     http://www.opensource.org/licenses/mit-license.php
 185 | 
 186 |     Copyright (c) 2006-2012, Brodie Thiesfield
 187 | 
 188 |     Permission is hereby granted, free of charge, to any person obtaining a copy
 189 |     of this software and associated documentation files (the "Software"), to deal
 190 |     in the Software without restriction, including without limitation the rights
 191 |     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 192 |     copies of the Software, and to permit persons to whom the Software is furnished
 193 |     to do so, subject to the following conditions:
 194 | 
 195 |     The above copyright notice and this permission notice shall be included in
 196 |     all copies or substantial portions of the Software.
 197 | 
 198 |     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 199 |     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 200 |     FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 201 |     COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 202 |     IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 203 |     CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 204 | */
 205 | 
 206 | #ifndef INCLUDED_SimpleIni_h
 207 | #define INCLUDED_SimpleIni_h
 208 | 
 209 | #if defined(_MSC_VER) && (_MSC_VER >= 1020)
 210 | # pragma once
 211 | #endif
 212 | 
 213 | // Disable these warnings in MSVC:
 214 | //  4127 "conditional expression is constant" as the conversion classes trigger
 215 | //  it with the statement if (sizeof(SI_CHAR) == sizeof(char)). This test will
 216 | //  be optimized away in a release build.
 217 | //  4503 'insert' : decorated name length exceeded, name was truncated
 218 | //  4702 "unreachable code" as the MS STL header causes it in release mode.
 219 | //  Again, the code causing the warning will be cleaned up by the compiler.
 220 | //  4786 "identifier truncated to 256 characters" as this is thrown hundreds
 221 | //  of times VC6 as soon as STL is used.
 222 | #ifdef _MSC_VER
 223 | # pragma warning (push)
 224 | # pragma warning (disable: 4127 4503 4702 4786)
 225 | #endif
 226 | 
 227 | #include 
 228 | #include 
 229 | #include 
 230 | #include 
 231 | #include 
 232 | #include 
 233 | #include 
 234 | 
 235 | #ifdef SI_SUPPORT_IOSTREAMS
 236 | # include 
 237 | #endif // SI_SUPPORT_IOSTREAMS
 238 | 
 239 | #ifdef _DEBUG
 240 | # ifndef assert
 241 | #  include 
 242 | # endif
 243 | # define SI_ASSERT(x)   assert(x)
 244 | #else
 245 | # define SI_ASSERT(x)
 246 | #endif
 247 | 
 248 | using SI_Error = int;
 249 | 
 250 | constexpr int SI_OK = 0;        //!< No error
 251 | constexpr int SI_UPDATED = 1;   //!< An existing value was updated
 252 | constexpr int SI_INSERTED = 2;  //!< A new value was inserted
 253 | 
 254 | // note: test for any error with (retval < 0)
 255 | constexpr int SI_FAIL = -1;     //!< Generic failure
 256 | constexpr int SI_NOMEM = -2;    //!< Out of memory error
 257 | constexpr int SI_FILE = -3;     //!< File error (see errno for detail error)
 258 | 
 259 | #define SI_UTF8_SIGNATURE     "\xEF\xBB\xBF"
 260 | 
 261 | #ifdef _WIN32
 262 | # define SI_NEWLINE_A   "\r\n"
 263 | # define SI_NEWLINE_W   L"\r\n"
 264 | #else // !_WIN32
 265 | # define SI_NEWLINE_A   "\n"
 266 | # define SI_NEWLINE_W   L"\n"
 267 | #endif // _WIN32
 268 | 
 269 | #if defined(SI_CONVERT_ICU)
 270 | # include 
 271 | #endif
 272 | 
 273 | #if defined(_WIN32)
 274 | # define SI_HAS_WIDE_FILE
 275 | # define SI_WCHAR_T     wchar_t
 276 | #elif defined(SI_CONVERT_ICU)
 277 | # define SI_HAS_WIDE_FILE
 278 | # define SI_WCHAR_T     UChar
 279 | #endif
 280 | 
 281 | 
 282 | // ---------------------------------------------------------------------------
 283 | //                              MAIN TEMPLATE CLASS
 284 | // ---------------------------------------------------------------------------
 285 | 
 286 | /** Simple INI file reader.
 287 | 
 288 |     This can be instantiated with the choice of unicode or native characterset,
 289 |     and case sensitive or insensitive comparisons of section and key names.
 290 |     The supported combinations are pre-defined with the following typedefs:
 291 | 
 292 |     
 293 |         
Interface Case-sensitive Typedef 294 |
char No CSimpleIniA 295 |
char Yes CSimpleIniCaseA 296 |
wchar_t No CSimpleIniW 297 |
wchar_t Yes CSimpleIniCaseW 298 |
299 | 300 | Note that using other types for the SI_CHAR is supported. For instance, 301 | unsigned char, unsigned short, etc. Note that where the alternative type 302 | is a different size to char/wchar_t you may need to supply new helper 303 | classes for SI_STRLESS and SI_CONVERTER. 304 | */ 305 | template 306 | class CSimpleIniTempl 307 | { 308 | public: 309 | typedef SI_CHAR SI_CHAR_T; 310 | 311 | /** key entry */ 312 | struct Entry { 313 | const SI_CHAR * pItem; 314 | const SI_CHAR * pComment; 315 | int nOrder; 316 | 317 | Entry(const SI_CHAR * a_pszItem = NULL, int a_nOrder = 0) 318 | : pItem(a_pszItem) 319 | , pComment(NULL) 320 | , nOrder(a_nOrder) 321 | { } 322 | Entry(const SI_CHAR * a_pszItem, const SI_CHAR * a_pszComment, int a_nOrder) 323 | : pItem(a_pszItem) 324 | , pComment(a_pszComment) 325 | , nOrder(a_nOrder) 326 | { } 327 | Entry(const Entry & rhs) { operator=(rhs); } 328 | Entry & operator=(const Entry & rhs) { 329 | pItem = rhs.pItem; 330 | pComment = rhs.pComment; 331 | nOrder = rhs.nOrder; 332 | return *this; 333 | } 334 | 335 | #if defined(_MSC_VER) && _MSC_VER <= 1200 336 | /** STL of VC6 doesn't allow me to specify my own comparator for list::sort() */ 337 | bool operator<(const Entry & rhs) const { return LoadOrder()(*this, rhs); } 338 | bool operator>(const Entry & rhs) const { return LoadOrder()(rhs, *this); } 339 | #endif 340 | 341 | /** Strict less ordering by name of key only */ 342 | struct KeyOrder { 343 | bool operator()(const Entry & lhs, const Entry & rhs) const { 344 | const static SI_STRLESS isLess = SI_STRLESS(); 345 | return isLess(lhs.pItem, rhs.pItem); 346 | } 347 | }; 348 | 349 | /** Strict less ordering by order, and then name of key */ 350 | struct LoadOrder { 351 | bool operator()(const Entry & lhs, const Entry & rhs) const { 352 | if (lhs.nOrder != rhs.nOrder) { 353 | return lhs.nOrder < rhs.nOrder; 354 | } 355 | return KeyOrder()(lhs.pItem, rhs.pItem); 356 | } 357 | }; 358 | }; 359 | 360 | /** map keys to values */ 361 | typedef std::multimap TKeyVal; 362 | 363 | /** map sections to key/value map */ 364 | typedef std::map TSection; 365 | 366 | /** set of dependent string pointers. Note that these pointers are 367 | dependent on memory owned by CSimpleIni. 368 | */ 369 | typedef std::list TNamesDepend; 370 | 371 | /** interface definition for the OutputWriter object to pass to Save() 372 | in order to output the INI file data. 373 | */ 374 | class OutputWriter { 375 | public: 376 | OutputWriter() { } 377 | virtual ~OutputWriter() { } 378 | virtual void Write(const char * a_pBuf) = 0; 379 | private: 380 | OutputWriter(const OutputWriter &); // disable 381 | OutputWriter & operator=(const OutputWriter &); // disable 382 | }; 383 | 384 | /** OutputWriter class to write the INI data to a file */ 385 | class FileWriter : public OutputWriter { 386 | FILE * m_file; 387 | public: 388 | FileWriter(FILE * a_file) : m_file(a_file) { } 389 | void Write(const char * a_pBuf) { 390 | fputs(a_pBuf, m_file); 391 | } 392 | private: 393 | FileWriter(const FileWriter &); // disable 394 | FileWriter & operator=(const FileWriter &); // disable 395 | }; 396 | 397 | /** OutputWriter class to write the INI data to a string */ 398 | class StringWriter : public OutputWriter { 399 | std::string & m_string; 400 | public: 401 | StringWriter(std::string & a_string) : m_string(a_string) { } 402 | void Write(const char * a_pBuf) { 403 | m_string.append(a_pBuf); 404 | } 405 | private: 406 | StringWriter(const StringWriter &); // disable 407 | StringWriter & operator=(const StringWriter &); // disable 408 | }; 409 | 410 | #ifdef SI_SUPPORT_IOSTREAMS 411 | /** OutputWriter class to write the INI data to an ostream */ 412 | class StreamWriter : public OutputWriter { 413 | std::ostream & m_ostream; 414 | public: 415 | StreamWriter(std::ostream & a_ostream) : m_ostream(a_ostream) { } 416 | void Write(const char * a_pBuf) { 417 | m_ostream << a_pBuf; 418 | } 419 | private: 420 | StreamWriter(const StreamWriter &); // disable 421 | StreamWriter & operator=(const StreamWriter &); // disable 422 | }; 423 | #endif // SI_SUPPORT_IOSTREAMS 424 | 425 | /** Characterset conversion utility class to convert strings to the 426 | same format as is used for the storage. 427 | */ 428 | class Converter : private SI_CONVERTER { 429 | public: 430 | Converter(bool a_bStoreIsUtf8) : SI_CONVERTER(a_bStoreIsUtf8) { 431 | m_scratch.resize(1024); 432 | } 433 | Converter(const Converter & rhs) { operator=(rhs); } 434 | Converter & operator=(const Converter & rhs) { 435 | m_scratch = rhs.m_scratch; 436 | return *this; 437 | } 438 | bool ConvertToStore(const SI_CHAR * a_pszString) { 439 | size_t uLen = SI_CONVERTER::SizeToStore(a_pszString); 440 | if (uLen == (size_t)(-1)) { 441 | return false; 442 | } 443 | while (uLen > m_scratch.size()) { 444 | m_scratch.resize(m_scratch.size() * 2); 445 | } 446 | return SI_CONVERTER::ConvertToStore( 447 | a_pszString, 448 | const_cast(m_scratch.data()), 449 | m_scratch.size()); 450 | } 451 | const char * Data() { return m_scratch.data(); } 452 | private: 453 | std::string m_scratch; 454 | }; 455 | 456 | public: 457 | /*-----------------------------------------------------------------------*/ 458 | 459 | /** Default constructor. 460 | 461 | @param a_bIsUtf8 See the method SetUnicode() for details. 462 | @param a_bMultiKey See the method SetMultiKey() for details. 463 | @param a_bMultiLine See the method SetMultiLine() for details. 464 | */ 465 | CSimpleIniTempl( 466 | bool a_bIsUtf8 = false, 467 | bool a_bMultiKey = false, 468 | bool a_bMultiLine = false 469 | ); 470 | 471 | /** Destructor */ 472 | ~CSimpleIniTempl(); 473 | 474 | /** Deallocate all memory stored by this object */ 475 | void Reset(); 476 | 477 | /** Has any data been loaded */ 478 | bool IsEmpty() const { return m_data.empty(); } 479 | 480 | /*-----------------------------------------------------------------------*/ 481 | /** @{ @name Settings */ 482 | 483 | /** Set the storage format of the INI data. This affects both the loading 484 | and saving of the INI data using all of the Load/Save API functions. 485 | This value cannot be changed after any INI data has been loaded. 486 | 487 | If the file is not set to Unicode (UTF-8), then the data encoding is 488 | assumed to be the OS native encoding. This encoding is the system 489 | locale on Linux/Unix and the legacy MBCS encoding on Windows NT/2K/XP. 490 | If the storage format is set to Unicode then the file will be loaded 491 | as UTF-8 encoded data regardless of the native file encoding. If 492 | SI_CHAR == char then all of the char* parameters take and return UTF-8 493 | encoded data regardless of the system locale. 494 | 495 | \param a_bIsUtf8 Assume UTF-8 encoding for the source? 496 | */ 497 | void SetUnicode(bool a_bIsUtf8 = true) { 498 | if (!m_pData) m_bStoreIsUtf8 = a_bIsUtf8; 499 | } 500 | 501 | /** Get the storage format of the INI data. */ 502 | bool IsUnicode() const { return m_bStoreIsUtf8; } 503 | 504 | /** Should multiple identical keys be permitted in the file. If set to false 505 | then the last value encountered will be used as the value of the key. 506 | If set to true, then all values will be available to be queried. For 507 | example, with the following input: 508 | 509 |
 510 |         [section]
 511 |         test=value1
 512 |         test=value2
 513 |         
514 | 515 | Then with SetMultiKey(true), both of the values "value1" and "value2" 516 | will be returned for the key test. If SetMultiKey(false) is used, then 517 | the value for "test" will only be "value2". This value may be changed 518 | at any time. 519 | 520 | \param a_bAllowMultiKey Allow multi-keys in the source? 521 | */ 522 | void SetMultiKey(bool a_bAllowMultiKey = true) { 523 | m_bAllowMultiKey = a_bAllowMultiKey; 524 | } 525 | 526 | /** Get the storage format of the INI data. */ 527 | bool IsMultiKey() const { return m_bAllowMultiKey; } 528 | 529 | /** Should data values be permitted to span multiple lines in the file. If 530 | set to false then the multi-line construct << 717 | SI_CHAR FORMAT 718 | char same format as when loaded (MBCS or UTF-8) 719 | wchar_t UTF-8 720 | other UTF-8 721 | 722 | 723 | Note that comments from the original data is preserved as per the 724 | documentation on comments. The order of the sections and values 725 | from the original file will be preserved. 726 | 727 | Any data prepended or appended to the output device must use the the 728 | same format (MBCS or UTF-8). You may use the GetConverter() method to 729 | convert text to the correct format regardless of the output format 730 | being used by SimpleIni. 731 | 732 | To add a BOM to UTF-8 data, write it out manually at the very beginning 733 | like is done in SaveFile when a_bUseBOM is true. 734 | 735 | @param a_oOutput Output writer to write the data to. 736 | 737 | @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in 738 | UTF-8 format. If it is not UTF-8 then this value is 739 | ignored. Do not set this to true if anything has 740 | already been written to the OutputWriter. 741 | 742 | @return SI_Error See error definitions 743 | */ 744 | SI_Error Save( 745 | OutputWriter & a_oOutput, 746 | bool a_bAddSignature = false 747 | ) const; 748 | 749 | #ifdef SI_SUPPORT_IOSTREAMS 750 | /** Save the INI data to an ostream. See Save() for details. 751 | 752 | @param a_ostream String to have the INI data appended to. 753 | 754 | @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in 755 | UTF-8 format. If it is not UTF-8 then this value is 756 | ignored. Do not set this to true if anything has 757 | already been written to the stream. 758 | 759 | @return SI_Error See error definitions 760 | */ 761 | SI_Error Save( 762 | std::ostream & a_ostream, 763 | bool a_bAddSignature = false 764 | ) const 765 | { 766 | StreamWriter writer(a_ostream); 767 | return Save(writer, a_bAddSignature); 768 | } 769 | #endif // SI_SUPPORT_IOSTREAMS 770 | 771 | /** Append the INI data to a string. See Save() for details. 772 | 773 | @param a_sBuffer String to have the INI data appended to. 774 | 775 | @param a_bAddSignature Prepend the UTF-8 BOM if the output data is in 776 | UTF-8 format. If it is not UTF-8 then this value is 777 | ignored. Do not set this to true if anything has 778 | already been written to the string. 779 | 780 | @return SI_Error See error definitions 781 | */ 782 | SI_Error Save( 783 | std::string & a_sBuffer, 784 | bool a_bAddSignature = false 785 | ) const 786 | { 787 | StringWriter writer(a_sBuffer); 788 | return Save(writer, a_bAddSignature); 789 | } 790 | 791 | /*-----------------------------------------------------------------------*/ 792 | /** @} 793 | @{ @name Accessing INI Data */ 794 | 795 | /** Retrieve all section names. The list is returned as an STL vector of 796 | names and can be iterated or searched as necessary. Note that the 797 | sort order of the returned strings is NOT DEFINED. You can sort 798 | the names into the load order if desired. Search this file for ".sort" 799 | for an example. 800 | 801 | NOTE! This structure contains only pointers to strings. The actual 802 | string data is stored in memory owned by CSimpleIni. Ensure that the 803 | CSimpleIni object is not destroyed or Reset() while these pointers 804 | are in use! 805 | 806 | @param a_names Vector that will receive all of the section 807 | names. See note above! 808 | */ 809 | void GetAllSections( 810 | TNamesDepend & a_names 811 | ) const; 812 | 813 | /** Retrieve all unique key names in a section. The sort order of the 814 | returned strings is NOT DEFINED. You can sort the names into the load 815 | order if desired. Search this file for ".sort" for an example. Only 816 | unique key names are returned. 817 | 818 | NOTE! This structure contains only pointers to strings. The actual 819 | string data is stored in memory owned by CSimpleIni. Ensure that the 820 | CSimpleIni object is not destroyed or Reset() while these strings 821 | are in use! 822 | 823 | @param a_pSection Section to request data for 824 | @param a_names List that will receive all of the key 825 | names. See note above! 826 | 827 | @return true Section was found. 828 | @return false Matching section was not found. 829 | */ 830 | bool GetAllKeys( 831 | const SI_CHAR * a_pSection, 832 | TNamesDepend & a_names 833 | ) const; 834 | 835 | /** Retrieve all values for a specific key. This method can be used when 836 | multiple keys are both enabled and disabled. Note that the sort order 837 | of the returned strings is NOT DEFINED. You can sort the names into 838 | the load order if desired. Search this file for ".sort" for an example. 839 | 840 | NOTE! The returned values are pointers to string data stored in memory 841 | owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed 842 | or Reset while you are using this pointer! 843 | 844 | @param a_pSection Section to search 845 | @param a_pKey Key to search for 846 | @param a_values List to return if the key is not found 847 | 848 | @return true Key was found. 849 | @return false Matching section/key was not found. 850 | */ 851 | bool GetAllValues( 852 | const SI_CHAR * a_pSection, 853 | const SI_CHAR * a_pKey, 854 | TNamesDepend & a_values 855 | ) const; 856 | 857 | /** Query the number of keys in a specific section. Note that if multiple 858 | keys are enabled, then this value may be different to the number of 859 | keys returned by GetAllKeys. 860 | 861 | @param a_pSection Section to request data for 862 | 863 | @return -1 Section does not exist in the file 864 | @return >=0 Number of keys in the section 865 | */ 866 | int GetSectionSize( 867 | const SI_CHAR * a_pSection 868 | ) const; 869 | 870 | /** Retrieve all key and value pairs for a section. The data is returned 871 | as a pointer to an STL map and can be iterated or searched as 872 | desired. Note that multiple entries for the same key may exist when 873 | multiple keys have been enabled. 874 | 875 | NOTE! This structure contains only pointers to strings. The actual 876 | string data is stored in memory owned by CSimpleIni. Ensure that the 877 | CSimpleIni object is not destroyed or Reset() while these strings 878 | are in use! 879 | 880 | @param a_pSection Name of the section to return 881 | @return Section data 882 | */ 883 | const TKeyVal * GetSection( 884 | const SI_CHAR * a_pSection 885 | ) const; 886 | 887 | /** Test if a section exists. Convenience function */ 888 | inline const bool SectionExists( 889 | const SI_CHAR * a_pSection 890 | ) const { 891 | return GetSection(a_pSection) != NULL; 892 | } 893 | 894 | /** Test if the key exists in a section. Convenience function. */ 895 | inline const bool KeyExists( 896 | const SI_CHAR * a_pSection, 897 | const SI_CHAR * a_pKey 898 | ) const { 899 | return GetValue(a_pSection, a_pKey) != NULL; 900 | } 901 | 902 | /** Retrieve the value for a specific key. If multiple keys are enabled 903 | (see SetMultiKey) then only the first value associated with that key 904 | will be returned, see GetAllValues for getting all values with multikey. 905 | 906 | NOTE! The returned value is a pointer to string data stored in memory 907 | owned by CSimpleIni. Ensure that the CSimpleIni object is not destroyed 908 | or Reset while you are using this pointer! 909 | 910 | @param a_pSection Section to search 911 | @param a_pKey Key to search for 912 | @param a_pDefault Value to return if the key is not found 913 | @param a_pHasMultiple Optionally receive notification of if there are 914 | multiple entries for this key. 915 | 916 | @return a_pDefault Key was not found in the section 917 | @return other Value of the key 918 | */ 919 | const SI_CHAR * GetValue( 920 | const SI_CHAR * a_pSection, 921 | const SI_CHAR * a_pKey, 922 | const SI_CHAR * a_pDefault = NULL, 923 | bool * a_pHasMultiple = NULL 924 | ) const; 925 | 926 | /** Retrieve a numeric value for a specific key. If multiple keys are enabled 927 | (see SetMultiKey) then only the first value associated with that key 928 | will be returned, see GetAllValues for getting all values with multikey. 929 | 930 | @param a_pSection Section to search 931 | @param a_pKey Key to search for 932 | @param a_nDefault Value to return if the key is not found 933 | @param a_pHasMultiple Optionally receive notification of if there are 934 | multiple entries for this key. 935 | 936 | @return a_nDefault Key was not found in the section 937 | @return other Value of the key 938 | */ 939 | long GetLongValue( 940 | const SI_CHAR * a_pSection, 941 | const SI_CHAR * a_pKey, 942 | long a_nDefault = 0, 943 | bool * a_pHasMultiple = NULL 944 | ) const; 945 | 946 | /** Retrieve a numeric value for a specific key. If multiple keys are enabled 947 | (see SetMultiKey) then only the first value associated with that key 948 | will be returned, see GetAllValues for getting all values with multikey. 949 | 950 | @param a_pSection Section to search 951 | @param a_pKey Key to search for 952 | @param a_nDefault Value to return if the key is not found 953 | @param a_pHasMultiple Optionally receive notification of if there are 954 | multiple entries for this key. 955 | 956 | @return a_nDefault Key was not found in the section 957 | @return other Value of the key 958 | */ 959 | double GetDoubleValue( 960 | const SI_CHAR * a_pSection, 961 | const SI_CHAR * a_pKey, 962 | double a_nDefault = 0, 963 | bool * a_pHasMultiple = NULL 964 | ) const; 965 | 966 | /** Retrieve a boolean value for a specific key. If multiple keys are enabled 967 | (see SetMultiKey) then only the first value associated with that key 968 | will be returned, see GetAllValues for getting all values with multikey. 969 | 970 | Strings starting with "t", "y", "on" or "1" are returned as logically true. 971 | Strings starting with "f", "n", "of" or "0" are returned as logically false. 972 | For all other values the default is returned. Character comparisons are 973 | case-insensitive. 974 | 975 | @param a_pSection Section to search 976 | @param a_pKey Key to search for 977 | @param a_bDefault Value to return if the key is not found 978 | @param a_pHasMultiple Optionally receive notification of if there are 979 | multiple entries for this key. 980 | 981 | @return a_nDefault Key was not found in the section 982 | @return other Value of the key 983 | */ 984 | bool GetBoolValue( 985 | const SI_CHAR * a_pSection, 986 | const SI_CHAR * a_pKey, 987 | bool a_bDefault = false, 988 | bool * a_pHasMultiple = NULL 989 | ) const; 990 | 991 | /** Add or update a section or value. This will always insert 992 | when multiple keys are enabled. 993 | 994 | @param a_pSection Section to add or update 995 | @param a_pKey Key to add or update. Set to NULL to 996 | create an empty section. 997 | @param a_pValue Value to set. Set to NULL to create an 998 | empty section. 999 | @param a_pComment Comment to be associated with the section or the 1000 | key. If a_pKey is NULL then it will be associated 1001 | with the section, otherwise the key. Note that a 1002 | comment may be set ONLY when the section or key is 1003 | first created (i.e. when this function returns the 1004 | value SI_INSERTED). If you wish to create a section 1005 | with a comment then you need to create the section 1006 | separately to the key. The comment string must be 1007 | in full comment form already (have a comment 1008 | character starting every line). 1009 | @param a_bForceReplace Should all existing values in a multi-key INI 1010 | file be replaced with this entry. This option has 1011 | no effect if not using multi-key files. The 1012 | difference between Delete/SetValue and SetValue 1013 | with a_bForceReplace = true, is that the load 1014 | order and comment will be preserved this way. 1015 | 1016 | @return SI_Error See error definitions 1017 | @return SI_UPDATED Value was updated 1018 | @return SI_INSERTED Value was inserted 1019 | */ 1020 | SI_Error SetValue( 1021 | const SI_CHAR * a_pSection, 1022 | const SI_CHAR * a_pKey, 1023 | const SI_CHAR * a_pValue, 1024 | const SI_CHAR * a_pComment = NULL, 1025 | bool a_bForceReplace = false 1026 | ) 1027 | { 1028 | return AddEntry(a_pSection, a_pKey, a_pValue, a_pComment, a_bForceReplace, true); 1029 | } 1030 | 1031 | /** Add or update a numeric value. This will always insert 1032 | when multiple keys are enabled. 1033 | 1034 | @param a_pSection Section to add or update 1035 | @param a_pKey Key to add or update. 1036 | @param a_nValue Value to set. 1037 | @param a_pComment Comment to be associated with the key. See the 1038 | notes on SetValue() for comments. 1039 | @param a_bUseHex By default the value will be written to the file 1040 | in decimal format. Set this to true to write it 1041 | as hexadecimal. 1042 | @param a_bForceReplace Should all existing values in a multi-key INI 1043 | file be replaced with this entry. This option has 1044 | no effect if not using multi-key files. The 1045 | difference between Delete/SetLongValue and 1046 | SetLongValue with a_bForceReplace = true, is that 1047 | the load order and comment will be preserved this 1048 | way. 1049 | 1050 | @return SI_Error See error definitions 1051 | @return SI_UPDATED Value was updated 1052 | @return SI_INSERTED Value was inserted 1053 | */ 1054 | SI_Error SetLongValue( 1055 | const SI_CHAR * a_pSection, 1056 | const SI_CHAR * a_pKey, 1057 | long a_nValue, 1058 | const SI_CHAR * a_pComment = NULL, 1059 | bool a_bUseHex = false, 1060 | bool a_bForceReplace = false 1061 | ); 1062 | 1063 | /** Add or update a double value. This will always insert 1064 | when multiple keys are enabled. 1065 | 1066 | @param a_pSection Section to add or update 1067 | @param a_pKey Key to add or update. 1068 | @param a_nValue Value to set. 1069 | @param a_pComment Comment to be associated with the key. See the 1070 | notes on SetValue() for comments. 1071 | @param a_bForceReplace Should all existing values in a multi-key INI 1072 | file be replaced with this entry. This option has 1073 | no effect if not using multi-key files. The 1074 | difference between Delete/SetDoubleValue and 1075 | SetDoubleValue with a_bForceReplace = true, is that 1076 | the load order and comment will be preserved this 1077 | way. 1078 | 1079 | @return SI_Error See error definitions 1080 | @return SI_UPDATED Value was updated 1081 | @return SI_INSERTED Value was inserted 1082 | */ 1083 | SI_Error SetDoubleValue( 1084 | const SI_CHAR * a_pSection, 1085 | const SI_CHAR * a_pKey, 1086 | double a_nValue, 1087 | const SI_CHAR * a_pComment = NULL, 1088 | bool a_bForceReplace = false 1089 | ); 1090 | 1091 | /** Add or update a boolean value. This will always insert 1092 | when multiple keys are enabled. 1093 | 1094 | @param a_pSection Section to add or update 1095 | @param a_pKey Key to add or update. 1096 | @param a_bValue Value to set. 1097 | @param a_pComment Comment to be associated with the key. See the 1098 | notes on SetValue() for comments. 1099 | @param a_bForceReplace Should all existing values in a multi-key INI 1100 | file be replaced with this entry. This option has 1101 | no effect if not using multi-key files. The 1102 | difference between Delete/SetBoolValue and 1103 | SetBoolValue with a_bForceReplace = true, is that 1104 | the load order and comment will be preserved this 1105 | way. 1106 | 1107 | @return SI_Error See error definitions 1108 | @return SI_UPDATED Value was updated 1109 | @return SI_INSERTED Value was inserted 1110 | */ 1111 | SI_Error SetBoolValue( 1112 | const SI_CHAR * a_pSection, 1113 | const SI_CHAR * a_pKey, 1114 | bool a_bValue, 1115 | const SI_CHAR * a_pComment = NULL, 1116 | bool a_bForceReplace = false 1117 | ); 1118 | 1119 | /** Delete an entire section, or a key from a section. Note that the 1120 | data returned by GetSection is invalid and must not be used after 1121 | anything has been deleted from that section using this method. 1122 | Note when multiple keys is enabled, this will delete all keys with 1123 | that name; to selectively delete individual key/values, use 1124 | DeleteValue. 1125 | 1126 | @param a_pSection Section to delete key from, or if 1127 | a_pKey is NULL, the section to remove. 1128 | @param a_pKey Key to remove from the section. Set to 1129 | NULL to remove the entire section. 1130 | @param a_bRemoveEmpty If the section is empty after this key has 1131 | been deleted, should the empty section be 1132 | removed? 1133 | 1134 | @return true Key or section was deleted. 1135 | @return false Key or section was not found. 1136 | */ 1137 | bool Delete( 1138 | const SI_CHAR * a_pSection, 1139 | const SI_CHAR * a_pKey, 1140 | bool a_bRemoveEmpty = false 1141 | ); 1142 | 1143 | /** Delete an entire section, or a key from a section. If value is 1144 | provided, only remove keys with the value. Note that the data 1145 | returned by GetSection is invalid and must not be used after 1146 | anything has been deleted from that section using this method. 1147 | Note when multiple keys is enabled, all keys with the value will 1148 | be deleted. 1149 | 1150 | @param a_pSection Section to delete key from, or if 1151 | a_pKey is NULL, the section to remove. 1152 | @param a_pKey Key to remove from the section. Set to 1153 | NULL to remove the entire section. 1154 | @param a_pValue Value of key to remove from the section. 1155 | Set to NULL to remove all keys. 1156 | @param a_bRemoveEmpty If the section is empty after this key has 1157 | been deleted, should the empty section be 1158 | removed? 1159 | 1160 | @return true Key/value or section was deleted. 1161 | @return false Key/value or section was not found. 1162 | */ 1163 | bool DeleteValue( 1164 | const SI_CHAR * a_pSection, 1165 | const SI_CHAR * a_pKey, 1166 | const SI_CHAR * a_pValue, 1167 | bool a_bRemoveEmpty = false 1168 | ); 1169 | 1170 | /*-----------------------------------------------------------------------*/ 1171 | /** @} 1172 | @{ @name Converter */ 1173 | 1174 | /** Return a conversion object to convert text to the same encoding 1175 | as is used by the Save(), SaveFile() and SaveString() functions. 1176 | Use this to prepare the strings that you wish to append or prepend 1177 | to the output INI data. 1178 | */ 1179 | Converter GetConverter() const { 1180 | return Converter(m_bStoreIsUtf8); 1181 | } 1182 | 1183 | /*-----------------------------------------------------------------------*/ 1184 | /** @} */ 1185 | 1186 | private: 1187 | // copying is not permitted 1188 | CSimpleIniTempl(const CSimpleIniTempl &); // disabled 1189 | CSimpleIniTempl & operator=(const CSimpleIniTempl &); // disabled 1190 | 1191 | /** Parse the data looking for a file comment and store it if found. 1192 | */ 1193 | SI_Error FindFileComment( 1194 | SI_CHAR *& a_pData, 1195 | bool a_bCopyStrings 1196 | ); 1197 | 1198 | /** Parse the data looking for the next valid entry. The memory pointed to 1199 | by a_pData is modified by inserting NULL characters. The pointer is 1200 | updated to the current location in the block of text. 1201 | */ 1202 | bool FindEntry( 1203 | SI_CHAR *& a_pData, 1204 | const SI_CHAR *& a_pSection, 1205 | const SI_CHAR *& a_pKey, 1206 | const SI_CHAR *& a_pVal, 1207 | const SI_CHAR *& a_pComment 1208 | ) const; 1209 | 1210 | /** Add the section/key/value to our data. 1211 | 1212 | @param a_pSection Section name. Sections will be created if they 1213 | don't already exist. 1214 | @param a_pKey Key name. May be NULL to create an empty section. 1215 | Existing entries will be updated. New entries will 1216 | be created. 1217 | @param a_pValue Value for the key. 1218 | @param a_pComment Comment to be associated with the section or the 1219 | key. If a_pKey is NULL then it will be associated 1220 | with the section, otherwise the key. This must be 1221 | a string in full comment form already (have a 1222 | comment character starting every line). 1223 | @param a_bForceReplace Should all existing values in a multi-key INI 1224 | file be replaced with this entry. This option has 1225 | no effect if not using multi-key files. The 1226 | difference between Delete/AddEntry and AddEntry 1227 | with a_bForceReplace = true, is that the load 1228 | order and comment will be preserved this way. 1229 | @param a_bCopyStrings Should copies of the strings be made or not. 1230 | If false then the pointers will be used as is. 1231 | */ 1232 | SI_Error AddEntry( 1233 | const SI_CHAR * a_pSection, 1234 | const SI_CHAR * a_pKey, 1235 | const SI_CHAR * a_pValue, 1236 | const SI_CHAR * a_pComment, 1237 | bool a_bForceReplace, 1238 | bool a_bCopyStrings 1239 | ); 1240 | 1241 | /** Is the supplied character a whitespace character? */ 1242 | inline bool IsSpace(SI_CHAR ch) const { 1243 | return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'); 1244 | } 1245 | 1246 | /** Does the supplied character start a comment line? */ 1247 | inline bool IsComment(SI_CHAR ch) const { 1248 | return (ch == ';' || ch == '#'); 1249 | } 1250 | 1251 | 1252 | /** Skip over a newline character (or characters) for either DOS or UNIX */ 1253 | inline void SkipNewLine(SI_CHAR *& a_pData) const { 1254 | a_pData += (*a_pData == '\r' && *(a_pData+1) == '\n') ? 2 : 1; 1255 | } 1256 | 1257 | /** Make a copy of the supplied string, replacing the original pointer */ 1258 | SI_Error CopyString(const SI_CHAR *& a_pString); 1259 | 1260 | /** Delete a string from the copied strings buffer if necessary */ 1261 | void DeleteString(const SI_CHAR * a_pString); 1262 | 1263 | /** Internal use of our string comparison function */ 1264 | bool IsLess(const SI_CHAR * a_pLeft, const SI_CHAR * a_pRight) const { 1265 | const static SI_STRLESS isLess = SI_STRLESS(); 1266 | return isLess(a_pLeft, a_pRight); 1267 | } 1268 | 1269 | bool IsMultiLineTag(const SI_CHAR * a_pData) const; 1270 | bool IsMultiLineData(const SI_CHAR * a_pData) const; 1271 | bool IsSingleLineQuotedValue(const SI_CHAR* a_pData) const; 1272 | bool LoadMultiLineText( 1273 | SI_CHAR *& a_pData, 1274 | const SI_CHAR *& a_pVal, 1275 | const SI_CHAR * a_pTagName, 1276 | bool a_bAllowBlankLinesInComment = false 1277 | ) const; 1278 | bool IsNewLineChar(SI_CHAR a_c) const; 1279 | 1280 | bool OutputMultiLineText( 1281 | OutputWriter & a_oOutput, 1282 | Converter & a_oConverter, 1283 | const SI_CHAR * a_pText 1284 | ) const; 1285 | 1286 | private: 1287 | /** Copy of the INI file data in our character format. This will be 1288 | modified when parsed to have NULL characters added after all 1289 | interesting string entries. All of the string pointers to sections, 1290 | keys and values point into this block of memory. 1291 | */ 1292 | SI_CHAR * m_pData; 1293 | 1294 | /** Length of the data that we have stored. Used when deleting strings 1295 | to determine if the string is stored here or in the allocated string 1296 | buffer. 1297 | */ 1298 | size_t m_uDataLen; 1299 | 1300 | /** File comment for this data, if one exists. */ 1301 | const SI_CHAR * m_pFileComment; 1302 | 1303 | /** constant empty string */ 1304 | const SI_CHAR m_cEmptyString; 1305 | 1306 | /** Parsed INI data. Section -> (Key -> Value). */ 1307 | TSection m_data; 1308 | 1309 | /** This vector stores allocated memory for copies of strings that have 1310 | been supplied after the file load. It will be empty unless SetValue() 1311 | has been called. 1312 | */ 1313 | TNamesDepend m_strings; 1314 | 1315 | /** Is the format of our datafile UTF-8 or MBCS? */ 1316 | bool m_bStoreIsUtf8; 1317 | 1318 | /** Are multiple values permitted for the same key? */ 1319 | bool m_bAllowMultiKey; 1320 | 1321 | /** Are data values permitted to span multiple lines? */ 1322 | bool m_bAllowMultiLine; 1323 | 1324 | /** Should spaces be written out surrounding the equals sign? */ 1325 | bool m_bSpaces; 1326 | 1327 | /** Should quoted data in values be recognized and parsed? */ 1328 | bool m_bParseQuotes; 1329 | 1330 | /** Do keys always need to have an equals sign when reading/writing? */ 1331 | bool m_bAllowKeyOnly; 1332 | 1333 | /** Next order value, used to ensure sections and keys are output in the 1334 | same order that they are loaded/added. 1335 | */ 1336 | int m_nOrder; 1337 | }; 1338 | 1339 | // --------------------------------------------------------------------------- 1340 | // IMPLEMENTATION 1341 | // --------------------------------------------------------------------------- 1342 | 1343 | template 1344 | CSimpleIniTempl::CSimpleIniTempl( 1345 | bool a_bIsUtf8, 1346 | bool a_bAllowMultiKey, 1347 | bool a_bAllowMultiLine 1348 | ) 1349 | : m_pData(0) 1350 | , m_uDataLen(0) 1351 | , m_pFileComment(NULL) 1352 | , m_cEmptyString(0) 1353 | , m_bStoreIsUtf8(a_bIsUtf8) 1354 | , m_bAllowMultiKey(a_bAllowMultiKey) 1355 | , m_bAllowMultiLine(a_bAllowMultiLine) 1356 | , m_bSpaces(true) 1357 | , m_bParseQuotes(false) 1358 | , m_bAllowKeyOnly(false) 1359 | , m_nOrder(0) 1360 | { } 1361 | 1362 | template 1363 | CSimpleIniTempl::~CSimpleIniTempl() 1364 | { 1365 | Reset(); 1366 | } 1367 | 1368 | template 1369 | void 1370 | CSimpleIniTempl::Reset() 1371 | { 1372 | // remove all data 1373 | delete[] m_pData; 1374 | m_pData = NULL; 1375 | m_uDataLen = 0; 1376 | m_pFileComment = NULL; 1377 | if (!m_data.empty()) { 1378 | m_data.erase(m_data.begin(), m_data.end()); 1379 | } 1380 | 1381 | // remove all strings 1382 | if (!m_strings.empty()) { 1383 | typename TNamesDepend::iterator i = m_strings.begin(); 1384 | for (; i != m_strings.end(); ++i) { 1385 | delete[] const_cast(i->pItem); 1386 | } 1387 | m_strings.erase(m_strings.begin(), m_strings.end()); 1388 | } 1389 | } 1390 | 1391 | template 1392 | SI_Error 1393 | CSimpleIniTempl::LoadFile( 1394 | const char * a_pszFile 1395 | ) 1396 | { 1397 | FILE * fp = NULL; 1398 | #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE 1399 | fopen_s(&fp, a_pszFile, "rb"); 1400 | #else // !__STDC_WANT_SECURE_LIB__ 1401 | fp = fopen(a_pszFile, "rb"); 1402 | #endif // __STDC_WANT_SECURE_LIB__ 1403 | if (!fp) { 1404 | return SI_FILE; 1405 | } 1406 | SI_Error rc = LoadFile(fp); 1407 | fclose(fp); 1408 | return rc; 1409 | } 1410 | 1411 | #ifdef SI_HAS_WIDE_FILE 1412 | template 1413 | SI_Error 1414 | CSimpleIniTempl::LoadFile( 1415 | const SI_WCHAR_T * a_pwszFile 1416 | ) 1417 | { 1418 | #ifdef _WIN32 1419 | FILE * fp = NULL; 1420 | #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE 1421 | _wfopen_s(&fp, a_pwszFile, L"rb"); 1422 | #else // !__STDC_WANT_SECURE_LIB__ 1423 | fp = _wfopen(a_pwszFile, L"rb"); 1424 | #endif // __STDC_WANT_SECURE_LIB__ 1425 | if (!fp) return SI_FILE; 1426 | SI_Error rc = LoadFile(fp); 1427 | fclose(fp); 1428 | return rc; 1429 | #else // !_WIN32 (therefore SI_CONVERT_ICU) 1430 | char szFile[256]; 1431 | u_austrncpy(szFile, a_pwszFile, sizeof(szFile)); 1432 | return LoadFile(szFile); 1433 | #endif // _WIN32 1434 | } 1435 | #endif // SI_HAS_WIDE_FILE 1436 | 1437 | template 1438 | SI_Error 1439 | CSimpleIniTempl::LoadFile( 1440 | FILE * a_fpFile 1441 | ) 1442 | { 1443 | // load the raw file data 1444 | int retval = fseek(a_fpFile, 0, SEEK_END); 1445 | if (retval != 0) { 1446 | return SI_FILE; 1447 | } 1448 | long lSize = ftell(a_fpFile); 1449 | if (lSize < 0) { 1450 | return SI_FILE; 1451 | } 1452 | if (lSize == 0) { 1453 | return SI_OK; 1454 | } 1455 | 1456 | // allocate and ensure NULL terminated 1457 | char * pData = new(std::nothrow) char[lSize+static_cast(1)]; 1458 | if (!pData) { 1459 | return SI_NOMEM; 1460 | } 1461 | pData[lSize] = 0; 1462 | 1463 | // load data into buffer 1464 | fseek(a_fpFile, 0, SEEK_SET); 1465 | size_t uRead = fread(pData, sizeof(char), lSize, a_fpFile); 1466 | if (uRead != (size_t) lSize) { 1467 | delete[] pData; 1468 | return SI_FILE; 1469 | } 1470 | 1471 | // convert the raw data to unicode 1472 | SI_Error rc = LoadData(pData, uRead); 1473 | delete[] pData; 1474 | return rc; 1475 | } 1476 | 1477 | template 1478 | SI_Error 1479 | CSimpleIniTempl::LoadData( 1480 | const char * a_pData, 1481 | size_t a_uDataLen 1482 | ) 1483 | { 1484 | if (!a_pData) { 1485 | return SI_OK; 1486 | } 1487 | 1488 | // if the UTF-8 BOM exists, consume it and set mode to unicode, if we have 1489 | // already loaded data and try to change mode half-way through then this will 1490 | // be ignored and we will assert in debug versions 1491 | if (a_uDataLen >= 3 && memcmp(a_pData, SI_UTF8_SIGNATURE, 3) == 0) { 1492 | a_pData += 3; 1493 | a_uDataLen -= 3; 1494 | SI_ASSERT(m_bStoreIsUtf8 || !m_pData); // we don't expect mixed mode data 1495 | SetUnicode(); 1496 | } 1497 | 1498 | if (a_uDataLen == 0) { 1499 | return SI_OK; 1500 | } 1501 | 1502 | // determine the length of the converted data 1503 | SI_CONVERTER converter(m_bStoreIsUtf8); 1504 | size_t uLen = converter.SizeFromStore(a_pData, a_uDataLen); 1505 | if (uLen == (size_t)(-1)) { 1506 | return SI_FAIL; 1507 | } 1508 | 1509 | // allocate memory for the data, ensure that there is a NULL 1510 | // terminator wherever the converted data ends 1511 | SI_CHAR * pData = new(std::nothrow) SI_CHAR[uLen+1]; 1512 | if (!pData) { 1513 | return SI_NOMEM; 1514 | } 1515 | memset(pData, 0, sizeof(SI_CHAR)*(uLen+1)); 1516 | 1517 | // convert the data 1518 | if (!converter.ConvertFromStore(a_pData, a_uDataLen, pData, uLen)) { 1519 | delete[] pData; 1520 | return SI_FAIL; 1521 | } 1522 | 1523 | // parse it 1524 | const static SI_CHAR empty = 0; 1525 | SI_CHAR * pWork = pData; 1526 | const SI_CHAR * pSection = ∅ 1527 | const SI_CHAR * pItem = NULL; 1528 | const SI_CHAR * pVal = NULL; 1529 | const SI_CHAR * pComment = NULL; 1530 | 1531 | // We copy the strings if we are loading data into this class when we 1532 | // already have stored some. 1533 | bool bCopyStrings = (m_pData != NULL); 1534 | 1535 | // find a file comment if it exists, this is a comment that starts at the 1536 | // beginning of the file and continues until the first blank line. 1537 | SI_Error rc = FindFileComment(pWork, bCopyStrings); 1538 | if (rc < 0) return rc; 1539 | 1540 | // add every entry in the file to the data table 1541 | while (FindEntry(pWork, pSection, pItem, pVal, pComment)) { 1542 | rc = AddEntry(pSection, pItem, pVal, pComment, false, bCopyStrings); 1543 | if (rc < 0) return rc; 1544 | } 1545 | 1546 | // store these strings if we didn't copy them 1547 | if (bCopyStrings) { 1548 | delete[] pData; 1549 | } 1550 | else { 1551 | m_pData = pData; 1552 | m_uDataLen = uLen+1; 1553 | } 1554 | 1555 | return SI_OK; 1556 | } 1557 | 1558 | #ifdef SI_SUPPORT_IOSTREAMS 1559 | template 1560 | SI_Error 1561 | CSimpleIniTempl::LoadData( 1562 | std::istream & a_istream 1563 | ) 1564 | { 1565 | std::string strData; 1566 | char szBuf[512]; 1567 | do { 1568 | a_istream.get(szBuf, sizeof(szBuf), '\0'); 1569 | strData.append(szBuf); 1570 | } 1571 | while (a_istream.good()); 1572 | return LoadData(strData); 1573 | } 1574 | #endif // SI_SUPPORT_IOSTREAMS 1575 | 1576 | template 1577 | SI_Error 1578 | CSimpleIniTempl::FindFileComment( 1579 | SI_CHAR *& a_pData, 1580 | bool a_bCopyStrings 1581 | ) 1582 | { 1583 | // there can only be a single file comment 1584 | if (m_pFileComment) { 1585 | return SI_OK; 1586 | } 1587 | 1588 | // Load the file comment as multi-line text, this will modify all of 1589 | // the newline characters to be single \n chars 1590 | if (!LoadMultiLineText(a_pData, m_pFileComment, NULL, false)) { 1591 | return SI_OK; 1592 | } 1593 | 1594 | // copy the string if necessary 1595 | if (a_bCopyStrings) { 1596 | SI_Error rc = CopyString(m_pFileComment); 1597 | if (rc < 0) return rc; 1598 | } 1599 | 1600 | return SI_OK; 1601 | } 1602 | 1603 | template 1604 | bool 1605 | CSimpleIniTempl::FindEntry( 1606 | SI_CHAR *& a_pData, 1607 | const SI_CHAR *& a_pSection, 1608 | const SI_CHAR *& a_pKey, 1609 | const SI_CHAR *& a_pVal, 1610 | const SI_CHAR *& a_pComment 1611 | ) const 1612 | { 1613 | a_pComment = NULL; 1614 | 1615 | bool bHaveValue = false; 1616 | SI_CHAR * pTrail = NULL; 1617 | while (*a_pData) { 1618 | // skip spaces and empty lines 1619 | while (*a_pData && IsSpace(*a_pData)) { 1620 | ++a_pData; 1621 | } 1622 | if (!*a_pData) { 1623 | break; 1624 | } 1625 | 1626 | // skip processing of comment lines but keep a pointer to 1627 | // the start of the comment. 1628 | if (IsComment(*a_pData)) { 1629 | LoadMultiLineText(a_pData, a_pComment, NULL, true); 1630 | continue; 1631 | } 1632 | 1633 | // process section names 1634 | if (*a_pData == '[') { 1635 | // skip leading spaces 1636 | ++a_pData; 1637 | while (*a_pData && IsSpace(*a_pData)) { 1638 | ++a_pData; 1639 | } 1640 | 1641 | // find the end of the section name (it may contain spaces) 1642 | // and convert it to lowercase as necessary 1643 | a_pSection = a_pData; 1644 | while (*a_pData && *a_pData != ']' && !IsNewLineChar(*a_pData)) { 1645 | ++a_pData; 1646 | } 1647 | 1648 | // if it's an invalid line, just skip it 1649 | if (*a_pData != ']') { 1650 | continue; 1651 | } 1652 | 1653 | // remove trailing spaces from the section 1654 | pTrail = a_pData - 1; 1655 | while (pTrail >= a_pSection && IsSpace(*pTrail)) { 1656 | --pTrail; 1657 | } 1658 | ++pTrail; 1659 | *pTrail = 0; 1660 | 1661 | // skip to the end of the line 1662 | ++a_pData; // safe as checked that it == ']' above 1663 | while (*a_pData && !IsNewLineChar(*a_pData)) { 1664 | ++a_pData; 1665 | } 1666 | 1667 | a_pKey = NULL; 1668 | a_pVal = NULL; 1669 | return true; 1670 | } 1671 | 1672 | // find the end of the key name (it may contain spaces) 1673 | a_pKey = a_pData; 1674 | while (*a_pData && *a_pData != '=' && !IsNewLineChar(*a_pData)) { 1675 | ++a_pData; 1676 | } 1677 | // *a_pData is null, equals, or newline 1678 | 1679 | // if no value and we don't allow no value, then invalid 1680 | bHaveValue = (*a_pData == '='); 1681 | if (!bHaveValue && !m_bAllowKeyOnly) { 1682 | continue; 1683 | } 1684 | 1685 | // empty keys are invalid 1686 | if (bHaveValue && a_pKey == a_pData) { 1687 | while (*a_pData && !IsNewLineChar(*a_pData)) { 1688 | ++a_pData; 1689 | } 1690 | continue; 1691 | } 1692 | 1693 | // remove trailing spaces from the key 1694 | pTrail = a_pData - 1; 1695 | while (pTrail >= a_pKey && IsSpace(*pTrail)) { 1696 | --pTrail; 1697 | } 1698 | ++pTrail; 1699 | 1700 | if (bHaveValue) { 1701 | // process the value 1702 | *pTrail = 0; 1703 | 1704 | // skip leading whitespace on the value 1705 | ++a_pData; // safe as checked that it == '=' above 1706 | while (*a_pData && !IsNewLineChar(*a_pData) && IsSpace(*a_pData)) { 1707 | ++a_pData; 1708 | } 1709 | 1710 | // find the end of the value which is the end of this line 1711 | a_pVal = a_pData; 1712 | while (*a_pData && !IsNewLineChar(*a_pData)) { 1713 | ++a_pData; 1714 | } 1715 | 1716 | // remove trailing spaces from the value 1717 | pTrail = a_pData - 1; 1718 | if (*a_pData) { // prepare for the next round 1719 | SkipNewLine(a_pData); 1720 | } 1721 | while (pTrail >= a_pVal && IsSpace(*pTrail)) { 1722 | --pTrail; 1723 | } 1724 | ++pTrail; 1725 | *pTrail = 0; 1726 | 1727 | // check for multi-line entries 1728 | if (m_bAllowMultiLine && IsMultiLineTag(a_pVal)) { 1729 | // skip the "<<<" to get the tag that will end the multiline 1730 | const SI_CHAR* pTagName = a_pVal + 3; 1731 | return LoadMultiLineText(a_pData, a_pVal, pTagName); 1732 | } 1733 | 1734 | // check for quoted values, we are not supporting escapes in quoted values (yet) 1735 | if (m_bParseQuotes) { 1736 | --pTrail; 1737 | if (pTrail > a_pVal && *a_pVal == '"' && *pTrail == '"') { 1738 | ++a_pVal; 1739 | *pTrail = 0; 1740 | } 1741 | } 1742 | } 1743 | else { 1744 | // no value to process, just prepare for the next 1745 | if (*a_pData) { 1746 | SkipNewLine(a_pData); 1747 | } 1748 | *pTrail = 0; 1749 | } 1750 | 1751 | // return the standard entry 1752 | return true; 1753 | } 1754 | 1755 | return false; 1756 | } 1757 | 1758 | template 1759 | bool 1760 | CSimpleIniTempl::IsMultiLineTag( 1761 | const SI_CHAR * a_pVal 1762 | ) const 1763 | { 1764 | // check for the "<<<" prefix for a multi-line entry 1765 | if (*a_pVal++ != '<') return false; 1766 | if (*a_pVal++ != '<') return false; 1767 | if (*a_pVal++ != '<') return false; 1768 | return true; 1769 | } 1770 | 1771 | template 1772 | bool 1773 | CSimpleIniTempl::IsMultiLineData( 1774 | const SI_CHAR * a_pData 1775 | ) const 1776 | { 1777 | // data is multi-line if it has any of the following features: 1778 | // * whitespace prefix 1779 | // * embedded newlines 1780 | // * whitespace suffix 1781 | 1782 | // empty string 1783 | if (!*a_pData) { 1784 | return false; 1785 | } 1786 | 1787 | // check for prefix 1788 | if (IsSpace(*a_pData)) { 1789 | return true; 1790 | } 1791 | 1792 | // embedded newlines 1793 | while (*a_pData) { 1794 | if (IsNewLineChar(*a_pData)) { 1795 | return true; 1796 | } 1797 | ++a_pData; 1798 | } 1799 | 1800 | // check for suffix 1801 | if (IsSpace(*--a_pData)) { 1802 | return true; 1803 | } 1804 | 1805 | return false; 1806 | } 1807 | 1808 | template 1809 | bool 1810 | CSimpleIniTempl::IsSingleLineQuotedValue( 1811 | const SI_CHAR* a_pData 1812 | ) const 1813 | { 1814 | // data needs quoting if it starts or ends with whitespace 1815 | // and doesn't have embedded newlines 1816 | 1817 | // empty string 1818 | if (!*a_pData) { 1819 | return false; 1820 | } 1821 | 1822 | // check for prefix 1823 | if (IsSpace(*a_pData)) { 1824 | return true; 1825 | } 1826 | 1827 | // embedded newlines 1828 | while (*a_pData) { 1829 | if (IsNewLineChar(*a_pData)) { 1830 | return false; 1831 | } 1832 | ++a_pData; 1833 | } 1834 | 1835 | // check for suffix 1836 | if (IsSpace(*--a_pData)) { 1837 | return true; 1838 | } 1839 | 1840 | return false; 1841 | } 1842 | 1843 | template 1844 | bool 1845 | CSimpleIniTempl::IsNewLineChar( 1846 | SI_CHAR a_c 1847 | ) const 1848 | { 1849 | return (a_c == '\n' || a_c == '\r'); 1850 | } 1851 | 1852 | template 1853 | bool 1854 | CSimpleIniTempl::LoadMultiLineText( 1855 | SI_CHAR *& a_pData, 1856 | const SI_CHAR *& a_pVal, 1857 | const SI_CHAR * a_pTagName, 1858 | bool a_bAllowBlankLinesInComment 1859 | ) const 1860 | { 1861 | // we modify this data to strip all newlines down to a single '\n' 1862 | // character. This means that on Windows we need to strip out some 1863 | // characters which will make the data shorter. 1864 | // i.e. LINE1-LINE1\r\nLINE2-LINE2\0 will become 1865 | // LINE1-LINE1\nLINE2-LINE2\0 1866 | // The pDataLine entry is the pointer to the location in memory that 1867 | // the current line needs to start to run following the existing one. 1868 | // This may be the same as pCurrLine in which case no move is needed. 1869 | SI_CHAR * pDataLine = a_pData; 1870 | SI_CHAR * pCurrLine; 1871 | 1872 | // value starts at the current line 1873 | a_pVal = a_pData; 1874 | 1875 | // find the end tag. This tag must start in column 1 and be 1876 | // followed by a newline. We ignore any whitespace after the end 1877 | // tag but not whitespace before it. 1878 | SI_CHAR cEndOfLineChar = *a_pData; 1879 | for(;;) { 1880 | // if we are loading comments then we need a comment character as 1881 | // the first character on every line 1882 | if (!a_pTagName && !IsComment(*a_pData)) { 1883 | // if we aren't allowing blank lines then we're done 1884 | if (!a_bAllowBlankLinesInComment) { 1885 | break; 1886 | } 1887 | 1888 | // if we are allowing blank lines then we only include them 1889 | // in this comment if another comment follows, so read ahead 1890 | // to find out. 1891 | SI_CHAR * pCurr = a_pData; 1892 | int nNewLines = 0; 1893 | while (IsSpace(*pCurr)) { 1894 | if (IsNewLineChar(*pCurr)) { 1895 | ++nNewLines; 1896 | SkipNewLine(pCurr); 1897 | } 1898 | else { 1899 | ++pCurr; 1900 | } 1901 | } 1902 | 1903 | // we have a comment, add the blank lines to the output 1904 | // and continue processing from here 1905 | if (IsComment(*pCurr)) { 1906 | for (; nNewLines > 0; --nNewLines) *pDataLine++ = '\n'; 1907 | a_pData = pCurr; 1908 | continue; 1909 | } 1910 | 1911 | // the comment ends here 1912 | break; 1913 | } 1914 | 1915 | // find the end of this line 1916 | pCurrLine = a_pData; 1917 | while (*a_pData && !IsNewLineChar(*a_pData)) ++a_pData; 1918 | 1919 | // move this line down to the location that it should be if necessary 1920 | if (pDataLine < pCurrLine) { 1921 | size_t nLen = (size_t) (a_pData - pCurrLine); 1922 | memmove(pDataLine, pCurrLine, nLen * sizeof(SI_CHAR)); 1923 | pDataLine[nLen] = '\0'; 1924 | } 1925 | 1926 | // end the line with a NULL 1927 | cEndOfLineChar = *a_pData; 1928 | *a_pData = 0; 1929 | 1930 | // if are looking for a tag then do the check now. This is done before 1931 | // checking for end of the data, so that if we have the tag at the end 1932 | // of the data then the tag is removed correctly. 1933 | if (a_pTagName) { 1934 | // strip whitespace from the end of this tag 1935 | SI_CHAR* pc = a_pData - 1; 1936 | while (pc > pDataLine && IsSpace(*pc)) --pc; 1937 | SI_CHAR ch = *++pc; 1938 | *pc = 0; 1939 | 1940 | if (!IsLess(pDataLine, a_pTagName) && !IsLess(a_pTagName, pDataLine)) { 1941 | break; 1942 | } 1943 | 1944 | *pc = ch; 1945 | } 1946 | 1947 | // if we are at the end of the data then we just automatically end 1948 | // this entry and return the current data. 1949 | if (!cEndOfLineChar) { 1950 | return true; 1951 | } 1952 | 1953 | // otherwise we need to process this newline to ensure that it consists 1954 | // of just a single \n character. 1955 | pDataLine += (a_pData - pCurrLine); 1956 | *a_pData = cEndOfLineChar; 1957 | SkipNewLine(a_pData); 1958 | *pDataLine++ = '\n'; 1959 | } 1960 | 1961 | // if we didn't find a comment at all then return false 1962 | if (a_pVal == a_pData) { 1963 | a_pVal = NULL; 1964 | return false; 1965 | } 1966 | 1967 | // the data (which ends at the end of the last line) needs to be 1968 | // null-terminated BEFORE before the newline character(s). If the 1969 | // user wants a new line in the multi-line data then they need to 1970 | // add an empty line before the tag. 1971 | *--pDataLine = '\0'; 1972 | 1973 | // if looking for a tag and if we aren't at the end of the data, 1974 | // then move a_pData to the start of the next line. 1975 | if (a_pTagName && cEndOfLineChar) { 1976 | SI_ASSERT(IsNewLineChar(cEndOfLineChar)); 1977 | *a_pData = cEndOfLineChar; 1978 | SkipNewLine(a_pData); 1979 | } 1980 | 1981 | return true; 1982 | } 1983 | 1984 | template 1985 | SI_Error 1986 | CSimpleIniTempl::CopyString( 1987 | const SI_CHAR *& a_pString 1988 | ) 1989 | { 1990 | size_t uLen = 0; 1991 | if (sizeof(SI_CHAR) == sizeof(char)) { 1992 | uLen = strlen((const char *)a_pString); 1993 | } 1994 | else if (sizeof(SI_CHAR) == sizeof(wchar_t)) { 1995 | uLen = wcslen((const wchar_t *)a_pString); 1996 | } 1997 | else { 1998 | for ( ; a_pString[uLen]; ++uLen) /*loop*/ ; 1999 | } 2000 | ++uLen; // NULL character 2001 | SI_CHAR * pCopy = new(std::nothrow) SI_CHAR[uLen]; 2002 | if (!pCopy) { 2003 | return SI_NOMEM; 2004 | } 2005 | memcpy(pCopy, a_pString, sizeof(SI_CHAR)*uLen); 2006 | m_strings.push_back(pCopy); 2007 | a_pString = pCopy; 2008 | return SI_OK; 2009 | } 2010 | 2011 | template 2012 | SI_Error 2013 | CSimpleIniTempl::AddEntry( 2014 | const SI_CHAR * a_pSection, 2015 | const SI_CHAR * a_pKey, 2016 | const SI_CHAR * a_pValue, 2017 | const SI_CHAR * a_pComment, 2018 | bool a_bForceReplace, 2019 | bool a_bCopyStrings 2020 | ) 2021 | { 2022 | SI_Error rc; 2023 | bool bInserted = false; 2024 | 2025 | SI_ASSERT(!a_pComment || IsComment(*a_pComment)); 2026 | 2027 | // if we are copying strings then make a copy of the comment now 2028 | // because we will need it when we add the entry. 2029 | if (a_bCopyStrings && a_pComment) { 2030 | rc = CopyString(a_pComment); 2031 | if (rc < 0) return rc; 2032 | } 2033 | 2034 | // create the section entry if necessary 2035 | typename TSection::iterator iSection = m_data.find(a_pSection); 2036 | if (iSection == m_data.end()) { 2037 | // if the section doesn't exist then we need a copy as the 2038 | // string needs to last beyond the end of this function 2039 | if (a_bCopyStrings) { 2040 | rc = CopyString(a_pSection); 2041 | if (rc < 0) return rc; 2042 | } 2043 | 2044 | // only set the comment if this is a section only entry 2045 | Entry oSection(a_pSection, ++m_nOrder); 2046 | if (a_pComment && !a_pKey) { 2047 | oSection.pComment = a_pComment; 2048 | } 2049 | 2050 | typename TSection::value_type oEntry(oSection, TKeyVal()); 2051 | typedef typename TSection::iterator SectionIterator; 2052 | std::pair i = m_data.insert(oEntry); 2053 | iSection = i.first; 2054 | bInserted = true; 2055 | } 2056 | if (!a_pKey) { 2057 | // section only entries are specified with pItem as NULL 2058 | return bInserted ? SI_INSERTED : SI_UPDATED; 2059 | } 2060 | 2061 | // check for existence of the key 2062 | TKeyVal & keyval = iSection->second; 2063 | typename TKeyVal::iterator iKey = keyval.find(a_pKey); 2064 | bInserted = iKey == keyval.end(); 2065 | 2066 | // remove all existing entries but save the load order and 2067 | // comment of the first entry 2068 | int nLoadOrder = ++m_nOrder; 2069 | if (iKey != keyval.end() && m_bAllowMultiKey && a_bForceReplace) { 2070 | const SI_CHAR * pComment = NULL; 2071 | while (iKey != keyval.end() && !IsLess(a_pKey, iKey->first.pItem)) { 2072 | if (iKey->first.nOrder < nLoadOrder) { 2073 | nLoadOrder = iKey->first.nOrder; 2074 | pComment = iKey->first.pComment; 2075 | } 2076 | ++iKey; 2077 | } 2078 | if (pComment) { 2079 | DeleteString(a_pComment); 2080 | a_pComment = pComment; 2081 | CopyString(a_pComment); 2082 | } 2083 | Delete(a_pSection, a_pKey); 2084 | iKey = keyval.end(); 2085 | } 2086 | 2087 | // values need to be a valid string, even if they are an empty string 2088 | if (!a_pValue) { 2089 | a_pValue = &m_cEmptyString; 2090 | } 2091 | 2092 | // make string copies if necessary 2093 | bool bForceCreateNewKey = m_bAllowMultiKey && !a_bForceReplace; 2094 | if (a_bCopyStrings) { 2095 | if (bForceCreateNewKey || iKey == keyval.end()) { 2096 | // if the key doesn't exist then we need a copy as the 2097 | // string needs to last beyond the end of this function 2098 | // because we will be inserting the key next 2099 | rc = CopyString(a_pKey); 2100 | if (rc < 0) return rc; 2101 | } 2102 | 2103 | // we always need a copy of the value 2104 | rc = CopyString(a_pValue); 2105 | if (rc < 0) return rc; 2106 | } 2107 | 2108 | // create the key entry 2109 | if (iKey == keyval.end() || bForceCreateNewKey) { 2110 | Entry oKey(a_pKey, nLoadOrder); 2111 | if (a_pComment) { 2112 | oKey.pComment = a_pComment; 2113 | } 2114 | typename TKeyVal::value_type oEntry(oKey, static_cast(NULL)); 2115 | iKey = keyval.insert(oEntry); 2116 | } 2117 | 2118 | iKey->second = a_pValue; 2119 | return bInserted ? SI_INSERTED : SI_UPDATED; 2120 | } 2121 | 2122 | template 2123 | const SI_CHAR * 2124 | CSimpleIniTempl::GetValue( 2125 | const SI_CHAR * a_pSection, 2126 | const SI_CHAR * a_pKey, 2127 | const SI_CHAR * a_pDefault, 2128 | bool * a_pHasMultiple 2129 | ) const 2130 | { 2131 | if (a_pHasMultiple) { 2132 | *a_pHasMultiple = false; 2133 | } 2134 | if (!a_pSection || !a_pKey) { 2135 | return a_pDefault; 2136 | } 2137 | typename TSection::const_iterator iSection = m_data.find(a_pSection); 2138 | if (iSection == m_data.end()) { 2139 | return a_pDefault; 2140 | } 2141 | typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey); 2142 | if (iKeyVal == iSection->second.end()) { 2143 | return a_pDefault; 2144 | } 2145 | 2146 | // check for multiple entries with the same key 2147 | if (m_bAllowMultiKey && a_pHasMultiple) { 2148 | typename TKeyVal::const_iterator iTemp = iKeyVal; 2149 | if (++iTemp != iSection->second.end()) { 2150 | if (!IsLess(a_pKey, iTemp->first.pItem)) { 2151 | *a_pHasMultiple = true; 2152 | } 2153 | } 2154 | } 2155 | 2156 | return iKeyVal->second; 2157 | } 2158 | 2159 | template 2160 | long 2161 | CSimpleIniTempl::GetLongValue( 2162 | const SI_CHAR * a_pSection, 2163 | const SI_CHAR * a_pKey, 2164 | long a_nDefault, 2165 | bool * a_pHasMultiple 2166 | ) const 2167 | { 2168 | // return the default if we don't have a value 2169 | const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple); 2170 | if (!pszValue || !*pszValue) return a_nDefault; 2171 | 2172 | // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII 2173 | char szValue[64] = { 0 }; 2174 | SI_CONVERTER c(m_bStoreIsUtf8); 2175 | if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) { 2176 | return a_nDefault; 2177 | } 2178 | 2179 | // handle the value as hex if prefaced with "0x" 2180 | long nValue = a_nDefault; 2181 | char * pszSuffix = szValue; 2182 | if (szValue[0] == '0' && (szValue[1] == 'x' || szValue[1] == 'X')) { 2183 | if (!szValue[2]) return a_nDefault; 2184 | nValue = strtol(&szValue[2], &pszSuffix, 16); 2185 | } 2186 | else { 2187 | nValue = strtol(szValue, &pszSuffix, 10); 2188 | } 2189 | 2190 | // any invalid strings will return the default value 2191 | if (*pszSuffix) { 2192 | return a_nDefault; 2193 | } 2194 | 2195 | return nValue; 2196 | } 2197 | 2198 | template 2199 | SI_Error 2200 | CSimpleIniTempl::SetLongValue( 2201 | const SI_CHAR * a_pSection, 2202 | const SI_CHAR * a_pKey, 2203 | long a_nValue, 2204 | const SI_CHAR * a_pComment, 2205 | bool a_bUseHex, 2206 | bool a_bForceReplace 2207 | ) 2208 | { 2209 | // use SetValue to create sections 2210 | if (!a_pSection || !a_pKey) return SI_FAIL; 2211 | 2212 | // convert to an ASCII string 2213 | char szInput[64]; 2214 | #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE 2215 | sprintf_s(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue); 2216 | #else // !__STDC_WANT_SECURE_LIB__ 2217 | sprintf(szInput, a_bUseHex ? "0x%lx" : "%ld", a_nValue); 2218 | #endif // __STDC_WANT_SECURE_LIB__ 2219 | 2220 | // convert to output text 2221 | SI_CHAR szOutput[64]; 2222 | SI_CONVERTER c(m_bStoreIsUtf8); 2223 | c.ConvertFromStore(szInput, strlen(szInput) + 1, 2224 | szOutput, sizeof(szOutput) / sizeof(SI_CHAR)); 2225 | 2226 | // actually add it 2227 | return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true); 2228 | } 2229 | 2230 | template 2231 | double 2232 | CSimpleIniTempl::GetDoubleValue( 2233 | const SI_CHAR * a_pSection, 2234 | const SI_CHAR * a_pKey, 2235 | double a_nDefault, 2236 | bool * a_pHasMultiple 2237 | ) const 2238 | { 2239 | // return the default if we don't have a value 2240 | const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple); 2241 | if (!pszValue || !*pszValue) return a_nDefault; 2242 | 2243 | // convert to UTF-8/MBCS which for a numeric value will be the same as ASCII 2244 | char szValue[64] = { 0 }; 2245 | SI_CONVERTER c(m_bStoreIsUtf8); 2246 | if (!c.ConvertToStore(pszValue, szValue, sizeof(szValue))) { 2247 | return a_nDefault; 2248 | } 2249 | 2250 | char * pszSuffix = NULL; 2251 | double nValue = strtod(szValue, &pszSuffix); 2252 | 2253 | // any invalid strings will return the default value 2254 | if (!pszSuffix || *pszSuffix) { 2255 | return a_nDefault; 2256 | } 2257 | 2258 | return nValue; 2259 | } 2260 | 2261 | template 2262 | SI_Error 2263 | CSimpleIniTempl::SetDoubleValue( 2264 | const SI_CHAR * a_pSection, 2265 | const SI_CHAR * a_pKey, 2266 | double a_nValue, 2267 | const SI_CHAR * a_pComment, 2268 | bool a_bForceReplace 2269 | ) 2270 | { 2271 | // use SetValue to create sections 2272 | if (!a_pSection || !a_pKey) return SI_FAIL; 2273 | 2274 | // convert to an ASCII string 2275 | char szInput[64]; 2276 | #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE 2277 | sprintf_s(szInput, "%f", a_nValue); 2278 | #else // !__STDC_WANT_SECURE_LIB__ 2279 | sprintf(szInput, "%f", a_nValue); 2280 | #endif // __STDC_WANT_SECURE_LIB__ 2281 | 2282 | // convert to output text 2283 | SI_CHAR szOutput[64]; 2284 | SI_CONVERTER c(m_bStoreIsUtf8); 2285 | c.ConvertFromStore(szInput, strlen(szInput) + 1, 2286 | szOutput, sizeof(szOutput) / sizeof(SI_CHAR)); 2287 | 2288 | // actually add it 2289 | return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true); 2290 | } 2291 | 2292 | template 2293 | bool 2294 | CSimpleIniTempl::GetBoolValue( 2295 | const SI_CHAR * a_pSection, 2296 | const SI_CHAR * a_pKey, 2297 | bool a_bDefault, 2298 | bool * a_pHasMultiple 2299 | ) const 2300 | { 2301 | // return the default if we don't have a value 2302 | const SI_CHAR * pszValue = GetValue(a_pSection, a_pKey, NULL, a_pHasMultiple); 2303 | if (!pszValue || !*pszValue) return a_bDefault; 2304 | 2305 | // we only look at the minimum number of characters 2306 | switch (pszValue[0]) { 2307 | case 't': case 'T': // true 2308 | case 'y': case 'Y': // yes 2309 | case '1': // 1 (one) 2310 | return true; 2311 | 2312 | case 'f': case 'F': // false 2313 | case 'n': case 'N': // no 2314 | case '0': // 0 (zero) 2315 | return false; 2316 | 2317 | case 'o': case 'O': 2318 | if (pszValue[1] == 'n' || pszValue[1] == 'N') return true; // on 2319 | if (pszValue[1] == 'f' || pszValue[1] == 'F') return false; // off 2320 | break; 2321 | } 2322 | 2323 | // no recognized value, return the default 2324 | return a_bDefault; 2325 | } 2326 | 2327 | template 2328 | SI_Error 2329 | CSimpleIniTempl::SetBoolValue( 2330 | const SI_CHAR * a_pSection, 2331 | const SI_CHAR * a_pKey, 2332 | bool a_bValue, 2333 | const SI_CHAR * a_pComment, 2334 | bool a_bForceReplace 2335 | ) 2336 | { 2337 | // use SetValue to create sections 2338 | if (!a_pSection || !a_pKey) return SI_FAIL; 2339 | 2340 | // convert to an ASCII string 2341 | const char * pszInput = a_bValue ? "true" : "false"; 2342 | 2343 | // convert to output text 2344 | SI_CHAR szOutput[64]; 2345 | SI_CONVERTER c(m_bStoreIsUtf8); 2346 | c.ConvertFromStore(pszInput, strlen(pszInput) + 1, 2347 | szOutput, sizeof(szOutput) / sizeof(SI_CHAR)); 2348 | 2349 | // actually add it 2350 | return AddEntry(a_pSection, a_pKey, szOutput, a_pComment, a_bForceReplace, true); 2351 | } 2352 | 2353 | template 2354 | bool 2355 | CSimpleIniTempl::GetAllValues( 2356 | const SI_CHAR * a_pSection, 2357 | const SI_CHAR * a_pKey, 2358 | TNamesDepend & a_values 2359 | ) const 2360 | { 2361 | a_values.clear(); 2362 | 2363 | if (!a_pSection || !a_pKey) { 2364 | return false; 2365 | } 2366 | typename TSection::const_iterator iSection = m_data.find(a_pSection); 2367 | if (iSection == m_data.end()) { 2368 | return false; 2369 | } 2370 | typename TKeyVal::const_iterator iKeyVal = iSection->second.find(a_pKey); 2371 | if (iKeyVal == iSection->second.end()) { 2372 | return false; 2373 | } 2374 | 2375 | // insert all values for this key 2376 | a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder)); 2377 | if (m_bAllowMultiKey) { 2378 | ++iKeyVal; 2379 | while (iKeyVal != iSection->second.end() && !IsLess(a_pKey, iKeyVal->first.pItem)) { 2380 | a_values.push_back(Entry(iKeyVal->second, iKeyVal->first.pComment, iKeyVal->first.nOrder)); 2381 | ++iKeyVal; 2382 | } 2383 | } 2384 | 2385 | return true; 2386 | } 2387 | 2388 | template 2389 | int 2390 | CSimpleIniTempl::GetSectionSize( 2391 | const SI_CHAR * a_pSection 2392 | ) const 2393 | { 2394 | if (!a_pSection) { 2395 | return -1; 2396 | } 2397 | 2398 | typename TSection::const_iterator iSection = m_data.find(a_pSection); 2399 | if (iSection == m_data.end()) { 2400 | return -1; 2401 | } 2402 | const TKeyVal & section = iSection->second; 2403 | 2404 | // if multi-key isn't permitted then the section size is 2405 | // the number of keys that we have. 2406 | if (!m_bAllowMultiKey || section.empty()) { 2407 | return (int) section.size(); 2408 | } 2409 | 2410 | // otherwise we need to count them 2411 | int nCount = 0; 2412 | const SI_CHAR * pLastKey = NULL; 2413 | typename TKeyVal::const_iterator iKeyVal = section.begin(); 2414 | for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n) { 2415 | if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) { 2416 | ++nCount; 2417 | pLastKey = iKeyVal->first.pItem; 2418 | } 2419 | } 2420 | return nCount; 2421 | } 2422 | 2423 | template 2424 | const typename CSimpleIniTempl::TKeyVal * 2425 | CSimpleIniTempl::GetSection( 2426 | const SI_CHAR * a_pSection 2427 | ) const 2428 | { 2429 | if (a_pSection) { 2430 | typename TSection::const_iterator i = m_data.find(a_pSection); 2431 | if (i != m_data.end()) { 2432 | return &(i->second); 2433 | } 2434 | } 2435 | return 0; 2436 | } 2437 | 2438 | template 2439 | void 2440 | CSimpleIniTempl::GetAllSections( 2441 | TNamesDepend & a_names 2442 | ) const 2443 | { 2444 | a_names.clear(); 2445 | typename TSection::const_iterator i = m_data.begin(); 2446 | for (int n = 0; i != m_data.end(); ++i, ++n ) { 2447 | a_names.push_back(i->first); 2448 | } 2449 | } 2450 | 2451 | template 2452 | bool 2453 | CSimpleIniTempl::GetAllKeys( 2454 | const SI_CHAR * a_pSection, 2455 | TNamesDepend & a_names 2456 | ) const 2457 | { 2458 | a_names.clear(); 2459 | 2460 | if (!a_pSection) { 2461 | return false; 2462 | } 2463 | 2464 | typename TSection::const_iterator iSection = m_data.find(a_pSection); 2465 | if (iSection == m_data.end()) { 2466 | return false; 2467 | } 2468 | 2469 | const TKeyVal & section = iSection->second; 2470 | const SI_CHAR * pLastKey = NULL; 2471 | typename TKeyVal::const_iterator iKeyVal = section.begin(); 2472 | for (int n = 0; iKeyVal != section.end(); ++iKeyVal, ++n ) { 2473 | if (!pLastKey || IsLess(pLastKey, iKeyVal->first.pItem)) { 2474 | a_names.push_back(iKeyVal->first); 2475 | pLastKey = iKeyVal->first.pItem; 2476 | } 2477 | } 2478 | 2479 | return true; 2480 | } 2481 | 2482 | template 2483 | SI_Error 2484 | CSimpleIniTempl::SaveFile( 2485 | const char * a_pszFile, 2486 | bool a_bAddSignature 2487 | ) const 2488 | { 2489 | FILE * fp = NULL; 2490 | #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE 2491 | fopen_s(&fp, a_pszFile, "wb"); 2492 | #else // !__STDC_WANT_SECURE_LIB__ 2493 | fp = fopen(a_pszFile, "wb"); 2494 | #endif // __STDC_WANT_SECURE_LIB__ 2495 | if (!fp) return SI_FILE; 2496 | SI_Error rc = SaveFile(fp, a_bAddSignature); 2497 | fclose(fp); 2498 | return rc; 2499 | } 2500 | 2501 | #ifdef SI_HAS_WIDE_FILE 2502 | template 2503 | SI_Error 2504 | CSimpleIniTempl::SaveFile( 2505 | const SI_WCHAR_T * a_pwszFile, 2506 | bool a_bAddSignature 2507 | ) const 2508 | { 2509 | #ifdef _WIN32 2510 | FILE * fp = NULL; 2511 | #if __STDC_WANT_SECURE_LIB__ && !_WIN32_WCE 2512 | _wfopen_s(&fp, a_pwszFile, L"wb"); 2513 | #else // !__STDC_WANT_SECURE_LIB__ 2514 | fp = _wfopen(a_pwszFile, L"wb"); 2515 | #endif // __STDC_WANT_SECURE_LIB__ 2516 | if (!fp) return SI_FILE; 2517 | SI_Error rc = SaveFile(fp, a_bAddSignature); 2518 | fclose(fp); 2519 | return rc; 2520 | #else // !_WIN32 (therefore SI_CONVERT_ICU) 2521 | char szFile[256]; 2522 | u_austrncpy(szFile, a_pwszFile, sizeof(szFile)); 2523 | return SaveFile(szFile, a_bAddSignature); 2524 | #endif // _WIN32 2525 | } 2526 | #endif // SI_HAS_WIDE_FILE 2527 | 2528 | template 2529 | SI_Error 2530 | CSimpleIniTempl::SaveFile( 2531 | FILE * a_pFile, 2532 | bool a_bAddSignature 2533 | ) const 2534 | { 2535 | FileWriter writer(a_pFile); 2536 | return Save(writer, a_bAddSignature); 2537 | } 2538 | 2539 | template 2540 | SI_Error 2541 | CSimpleIniTempl::Save( 2542 | OutputWriter & a_oOutput, 2543 | bool a_bAddSignature 2544 | ) const 2545 | { 2546 | Converter convert(m_bStoreIsUtf8); 2547 | 2548 | // add the UTF-8 signature if it is desired 2549 | if (m_bStoreIsUtf8 && a_bAddSignature) { 2550 | a_oOutput.Write(SI_UTF8_SIGNATURE); 2551 | } 2552 | 2553 | // get all of the sections sorted in load order 2554 | TNamesDepend oSections; 2555 | GetAllSections(oSections); 2556 | #if defined(_MSC_VER) && _MSC_VER <= 1200 2557 | oSections.sort(); 2558 | #elif defined(__BORLANDC__) 2559 | oSections.sort(Entry::LoadOrder()); 2560 | #else 2561 | oSections.sort(typename Entry::LoadOrder()); 2562 | #endif 2563 | 2564 | // if there is an empty section name, then it must be written out first 2565 | // regardless of the load order 2566 | typename TNamesDepend::iterator is = oSections.begin(); 2567 | for (; is != oSections.end(); ++is) { 2568 | if (!*is->pItem) { 2569 | // move the empty section name to the front of the section list 2570 | if (is != oSections.begin()) { 2571 | oSections.splice(oSections.begin(), oSections, is, std::next(is)); 2572 | } 2573 | break; 2574 | } 2575 | } 2576 | 2577 | // write the file comment if we have one 2578 | bool bNeedNewLine = false; 2579 | if (m_pFileComment) { 2580 | if (!OutputMultiLineText(a_oOutput, convert, m_pFileComment)) { 2581 | return SI_FAIL; 2582 | } 2583 | bNeedNewLine = true; 2584 | } 2585 | 2586 | // iterate through our sections and output the data 2587 | typename TNamesDepend::const_iterator iSection = oSections.begin(); 2588 | for ( ; iSection != oSections.end(); ++iSection ) { 2589 | // write out the comment if there is one 2590 | if (iSection->pComment) { 2591 | if (bNeedNewLine) { 2592 | a_oOutput.Write(SI_NEWLINE_A); 2593 | a_oOutput.Write(SI_NEWLINE_A); 2594 | } 2595 | if (!OutputMultiLineText(a_oOutput, convert, iSection->pComment)) { 2596 | return SI_FAIL; 2597 | } 2598 | bNeedNewLine = false; 2599 | } 2600 | 2601 | if (bNeedNewLine) { 2602 | a_oOutput.Write(SI_NEWLINE_A); 2603 | a_oOutput.Write(SI_NEWLINE_A); 2604 | bNeedNewLine = false; 2605 | } 2606 | 2607 | // write the section (unless there is no section name) 2608 | if (*iSection->pItem) { 2609 | if (!convert.ConvertToStore(iSection->pItem)) { 2610 | return SI_FAIL; 2611 | } 2612 | a_oOutput.Write("["); 2613 | a_oOutput.Write(convert.Data()); 2614 | a_oOutput.Write("]"); 2615 | a_oOutput.Write(SI_NEWLINE_A); 2616 | } 2617 | 2618 | // get all of the keys sorted in load order 2619 | TNamesDepend oKeys; 2620 | GetAllKeys(iSection->pItem, oKeys); 2621 | #if defined(_MSC_VER) && _MSC_VER <= 1200 2622 | oKeys.sort(); 2623 | #elif defined(__BORLANDC__) 2624 | oKeys.sort(Entry::LoadOrder()); 2625 | #else 2626 | oKeys.sort(typename Entry::LoadOrder()); 2627 | #endif 2628 | 2629 | // write all keys and values 2630 | typename TNamesDepend::const_iterator iKey = oKeys.begin(); 2631 | for ( ; iKey != oKeys.end(); ++iKey) { 2632 | // get all values for this key 2633 | TNamesDepend oValues; 2634 | GetAllValues(iSection->pItem, iKey->pItem, oValues); 2635 | 2636 | typename TNamesDepend::const_iterator iValue = oValues.begin(); 2637 | for ( ; iValue != oValues.end(); ++iValue) { 2638 | // write out the comment if there is one 2639 | if (iValue->pComment) { 2640 | a_oOutput.Write(SI_NEWLINE_A); 2641 | if (!OutputMultiLineText(a_oOutput, convert, iValue->pComment)) { 2642 | return SI_FAIL; 2643 | } 2644 | } 2645 | 2646 | // write the key 2647 | if (!convert.ConvertToStore(iKey->pItem)) { 2648 | return SI_FAIL; 2649 | } 2650 | a_oOutput.Write(convert.Data()); 2651 | 2652 | // write the value as long 2653 | if (*iValue->pItem || !m_bAllowKeyOnly) { 2654 | if (!convert.ConvertToStore(iValue->pItem)) { 2655 | return SI_FAIL; 2656 | } 2657 | a_oOutput.Write(m_bSpaces ? " = " : "="); 2658 | if (m_bParseQuotes && IsSingleLineQuotedValue(iValue->pItem)) { 2659 | // the only way to preserve external whitespace on a value (i.e. before or after) 2660 | // is to quote it. This is simple quoting, we don't escape quotes within the data. 2661 | a_oOutput.Write("\""); 2662 | a_oOutput.Write(convert.Data()); 2663 | a_oOutput.Write("\""); 2664 | } 2665 | else if (m_bAllowMultiLine && IsMultiLineData(iValue->pItem)) { 2666 | // multi-line data needs to be processed specially to ensure 2667 | // that we use the correct newline format for the current system 2668 | a_oOutput.Write("<<pItem)) { 2670 | return SI_FAIL; 2671 | } 2672 | a_oOutput.Write("END_OF_TEXT"); 2673 | } 2674 | else { 2675 | a_oOutput.Write(convert.Data()); 2676 | } 2677 | } 2678 | a_oOutput.Write(SI_NEWLINE_A); 2679 | } 2680 | } 2681 | 2682 | bNeedNewLine = true; 2683 | } 2684 | 2685 | return SI_OK; 2686 | } 2687 | 2688 | template 2689 | bool 2690 | CSimpleIniTempl::OutputMultiLineText( 2691 | OutputWriter & a_oOutput, 2692 | Converter & a_oConverter, 2693 | const SI_CHAR * a_pText 2694 | ) const 2695 | { 2696 | const SI_CHAR * pEndOfLine; 2697 | SI_CHAR cEndOfLineChar = *a_pText; 2698 | while (cEndOfLineChar) { 2699 | // find the end of this line 2700 | pEndOfLine = a_pText; 2701 | for (; *pEndOfLine && *pEndOfLine != '\n'; ++pEndOfLine) /*loop*/ ; 2702 | cEndOfLineChar = *pEndOfLine; 2703 | 2704 | // temporarily null terminate, convert and output the line 2705 | *const_cast(pEndOfLine) = 0; 2706 | if (!a_oConverter.ConvertToStore(a_pText)) { 2707 | return false; 2708 | } 2709 | *const_cast(pEndOfLine) = cEndOfLineChar; 2710 | a_pText += (pEndOfLine - a_pText) + 1; 2711 | a_oOutput.Write(a_oConverter.Data()); 2712 | a_oOutput.Write(SI_NEWLINE_A); 2713 | } 2714 | return true; 2715 | } 2716 | 2717 | template 2718 | bool 2719 | CSimpleIniTempl::Delete( 2720 | const SI_CHAR * a_pSection, 2721 | const SI_CHAR * a_pKey, 2722 | bool a_bRemoveEmpty 2723 | ) 2724 | { 2725 | return DeleteValue(a_pSection, a_pKey, NULL, a_bRemoveEmpty); 2726 | } 2727 | 2728 | template 2729 | bool 2730 | CSimpleIniTempl::DeleteValue( 2731 | const SI_CHAR * a_pSection, 2732 | const SI_CHAR * a_pKey, 2733 | const SI_CHAR * a_pValue, 2734 | bool a_bRemoveEmpty 2735 | ) 2736 | { 2737 | if (!a_pSection) { 2738 | return false; 2739 | } 2740 | 2741 | typename TSection::iterator iSection = m_data.find(a_pSection); 2742 | if (iSection == m_data.end()) { 2743 | return false; 2744 | } 2745 | 2746 | // remove a single key if we have a keyname 2747 | if (a_pKey) { 2748 | typename TKeyVal::iterator iKeyVal = iSection->second.find(a_pKey); 2749 | if (iKeyVal == iSection->second.end()) { 2750 | return false; 2751 | } 2752 | 2753 | const static SI_STRLESS isLess = SI_STRLESS(); 2754 | 2755 | // remove any copied strings and then the key 2756 | typename TKeyVal::iterator iDelete; 2757 | bool bDeleted = false; 2758 | do { 2759 | iDelete = iKeyVal++; 2760 | 2761 | if(a_pValue == NULL || 2762 | (isLess(a_pValue, iDelete->second) == false && 2763 | isLess(iDelete->second, a_pValue) == false)) { 2764 | DeleteString(iDelete->first.pItem); 2765 | DeleteString(iDelete->second); 2766 | iSection->second.erase(iDelete); 2767 | bDeleted = true; 2768 | } 2769 | } 2770 | while (iKeyVal != iSection->second.end() 2771 | && !IsLess(a_pKey, iKeyVal->first.pItem)); 2772 | 2773 | if(!bDeleted) { 2774 | return false; 2775 | } 2776 | 2777 | // done now if the section is not empty or we are not pruning away 2778 | // the empty sections. Otherwise let it fall through into the section 2779 | // deletion code 2780 | if (!a_bRemoveEmpty || !iSection->second.empty()) { 2781 | return true; 2782 | } 2783 | } 2784 | else { 2785 | // delete all copied strings from this section. The actual 2786 | // entries will be removed when the section is removed. 2787 | typename TKeyVal::iterator iKeyVal = iSection->second.begin(); 2788 | for ( ; iKeyVal != iSection->second.end(); ++iKeyVal) { 2789 | DeleteString(iKeyVal->first.pItem); 2790 | DeleteString(iKeyVal->second); 2791 | } 2792 | } 2793 | 2794 | // delete the section itself 2795 | DeleteString(iSection->first.pItem); 2796 | m_data.erase(iSection); 2797 | 2798 | return true; 2799 | } 2800 | 2801 | template 2802 | void 2803 | CSimpleIniTempl::DeleteString( 2804 | const SI_CHAR * a_pString 2805 | ) 2806 | { 2807 | // strings may exist either inside the data block, or they will be 2808 | // individually allocated and stored in m_strings. We only physically 2809 | // delete those stored in m_strings. 2810 | if (a_pString < m_pData || a_pString >= m_pData + m_uDataLen) { 2811 | typename TNamesDepend::iterator i = m_strings.begin(); 2812 | for (;i != m_strings.end(); ++i) { 2813 | if (a_pString == i->pItem) { 2814 | delete[] const_cast(i->pItem); 2815 | m_strings.erase(i); 2816 | break; 2817 | } 2818 | } 2819 | } 2820 | } 2821 | 2822 | // --------------------------------------------------------------------------- 2823 | // CONVERSION FUNCTIONS 2824 | // --------------------------------------------------------------------------- 2825 | 2826 | // Defines the conversion classes for different libraries. Before including 2827 | // SimpleIni.h, set the converter that you wish you use by defining one of the 2828 | // following symbols. 2829 | // 2830 | // SI_NO_CONVERSION Do not make the "W" wide character version of the 2831 | // library available. Only CSimpleIniA etc is defined. 2832 | // SI_CONVERT_GENERIC Use the Unicode reference conversion library in 2833 | // the accompanying files ConvertUTF.h/c 2834 | // SI_CONVERT_ICU Use the IBM ICU conversion library. Requires 2835 | // ICU headers on include path and icuuc.lib 2836 | // SI_CONVERT_WIN32 Use the Win32 API functions for conversion. 2837 | 2838 | #if !defined(SI_NO_CONVERSION) && !defined(SI_CONVERT_GENERIC) && !defined(SI_CONVERT_WIN32) && !defined(SI_CONVERT_ICU) 2839 | # ifdef _WIN32 2840 | # define SI_CONVERT_WIN32 2841 | # else 2842 | # define SI_CONVERT_GENERIC 2843 | # endif 2844 | #endif 2845 | 2846 | /** 2847 | * Generic case-sensitive less than comparison. This class returns numerically 2848 | * ordered ASCII case-sensitive text for all possible sizes and types of 2849 | * SI_CHAR. 2850 | */ 2851 | template 2852 | struct SI_GenericCase { 2853 | bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { 2854 | long cmp; 2855 | for ( ;*pLeft && *pRight; ++pLeft, ++pRight) { 2856 | cmp = (long) *pLeft - (long) *pRight; 2857 | if (cmp != 0) { 2858 | return cmp < 0; 2859 | } 2860 | } 2861 | return *pRight != 0; 2862 | } 2863 | }; 2864 | 2865 | /** 2866 | * Generic ASCII case-insensitive less than comparison. This class returns 2867 | * numerically ordered ASCII case-insensitive text for all possible sizes 2868 | * and types of SI_CHAR. It is not safe for MBCS text comparison where 2869 | * ASCII A-Z characters are used in the encoding of multi-byte characters. 2870 | */ 2871 | template 2872 | struct SI_GenericNoCase { 2873 | inline SI_CHAR locase(SI_CHAR ch) const { 2874 | return (ch < 'A' || ch > 'Z') ? ch : (ch - 'A' + 'a'); 2875 | } 2876 | bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { 2877 | long cmp; 2878 | for ( ;*pLeft && *pRight; ++pLeft, ++pRight) { 2879 | cmp = (long) locase(*pLeft) - (long) locase(*pRight); 2880 | if (cmp != 0) { 2881 | return cmp < 0; 2882 | } 2883 | } 2884 | return *pRight != 0; 2885 | } 2886 | }; 2887 | 2888 | /** 2889 | * Null conversion class for MBCS/UTF-8 to char (or equivalent). 2890 | */ 2891 | template 2892 | class SI_ConvertA { 2893 | bool m_bStoreIsUtf8; 2894 | protected: 2895 | SI_ConvertA() { } 2896 | public: 2897 | SI_ConvertA(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { } 2898 | 2899 | /* copy and assignment */ 2900 | SI_ConvertA(const SI_ConvertA & rhs) { operator=(rhs); } 2901 | SI_ConvertA & operator=(const SI_ConvertA & rhs) { 2902 | m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8; 2903 | return *this; 2904 | } 2905 | 2906 | /** Calculate the number of SI_CHAR required for converting the input 2907 | * from the storage format. The storage format is always UTF-8 or MBCS. 2908 | * 2909 | * @param a_pInputData Data in storage format to be converted to SI_CHAR. 2910 | * @param a_uInputDataLen Length of storage format data in bytes. This 2911 | * must be the actual length of the data, including 2912 | * NULL byte if NULL terminated string is required. 2913 | * @return Number of SI_CHAR required by the string when 2914 | * converted. If there are embedded NULL bytes in the 2915 | * input data, only the string up and not including 2916 | * the NULL byte will be converted. 2917 | * @return -1 cast to size_t on a conversion error. 2918 | */ 2919 | size_t SizeFromStore( 2920 | const char * a_pInputData, 2921 | size_t a_uInputDataLen) 2922 | { 2923 | (void)a_pInputData; 2924 | SI_ASSERT(a_uInputDataLen != (size_t) -1); 2925 | 2926 | // ASCII/MBCS/UTF-8 needs no conversion 2927 | return a_uInputDataLen; 2928 | } 2929 | 2930 | /** Convert the input string from the storage format to SI_CHAR. 2931 | * The storage format is always UTF-8 or MBCS. 2932 | * 2933 | * @param a_pInputData Data in storage format to be converted to SI_CHAR. 2934 | * @param a_uInputDataLen Length of storage format data in bytes. This 2935 | * must be the actual length of the data, including 2936 | * NULL byte if NULL terminated string is required. 2937 | * @param a_pOutputData Pointer to the output buffer to received the 2938 | * converted data. 2939 | * @param a_uOutputDataSize Size of the output buffer in SI_CHAR. 2940 | * @return true if all of the input data was successfully 2941 | * converted. 2942 | */ 2943 | bool ConvertFromStore( 2944 | const char * a_pInputData, 2945 | size_t a_uInputDataLen, 2946 | SI_CHAR * a_pOutputData, 2947 | size_t a_uOutputDataSize) 2948 | { 2949 | // ASCII/MBCS/UTF-8 needs no conversion 2950 | if (a_uInputDataLen > a_uOutputDataSize) { 2951 | return false; 2952 | } 2953 | memcpy(a_pOutputData, a_pInputData, a_uInputDataLen); 2954 | return true; 2955 | } 2956 | 2957 | /** Calculate the number of char required by the storage format of this 2958 | * data. The storage format is always UTF-8 or MBCS. 2959 | * 2960 | * @param a_pInputData NULL terminated string to calculate the number of 2961 | * bytes required to be converted to storage format. 2962 | * @return Number of bytes required by the string when 2963 | * converted to storage format. This size always 2964 | * includes space for the terminating NULL character. 2965 | * @return -1 cast to size_t on a conversion error. 2966 | */ 2967 | size_t SizeToStore( 2968 | const SI_CHAR * a_pInputData) 2969 | { 2970 | // ASCII/MBCS/UTF-8 needs no conversion 2971 | return strlen((const char *)a_pInputData) + 1; 2972 | } 2973 | 2974 | /** Convert the input string to the storage format of this data. 2975 | * The storage format is always UTF-8 or MBCS. 2976 | * 2977 | * @param a_pInputData NULL terminated source string to convert. All of 2978 | * the data will be converted including the 2979 | * terminating NULL character. 2980 | * @param a_pOutputData Pointer to the buffer to receive the converted 2981 | * string. 2982 | * @param a_uOutputDataSize Size of the output buffer in char. 2983 | * @return true if all of the input data, including the 2984 | * terminating NULL character was successfully 2985 | * converted. 2986 | */ 2987 | bool ConvertToStore( 2988 | const SI_CHAR * a_pInputData, 2989 | char * a_pOutputData, 2990 | size_t a_uOutputDataSize) 2991 | { 2992 | // calc input string length (SI_CHAR type and size independent) 2993 | size_t uInputLen = strlen((const char *)a_pInputData) + 1; 2994 | if (uInputLen > a_uOutputDataSize) { 2995 | return false; 2996 | } 2997 | 2998 | // ascii/UTF-8 needs no conversion 2999 | memcpy(a_pOutputData, a_pInputData, uInputLen); 3000 | return true; 3001 | } 3002 | }; 3003 | 3004 | 3005 | // --------------------------------------------------------------------------- 3006 | // SI_CONVERT_GENERIC 3007 | // --------------------------------------------------------------------------- 3008 | #ifdef SI_CONVERT_GENERIC 3009 | 3010 | #define SI_Case SI_GenericCase 3011 | #define SI_NoCase SI_GenericNoCase 3012 | 3013 | #include 3014 | #include "ConvertUTF.h" 3015 | 3016 | /** 3017 | * Converts UTF-8 to a wchar_t (or equivalent) using the Unicode reference 3018 | * library functions. This can be used on all platforms. 3019 | */ 3020 | template 3021 | class SI_ConvertW { 3022 | bool m_bStoreIsUtf8; 3023 | protected: 3024 | SI_ConvertW() { } 3025 | public: 3026 | SI_ConvertW(bool a_bStoreIsUtf8) : m_bStoreIsUtf8(a_bStoreIsUtf8) { } 3027 | 3028 | /* copy and assignment */ 3029 | SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); } 3030 | SI_ConvertW & operator=(const SI_ConvertW & rhs) { 3031 | m_bStoreIsUtf8 = rhs.m_bStoreIsUtf8; 3032 | return *this; 3033 | } 3034 | 3035 | /** Calculate the number of SI_CHAR required for converting the input 3036 | * from the storage format. The storage format is always UTF-8 or MBCS. 3037 | * 3038 | * @param a_pInputData Data in storage format to be converted to SI_CHAR. 3039 | * @param a_uInputDataLen Length of storage format data in bytes. This 3040 | * must be the actual length of the data, including 3041 | * NULL byte if NULL terminated string is required. 3042 | * @return Number of SI_CHAR required by the string when 3043 | * converted. If there are embedded NULL bytes in the 3044 | * input data, only the string up and not including 3045 | * the NULL byte will be converted. 3046 | * @return -1 cast to size_t on a conversion error. 3047 | */ 3048 | size_t SizeFromStore( 3049 | const char * a_pInputData, 3050 | size_t a_uInputDataLen) 3051 | { 3052 | SI_ASSERT(a_uInputDataLen != (size_t) -1); 3053 | 3054 | if (m_bStoreIsUtf8) { 3055 | // worst case scenario for UTF-8 to wchar_t is 1 char -> 1 wchar_t 3056 | // so we just return the same number of characters required as for 3057 | // the source text. 3058 | return a_uInputDataLen; 3059 | } 3060 | 3061 | #if defined(SI_NO_MBSTOWCS_NULL) || (!defined(_MSC_VER) && !defined(_linux)) 3062 | // fall back processing for platforms that don't support a NULL dest to mbstowcs 3063 | // worst case scenario is 1:1, this will be a sufficient buffer size 3064 | (void)a_pInputData; 3065 | return a_uInputDataLen; 3066 | #else 3067 | // get the actual required buffer size 3068 | return mbstowcs(NULL, a_pInputData, a_uInputDataLen); 3069 | #endif 3070 | } 3071 | 3072 | /** Convert the input string from the storage format to SI_CHAR. 3073 | * The storage format is always UTF-8 or MBCS. 3074 | * 3075 | * @param a_pInputData Data in storage format to be converted to SI_CHAR. 3076 | * @param a_uInputDataLen Length of storage format data in bytes. This 3077 | * must be the actual length of the data, including 3078 | * NULL byte if NULL terminated string is required. 3079 | * @param a_pOutputData Pointer to the output buffer to received the 3080 | * converted data. 3081 | * @param a_uOutputDataSize Size of the output buffer in SI_CHAR. 3082 | * @return true if all of the input data was successfully 3083 | * converted. 3084 | */ 3085 | bool ConvertFromStore( 3086 | const char * a_pInputData, 3087 | size_t a_uInputDataLen, 3088 | SI_CHAR * a_pOutputData, 3089 | size_t a_uOutputDataSize) 3090 | { 3091 | if (m_bStoreIsUtf8) { 3092 | // This uses the Unicode reference implementation to do the 3093 | // conversion from UTF-8 to wchar_t. The required files are 3094 | // ConvertUTF.h and ConvertUTF.c which should be included in 3095 | // the distribution but are publically available from unicode.org 3096 | // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/ 3097 | ConversionResult retval; 3098 | const UTF8 * pUtf8 = (const UTF8 *) a_pInputData; 3099 | if (sizeof(wchar_t) == sizeof(UTF32)) { 3100 | UTF32 * pUtf32 = (UTF32 *) a_pOutputData; 3101 | retval = ConvertUTF8toUTF32( 3102 | &pUtf8, pUtf8 + a_uInputDataLen, 3103 | &pUtf32, pUtf32 + a_uOutputDataSize, 3104 | lenientConversion); 3105 | } 3106 | else if (sizeof(wchar_t) == sizeof(UTF16)) { 3107 | UTF16 * pUtf16 = (UTF16 *) a_pOutputData; 3108 | retval = ConvertUTF8toUTF16( 3109 | &pUtf8, pUtf8 + a_uInputDataLen, 3110 | &pUtf16, pUtf16 + a_uOutputDataSize, 3111 | lenientConversion); 3112 | } 3113 | return retval == conversionOK; 3114 | } 3115 | 3116 | // convert to wchar_t 3117 | size_t retval = mbstowcs(a_pOutputData, 3118 | a_pInputData, a_uOutputDataSize); 3119 | return retval != (size_t)(-1); 3120 | } 3121 | 3122 | /** Calculate the number of char required by the storage format of this 3123 | * data. The storage format is always UTF-8 or MBCS. 3124 | * 3125 | * @param a_pInputData NULL terminated string to calculate the number of 3126 | * bytes required to be converted to storage format. 3127 | * @return Number of bytes required by the string when 3128 | * converted to storage format. This size always 3129 | * includes space for the terminating NULL character. 3130 | * @return -1 cast to size_t on a conversion error. 3131 | */ 3132 | size_t SizeToStore( 3133 | const SI_CHAR * a_pInputData) 3134 | { 3135 | if (m_bStoreIsUtf8) { 3136 | // worst case scenario for wchar_t to UTF-8 is 1 wchar_t -> 6 char 3137 | size_t uLen = 0; 3138 | while (a_pInputData[uLen]) { 3139 | ++uLen; 3140 | } 3141 | return (6 * uLen) + 1; 3142 | } 3143 | else { 3144 | size_t uLen = wcstombs(NULL, a_pInputData, 0); 3145 | if (uLen == (size_t)(-1)) { 3146 | return uLen; 3147 | } 3148 | return uLen + 1; // include NULL terminator 3149 | } 3150 | } 3151 | 3152 | /** Convert the input string to the storage format of this data. 3153 | * The storage format is always UTF-8 or MBCS. 3154 | * 3155 | * @param a_pInputData NULL terminated source string to convert. All of 3156 | * the data will be converted including the 3157 | * terminating NULL character. 3158 | * @param a_pOutputData Pointer to the buffer to receive the converted 3159 | * string. 3160 | * @param a_uOutputDataSize Size of the output buffer in char. 3161 | * @return true if all of the input data, including the 3162 | * terminating NULL character was successfully 3163 | * converted. 3164 | */ 3165 | bool ConvertToStore( 3166 | const SI_CHAR * a_pInputData, 3167 | char * a_pOutputData, 3168 | size_t a_uOutputDataSize 3169 | ) 3170 | { 3171 | if (m_bStoreIsUtf8) { 3172 | // calc input string length (SI_CHAR type and size independent) 3173 | size_t uInputLen = 0; 3174 | while (a_pInputData[uInputLen]) { 3175 | ++uInputLen; 3176 | } 3177 | ++uInputLen; // include the NULL char 3178 | 3179 | // This uses the Unicode reference implementation to do the 3180 | // conversion from wchar_t to UTF-8. The required files are 3181 | // ConvertUTF.h and ConvertUTF.c which should be included in 3182 | // the distribution but are publically available from unicode.org 3183 | // at http://www.unicode.org/Public/PROGRAMS/CVTUTF/ 3184 | ConversionResult retval; 3185 | UTF8 * pUtf8 = (UTF8 *) a_pOutputData; 3186 | if (sizeof(wchar_t) == sizeof(UTF32)) { 3187 | const UTF32 * pUtf32 = (const UTF32 *) a_pInputData; 3188 | retval = ConvertUTF32toUTF8( 3189 | &pUtf32, pUtf32 + uInputLen, 3190 | &pUtf8, pUtf8 + a_uOutputDataSize, 3191 | lenientConversion); 3192 | } 3193 | else if (sizeof(wchar_t) == sizeof(UTF16)) { 3194 | const UTF16 * pUtf16 = (const UTF16 *) a_pInputData; 3195 | retval = ConvertUTF16toUTF8( 3196 | &pUtf16, pUtf16 + uInputLen, 3197 | &pUtf8, pUtf8 + a_uOutputDataSize, 3198 | lenientConversion); 3199 | } 3200 | return retval == conversionOK; 3201 | } 3202 | else { 3203 | size_t retval = wcstombs(a_pOutputData, 3204 | a_pInputData, a_uOutputDataSize); 3205 | return retval != (size_t) -1; 3206 | } 3207 | } 3208 | }; 3209 | 3210 | #endif // SI_CONVERT_GENERIC 3211 | 3212 | 3213 | // --------------------------------------------------------------------------- 3214 | // SI_CONVERT_ICU 3215 | // --------------------------------------------------------------------------- 3216 | #ifdef SI_CONVERT_ICU 3217 | 3218 | #define SI_Case SI_GenericCase 3219 | #define SI_NoCase SI_GenericNoCase 3220 | 3221 | #include 3222 | 3223 | /** 3224 | * Converts MBCS/UTF-8 to UChar using ICU. This can be used on all platforms. 3225 | */ 3226 | template 3227 | class SI_ConvertW { 3228 | const char * m_pEncoding; 3229 | UConverter * m_pConverter; 3230 | protected: 3231 | SI_ConvertW() : m_pEncoding(NULL), m_pConverter(NULL) { } 3232 | public: 3233 | SI_ConvertW(bool a_bStoreIsUtf8) : m_pConverter(NULL) { 3234 | m_pEncoding = a_bStoreIsUtf8 ? "UTF-8" : NULL; 3235 | } 3236 | 3237 | /* copy and assignment */ 3238 | SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); } 3239 | SI_ConvertW & operator=(const SI_ConvertW & rhs) { 3240 | m_pEncoding = rhs.m_pEncoding; 3241 | m_pConverter = NULL; 3242 | return *this; 3243 | } 3244 | ~SI_ConvertW() { if (m_pConverter) ucnv_close(m_pConverter); } 3245 | 3246 | /** Calculate the number of UChar required for converting the input 3247 | * from the storage format. The storage format is always UTF-8 or MBCS. 3248 | * 3249 | * @param a_pInputData Data in storage format to be converted to UChar. 3250 | * @param a_uInputDataLen Length of storage format data in bytes. This 3251 | * must be the actual length of the data, including 3252 | * NULL byte if NULL terminated string is required. 3253 | * @return Number of UChar required by the string when 3254 | * converted. If there are embedded NULL bytes in the 3255 | * input data, only the string up and not including 3256 | * the NULL byte will be converted. 3257 | * @return -1 cast to size_t on a conversion error. 3258 | */ 3259 | size_t SizeFromStore( 3260 | const char * a_pInputData, 3261 | size_t a_uInputDataLen) 3262 | { 3263 | SI_ASSERT(a_uInputDataLen != (size_t) -1); 3264 | 3265 | UErrorCode nError; 3266 | 3267 | if (!m_pConverter) { 3268 | nError = U_ZERO_ERROR; 3269 | m_pConverter = ucnv_open(m_pEncoding, &nError); 3270 | if (U_FAILURE(nError)) { 3271 | return (size_t) -1; 3272 | } 3273 | } 3274 | 3275 | nError = U_ZERO_ERROR; 3276 | int32_t nLen = ucnv_toUChars(m_pConverter, NULL, 0, 3277 | a_pInputData, (int32_t) a_uInputDataLen, &nError); 3278 | if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) { 3279 | return (size_t) -1; 3280 | } 3281 | 3282 | return (size_t) nLen; 3283 | } 3284 | 3285 | /** Convert the input string from the storage format to UChar. 3286 | * The storage format is always UTF-8 or MBCS. 3287 | * 3288 | * @param a_pInputData Data in storage format to be converted to UChar. 3289 | * @param a_uInputDataLen Length of storage format data in bytes. This 3290 | * must be the actual length of the data, including 3291 | * NULL byte if NULL terminated string is required. 3292 | * @param a_pOutputData Pointer to the output buffer to received the 3293 | * converted data. 3294 | * @param a_uOutputDataSize Size of the output buffer in UChar. 3295 | * @return true if all of the input data was successfully 3296 | * converted. 3297 | */ 3298 | bool ConvertFromStore( 3299 | const char * a_pInputData, 3300 | size_t a_uInputDataLen, 3301 | UChar * a_pOutputData, 3302 | size_t a_uOutputDataSize) 3303 | { 3304 | UErrorCode nError; 3305 | 3306 | if (!m_pConverter) { 3307 | nError = U_ZERO_ERROR; 3308 | m_pConverter = ucnv_open(m_pEncoding, &nError); 3309 | if (U_FAILURE(nError)) { 3310 | return false; 3311 | } 3312 | } 3313 | 3314 | nError = U_ZERO_ERROR; 3315 | ucnv_toUChars(m_pConverter, 3316 | a_pOutputData, (int32_t) a_uOutputDataSize, 3317 | a_pInputData, (int32_t) a_uInputDataLen, &nError); 3318 | if (U_FAILURE(nError)) { 3319 | return false; 3320 | } 3321 | 3322 | return true; 3323 | } 3324 | 3325 | /** Calculate the number of char required by the storage format of this 3326 | * data. The storage format is always UTF-8 or MBCS. 3327 | * 3328 | * @param a_pInputData NULL terminated string to calculate the number of 3329 | * bytes required to be converted to storage format. 3330 | * @return Number of bytes required by the string when 3331 | * converted to storage format. This size always 3332 | * includes space for the terminating NULL character. 3333 | * @return -1 cast to size_t on a conversion error. 3334 | */ 3335 | size_t SizeToStore( 3336 | const UChar * a_pInputData) 3337 | { 3338 | UErrorCode nError; 3339 | 3340 | if (!m_pConverter) { 3341 | nError = U_ZERO_ERROR; 3342 | m_pConverter = ucnv_open(m_pEncoding, &nError); 3343 | if (U_FAILURE(nError)) { 3344 | return (size_t) -1; 3345 | } 3346 | } 3347 | 3348 | nError = U_ZERO_ERROR; 3349 | int32_t nLen = ucnv_fromUChars(m_pConverter, NULL, 0, 3350 | a_pInputData, -1, &nError); 3351 | if (U_FAILURE(nError) && nError != U_BUFFER_OVERFLOW_ERROR) { 3352 | return (size_t) -1; 3353 | } 3354 | 3355 | return (size_t) nLen + 1; 3356 | } 3357 | 3358 | /** Convert the input string to the storage format of this data. 3359 | * The storage format is always UTF-8 or MBCS. 3360 | * 3361 | * @param a_pInputData NULL terminated source string to convert. All of 3362 | * the data will be converted including the 3363 | * terminating NULL character. 3364 | * @param a_pOutputData Pointer to the buffer to receive the converted 3365 | * string. 3366 | * @param a_pOutputDataSize Size of the output buffer in char. 3367 | * @return true if all of the input data, including the 3368 | * terminating NULL character was successfully 3369 | * converted. 3370 | */ 3371 | bool ConvertToStore( 3372 | const UChar * a_pInputData, 3373 | char * a_pOutputData, 3374 | size_t a_uOutputDataSize) 3375 | { 3376 | UErrorCode nError; 3377 | 3378 | if (!m_pConverter) { 3379 | nError = U_ZERO_ERROR; 3380 | m_pConverter = ucnv_open(m_pEncoding, &nError); 3381 | if (U_FAILURE(nError)) { 3382 | return false; 3383 | } 3384 | } 3385 | 3386 | nError = U_ZERO_ERROR; 3387 | ucnv_fromUChars(m_pConverter, 3388 | a_pOutputData, (int32_t) a_uOutputDataSize, 3389 | a_pInputData, -1, &nError); 3390 | if (U_FAILURE(nError)) { 3391 | return false; 3392 | } 3393 | 3394 | return true; 3395 | } 3396 | }; 3397 | 3398 | #endif // SI_CONVERT_ICU 3399 | 3400 | 3401 | // --------------------------------------------------------------------------- 3402 | // SI_CONVERT_WIN32 3403 | // --------------------------------------------------------------------------- 3404 | #ifdef SI_CONVERT_WIN32 3405 | 3406 | #define SI_Case SI_GenericCase 3407 | 3408 | // Windows CE doesn't have errno or MBCS libraries 3409 | #ifdef _WIN32_WCE 3410 | # ifndef SI_NO_MBCS 3411 | # define SI_NO_MBCS 3412 | # endif 3413 | #endif 3414 | 3415 | #include 3416 | #ifdef SI_NO_MBCS 3417 | # define SI_NoCase SI_GenericNoCase 3418 | #else // !SI_NO_MBCS 3419 | /** 3420 | * Case-insensitive comparison class using Win32 MBCS functions. This class 3421 | * returns a case-insensitive semi-collation order for MBCS text. It may not 3422 | * be safe for UTF-8 text returned in char format as we don't know what 3423 | * characters will be folded by the function! Therefore, if you are using 3424 | * SI_CHAR == char and SetUnicode(true), then you need to use the generic 3425 | * SI_NoCase class instead. 3426 | */ 3427 | #include 3428 | template 3429 | struct SI_NoCase { 3430 | bool operator()(const SI_CHAR * pLeft, const SI_CHAR * pRight) const { 3431 | if (sizeof(SI_CHAR) == sizeof(char)) { 3432 | return _mbsicmp((const unsigned char *)pLeft, 3433 | (const unsigned char *)pRight) < 0; 3434 | } 3435 | if (sizeof(SI_CHAR) == sizeof(wchar_t)) { 3436 | return _wcsicmp((const wchar_t *)pLeft, 3437 | (const wchar_t *)pRight) < 0; 3438 | } 3439 | return SI_GenericNoCase()(pLeft, pRight); 3440 | } 3441 | }; 3442 | #endif // SI_NO_MBCS 3443 | 3444 | /** 3445 | * Converts MBCS and UTF-8 to a wchar_t (or equivalent) on Windows. This uses 3446 | * only the Win32 functions and doesn't require the external Unicode UTF-8 3447 | * conversion library. It will not work on Windows 95 without using Microsoft 3448 | * Layer for Unicode in your application. 3449 | */ 3450 | template 3451 | class SI_ConvertW { 3452 | UINT m_uCodePage; 3453 | protected: 3454 | SI_ConvertW() { } 3455 | public: 3456 | SI_ConvertW(bool a_bStoreIsUtf8) { 3457 | m_uCodePage = a_bStoreIsUtf8 ? CP_UTF8 : CP_ACP; 3458 | } 3459 | 3460 | /* copy and assignment */ 3461 | SI_ConvertW(const SI_ConvertW & rhs) { operator=(rhs); } 3462 | SI_ConvertW & operator=(const SI_ConvertW & rhs) { 3463 | m_uCodePage = rhs.m_uCodePage; 3464 | return *this; 3465 | } 3466 | 3467 | /** Calculate the number of SI_CHAR required for converting the input 3468 | * from the storage format. The storage format is always UTF-8 or MBCS. 3469 | * 3470 | * @param a_pInputData Data in storage format to be converted to SI_CHAR. 3471 | * @param a_uInputDataLen Length of storage format data in bytes. This 3472 | * must be the actual length of the data, including 3473 | * NULL byte if NULL terminated string is required. 3474 | * @return Number of SI_CHAR required by the string when 3475 | * converted. If there are embedded NULL bytes in the 3476 | * input data, only the string up and not including 3477 | * the NULL byte will be converted. 3478 | * @return -1 cast to size_t on a conversion error. 3479 | */ 3480 | size_t SizeFromStore( 3481 | const char * a_pInputData, 3482 | size_t a_uInputDataLen) 3483 | { 3484 | SI_ASSERT(a_uInputDataLen != (size_t) -1); 3485 | 3486 | int retval = MultiByteToWideChar( 3487 | m_uCodePage, 0, 3488 | a_pInputData, (int) a_uInputDataLen, 3489 | 0, 0); 3490 | return (size_t)(retval > 0 ? retval : -1); 3491 | } 3492 | 3493 | /** Convert the input string from the storage format to SI_CHAR. 3494 | * The storage format is always UTF-8 or MBCS. 3495 | * 3496 | * @param a_pInputData Data in storage format to be converted to SI_CHAR. 3497 | * @param a_uInputDataLen Length of storage format data in bytes. This 3498 | * must be the actual length of the data, including 3499 | * NULL byte if NULL terminated string is required. 3500 | * @param a_pOutputData Pointer to the output buffer to received the 3501 | * converted data. 3502 | * @param a_uOutputDataSize Size of the output buffer in SI_CHAR. 3503 | * @return true if all of the input data was successfully 3504 | * converted. 3505 | */ 3506 | bool ConvertFromStore( 3507 | const char * a_pInputData, 3508 | size_t a_uInputDataLen, 3509 | SI_CHAR * a_pOutputData, 3510 | size_t a_uOutputDataSize) 3511 | { 3512 | int nSize = MultiByteToWideChar( 3513 | m_uCodePage, 0, 3514 | a_pInputData, (int) a_uInputDataLen, 3515 | (wchar_t *) a_pOutputData, (int) a_uOutputDataSize); 3516 | return (nSize > 0); 3517 | } 3518 | 3519 | /** Calculate the number of char required by the storage format of this 3520 | * data. The storage format is always UTF-8. 3521 | * 3522 | * @param a_pInputData NULL terminated string to calculate the number of 3523 | * bytes required to be converted to storage format. 3524 | * @return Number of bytes required by the string when 3525 | * converted to storage format. This size always 3526 | * includes space for the terminating NULL character. 3527 | * @return -1 cast to size_t on a conversion error. 3528 | */ 3529 | size_t SizeToStore( 3530 | const SI_CHAR * a_pInputData) 3531 | { 3532 | int retval = WideCharToMultiByte( 3533 | m_uCodePage, 0, 3534 | (const wchar_t *) a_pInputData, -1, 3535 | 0, 0, 0, 0); 3536 | return (size_t) (retval > 0 ? retval : -1); 3537 | } 3538 | 3539 | /** Convert the input string to the storage format of this data. 3540 | * The storage format is always UTF-8 or MBCS. 3541 | * 3542 | * @param a_pInputData NULL terminated source string to convert. All of 3543 | * the data will be converted including the 3544 | * terminating NULL character. 3545 | * @param a_pOutputData Pointer to the buffer to receive the converted 3546 | * string. 3547 | * @param a_pOutputDataSize Size of the output buffer in char. 3548 | * @return true if all of the input data, including the 3549 | * terminating NULL character was successfully 3550 | * converted. 3551 | */ 3552 | bool ConvertToStore( 3553 | const SI_CHAR * a_pInputData, 3554 | char * a_pOutputData, 3555 | size_t a_uOutputDataSize) 3556 | { 3557 | int retval = WideCharToMultiByte( 3558 | m_uCodePage, 0, 3559 | (const wchar_t *) a_pInputData, -1, 3560 | a_pOutputData, (int) a_uOutputDataSize, 0, 0); 3561 | return retval > 0; 3562 | } 3563 | }; 3564 | 3565 | #endif // SI_CONVERT_WIN32 3566 | 3567 | 3568 | 3569 | // --------------------------------------------------------------------------- 3570 | // SI_NO_CONVERSION 3571 | // --------------------------------------------------------------------------- 3572 | #ifdef SI_NO_CONVERSION 3573 | 3574 | #define SI_Case SI_GenericCase 3575 | #define SI_NoCase SI_GenericNoCase 3576 | 3577 | #endif // SI_NO_CONVERSION 3578 | 3579 | 3580 | 3581 | // --------------------------------------------------------------------------- 3582 | // TYPE DEFINITIONS 3583 | // --------------------------------------------------------------------------- 3584 | 3585 | typedef CSimpleIniTempl,SI_ConvertA > CSimpleIniA; 3587 | typedef CSimpleIniTempl,SI_ConvertA > CSimpleIniCaseA; 3589 | 3590 | #if defined(SI_NO_CONVERSION) 3591 | // if there is no wide char conversion then we don't need to define the 3592 | // widechar "W" versions of CSimpleIni 3593 | # define CSimpleIni CSimpleIniA 3594 | # define CSimpleIniCase CSimpleIniCaseA 3595 | # define SI_NEWLINE SI_NEWLINE_A 3596 | #else 3597 | # if defined(SI_CONVERT_ICU) 3598 | typedef CSimpleIniTempl,SI_ConvertW > CSimpleIniW; 3600 | typedef CSimpleIniTempl,SI_ConvertW > CSimpleIniCaseW; 3602 | # else 3603 | typedef CSimpleIniTempl,SI_ConvertW > CSimpleIniW; 3605 | typedef CSimpleIniTempl,SI_ConvertW > CSimpleIniCaseW; 3607 | # endif 3608 | 3609 | # ifdef _UNICODE 3610 | # define CSimpleIni CSimpleIniW 3611 | # define CSimpleIniCase CSimpleIniCaseW 3612 | # define SI_NEWLINE SI_NEWLINE_W 3613 | # else // !_UNICODE 3614 | # define CSimpleIni CSimpleIniA 3615 | # define CSimpleIniCase CSimpleIniCaseA 3616 | # define SI_NEWLINE SI_NEWLINE_A 3617 | # endif // _UNICODE 3618 | #endif 3619 | 3620 | #ifdef _MSC_VER 3621 | # pragma warning (pop) 3622 | #endif 3623 | 3624 | #endif // INCLUDED_SimpleIni_h 3625 | 3626 | -------------------------------------------------------------------------------- /Manual Map Injector/injector.cpp: -------------------------------------------------------------------------------- 1 | #include "injector.h" 2 | 3 | #if defined(DISABLE_OUTPUT) 4 | #define ILog(data, ...) 5 | #else 6 | #define ILog(text, ...) printf(text, __VA_ARGS__); 7 | #endif 8 | 9 | #ifdef _WIN64 10 | #define CURRENT_ARCH IMAGE_FILE_MACHINE_AMD64 11 | #else 12 | #define CURRENT_ARCH IMAGE_FILE_MACHINE_I386 13 | #endif 14 | 15 | bool ManualMapDll(HANDLE hProc, BYTE *pSrcData, SIZE_T FileSize, bool ClearHeader, bool ClearNonNeededSections, bool AdjustProtections, bool SEHExceptionSupport, DWORD fdwReason, LPVOID lpReserved) { 16 | IMAGE_NT_HEADERS *pOldNtHeader = nullptr; 17 | IMAGE_OPTIONAL_HEADER *pOldOptHeader = nullptr; 18 | IMAGE_FILE_HEADER *pOldFileHeader = nullptr; 19 | BYTE *pTargetBase = nullptr; 20 | 21 | if (reinterpret_cast(pSrcData)->e_magic != 0x5A4D) { //"MZ" 22 | ILog("Invalid file!\n"); 23 | return false; 24 | } 25 | 26 | pOldNtHeader = reinterpret_cast(pSrcData + reinterpret_cast(pSrcData)->e_lfanew); 27 | pOldOptHeader = &pOldNtHeader->OptionalHeader; 28 | pOldFileHeader = &pOldNtHeader->FileHeader; 29 | 30 | if (pOldFileHeader->Machine != CURRENT_ARCH) { 31 | ILog("Invalid platform!\n"); 32 | return false; 33 | } 34 | 35 | pTargetBase = reinterpret_cast(VirtualAllocEx(hProc, nullptr, pOldOptHeader->SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)); 36 | if (!pTargetBase) { 37 | ILog("Target process memory allocation failed(ex) 0x%X\n", GetLastError()); 38 | return false; 39 | } 40 | 41 | DWORD oldp = 0; 42 | VirtualProtectEx(hProc, pTargetBase, pOldOptHeader->SizeOfImage, PAGE_EXECUTE_READWRITE, &oldp); 43 | 44 | MANUAL_MAPPING_DATA data{ 0 }; 45 | data.pLoadLibraryA = LoadLibraryA; 46 | data.pGetProcAddress = GetProcAddress; 47 | #ifdef _WIN64 48 | data.pRtlAddFunctionTable = (f_RtlAddFunctionTable)RtlAddFunctionTable; 49 | #else 50 | SEHExceptionSupport = false; 51 | #endif 52 | data.pbase = pTargetBase; 53 | data.fdwReasonParam = fdwReason; 54 | data.reservedParam = lpReserved; 55 | data.SEHSupport = SEHExceptionSupport; 56 | 57 | 58 | //File header 59 | if (!WriteProcessMemory(hProc, pTargetBase, pSrcData, 0x1000, nullptr)) { //only first 0x1000 bytes for the header 60 | ILog("Write file header failed: 0x%X\n", GetLastError()); 61 | VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE); 62 | return false; 63 | } 64 | 65 | IMAGE_SECTION_HEADER *pSectionHeader = IMAGE_FIRST_SECTION(pOldNtHeader); 66 | for (UINT i = 0; i != pOldFileHeader->NumberOfSections; ++i, ++pSectionHeader) { 67 | if (pSectionHeader->SizeOfRawData) { 68 | if (!WriteProcessMemory(hProc, pTargetBase + pSectionHeader->VirtualAddress, pSrcData + pSectionHeader->PointerToRawData, pSectionHeader->SizeOfRawData, nullptr)) { 69 | ILog("Map sections failed: 0x%x\n", GetLastError()); 70 | VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE); 71 | return false; 72 | } 73 | } 74 | } 75 | 76 | //Mapping params 77 | BYTE *MappingDataAlloc = reinterpret_cast(VirtualAllocEx(hProc, nullptr, sizeof(MANUAL_MAPPING_DATA), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)); 78 | if (!MappingDataAlloc) { 79 | ILog("Target process mapping allocation failed: 0x%X\n", GetLastError()); 80 | VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE); 81 | return false; 82 | } 83 | 84 | if (!WriteProcessMemory(hProc, MappingDataAlloc, &data, sizeof(MANUAL_MAPPING_DATA), nullptr)) { 85 | ILog("Write mapping failed: 0x%X\n", GetLastError()); 86 | VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE); 87 | VirtualFreeEx(hProc, MappingDataAlloc, 0, MEM_RELEASE); 88 | return false; 89 | } 90 | 91 | //Shell code 92 | void *pShellcode = VirtualAllocEx(hProc, nullptr, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); 93 | if (!pShellcode) { 94 | ILog("Memory shellcode allocation failed: 0x%X\n", GetLastError()); 95 | VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE); 96 | VirtualFreeEx(hProc, MappingDataAlloc, 0, MEM_RELEASE); 97 | return false; 98 | } 99 | 100 | if (!WriteProcessMemory(hProc, pShellcode, Shellcode, 0x1000, nullptr)) { 101 | ILog("Write shellcode failed: 0x%X\n", GetLastError()); 102 | VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE); 103 | VirtualFreeEx(hProc, MappingDataAlloc, 0, MEM_RELEASE); 104 | VirtualFreeEx(hProc, pShellcode, 0, MEM_RELEASE); 105 | return false; 106 | } 107 | 108 | ILog("Mapped DLL at %p\n", pTargetBase); 109 | ILog("Mapping info at %p\n", MappingDataAlloc); 110 | ILog("Shell code at %p\n", pShellcode); 111 | 112 | ILog("Data allocated!\n"); 113 | 114 | #ifdef _DEBUG 115 | ILog("My shellcode pointer %p\n", Shellcode); 116 | ILog("Target point %p\n", pShellcode); 117 | system("pause"); 118 | #endif 119 | 120 | HANDLE hThread = CreateRemoteThread(hProc, nullptr, 0, reinterpret_cast(pShellcode), MappingDataAlloc, 0, nullptr); 121 | if (!hThread) { 122 | ILog("Thread creation failed 0x%X\n", GetLastError()); 123 | VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE); 124 | VirtualFreeEx(hProc, MappingDataAlloc, 0, MEM_RELEASE); 125 | VirtualFreeEx(hProc, pShellcode, 0, MEM_RELEASE); 126 | return false; 127 | } 128 | CloseHandle(hThread); 129 | 130 | ILog("[INFO] Thread created at: %p, waiting for return...\n", pShellcode); 131 | 132 | HINSTANCE hCheck = NULL; 133 | while (!hCheck) { 134 | DWORD exitcode = 0; 135 | GetExitCodeProcess(hProc, &exitcode); 136 | if (exitcode != STILL_ACTIVE) { 137 | ILog("Process crashed! Exit code: %d\n", exitcode); 138 | return false; 139 | } 140 | 141 | MANUAL_MAPPING_DATA data_checked{ 0 }; 142 | ReadProcessMemory(hProc, MappingDataAlloc, &data_checked, sizeof(data_checked), nullptr); 143 | hCheck = data_checked.hMod; 144 | 145 | if (hCheck == (HINSTANCE)0x404040) { 146 | ILog("Wrong mapping ptr!\n"); 147 | VirtualFreeEx(hProc, pTargetBase, 0, MEM_RELEASE); 148 | VirtualFreeEx(hProc, MappingDataAlloc, 0, MEM_RELEASE); 149 | VirtualFreeEx(hProc, pShellcode, 0, MEM_RELEASE); 150 | return false; 151 | } 152 | else if (hCheck == (HINSTANCE)0x505050) { 153 | ILog("WARNING: Exception support failed!\n"); 154 | } 155 | 156 | Sleep(10); 157 | } 158 | 159 | BYTE *emptyBuffer = (BYTE *)malloc(1024 * 1024 * 20); 160 | if (emptyBuffer == nullptr) { 161 | ILog("Allocate memory failed!\n"); 162 | return false; 163 | } 164 | memset(emptyBuffer, 0, 1024 * 1024 * 20); 165 | 166 | //CLEAR PE HEAD 167 | if (ClearHeader) { 168 | if (!WriteProcessMemory(hProc, pTargetBase, emptyBuffer, 0x1000, nullptr)) { 169 | ILog("WARNING!: Can't clear HEADER\n"); 170 | } 171 | } 172 | //END CLEAR PE HEAD 173 | 174 | 175 | if (ClearNonNeededSections) { 176 | pSectionHeader = IMAGE_FIRST_SECTION(pOldNtHeader); 177 | for (UINT i = 0; i != pOldFileHeader->NumberOfSections; ++i, ++pSectionHeader) { 178 | if (pSectionHeader->Misc.VirtualSize) { 179 | if ((SEHExceptionSupport ? 0 : strcmp((char *)pSectionHeader->Name, ".pdata") == 0) || 180 | strcmp((char *)pSectionHeader->Name, ".rsrc") == 0 || 181 | strcmp((char *)pSectionHeader->Name, ".reloc") == 0) { 182 | ILog("Processing %s removal\n", pSectionHeader->Name); 183 | if (!WriteProcessMemory(hProc, pTargetBase + pSectionHeader->VirtualAddress, emptyBuffer, pSectionHeader->Misc.VirtualSize, nullptr)) { 184 | ILog("Clear section %s failed: 0x%x\n", pSectionHeader->Name, GetLastError()); 185 | } 186 | } 187 | } 188 | } 189 | } 190 | 191 | if (AdjustProtections) { 192 | pSectionHeader = IMAGE_FIRST_SECTION(pOldNtHeader); 193 | for (UINT i = 0; i != pOldFileHeader->NumberOfSections; ++i, ++pSectionHeader) { 194 | if (pSectionHeader->Misc.VirtualSize) { 195 | DWORD old = 0; 196 | DWORD newP = PAGE_READONLY; 197 | 198 | if ((pSectionHeader->Characteristics & IMAGE_SCN_MEM_WRITE) > 0) { 199 | newP = PAGE_READWRITE; 200 | } 201 | else if ((pSectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE) > 0) { 202 | newP = PAGE_EXECUTE_READ; 203 | } 204 | if (VirtualProtectEx(hProc, pTargetBase + pSectionHeader->VirtualAddress, pSectionHeader->Misc.VirtualSize, newP, &old)) { 205 | ILog("Section %s set as %lX\n", (char *)pSectionHeader->Name, newP); 206 | } 207 | else { 208 | ILog("Error: section %s not set as %lX\n", (char *)pSectionHeader->Name, newP); 209 | } 210 | } 211 | } 212 | DWORD old = 0; 213 | VirtualProtectEx(hProc, pTargetBase, IMAGE_FIRST_SECTION(pOldNtHeader)->VirtualAddress, PAGE_READONLY, &old); 214 | } 215 | 216 | if (!WriteProcessMemory(hProc, pShellcode, emptyBuffer, 0x1000, nullptr)) { 217 | ILog("WARN: Clear shellcode failed\n"); 218 | } 219 | if (!VirtualFreeEx(hProc, pShellcode, 0, MEM_RELEASE)) { 220 | ILog("WARN: Release shell code memory failed\n"); 221 | } 222 | if (!VirtualFreeEx(hProc, MappingDataAlloc, 0, MEM_RELEASE)) { 223 | ILog("WARN: Release mapping data memory failed\n"); 224 | } 225 | 226 | return true; 227 | } 228 | 229 | #define RELOC_FLAG32(RelInfo) ((RelInfo >> 0x0C) == IMAGE_REL_BASED_HIGHLOW) 230 | #define RELOC_FLAG64(RelInfo) ((RelInfo >> 0x0C) == IMAGE_REL_BASED_DIR64) 231 | 232 | #ifdef _WIN64 233 | #define RELOC_FLAG RELOC_FLAG64 234 | #else 235 | #define RELOC_FLAG RELOC_FLAG32 236 | #endif 237 | 238 | #pragma runtime_checks( "", off ) 239 | #pragma optimize( "", off ) 240 | void __stdcall Shellcode(MANUAL_MAPPING_DATA *pData) { 241 | if (!pData) { 242 | pData->hMod = (HINSTANCE)0x404040; 243 | return; 244 | } 245 | 246 | BYTE *pBase = pData->pbase; 247 | auto *pOpt = &reinterpret_cast(pBase + reinterpret_cast((uintptr_t)pBase)->e_lfanew)->OptionalHeader; 248 | 249 | auto _LoadLibraryA = pData->pLoadLibraryA; 250 | auto _GetProcAddress = pData->pGetProcAddress; 251 | #ifdef _WIN64 252 | auto _RtlAddFunctionTable = pData->pRtlAddFunctionTable; 253 | #endif 254 | auto _DllMain = reinterpret_cast(pBase + pOpt->AddressOfEntryPoint); 255 | 256 | BYTE *LocationDelta = pBase - pOpt->ImageBase; 257 | if (LocationDelta) { 258 | if (pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size) { 259 | auto *pRelocData = reinterpret_cast(pBase + pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); 260 | const auto *pRelocEnd = reinterpret_cast(reinterpret_cast(pRelocData) + pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size); 261 | while (pRelocData < pRelocEnd && pRelocData->SizeOfBlock) { 262 | UINT AmountOfEntries = (pRelocData->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD); 263 | WORD *pRelativeInfo = reinterpret_cast(pRelocData + 1); 264 | 265 | for (UINT i = 0; i != AmountOfEntries; ++i, ++pRelativeInfo) { 266 | if (RELOC_FLAG(*pRelativeInfo)) { 267 | UINT_PTR *pPatch = reinterpret_cast(pBase + pRelocData->VirtualAddress + ((*pRelativeInfo) & 0xFFF)); 268 | *pPatch += reinterpret_cast(LocationDelta); 269 | } 270 | } 271 | pRelocData = reinterpret_cast(reinterpret_cast(pRelocData) + pRelocData->SizeOfBlock); 272 | } 273 | } 274 | } 275 | 276 | if (pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size) { 277 | auto *pImportDescr = reinterpret_cast(pBase + pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); 278 | while (pImportDescr->Name) { 279 | char *szMod = reinterpret_cast(pBase + pImportDescr->Name); 280 | HINSTANCE hDll = _LoadLibraryA(szMod); 281 | 282 | ULONG_PTR *pThunkRef = reinterpret_cast(pBase + pImportDescr->OriginalFirstThunk); 283 | ULONG_PTR *pFuncRef = reinterpret_cast(pBase + pImportDescr->FirstThunk); 284 | 285 | if (!pThunkRef) 286 | pThunkRef = pFuncRef; 287 | 288 | for (; *pThunkRef; ++pThunkRef, ++pFuncRef) { 289 | if (IMAGE_SNAP_BY_ORDINAL(*pThunkRef)) { 290 | *pFuncRef = (ULONG_PTR)_GetProcAddress(hDll, reinterpret_cast(*pThunkRef & 0xFFFF)); 291 | } 292 | else { 293 | auto *pImport = reinterpret_cast(pBase + (*pThunkRef)); 294 | *pFuncRef = (ULONG_PTR)_GetProcAddress(hDll, pImport->Name); 295 | } 296 | } 297 | ++pImportDescr; 298 | } 299 | } 300 | 301 | if (pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].Size) { 302 | auto *pTLS = reinterpret_cast(pBase + pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress); 303 | auto *pCallback = reinterpret_cast(pTLS->AddressOfCallBacks); 304 | for (; pCallback && *pCallback; ++pCallback) 305 | (*pCallback)(pBase, DLL_PROCESS_ATTACH, nullptr); 306 | } 307 | 308 | bool ExceptionSupportFailed = false; 309 | 310 | #ifdef _WIN64 311 | 312 | if (pData->SEHSupport) { 313 | auto excep = pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION]; 314 | if (excep.Size) { 315 | if (!_RtlAddFunctionTable( 316 | reinterpret_cast(pBase + excep.VirtualAddress), 317 | excep.Size / sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY), (DWORD64)pBase)) { 318 | ExceptionSupportFailed = true; 319 | } 320 | } 321 | } 322 | 323 | #endif 324 | 325 | _DllMain(pBase, pData->fdwReasonParam, pData->reservedParam); 326 | 327 | if (ExceptionSupportFailed) 328 | pData->hMod = reinterpret_cast(0x505050); 329 | else 330 | pData->hMod = reinterpret_cast(pBase); 331 | } 332 | 333 | bool IsCorrectTargetArchitecture(HANDLE hProc) { 334 | BOOL bTarget = FALSE; 335 | if (!IsWow64Process(hProc, &bTarget)) { 336 | printf("Can't confirm target process architecture: 0x%X\n", GetLastError()); 337 | return false; 338 | } 339 | 340 | BOOL bHost = FALSE; 341 | IsWow64Process(GetCurrentProcess(), &bHost); 342 | 343 | return (bTarget == bHost); 344 | } 345 | 346 | char *w2c(const wchar_t *wchar) { 347 | char *m_char; 348 | int len = WideCharToMultiByte(CP_ACP, 0, wchar, wcslen(wchar), NULL, 0, NULL, NULL); 349 | m_char = new char[len + 1]; 350 | WideCharToMultiByte(CP_ACP, 0, wchar, wcslen(wchar), m_char, len, NULL, NULL); 351 | m_char[len] = '\0'; 352 | return m_char; 353 | } 354 | 355 | wchar_t *c2w(const char *cchar) { 356 | wchar_t *m_wchar; 357 | int len = MultiByteToWideChar(CP_ACP, 0, cchar, strlen(cchar), NULL, 0); 358 | m_wchar = new wchar_t[len + 1]; 359 | MultiByteToWideChar(CP_ACP, 0, cchar, strlen(cchar), m_wchar, len); 360 | m_wchar[len] = '\0'; 361 | return m_wchar; 362 | } 363 | 364 | bool InjectDll(HANDLE hProc, wchar_t *dllPath) { 365 | if (!IsCorrectTargetArchitecture(hProc)) { 366 | printf("Invalid Process Architecture!\n"); 367 | CloseHandle(hProc); 368 | system("pause"); 369 | exit(-4); 370 | } 371 | 372 | if (GetFileAttributes(dllPath) == INVALID_FILE_ATTRIBUTES) { 373 | printf("Failed to found %s\n", w2c(dllPath)); 374 | system("pause"); 375 | return false; 376 | } 377 | 378 | std::ifstream File(dllPath, std::ios::binary | std::ios::ate); 379 | 380 | if (File.fail()) { 381 | printf("Failed to Open the file: %X\n", (DWORD)File.rdstate()); 382 | File.close(); 383 | system("pause"); 384 | return false; 385 | } 386 | 387 | auto FileSize = File.tellg(); 388 | if (FileSize < 0x1000) { 389 | printf("Invalid filesize!\n"); 390 | File.close(); 391 | CloseHandle(hProc); 392 | system("pause"); 393 | exit(-5); 394 | } 395 | 396 | BYTE *pSrcData = new BYTE[(UINT_PTR)FileSize]; 397 | if (!pSrcData) { 398 | printf("Failed to allocate dll file!\n"); 399 | File.close(); 400 | CloseHandle(hProc); 401 | system("pause"); 402 | exit(-6); 403 | } 404 | 405 | File.seekg(0, std::ios::beg); 406 | File.read((char *)(pSrcData), FileSize); 407 | File.close(); 408 | 409 | printf("[INFO] Mapping...\n"); 410 | if (!ManualMapDll(hProc, pSrcData, FileSize)) { 411 | delete[] pSrcData; 412 | CloseHandle(hProc); 413 | printf("Error while mapping.\n"); 414 | system("pause"); 415 | exit(-7); 416 | } 417 | delete[] pSrcData; 418 | 419 | printf("[INFO] Manual Map Injection Successfully!\n"); 420 | return true; 421 | } 422 | -------------------------------------------------------------------------------- /Manual Map Injector/injector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using f_LoadLibraryA = HINSTANCE(WINAPI *)(const char *lpLibFilename); 11 | using f_GetProcAddress = FARPROC(WINAPI *)(HMODULE hModule, LPCSTR lpProcName); 12 | using f_DLL_ENTRY_POINT = BOOL(WINAPI *)(void *hDll, DWORD dwReason, void *pReserved); 13 | 14 | #ifdef _WIN64 15 | using f_RtlAddFunctionTable = BOOL(WINAPIV *)(PRUNTIME_FUNCTION FunctionTable, DWORD EntryCount, DWORD64 BaseAddress); 16 | #endif 17 | 18 | struct MANUAL_MAPPING_DATA 19 | { 20 | f_LoadLibraryA pLoadLibraryA; 21 | f_GetProcAddress pGetProcAddress; 22 | #ifdef _WIN64 23 | f_RtlAddFunctionTable pRtlAddFunctionTable; 24 | #endif 25 | BYTE *pbase; 26 | HINSTANCE hMod; 27 | DWORD fdwReasonParam; 28 | LPVOID reservedParam; 29 | BOOL SEHSupport; 30 | }; 31 | 32 | 33 | //Note: Exception support only x64 with build params /EHa or /EHc 34 | bool ManualMapDll(HANDLE hProc, BYTE *pSrcData, SIZE_T FileSize, bool ClearHeader = true, bool ClearNonNeededSections = true, bool AdjustProtections = true, bool SEHExceptionSupport = true, DWORD fdwReason = DLL_PROCESS_ATTACH, LPVOID lpReserved = 0); 35 | void __stdcall Shellcode(MANUAL_MAPPING_DATA *pData); 36 | char *w2c(const wchar_t *wchar); 37 | wchar_t *c2w(const char *cchar); 38 | bool InjectDll(HANDLE hProc, wchar_t *dllPath); -------------------------------------------------------------------------------- /Manual Map Injector/main.cpp: -------------------------------------------------------------------------------- 1 | #include "injector.h" 2 | #include "resource.h" 3 | #include "ReleaseHelper.h" 4 | #include "includes/SimpleIni.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | 13 | static CSimpleIni ini; 14 | 15 | DWORD GetProcessIdByName(wchar_t *name) { 16 | PROCESSENTRY32 entry; 17 | entry.dwSize = sizeof(PROCESSENTRY32); 18 | 19 | HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); 20 | 21 | if (Process32First(snapshot, &entry) == TRUE) { 22 | while (Process32Next(snapshot, &entry) == TRUE) { 23 | if (_wcsicmp(entry.szExeFile, name) == 0) { 24 | CloseHandle(snapshot); //thanks to Pvt Comfy 25 | return entry.th32ProcessID; 26 | } 27 | } 28 | } 29 | 30 | CloseHandle(snapshot); 31 | return 0; 32 | } 33 | 34 | HANDLE CreateYuanShenProc() { 35 | auto YSPath = ini.GetValue(c2w("Inject"), c2w("GenshinPath")); 36 | if (YSPath == nullptr) ini.GetValue(c2w("GenshinImpact"), c2w("Path")); 37 | if (YSPath == nullptr) { 38 | printf("Failed to found YuanShen path\n"); 39 | system("pause"); 40 | exit(-1); 41 | } 42 | 43 | printf("YuanShen Path: %s\n", w2c(YSPath)); 44 | 45 | STARTUPINFO si; 46 | PROCESS_INFORMATION pi; 47 | 48 | ZeroMemory(&si, sizeof(si)); 49 | si.cb = sizeof(si); 50 | ZeroMemory(&pi, sizeof(pi)); 51 | 52 | if (!CreateProcess(NULL, const_cast(YSPath), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { 53 | printf("Failed to create YuanShen process"); 54 | exit(-2); 55 | } 56 | return pi.hProcess; 57 | } 58 | 59 | int main(int argc, wchar_t *argv[]) { 60 | ini.SetUnicode(); 61 | ini.LoadFile("cfg.ini"); 62 | char *tempDir = getenv("TEMP"); 63 | 64 | if (!ReleaseLibrary(IDR_DLL1, "Dll", format("{}\\DebuggerBypass.dll", tempDir).c_str())) { 65 | printf("Failed to release dll\n"); 66 | system("pause"); 67 | return -3; 68 | } 69 | 70 | 71 | TOKEN_PRIVILEGES priv; 72 | ZeroMemory(&priv, sizeof(priv)); 73 | HANDLE hToken = NULL; 74 | if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { 75 | priv.PrivilegeCount = 1; 76 | priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 77 | 78 | if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &priv.Privileges[0].Luid)) 79 | AdjustTokenPrivileges(hToken, FALSE, &priv, 0, NULL, NULL); 80 | 81 | CloseHandle(hToken); 82 | } 83 | 84 | HANDLE hProc = CreateYuanShenProc(); 85 | if (!hProc) { 86 | DWORD Err = GetLastError(); 87 | printf("Failed to create process: 0x%X\n", Err); 88 | system("pause"); 89 | return -3; 90 | } 91 | 92 | printf("[INFO] YuanShen process handle: %p\n", hProc); 93 | InjectDll(hProc, c2w(format("{}\\DebuggerBypass.dll", c2w(tempDir)).c_str())); 94 | 95 | #ifdef _DEBUG 96 | printf("Wait 10 seconds for YuanShen init...\n"); 97 | Sleep(10000); 98 | #else 99 | printf("Wait 30 seconds for YuanShen init...\n"); 100 | Sleep(30000); 101 | #endif 102 | 103 | while (true) { 104 | system("cls"); 105 | 106 | char dllPathC[256]; 107 | wchar_t *dllPathW; 108 | 109 | printf("Input dlls path you want to inject below:\n"); 110 | cin.getline(dllPathC, 256); 111 | 112 | dllPathW = c2w(dllPathC); 113 | #ifdef _DEBUG 114 | InjectDll(hProc, dllPathW); 115 | #else 116 | if (InjectDll(hProc, dllPathW)) Sleep(5000); 117 | #endif 118 | 119 | 120 | fflush(stdin); 121 | } 122 | 123 | return 0; 124 | } 125 | -------------------------------------------------------------------------------- /Manual Map Injector/resource.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RinOfficial0615/Manual-Map-Injector/143f8225cda3da8ae3735867fad1f26d59c9dc64/Manual Map Injector/resource.h -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Simple Manual Map Injector 3 | 4 | - Supports ~~x86 and~~ x64 (Compiling depending the targets) 5 | - Supports x64 exceptions (SEH) (only /EHa and /EHc) 6 | - Supports multiple dlls injection 7 | - Release & Debug 8 | - Removes PE Header and some sections (Configurable) 9 | - Configurable DllMain params (default DLL_PROCESS_ATTACH) 10 | - Add sections protections (Configurable) 11 | 12 | ## Devs 13 | 14 | - Add **DISABLE_OUTPUT** definition if you want to disable injector.cpp output 15 | - main.cpp is just an example but powerfull 16 | - Hello World dlls added from https://github.com/carterjones/hello-world-dll for easy testing 17 | -------------------------------------------------------------------------------- /hello-world-x64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RinOfficial0615/Manual-Map-Injector/143f8225cda3da8ae3735867fad1f26d59c9dc64/hello-world-x64.dll --------------------------------------------------------------------------------