Blame view

d2char.c 7.39 KB
1
2
#include <stdio.h>
Imanol-Mikel Barba Sabariego authored
3
#include "d2char.h"
4
#include "d2mercs.h"
5
#include "d2skills.h"
Imanol-Mikel Barba Sabariego authored
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

uint32_t calcChecksum(D2CharHeader* c, void* charData) {
    uint32_t origChecksum = c->checksum;
    c->checksum = 0;
    uint32_t sum = 0;
    void* data = malloc(c->fileSize);
    memcpy(data, (void*)c, D2S_HEADER_LENGTH);
    memcpy(data + D2S_HEADER_LENGTH, charData, c->fileSize - D2S_HEADER_LENGTH);
    for(int i = 0; i < c->fileSize; ++i) {
        sum = (sum << 1) + ((uint8_t*)data)[i];
    }
    free(data);
    c->checksum = origChecksum;
    return sum;
}

int checkChecksum(D2CharHeader* c, void* charData) {
    uint32_t checksum = calcChecksum(c, charData);
    return c->checksum == checksum;
}

int isHardcore(D2CharHeader* c) {
    return c->charStatus & D2S_CHARSTATUS_HARDCORE;
}
31
32
33
34
35
36
void setHardcore(D2CharHeader* c, int bool) {
    if(bool) {
        c->charProgress |= D2S_CHARSTATUS_HARDCORE;
    } else {
        c->charProgress &= ~D2S_CHARSTATUS_HARDCORE;
    }
37
38
}
Imanol-Mikel Barba Sabariego authored
39
40
41
42
int hasDied(D2CharHeader* c) {
    return c->charStatus & D2S_CHARSTATUS_DIED;
}
43
44
45
46
47
48
void setDied(D2CharHeader* c, int bool) {
    if(bool) {
        c->charProgress |= D2S_CHARSTATUS_DIED;
    } else {
        c->charProgress &= ~D2S_CHARSTATUS_DIED;
    }
49
50
}
Imanol-Mikel Barba Sabariego authored
51
52
53
54
int isExpansion(D2CharHeader* c) {
    return c->charStatus & D2S_CHARSTATUS_EXPANSION;
}
55
56
57
58
59
60
void setExpansion(D2CharHeader* c, int bool) {
    if(bool) {
        c->charProgress |= D2S_CHARSTATUS_EXPANSION;
    } else {
        c->charProgress &= ~D2S_CHARSTATUS_EXPANSION;
    }
61
62
}
Imanol-Mikel Barba Sabariego authored
63
64
int isLadder(D2CharHeader* c) {
    return c->charStatus & D2S_CHARSTATUS_LADDER;
Imanol-Mikel Barba Sabariego authored
65
66
}
67
68
69
void setLadder(D2CharHeader* c, int bool) {
    if(bool) {
        c->charProgress |= D2S_CHARSTATUS_LADDER;
Imanol-Mikel Barba Sabariego authored
70
    } else {
71
        c->charProgress &= ~D2S_CHARSTATUS_LADDER;
Imanol-Mikel Barba Sabariego authored
72
73
74
75
76
77
78
79
80
81
    }
}

int isFemale(D2CharHeader* c) {
    return (c->charClass == D2S_CHARCLASS_AMAZON || 
            c->charClass == D2S_CHARCLASS_ASSASSIN ||
            c->charClass == D2S_CHARCLASS_SORCERESS);
}

const char* getCharacterTitle(D2CharHeader* c) {
82
83
84
    D2S_DIFFICULTY hardestCleared;
    D2S_ACT act; // We don't care, but need to provide it
    getProgress(c,&act,&hardestCleared);
Imanol-Mikel Barba Sabariego authored
85
86
87
88
    if(isExpansion(c)) {
        // Expansion
        if(isHardcore(c)) {
            // Expansion Hardcore
89
90
            switch(hardestCleared) {
                case D2S_DIFFICULTY_NORMAL:
Imanol-Mikel Barba Sabariego authored
91
92
                return D2S_CHARPROGRESS_EXPANSION_TIER1_NAME_HARDCORE;
                break;
93
                case D2S_DIFFICULTY_NIGHTMARE:
Imanol-Mikel Barba Sabariego authored
94
95
                return D2S_CHARPROGRESS_EXPANSION_TIER2_NAME_HARDCORE;
                break;
96
                case D2S_DIFFICULTY_HELL:
Imanol-Mikel Barba Sabariego authored
97
98
99
100
101
                return D2S_CHARPROGRESS_EXPANSION_TIER3_NAME_HARDCORE;
                break;
            }
        } else {
            // Expansion Softcore
102
103
            switch(hardestCleared) {
                case D2S_DIFFICULTY_NORMAL:
Imanol-Mikel Barba Sabariego authored
104
105
                return D2S_CHARPROGRESS_EXPANSION_TIER1_NAME;
                break;
106
                case D2S_DIFFICULTY_NIGHTMARE:
Imanol-Mikel Barba Sabariego authored
107
108
                return D2S_CHARPROGRESS_EXPANSION_TIER2_NAME;
                break;
109
                case D2S_DIFFICULTY_HELL:
Imanol-Mikel Barba Sabariego authored
110
111
112
113
114
115
116
117
                return isFemale(c) ? D2S_CHARPROGRESS_EXPANSION_TIER3_NAME_F : D2S_CHARPROGRESS_EXPANSION_TIER3_NAME_M;
                break;
            }
        }
    } else {
        // Classic
        if(isHardcore(c)) {
            // Classic Hardcore
118
119
            switch(hardestCleared) {
                case D2S_DIFFICULTY_NORMAL:
Imanol-Mikel Barba Sabariego authored
120
121
                return isFemale(c) ? D2S_CHARPROGRESS_CLASSIC_TIER1_NAME_HARDCORE_F : D2S_CHARPROGRESS_CLASSIC_TIER1_NAME_HARDCORE_M;
                break;
122
                case D2S_DIFFICULTY_NIGHTMARE:
Imanol-Mikel Barba Sabariego authored
123
124
                return isFemale(c) ? D2S_CHARPROGRESS_CLASSIC_TIER2_NAME_HARDCORE_F : D2S_CHARPROGRESS_CLASSIC_TIER2_NAME_HARDCORE_M;
                break;
125
                case D2S_DIFFICULTY_HELL:
Imanol-Mikel Barba Sabariego authored
126
127
128
129
130
                return isFemale(c) ? D2S_CHARPROGRESS_CLASSIC_TIER3_NAME_HARDCORE_F : D2S_CHARPROGRESS_CLASSIC_TIER3_NAME_HARDCORE_M;
                break;
            }
        } else {
            // Classic Softcore
131
132
            switch(hardestCleared) {
                case D2S_DIFFICULTY_NORMAL:
Imanol-Mikel Barba Sabariego authored
133
134
                return isFemale(c) ? D2S_CHARPROGRESS_CLASSIC_TIER1_NAME_F : D2S_CHARPROGRESS_CLASSIC_TIER1_NAME_M;
                break;
135
                case D2S_DIFFICULTY_NIGHTMARE:
Imanol-Mikel Barba Sabariego authored
136
137
                return isFemale(c) ? D2S_CHARPROGRESS_CLASSIC_TIER2_NAME_F : D2S_CHARPROGRESS_CLASSIC_TIER2_NAME_M;
                break;
138
                case D2S_DIFFICULTY_HELL:
Imanol-Mikel Barba Sabariego authored
139
140
141
142
143
                return isFemale(c) ? D2S_CHARPROGRESS_CLASSIC_TIER3_NAME_F : D2S_CHARPROGRESS_CLASSIC_TIER3_NAME_M;
                break;
            }
        }
    }
144
    return D2S_CHARPROGRESS_TIER0_NAME;
145
146
147
}

size_t getLastPlayed(D2CharHeader* c, char* buf, size_t bufLen) {
148
149
150
151
152
153
154
155
    // In amd64, since long is 64 bits long, time_t is also 64 bits long
    // thus needing conversion from the save file type, which is a uint32_t
    #ifdef __x86_64__
    uint64_t convTimestamp = c->lastPlayed;
    #else
    uint32_t convTimestamp = c->lastPlayed;
    #endif
    struct tm* time = localtime((time_t*)&convTimestamp);
156
157
158
159
160
161
162
    size_t ret = strftime(buf, bufLen, "%c", time);
    if(!ret) {
        fprintf(stderr,"libd2char error: Provided buffer for time string was too small\n");
    }
    return ret;
}
163
D2S_DIFFICULTY getCurrentDifficulty(D2CharHeader* c) {
164
165
166
167
168
169
170
171
    for(int i = D2S_DIFFICULTY_NORMAL; i <= D2S_DIFFICULTY_HELL; ++i) {
        if(c->difficulty[i] & D2S_DIFFICULTY_ACTIVE) {
            return i;
        }
    }
    return D2S_DIFFICULTY_UNKNOWN;
}
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
D2S_ACT getCurrentAct(D2CharHeader* c) {
   for(int i = D2S_DIFFICULTY_NORMAL; i <= D2S_DIFFICULTY_HELL; ++i) {
        if(c->difficulty[i] & D2S_DIFFICULTY_ACTIVE) {
            return (D2S_ACT)(c->difficulty[i] & 0x07);
        }
    }
    return D2S_ACT_UNKNOWN;
}

void getProgress(D2CharHeader* c, D2S_ACT* act, D2S_DIFFICULTY* difficulty) {
    if(isExpansion(c)) {
        *difficulty = c->charProgress / 5;
        *act = c->charProgress % 5;
        // Unfortunately, this won't distinguish between act IV and act V, because
        // the game does not increment the charProgress when you kill Diablo, so
        // the only way to know is check whether or not you started the act on the
        // quest data
        // NB: This doesn't happen in Classic
        if(*act == D2S_ACT4) {
            if(c->questData.quests[*difficulty].expansionAct.actStarted) {
                ++*act;
            }
        }
    } else {
        *difficulty = c->charProgress / 4;
        *act = c->charProgress % 4;
    }
}

int setProgress(D2CharHeader* c, D2S_ACT act, D2S_DIFFICULTY difficulty) {
    if(difficulty != D2S_DIFFICULTY_NORMAL ||
       difficulty != D2S_DIFFICULTY_NIGHTMARE ||
       difficulty != D2S_DIFFICULTY_HELL) {
        fprintf(stderr,"libd2char error: Invalid difficulty specified: %d\n", difficulty);
        return -1;
    }
    if(act != D2S_ACT1 ||
       act != D2S_ACT2 ||
       act != D2S_ACT3 ||
       act != D2S_ACT4 ||
       act != D2S_ACT5) {
        fprintf(stderr,"libd2char error: Invalid act specified: %d\n", act);
        return -1;
    }
    uint8_t progress = 0;
    if(isExpansion(c)) {
        progress += difficulty * 5;
        progress += act;
        if(act == D2S_ACT5) {
            // See comment above
            --progress;
            c->questData.quests[difficulty].expansionAct.actStarted = 1;
        }
    } else {
        progress = (difficulty * 4) + act;
    }
    c->charProgress = progress;
229
    return 0;
Imanol-Mikel Barba Sabariego authored
230
}