-
Notifications
You must be signed in to change notification settings - Fork 2
/
types.go
559 lines (518 loc) · 16.2 KB
/
types.go
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
package parseciv3
import (
"encoding/binary"
"io"
)
/*
The save file seems to be a simple binary dump of C++ data structures
packed with no byte padding. Generally speaking, most of the data is in
classes inherited from two basic classes. Both start with a 4-byte string
which appears to be a class name closely related to its function. One class
then has a 32-bit integer expressing the length in bytes of the data structure
following. The other has a 32-bit integer as a count of records. Each record
begins with a 32-bit length in bytes followed by the data. Before I knew this
I called each labeled length a "section", so I'll sometimes use that term
even now.
Some non-conformers appear to be the inital CIV3 section, but it's at least a
consistent length. The FLAV section is a list of lists. The second GAME section
in the SAV (which is the first GAME section of the non-BIC info) has an
apparently meaningless integer after the header followed by some predictable
data and then some as-yet unpredictable data which may be integer arrays, but
I haven't yet found the count. The length in bytes from GAME to DATE seems to
always be odd, so there must be a lone byte or a byte array in there somewhere.
I found a couple of stray apparent int32s after one of the DATE sections.
Later after the map tile data I have yet to figure out, too.
My strategy in the 2013-2015 Python version of this parser and my strategy so
far in Go is to parse the header, length/count and the data and then interpret
it. But several of the sections repeat with different lengths and data, especially
TILE but also WRLD and some others. I am presuming this is due to successive
versions of the game inheriting classes from the earlier game and adding to them,
and it shows up in the SAV file as the inheritance chain with data from each
generation. Mechanically parsing lenghts and counts works, but there really is
no advantage in meaning.
So I'm going to instead start making Go structs that will capture the entire
inheritance chain in one read which should make more sense programatically.
As I type this, I am mechanically parsing to WRLD but extracting little meaning
so far. During transition I'll be reading with two different methods.
*/
// ParsedData is the structure of the parsed data
type ParsedData map[string]Section
// Civ3Data contains the game data
type Civ3Data struct {
FileName string
Compressed bool
Data ParsedData
Civ3 Civ3Header
BicResources BicResources
BicFileHeader [4]byte
VerNum []VerNum
Bldg []Bldg
Ctzn []Ctzn
Cult []Cult
Diff []Difficulty
Eras []Era
Espn []Espn
Expr []Expr
Flav [][]Flavor
Good []Good
//
Wrld Wrld
Tile []Tile
Cont []Continent
ResourceCounts []int32
Next string
}
// Section is the inteface for the various structs decoded from the data files
type Section interface{}
// ListItem are the structs in a list
type ListItem interface{}
// Civ3Header is the SAV file header
// The Gobbledygook values appear to be 16 bytes of uncorrelated data, perhaps a hash or checksum?
type Civ3Header struct {
Name [4]byte
Always0x1a00 int16
MaybeVersionMinor, MaybeVersionMajor int32
Gobbeldygook1, Gobbeldygook2, Gobbeldygook3, Gobbeldygook4 uint32
}
// BicResources is part of the second SAV file section. Guessing at the alignment
type BicResources struct {
Name [4]byte
Length int32
A int32
ResourcePath [0x100]byte
B int32
BicPath [0x100]byte
C int32
}
// ListHeader ...
type ListHeader struct {
Name [4]byte
Count int32
}
// VerNum ...
type VerNum struct {
Length int32
A, B uint32
BicMajorVersion int32
BicMinorVersion int32
Description [640]byte
Title [64]byte
}
// Bldg ...
type Bldg struct {
Length int32
Description [64]byte
Name [32]byte
CivilopediaEntry [32]byte
DoublesHappinessOf4 int32
GainInEveryCity int32
GainInEveryCityOnContinent int32
RequiredBuilding int32
Cost int32
Culture int32
BombardmentDefense int32
NavalBombardmentDefense int32
DefenseBonus int32
NavalDefenseBonus int32
MaintenanceCost int32
HappyFacesAllCities int32
HappyFaces int32
UnhappyFacesAllCities int32
UnhappyFaces int32
NumberOfRequiredBuildings int32
AirPower int32
NavalPower int32
Pollution int32
Production int32
RequiredGovernment int32
SpaceshipPart int32
RequiredAdvance int32
RenderedObsoleteBy int32
RequiredResource1 int32
RequiredResource2 int32
ImprovementsBitMap int32
OtherCharacteristicsBitMap int32
SmallWondersBitMap int32
WondersBitMap int32
NumberOfArmiesRequired int32
FlavorsBitMap int32
A int32
UnitProducedPRTORef int32
UnitFrequency int32
}
// Ctzn ...
type Ctzn struct {
Length int32
DefaultCitizen int32
CitizensSingularName [32]byte
CivilopediaEntry [32]byte
PluralName [32]byte
Prerequisite int32
Luxuries int32
Research int32
Taxes int32
Corruption int32
Construction int32
}
// Cult ...
type Cult struct {
Length int32
CultureOpinionName [64]byte
ChanceOfSuccessfulPropaganda int32
CultureRatioPercentage int32
CultureRatioDenominator int32
CultureRatioNumerator int32
InitialResistanceChance int32
ContinuedResistanceChance int32
}
// Difficulty ...
type Difficulty struct {
Length int32
DifficultyLevelName [64]byte
NumberOfCitizensBornContent int32
MaxGovernmentTransitionTime int32
NumberOfDefensiveLandUnitsAIStartsWith int32
NumberOfOffensiveLandUnitsAIStartsWith int32
ExtraStartUnit1 int32
ExtraStartUnit2 int32
AdditionalFreeSupport int32
BonusForEachCity int32
AttackBonusAgainstBarbarians int32
CostFactor int32
PercentageOfOptimalCities int32
AIToAITradeRate int32
CorruptionPct int32
MilitaryLaw int32
}
// Era ...
type Era struct {
Length int32
EraName [64]byte
CivilopediaEntry [32]byte
Researcher1 [32]byte
Researcher2 [32]byte
Researcher3 [32]byte
Researcher4 [32]byte
Researcher5 [32]byte
NumberOfUsedResearcherNames int32
A int32
}
// Espn ...
type Espn struct {
Length int32
Description [128]byte
MissionName [64]byte
CivilopediaEntry [32]byte
MissionPerformedByBitMap int32
BaseCost int32
}
// Expr ...
type Expr struct {
Length int32
ExperienceLevelName [32]byte
BaseHitPoints int32
RetreatBonus int32
}
// Good are the resource types
type Good struct {
LengthOfResourceData88 int32
NaturalResourceName [24]byte
CivilopediaEntry [32]byte
Type int32
AppearanceRatio int32
DisappearanceProbability int32
Icon int32
Prerequisite int32
FoodBonus int32
ShieldsBonus int32
CommerceBonus int32
}
// Base is one of the basic section structures of the game data
type Base struct {
Name [4]byte
Length int32
RawData []byte
}
func newBase(r io.ReadSeeker) (Base, error) {
var base Base
var err error
err = binary.Read(r, binary.LittleEndian, &base.Name)
if err != nil {
return base, ReadError{err}
}
err = binary.Read(r, binary.LittleEndian, &base.Length)
if err != nil {
return base, ReadError{err}
}
base.RawData = make([]byte, base.Length)
err = binary.Read(r, binary.LittleEndian, &base.RawData)
if err != nil {
return base, ReadError{err}
}
return base, nil
}
// List is one of the basic section structures of the game data
type List struct {
Name [4]byte
Count int32
List [][]byte
}
func newList(r io.ReadSeeker) (List, error) {
var list List
var err error
err = binary.Read(r, binary.LittleEndian, &list.Name)
if err != nil {
return list, ReadError{err}
}
err = binary.Read(r, binary.LittleEndian, &list.Count)
if err != nil {
return list, ReadError{err}
}
for i := int32(0); i < list.Count; i++ {
var length int32
err = binary.Read(r, binary.LittleEndian, &length)
if err != nil {
return list, ReadError{err}
}
temp := make([]byte, length)
err = binary.Read(r, binary.LittleEndian, &temp)
list.List = append(list.List, temp)
}
return list, nil
}
// Flav is one of the basic section structures of the game data
type Flav struct {
Name [4]byte
Count int32
List [][]Flavor
}
func newFlav(r io.ReadSeeker) (Flav, error) {
var flav Flav
var err error
err = binary.Read(r, binary.LittleEndian, &flav.Name)
if err != nil {
return flav, ReadError{err}
}
err = binary.Read(r, binary.LittleEndian, &flav.Count)
if err != nil {
return flav, ReadError{err}
}
for i := int32(0); i < flav.Count; i++ {
var count int32
err = binary.Read(r, binary.LittleEndian, &count)
if err != nil {
return flav, ReadError{err}
}
flavorGroups := make([]Flavor, count)
flav.List = append(flav.List, flavorGroups)
for j := int32(0); j < count; j++ {
flav.List[i][j] = Flavor{}
err = binary.Read(r, binary.LittleEndian, &flav.List[i][j])
if err != nil {
return flav, ReadError{err}
}
}
}
return flav, nil
}
// Flavor is the leaf element of FLAV
// Hard-coding FlavorRelations at 7. Hopefully that always works
type Flavor struct {
A int32
FlavorName [0x100]byte
NumFlavorRelations int32
FlavorRelations [7]int32
}
// Game is the first section after the BIC.
type Game struct {
// First two fields count for "class base"
Name [4]byte
_ int32
_ [3]int32
RenderFlags int32
DifficultyLevel int32
_ int32
UnitsCount int32
CitiesCount int32
_ int32
_ int32
GlobalWarmingLevel int32
_ int32
_ int32
_ int32
CurrentTurn int32
_ int32
Random int32
_ int32
CivFlags2 int32
CivFlags1 int32
_ int32
_ int32
_ int32
_ int32
_ int32
_ int32
_ [48]int32
Value1 int32
_ [72]int32
GameLimitPoints int32
GameLimitTurns int32
_ [50]int32
_ int32
_ int32
GameLimitDestroyedCities int32
GameLimitCityCulture int32
GameLimitCivCulture int32
GameLimitPopulation int32
GameLimitTerritory int32
GameLimitWonders int32
GameLimitDestroyedWonders int32
GameLimitAdvances int32
GameLimitCapturedCities int32
GameLimitVictoryPointPrice int32
GameLimitPrincessRansom int32
DefaultDate1 int32
}
// GameNext is what Antal1987's dumps suggest is next, but I don't think so
type GameNext struct {
_ [27]int32
PLGI [10]int32
Date2 Date
Date3 Date
GameAggression int32
_ int32
CityStatIntArray int32
ResearchedAdvances int32
Wonders int32
WonderFlags int32
ImprovementTypesData1 int32
ImprovementTypesData2 int32
UnitTypesData1 int32
UnitTypesData2 int32
_ int32
_ int32
DefaultGameSettings DefaultGameSettings
}
// Date DATE section ... I don't think this is nearly right
type Date struct {
Name [4]byte
Length int32
Text [16]byte
_ [12]int32
BaseTimeUnit int32
Month int32
Week int32
Year int32
_ int32
}
// DefaultGameSettings from Antal1987's dump
type DefaultGameSettings struct {
TurnsLimit int32
PointsLimit int32
DestroyedCitiesLimit int32
CityCultureLimit int32
CivCultureLimit int32
PopulationLimit int32
TerritoryLimit int32
WondersLimit int32
DestroyedUnitsLimit int32
AdvancesLimit int32
CapturedCitiesLimit int32
VictoryPointPrice int32
PrincessPrice int32
PrincessRansom int32
}
// Wrld is the Conquests' 3 WRLD sections combined
type Wrld struct {
Name [4]byte `json:"-"`
Length int32 `json:"-"`
NumContinents int16
Name2 [4]byte `json:"-"`
Length2 int32 `json:"-"`
OceanContinentID int32
MapHeight int32
DistanceBetweencivs int32
MaybeCivCount int32
D int32
E int32
MapWidth int32
CivStartLocationTileID [32]int32
WorldSeed int32
G int32
Name3 [4]byte `json:"-"`
Length3 int32 `json:"-"`
GenOptions WorldFeatures
}
// WorldFeatures is the map generation settings
// Barbs: -1 is off, 0 sedendary...3 raging
type WorldFeatures struct {
Climate int32
ClimateFinal int32
Barbarians int32
BarbariansFinal int32
Landmass int32
LandmassFinal int32
OceanCoverage int32
OceanCoverageFinal int32
Temperature int32
TemperatureFinal int32
Age int32
AgeFinal int32
Size int32
}
// Tile is Conquests' 4 TILE sections per world tile combined
type Tile struct {
Name [4]byte `json:"-"`
Length int32 `json:"-"`
Rivers uint8
MaybeTerritoryCivID int8
MaybeLandmarkTerrain int16
ResourceType int32
TileUnitID int32
MaybeSquarePart int16
MaybeVictoryPoint int16
MaybePreConquestsTileInfo int32
BarbTribeID int16
CityID int16
MaybeColonyID int16
ContinentID int16
A int32
B int32
Name2 [4]byte `json:"-"`
Length2 int32 `json:"-"`
Improvements int32
C int8
Terrain uint8
MaybeByCivBitMask uint32
TerrainFeatures uint16
Name3 [4]byte `json:"-"`
Length3 int32 `json:"-"`
D int32
Name4 [4]byte `json:"-"`
Length4 int32 `json:"-"`
VisibleToCiv uint32
VisibleNowToCivUnits uint32
MaybeVisibleToColonies uint32
VisibleToCivCulture uint32
E int32
CityID2 int16
TradeNetworkIDByCiv [32]int16
MaybeImprovementsKnownToCiv [32]uint8
F uint16
G int32
H int32
}
// Continent is the CONT section
// Land is 1 for land, 0 for water
// Size is number of tiles
type Continent struct {
Name [4]byte
Length int32
Land int32
Size int32
}
// Lead is the LEAD section in the game, not the BIC
type Lead struct {
Name [4]byte
A int32
}