Skip to content

Commit 26c94eb

Browse files
committed
Finalize import components
1 parent dced4a0 commit 26c94eb

File tree

3 files changed

+265
-83
lines changed

3 files changed

+265
-83
lines changed

scripting/include/srccoop/import.inc

Lines changed: 186 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,165 @@
66
#pragma newdecls required
77
#pragma semicolon 1
88

9+
// --------------------------------------------------------------------------------------------------------------------------------
10+
// Import sources
11+
// --------------------------------------------------------------------------------------------------------------------------------
12+
913
enum ImportSource
1014
{
1115
IMPORT_STRIPPER
1216
}
1317

18+
// --------------------------------------------------------------------------------------------------------------------------------
19+
// Import utils
20+
// --------------------------------------------------------------------------------------------------------------------------------
21+
22+
methodmap ImportUtil
23+
{
24+
public static bool FileFilter(ImportSource src, const char[] szFileName, char szMapName[MAX_MAPNAME])
25+
{
26+
int len = strlen(szFileName);
27+
int extLen;
28+
29+
if (src == IMPORT_STRIPPER)
30+
{
31+
if (!StrEndsWithEx(szFileName, len, ".cfg", false))
32+
return false;
33+
extLen = 4;
34+
}
35+
else ThrowError("ImportUtil.FileFilter not implemented for this import source.");
36+
37+
len = len - extLen + 1;
38+
if (len > MAX_MAPNAME)
39+
{
40+
MsgSrv("Map name in \"%s\" exceeds limit of %d characters and won't be imported.", szFileName, MAX_MAPNAME - 1);
41+
return false;
42+
}
43+
44+
strcopy(szMapName, len, szFileName);
45+
return true;
46+
}
47+
48+
public static bool ImportAsNode(const char[] szNode, const char[] szNodeContent, StringBuilder pConfigBuilder, char[] szError, int maxErrorLen)
49+
{
50+
// This regex enters first child key from given position.
51+
// Matching stops after the opening { brace.
52+
//
53+
// Raw form:
54+
// /^(?:\/\/.*|"(?:\\.|[^"])*"|\s|[^}{"])*?{/
55+
56+
Regex pEnterChildNode = new Regex("^(?:\\/\\/.*|\"(?:\\\\.|[^\"])*\"|\\s|[^}{\"])*?{");
57+
58+
// This regex consists of OR'd groups each representing a part of KV syntax. The <braces> group is recursive.
59+
// The target node is searched at current depth, as in kv.JumpToKey().
60+
// Matching stops after the name of the specified node.
61+
//
62+
// Raw form:
63+
// /^(?:(?<comments>\/\/.*)|(?<braces>{(?:(?&comments)|(?&braces)|(?&quotes)|(?&misc))*})|(?<quotes>"(?:\\.|[^"])*")|(?<misc>\s|[^"{}]))*?(?:(?<=\s)(?<target>entity)(?=[{\s])|"(?&target)")/
64+
65+
char szGoToNode[256] = "^(?:(?<comments>\\/\\/.*)|(?<braces>{(?:(?&comments)|(?&braces)|(?&quotes)|(?&misc))*})|(?<quotes>\"(?:\\\\.|[^\"])*\")|(?<misc>\\s|[^\"{}]))*?(?:(?<=\\s)(?<target>%s)(?=[{\\s])|\"(?&target)\")";
66+
Format(szGoToNode, sizeof(szGoToNode), szGoToNode, szNode);
67+
Regex pGoToNode = new Regex(szGoToNode);
68+
69+
// Moves to end of current node.
70+
// Same as above, but without the target node mathing part at the end.
71+
// Matching stops before closing } brace.
72+
//
73+
// Raw form:
74+
// /^(?:(?<comments>\/\/.*)|(?<braces>{(?:(?&comments)|(?&braces)|(?&quotes)|(?&misc))*})|(?<quotes>"(?:\\.|[^"])*")|(?<misc>\s|[^"{}]))*/
75+
76+
Regex pGoToEnd = new Regex("^(?:(?<comments>\\/\\/.*)|(?<braces>{(?:(?&comments)|(?&braces)|(?&quotes)|(?&misc))*})|(?<quotes>\"(?:\\\\.|[^\"])*\")|(?<misc>\\s|[^\"{}]))*");
77+
78+
char[] szConfig = new char[pConfigBuilder.BufferSize];
79+
pConfigBuilder.ToString(szConfig).Reset();
80+
81+
int offset;
82+
if (!ImportUtil.RegexAdvance(pEnterChildNode, szConfig, offset))
83+
{
84+
Format(szError, maxErrorLen, "SourceCoop config root element not found");
85+
}
86+
else
87+
{
88+
if (ImportUtil.RegexAdvance(pGoToNode, szConfig, offset))
89+
{
90+
// found requested node
91+
92+
if (ImportUtil.RegexAdvance(pEnterChildNode, szConfig, offset))
93+
{
94+
pConfigBuilder.AppendEx(szConfig, offset);
95+
pConfigBuilder.Append("\n");
96+
pConfigBuilder.Append(szNodeContent);
97+
pConfigBuilder.Append("\t");
98+
if (ImportUtil.RegexAdvance(pGoToEnd, szConfig, offset))
99+
{
100+
pConfigBuilder.Append(szConfig[offset]);
101+
}
102+
else
103+
{
104+
Format(szError, maxErrorLen, "Unable to traverse SourceCoop config (#3)");
105+
}
106+
}
107+
else
108+
{
109+
Format(szError, maxErrorLen, "Unable to traverse SourceCoop config (#1)");
110+
}
111+
}
112+
else
113+
{
114+
// requested node missing
115+
116+
if (ImportUtil.RegexAdvance(pGoToEnd, szConfig, offset))
117+
{
118+
pConfigBuilder.AppendEx(szConfig, offset);
119+
pConfigBuilder.Append("\t\"");
120+
pConfigBuilder.Append(szNode);
121+
pConfigBuilder.Append("\"\n\t{\n");
122+
pConfigBuilder.Append(szNodeContent);
123+
pConfigBuilder.Append("\t}\n");
124+
pConfigBuilder.Append(szConfig[offset]);
125+
}
126+
else
127+
{
128+
Format(szError, maxErrorLen, "Unable to traverse SourceCoop config (#2)");
129+
}
130+
}
131+
}
132+
133+
pEnterChildNode.Close();
134+
pGoToNode.Close();
135+
pGoToEnd.Close();
136+
return (szError[0] == EOS);
137+
}
138+
139+
public static bool RegexAdvance(Regex regex, const char[] szStr, int &offset = 0)
140+
{
141+
int ret = regex.Match(szStr[offset]);
142+
if (ret != -1 && regex.MatchCount())
143+
{
144+
offset += regex.MatchOffset();
145+
return true;
146+
}
147+
return false;
148+
}
149+
150+
public static void AppendLine(StringBuilder sb, const char[] szText, int indent)
151+
{
152+
ImportUtil.Indent(sb, indent);
153+
sb.Append(szText);
154+
sb.Append("\n");
155+
}
156+
157+
public static void Indent(StringBuilder sb, int indent)
158+
{
159+
for (int i = 0; i < indent; i++)
160+
sb.Append("\t");
161+
}
162+
}
163+
164+
// --------------------------------------------------------------------------------------------------------------------------------
165+
// Import boilerplate
166+
// --------------------------------------------------------------------------------------------------------------------------------
167+
14168
bool ImportConfigs(ImportSource src, const char[] szMapFilter, bool bCreate, bool bDryRun, int &count, int &createCount, int &errors)
15169
{
16170
if (src == IMPORT_STRIPPER)
@@ -20,11 +174,11 @@ bool ImportConfigs(ImportSource src, const char[] szMapFilter, bool bCreate, boo
20174
ThrowError("ImportConfigs not implemented for this import source."); return false;
21175
}
22176

23-
bool ImportConfig(ImportSource src, const char[] szPath, File pSrcCoopConfig, char[] szError, int maxErrorLen)
177+
bool ImportConfig(ImportSource src, const char[] szPath, StringBuilder pConfigBuilder, char[] szError, int maxErrorLen)
24178
{
25179
if (src == IMPORT_STRIPPER)
26180
{
27-
return ImportStripperConfig(szPath, pSrcCoopConfig, szError, maxErrorLen);
181+
return ImportStripperConfig(szPath, pConfigBuilder, szError, maxErrorLen);
28182
}
29183
ThrowError("ImportConfig not implemented for this import source."); return false;
30184
}
@@ -61,11 +215,13 @@ bool ImportConfigsDir(ImportSource src, const char[] szDir, const char[] szMapFi
61215
{
62216
if (!bCreate)
63217
{
64-
MsgSrv("Found config for \"%s\", but no SourceCoop config. Run with <CREATE> set to 1 to remedy this.", szMapName);
218+
MsgSrv("Found config for \"%s\", but no SourceCoop config. Run with <CREATE> set to 1 to auto-create missing configs.", szMapName);
65219
continue;
66220
}
67221
if (bDryRun)
68222
{
223+
MsgSrv("- Create: %s", szSrcCoopConfig);
224+
count++; createCount++;
69225
continue;
70226
}
71227
if (!CoopManager.FindMapConfig(szMapName, szSrcCoopConfig, ccl, true))
@@ -77,17 +233,38 @@ bool ImportConfigsDir(ImportSource src, const char[] szDir, const char[] szMapFi
77233
bCreated = true;
78234
}
79235

80-
File pSrcCoopConfig = OpenFile(szSrcCoopConfig, "r+", CoopManager.ConfigUsesValveFS(ccl));
236+
File pSrcCoopConfig = OpenFileEx(szSrcCoopConfig, "r+", CoopManager.ConfigUsesValveFS(ccl));
81237
if (!pSrcCoopConfig)
82238
{
83239
errors++;
84240
MsgSrv("Unable to read SourceCoop config \"%s\" (ValveFS=%d)", szSrcCoopConfig, CoopManager.ConfigUsesValveFS(ccl));
85241
continue;
86242
}
87-
243+
244+
szError = "";
88245
Format(szFileName, sizeof(szFileName), "%s/%s", szDir, szFileName);
89-
if (bDryRun || ImportConfig(src, szFileName, pSrcCoopConfig, szError, sizeof(szError)))
246+
StringBuilder pConfigBuilder = new StringBuilder();
247+
248+
if (!pConfigBuilder.AppendFile(pSrcCoopConfig))
90249
{
250+
Format(szError, sizeof(szError), "Unable to read from SourceCoop config");
251+
}
252+
else
253+
{
254+
if (ImportConfig(src, szFileName, pConfigBuilder, szError, sizeof(szError)))
255+
{
256+
if (!bDryRun)
257+
{
258+
if (!pSrcCoopConfig.Seek(0, SEEK_SET) || !pConfigBuilder.ToFile(pSrcCoopConfig))
259+
{
260+
Format(szError, sizeof(szError), "Unable to write to SourceCoop config");
261+
}
262+
}
263+
}
264+
}
265+
266+
if (szError[0] == EOS)
267+
{
91268
count++;
92269
if (bCreated)
93270
{
@@ -111,6 +288,7 @@ bool ImportConfigsDir(ImportSource src, const char[] szDir, const char[] szMapFi
111288
DeleteFile(szSrcCoopConfig, CoopManager.ConfigUsesValveFS(ccl));
112289
}
113290
}
291+
pConfigBuilder.Close();
114292
}
115293
dir.Close();
116294
return true;
@@ -134,7 +312,7 @@ bool ImportConfigsDir(ImportSource src, const char[] szDir, const char[] szMapFi
134312
#define STRIPPER_ELEMENTS_ROOT (STRIPPER_TOK_FILTER|STRIPPER_TOK_ADD|STRIPPER_TOK_MODIFY)
135313
#define STRIPPER_ELEMENTS_MODIFY (STRIPPER_TOK_MATCH|STRIPPER_TOK_REPLACE|STRIPPER_TOK_DELETE|STRIPPER_TOK_INSERT)
136314

137-
bool ImportStripperConfig(const char[] szStripperConfig, File pSrcCoopConfig, char[] szError, int maxErrorLen)
315+
bool ImportStripperConfig(const char[] szStripperConfig, StringBuilder pConfigBuilder, char[] szError, int maxErrorLen)
138316
{
139317
File pStripperCfg = OpenFile(szStripperConfig, "rt");
140318
if (!pStripperCfg)
@@ -281,7 +459,7 @@ bool ImportStripperConfig(const char[] szStripperConfig, File pSrcCoopConfig, ch
281459
{
282460
char[] szOut = new char[pOutBuilder.BufferSize];
283461
pOutBuilder.ToString(szOut);
284-
bError = !ImportUtil.ImportAsNode("entity_import_stripper", true, szOut, pSrcCoopConfig, szError, maxErrorLen);
462+
bError = !ImportUtil.ImportAsNode("entity_import_stripper", szOut, pConfigBuilder, szError, maxErrorLen);
285463
}
286464

287465
pStripperCfg.Close();
@@ -290,79 +468,4 @@ bool ImportStripperConfig(const char[] szStripperConfig, File pSrcCoopConfig, ch
290468
pKvDataRegex.Close();
291469

292470
return !bError;
293-
}
294-
295-
// --------------------------------------------------------------------------------------------------------------------------------
296-
// Import utils
297-
// --------------------------------------------------------------------------------------------------------------------------------
298-
299-
methodmap ImportUtil
300-
{
301-
public static bool FileFilter(ImportSource src, const char[] szFileName, char szMapName[MAX_MAPNAME])
302-
{
303-
int len = strlen(szFileName);
304-
int extLen;
305-
306-
if (src == IMPORT_STRIPPER)
307-
{
308-
if (!StrEndsWithEx(szFileName, len, ".cfg", false))
309-
return false;
310-
extLen = 4;
311-
}
312-
else ThrowError("ImportUtil.FileFilter not implemented for this import source.");
313-
314-
len = len - extLen + 1;
315-
if (len > MAX_MAPNAME)
316-
{
317-
MsgSrv("Map name in \"%s\" exceeds limit of %d characters and won't be imported.", szFileName, MAX_MAPNAME - 1);
318-
return false;
319-
}
320-
321-
strcopy(szMapName, len, szFileName);
322-
return true;
323-
}
324-
325-
public static bool ImportAsNode(const char[] szNode, bool bReplaceNode, const char[] szNodeContent, File pSrcCoopConfig, char[] szError, int maxErrorLen)
326-
{
327-
StringBuilder sb = new StringBuilder();
328-
if (!sb.AppendFile(pSrcCoopConfig))
329-
{
330-
sb.Close();
331-
Format(szError, maxErrorLen, "Unable to read from SourceCoop config");
332-
return false;
333-
}
334-
335-
int iConfigLen = sb.BufferSize;
336-
char[] szConfig = new char[iConfigLen];
337-
sb.ToString(szConfig);
338-
339-
PrintToServer(szConfig);
340-
341-
// This regex enters the next (or first) child from given position. Matching stops at opening { brace.
342-
// /^(?:\/\/.*|"(?:\\.|[^"])*"|\s|[^}{"])*?{/
343-
Regex pEnterChildNode = new Regex("^(?:\\/\\/.*|\"(?:\\\\.|[^\"])*\"|\\s|[^}{\"])*?{");
344-
345-
// This regex consists of OR'd groups each representing a part of KV syntax. The <braces> group is able to recurse itself.
346-
// The target node is searched only at current depth, as in kv.JumpToKey(). Matching stops at the start of the specified node (positive lookeahead).
347-
// /^(?:(?<comments>\/\/.*)|(?="?nodename"?)|(?<braces>{(?:(?&comments)|(?&braces)|(?&quotes)|(?&misc))*})|(?<quotes>"(?:\\.|[^"])*")|(?<misc>\s|[^"{}]))*/
348-
char szGoToNode[256] = "^(?:(?<comments>\\/\\/.*)|(?=\"?%s\"?)|(?<braces>{(?:(?&comments)|(?&braces)|(?&quotes)|(?&misc))*})|(?<quotes>\"(?:\\\\.|[^\"])*\")|(?<misc>\\s|[^\"{}]))*";
349-
Format(szGoToNode, sizeof(szGoToNode), szGoToNode, szNode);
350-
Regex pGoToNode = new Regex(szGoToNode);
351-
352-
sb.Close();
353-
return true;
354-
}
355-
356-
public static void AppendLine(StringBuilder sb, const char[] szText, int indent)
357-
{
358-
ImportUtil.Indent(sb, indent);
359-
sb.Append(szText);
360-
sb.Append("\n");
361-
}
362-
363-
public static void Indent(StringBuilder sb, int indent)
364-
{
365-
for (int i = 0; i < indent; i++)
366-
sb.Append("\t");
367-
}
368471
}

0 commit comments

Comments
 (0)