This repository has been archived by the owner on Jun 16, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 13
/
autoexecconfig.inc
768 lines (547 loc) · 18.6 KB
/
autoexecconfig.inc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
/**
* AutoExecConfig
*
* Copyright (C) 2013-2023 Impact
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#if defined _autoexecconfig_included
#endinput
#endif
#define _autoexecconfig_included
#include <sourcemod>
#pragma semicolon 1
#pragma newdecls required
#define AUTOEXECCONFIG_VERSION "0.1.6"
#define AUTOEXECCONFIG_URL "https://forums.alliedmods.net/showthread.php?t=204254"
// Append
#define AUTOEXEC_APPEND_BAD_FILENAME 0
#define AUTOEXEC_APPEND_FILE_NOT_FOUND 1
#define AUTOEXEC_APPEND_BAD_HANDLE 2
#define AUTOEXEC_APPEND_SUCCESS 3
// Find
#define AUTOEXEC_FIND_BAD_FILENAME 10
#define AUTOEXEC_FIND_FILE_NOT_FOUND 11
#define AUTOEXEC_FIND_BAD_HANDLE 12
#define AUTOEXEC_FIND_NOT_FOUND 13
#define AUTOEXEC_FIND_SUCCESS 14
// Clean
#define AUTOEXEC_CLEAN_FILE_NOT_FOUND 20
#define AUTOEXEC_CLEAN_BAD_HANDLE 21
#define AUTOEXEC_CLEAN_SUCCESS 22
// General
#define AUTOEXEC_NO_CONFIG 30
// Formatter
#define AUTOEXEC_FORMAT_BAD_FILENAME 40
#define AUTOEXEC_FORMAT_SUCCESS 41
// Global variables
static char g_sConfigFile[PLATFORM_MAX_PATH];
static char g_sRawFileName[PLATFORM_MAX_PATH];
static char g_sFolderPath[PLATFORM_MAX_PATH];
static bool g_bCreateFile = false;
static Handle g_hPluginHandle = null;
static bool g_bCreateDirectory = false;
static int g_bCreateDirectoryMode = FPERM_U_READ|FPERM_U_WRITE|FPERM_U_EXEC|FPERM_G_READ|FPERM_G_EXEC|FPERM_O_READ|FPERM_O_EXEC;
// Workaround for now
static int g_iLastFindResult;
static int g_iLastAppendResult;
/**
* Returns the last result from the parser.
*
* @return Returns one of the AUTOEXEC_FIND values or -1 if not set.
*/
stock int AutoExecConfig_GetFindResult()
{
return g_iLastFindResult;
}
/**
* Returns the last result from the appender.
*
* @return Returns one of the AUTOEXEC_APPEND values or -1 if not set.
*/
stock int AutoExecConfig_GetAppendResult()
{
return g_iLastAppendResult;
}
/**
* Set if the config file should be created by the autoexecconfig include itself if it doesn't exist.
*
* @param create True if config file should be created, false otherwise.
* @noreturn
*/
stock void AutoExecConfig_SetCreateFile(bool create)
{
g_bCreateFile = create;
}
/**
* Set if the config file's folder should be created by the autoexecconfig include itself if it doesn't exist.
* Note: Must be used before AutoExecConfig_SetFile as the potential creation of it happens there
*
* @param create True if config file should be created, false otherwise.
* @param mode Folder permission mode, default is u=rwx,g=rx,o=rx.
* @noreturn
*/
stock void AutoExecConfig_SetCreateDirectory(bool create, int mode=FPERM_U_READ|FPERM_U_WRITE|FPERM_U_EXEC|FPERM_G_READ|FPERM_G_EXEC|FPERM_O_READ|FPERM_O_EXEC)
{
g_bCreateDirectory = create;
g_bCreateDirectoryMode = mode;
}
/**
* Returns if the config file should be created if it doesn't exist.
*
* @return Returns true, if the config file should be created or false if it should not.
*/
stock bool AutoExecConfig_GetCreateFile()
{
return g_bCreateFile;
}
/**
* Set the plugin for which the config file should be created.
* Set to null to use the calling plugin.
* Used to print the correct filename in the top comment when creating the file.
*
* @param plugin The plugin to create convars for or null to use the calling plugin.
* @noreturn
*/
stock void AutoExecConfig_SetPlugin(Handle plugin)
{
g_hPluginHandle = plugin;
}
/**
* Returns the plugin's handle for which the config file is created.
*
* @return The plugin handle
*/
stock Handle AutoExecConfig_GetPlugin()
{
return g_hPluginHandle;
}
/**
* Set the global autoconfigfile used by functions of this file.
* Note: does not support subfolders like folder1/folder2
*
* @param file Name of the config file, path and .cfg extension is being added if not given.
* @param folder Folder under cfg/ to use. By default this is "sourcemod."
* @return True if formatter returned success, false otherwise.
*/
stock bool AutoExecConfig_SetFile(char[] file, char[] folder="sourcemod")
{
Format(g_sConfigFile, sizeof(g_sConfigFile), "%s", file);
// Global buffers for cfg execution
strcopy(g_sRawFileName, sizeof(g_sRawFileName), file);
strcopy(g_sFolderPath, sizeof(g_sFolderPath), folder);
// Format the filename
return AutoExecConfig_FormatFileName(g_sConfigFile, sizeof(g_sConfigFile), folder) == AUTOEXEC_FORMAT_SUCCESS;
}
/**
* Get the formatted autoconfigfile used by functions of this file.
*
* @param buffer String to format.
* @param size Maximum size of buffer
* @return True if filename was set, false otherwise.
*/
stock bool AutoExecConfig_GetFile(char[] buffer,int size)
{
if (strlen(g_sConfigFile) > 0)
{
strcopy(buffer, size, g_sConfigFile);
return true;
}
// Security for decl users
buffer[0] = '\0';
return false;
}
/**
* Creates a convar and appends it to the autoconfigfile if not found.
* FCVAR_DONTRECORD will be skipped.
*
* @param name Name of new convar.
* @param defaultValue String containing the default value of new convar.
* @param description Optional description of the convar.
* @param flags Optional bitstring of flags determining how the convar should be handled. See FCVAR_* constants for more details.
* @param hasMin Optional boolean that determines if the convar has a minimum value.
* @param min Minimum floating point value that the convar can have if hasMin is true.
* @param hasMax Optional boolean that determines if the convar has a maximum value.
* @param max Maximum floating point value that the convar can have if hasMax is true.
* @return A handle to the newly created convar. If the convar already exists, a handle to it will still be returned.
* @error Convar name is blank or is the same as an existing console command.
*/
stock ConVar AutoExecConfig_CreateConVar(const char[] name, const char[] defaultValue, const char[] description="", int flags=0, bool hasMin=false, float min=0.0, bool hasMax=false, float max=0.0)
{
// If configfile was set and convar has no dontrecord flag
if (!(flags & FCVAR_DONTRECORD) && strlen(g_sConfigFile) > 0)
{
// Reset the results
g_iLastFindResult = -1;
g_iLastAppendResult = -1;
// Add it if not found
char buffer[64];
g_iLastFindResult = AutoExecConfig_FindValue(name, buffer, sizeof(buffer), true);
// We only add this convar if it doesn't exist, or the file doesn't exist and it should be auto-generated
if (g_iLastFindResult == AUTOEXEC_FIND_NOT_FOUND || (g_iLastFindResult == AUTOEXEC_FIND_FILE_NOT_FOUND && g_bCreateFile))
{
g_iLastAppendResult = AutoExecConfig_AppendValue(name, defaultValue, description, flags, hasMin, min, hasMax, max);
}
}
// Create the convar
return CreateConVar(name, defaultValue, description, flags, hasMin, min, hasMax, max);
}
/**
* Executes the autoconfigfile and adds it to the OnConfigsExecuted forward.
* If we didn't create it ourselves we let SourceMod create it.
*
* @noreturn
*/
stock void AutoExecConfig_ExecuteFile()
{
// Only let sourcemod create the file, if we didn't do that already.
AutoExecConfig(!g_bCreateFile, g_sRawFileName, g_sFolderPath);
}
/**
* Formats a autoconfigfile, prefixes path and adds .cfg extension if missing.
*
* @param buffer String to format.
* @param size Maximum size of buffer.
* @return Returns one of the AUTOEXEC_FORMAT values..
*/
stock static int AutoExecConfig_FormatFileName(char[] buffer, int size, char[] folder="sourcemod")
{
// No config set
if (strlen(g_sConfigFile) < 1)
{
return AUTOEXEC_NO_CONFIG;
}
// Can't be an cfgfile
if (StrContains(g_sConfigFile, ".cfg") != -1 && strlen(g_sConfigFile) < 4)
{
return AUTOEXEC_FORMAT_BAD_FILENAME;
}
// Pathprefix
char pathprefixbuffer[PLATFORM_MAX_PATH];
if (strlen(folder) > 0)
{
Format(pathprefixbuffer, sizeof(pathprefixbuffer), "cfg/%s/", folder);
if (g_bCreateDirectory && !DirExists(pathprefixbuffer))
{
CreateDirectory(pathprefixbuffer, g_bCreateDirectoryMode);
}
}
else
{
Format(pathprefixbuffer, sizeof(pathprefixbuffer), "cfg/");
}
char filebuffer[PLATFORM_MAX_PATH];
filebuffer[0] = '\0';
// Add path if file doesn't begin with it
if (StrContains(buffer, pathprefixbuffer) != 0)
{
StrCat(filebuffer, sizeof(filebuffer), pathprefixbuffer);
}
StrCat(filebuffer, sizeof(filebuffer), g_sConfigFile);
// Add .cfg extension if file doesn't end with it
if (StrContains(filebuffer[strlen(filebuffer) - 4], ".cfg") != 0)
{
StrCat(filebuffer, sizeof(filebuffer), ".cfg");
}
strcopy(buffer, size, filebuffer);
return AUTOEXEC_FORMAT_SUCCESS;
}
/**
* Appends a convar to the global autoconfigfile
*
* @param name Name of new convar.
* @param defaultValue String containing the default value of new convar.
* @param description Optional description of the convar.
* @param flags Optional bitstring of flags determining how the convar should be handled. See FCVAR_* constants for more details.
* @param hasMin Optional boolean that determines if the convar has a minimum value.
* @param min Minimum floating point value that the convar can have if hasMin is true.
* @param hasMax Optional boolean that determines if the convar has a maximum value.
* @param max Maximum floating point value that the convar can have if hasMax is true.
* @return Returns one of the AUTOEXEC_APPEND values
*/
stock int AutoExecConfig_AppendValue(const char[] name, const char[] defaultValue, const char[] description, int flags, bool hasMin, float min, bool hasMax, float max)
{
// No config set
if (strlen(g_sConfigFile) < 1)
{
return AUTOEXEC_NO_CONFIG;
}
char filebuffer[PLATFORM_MAX_PATH];
strcopy(filebuffer, sizeof(filebuffer), g_sConfigFile);
//PrintToServer("pathbuffer: %s", filebuffer);
bool bFileExists = FileExists(filebuffer);
if (g_bCreateFile || bFileExists)
{
// If the file already exists we open it in append mode, otherwise we use a write mode which creates the file
File fFile = OpenFile(filebuffer, (bFileExists ? "a" : "w"));
char writebuffer[2048];
if (fFile == null)
{
return AUTOEXEC_APPEND_BAD_HANDLE;
}
// We just created the file, so add some header about version and stuff
if (g_bCreateFile && !bFileExists)
{
fFile.WriteLine( "// This file was auto-generated by AutoExecConfig v%s (%s)", AUTOEXECCONFIG_VERSION, AUTOEXECCONFIG_URL);
GetPluginFilename(g_hPluginHandle, writebuffer, sizeof(writebuffer));
Format(writebuffer, sizeof(writebuffer), "// ConVars for plugin \"%s\"", writebuffer);
fFile.WriteLine("%s", writebuffer);
}
// Spacer
fFile.WriteLine("\n");
// This is used for multiline comments
int newlines = GetCharCountInStr('\n', description);
if (newlines == 0)
{
// We have no newlines, we can write the description to the file as is
Format(writebuffer, sizeof(writebuffer), "// %s", description);
fFile.WriteLine("%s", writebuffer);
}
else
{
char[][] newlineBuf = new char[newlines +1][2048];
ExplodeString(description, "\n", newlineBuf, newlines +1, 2048, false);
// Each newline gets a commented newline
for (int i; i <= newlines; i++)
{
if (strlen(newlineBuf[i]) > 0)
{
fFile.WriteLine("// %s", newlineBuf[i]);
}
}
}
// Descspacer
fFile.WriteLine("// -");
// Default
Format(writebuffer, sizeof(writebuffer), "// Default: \"%s\"", defaultValue);
fFile.WriteLine("%s", writebuffer);
// Minimum
if (hasMin)
{
Format(writebuffer, sizeof(writebuffer), "// Minimum: \"%f\"", min);
fFile.WriteLine("%s", writebuffer);
}
// Maximum
if (hasMax)
{
Format(writebuffer, sizeof(writebuffer), "// Maximum: \"%f\"", max);
fFile.WriteLine("%s", writebuffer);
}
// Write end and defaultvalue
Format(writebuffer, sizeof(writebuffer), "%s \"%s\"", name, defaultValue);
fFile.WriteLine("%s", writebuffer);
fFile.Close();
return AUTOEXEC_APPEND_SUCCESS;
}
return AUTOEXEC_APPEND_FILE_NOT_FOUND;
}
/**
* Returns a convar's value from the global autoconfigfile
*
* @param cvar Cvar to search for.
* @param value Buffer to store result into.
* @param size Maximum size of buffer.
* @param caseSensitive Whether or not the search should be case sensitive.
* @return Returns one of the AUTOEXEC_FIND values
*/
stock int AutoExecConfig_FindValue(const char[] cvar, char[] value, int size, bool caseSensitive=false)
{
// Security for decl users
value[0] = '\0';
// No config set
if (strlen(g_sConfigFile) < 1)
{
return AUTOEXEC_NO_CONFIG;
}
char filebuffer[PLATFORM_MAX_PATH];
strcopy(filebuffer, sizeof(filebuffer), g_sConfigFile);
//PrintToServer("pathbuffer: %s", filebuffer);
bool bFileExists = FileExists(filebuffer);
// We want to create the config file and it doesn't exist yet.
if (g_bCreateFile && !bFileExists)
{
return AUTOEXEC_FIND_FILE_NOT_FOUND;
}
if (bFileExists)
{
File fFile = OpenFile(filebuffer, "r");
int valuestart;
int valueend;
int cvarend;
// Just a reminder to self, leave the values that high
char sConvar[64];
char sValue[64];
char readbuffer[2048];
char copybuffer[2048];
if (fFile == null)
{
return AUTOEXEC_FIND_BAD_HANDLE;
}
while (!fFile.EndOfFile() && fFile.ReadLine(readbuffer, sizeof(readbuffer)))
{
// Is a comment or not valid
if (IsCharSpace(readbuffer[0]) || readbuffer[0] == '/')
{
continue;
}
// Has not enough spaces, must have at least 1
if (GetCharCountInStr(' ', readbuffer) < 1)
{
continue;
}
// Ignore cvars which aren't quoted
if (GetCharCountInStr('"', readbuffer) != 2)
{
continue;
}
// Get the start of the value
if ( (valuestart = StrContains(readbuffer, "\"")) == -1 )
{
continue;
}
// Get the end of the value
if ( (valueend = StrContains(readbuffer[valuestart+1], "\"")) == -1 )
{
continue;
}
// Get the start of the cvar,
if ( (cvarend = StrContains(readbuffer, " ")) == -1 || cvarend >= valuestart)
{
continue;
}
// Skip if cvarendindex is before valuestartindex
if (cvarend >= valuestart)
{
continue;
}
// Convar
// Tempcopy for security
strcopy(copybuffer, sizeof(copybuffer), readbuffer);
copybuffer[cvarend] = '\0';
strcopy(sConvar, sizeof(sConvar), copybuffer);
// Value
// Tempcopy for security
strcopy(copybuffer, sizeof(copybuffer), readbuffer[valuestart+1]);
copybuffer[valueend] = '\0';
strcopy(sValue, sizeof(sValue), copybuffer);
//PrintToServer("Cvar %s has a value of %s", sConvar, sValue);
if (StrEqual(sConvar, cvar, caseSensitive))
{
Format(value, size, "%s", sConvar);
fFile.Close();
return AUTOEXEC_FIND_SUCCESS;
}
}
fFile.Close();
return AUTOEXEC_FIND_NOT_FOUND;
}
return AUTOEXEC_FIND_FILE_NOT_FOUND;
}
/**
* Cleans the global autoconfigfile from too much spaces
*
* @return One of the AUTOEXEC_CLEAN values.
*/
stock int AutoExecConfig_CleanFile()
{
// No config set
if (strlen(g_sConfigFile) < 1)
{
return AUTOEXEC_NO_CONFIG;
}
char sfile[PLATFORM_MAX_PATH];
strcopy(sfile, sizeof(sfile), g_sConfigFile);
// Security
if (!FileExists(sfile))
{
return AUTOEXEC_CLEAN_FILE_NOT_FOUND;
}
char sfile2[PLATFORM_MAX_PATH];
Format(sfile2, sizeof(sfile2), "%s_tempcopy", sfile);
char readbuffer[2048];
int count;
bool firstreached;
// Open files
File fFile1 = OpenFile(sfile, "r");
File fFile2 = OpenFile(sfile2, "w");
// Check filehandles
if (fFile1 == null || fFile2 == null)
{
if (fFile1 != null)
{
//PrintToServer("Handle1 invalid");
fFile1.Close();
}
if (fFile2 != null)
{
//PrintToServer("Handle2 invalid");
fFile2.Close();
}
return AUTOEXEC_CLEAN_BAD_HANDLE;
}
while (!fFile1.EndOfFile() && fFile1.ReadLine(readbuffer, sizeof(readbuffer)))
{
// Is space
if (IsCharSpace(readbuffer[0]))
{
count++;
}
// No space, count from start
else
{
count = 0;
}
// Don't write more than 1 space if seperation after informations have been reached
if (count < 2 || !firstreached)
{
ReplaceString(readbuffer, sizeof(readbuffer), "\n", "");
fFile2.WriteLine("%s", readbuffer);
}
// First bigger seperation after informations has been reached
if (count == 2)
{
firstreached = true;
}
}
fFile1.Close();
fFile2.Close();
// This might be a risk, for now it works
DeleteFile(sfile);
RenameFile(sfile, sfile2);
return AUTOEXEC_CLEAN_SUCCESS;
}
/**
* Returns how many times the given char occures in the given string.
*
* @param str String to search for in.
* @return Occurences of the given char found in string.
*/
stock static int GetCharCountInStr(int character, const char[] str)
{
int len = strlen(str);
int count;
for (int i; i < len; i++)
{
if (str[i] == character)
{
count++;
}
}
return count;
}
#pragma deprecated
stock bool AutoExecConfig_CacheConvars()
{
return false;
}