-
Notifications
You must be signed in to change notification settings - Fork 4
/
Mercenary.java
319 lines (286 loc) · 12 KB
/
Mercenary.java
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
/*
Written 1999 by Douglas Greiman.
This software may be used and distributed according to the terms
of the GNU Public License, incorporated herein by reference.
*/
package duggelz.jape;
import java.util.Hashtable;
public class Mercenary extends BasicStructure
{
// Static data
public static final int CIPHERTEXT_OFFSET = 1;
public static final int CIPHERTEXT_LENGTH = 0x918;
public static final int FIRST_ITEM_OFFSET = 0x0c;
public static final int ITEM_LENGTH = 0x24;
public static final int ITEM_COUNT = 19;
public static final int HELMET_INDEX = 0;
public static final int BODY_ARMOR_INDEX = 1;
public static final int LEG_ARMOR_INDEX = 2;
public static final int HEADGEAR_1_INDEX = 3;
public static final int HEADGEAR_2_INDEX = 4;
public static final int RIGHT_HAND_INDEX = 5;
public static final int LEFT_HAND_INDEX = 6;
public static final int BACKPACK_1_3_INDEX = 7;
public static final int BACKPACK_2_3_INDEX = 8;
public static final int BACKPACK_3_3_INDEX = 9;
public static final int BACKPACK_4_3_INDEX = 10;
public static final int BACKPACK_1_1_INDEX = 11;
public static final int BACKPACK_2_1_INDEX = 12;
public static final int BACKPACK_3_1_INDEX = 13;
public static final int BACKPACK_4_1_INDEX = 14;
public static final int BACKPACK_1_2_INDEX = 15;
public static final int BACKPACK_2_2_INDEX = 16;
public static final int BACKPACK_3_2_INDEX = 17;
public static final int BACKPACK_4_2_INDEX = 18;
public static final int ENERGY_OFFSET_99 = 0x2c7;
public static final int ENERGY_OFFSET_103 = 45;
public static final int MAX_ENERGY_OFFSET_99 = 0x2c8;
public static final int MAX_ENERGY_OFFSET_103 = 46;
public static final int NICK_OFFSET_99 = 0x2da;
public static final int NICK_OFFSET_103 = 2;
public static final int NICK_LENGTH = 20;
public static final int ubProfile_Offset_103 = 717;
public static final int CHECKSUM_OFFSET_99 = 0x8a0;
public static final int CHECKSUM_OFFSET_103 = 972;
public static final int CHECKSUM_LENGTH = 4;
// These stats are in STRUCT_AIData in 103
public static final int MORALE_OFFSET_99 = 0x6c8;
public static final int MORALE_OFFSET_103 = 204;
// These stats are in STRUCT_Statistics in 103
public static final int statsOffset_103 = 428;
public static final int SKILL1_OFFSET_99 = 0x340;
public static final int SKILL1_OFFSET_103 = 8 + statsOffset_103;
public static final int SKILL2_OFFSET_99 = 0x341;
public static final int SKILL2_OFFSET_103 = 9 + statsOffset_103;
public static final int DEX_OFFSET_99 = 0x348;
public static final int DEX_OFFSET_103 = 10 + statsOffset_103;
public static final int WIS_OFFSET_99 = 0x349;
public static final int WIS_OFFSET_103 = 11 + statsOffset_103;
public static final int LVL_OFFSET_99 = 0x351;
public static final int LVL_OFFSET_103 = 2 + statsOffset_103;
public static final int HEALTH_OFFSET_99 = 0x364;
public static final int HEALTH_OFFSET_103 = 0 + statsOffset_103;
public static final int AGI_OFFSET_99 = 0x370;
public static final int AGI_OFFSET_103 = 3 + statsOffset_103;
public static final int STR_OFFSET_99 = 0x376;
public static final int STR_OFFSET_103 = 4 + statsOffset_103;
public static final int LDR_OFFSET_99 = 0x37f;
public static final int LDR_OFFSET_103 = 14 + statsOffset_103;
public static final int MEC_OFFSET_99 = 0x394;
public static final int MEC_OFFSET_103 = 5 + statsOffset_103;
public static final int MAX_HEALTH_OFFSET_99 = 0x395;
public static final int MAX_HEALTH_OFFSET_103 = 1 + statsOffset_103;
public static final int MED_OFFSET_99 = 0x55c;
public static final int MED_OFFSET_103 = 12 + statsOffset_103;
public static final int MRK_OFFSET_99 = 0x561;
public static final int MRK_OFFSET_103 = 6 + statsOffset_103;
public static final int EXP_OFFSET_99 = 0x562;
public static final int EXP_OFFSET_103 = 7 + statsOffset_103;
// Instance data
public byte[] allData;
public Item[] items = new Item[ITEM_COUNT];
public int savedGameVersion;
public int trailingOffset;
public int inventorySum;
public int ENERGY_OFFSET;
public int MAX_ENERGY_OFFSET;
public int NICK_OFFSET;
public int CHECKSUM_OFFSET;
public int MORALE_OFFSET;
public int SKILL1_OFFSET;
public int SKILL2_OFFSET;
public int DEX_OFFSET;
public int WIS_OFFSET;
public int LVL_OFFSET;
public int HEALTH_OFFSET;
public int AGI_OFFSET;
public int STR_OFFSET;
public int LDR_OFFSET;
public int MEC_OFFSET;
public int MAX_HEALTH_OFFSET;
public int MED_OFFSET;
public int MRK_OFFSET;
public int EXP_OFFSET;
// Instance methods
public Mercenary(byte[] rawData, int savedGameVersion,
int trailingOffset, int inventorySum,
int[] table) {
super(new Hashtable());
this.savedGameVersion = savedGameVersion;
this.trailingOffset = trailingOffset;
this.inventorySum = inventorySum;
this.decode(rawData, table);
// Compute field offsets
if (savedGameVersion <= 99) {
ENERGY_OFFSET = ENERGY_OFFSET_99;
MAX_ENERGY_OFFSET = MAX_ENERGY_OFFSET_99;
NICK_OFFSET = NICK_OFFSET_99;
CHECKSUM_OFFSET = CHECKSUM_OFFSET_99;
MORALE_OFFSET = MORALE_OFFSET_99;
SKILL1_OFFSET = SKILL1_OFFSET_99;
SKILL2_OFFSET = SKILL2_OFFSET_99;
DEX_OFFSET = DEX_OFFSET_99;
WIS_OFFSET = WIS_OFFSET_99;
LVL_OFFSET = LVL_OFFSET_99;
HEALTH_OFFSET = HEALTH_OFFSET_99;
AGI_OFFSET = AGI_OFFSET_99;
STR_OFFSET = STR_OFFSET_99;
LDR_OFFSET = LDR_OFFSET_99;
MEC_OFFSET = MEC_OFFSET_99;
MAX_HEALTH_OFFSET = MAX_HEALTH_OFFSET_99;
MED_OFFSET = MED_OFFSET_99;
MRK_OFFSET = MRK_OFFSET_99;
EXP_OFFSET = EXP_OFFSET_99;
} else {
ENERGY_OFFSET = ENERGY_OFFSET_103;
MAX_ENERGY_OFFSET = MAX_ENERGY_OFFSET_103;
NICK_OFFSET = NICK_OFFSET_103;
CHECKSUM_OFFSET = CHECKSUM_OFFSET_103;
MORALE_OFFSET = trailingOffset + MORALE_OFFSET_103;
SKILL1_OFFSET = trailingOffset + SKILL1_OFFSET_103;
SKILL2_OFFSET = trailingOffset + SKILL2_OFFSET_103;
DEX_OFFSET = trailingOffset + DEX_OFFSET_103;
WIS_OFFSET = trailingOffset + WIS_OFFSET_103;
LVL_OFFSET = trailingOffset + LVL_OFFSET_103;
HEALTH_OFFSET = trailingOffset + HEALTH_OFFSET_103;
AGI_OFFSET = trailingOffset + AGI_OFFSET_103;
STR_OFFSET = trailingOffset + STR_OFFSET_103;
LDR_OFFSET = trailingOffset + LDR_OFFSET_103;
MEC_OFFSET = trailingOffset + MEC_OFFSET_103;
MAX_HEALTH_OFFSET = trailingOffset + MAX_HEALTH_OFFSET_103;
MED_OFFSET = trailingOffset + MED_OFFSET_103;
MRK_OFFSET = trailingOffset + MRK_OFFSET_103;
EXP_OFFSET = trailingOffset + EXP_OFFSET_103;
}
this.fields.put("Energy", new ByteField(ENERGY_OFFSET));
this.fields.put("Max Energy",
new ByteField(MAX_ENERGY_OFFSET));
this.fields.put("Nickname",
new StringField(NICK_OFFSET, NICK_LENGTH));
this.fields.put("Skill1",
new ChoiceField(new ByteField(SKILL1_OFFSET),
Skill.table));
this.fields.put("Skill2",
new ChoiceField(new ByteField(SKILL2_OFFSET),
Skill.table));
this.fields.put("Dexterity", new ByteField(DEX_OFFSET));
this.fields.put("Wisdom", new ByteField(WIS_OFFSET));
this.fields.put("Level", new ByteField(LVL_OFFSET));
this.fields.put("Health", new ByteField(HEALTH_OFFSET));
this.fields.put("Agility", new ByteField(AGI_OFFSET));
this.fields.put("Strength", new ByteField(STR_OFFSET));
this.fields.put("Leadership", new ByteField(LDR_OFFSET));
this.fields.put("Mechanical", new ByteField(MEC_OFFSET));
this.fields.put("Max Health", new ByteField(MAX_HEALTH_OFFSET));
this.fields.put("Medical", new ByteField(MED_OFFSET));
this.fields.put("Marksmanship", new ByteField(MRK_OFFSET));
this.fields.put("Explosives", new ByteField(EXP_OFFSET));
this.fields.put("Morale", new ByteField(MORALE_OFFSET));
this.fields.put("ubProfile", new ByteField(ubProfile_Offset_103));
this.fields.put("Checksum", new IntField(CHECKSUM_OFFSET));
}
public void decode(
byte[] rawData,
int[] table)
{
// Save all data
this.allData = new byte[rawData.length];
System.arraycopy(rawData, 0, this.allData, 0, this.allData.length);
// Decode the encoded portion
if (this.savedGameVersion <= 99) {
byte[] ciphertext = new byte[CIPHERTEXT_LENGTH];
System.arraycopy(rawData, CIPHERTEXT_OFFSET, ciphertext, 0,
CIPHERTEXT_LENGTH);
byte[] plaintext = JapeAlg.Decode(ciphertext, ciphertext.length,
table);
this.data = plaintext;
// Instantiate the merc's items
for( int idx = 0; idx < this.ITEM_COUNT; ++idx ) {
byte[] itemData = new byte[ITEM_LENGTH];
int itemOffset = FIRST_ITEM_OFFSET + idx * ITEM_LENGTH;
System.arraycopy(this.data, itemOffset, itemData, 0,
ITEM_LENGTH);
Item item = new Item(itemData);
this.items[idx] = item;
}
} else {
// No encryption used
this.data = new byte[rawData.length-1];
System.arraycopy(rawData, 1, this.data, 0, this.data.length);
}
}
public byte[] encode(
int[] table)
{
// Copy in the merc's items
if (this.savedGameVersion <= 99) {
for( int idx = 0; idx < this.ITEM_COUNT; ++idx ) {
Item item = this.items[idx];
byte[] itemData = item.encode();
int itemOffset = FIRST_ITEM_OFFSET + idx * ITEM_LENGTH;
System.arraycopy(itemData, 0, this.data, itemOffset,
ITEM_LENGTH);
}
}
// Make sure checksum is valid
this.recomputeChecksum();
// Copy over non-encrypted data
byte[] rawData = new byte[this.allData.length];
System.arraycopy(this.allData, 0, rawData, 0, this.allData.length);
// Start copying in decrypted data
if (this.savedGameVersion <= 99) {
byte[] plaintext = new byte[CIPHERTEXT_LENGTH];
System.arraycopy(this.data, 0, plaintext, 0, CIPHERTEXT_LENGTH);
// Encode the decoded portion
byte[] ciphertext = JapeAlg.Encode(plaintext, plaintext.length,
table);
System.arraycopy(ciphertext, 0, rawData, CIPHERTEXT_OFFSET,
CIPHERTEXT_LENGTH);
} else {
System.arraycopy(this.data, 0, rawData, 1,
this.data.length);
}
return rawData;
}
public int computeChecksum() {
if (this.savedGameVersion <= 99) {
return JapeAlg.MercChecksum(this.data);
} else {
int checksum = 1;
checksum += (this.getInt("Health") + 1);
checksum *= (this.getInt("Max Health") + 1);
checksum += (this.getInt("Agility") + 1);
checksum *= (this.getInt("Dexterity") + 1);
checksum += (this.getInt("Strength") + 1);
checksum *= (this.getInt("Marksmanship") + 1);
checksum += (this.getInt("Medical") + 1);
checksum *= (this.getInt("Mechanical") + 1);
checksum += (this.getInt("Explosives") + 1);
checksum *= (this.getInt("Level") + 1);
checksum += (this.getInt("ubProfile") + 1);
// Iterate through inventory
checksum += this.inventorySum;
return checksum;
}
}
public void validateChecksum() throws SaveGame.FormatException
{
int checksum = this.computeChecksum();
int savedChecksum = this.getInt("Checksum");
if (checksum != savedChecksum) {
throw new SaveGame.FormatException(
"Invalid checksum (0x" + Integer.toHexString(checksum) +
" vs 0x" + Integer.toHexString(savedChecksum) +
") in SOLDIERTYPE for '" +
this.get("Nickname") + "'");
}
}
public void recomputeChecksum()
{
int checksum = this.computeChecksum();
this.setInt("Checksum", checksum);
}
}
// Local Variables:
// tab-width: 8
// End: