Commit 67c0083bbda0760634bfc0e0a0a00f74d42347a1
1 parent
6d88e3be
Missing some changes on previous commits
Showing
8 changed files
with
87 additions
and
5 deletions
d2char.c
... | ... | @@ -136,6 +136,10 @@ size_t getLastPlayed(D2CharHeader* c, char* buf, size_t bufLen) { |
136 | 136 | } |
137 | 137 | |
138 | 138 | const char* getSkillName(int skillID) { |
139 | + if(skillID > D2S_SKILL_NUMSKILLS) { | |
140 | + fprintf(stderr,"libd2char error: skillID %d doesn't exist\n",skillID); | |
141 | + return NULL; | |
142 | + } | |
139 | 143 | return skills[skillID]; |
140 | 144 | } |
141 | 145 | ... | ... |
d2char.h
... | ... | @@ -17,7 +17,7 @@ |
17 | 17 | #define D2S_HOTKEYS_LENGTH 64 |
18 | 18 | #define D2S_CHAR_APPEARANCE_LENGTH 32 |
19 | 19 | #define D2S_DIFFICULTY_LENGTH 3 |
20 | -#define D2S_WAYPOINTS_LENGTH 81 | |
20 | +#define D2S_WAYPOINTS_LENGTH 80 | |
21 | 21 | #define D2S_NPCDATA_LENGTH 51 |
22 | 22 | |
23 | 23 | #define D2S_CHARSTATUS_HARDCORE 0x04 |
... | ... | @@ -92,11 +92,18 @@ typedef struct __attribute__((packed)){ |
92 | 92 | uint8_t unknown6[144]; // TODO |
93 | 93 | D2QuestData questData; |
94 | 94 | uint8_t waypointData[D2S_WAYPOINTS_LENGTH]; |
95 | + uint8_t unknown7; // TODO. Apparently this is always 0x01 | |
95 | 96 | uint8_t NPCIntroductions[D2S_NPCDATA_LENGTH]; // TODO: Not implemented |
96 | 97 | } D2CharHeader; |
97 | 98 | |
98 | 99 | // TODO: All setX functions |
99 | 100 | |
101 | +// TODO: Load from file. | |
102 | +// int loadD2CharFromFile(const char* file, D2CharHeader** header, void** data); | |
103 | + | |
104 | +// TODO: Write to file. | |
105 | +// int writeD2CharToFile(const char* file, D2CharHeader* header, void* charData,) | |
106 | + | |
100 | 107 | uint32_t calcChecksum(D2CharHeader* c, void* charData); |
101 | 108 | int checkChecksum(D2CharHeader* c, void* charData); |
102 | 109 | int isHardcore(D2CharHeader* c); | ... | ... |
d2mercs.c
1 | 1 | #include "d2mercs.h" |
2 | 2 | |
3 | +#include <stdlib.h> | |
4 | + | |
3 | 5 | int _getMercType(int mercID) { |
4 | 6 | if(mercID >= 0 && mercID <= 5) { |
5 | 7 | return D2S_MERCTYPE_ROGUE; |
... | ... | @@ -16,5 +18,8 @@ int _getMercType(int mercID) { |
16 | 18 | |
17 | 19 | const char* _getMercName(int mercID, int mercNameID) { |
18 | 20 | int offset = _getMercType(mercID); |
21 | + if(offset == D2S_MERCTYPE_UNKNOWN) { | |
22 | + return NULL; | |
23 | + } | |
19 | 24 | return mercNames[mercNameID + offset]; |
20 | 25 | } |
21 | 26 | \ No newline at end of file | ... | ... |
d2mercs.h
d2quest.c
1 | 1 | #include "d2char.h" |
2 | 2 | #include "d2quest.h" |
3 | 3 | |
4 | +#include <stdio.h> | |
5 | + | |
4 | 6 | void getCheckpointDescriptions(unsigned int quest, const char* *descriptions[16]) { |
7 | + if(quest > D2S_QUESTDATA_NUMQUESTS) { | |
8 | + fprintf(stderr,"libd2char error: quest %d doesn't exist\n",quest); | |
9 | + return; | |
10 | + } | |
5 | 11 | memcpy(descriptions,(&checkpointDescriptions) + (quest * 16 * sizeof(const char*)), 16 * sizeof(const char*)); |
6 | 12 | } |
7 | 13 | |
... | ... | @@ -17,7 +23,8 @@ uint16_t getQuestStatus(D2QuestData* d, unsigned int quest, unsigned int difficu |
17 | 23 | } else if(quest >= D2S_QUEST_SIEGE_ON_HARROGATH && quest <= D2S_QUEST_EVE_OF_DESTRUCTION) { |
18 | 24 | return d->quests[difficulty].expansionAct.questCheckpoints[quest - D2S_QUEST_SIEGE_ON_HARROGATH]; |
19 | 25 | } |
20 | - return D2S_QUEST_UNKNOWN; | |
26 | + fprintf(stderr,"libd2char error: quest %d doesn't exist\n",quest); | |
27 | + return 0; | |
21 | 28 | } |
22 | 29 | |
23 | 30 | void setQuestStatus(D2QuestData* d, unsigned int quest, unsigned int difficulty, uint16_t questData) { |
... | ... | @@ -31,6 +38,8 @@ void setQuestStatus(D2QuestData* d, unsigned int quest, unsigned int difficulty, |
31 | 38 | d->quests[difficulty].actData[D2S_ACT4].questCheckpoints[quest - D2S_QUEST_FALLEN_ANGEL] = questData; |
32 | 39 | } else if(quest >= D2S_QUEST_SIEGE_ON_HARROGATH && quest <= D2S_QUEST_EVE_OF_DESTRUCTION) { |
33 | 40 | d->quests[difficulty].expansionAct.questCheckpoints[quest - D2S_QUEST_SIEGE_ON_HARROGATH] = questData; |
41 | + } else { | |
42 | + fprintf(stderr,"libd2char error: quest %d doesn't exist\n",quest); | |
34 | 43 | } |
35 | 44 | } |
36 | 45 | |
... | ... | @@ -98,6 +107,11 @@ int getSpecialQuestStatus(D2QuestData* d, unsigned int specialQuestState, unsign |
98 | 107 | } |
99 | 108 | |
100 | 109 | void setSpecialQuestStatus(D2QuestData* d, unsigned int specialQuestState, unsigned int difficulty, int bool) { |
110 | + if(difficulty != D2S_DIFFICULTY_NORMAL || | |
111 | + difficulty != D2S_DIFFICULTY_NIGHTMARE || | |
112 | + difficulty != D2S_DIFFICULTY_HELL) { | |
113 | + fprintf(stderr,"libd2char error: difficulty %d doesn't exist\n",difficulty); | |
114 | + } | |
101 | 115 | switch(specialQuestState) { |
102 | 116 | case D2S_SPECIALQUEST_AKARA_RESPEC: |
103 | 117 | // This operation only makes sense if the quest is actually completed, otherwise ignore request | ... | ... |
d2quest.h
... | ... | @@ -6,6 +6,7 @@ |
6 | 6 | #include "d2strings.h" |
7 | 7 | |
8 | 8 | #define D2S_QUESTDATA_HEADER_LENGTH 4 |
9 | +#define D2S_QUESTDATA_NUMQUESTS 27 | |
9 | 10 | |
10 | 11 | enum D2S_QUEST { |
11 | 12 | D2S_QUEST_UNKNOWN = -1, |
... | ... | @@ -500,17 +501,22 @@ typedef struct __attribute__((packed)) { |
500 | 501 | typedef struct __attribute__((packed)) { |
501 | 502 | D2ActData actData[4]; |
502 | 503 | D2XActData expansionAct; |
504 | + // My guess is that the following `uint16_t`s all represents some quest data that was added after | |
505 | + // the game was launched. This also explains why there's version data (supposedly) and length | |
506 | + // of the quest data in bytes in the header. | |
503 | 507 | uint16_t akaraRespecData; // This uint16_t determines if the player has used Akara's respec or not |
504 | 508 | uint16_t unknown1[6]; |
505 | 509 | } D2Quests; |
506 | 510 | |
507 | 511 | typedef struct __attribute__((packed)) { |
508 | - const char* header[D2S_QUESTDATA_HEADER_LENGTH]; | |
509 | - uint32_t unknown1; | |
512 | + const char* header[D2S_QUESTDATA_HEADER_LENGTH]; // Woo! | |
513 | + uint32_t unknown1; // This is likely version data | |
510 | 514 | uint16_t size; // in bytes |
511 | 515 | D2Quests quests[3]; // 1 set for each difficulty |
512 | 516 | } D2QuestData; |
513 | 517 | |
518 | +// TODO: These functions are completely unsafe, and don't return success | |
519 | + | |
514 | 520 | // Populates `descriptions` with static strings from library memory, no need to free. |
515 | 521 | // `descriptions` contains one string for each bit field of the quest's uint16_t data. |
516 | 522 | // Empty entries (non-existant or unknown checkpoints) will have NULL value. Be careful! |
... | ... | @@ -527,9 +533,10 @@ int isQuestCompleted(D2QuestData* d, unsigned int quest, unsigned int difficulty |
527 | 533 | // Set bool to 1 to set the status or 0 to remove it |
528 | 534 | void setQuestStarted(D2QuestData* d, unsigned int quest, unsigned int difficulty, int bool); |
529 | 535 | void setQuestRewardCollected(D2QuestData* d, unsigned int quest, unsigned int difficulty, int bool); |
536 | + | |
530 | 537 | // When called to set the request to NOT completed (`bool` = 0), this will NOT set the reward collected status |
531 | 538 | // which *may* render the quest unobtainable, this is done in case we may want to further modify the quest state. |
532 | -// If you want to mark the quest as not completed just to get the reward, please use `setQuestRewardCollected()` | |
539 | +// If you want to mark the quest as not completed just to get the reward, please use `setQuestRewardCollected(,,0)` | |
533 | 540 | // instead. |
534 | 541 | void setQuestCompleted(D2QuestData* d, unsigned int quest, unsigned int difficulty, int bool); |
535 | 542 | ... | ... |
d2skills.h
d2strings.h
... | ... | @@ -1035,4 +1035,45 @@ const char* D2S_CHARPROGRESS_EXPANSION_TIER3_NAME_HARDCORE = "Guardian"; |
1035 | 1035 | #define D2S_QUEST_CHECKPOINT_430 NULL |
1036 | 1036 | #define D2S_QUEST_CHECKPOINT_431 NULL |
1037 | 1037 | |
1038 | +// Waypoints | |
1039 | +#define D2S_WAYPOINT_0 "Rogue Encampment" | |
1040 | +#define D2S_WAYPOINT_1 "Cold Plains" | |
1041 | +#define D2S_WAYPOINT_2 "Stony Field" | |
1042 | +#define D2S_WAYPOINT_3 "Dark Wood" | |
1043 | +#define D2S_WAYPOINT_4 "Black Marsh" | |
1044 | +#define D2S_WAYPOINT_5 "Outer Cloister" | |
1045 | +#define D2S_WAYPOINT_6 "Jail Level 1" | |
1046 | +#define D2S_WAYPOINT_7 "Inner Cloister" | |
1047 | +#define D2S_WAYPOINT_8 "Catacombs Level 2" | |
1048 | +#define D2S_WAYPOINT_9 "Lut Gholein" | |
1049 | +#define D2S_WAYPOINT_10 "Sewers Level 2" | |
1050 | +#define D2S_WAYPOINT_11 "Dry Hills" | |
1051 | +#define D2S_WAYPOINT_12 "Halls of the Dead Level 2" | |
1052 | +#define D2S_WAYPOINT_13 "Far Oasis" | |
1053 | +#define D2S_WAYPOINT_14 "Lost City" | |
1054 | +#define D2S_WAYPOINT_15 "Palace Cellar Level 1" | |
1055 | +#define D2S_WAYPOINT_16 "Arcane Sanctuary" | |
1056 | +#define D2S_WAYPOINT_17 "Canyon of the Magi" | |
1057 | +#define D2S_WAYPOINT_18 "Kurast Docks" | |
1058 | +#define D2S_WAYPOINT_19 "Spider Forest" | |
1059 | +#define D2S_WAYPOINT_20 "Great Marsh" | |
1060 | +#define D2S_WAYPOINT_21 "Flayer Jungle" | |
1061 | +#define D2S_WAYPOINT_22 "Lower Kurast" | |
1062 | +#define D2S_WAYPOINT_23 "Kurast Bazaar" | |
1063 | +#define D2S_WAYPOINT_24 "Upper Kurast" | |
1064 | +#define D2S_WAYPOINT_25 "Travincal" | |
1065 | +#define D2S_WAYPOINT_26 "Durance of Hate Level 2" | |
1066 | +#define D2S_WAYPOINT_27 "Pandemonium Fortress" | |
1067 | +#define D2S_WAYPOINT_28 "City of the Damned" | |
1068 | +#define D2S_WAYPOINT_29 "River of Flames" | |
1069 | +#define D2S_WAYPOINT_30 "Harrogath" | |
1070 | +#define D2S_WAYPOINT_31 "Frigid Highlands" | |
1071 | +#define D2S_WAYPOINT_32 "Arreat Plateau" | |
1072 | +#define D2S_WAYPOINT_33 "Crystalline Passage" | |
1073 | +#define D2S_WAYPOINT_34 "Halls of Pain" | |
1074 | +#define D2S_WAYPOINT_35 "Glacial Trail" | |
1075 | +#define D2S_WAYPOINT_36 "Frozen Tundra" | |
1076 | +#define D2S_WAYPOINT_37 "The Ancient's Way" | |
1077 | +#define D2S_WAYPOINT_38 "Worldstone Keep Level 2" | |
1078 | + | |
1038 | 1079 | #endif |
1039 | 1080 | \ No newline at end of file | ... | ... |