-
Notifications
You must be signed in to change notification settings - Fork 23
/
itemcounter.c
executable file
·294 lines (243 loc) · 8.42 KB
/
itemcounter.c
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
/****************************************************************************
* Diablo 2 1.09 Itemcounter *
****************************************************************************
* This program reads the Diablo 2 1.09 character files described in [1] *
* and counts the number of items, printing the item count for every char *
* and a total count. Counting includes *
* - low quality items *
* - high quality items *
* - normal quality items *
* - magical items *
* - set items *
* - rare items *
* - uniq items *
* - crafted items *
* - Stone of Jordan (SoJ) *
* *
* [1]: http://www.ladderhall.com/ericjwin/109/trevin/trevinfileformat.html *
****************************************************************************
* Written by Florian 'fw' Weingarten <http://hackvalue.de/> *
* This file is free software and published under the GPL *
* (GNU General Public License) *
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>
#define CHARSAVEDIR "/home/diablo/var/charsave/"
#define ITEM_LOWQUAL 1
#define ITEM_NORMALQUAL 2
#define ITEM_HIGHQUAL 3
#define ITEM_MAGIC 4
#define ITEM_SET 5
#define ITEM_RARE 6
#define ITEM_UNIQ 7
#define ITEM_CRAFTED 8
#define ITEM_UNIQ_SOJ 122
int debug=0;
struct filebuf* readfile(char*);
struct itemlist* find_itemlist(struct filebuf*);
struct stats* parse_itemlist(struct itemlist*);
void print_stats(struct stats*, char*);
void xerror(char*, char*);
void xerrorexit(char*, char*);
struct itemlist {
struct d2s_item *item;
struct itemlist *next;
};
struct filebuf {
char *buf;
int bufsize;
};
struct stats {
int total;
int lowqual;
int normalqual;
int highqual;
int magic;
int set;
int rare;
int uniq;
int crafted;
int soj;
};
struct d2s_item {
int dontcare1:32;
int dontcare2:5;
int simple:1; // Bit position 37
int dontcare3:32;
int dontcare4:6;
int c1:8; // bit position 76 (37+1+32+6)
int c2:8;
int c3:8;
int c4:8;
int dontcare5:32; // position 140
int dontcare6:10; // +10
int quality:4; // +32 = Bit position 150 (37+1+11)
int isring:1;
// from now on, the length is not fixed
// if(isring==1): 3 bits for ring picture
// ... see Trevin Beatties website
int ringpic:3;
int classspec:1;
int uniqident:12;
} __attribute__((__packed__));
int main(int argc, char *argv[])
{
struct filebuf *fb;
struct stats overallstats;
struct stats *s;
DIR *dir;
struct dirent *dirlist;
int size;
char *filename;
if(argc > 1 && strcmp(argv[1], "-d") == 0) {
debug = 1;
}
if((dir = opendir(CHARSAVEDIR)) == NULL) {
xerrorexit("Error opening directory "CHARSAVEDIR, "opendir()");
}
memset(&overallstats, 0, sizeof(struct stats));
while((dirlist = readdir(dir)) != NULL) {
if(strcmp(dirlist->d_name, ".") == 0 || strcmp(dirlist->d_name, "..") == 0) {
continue;
}
size = strlen(CHARSAVEDIR) + 1 + strlen(dirlist->d_name) + 1;
if((filename = malloc(size)) == NULL) {
xerrorexit("Memory allocation error", "main()");
}
snprintf(filename, size, "%s/%s", CHARSAVEDIR, dirlist->d_name);
fb = readfile(filename);
s = parse_itemlist(find_itemlist(fb));
print_stats(s, dirlist->d_name);
overallstats.total += s->total;
overallstats.lowqual += s->lowqual;
overallstats.normalqual += s->normalqual;
overallstats.highqual += s->highqual;
overallstats.magic += s->magic;
overallstats.set += s->set;
overallstats.rare += s->rare;
overallstats.uniq += s->uniq;
overallstats.crafted += s->crafted;
overallstats.soj += s->soj;
free(filename);
}
puts("");
print_stats(&overallstats, "Total");
puts("");
return EXIT_SUCCESS;
}
void print_stats(struct stats *s, char *prefix)
{
printf("%15s: %4d items, %3d low, %3d norm, %3d high, %4d magic, %4d set, %4d rare, %4d uniq, %4d crafted, %2d soj\n", \
prefix, s->total, s->lowqual, s->normalqual, s->highqual, s->magic, s->set, s->rare, s->uniq, s->crafted, s->soj);
}
struct stats* parse_itemlist(struct itemlist* items)
{
struct stats *ret = malloc(sizeof(struct stats));
struct itemlist *ptr = items;
// zero the struct
memset(ret, 0, sizeof(struct stats));
while(ptr != NULL && ptr->item != NULL) {
if(debug) printf("DEBUG: ptr: %p, ptr->next: %p, ptr->item: %p\n", (void*)ptr, (void*)ptr->next, (void*)ptr->item);
if(debug) printf("DEBUG: item type=\"%c%c%c%c\"\n", ptr->item->c1, ptr->item->c2, ptr->item->c3, ptr->item->c4);
ret->total++;
switch(ptr->item->quality) {
case ITEM_LOWQUAL: ret->lowqual++; break;
case ITEM_NORMALQUAL: ret->normalqual++; break;
case ITEM_HIGHQUAL: ret->highqual++; break;
case ITEM_MAGIC: ret->magic++; break;
case ITEM_SET: ret->set++; break;
case ITEM_RARE: ret->rare++; break;
case ITEM_UNIQ: ret->uniq++; break;
case ITEM_CRAFTED: ret->crafted++; break;
}
// if the item is uniq, a ring, not class specific and has the SoJ identifier...
if(ptr->item->quality == ITEM_UNIQ && ptr->item->isring && !ptr->item->classspec && ptr->item->uniqident == ITEM_UNIQ_SOJ) {
if(debug) puts("DEBUG: Stone of Jordan (SoJ) found");
ret->soj++;
}
ptr = ptr->next;
}
return ret;
}
void xerror(char *message, char *cause)
{
fprintf(stderr, "%s: %s\n", cause, message);
}
void xerrorexit(char *message, char *cause)
{
fprintf(stderr, "%s: %s\n", cause, message);
exit(EXIT_FAILURE);
}
struct itemlist* find_itemlist(struct filebuf* mybuf)
{
char *buf = mybuf->buf;
int i, gotfirst=0;
struct itemlist *ptr = malloc(sizeof(struct itemlist));
struct itemlist *retval = ptr;
if(!ptr) {
xerrorexit("Memory allocation error", "find_itemlist()");
}
ptr->next = NULL;
ptr->item = NULL;
for(i=0; i < mybuf->bufsize; i++) {
if(i+1 < mybuf->bufsize)
if(buf[i] == 'J' && buf[i+1] == 'M') {
// got first JM. Itemlist starts here
if(gotfirst == 0) {
if(debug) printf("DEBUG: Got first JM at byte offset %d, item list starts here!\n", i);
gotfirst = 1; continue;
}
// got last JM. Itemlist ends here
if(i+3 < mybuf->bufsize && buf[i+2] == 0 && buf[i+3] == 0) {
if(debug) printf("DEBUG: Got last JM at byte offset %d, item list ends here!\n", i);
break;
}
// got a JM which is not the first and not the last one
if(debug) printf("DEBUG: \tGot item JM at byte offset %d, here starts an item!\n", i);
ptr->item = (struct d2s_item*)(buf + i);
if(ptr->item->simple) {
if(debug) printf("DEBUG: \t\tItem is not extended. Skipping.\n");
ptr->item = NULL;
continue;
}
ptr->next = malloc(sizeof(struct itemlist));
if(!(ptr->next)) xerrorexit("Memory allocation error", "find_itemlist()");
ptr->next->next = NULL;
ptr = ptr->next;
}
}
return retval;
}
struct filebuf* readfile(char *filename)
{
FILE *file;
char *buf;
struct stat *filestruct = malloc(sizeof(struct stat));
struct filebuf *mybuf = malloc(sizeof(struct filebuf));
int bufsize, size=0;
if(stat(filename,filestruct) == -1) {
xerror(strerror(errno), filename);
return NULL;
}
bufsize = filestruct->st_size;
if((buf = malloc(bufsize)) == NULL) {
xerror("Memory allocation error: buf, readfile()", filename);
return NULL;
}
if((file = fopen(filename, "r")) == NULL) {
xerror(strerror(errno), filename);
return NULL;
}
while(!feof(file)) {
size = fread(buf, 1, bufsize+1, file);
}
if(debug) printf("DEBUG: read %d bytes (of total %d) from %s\n", size, bufsize, filename);
mybuf->buf = buf;
mybuf->bufsize = bufsize;
return mybuf;
}