Commit 24639c0433774cae725aceaaffbeb7962acd7084
1 parent
f8fab420
Added character skill parsing
Showing
2 changed files
with
100 additions
and
13 deletions
d2skills.c
1 | #include "d2skills.h" | 1 | #include "d2skills.h" |
2 | +#include "d2stat.h" | ||
2 | 3 | ||
3 | #include <stdio.h> | 4 | #include <stdio.h> |
4 | #include <stdlib.h> | 5 | #include <stdlib.h> |
@@ -11,36 +12,118 @@ const char* getSkillName(D2S_SKILL skillID) { | @@ -11,36 +12,118 @@ const char* getSkillName(D2S_SKILL skillID) { | ||
11 | return skillNames[skillID]; | 12 | return skillNames[skillID]; |
12 | } | 13 | } |
13 | 14 | ||
14 | -D2S_SKILL getSkillIDFromCharOffset(D2S_CHARCLASS class, unsigned int offset) { | ||
15 | - if(offset < 0 || offset > D2S_SKILL_MAXSKILLS_PER_CHAR) { | ||
16 | - fprintf(stderr,"libd2char error: Invalid character skill offset: %d\n",offset); | ||
17 | - return D2S_SKILL_UNKNOWN; | ||
18 | - } | 15 | +const D2S_SKILL* _getCharSkillTree(D2S_CHARCLASS class) { |
19 | switch(class) { | 16 | switch(class) { |
20 | case D2S_CHARCLASS_AMAZON: | 17 | case D2S_CHARCLASS_AMAZON: |
21 | - return amazonSkills[offset]; | 18 | + return amazonSkills; |
22 | break; | 19 | break; |
23 | case D2S_CHARCLASS_SORCERESS: | 20 | case D2S_CHARCLASS_SORCERESS: |
24 | - return sorceressSkills[offset]; | 21 | + return sorceressSkills; |
25 | break; | 22 | break; |
26 | case D2S_CHARCLASS_NECROMANCER: | 23 | case D2S_CHARCLASS_NECROMANCER: |
27 | - return necromancerSkills[offset]; | 24 | + return necromancerSkills; |
28 | break; | 25 | break; |
29 | case D2S_CHARCLASS_PALADIN: | 26 | case D2S_CHARCLASS_PALADIN: |
30 | - return paladinSkills[offset]; | 27 | + return paladinSkills; |
31 | break; | 28 | break; |
32 | case D2S_CHARCLASS_BARBARIAN: | 29 | case D2S_CHARCLASS_BARBARIAN: |
33 | - return barbarianSkills[offset]; | 30 | + return barbarianSkills; |
34 | break; | 31 | break; |
35 | case D2S_CHARCLASS_DRUID: | 32 | case D2S_CHARCLASS_DRUID: |
36 | - return druidSkills[offset]; | 33 | + return druidSkills; |
37 | break; | 34 | break; |
38 | case D2S_CHARCLASS_ASSASSIN: | 35 | case D2S_CHARCLASS_ASSASSIN: |
39 | - return assassinSkills[offset]; | 36 | + return assassinSkills; |
40 | break; | 37 | break; |
41 | case D2S_CHARCLASS_UNKNOWN: | 38 | case D2S_CHARCLASS_UNKNOWN: |
42 | default: | 39 | default: |
43 | - return D2S_SKILL_UNKNOWN; | 40 | + return NULL; |
44 | break; | 41 | break; |
45 | } | 42 | } |
43 | +} | ||
44 | + | ||
45 | +D2S_SKILL getSkillIDFromCharOffset(D2S_CHARCLASS class, unsigned int offset) { | ||
46 | + if(offset < 0 || offset > D2S_SKILL_MAXSKILLS_PER_CHAR) { | ||
47 | + fprintf(stderr,"libd2char error: Invalid character skill offset: %d\n",offset); | ||
48 | + return D2S_SKILL_UNKNOWN; | ||
49 | + } | ||
50 | + const D2S_SKILL* skillTree = _getCharSkillTree(class); | ||
51 | + if(!skillTree) { | ||
52 | + fprintf(stderr,"libd2char error: Invalid character class: %d\n",class); | ||
53 | + return D2S_SKILL_UNKNOWN; | ||
54 | + } | ||
55 | + return skillTree[offset]; | ||
56 | +} | ||
57 | + | ||
58 | +void* _getCharSkillsOffset(void* charData, size_t dataLen) { | ||
59 | + // Find skill header and look for previous 2 bytes | ||
60 | + // possible good candidates: | ||
61 | + // | ||
62 | + // 0b0000000111111111 | ||
63 | + // 0b000000111111111x | ||
64 | + // 0b00000111111111xx | ||
65 | + // 0b0000111111111xxx | ||
66 | + // 0b000111111111xxxx | ||
67 | + // 0b00111111111xxxxx | ||
68 | + // 0b0111111111xxxxxx | ||
69 | + // 0b111111111xxxxxxx | ||
70 | + unsigned int offset = 0; | ||
71 | + while(offset < dataLen) { | ||
72 | + void* offsetAddr = charData + (offset++); | ||
73 | + if(!memcmp(offsetAddr, D2S_SKILL_HEADER, D2S_SKILL_HEADER_LENGTH)) { | ||
74 | + uint16_t prevBytes = 0; | ||
75 | + memcpy(&prevBytes, offsetAddr, sizeof(prevBytes)); | ||
76 | + // Shift a maximum of 7 positions, and check if at some point the candidate | ||
77 | + // turns into the 9 bit D2S_STAT_FOOTER value | ||
78 | + for(int i = 0; i < 7; ++i) { | ||
79 | + if(prevBytes == D2S_STAT_FOOTER) { | ||
80 | + return offsetAddr; | ||
81 | + } | ||
82 | + prevBytes >>= 1; | ||
83 | + } | ||
84 | + // If we reach here, candidate was not viable, let's find a new candidate | ||
85 | + } | ||
86 | + } | ||
87 | + return NULL; | ||
88 | +} | ||
89 | + | ||
90 | +int getSkillLevel(D2CharHeader* c, D2S_SKILL skillID, void* charData, size_t dataLen) { | ||
91 | + const D2S_SKILL* skillTree = _getCharSkillTree(c->charClass); | ||
92 | + if(!skillTree) { | ||
93 | + fprintf(stderr,"libd2char error: Invalid character class: %d\n",c->charClass); | ||
94 | + return D2S_SKILL_UNKNOWN; | ||
95 | + } | ||
96 | + for(int i = 0; i < D2S_SKILL_MAXSKILLS_PER_CHAR; ++i) { | ||
97 | + if(skillTree[i] == skillID) { | ||
98 | + void* skillOffset = _getCharSkillsOffset(charData, dataLen); | ||
99 | + if(!skillOffset) { | ||
100 | + fprintf(stderr,"libd2char error: Unable to find skill data offset in charData\n"); | ||
101 | + return -1; | ||
102 | + } | ||
103 | + return ((uint8_t*)skillOffset)[i + D2S_SKILL_HEADER_LENGTH]; | ||
104 | + } | ||
105 | + } | ||
106 | + fprintf(stderr,"libd2char error: The specified skillID is not in your class's skill tree\n"); | ||
107 | + return -1; | ||
108 | +} | ||
109 | + | ||
110 | +int setSkillLevel(D2CharHeader* c, D2S_SKILL skillID, unsigned int level, void* charData, size_t dataLen) { | ||
111 | + const D2S_SKILL* skillTree = _getCharSkillTree(c->charClass); | ||
112 | + if(!skillTree) { | ||
113 | + fprintf(stderr,"libd2char error: Invalid character class: %d\n",c->charClass); | ||
114 | + return D2S_SKILL_UNKNOWN; | ||
115 | + } | ||
116 | + for(int i = 0; i < D2S_SKILL_MAXSKILLS_PER_CHAR; ++i) { | ||
117 | + if(skillTree[i] == skillID) { | ||
118 | + void* skillOffset = _getCharSkillsOffset(charData, dataLen); | ||
119 | + if(!skillOffset) { | ||
120 | + fprintf(stderr,"libd2char error: Unable to find skill data offset in charData\n"); | ||
121 | + return -1; | ||
122 | + } | ||
123 | + ((uint8_t*)skillOffset)[i + D2S_SKILL_HEADER_LENGTH] = (uint8_t)level; | ||
124 | + return 0; | ||
125 | + } | ||
126 | + } | ||
127 | + fprintf(stderr,"libd2char error: The specified skillID is not in your class's skill tree\n"); | ||
128 | + return -1; | ||
46 | } | 129 | } |
47 | \ No newline at end of file | 130 | \ No newline at end of file |
d2skills.h
@@ -8,6 +8,8 @@ | @@ -8,6 +8,8 @@ | ||
8 | 8 | ||
9 | #define D2S_SKILL_NUMSKILLS 357 | 9 | #define D2S_SKILL_NUMSKILLS 357 |
10 | #define D2S_SKILL_MAXSKILLS_PER_CHAR 30 | 10 | #define D2S_SKILL_MAXSKILLS_PER_CHAR 30 |
11 | +#define D2S_SKILL_HEADER "if" | ||
12 | +#define D2S_SKILL_HEADER_LENGTH 2 | ||
11 | 13 | ||
12 | typedef enum D2S_SKILL { | 14 | typedef enum D2S_SKILL { |
13 | D2S_SKILL_UNKNOWN = -1, | 15 | D2S_SKILL_UNKNOWN = -1, |
@@ -964,5 +966,7 @@ const char* const skillNames[] = { | @@ -964,5 +966,7 @@ const char* const skillNames[] = { | ||
964 | // Returns static string from library memory, no need to free | 966 | // Returns static string from library memory, no need to free |
965 | const char* getSkillName(D2S_SKILL skillID); | 967 | const char* getSkillName(D2S_SKILL skillID); |
966 | D2S_SKILL getSkillIDFromCharOffset(D2S_CHARCLASS class, unsigned int offset); | 968 | D2S_SKILL getSkillIDFromCharOffset(D2S_CHARCLASS class, unsigned int offset); |
969 | +int getSkillLevel(D2CharHeader* c, D2S_SKILL skillID, void* charData, size_t dataLen); | ||
970 | +int setSkillLevel(D2CharHeader* c, D2S_SKILL skillID, unsigned int level, void* charData, size_t dataLen); | ||
967 | 971 | ||
968 | #endif | 972 | #endif |
969 | \ No newline at end of file | 973 | \ No newline at end of file |