forked from SCP-CB-CPP/scpcb
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathModManager.bb
More file actions
350 lines (310 loc) · 10.9 KB
/
Copy pathModManager.bb
File metadata and controls
350 lines (310 loc) · 10.9 KB
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
Type ActiveMods
Field Path$
Field IsLocale%
End Type
Type Mods
Field Id$
Field Path$
Field Name$
Field Description$
Field Author$
Field IconPath$, Icon%, DisabledIcon%
Field RequiresReload%
Field IsNew%, IsActive%
Field SteamWorkshopId$
Field IsUserOwner%
End Type
Global ModCount%
Const STEAM_ITEM_ID_FILENAME$ = "steam_itemid.txt"
Function InstantiateMod.Mods(id$, path$)
m.Mods = new Mods
m\IsNew = True
m\Id = id
m\Path = path
Local modIni$ = m\Path + "info.ini"
If FileType(modIni) <> 1 Then RuntimeErrorExt("Mod at " + Chr(34) + m\Path + Chr(34) + " is missing an info.ini file.")
Local ini% = OpenFile(modIni)
While Not Eof(ini)
Local l$ = Trim(ReadLine(ini))
If l <> "" And Instr(l, "#") <> 1 And Instr(l, ";") <> 1 Then
Local splitterPos = Instr(l, "=")
Local key$ = Trim(Left(l, splitterPos - 1))
Local value$ = Trim(Right(l, Len(l) - splitterPos))
Select key
Case "name"
m\Name = value
Case "desc"
m\Description = value
Case "author"
m\Author = value
Case "requires reload"
m\RequiresReload = ParseINIInt(value)
End Select
EndIf
Wend
CloseFile(ini)
If m\Name = "" Then RuntimeErrorExt("Mod at " + Chr(34) + m\Path + Chr(34) + " is missing a name in its info.ini file.")
For m2.Mods = Each Mods
; ID collisions should be impossible.
If m2 <> m And m2\Id = m\Id Then RuntimeErrorExt("Mod at " + Chr(34) + m\Path + Chr(34) + " and mod at " + Chr(34) + m\Path + Chr(34) + " share a mod ID.")
Next
m\IconPath = DetermineIcon(m, False)
ModCount = ModCount + 1
Return m
End Function
Function ReloadMods()
For m.Mods = Each Mods
If Icon <> 0 Then FreeImage(Icon)
Next
Delete Each Mods
ModCount = 0
d% = ReadDir("Mods")
Repeat
f$=NextFile(d)
If f$="" Then Exit
If f$<>"." And f$<>".." And FileType("Mods\"+f) = 2 Then
m.Mods = InstantiateMod("local " + f, "Mods\"+f+"\")
; These files will find their way onto the workshop on the first update, so we only set the owner for local mods.
If FileType(m\Path + STEAM_ITEM_ID_FILENAME) = 1 Then
Local idFile% = OpenFile(m\Path + STEAM_ITEM_ID_FILENAME)
m\SteamWorkshopId = ReadLine(idFile)
m\IsUserOwner = True
CloseFile(idFile)
EndIf
EndIf
Forever
CloseDir(d)
If SteamActive
Steam_LoadSubscribedItems()
Local itemCount% = Steam_GetSubscribedItemCount()
For i = 0 To itemCount-1
Local id$ = Steam_GetSubscribedItemID(i)
Local itemPath$ = Steam_GetSubscribedItemPath(i)
If id <> "" And itemPath <> ""
m.Mods = InstantiateMod(id, itemPath + "\")
m\SteamWorkshopId = id
EndIf
Next
EndIf
If FileType(ModsFile) = 1 Then
Local mods% = OpenFile(ModsFile)
Local firstSorted.Mods = First Mods
While Not Eof(mods)
l$ = Trim(ReadLine(mods))
If l <> "" And Instr(l, "#") <> 1 And Instr(l, ";") <> 1 Then
splitterPos = Instr(l, "=")
key$ = Trim(Left(l, splitterPos - 1))
value$ = Trim(Right(l, Len(l) - splitterPos))
For m.Mods = Each Mods
If key = m\Id Then
Insert m After firstSorted
firstSorted = m
m\IsActive = ParseINIInt(value)
m\IsNew = False
Exit
EndIf
Next
EndIf
Wend
CloseFile(mods)
EndIf
UpdateActiveMods()
End Function
Function SerializeMods()
Local f% = WriteFile(ModsFile)
For m.Mods = Each Mods
WriteLine(f, m\Id + "=" + Str(m\IsActive))
Next
CloseFile(f)
End Function
Const LOCALIZATIONS_DIR$ = "Localization\"
Function UpdateActiveMods()
HasDubbedAudio = False
Local txt$
Delete Each ActiveMods
Local mm.ActiveMods
Local locale$ = GetCLIString("locale", GetOptionString("general", "locale"))
If locale = "" And SteamActive Then locale = Steam_GetGameLanguage()
If locale <> "" And FileType(LOCALIZATIONS_DIR + locale) = 2 Then
mm = New ActiveMods
mm\Path = LOCALIZATIONS_DIR + locale + "\"
mm\IsLocale = True
If FileType(mm\Path + "SFX") = 2 Then HasDubbedAudio = True
txt = "locale " + locale
EndIf
For m.Mods = Each Mods
If m\IsActive Then
If locale <> "" And FileType(m\Path + LOCALIZATIONS_DIR + locale) = 2 Then
mm.ActiveMods = New ActiveMods
mm\Path = m\Path + LOCALIZATIONS_DIR + locale + "\"
mm\IsLocale = True
If FileType(mm\Path + "SFX") = 2 Then HasDubbedAudio = True
If txt <> "" Then txt = txt + ", "
txt = txt + m\Id + " locale " + locale
EndIf
mm.ActiveMods = New ActiveMods
mm\Path = m\Path
If txt <> "" Then txt = txt + ", "
txt = txt + m\Id
EndIf
Next
SetErrorMsg(10, "Active mods: " + txt)
UsesDubbedAudio = HasDubbedAudio And DubbedAudio
End Function
Function LoadModdedTextureNonStrict%(file$, flags%)
Local ext$ = File_GetExtension(File)
Local fileNoExt$ = Left(File, Len(File) - Len(ext))
Local tmp%
For m.ActiveMods = Each ActiveMods
For i = 0 To ImageExtensionCount
Local usedExtension$
If i = ImageExtensionCount Then
usedExtension = ext
Else
usedExtension = ImageExtensions[i]
EndIf
Local modPath$ = m\Path + fileNoExt + usedExtension
If FileType(modPath) = 1 Then
tmp = LoadTexture(modPath, flags)
If tmp <> 0 Then
Return tmp
Else If DebugResourcePacks Then
RuntimeErrorExt("Failed to load texture " + Chr(34) + modPath + Chr(34) + ".")
EndIf
EndIf
Next
Next
tmp = LoadTexture(file, flags)
If tmp <> 0 Then Return tmp
For i = 0 To ImageExtensionCount-1
usedExtension$ = ImageExtensions[i]
Local path$ = fileNoExt + usedExtension
If FileType(path) = 1 Then
tmp = LoadTexture(path, flags)
If tmp <> 0 Then Return tmp
EndIf
Next
Return 0
End Function
Function LoadModdedMeshNonStrict%(File$, parent%=0)
Local ext$ = File_GetExtension(File)
Local fileNoExt$ = Left(File, Len(File) - Len(ext))
Local tmp%
For m.ActiveMods = Each ActiveMods
For i = 0 To ModelExtensionCount
Local usedExtension$
If i = ModelExtensionCount Then
usedExtension = ext
Else
usedExtension = ModelExtensions[i]
EndIf
Local modPath$ = m\Path + fileNoExt + usedExtension
If FileType(modPath) = 1 Then
tmp = LoadMesh(modPath, parent)
If tmp <> 0 Then
Return tmp
Else If DebugResourcePacks Then
RuntimeErrorExt("Failed to load 3D Mesh " + Chr(34) + modPath + Chr(34) + ".")
EndIf
EndIf
Next
Next
Return LoadMesh(File, parent)
End Function
Function DetermineModdedPath$(f$)
For m.ActiveMods = Each ActiveMods
modPath$ = m\Path + f
If FileType(modPath) = 1 Then Return modPath
Next
Return f
End Function
Function DetermineIcon$(m.Mods, allowGif% = True)
If allowGif And FileType(m\Path + "icon.gif") = 1 Then Return m\Path + "icon.gif"
If FileType(m\Path + "icon.png") = 1 Then Return m\Path + "icon.png"
If FileType(m\Path + "icon.jpg") = 1 Then Return m\Path + "icon.jpg"
Return ""
End Function
Global UpdatingMod.Mods
Global UpdateModErrorCode%
Function UploadMod(m.Mods)
If UpdatingMod <> Null Then Return
UpdatingMod = m
Steam_PublishItem(m\Name, m\Description, m\Path, DetermineIcon(m))
End Function
Function UpdateMod(m.Mods, changelog$)
If UpdatingMod <> Null Then Return
UpdatingMod = m
Local desc$ = ""
If Not ShouldKeepModDescription Then
desc = m\Description
EndIf
Steam_UpdateItem(m\SteamworkshopId, m\Name, desc, m\Path, DetermineIcon(m), changelog, True)
End Function
Function UpdateUpdatingMod()
If UpdatingMod = Null Then Return
Local status% = Steam_QueryUpdateItemStatus()
If status = 3 Then
UpdatingMod\SteamWorkshopId = Steam_GetPublishedItemID()
UpdatingMod\IsUserOwner = True
Local f% = WriteFile(UpdatingMod\Path + STEAM_ITEM_ID_FILENAME)
WriteLine(f, UpdatingMod\SteamWorkshopId)
CloseFile(f)
VisitModPage(UpdatingMod)
UpdatingMod = Null
EndIf
If status => 100 Then
UpdateModErrorCode = status
UpdatingMod = Null
EndIf
End Function
Function VisitModPage(m.Mods)
If m\SteamWorkshopId = "" Then Return
ExecFile("https://steamcommunity.com/sharedfiles/filedetails/?id=" + m\SteamworkshopId)
End Function
Function GetWorkshopErrorCodeStr$(err%)
Local txt$ = ""
Select err Mod 1000
Case 2 txt = "Generic failure. Try changing your Steam download region or your connection type."
Case 24 txt = "You are restricted from uploading content. Please contact Steam support."
Case 17 txt = "You are currently VAC or game banned."
Case 16 txt = "The operation timed out. Please try again."
Case 21 txt = "You are not currently logged into Steam."
Case 20 txt = "The workshop server is currently unavailable. Please try again later."
Case 8 txt = "One of the submissions data fields is invalid."
Case 15 txt = "Access denied."
Case 25 txt = "The preview image is too large, it must be less than 1 MB or you have exceeded your Steam Cloud quota."
Case 9 txt = "An uploaded file could not be found."
Case 14 txt = "You already have a workshop item with that name."
Case 44 txt = "Due to a recent password or email change you are not currently allowed to upload new content."
End Select
If txt <> "" Then
Return "Code " + Str(err) + ": " + txt
Else
Return "Code " + Str(err)
EndIf
End Function
Function GetModdedINIString$(file$, section$, key$)
For m.ActiveMods = Each ActiveMods
Local moddedPath$ = m\Path + file
If FileType(moddedPath) = 1
Local ret$ = GetINIString(moddedPath, section, key)
If ret <> "" Then Return ret
EndIf
Next
Return GetINIString(file, section, key)
End Function
Function GetModdedINIInt%(file$, section$, key$)
Return Int(GetModdedINIString(file, section, key))
End Function
Function GetModdedINIFloat#(file$, section$, key$)
Return Float(GetModdedINIString(file, section, key))
End Function
Function Format$(txt$, arg1$)
Return Replace(txt, "{0}", arg1)
End Function
Function Format$(txt$, arg1$, arg2$)
Return Replace(Format(txt, arg1), "{1}", arg2)
End Function
Function Format$(txt$, arg1$, arg2$, arg3$)
Return Replace(Format(txt, arg1, arg2), "{2}", arg3)
End Function