-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmapParser.cs
201 lines (167 loc) · 7.17 KB
/
mapParser.cs
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
// Copyright 2013 Jim Nelson
//
// This file is part of Civ3 Show-And-Tell.
// For console output.
using System;
// For file I/O.
using System.IO;
// For Lists.
using System.Collections.Generic;
// Extending BinaryReader to read 4-char strings from stream.
class MyBinaryReader: System.IO.BinaryReader {
// Constructor.
public MyBinaryReader(System.IO.Stream input) :base(input) {
}
// Read char[] of given length and return string.
public string ReadCharString(int length) {
char[] charArray = this.ReadChars(length);
string myString = new string(charArray);
return myString;
}
}
// Base class for reading SAV files.
class GenericSection {
protected internal string Name;
protected internal byte[] buffer;
// Constructor.
public GenericSection(MyBinaryReader saveStream) {
Console.WriteLine("GenericSection constrcutor");
this.Name = saveStream.ReadCharString(4);
Console.WriteLine(this.Name);
}
// Constructor with specified data length.
public GenericSection(MyBinaryReader saveStream, int length) {
Console.WriteLine("GenericSection constrcutor");
this.Name = saveStream.ReadCharString(4);
Console.WriteLine(this.Name);
buffer = saveStream.ReadBytes(length);
}
}
class SectionWithLength: GenericSection {
protected int Length;
// protected byte[] buffer;
// Constructor.
public SectionWithLength(MyBinaryReader saveStream) :base(saveStream) {
Console.WriteLine("SectionWithLength constrcutor");
this.Length = saveStream.ReadInt32();
//Console.WriteLine(this.Name);
Console.WriteLine(this.Length);
buffer = saveStream.ReadBytes(this.Length);
}
}
class SectionWithLengthNoName {
protected int Length;
protected byte[] buffer;
// Constructor.
public SectionWithLengthNoName(MyBinaryReader saveStream) {
Console.WriteLine("SectionWithLengthNoName constrcutor");
this.Length = saveStream.ReadInt32();
Console.WriteLine(this.Length);
buffer = saveStream.ReadBytes(this.Length);
}
}
class SectionWithSubrecords: GenericSection {
protected int numSubRecords;
protected List<SectionWithLengthNoName> subRecord;
public SectionWithSubrecords(MyBinaryReader saveStream) :base(saveStream) {
this.subRecord = new List<SectionWithLengthNoName>();
this.numSubRecords = saveStream.ReadInt32();
for (int i = 0; i < this.numSubRecords; i++) {
this.subRecord.Add(new SectionWithLengthNoName(saveStream));
//Console.WriteLine(i);
}
}
}
class BicqSection: GenericSection {
protected SectionWithSubrecords verNum, game;
public BicqSection(MyBinaryReader saveStream) :base (saveStream) {
this.verNum = new SectionWithSubrecords(saveStream);
this.game = new SectionWithSubrecords(saveStream);
}
}
// The object to read the save game stream and extract info.
class civ3Game {
protected GenericSection civ3Section, gameSection;
protected SectionWithLength bicSection;
protected BicqSection bicqSection;
protected List<SectionWithLength> tileSection, pileOfSectionsToSortLater;
protected List<byte[]> bytePads;
// Constructor.
public civ3Game(MyBinaryReader saveStream) {
// Initialize my list of sections I don't know what to do with yet.
this.pileOfSectionsToSortLater = new List<SectionWithLength>();
// Initialize empty byte[] pad list.
this.bytePads = new List<byte[]>();
// Initialize empty tile list.
this.tileSection = new List<SectionWithLength>();
// Read save file stream assuming fixed order and general structure.
// Trying to use class constructors but reading some padding in manually.
// FIX: hard-coding CIV3 length of 0x1a
this.civ3Section = new GenericSection(saveStream, 0x1a);
this.bicSection = new SectionWithLength(saveStream);
this.bicqSection = new BicqSection(saveStream);
this.gameSection = new GenericSection(saveStream, 0x0ecf);
// reading in five sections (DATE, PLGI, PLGI, DATE and DATE)
this.pileOfSectionsToSortLater.Add(new SectionWithLength(saveStream));
this.pileOfSectionsToSortLater.Add(new SectionWithLength(saveStream));
this.pileOfSectionsToSortLater.Add(new SectionWithLength(saveStream));
this.pileOfSectionsToSortLater.Add(new SectionWithLength(saveStream));
this.pileOfSectionsToSortLater.Add(new SectionWithLength(saveStream));
// FIX: hard-coded byte padding here. Is it algined? Other way to realign?
this.bytePads.Add(saveStream.ReadBytes(8));
// reading CNSL section
this.pileOfSectionsToSortLater.Add(new SectionWithLength(saveStream));
// reading three WRLD sections
this.pileOfSectionsToSortLater.Add(new SectionWithLength(saveStream));
this.pileOfSectionsToSortLater.Add(new SectionWithLength(saveStream));
// FIX: Ugly hack to grab presumed map width and height.
int mapWidth = BitConverter.ToInt32(pileOfSectionsToSortLater[pileOfSectionsToSortLater.Count - 1].buffer, 0x04);
int mapHeight = BitConverter.ToInt32(pileOfSectionsToSortLater[pileOfSectionsToSortLater.Count - 1].buffer, 0x18);
Console.WriteLine(mapWidth);
Console.WriteLine(mapHeight);
this.pileOfSectionsToSortLater.Add(new SectionWithLength(saveStream));
// FIX: hard-seeking to first TILE section of my test file.
// saveStream.BaseStream.Seek(0x34a4, SeekOrigin.Begin);
// Read all TILE sections. There are mapWidth/2 * mapHeight * 4 of them
// FIX: I really want one entity per game tile, but there are four TILE sections per game tile
for (int i = 0; i < (mapWidth / 2) * mapHeight * 4; i++) {
this.tileSection.Add(new SectionWithLength(saveStream));
}
Console.WriteLine(tileSection.Count);
/*
// FIX: Need to load all tiles. Load a single tile into the list.
this.tileSection.Add(new SectionWithLength(saveStream));
this.tileSection.Add(new SectionWithLength(saveStream));
this.tileSection.Add(new SectionWithLength(saveStream));
this.tileSection.Add(new SectionWithLength(saveStream));
this.tileSection.Add(new SectionWithLength(saveStream));
this.tileSection.Add(new SectionWithLength(saveStream));
*/
// Iterating and printing out section names of my junk pile.
foreach (SectionWithLength mySection in this.pileOfSectionsToSortLater) {
Console.WriteLine(mySection.Name);
}
}
}
class KickItOff {
static void Main() {
Console.WriteLine("Hello, Civ3'ers!");
// FIX: Hard-coding the save file name to my test save in the current folder
string SaveFilePath = "unc-test.sav";
/* Trying BinaryReader instead of File.ReadAllBytes
// Read the file into a byte array.
byte[] SaveFileByteArray = File.ReadAllBytes(SaveFilePath);
System.Text.ASCIIEncoding ascii = new System.Text.ASCIIEncoding();
Console.WriteLine(ascii.GetString(SaveFileByteArray, 0, 4));
*/
// Open the binary file.
// Does the 'using' statement do anything for me here? Seems to crash if the file doesn't exist.
using (MyBinaryReader saveStream = new MyBinaryReader(File.Open(SaveFilePath,FileMode.Open))) {
//Console.WriteLine("Inside the using file statement");
//Console.WriteLine(saveStream.ReadChars(4));
//GenericSection TempVar = new GenericSection(saveStream);
//SectionWithLength TempVar = new SectionWithLength(saveStream);
civ3Game game = new civ3Game(saveStream);
}
}
}