├── .gitignore ├── README.md └── tools └── esCharView └── CharacterEditor ├── BitStream.cs ├── BitStream.resx ├── Character.cs ├── CharacterEditor.csproj ├── Inventory.cs ├── Item.cs ├── ItemDefs.cs ├── ItemStatCost.cs ├── Properties └── AssemblyInfo.cs ├── Resources.cs ├── Resources ├── 1.13c │ ├── AllItems.txt │ ├── ItemGroups.txt │ ├── ItemStatCost.txt │ └── Sets.txt ├── AllItems.txt ├── ItemGroups.txt ├── ItemStatCost.txt ├── RoT_1.A3.1 │ ├── AllItems.txt │ ├── ItemGroups.txt │ ├── ItemStatCost.txt │ └── Sets.txt ├── Sets.txt └── es300_R6D │ ├── AllItems.txt │ ├── ItemGroups.txt │ ├── ItemStatCost.txt │ └── Sets.txt ├── SaveReader.cs ├── Skill.cs ├── Stat.cs └── Utils.cs /.gitignore: -------------------------------------------------------------------------------- 1 | Debug 2 | Release 3 | Bin 4 | Obj 5 | Output 6 | *.user 7 | *.sdf 8 | *.suo 9 | *.ipch 10 | *.opensdf 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | I pay attention to a project `tools/esCharView/CharacterEditor` in the repository forked from [nooperation/d2es-stuff](https://github.com/nooperation/d2es-stuff). 2 | 3 | **CharacterEditor** is .NET library for editing Diablo 2 character files (`.d2s`). 4 | 5 | Several changes from me: 6 | * fix a bug in inventory 7 | * add feature to get bytes of `d2s` to save data to a file 8 | * feature to add new items to a character 9 | * add resources for 1.13c to make it possible edit original d2s files. 10 | 11 | This library is used in online service that can edit characters through REST API, there is PHP interface to consume it https://github.com/pvpgn/d2smanager-api 12 | 13 | 14 | 15 | 16 | ## How to use CharacterEditor.dll 17 | 18 | Put `Resources` directory in the root with your application, then you can pass different "mods" (a directory name) to `SaveReader` constructor. For 1.13c you should use `new SaveReader("1.13c")`. 19 | 20 | It is possible to set a directory with Resources in code: 21 | ```c# 22 | CharacterEditor.Resources.CurrentDirectory = @"C:\MyProject\App_Data\"; 23 | ``` 24 | 25 | Read character into object 26 | ```c# 27 | var reader = new SaveReader("1.13c") 28 | 29 | var charBytes = File.ReadAllBytes("harpywar.d2s"); 30 | var d2s = reader.Read(charBytes); 31 | ``` 32 | 33 | 34 | Split all character items into different files 35 | ```c# 36 | var items = new List(); 37 | items.AddRange(d2s.Inventory.PlayerItems); 38 | items.AddRange(d2s.Inventory.CorpseItems); 39 | items.AddRange(d2s.Inventory.GolemItems); 40 | items.AddRange(d2s.Inventory.MercItems); 41 | 42 | for (var i = 0; i < items.Length; i++) 43 | { 44 | File.WriteAllBytes(i + ".d2i", items[i].GetItemBytes()); 45 | } 46 | ``` 47 | 48 | Add new item from `.d2i` file to a character's inventory 49 | ```c# 50 | var itemBytes = File.ReadAllBytes("item.d2i"); 51 | var item = new Item(itemBytes, true) 52 | 53 | // add to character inventory 54 | d2s.Inventory.PlayerItems.Add( FixItem(item) ); 55 | 56 | // set default item position in inventory in left-top corner 57 | public static Item FixItem(Item item) 58 | { 59 | var newItem = new Item(item.GetItemBytes(), true); 60 | 61 | newItem.Id = 0; 62 | newItem.Location = (uint)Item.ItemLocation.Stored; 63 | newItem.PositionOnBody = (uint)Item.EquipmentLocation.None; 64 | newItem.PositionX = 0; 65 | newItem.PositionY = 0; 66 | newItem.StorageId = (uint) Item.StorageType.Inventory; 67 | 68 | return newItem; 69 | } 70 | ``` 71 | 72 | Remove item from a character 73 | ```c# 74 | // item you want to remove 75 | Item findItem; 76 | 77 | // find item on body 78 | if ( findInItems(d2s.Inventory.PlayerItems, findItem) ) 79 | { 80 | d2s.Inventory.PlayerItems.Remove(item); 81 | } 82 | // find in merc 83 | if ( findInItems(d2s.Inventory.MercItems, findItem) ) 84 | { 85 | d2s.Inventory.MercItems.Remove(item); 86 | } 87 | // find in golem 88 | if ( findInItems(d2s.Inventory.GolemItems, findItem) ) 89 | { 90 | d2s.Inventory.GolemItems.Remove(item); 91 | } 92 | // find in corpse 93 | if ( findInItems(d2s.Inventory.CorpseItems, findItem) ) 94 | { 95 | d2s.Inventory.CorpseItems.Remove(item); 96 | } 97 | 98 | // find an item in items list 99 | private findInItems(List items, Item findItem) 100 | { 101 | foreach (var item in items) 102 | { 103 | if (ByteArrayCompare(item.GetItemBytes(), findItem.GetItemBytes())) 104 | { 105 | return true; 106 | } 107 | } 108 | return false; 109 | } 110 | // just compare two byte arrays 111 | static bool ByteArrayCompare(byte[] a1, byte[] a2) 112 | { 113 | return StructuralComparisons.StructuralEqualityComparer.Equals(a1, a2); 114 | } 115 | ``` 116 | 117 | Save modified character 118 | ```c# 119 | File.WriteAllBytes("newchar.d2s", d2s.GetBytes()); 120 | ``` 121 | 122 | 123 | 124 | Use IClonable interface to work with many characters without preloading resources each time: 125 | ```c# 126 | 127 | foreach (var file in Directory.GetFiles(@"C:\pvpgn\var\charsave")) 128 | { 129 | ReadCharacter(file); 130 | } 131 | 132 | private static void ReadCharacter(string fileName) 133 | { 134 | var charBytes = File.ReadAllBytes(fileName); 135 | var d2s = CharManager.Read(charBytes); 136 | 137 | // other manipulates with "d2s" object 138 | ... 139 | } 140 | 141 | public static class CharManager 142 | { 143 | private static SaveReader _reader; 144 | public static SaveReader Reader 145 | { 146 | get 147 | { 148 | // lazy loading resources 149 | if (_reader == null) 150 | { 151 | // set working directory 152 | CharacterEditor.Resources.CurrentDirectory = Directory.GetCurrentDirectory(); 153 | // load "ItemDefs" from Resources 154 | _reader = new SaveReader("1.13c"); 155 | } 156 | return (SaveReader)_reader.Clone(); // create new instance 157 | } 158 | } 159 | 160 | public static SaveReader Read(byte[] charBytes) 161 | { 162 | return Reader.Read(charBytes); 163 | } 164 | } 165 | ``` 166 | 167 | -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/BitStream.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarpyWar/d2s-character-editor/6304f41ca3b5973052ea86ace8f06d67824471b9/tools/esCharView/CharacterEditor/BitStream.cs -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/BitStream.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 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 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | BitStream lengths must be the same. 122 | 123 | 124 | count + bitIndex exceeds the length of the 8-bit value. 125 | 126 | 127 | count + bitIndex exceeds the length of the 16-bit value. 128 | 129 | 130 | count + bitIndex exceeds the length of the 32-bit value. 131 | 132 | 133 | count + bitIndex exceeds the length of the 64-bit value. 134 | 135 | 136 | count + offset were out of bounds for the BitStream or count is greater than the number of elements from bitIndex to the end of the BitStream. 137 | 138 | 139 | count + offset were out of bounds for the array or count is greater than the number of elements from offset to the end of the array. 140 | 141 | 142 | The bit buffer cannot be null. 143 | 144 | 145 | The BitStream cannot be null. 146 | 147 | 148 | The Stream cannot be null. 149 | 150 | 151 | Capacity must be greater than zero. 152 | 153 | 154 | Parameter must be greater than or equal to zero. 155 | 156 | 157 | Position must be greater than or equal to zero. 158 | 159 | 160 | Position must be less than or equal to the Bit Stream Length. 161 | 162 | 163 | The internal bit buffer's bit index cannot be greater than 32. 164 | 165 | 166 | Asynchronous operations are not supported by the BitStream class. 167 | 168 | 169 | Cannot Flush a BitStream. 170 | 171 | 172 | Cannot Seek on a BitStream. Use Position property instead. 173 | 174 | 175 | Cannot SetLength of a BitStream. 176 | 177 | 178 | The inherited ReadByte method is not supported by the BitStream class. Use one of the overloaded Read methods instead. 179 | 180 | 181 | The inherited WriteByte method is not supported by the BitStream class. Use one of the overloaded Write methods instead. 182 | 183 | 184 | Cannot access a closed BitStream. 185 | 186 | 187 | count + bitIndex exceeds the length of the 128-bit value. 188 | 189 | 190 | count + bitIndex exceeds the length of the 16-bit value. 191 | 192 | 193 | The MemoryStream cannot be null. 194 | 195 | 196 | The FileStream cannot be null. 197 | 198 | 199 | The BufferedStream cannot be null. 200 | 201 | 202 | The NetworkStream cannot be null. 203 | 204 | 205 | The CryptoStream cannot be null. 206 | 207 | -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/Character.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.IO; 5 | using System.Collections; 6 | using System.ComponentModel; 7 | 8 | namespace CharacterEditor 9 | { 10 | public class Character : INotifyPropertyChanged 11 | { 12 | private byte[] characterBytes; 13 | private byte characterFlags; 14 | 15 | /// 16 | /// Character classes 17 | /// 18 | public enum CharacterClass 19 | { 20 | Amazon, 21 | Sorceress, 22 | Necromancer, 23 | Paladin, 24 | Barbarian, 25 | Druid, 26 | Assassin, 27 | } 28 | 29 | /// 30 | /// Character's name 31 | /// 32 | public String Name 33 | { 34 | get 35 | { 36 | string name = UnicodeEncoding.UTF8.GetString(characterBytes, 0x14, 16); 37 | return name.Substring(0, name.IndexOf('\0')); 38 | } 39 | set 40 | { 41 | byte[] paddedName = new byte[16]; 42 | byte[] newName = UnicodeEncoding.UTF8.GetBytes(value); 43 | 44 | //15 instead of 16 to keep trailing null character 45 | Array.Copy(newName, paddedName, newName.Length < 15 ? newName.Length : 15); 46 | Array.Copy(paddedName, 0, characterBytes, 0x14, paddedName.Length); 47 | OnPropertyChange("Name"); 48 | } 49 | } 50 | 51 | /// 52 | /// Character is in hardcore mode 53 | /// 54 | public bool Hardcore 55 | { 56 | get 57 | { 58 | return (characterFlags & 0x04) > 0; 59 | } 60 | set 61 | { 62 | if (value) 63 | { 64 | characterFlags |= 0x04; 65 | } 66 | else 67 | { 68 | characterFlags &= 0xfb; 69 | } 70 | OnPropertyChange("Hardcore"); 71 | } 72 | } 73 | 74 | /// 75 | /// Character has died before 76 | /// 77 | public bool Died 78 | { 79 | get 80 | { 81 | return (characterFlags & 0x08) > 0; 82 | } 83 | set 84 | { 85 | if (value) 86 | { 87 | characterFlags |= 0x08; 88 | } 89 | else 90 | { 91 | characterFlags &= 0xf7; 92 | } 93 | OnPropertyChange("Died"); 94 | } 95 | } 96 | 97 | /// 98 | /// Character is expansion character 99 | /// 100 | public bool Expansion 101 | { 102 | get 103 | { 104 | return (characterFlags & 0x20) > 0; 105 | } 106 | set 107 | { 108 | if (value) 109 | { 110 | characterFlags |= 0x20; 111 | } 112 | else 113 | { 114 | characterFlags &= 0xDF; 115 | } 116 | OnPropertyChange("Expansion"); 117 | } 118 | } 119 | 120 | /// 121 | /// Collection of unknown flags 122 | /// 123 | public byte UnknownFlags 124 | { 125 | get 126 | { 127 | return (byte)(characterFlags & (byte)0xd3); 128 | } 129 | set 130 | { 131 | characterFlags &= 0x2C; 132 | characterFlags |= value; 133 | OnPropertyChange("UnknownFlags"); 134 | } 135 | } 136 | 137 | /// 138 | /// Character's title / progression 139 | /// 140 | public byte Progression 141 | { 142 | get { return characterBytes[37]; } 143 | set 144 | { 145 | characterBytes[37] = value; 146 | OnPropertyChange("Progression"); 147 | } 148 | } 149 | 150 | /// 151 | /// Character's class 152 | /// 153 | public CharacterClass Class 154 | { 155 | get { return (CharacterClass)characterBytes[40]; } 156 | set 157 | { 158 | characterBytes[40] = (byte)value; 159 | OnPropertyChange("Class"); 160 | } 161 | } 162 | 163 | /// 164 | /// Level displayed on character list ? 165 | /// 166 | public byte LevelDisplay 167 | { 168 | get { return characterBytes[43]; } 169 | set 170 | { 171 | characterBytes[43] = value; 172 | OnPropertyChange("LevelDisplay"); 173 | } 174 | } 175 | 176 | /// 177 | /// Chracter has a mercenary 178 | /// 179 | public bool HasMercenary 180 | { 181 | get 182 | { 183 | return BitConverter.ToUInt32(characterBytes, 179) != 0; 184 | } 185 | } 186 | 187 | /// 188 | /// ID Of mercenary's name 189 | /// 190 | public ushort MercenaryNameId 191 | { 192 | get 193 | { 194 | return BitConverter.ToUInt16(characterBytes, 183); 195 | } 196 | } 197 | 198 | /// 199 | /// Mercenary type 200 | /// 201 | public ushort MercenaryType 202 | { 203 | get 204 | { 205 | return BitConverter.ToUInt16(characterBytes, 185); 206 | } 207 | } 208 | 209 | /// 210 | /// Mercenary's experience points 211 | /// 212 | public uint MercenaryExp 213 | { 214 | get 215 | { 216 | return BitConverter.ToUInt32(characterBytes, 185); 217 | } 218 | } 219 | 220 | /// 221 | /// Creates a new character reader with specified header 222 | /// 223 | /// Raw character bytes from save file 224 | public Character(byte[] characterBytes) 225 | { 226 | if (characterBytes[0] != 0x55 || characterBytes[1] != 0xAA || characterBytes[2] != 0x55 || characterBytes[3] != 0xAA) 227 | { 228 | throw new Exception("CharacterByte data missing 0x55AA55AA header"); 229 | } 230 | 231 | this.characterBytes = characterBytes; 232 | characterFlags = characterBytes[0x24]; 233 | } 234 | 235 | /// 236 | /// Returns modified character bytes for use in save file 237 | /// 238 | /// Raw character bytes 239 | public byte[] GetCharacterBytes() 240 | { 241 | characterBytes[0x24] = characterFlags; 242 | 243 | return characterBytes; 244 | } 245 | 246 | #region INotifyPropertyChanged Members 247 | 248 | public event PropertyChangedEventHandler PropertyChanged; 249 | 250 | private void OnPropertyChange(string propertyName) 251 | { 252 | if (PropertyChanged != null) 253 | { 254 | PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 255 | } 256 | } 257 | 258 | #endregion 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/CharacterEditor.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {69326F80-7E22-41AD-BF83-02829A2BA924} 9 | Library 10 | Properties 11 | CharacterEditor 12 | CharacterEditor 13 | v3.5 14 | 512 15 | 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | TRACE;DEBUG;DESKTOP 23 | prompt 24 | 4 25 | true 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | true 35 | AnyCPU 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 | PreserveNewest 62 | 63 | 64 | PreserveNewest 65 | 66 | 67 | PreserveNewest 68 | 69 | 70 | PreserveNewest 71 | 72 | 73 | PreserveNewest 74 | 75 | 76 | PreserveNewest 77 | 78 | 79 | PreserveNewest 80 | 81 | 82 | PreserveNewest 83 | 84 | 85 | 86 | 87 | BitStream.cs 88 | Designer 89 | 90 | 91 | 92 | 93 | 100 | -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/Inventory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace CharacterEditor 5 | { 6 | public class Inventory 7 | { 8 | public Inventory(byte[] inventoryBytes) 9 | { 10 | ProcessInventoryBytes(inventoryBytes); 11 | } 12 | 13 | enum ItemContainer 14 | { 15 | Player, 16 | Corpse, 17 | Mercenary, 18 | Golem 19 | } 20 | 21 | private byte[] unknownCorpseData = new byte[12]; 22 | private List playerItems = new List(); 23 | private List corpseItems = new List(); 24 | private List mercItems = new List(); 25 | private List golemItems = new List(); 26 | 27 | /// 28 | /// All player items 29 | /// 30 | public List PlayerItems 31 | { 32 | get { return playerItems; } 33 | set { playerItems = value; } 34 | } 35 | 36 | /// 37 | /// Items in corpse 38 | /// 39 | public List CorpseItems 40 | { 41 | get { return corpseItems; } 42 | set { corpseItems = value; } 43 | } 44 | 45 | /// 46 | /// Mercenary equipment 47 | /// 48 | public List MercItems 49 | { 50 | get { return mercItems; } 51 | set { mercItems = value; } 52 | } 53 | 54 | /// 55 | /// Item(s) golem was created from 56 | /// 57 | public List GolemItems 58 | { 59 | get { return golemItems; } 60 | set { golemItems = value; } 61 | } 62 | 63 | /// 64 | /// Number of items failed to be parsed, items will not be included when saving data 65 | /// 66 | public int FailedItemCount 67 | { 68 | get; 69 | protected set; 70 | } 71 | 72 | /// 73 | /// Obtains the length of next Item in raw inventory data at specified position 74 | /// 75 | /// Raw inventory data 76 | /// Position in raw inventory data to start looking for item 77 | /// Length in bytes of next item 78 | private int GetNextItemSize(byte[] inventoryBytes, int begin) 79 | { 80 | for (int i = begin + 2; i < inventoryBytes.Length - 1; i++) 81 | { 82 | // Header of next item record or end of inventory list reached 83 | if (inventoryBytes[i] == 'J' && inventoryBytes[i + 1] == 'M') 84 | { 85 | return i - begin; 86 | } 87 | } 88 | 89 | return inventoryBytes.Length - begin; 90 | } 91 | 92 | /// 93 | /// Retrieves the next item from specified position in raw inventory data 94 | /// 95 | /// Raw inventory data 96 | /// Position in raw inventory data to start looking for item 97 | /// Next item in raw inventory data 98 | private Item GetNextItem(byte[] inventoryBytes, ref int begin) 99 | { 100 | int itemDataSize = GetNextItemSize(inventoryBytes, begin); 101 | byte[] itemData = new byte[itemDataSize]; 102 | Array.Copy(inventoryBytes, begin, itemData, 0, itemDataSize); 103 | 104 | Item item; 105 | 106 | // TODO: This is a horror - should be rewritten soon 107 | try 108 | { 109 | item = new Item(itemData); 110 | } 111 | catch (IndexOutOfRangeException) 112 | { 113 | // Using a new itemDataSize because if this fails we need to increase begin by the original 114 | // itemDataSize 115 | int itemDataSizeNew = itemDataSize + GetNextItemSize(inventoryBytes, begin + itemDataSize); 116 | 117 | try 118 | { 119 | // Assume the JM found by GetNextItemSize was just part of the item's data. 120 | // Recalculate the length of the item ignoring the first JM it encounters 121 | itemData = new byte[itemDataSizeNew]; 122 | Array.Copy(inventoryBytes, begin, itemData, 0, itemData.Length); 123 | item = new Item(itemData); 124 | } 125 | catch (IndexOutOfRangeException) 126 | { 127 | // Not recoverable, handled by code below 128 | item = null; 129 | } 130 | 131 | if (item == null) 132 | { 133 | // Item not recoverable, skip it 134 | FailedItemCount++; 135 | begin += itemDataSize; 136 | return null; 137 | } 138 | else 139 | { 140 | // Set the new itemDataSize since the we fixed the problem 141 | itemDataSize = itemDataSizeNew; 142 | } 143 | } 144 | 145 | begin += itemDataSize; 146 | if (item.IsSocketed) 147 | { 148 | uint failedSockets = 0; 149 | 150 | for (int i = 0; i < item.SocketsFilled; i++) 151 | { 152 | Item nextItem = GetNextItem(inventoryBytes, ref begin); 153 | 154 | if (nextItem == null) 155 | { 156 | failedSockets++; 157 | continue; 158 | } 159 | 160 | item.Sockets.Add(nextItem); 161 | } 162 | 163 | if (failedSockets > 0) 164 | { 165 | item.ClearRunewordData(); 166 | 167 | for (int i = 0; i < item.Sockets.Count; i++) 168 | { 169 | Item socketedItem = item.Sockets[i].RemoveSocketedItem(i); 170 | CorpseItems.Add(socketedItem); 171 | } 172 | } 173 | } 174 | 175 | return item; 176 | } 177 | 178 | /// 179 | /// Separates raw inventory data into more specific chunks of data 180 | /// 181 | /// Raw inventory data from save file 182 | /// Inventory data for player 183 | /// Unknown data for corpse 184 | /// Inventory data for corpse 185 | /// Inventory data for mercenary 186 | /// Inventory data for golem 187 | private void SplitInventoryBytes(byte[] inventoryBytes, 188 | out byte[] playerInventoryBytes, 189 | out byte[] unknownCorpseDataBytes, 190 | out byte[] corpseInventoryBytes, 191 | out byte[] mercInventoryBytes, 192 | out byte[] golemInventoryBytes) 193 | { 194 | int playerInventoryLength = 0; 195 | int unknownCorpseDataLength = 0; 196 | int corpseInventoryLength = 0; 197 | int mercInventoryLength = 0; 198 | int golemInventoryLength = 0; 199 | 200 | int playerInventoryStart = 0; 201 | int unknownCorpseDataStart = 0; 202 | int corpseInventoryStart = 0; 203 | int mercInventoryStart = 0; 204 | int golemInventoryStart = 0; 205 | 206 | bool hasCorpseData = false; 207 | 208 | //TODO: Trust item count header and use it to detect player/corpse/merc/golem sections 209 | 210 | // Player inventory data ends with "JM\x00\x00" or "JM\x01\x00". Using +4 because we want to skip the initial JM## bytes. 211 | playerInventoryStart = 0; 212 | for (int i = playerInventoryStart + 4; i < inventoryBytes.Length; i++) 213 | { 214 | // End of player inventory list 215 | if (inventoryBytes[i] == 'J' && inventoryBytes[i + 1] == 'M' && inventoryBytes[i + 3] == 0x00) 216 | { 217 | // Corpse exists if JM\x01\x00 followed by 12 unknown bytes and JM (the first corpse item entry) 218 | if (inventoryBytes[i + 2] == 0x01 && inventoryBytes[i + 16] == 'J' && inventoryBytes[i + 17] == 'M') 219 | { 220 | hasCorpseData = true; 221 | playerInventoryLength = i - playerInventoryStart; 222 | break; 223 | } 224 | // No corpse exists if JM\x00\x00 followed by jf (ending tag of corpse data) 225 | else if (inventoryBytes[i + 2] == 0x00 && inventoryBytes[i + 4] == 'j' && inventoryBytes[i + 5] == 'f') 226 | { 227 | playerInventoryLength = i - playerInventoryStart; 228 | break; 229 | } 230 | } 231 | } 232 | 233 | // When corpse data is present, 4 bytes from last player inventory JM header and 12 bytes 234 | // of unknown corpse data exist. Must keep a copy of these 12 bytes for when we go to save 235 | // the file 236 | unknownCorpseDataStart = playerInventoryStart + playerInventoryLength + 4; 237 | if (hasCorpseData) 238 | { 239 | for (int i = unknownCorpseDataStart; i < inventoryBytes.Length - 1; i++) 240 | { 241 | if (inventoryBytes[i] == 'J' && inventoryBytes[i + 1] == 'M') 242 | { 243 | unknownCorpseDataLength = i - unknownCorpseDataStart; 244 | break; 245 | } 246 | } 247 | } 248 | 249 | // Corpse inventory data exists between the end of player inventory list "JM\x0#\x00" and ends with "jf" 250 | // 12 bytes of unknown data are at the beginning of corpseInventoryBytes whenever corpse data is present 251 | corpseInventoryStart = unknownCorpseDataStart + unknownCorpseDataLength; 252 | if (!hasCorpseData) 253 | { 254 | corpseInventoryLength = 0; 255 | } 256 | else 257 | { 258 | for (int i = corpseInventoryStart + 4; i < inventoryBytes.Length - 1; i++) 259 | { 260 | if (inventoryBytes[i] == 'j' && inventoryBytes[i + 1] == 'f') 261 | { 262 | corpseInventoryLength = i - corpseInventoryStart; 263 | break; 264 | } 265 | } 266 | } 267 | 268 | // Merc inventory data is everything between "jf" and "kf", start searching for "kf" from end of file 269 | mercInventoryStart = corpseInventoryStart + corpseInventoryLength + 2; 270 | for (int i = inventoryBytes.Length - 3; i > mercInventoryStart; i--) 271 | { 272 | if (inventoryBytes[i] == 'k' && inventoryBytes[i + 1] == 'f' && (inventoryBytes[i + 2] == 0 || inventoryBytes[i + 2] == 1)) 273 | { 274 | mercInventoryLength = i - mercInventoryStart; 275 | break; 276 | } 277 | } 278 | 279 | // Golem inventory exists iff the byte after the end of Merc inventory is set to 1 280 | golemInventoryStart = mercInventoryStart + mercInventoryLength + 2; 281 | if (golemInventoryStart < inventoryBytes.Length && inventoryBytes[golemInventoryStart] == '\x01') 282 | { 283 | golemInventoryStart++; // Skip the 01 flag, useless to us 284 | golemInventoryLength = inventoryBytes.Length - golemInventoryStart; 285 | } 286 | 287 | playerInventoryBytes = new byte[playerInventoryLength]; 288 | unknownCorpseDataBytes = new byte[unknownCorpseDataLength]; 289 | corpseInventoryBytes = new byte[corpseInventoryLength]; 290 | mercInventoryBytes = new byte[mercInventoryLength]; 291 | golemInventoryBytes = new byte[golemInventoryLength]; 292 | 293 | Array.Copy(inventoryBytes, playerInventoryStart, playerInventoryBytes, 0, playerInventoryBytes.Length); 294 | Array.Copy(inventoryBytes, unknownCorpseDataStart, unknownCorpseDataBytes, 0, unknownCorpseData.Length); 295 | Array.Copy(inventoryBytes, corpseInventoryStart, corpseInventoryBytes, 0, corpseInventoryBytes.Length); 296 | Array.Copy(inventoryBytes, mercInventoryStart, mercInventoryBytes, 0, mercInventoryBytes.Length); 297 | Array.Copy(inventoryBytes, golemInventoryStart, golemInventoryBytes, 0, golemInventoryBytes.Length); 298 | } 299 | 300 | /// 301 | /// Parses raw inventory data into Items 302 | /// 303 | /// Raw inventory data from save file 304 | private void ProcessInventoryBytes(byte[] inventoryBytes) 305 | { 306 | byte[] playerInventoryBytes; 307 | byte[] corpseInventoryBytes; 308 | byte[] mercInventoryBytes; 309 | byte[] golemInventoryBytes; 310 | 311 | SplitInventoryBytes(inventoryBytes, 312 | out playerInventoryBytes, 313 | out unknownCorpseData, 314 | out corpseInventoryBytes, 315 | out mercInventoryBytes, 316 | out golemInventoryBytes); 317 | 318 | int currentPosition; 319 | 320 | // Parse player's inventory items, skipping 4 byte header 321 | currentPosition = 4; 322 | while (currentPosition < playerInventoryBytes.Length) 323 | { 324 | Item currentItem = GetNextItem(playerInventoryBytes, ref currentPosition); 325 | if (currentItem == null) 326 | { 327 | continue; 328 | } 329 | 330 | playerItems.Add(currentItem); 331 | } 332 | 333 | // Parse corpse inventory, skipping 4 byte header and 12 byte unknown corpse data 334 | currentPosition = 4; 335 | while (currentPosition < corpseInventoryBytes.Length) 336 | { 337 | Item currentItem = GetNextItem(corpseInventoryBytes, ref currentPosition); 338 | if (currentItem == null) 339 | { 340 | continue; 341 | } 342 | 343 | corpseItems.Add(currentItem); 344 | } 345 | 346 | // Parse merc items 347 | currentPosition = 4; 348 | while (currentPosition < mercInventoryBytes.Length) 349 | { 350 | Item currentItem = GetNextItem(mercInventoryBytes, ref currentPosition); 351 | if (currentItem == null) 352 | { 353 | continue; 354 | } 355 | 356 | mercItems.Add(currentItem); 357 | } 358 | 359 | // Parse golem item(s) 360 | currentPosition = 0; 361 | while (currentPosition < golemInventoryBytes.Length) 362 | { 363 | Item currentItem = GetNextItem(golemInventoryBytes, ref currentPosition); 364 | if (currentItem == null) 365 | { 366 | continue; 367 | } 368 | 369 | golemItems.Add(currentItem); 370 | } 371 | } 372 | 373 | /// 374 | /// Adds raw data from an item to a list of raw data 375 | /// 376 | /// List of raw data to add item to 377 | /// Item to add 378 | private void AddItemsToInventoryBytes(ref List inventoryBytes, List items) 379 | { 380 | foreach (Item item in items) 381 | { 382 | inventoryBytes.AddRange(item.GetItemBytes()); 383 | } 384 | } 385 | 386 | /// 387 | /// Converts all item data into raw data for the save file 388 | /// 389 | /// Character has a mercenary 390 | /// Byte array containing raw inventory data 391 | public byte[] GetInventoryBytes(bool hasMercenary) 392 | { 393 | List inventoryBytes = new List(); 394 | byte[] magic = new byte[] { 0x4A, 0x4D }; 395 | 396 | inventoryBytes.AddRange(magic); 397 | inventoryBytes.AddRange(BitConverter.GetBytes((short)playerItems.Count)); 398 | 399 | // Dump inventory data 400 | AddItemsToInventoryBytes(ref inventoryBytes, playerItems); 401 | 402 | // Closing player inventory list header 403 | inventoryBytes.AddRange(magic); 404 | if (corpseItems.Count > 0) 405 | { 406 | inventoryBytes.Add(0x01); 407 | } 408 | else 409 | { 410 | inventoryBytes.Add(0x00); 411 | } 412 | inventoryBytes.Add(0x00); 413 | 414 | 415 | // Dump corpse data 416 | if (corpseItems.Count > 0) 417 | { 418 | if (unknownCorpseData.Length == 0) 419 | { 420 | // I don't know what the 12 bytes are, but 0 seems to work 421 | unknownCorpseData = new byte[12]; 422 | } 423 | 424 | if (unknownCorpseData[2] == 0) 425 | { 426 | // This byte has to be flagged to make the corpse appear, might be corpse count 427 | unknownCorpseData[2] = 1; 428 | } 429 | 430 | inventoryBytes.AddRange(unknownCorpseData); 431 | inventoryBytes.AddRange(magic); 432 | inventoryBytes.AddRange(BitConverter.GetBytes((short)corpseItems.Count)); 433 | 434 | AddItemsToInventoryBytes(ref inventoryBytes, corpseItems); 435 | } 436 | 437 | // Closing corpse inventory header 438 | inventoryBytes.Add(0x6a); // j 439 | inventoryBytes.Add(0x66); // f 440 | 441 | // Dump merc inventory 442 | if (hasMercenary) 443 | { 444 | inventoryBytes.AddRange(magic); 445 | inventoryBytes.AddRange(BitConverter.GetBytes((short)mercItems.Count)); 446 | 447 | AddItemsToInventoryBytes(ref inventoryBytes, mercItems); 448 | } 449 | 450 | // Closing merc inventory header 451 | inventoryBytes.Add(0x6b); // k 452 | inventoryBytes.Add(0x66); // f 453 | 454 | if (golemItems.Count == 0) 455 | { 456 | // No iron golem, finish file off with 0x00 457 | inventoryBytes.Add(0x00); 458 | } 459 | else 460 | { 461 | // Yes iron golem, finish file off with 0x01 followed by item golem was 462 | // spawned from 463 | inventoryBytes.Add((byte)golemItems.Count); 464 | AddItemsToInventoryBytes(ref inventoryBytes, golemItems); 465 | } 466 | 467 | return inventoryBytes.ToArray(); 468 | } 469 | } 470 | } 471 | -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/Item.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using BKSystem.IO; 7 | 8 | namespace CharacterEditor 9 | { 10 | public class Item : INotifyPropertyChanged, ICloneable 11 | { 12 | public enum ItemQuality 13 | { 14 | Unknown, // Shouldn't happen 15 | Inferior, 16 | Normal, 17 | Superior, 18 | Magic, 19 | Set, 20 | Rare, 21 | Unique, 22 | Craft 23 | } 24 | 25 | public enum EquipmentLocation 26 | { 27 | None, // Not equipped 28 | Head, 29 | Amulet, 30 | Body, 31 | RightPrimary, 32 | LeftPrimary, 33 | RightRing, 34 | LeftRing, 35 | Belt, 36 | Feet, 37 | Gloves, 38 | RightSecondary, 39 | LeftSecondary 40 | } 41 | 42 | public enum ItemLocation 43 | { 44 | Stored, 45 | Equipped, 46 | Belt, 47 | Ground, 48 | Tohand, 49 | Dropping, 50 | Socketed 51 | } 52 | 53 | public enum StorageType 54 | { 55 | Unknown, 56 | Inventory, 57 | UnknownB, 58 | UnknownC, 59 | Cube, 60 | Stash, 61 | } 62 | 63 | private class ItemData 64 | { 65 | public ItemData() 66 | { 67 | 68 | } 69 | 70 | public ItemData(string name, uint value, int bitCount, int index) 71 | { 72 | this.Index = index; 73 | this.Name = name; 74 | this.Value = value; 75 | this.BitCount = bitCount; 76 | } 77 | 78 | public int Index { get; set; } 79 | public string Name { get; set; } 80 | public object Value { get; set; } 81 | public int BitCount { get; set; } 82 | 83 | public override string ToString() 84 | { 85 | return Value.ToString(); 86 | } 87 | } 88 | 89 | public class PropertyInfo : INotifyPropertyChanged 90 | { 91 | private int id; 92 | 93 | public int ID 94 | { 95 | get { return id; } 96 | set 97 | { 98 | id = value; 99 | OnPropertyChange("ID"); 100 | OnPropertyChange("PropertyName"); 101 | } 102 | } 103 | 104 | public string PropertyName 105 | { 106 | get 107 | { 108 | return ItemDefs.GetPropertyName(ID); 109 | } 110 | } 111 | 112 | public int Value { get; set; } 113 | public int ParamValue { get; set; } 114 | public bool IsAdditionalProperty { get; set; } 115 | 116 | public override string ToString() 117 | { 118 | return string.Format("[{0}] {1} -> {2} [{3}]", ID, PropertyName, Value, ParamValue); 119 | } 120 | 121 | #region INotifyPropertyChanged Members 122 | 123 | public event PropertyChangedEventHandler PropertyChanged; 124 | 125 | private void OnPropertyChange(string propertyName) 126 | { 127 | if (PropertyChanged != null) 128 | { 129 | PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 130 | } 131 | } 132 | 133 | #endregion 134 | } 135 | 136 | private List properties = new List(); 137 | private List propertiesSet = new List(); 138 | private List propertiesRuneword = new List(); 139 | private List sockets = new List(); 140 | private static Dictionary dataIndicies = new Dictionary(); 141 | private Dictionary dataEntries = new Dictionary(); 142 | private BitStream bs; 143 | private byte[] remainingBytes; 144 | 145 | /// 146 | /// List of items stored in item's sockets 147 | /// 148 | public List Sockets 149 | { 150 | get { return sockets; } 151 | } 152 | /// 153 | /// Item properties 154 | /// 155 | public List Properties 156 | { 157 | get { return properties; } 158 | } 159 | /// 160 | /// Set bonuses for wearing multiple parts 161 | /// 162 | public List PropertiesSet 163 | { 164 | get { return propertiesSet; } 165 | } 166 | /// 167 | /// Runeword properties 168 | /// 169 | public List PropertiesRuneword 170 | { 171 | get { return propertiesRuneword; } 172 | } 173 | 174 | #region SimpleProperties 175 | 176 | // All items must contain these properties, if they don't it's an invalid item and should be caught 177 | public bool IsEquipped 178 | { 179 | get { return GetDataBoolean("IsEquipped"); } 180 | set { SetData("IsEquipped", value); } 181 | } 182 | public bool IsInSocket 183 | { 184 | get { return GetDataBoolean("IsInSocket"); } 185 | set { SetData("IsInSocket", value); } 186 | } 187 | public bool IsIdentified 188 | { 189 | get { return GetDataBoolean("IsIdentified"); } 190 | set { SetData("IsIdentified", value); } 191 | } 192 | public bool IsSwitchIn 193 | { 194 | get { return GetDataBoolean("IsSwitchIn"); } 195 | set { SetData("IsSwitchIn", value); } 196 | } 197 | public bool IsSwitchOut 198 | { 199 | get { return GetDataBoolean("IsSwitchOut"); } 200 | set { SetData("IsSwitchOut", value); } 201 | } 202 | public bool IsBroken 203 | { 204 | get { return GetDataBoolean("IsBroken"); } 205 | set { SetData("IsBroken", value); } 206 | } 207 | public bool IsSocketed 208 | { 209 | get { return GetDataBoolean("IsSocketed"); } 210 | set { SetData("IsSocketed", value); } 211 | } 212 | public bool IsPotion 213 | { 214 | get { return GetDataBoolean("IsPotion"); } 215 | set { SetData("IsPotion", value); } 216 | } 217 | public bool IsStoreItem 218 | { 219 | get { return GetDataBoolean("IsStoreItem"); } 220 | set { SetData("IsStoreItem", value); } 221 | } 222 | public bool IsNotInSocket 223 | { 224 | get { return GetDataBoolean("IsNotInSocket"); } 225 | set { SetData("IsNotInSocket", value); } 226 | } 227 | public bool IsEar 228 | { 229 | get { return GetDataBoolean("IsEar"); } 230 | set { SetData("IsEar", value); } 231 | } 232 | public bool IsStarterItem 233 | { 234 | get { return GetDataBoolean("IsStarterItem"); } 235 | set { SetData("IsStarterItem", value); } 236 | } 237 | public bool IsSimpleItem 238 | { 239 | get { return GetDataBoolean("IsSimpleItem"); } 240 | set { SetData("IsSimpleItem", value); } 241 | } 242 | public bool IsEthereal 243 | { 244 | get { return GetDataBoolean("IsEthereal"); } 245 | set { SetData("IsEthereal", value); } 246 | } 247 | public bool IsPersonalized 248 | { 249 | get { return GetDataBoolean("IsPersonalized"); } 250 | set { SetData("IsPersonalized", value); } 251 | } 252 | public bool IsGamble 253 | { 254 | get { return GetDataBoolean("IsGamble"); } 255 | set { SetData("IsGamble", value); } 256 | } 257 | public bool IsRuneword 258 | { 259 | get { return GetDataBoolean("IsRuneword"); } 260 | set { SetData("IsRuneword", value); } 261 | } 262 | /// 263 | /// ItemLocation enum 264 | /// 265 | public uint Location 266 | { 267 | get { return GetDataValue("Location"); } 268 | set { SetData("Location", value); } 269 | } 270 | /// 271 | /// EquipmentLocation enum 272 | /// 273 | public uint PositionOnBody 274 | { 275 | get { return GetDataValue("PositionOnBody"); } 276 | set { SetData("PositionOnBody", value); } 277 | } 278 | public uint PositionX 279 | { 280 | get { return GetDataValue("PositionX"); } 281 | set { SetData("PositionX", value); } 282 | } 283 | public uint PositionY 284 | { 285 | get { return GetDataValue("PositionY"); } 286 | set { SetData("PositionY", value); } 287 | } 288 | /// 289 | /// StorageType enum 290 | /// 291 | public uint StorageId 292 | { 293 | get { return GetDataValue("StorageId"); } 294 | set { SetData("StorageId", value); } 295 | } 296 | #endregion 297 | 298 | #region ExtendedProperties 299 | 300 | public string ItemCode 301 | { 302 | get 303 | { 304 | if (IsEar) 305 | { 306 | return "ear"; 307 | } 308 | 309 | return GetDataObject("ItemCode") as string; 310 | //return dataEntries["ItemCode"].Value as string; 311 | } 312 | set 313 | { 314 | if (value.Length < 3) 315 | { 316 | return; 317 | } 318 | SetData("ItemCode", value.Substring(0, 3)); 319 | } 320 | } 321 | 322 | public uint GoldAmountSmall 323 | { 324 | get { return GetDataValue("GoldAmountSmall"); } 325 | set { SetData("GoldAmountSmall", value); } 326 | } 327 | public uint GoldAmountLarge 328 | { 329 | get { return GetDataValue("GoldAmountLarge"); } 330 | set { SetData("GoldAmountLarge", value); } 331 | } 332 | public uint SocketsFilled 333 | { 334 | get { return GetDataValue("SocketsFilled"); } 335 | set { SetData("SocketsFilled", value); } 336 | } 337 | public uint Id 338 | { 339 | get { return GetDataValue("Id"); } 340 | set { SetData("Id", value); } 341 | } 342 | public uint Level 343 | { 344 | get 345 | { 346 | if (IsEar) 347 | { 348 | return EarLevel; 349 | } 350 | 351 | return GetDataValue("Level"); 352 | } 353 | set 354 | { 355 | SetData("Level", value); 356 | } 357 | } 358 | /// 359 | /// ItemQuality enum 360 | /// 361 | public uint Quality 362 | { 363 | get { return GetDataValue("Quality"); } 364 | set { SetData("Quality", value); } 365 | } 366 | public bool HasGraphic 367 | { 368 | get { return GetDataBoolean("HasGraphic"); } 369 | set { SetData("HasGraphic", value); } 370 | } 371 | public uint Graphic 372 | { 373 | get { return GetDataValue("Graphic"); } 374 | set { SetData("Graphic", value); } 375 | } 376 | public uint UniqueSetId 377 | { 378 | get { return GetDataValue("UniqueSetId"); } 379 | set { SetData("UniqueSetId", value); } 380 | } 381 | public uint Defense 382 | { 383 | get { return GetDataValue("Defense"); } 384 | set { SetData("Defense", value); } 385 | } 386 | public uint MaxDurability 387 | { 388 | get { return GetDataValue("MaxDurability"); } 389 | set { SetData("MaxDurability", value); } 390 | } 391 | public uint Durability 392 | { 393 | get { return GetDataValue("Durability"); } 394 | set { SetData("Durability", value); } 395 | } 396 | public bool IsIndestructable 397 | { 398 | get { return GetDataBoolean("IsIndestructable"); } 399 | set { SetData("IsIndestructable", value); } 400 | } 401 | public uint SocketCount 402 | { 403 | get { return GetDataValue("SocketCount"); } 404 | set { SetData("SocketCount", value); } 405 | } 406 | public uint Quantity 407 | { 408 | get { return GetDataValue("Quantity"); } 409 | set { SetData("Quantity", value); } 410 | } 411 | public bool RandomFlag 412 | { 413 | get { return GetDataBoolean("RandomFlag"); } 414 | set { SetData("RandomFlag", value); } 415 | } 416 | public bool UnknownGoldFlag 417 | { 418 | get { return GetDataBoolean("UnknownGoldFlag"); } 419 | set { SetData("UnknownGoldFlag", value); } 420 | } 421 | public bool ClassFlag 422 | { 423 | get { return GetDataBoolean("ClassFlag"); } 424 | set { SetData("ClassFlag", value); } 425 | } 426 | public uint ClassInfo 427 | { 428 | get { return GetDataValue("ClassInfo"); } 429 | set { SetData("ClassInfo", value); } 430 | } 431 | public uint InferiorQualityType 432 | { 433 | get { return GetDataValue("InferiorQualityType"); } 434 | set { SetData("InferiorQualityType", value); } 435 | } 436 | public uint SuperiorQualityType 437 | { 438 | get { return GetDataValue("SuperiorQualityType"); } 439 | set { SetData("SuperiorQualityType", value); } 440 | } 441 | public uint CharmData 442 | { 443 | get { return GetDataValue("CharmData"); } 444 | set { SetData("CharmData", value); } 445 | } 446 | public uint SpellId 447 | { 448 | get { return GetDataValue("SpellId"); } 449 | set { SetData("SpellId", value); } 450 | } 451 | public uint MonsterId 452 | { 453 | get { return GetDataValue("MonsterId"); } 454 | set { SetData("MonsterId", value); } 455 | } 456 | public uint EarClass 457 | { 458 | get { return GetDataValue("EarClass"); } 459 | set { SetData("EarClass", value); } 460 | } 461 | public uint EarLevel 462 | { 463 | get { return GetDataValue("EarLevel"); } 464 | set { SetData("EarLevel", value); } 465 | } 466 | public string EarName 467 | { 468 | get { return (string)GetDataObject("EarName"); } 469 | protected set { SetData("EarName", value.Length > 17 ? value.Substring(0, 17) : value); } 470 | } 471 | public string PersonalizedName 472 | { 473 | get { return (string)GetDataObject("PersonalizedName"); } 474 | protected set { SetData("PersonalizedName", value.Length > 17 ? value.Substring(0, 17) : value); } 475 | } 476 | public uint RunewordId 477 | { 478 | get { return GetDataValue("RunewordId"); } 479 | set { SetData("RunewordId", value); } 480 | } 481 | 482 | #endregion 483 | 484 | #region Affix 485 | 486 | public uint PrefixNameId 487 | { 488 | get { return GetDataValue("PrefixNameId"); } 489 | set { SetData("PrefixNameId", value); } 490 | } 491 | public uint SuffixNameId 492 | { 493 | get { return GetDataValue("SuffixNameId"); } 494 | set { SetData("SuffixNameId", value); } 495 | } 496 | public bool PrefixFlag0 497 | { 498 | get { return GetDataBoolean("PrefixFlag0"); } 499 | set { SetData("PrefixFlag0", value); } 500 | } 501 | public bool PrefixFlag1 502 | { 503 | get { return GetDataBoolean("PrefixFlag1"); } 504 | set { SetData("PrefixFlag1", value); } 505 | } 506 | public bool PrefixFlag2 507 | { 508 | get { return GetDataBoolean("PrefixFlag2"); } 509 | set { SetData("PrefixFlag2", value); } 510 | } 511 | public bool SuffixFlag0 512 | { 513 | get { return GetDataBoolean("SuffixFlag0"); } 514 | set { SetData("SuffixFlag0", value); } 515 | } 516 | public bool SuffixFlag1 517 | { 518 | get { return GetDataBoolean("SuffixFlag1"); } 519 | set { SetData("SuffixFlag1", value); } 520 | } 521 | public bool SuffixFlag2 522 | { 523 | get { return GetDataBoolean("SuffixFlag2"); } 524 | set { SetData("SuffixFlag2", value); } 525 | } 526 | public uint MagicPrefix 527 | { 528 | get { return GetDataValue("MagicPrefix"); } 529 | set { SetData("MagicPrefix", value); } 530 | } 531 | public uint MagicSuffix 532 | { 533 | get { return GetDataValue("MagicSuffix"); } 534 | set { SetData("MagicSuffix", value); } 535 | } 536 | public uint Prefix0 537 | { 538 | get { return GetDataValue("Prefix0"); } 539 | set { SetData("Prefix0", value); } 540 | } 541 | public uint Prefix1 542 | { 543 | get { return GetDataValue("Prefix1"); } 544 | set { SetData("Prefix1", value); } 545 | } 546 | public uint Prefix2 547 | { 548 | get { return GetDataValue("Prefix2"); } 549 | set { SetData("Prefix2", value); } 550 | } 551 | public uint Suffix0 552 | { 553 | get { return GetDataValue("Suffix0"); } 554 | set { SetData("Suffix0", value); } 555 | } 556 | public uint Suffix1 557 | { 558 | get { return GetDataValue("Suffix1"); } 559 | set { SetData("Suffix1", value); } 560 | } 561 | public uint Suffix2 562 | { 563 | get { return GetDataValue("Suffix2"); } 564 | set { SetData("Suffix2", value); } 565 | } 566 | #endregion 567 | 568 | 569 | static Item() 570 | { 571 | CreateDataIndicies(); 572 | } 573 | 574 | /// 575 | /// 576 | /// 577 | /// 578 | /// must be true only if new Item is created from byte[] array 579 | public Item(byte[] itemData, bool forceReadSockets) 580 | { 581 | if (itemData[0] != 'J' || itemData[1] != 'M') 582 | { 583 | throw new Exception("Item data missing JM header"); 584 | } 585 | 586 | bs = new BitStream(itemData); 587 | 588 | ReadItemData(); 589 | ReadRemainingBits(); 590 | 591 | if (forceReadSockets) 592 | ReadSockets(itemData); 593 | } 594 | 595 | 596 | /// 597 | /// Decodes raw item data 598 | /// 599 | private void ReadItemData() 600 | { 601 | ReadItemDataSimple(); 602 | 603 | if (IsEar) 604 | { 605 | ReadItemDataEar(); 606 | return; 607 | } 608 | 609 | ReadString("ItemCode", 3, 8, true); 610 | 611 | // Read gold data if it's gold 612 | if (ItemCode.ToLower() == "gld") 613 | { 614 | ReadItemDataGold(); 615 | return; 616 | } 617 | 618 | if (IsSimpleItem) 619 | { 620 | return; 621 | } 622 | 623 | ReadItemDataExtended(); 624 | 625 | // TODO: Not sure what this bit is, I can't find any items with it set 626 | // Extend.txt says it's some sort of random flag followed by 40 bits 627 | ReadData("RandomFlag", 1); 628 | if (RandomFlag) 629 | { 630 | ReadData("Unknown8", 8); 631 | ReadData("Unknown9", 32); 632 | } 633 | 634 | // Type specific extended data 635 | ReadItemDataExtendedSpecific(); 636 | } 637 | 638 | /// 639 | /// Decodes basic item data. All items have basic data 640 | /// 641 | private void ReadItemDataSimple() 642 | { 643 | bs.SkipBits(16); // "JM" header 644 | 645 | ReadData("IsEquipped", 1); 646 | ReadData("Unknown0", 2); 647 | ReadData("IsInSocket", 1); 648 | ReadData("IsIdentified", 1); 649 | ReadData("Unknown1", 1); 650 | ReadData("IsSwitchIn", 1); 651 | ReadData("IsSwitchOut", 1); 652 | ReadData("IsBroken", 1); 653 | ReadData("Unknown2", 1); 654 | ReadData("IsPotion", 1); 655 | ReadData("IsSocketed", 1); 656 | ReadData("Unknown3", 1); 657 | ReadData("IsStoreItem", 1); 658 | ReadData("IsNotInSocket", 1); 659 | ReadData("Unknown4", 1); 660 | ReadData("IsEar", 1); 661 | ReadData("IsStarterItem", 1); 662 | ReadData("Unknown5", 3); 663 | ReadData("IsSimpleItem", 1); 664 | ReadData("IsEthereal", 1); 665 | ReadData("Unknown6", 1); 666 | ReadData("IsPersonalized", 1); 667 | ReadData("IsGamble", 1); 668 | ReadData("IsRuneword", 1); 669 | ReadData("Unknown7", 15); 670 | ReadData("Location", 3); 671 | ReadData("PositionOnBody", 4); 672 | ReadData("PositionX", 4); 673 | ReadData("PositionY", 4); 674 | ReadData("StorageId", 3); 675 | } 676 | 677 | /// 678 | /// Decodes ear data 679 | /// 680 | private void ReadItemDataEar() 681 | { 682 | ReadData("EarClass", 3); 683 | ReadData("EarLevel", 7); 684 | ReadString("EarName", 7); 685 | 686 | if (EarName.Trim().Length == 0) 687 | { 688 | throw new Exception("Invalid Ear: Blank ear name"); 689 | } 690 | 691 | foreach (char ch in EarName) 692 | { 693 | if (!Char.IsLetterOrDigit(ch) && ch != '-' && ch != '_') 694 | { 695 | throw new Exception("Invalid Ear: Ear name contains invalid characters"); 696 | } 697 | } 698 | 699 | return; 700 | } 701 | 702 | /// 703 | /// Decodes gold data 704 | /// 705 | private void ReadItemDataGold() 706 | { 707 | ReadData("UnknownGoldFlag", 1); 708 | 709 | // Not sure if this is correct 710 | if (UnknownGoldFlag) 711 | ReadData("GoldAmountLarge", 32); 712 | else 713 | ReadData("GoldAmountSmall", 12); 714 | 715 | return; 716 | } 717 | 718 | /// 719 | /// Decodes extended item data 720 | /// 721 | private void ReadItemDataExtended() 722 | { 723 | ReadData("SocketsFilled", 3); 724 | ReadData("Id", 32); 725 | ReadData("Level", 7); 726 | ReadData("Quality", 4); 727 | ReadData("HasGraphic", 1); 728 | if (HasGraphic) 729 | { 730 | ReadData("Graphic", 3); 731 | } 732 | 733 | // No idea what this flag is. Some say Class data others say Color data 734 | ReadData("ClassFlag", 1); 735 | if (ClassFlag) 736 | { 737 | ReadData("ClassInfo", 11); 738 | } 739 | 740 | switch ((ItemQuality)Quality) 741 | { 742 | case ItemQuality.Inferior: 743 | ReadData("InferiorQualityType", 3); 744 | break; 745 | case ItemQuality.Superior: 746 | ReadData("SuperiorQualityType", 3); 747 | break; 748 | 749 | case ItemQuality.Normal: 750 | if (ItemDefs.IsCharm(ItemCode)) 751 | { 752 | ReadData("CharmData", 12); 753 | } 754 | if (ItemDefs.IsScrollOrTome(ItemCode)) 755 | { 756 | ReadData("SpellId", 5); 757 | } 758 | else if (ItemDefs.IsMonsterPart(ItemCode)) 759 | { 760 | ReadData("MonsterId", 10); 761 | } 762 | break; 763 | 764 | case ItemQuality.Magic: 765 | ReadData("MagicPrefix", 11); 766 | ReadData("MagicSuffix", 11); 767 | break; 768 | 769 | case ItemQuality.Rare: 770 | case ItemQuality.Craft: 771 | ReadData("PrefixNameId", 8); 772 | ReadData("SuffixNameId", 8); 773 | 774 | for (int i = 0; i < 3; i++) 775 | { 776 | string prefixIndex = string.Format("PrefixFlag{0}", i); 777 | 778 | ReadData(prefixIndex, 1); 779 | if (GetDataBoolean(prefixIndex)) 780 | { 781 | ReadData(string.Format("Prefix{0}", i), 11); 782 | } 783 | 784 | string suffixIndex = string.Format("SuffixFlag{0}", i); 785 | 786 | ReadData(suffixIndex, 1); 787 | if (GetDataBoolean(suffixIndex)) 788 | { 789 | ReadData(string.Format("Suffix{0}", i), 11); 790 | } 791 | } 792 | break; 793 | 794 | case ItemQuality.Unique: 795 | case ItemQuality.Set: 796 | ReadData("UniqueSetId", 12); 797 | break; 798 | 799 | default: 800 | break; 801 | } 802 | 803 | if (IsRuneword) 804 | { 805 | ReadData("RunewordId", 16); 806 | } 807 | if (IsPersonalized) 808 | { 809 | ReadString("PersonalizedName", 7); //TODO: 15 in ROT? 810 | } 811 | } 812 | 813 | /// 814 | /// Decodes item specific data 815 | /// 816 | private void ReadItemDataExtendedSpecific() 817 | { 818 | if (ItemDefs.IsArmor(ItemCode)) 819 | { 820 | ReadData("Defense", ItemDefs.ItemStatCostsByName["armorclass"].SaveBits); 821 | Defense -= (uint)ItemDefs.ItemStatCostsByName["armorclass"].SaveAdd; 822 | } 823 | 824 | if (ItemDefs.IsWeapon(ItemCode) || ItemDefs.IsArmor(ItemCode)) 825 | { 826 | ReadData("MaxDurability", ItemDefs.ItemStatCostsByName["maxdurability"].SaveBits); 827 | MaxDurability -= (uint)ItemDefs.ItemStatCostsByName["maxdurability"].SaveAdd; 828 | 829 | if (MaxDurability != 0) 830 | { 831 | ReadData("Durability", ItemDefs.ItemStatCostsByName["durability"].SaveBits); 832 | Durability -= (uint)ItemDefs.ItemStatCostsByName["durability"].SaveAdd; 833 | } 834 | else 835 | { 836 | IsIndestructable = true; 837 | } 838 | } 839 | 840 | // Not sure of the order of socketed+stackable. Socketed arrows seem to mess up 841 | if (ItemDefs.IsStackable(ItemCode)) 842 | { 843 | ReadData("Quantity", 9); 844 | } 845 | if (IsSocketed) 846 | { 847 | ReadData("SocketCount", 4); 848 | } 849 | 850 | if ((ItemQuality)Quality == ItemQuality.Set) 851 | { 852 | ReadData("NumberOfSetProperties", 5); 853 | } 854 | 855 | ReadPropertyList(properties); 856 | 857 | if ((ItemQuality)Quality == ItemQuality.Set) 858 | { 859 | int numberOfSetProperties = (int)GetDataValue("NumberOfSetProperties"); 860 | 861 | while (bs.RemainingBits > 9) 862 | { 863 | ReadPropertyList(propertiesSet); 864 | } 865 | } 866 | 867 | if (IsRuneword) 868 | { 869 | ReadPropertyList(propertiesRuneword); 870 | } 871 | } 872 | 873 | /// 874 | /// Reads a list of properties to specified property list from the BitReader 875 | /// 876 | /// List of properties to read properties into 877 | private void ReadPropertyList(List propertyList) 878 | { 879 | while (true) 880 | { 881 | int currentPropertyID = (int)bs.ReadReversed(9); 882 | if (currentPropertyID == 0x1ff) 883 | { 884 | propertyList.Add(new PropertyInfo() { ID = currentPropertyID }); 885 | break; 886 | } 887 | 888 | ReadPropertyData(propertyList, currentPropertyID); 889 | } 890 | } 891 | 892 | /// 893 | /// Reads property data for a specified ID from BitReader 894 | /// 895 | /// List of properties to add data to 896 | /// ID of property to read from BitReader 897 | /// Property to read has no header. Found in damage type properties 898 | private void ReadPropertyData(List propertyList, int currentPropertyID, bool isAdditional = false) 899 | { 900 | //ItemStatCost statCost = null; 901 | //if (ItemDefs.ItemStatCostsById.ContainsKey(currentPropertyID)) 902 | ItemStatCost statCost = ItemDefs.ItemStatCostsById[currentPropertyID]; 903 | 904 | if (statCost == null) 905 | return; 906 | 907 | PropertyInfo currentPropertyInfo = new PropertyInfo(); 908 | 909 | currentPropertyInfo.IsAdditionalProperty = isAdditional; 910 | currentPropertyInfo.ID = currentPropertyID; 911 | currentPropertyInfo.Value = (int)bs.ReadReversed(statCost.SaveBits) - statCost.SaveAdd; 912 | 913 | if (statCost.SaveParamBits > 0) 914 | { 915 | currentPropertyInfo.ParamValue = (int)bs.ReadReversed(statCost.SaveParamBits); 916 | } 917 | 918 | propertyList.Add(currentPropertyInfo); 919 | 920 | switch (statCost.Stat) 921 | { 922 | case "item_maxdamage_percent": 923 | ReadPropertyData(propertyList, ItemDefs.ItemStatCostsByName["item_mindamage_percent"].ID, true); 924 | break; 925 | case "firemindam": 926 | ReadPropertyData(propertyList, ItemDefs.ItemStatCostsByName["firemaxdam"].ID, true); 927 | break; 928 | case "lightmindam": 929 | ReadPropertyData(propertyList, ItemDefs.ItemStatCostsByName["lightmaxdam"].ID, true); 930 | break; 931 | case "magicmindam": 932 | ReadPropertyData(propertyList, ItemDefs.ItemStatCostsByName["magicmaxdam"].ID, true); 933 | break; 934 | case "coldmindam": 935 | ReadPropertyData(propertyList, ItemDefs.ItemStatCostsByName["coldmaxdam"].ID, true); 936 | ReadPropertyData(propertyList, ItemDefs.ItemStatCostsByName["coldlength"].ID, true); 937 | break; 938 | case "poisonmindam": 939 | ReadPropertyData(propertyList, ItemDefs.ItemStatCostsByName["poisonmaxdam"].ID, true); 940 | ReadPropertyData(propertyList, ItemDefs.ItemStatCostsByName["poisonlength"].ID, true); 941 | break; 942 | default: 943 | break; 944 | } 945 | 946 | } 947 | 948 | /// 949 | /// Removes a socketed item from this item and returns it for further processing 950 | /// 951 | /// Index of socket to remove 952 | /// Socketed item already modified to be stored 953 | public Item RemoveSocketedItem(int index) 954 | { 955 | if (index < 0 || index > SocketsFilled) 956 | { 957 | throw new Exception("RemoveSocketedItem: Socket index out of bounds"); 958 | } 959 | 960 | if (IsRuneword) 961 | { 962 | ClearRunewordData(); 963 | } 964 | 965 | Item socketedItem = Sockets[index]; 966 | socketedItem.IsNotInSocket = true; 967 | socketedItem.IsInSocket = false; 968 | socketedItem.Location = (uint)Item.ItemLocation.Stored; 969 | 970 | Sockets.RemoveAt(index); 971 | SocketsFilled--; 972 | 973 | return socketedItem; 974 | } 975 | 976 | public void ClearRunewordData() 977 | { 978 | IsRuneword = false; 979 | propertiesRuneword.Clear(); 980 | 981 | if (dataEntries.ContainsKey("RunewordId")) 982 | { 983 | dataEntries.Remove("RunewordId"); 984 | } 985 | } 986 | 987 | /// 988 | /// Reads the remaining bits to the remainingBytes array and LAST property 989 | /// 990 | private void ReadRemainingBits() 991 | { 992 | int paddingBitCount = GetPaddingBitCount(); 993 | int remainingByteCount = (int)((int)(bs.RemainingBits) - paddingBitCount) / 8; 994 | 995 | if (remainingByteCount > 0) 996 | { 997 | remainingBytes = bs.ReadReversedBytes(remainingByteCount); 998 | } 999 | 1000 | if ((bs.RemainingBits) - paddingBitCount > 0) 1001 | { 1002 | ReadData("LAST", (int)(bs.RemainingBits) - paddingBitCount); 1003 | } 1004 | } 1005 | 1006 | 1007 | // ADDED by HarpyWar 1008 | private void ReadSockets(byte[] itemBytes) 1009 | { 1010 | if (IsSocketed) 1011 | { 1012 | // set begin position (this is without sockets, so start to read sockets from here) 1013 | var begin = GetItemBytes().Length; 1014 | 1015 | uint failedSockets = 0; 1016 | 1017 | for (int i = 0; i < SocketsFilled; i++) 1018 | { 1019 | Item nextItem = GetNextSocket(itemBytes, ref begin); 1020 | 1021 | if (nextItem == null) 1022 | { 1023 | failedSockets++; 1024 | continue; 1025 | } 1026 | 1027 | Sockets.Add(nextItem); 1028 | } 1029 | } 1030 | } 1031 | private Item GetNextSocket(byte[] itemBytes, ref int begin) 1032 | { 1033 | int itemDataSize = GetNextSocketSize(itemBytes, begin); 1034 | byte[] itemData = new byte[itemDataSize]; 1035 | Array.Copy(itemBytes, begin, itemData, 0, itemDataSize); 1036 | 1037 | return new Item(itemData); 1038 | } 1039 | private int GetNextSocketSize(byte[] itemBytes, int begin) 1040 | { 1041 | for (int i = begin + 2; i < itemBytes.Length - 1; i++) 1042 | { 1043 | // Header of next item record or end of inventory list reached 1044 | if (itemBytes[i] == 'J' && itemBytes[i + 1] == 'M') 1045 | { 1046 | return i - begin; 1047 | } 1048 | } 1049 | 1050 | return itemBytes.Length - begin; 1051 | } 1052 | 1053 | 1054 | 1055 | /// 1056 | /// Reads a value (up to 32 bits) from the BitReader 1057 | /// 1058 | /// Name of the property to create to store the data 1059 | /// Number of bits to read 1060 | private void ReadData(string name, int bitCount) 1061 | { 1062 | ItemData data = new ItemData(); 1063 | 1064 | if (!dataIndicies.ContainsKey(name)) 1065 | { 1066 | throw new Exception("ReadData: Invalid property name specified"); 1067 | } 1068 | 1069 | data.Index = dataIndicies[name]; 1070 | data.BitCount = bitCount; 1071 | data.Value = bs.ReadReversed(bitCount); 1072 | data.Name = name; 1073 | 1074 | dataEntries.Add(data.Name, data); 1075 | } 1076 | 1077 | /// 1078 | /// Reads a sequence of characters from the BitStream 1079 | /// 1080 | /// Name of property to store data in 1081 | /// Number of characters to read 1082 | /// Skip an additional byte after reading the characters 1083 | private void ReadString(string name, int characterCount, int bitsPerChar, bool skipFollowingByte) 1084 | { 1085 | ItemData data = new ItemData(); 1086 | 1087 | if (!dataIndicies.ContainsKey(name)) 1088 | { 1089 | throw new Exception("ReadString: Invalid property name specified"); 1090 | } 1091 | 1092 | data.Index = dataIndicies[name]; 1093 | data.Value = bs.ReadReversedString(characterCount, bitsPerChar); 1094 | data.BitCount = (data.Value as string).Length * bitsPerChar; 1095 | data.Name = name; 1096 | 1097 | if (skipFollowingByte) 1098 | { 1099 | bs.ReadReversedByte(); 1100 | } 1101 | 1102 | dataEntries.Add(data.Name, data); 1103 | } 1104 | 1105 | /// 1106 | /// Reads a null terminated string from the BitStream 1107 | /// 1108 | /// Name of property to store data in 1109 | /// Number of characters to read 1110 | private void ReadString(string name, int bitsPerChar) 1111 | { 1112 | ItemData data = new ItemData(); 1113 | 1114 | if (!dataIndicies.ContainsKey(name)) 1115 | { 1116 | throw new Exception("ReadString: Invalid property name specified"); 1117 | } 1118 | 1119 | data.Index = dataIndicies[name]; 1120 | data.Value = bs.ReadReversedString(bitsPerChar); 1121 | data.BitCount = (data.Value as string).Length * bitsPerChar; 1122 | data.Name = name; 1123 | 1124 | dataEntries.Add(data.Name, data); 1125 | } 1126 | 1127 | /// 1128 | /// Gets an object item property 1129 | /// 1130 | /// Name of property to get 1131 | /// Value of property 1132 | private object GetDataObject(string name) 1133 | { 1134 | if (!dataEntries.ContainsKey(name)) 1135 | { 1136 | return null; 1137 | } 1138 | 1139 | return dataEntries[name].Value; 1140 | } 1141 | 1142 | /// 1143 | /// Gets an unsigned integer item property 1144 | /// 1145 | /// Name of property to get 1146 | /// Value of property 1147 | private uint GetDataValue(string name) 1148 | { 1149 | if (!dataEntries.ContainsKey(name)) 1150 | { 1151 | return 0; 1152 | } 1153 | 1154 | return (uint)dataEntries[name].Value; 1155 | } 1156 | 1157 | /// 1158 | /// Gets a boolean item property 1159 | /// 1160 | /// Name of property to get 1161 | /// Value of property 1162 | private bool GetDataBoolean(string name) 1163 | { 1164 | if (!dataEntries.ContainsKey(name)) 1165 | { 1166 | return false; 1167 | } 1168 | 1169 | if (dataEntries[name].Value.GetType() == typeof(bool)) 1170 | { 1171 | return (bool)dataEntries[name].Value; 1172 | } 1173 | else 1174 | { 1175 | return (uint)dataEntries[name].Value == 1; 1176 | } 1177 | } 1178 | 1179 | /// 1180 | /// Sets an item's property if it exists 1181 | /// 1182 | /// Name of property to set 1183 | /// Value to set property to 1184 | private void SetData(string name, object value) 1185 | { 1186 | if (!dataEntries.ContainsKey(name)) 1187 | { 1188 | return; 1189 | } 1190 | 1191 | dataEntries[name].Value = value; 1192 | OnPropertyChange(name); 1193 | } 1194 | 1195 | /// 1196 | /// Gets the number of bits used for 0 padding to set the remaining bits in last byte to 8 1197 | /// 1198 | /// Number of bits used for padding 1199 | private int GetPaddingBitCount() 1200 | { 1201 | long brPos = bs.Position; 1202 | 1203 | if (!IsSimpleItem) 1204 | { 1205 | bs.Position -= 9; 1206 | while (bs.Position > 0) 1207 | { 1208 | if (bs.ReadReversed(9) == 0x1ff) 1209 | { 1210 | bs.Position = brPos; 1211 | return (int)(bs.RemainingBits); 1212 | } 1213 | bs.Position -= 10; 1214 | } 1215 | } 1216 | 1217 | bs.Position = brPos; 1218 | return 0; 1219 | } 1220 | 1221 | /// 1222 | /// Converts specified item into item format for save file 1223 | /// 1224 | /// Byte representation of item for save file 1225 | public byte[] GetItemBytes() 1226 | { 1227 | BitStream bs = new BitStream(); 1228 | 1229 | bs.WriteReversed('J', 8); 1230 | bs.WriteReversed('M', 8); 1231 | 1232 | var ordered = dataEntries.OrderBy(n => n.Value.Index); 1233 | 1234 | foreach (var item in ordered) 1235 | { 1236 | if (item.Value.Value is string) 1237 | { 1238 | string value = item.Value.Value as string; 1239 | 1240 | if (item.Key == "ItemCode") 1241 | { 1242 | foreach (var ch in value) 1243 | { 1244 | bs.WriteReversed(ch, 8); 1245 | } 1246 | bs.WriteReversed(' ', 8); 1247 | } 1248 | else if (item.Key == "EarName" || item.Key == "PersonalizedName") 1249 | { 1250 | foreach (var ch in value) 1251 | { 1252 | bs.WriteReversed(ch, 7); 1253 | } 1254 | bs.WriteReversed(0, 7); 1255 | } 1256 | else 1257 | { 1258 | throw new Exception("Unknown string type in item data"); 1259 | } 1260 | } 1261 | else if (item.Value.Value is ValueType) 1262 | { 1263 | // LAST key is the very last property added to the item data 1264 | if (item.Key == "LAST") 1265 | continue; 1266 | 1267 | TypeCode valueType = Type.GetTypeCode(item.Value.Value.GetType()); 1268 | 1269 | if (valueType == TypeCode.UInt32) 1270 | { 1271 | uint value = (uint)item.Value.Value; 1272 | 1273 | if (item.Key == "Defense") 1274 | { 1275 | value += (uint)ItemDefs.ItemStatCostsByName["armorclass"].SaveAdd; 1276 | } 1277 | else if (item.Key == "MaxDurability") 1278 | { 1279 | value += (uint)ItemDefs.ItemStatCostsByName["maxdurability"].SaveAdd; 1280 | } 1281 | else if (item.Key == "Durability") 1282 | { 1283 | value += (uint)ItemDefs.ItemStatCostsByName["durability"].SaveAdd; 1284 | } 1285 | 1286 | bs.WriteReversed(value, item.Value.BitCount); 1287 | } 1288 | else if (valueType == TypeCode.Int32) 1289 | { 1290 | bs.WriteReversed((uint)((int)item.Value.Value), item.Value.BitCount); 1291 | } 1292 | else if (valueType == TypeCode.Boolean) 1293 | { 1294 | bs.Write((bool)item.Value.Value); 1295 | } 1296 | else 1297 | { 1298 | throw new Exception("Invalid ValueType!"); 1299 | } 1300 | } 1301 | else 1302 | { 1303 | throw new Exception("Invalid data type in item dataEntries"); 1304 | } 1305 | } 1306 | 1307 | foreach (var item in properties) 1308 | { 1309 | WriteItemProperty(bs, item); 1310 | } 1311 | 1312 | foreach (var item in propertiesSet) 1313 | { 1314 | WriteItemProperty(bs, item); 1315 | } 1316 | 1317 | foreach (var item in propertiesRuneword) 1318 | { 1319 | WriteItemProperty(bs, item); 1320 | } 1321 | 1322 | // Some simple items do have remaining data such as the soulstone 1323 | if (IsSimpleItem) 1324 | { 1325 | //TODO: Enable renaming of ear and personlized names? 1326 | if (remainingBytes != null) 1327 | { 1328 | bs.WriteReversed(remainingBytes); 1329 | } 1330 | 1331 | if (dataEntries.ContainsKey("LAST")) 1332 | { 1333 | var lastEntry = dataEntries["LAST"]; 1334 | bs.WriteReversed((uint)lastEntry.Value, lastEntry.BitCount); 1335 | } 1336 | } 1337 | else 1338 | { 1339 | // Fill the last byte with 0 if it's not already full 1340 | if ((bs.Position % 8) != 0) 1341 | { 1342 | int bitsToAdd = 8 - (int)(bs.Position % 8); 1343 | if (bitsToAdd > 0) 1344 | { 1345 | bs.WriteReversed(0, bitsToAdd); 1346 | } 1347 | } 1348 | } 1349 | 1350 | var itemBytes = bs.ToReversedByteArray(); 1351 | return addSocketsBytes(itemBytes); 1352 | } 1353 | 1354 | /// 1355 | /// Merge item bytes and sockets bytes 1356 | /// 1357 | /// 1358 | private byte[] addSocketsBytes(byte[] itemBytes) 1359 | { 1360 | var bytes = new List(); 1361 | bytes.AddRange(itemBytes); 1362 | 1363 | foreach (var socket in Sockets) 1364 | bytes.AddRange(socket.GetItemBytes()); 1365 | 1366 | return bytes.ToArray(); 1367 | } 1368 | 1369 | /// 1370 | /// Writes an item property to the BitStream 1371 | /// 1372 | /// Bitstream to write property to 1373 | /// Property to write 1374 | private void WriteItemProperty(BitStream bs, PropertyInfo property) 1375 | { 1376 | if (property.ID == 0x1ff) 1377 | { 1378 | bs.WriteReversed(property.ID, 9); 1379 | return; 1380 | } 1381 | 1382 | ItemStatCost statCost = ItemDefs.ItemStatCostsById[property.ID]; 1383 | 1384 | int fixedValue = property.Value + statCost.SaveAdd; 1385 | 1386 | if (!property.IsAdditionalProperty) 1387 | { 1388 | bs.WriteReversed(property.ID, 9); 1389 | } 1390 | 1391 | bs.WriteReversed(fixedValue, statCost.SaveBits); 1392 | 1393 | if (statCost.SaveParamBits > 0) 1394 | { 1395 | bs.WriteReversed(property.ParamValue, statCost.SaveParamBits); 1396 | } 1397 | } 1398 | 1399 | /// 1400 | /// Creates a list of indicies for each data entry name 1401 | /// 1402 | private static void CreateDataIndicies() 1403 | { 1404 | int index = 0; 1405 | 1406 | // Simple data 1407 | dataIndicies.Add("IsEquipped", index++); 1408 | dataIndicies.Add("Unknown0", index++); 1409 | dataIndicies.Add("IsInSocket", index++); 1410 | dataIndicies.Add("IsIdentified", index++); 1411 | dataIndicies.Add("Unknown1", index++); 1412 | dataIndicies.Add("IsSwitchIn", index++); 1413 | dataIndicies.Add("IsSwitchOut", index++); 1414 | dataIndicies.Add("IsBroken", index++); 1415 | dataIndicies.Add("Unknown2", index++); 1416 | dataIndicies.Add("IsPotion", index++); 1417 | dataIndicies.Add("IsSocketed", index++); 1418 | dataIndicies.Add("Unknown3", index++); 1419 | dataIndicies.Add("IsStoreItem", index++); 1420 | dataIndicies.Add("IsNotInSocket", index++); 1421 | dataIndicies.Add("Unknown4", index++); 1422 | dataIndicies.Add("IsEar", index++); 1423 | dataIndicies.Add("IsStarterItem", index++); 1424 | dataIndicies.Add("Unknown5", index++); 1425 | dataIndicies.Add("IsSimpleItem", index++); 1426 | dataIndicies.Add("IsEthereal", index++); 1427 | dataIndicies.Add("Unknown6", index++); 1428 | dataIndicies.Add("IsPersonalized", index++); 1429 | dataIndicies.Add("IsGamble", index++); 1430 | dataIndicies.Add("IsRuneword", index++); 1431 | dataIndicies.Add("Unknown7", index++); 1432 | dataIndicies.Add("Location", index++); 1433 | dataIndicies.Add("PositionOnBody", index++); 1434 | dataIndicies.Add("PositionX", index++); 1435 | dataIndicies.Add("PositionY", index++); 1436 | dataIndicies.Add("StorageId", index++); 1437 | 1438 | // Ear data 1439 | dataIndicies.Add("EarClass", index++); 1440 | dataIndicies.Add("EarLevel", index++); 1441 | dataIndicies.Add("EarName", index++); 1442 | 1443 | // Extended data 1444 | dataIndicies.Add("ItemCode", index++); 1445 | 1446 | // Gold Data 1447 | dataIndicies.Add("UnknownGoldFlag", index++); 1448 | dataIndicies.Add("GoldAmountLarge", index++); 1449 | dataIndicies.Add("GoldAmountSmall", index++); 1450 | 1451 | // Extended data 1452 | dataIndicies.Add("SocketsFilled", index++); 1453 | dataIndicies.Add("Id", index++); 1454 | dataIndicies.Add("Level", index++); 1455 | dataIndicies.Add("Quality", index++); 1456 | dataIndicies.Add("HasGraphic", index++); 1457 | dataIndicies.Add("Graphic", index++); 1458 | dataIndicies.Add("ClassFlag", index++); 1459 | dataIndicies.Add("ClassInfo", index++); 1460 | dataIndicies.Add("InferiorQualityType", index++); 1461 | dataIndicies.Add("SuperiorQualityType", index++); 1462 | dataIndicies.Add("CharmData", index++); 1463 | dataIndicies.Add("SpellId", index++); 1464 | dataIndicies.Add("MonsterId", index++); 1465 | dataIndicies.Add("MagicPrefix", index++); 1466 | dataIndicies.Add("MagicSuffix", index++); 1467 | dataIndicies.Add("PrefixNameId", index++); 1468 | dataIndicies.Add("SuffixNameId", index++); 1469 | dataIndicies.Add("PrefixFlag0", index++); 1470 | dataIndicies.Add("Prefix0", index++); 1471 | dataIndicies.Add("SuffixFlag0", index++); 1472 | dataIndicies.Add("Suffix0", index++); 1473 | dataIndicies.Add("PrefixFlag1", index++); 1474 | dataIndicies.Add("Prefix1", index++); 1475 | dataIndicies.Add("SuffixFlag1", index++); 1476 | dataIndicies.Add("Suffix1", index++); 1477 | dataIndicies.Add("PrefixFlag2", index++); 1478 | dataIndicies.Add("Prefix2", index++); 1479 | dataIndicies.Add("SuffixFlag2", index++); 1480 | dataIndicies.Add("Suffix2", index++); 1481 | dataIndicies.Add("UniqueSetId", index++); 1482 | dataIndicies.Add("RunewordId", index++); 1483 | dataIndicies.Add("PersonalizedName", index++); 1484 | 1485 | // Extended data 1486 | dataIndicies.Add("RandomFlag", index++); 1487 | dataIndicies.Add("Unknown8", index++); 1488 | dataIndicies.Add("Unknown9", index++); 1489 | 1490 | // Extended specific data 1491 | dataIndicies.Add("Defense", index++); 1492 | dataIndicies.Add("MaxDurability", index++); 1493 | dataIndicies.Add("Durability", index++); 1494 | dataIndicies.Add("Quantity", index++); 1495 | dataIndicies.Add("SocketCount", index++); 1496 | 1497 | 1498 | 1499 | dataIndicies.Add("NumberOfSetProperties", index++); 1500 | 1501 | // Properties are managed in lists 1502 | 1503 | // Very last data 1504 | dataIndicies.Add("LAST", int.MaxValue); 1505 | } 1506 | 1507 | public override string ToString() 1508 | { 1509 | StringBuilder sb = new StringBuilder(); 1510 | 1511 | sb.Append(String.Format("{0}", ItemDefs.GetItemDescription(ItemCode))); 1512 | 1513 | if (sockets.Count > 0) 1514 | { 1515 | sb.Append(" ("); 1516 | for (int i = 0; i < sockets.Count; i++) 1517 | { 1518 | sb.Append(ItemDefs.GetItemDescription(sockets[i].ItemCode)); 1519 | 1520 | if (i < sockets.Count - 1) 1521 | { 1522 | sb.Append(", "); 1523 | } 1524 | } 1525 | sb.Append(")"); 1526 | } 1527 | 1528 | return sb.ToString(); 1529 | } 1530 | 1531 | #region INotifyPropertyChanged Members 1532 | 1533 | public event PropertyChangedEventHandler PropertyChanged; 1534 | 1535 | private void OnPropertyChange(string propertyName) 1536 | { 1537 | if (PropertyChanged != null) 1538 | { 1539 | PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 1540 | } 1541 | } 1542 | 1543 | #endregion 1544 | 1545 | public object Clone() 1546 | { 1547 | return this.MemberwiseClone(); 1548 | } 1549 | } 1550 | } 1551 | -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/ItemDefs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.IO; 5 | using System.Collections; 6 | using System.Linq; 7 | 8 | namespace CharacterEditor 9 | { 10 | public class ItemDefs 11 | { 12 | private static Dictionary itemDescriptions = new Dictionary(); 13 | private static Dictionary setDescriptions = new Dictionary(); 14 | private static Dictionary> itemCodeSets = new Dictionary>(); 15 | private static Dictionary itemStatCostsByName = new Dictionary(); 16 | private static Dictionary itemStatCostsById = new Dictionary(); 17 | 18 | /// 19 | /// List of ItemStatCost records based on Name 20 | /// 21 | public static Dictionary ItemStatCostsByName 22 | { 23 | get { return itemStatCostsByName; } 24 | } 25 | 26 | /// 27 | /// List of ItemStatCost records based on ID 28 | /// 29 | public static Dictionary ItemStatCostsById 30 | { 31 | get { return itemStatCostsById; } 32 | } 33 | 34 | // TODO: Temp fix! Get rid of static class and move to singleton based on current resource 35 | // set (es300_r6d, rot_1.a3.1, etc) 36 | internal static void LoadItemDefs() 37 | { 38 | // Temp! 39 | itemDescriptions = new Dictionary(); 40 | setDescriptions = new Dictionary(); 41 | itemCodeSets = new Dictionary>(); 42 | itemStatCostsByName = new Dictionary(); 43 | itemStatCostsById = new Dictionary(); 44 | 45 | List fileContents = Resources.Instance.ReadAllLines("AllItems.txt"); 46 | 47 | foreach (String str in fileContents) 48 | { 49 | string cleanStr = str; 50 | 51 | while (true) 52 | { 53 | // SpecialChar is the color character (followed by 2 other characters), this 54 | // is just stripping it since it looks bad 55 | int specialCharLocation = cleanStr.IndexOf('\xfffd'); 56 | 57 | if (specialCharLocation == -1) 58 | break; 59 | 60 | cleanStr = cleanStr.Remove(specialCharLocation, 3); 61 | } 62 | 63 | itemDescriptions.Add(cleanStr.Substring(0, 3), cleanStr.Substring(4)); 64 | } 65 | 66 | // Just so it's guaranteed that these sections exist 67 | itemCodeSets.Add("weapons", new HashSet()); 68 | itemCodeSets.Add("armor", new HashSet()); 69 | itemCodeSets.Add("stackable", new HashSet()); 70 | itemCodeSets.Add("monsterparts", new HashSet()); 71 | itemCodeSets.Add("scrolltome", new HashSet()); 72 | itemCodeSets.Add("charms", new HashSet()); 73 | 74 | ReadItemCodeSet(itemCodeSets, "ItemGroups.txt"); 75 | 76 | foreach (string str in Resources.Instance.ReadAllLines("Sets.txt")) 77 | { 78 | string itemCode = str.Substring(0, 3); 79 | 80 | if (!setDescriptions.ContainsKey(itemCode)) 81 | setDescriptions.Add(itemCode, str.Substring(4)); 82 | } 83 | 84 | ReadItemStatCost(); 85 | } 86 | 87 | static ItemDefs() 88 | { 89 | //LoadItemDefs(); // TODO: No more static class 90 | } 91 | 92 | /// 93 | /// Read ItemStatCost data 94 | /// 95 | private static void ReadItemStatCost() 96 | { 97 | List statCosts = ItemStatCost.Read(Resources.Instance.OpenResourceText("ItemStatCost.txt")); 98 | 99 | itemStatCostsByName = statCosts.ToDictionary(v => v.Stat, v => v); 100 | itemStatCostsById = statCosts.ToDictionary(v => v.ID, v => v); 101 | } 102 | 103 | /// 104 | /// Returns the name of the specified property ID 105 | /// 106 | /// ID of property 107 | /// Name of property with specified ID 108 | public static string GetPropertyName(int id) 109 | { 110 | if (!itemStatCostsById.ContainsKey(id)) 111 | { 112 | if (id == 0x1ff) 113 | { 114 | return "EOF"; 115 | } 116 | 117 | return "UNKNOWN"; 118 | } 119 | 120 | return itemStatCostsById[id].Stat; 121 | } 122 | 123 | /// 124 | /// Returns the description of the specified item code 125 | /// 126 | /// Item code 127 | /// Item description 128 | public static string GetItemDescription(string itemCode) 129 | { 130 | if (itemDescriptions.ContainsKey(itemCode)) 131 | { 132 | return itemDescriptions[itemCode]; 133 | } 134 | 135 | return "UNKNOWN"; 136 | } 137 | 138 | /// 139 | /// Returns the name of the set item with specified item code 140 | /// 141 | /// Item code 142 | /// Name of set item 143 | public static string GetUniqueSetName(string itemCode) 144 | { 145 | if (!setDescriptions.ContainsKey(itemCode)) 146 | { 147 | return "UNKNOWN SET"; 148 | } 149 | 150 | return setDescriptions[itemCode]; 151 | } 152 | 153 | /// 154 | /// Item is armor 155 | /// 156 | /// Item code 157 | /// True if item is armor 158 | public static bool IsArmor(string itemCode) 159 | { 160 | return itemCodeSets["armor"].Contains(itemCode); 161 | } 162 | 163 | /// 164 | /// Item is a weapon 165 | /// 166 | /// Item code 167 | /// True if item is a weapon 168 | public static bool IsWeapon(string itemCode) 169 | { 170 | return itemCodeSets["weapons"].Contains(itemCode); 171 | } 172 | 173 | /// 174 | /// Item is stackable 175 | /// 176 | /// Item code 177 | /// True if item is stackable 178 | public static bool IsStackable(string itemCode) 179 | { 180 | return itemCodeSets["stackable"].Contains(itemCode); 181 | } 182 | 183 | /// 184 | /// Item is a monster part 185 | /// 186 | /// Item code 187 | /// True if item is a monster part 188 | public static bool IsMonsterPart(string itemCode) 189 | { 190 | return itemCodeSets["monsterparts"].Contains(itemCode); 191 | } 192 | 193 | /// 194 | /// Item is a scroll or tome 195 | /// 196 | /// Item code 197 | /// True if item is a scroll or tome 198 | public static bool IsScrollOrTome(string itemCode) 199 | { 200 | return itemCodeSets["scrolltome"].Contains(itemCode); 201 | } 202 | 203 | /// 204 | /// Item is a charm 205 | /// 206 | /// Item code 207 | /// True if item is a charm 208 | public static bool IsCharm(string itemCode) 209 | { 210 | return itemCodeSets["charms"].Contains(itemCode); 211 | } 212 | 213 | /// 214 | /// Reads multiple sets of item codes from disk. Each set has a unique section name defined in brackets (eg. [sectionName]). 215 | /// If multiple identical section names are exist, then the data for these sections are combined into the same set. 216 | /// 217 | /// Table of sets to store data 218 | /// Path containing sets of item codes 219 | /// 220 | /// 221 | /// [armor] 222 | /// cap Cap/hat 223 | /// skp Skull Cap 224 | /// 225 | /// [weapons] 226 | /// hax Hand Axe 227 | /// axe Axe 228 | /// 229 | /// 230 | public static void ReadItemCodeSet(Dictionary> itemCodeSets, string filePath) 231 | { 232 | List lines = Resources.Instance.ReadAllLines(filePath); 233 | HashSet currentSection = null; 234 | 235 | foreach (var line in lines) 236 | { 237 | if (line.Length <= 2) 238 | { 239 | continue; 240 | } 241 | 242 | if (line[0] == '[' && line[line.Length - 1] == ']') 243 | { 244 | string sectionName = line.Substring(1, line.Length - 2).ToLower(); 245 | 246 | if (!itemCodeSets.ContainsKey(sectionName)) 247 | { 248 | itemCodeSets.Add(sectionName, new HashSet()); 249 | } 250 | 251 | currentSection = itemCodeSets[sectionName]; 252 | } 253 | else if (currentSection != null) 254 | { 255 | string itemCode = line.Substring(0, 3).ToLower(); 256 | 257 | currentSection.Add(itemCode); 258 | } 259 | } 260 | } 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/ItemStatCost.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | using System.Reflection; 7 | 8 | namespace CharacterEditor 9 | { 10 | public class ItemStatCost 11 | { 12 | public string Stat { get; set; } 13 | public int ID { get; set; } 14 | public bool SendOther { get; set; } 15 | public bool Signed { get; set; } 16 | public int SendBits { get; set; } 17 | public int SendParamBits { get; set; } 18 | public bool UpdateAnimRate { get; set; } 19 | public bool Saved { get; set; } 20 | public bool CSvSigned { get; set; } 21 | public int CSvBits { get; set; } 22 | public int CSvParam { get; set; } 23 | public bool FCallback { get; set; } 24 | public bool FMin { get; set; } 25 | public int MinAccr { get; set; } 26 | public int Encode { get; set; } 27 | public int Add { get; set; } 28 | public int Multiply { get; set; } 29 | public int Divide { get; set; } 30 | public int ValShift { get; set; } 31 | public int OldSaveBits { get; set; } 32 | public int OldSaveAdd { get; set; } 33 | public int SaveBits { get; set; } 34 | public int SaveAdd { get; set; } 35 | public int SaveParamBits { get; set; } 36 | public int Keepzero { get; set; } 37 | public int Op { get; set; } 38 | public int OpParam { get; set; } 39 | public string OpBase { get; set; } 40 | public string OpStat1 { get; set; } 41 | public string OpStat2 { get; set; } 42 | public string OpStat3 { get; set; } 43 | public bool Direct { get; set; } 44 | public string Maxstat { get; set; } 45 | public bool Itemspecific { get; set; } 46 | public bool Damagerelated { get; set; } 47 | public string Itemevent1 { get; set; } 48 | public int Itemeventfunc1 { get; set; } 49 | public string Itemevent2 { get; set; } 50 | public int Itemeventfunc2 { get; set; } 51 | public int Descpriority { get; set; } 52 | public int Descfunc { get; set; } 53 | public int Descval { get; set; } 54 | public string Descstrpos { get; set; } 55 | public string Descstrneg { get; set; } 56 | public string Descstr2 { get; set; } 57 | public int Dgrp { get; set; } 58 | public int Dgrpfunc { get; set; } 59 | public int Dgrpval { get; set; } 60 | public string Dgrpstrpos { get; set; } 61 | public string Dgrpstrneg { get; set; } 62 | public string Dgrpstr2 { get; set; } 63 | public int Stuff { get; set; } 64 | public bool Eol { get; set; } 65 | 66 | public ItemStatCost() 67 | { 68 | 69 | } 70 | 71 | public ItemStatCost(string[] data) 72 | { 73 | int i = 0; 74 | 75 | // TODO: Horrible cut+paste+replace mess 76 | Stat = (string)data[i++]; 77 | ID = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 78 | SendOther = (bool)StringUtils.ConvertFromString(data[i++], typeof(bool)); 79 | Signed = (bool)StringUtils.ConvertFromString(data[i++], typeof(bool)); 80 | SendBits = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 81 | SendParamBits = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 82 | UpdateAnimRate = (bool)StringUtils.ConvertFromString(data[i++], typeof(bool)); 83 | Saved = (bool)StringUtils.ConvertFromString(data[i++], typeof(bool)); 84 | CSvSigned = (bool)StringUtils.ConvertFromString(data[i++], typeof(bool)); 85 | CSvBits = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 86 | CSvParam = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 87 | FCallback = (bool)StringUtils.ConvertFromString(data[i++], typeof(bool)); 88 | FMin = (bool)StringUtils.ConvertFromString(data[i++], typeof(bool)); 89 | MinAccr = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 90 | Encode = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 91 | Add = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 92 | Multiply = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 93 | Divide = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 94 | ValShift = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 95 | OldSaveBits = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 96 | OldSaveAdd = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 97 | SaveBits = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 98 | SaveAdd = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 99 | SaveParamBits = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 100 | Keepzero = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 101 | Op = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 102 | OpParam = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 103 | OpBase = (string)data[i++]; 104 | OpStat1 = (string)data[i++]; 105 | OpStat2 = (string)data[i++]; 106 | OpStat3 = (string)data[i++]; 107 | Direct = (bool)StringUtils.ConvertFromString(data[i++], typeof(bool)); 108 | Maxstat = (string)data[i++]; 109 | Itemspecific = (bool)StringUtils.ConvertFromString(data[i++], typeof(bool)); 110 | Damagerelated = (bool)StringUtils.ConvertFromString(data[i++], typeof(bool)); 111 | Itemevent1 = (string)data[i++]; 112 | Itemeventfunc1 = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 113 | Itemevent2 = (string)data[i++]; 114 | Itemeventfunc2 = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 115 | Descpriority = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 116 | Descfunc = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 117 | Descval = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 118 | Descstrpos = (string)data[i++]; 119 | Descstrneg = (string)data[i++]; 120 | Descstr2 = (string)data[i++]; 121 | Dgrp = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 122 | Dgrpfunc = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 123 | Dgrpval = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 124 | Dgrpstrpos = (string)data[i++]; 125 | Dgrpstrneg = (string)data[i++]; 126 | Dgrpstr2 = (string)data[i++]; 127 | Stuff = (int)StringUtils.ConvertFromString(data[i++], typeof(int)); 128 | Eol = (bool)StringUtils.ConvertFromString(data[i++], typeof(bool)); 129 | } 130 | 131 | public static List Read(StreamReader stream) 132 | { 133 | List statCosts = new List(); 134 | PropertyInfo[] properties = typeof(ItemStatCost).GetProperties(); 135 | 136 | using (stream) 137 | { 138 | stream.ReadLine(); 139 | 140 | while (!stream.EndOfStream) 141 | { 142 | string currentLine = stream.ReadLine(); 143 | string[] splitLine = currentLine.Split('\t'); 144 | 145 | 146 | statCosts.Add(new ItemStatCost(splitLine)); 147 | } 148 | } 149 | 150 | return statCosts; 151 | } 152 | 153 | public override string ToString() 154 | { 155 | return string.Format("{0} - {1}", ID, Stat); 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("CharacterEditor")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("CharacterEditor")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2010")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("4bd9a9cf-c9f9-4c01-bfb6-d4091e5197ac")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/Resources.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | using System.Resources; 7 | using System.Threading; 8 | using System.Collections; 9 | using System.Text.RegularExpressions; 10 | using System.Reflection; 11 | using System.Diagnostics; 12 | 13 | namespace CharacterEditor 14 | { 15 | public class Resources 16 | { 17 | private static readonly Resources instance = new Resources(); 18 | public static Resources Instance 19 | { 20 | get { return instance; } 21 | } 22 | 23 | private Resources() 24 | { 25 | 26 | } 27 | 28 | private string resourceSet; 29 | public string ResourceSet 30 | { 31 | get 32 | { 33 | return resourceSet; 34 | } 35 | set 36 | { 37 | if (IsValidResourceSet(value)) 38 | { 39 | resourceSet = value; 40 | } 41 | } 42 | } 43 | 44 | /// 45 | /// Obtains a list of all available resource sets. Resources must be marked as Resource when building project. 46 | /// 47 | /// Resources/es300_R6D/AllItems.txt -> es300_R6D 48 | /// 49 | /// http://blogs.microsoft.co.il/blogs/alex_golesh/archive/2009/11/13/silverlight-tip-enumerating-embedded-resources.aspx 50 | private static List GetResourceSetsSilverlight() 51 | { 52 | List resourceNames = new List(); 53 | Assembly currentAssembly = Assembly.GetExecutingAssembly(); 54 | string[] resources = currentAssembly.GetManifestResourceNames(); 55 | 56 | foreach (var resource in resources) 57 | { 58 | ResourceManager rm = new ResourceManager(resource.Replace(".resources", ""), currentAssembly); 59 | rm.GetStream("app.xaml"); // can't seem to GetResourceSet without getting a stream -> see above link where code comes from 60 | ResourceSet rs = rm.GetResourceSet(Thread.CurrentThread.CurrentCulture, false, true); 61 | 62 | foreach (DictionaryEntry item in rs) 63 | { 64 | string resourceName = (string)item.Key; 65 | 66 | // "Resources/es300_R6D/AllItems.txt" 67 | Regex regex = new Regex(@"^(?[^/]+)/(?[^/]+)/(?.*)$"); 68 | 69 | if (regex.IsMatch(resourceName)) 70 | { 71 | Match match = regex.Match(resourceName); 72 | 73 | if (match.Groups["file"].Value.ToLower() == "allitems.txt") 74 | { 75 | resourceNames.Add(match.Groups["set"].Value); 76 | } 77 | } 78 | } 79 | 80 | if (resourceNames.Count > 0) 81 | { 82 | return resourceNames; 83 | } 84 | } 85 | 86 | return resourceNames; 87 | } 88 | 89 | #if !SILVERLIGHT 90 | private static List GetResourceSetsDesktop() 91 | { 92 | List resourceNames = new List(); 93 | string[] directories = Directory.GetDirectories("Resources/"); 94 | 95 | foreach (var dir in directories) 96 | { 97 | if (File.Exists(dir + "/" + "allitems.txt")) 98 | { 99 | resourceNames.Add(Path.GetFileName(dir)); 100 | } 101 | } 102 | 103 | return resourceNames; 104 | } 105 | #endif 106 | 107 | public static string CurrentDirectory 108 | { 109 | get 110 | { 111 | return Directory.GetCurrentDirectory(); 112 | } 113 | set 114 | { 115 | Directory.SetCurrentDirectory(value); 116 | } 117 | } 118 | 119 | public static List GetResourceSets() 120 | { 121 | #if SILVERLIGHT 122 | return GetResourceSetsSilverlight(); 123 | #else 124 | return GetResourceSetsDesktop(); 125 | #endif 126 | } 127 | 128 | 129 | internal static bool IsValidResourceSet(string value) 130 | { 131 | #if SILVERLIGHT 132 | // Pretty lengthy process, but is rarely called 133 | return GetResourceSetsSilverlight().Contains(value.ToLower()); 134 | #else 135 | return Directory.Exists("Resources/" + value); 136 | #endif 137 | } 138 | 139 | public Stream OpenResource(string resourceName) 140 | { 141 | string resourcePath = "Resources/" + resourceSet + "/" + resourceName; 142 | 143 | return ResourceUtils.OpenResource("CharacterEditor.Silverlight", resourcePath); 144 | } 145 | 146 | public StreamReader OpenResourceText(string resourceName) 147 | { 148 | string resourcePath = "Resources/" + resourceSet + "/" + resourceName; 149 | 150 | return ResourceUtils.OpenResourceText("CharacterEditor.Silverlight", resourcePath); 151 | } 152 | 153 | public List ReadAllLines(string resourceName) 154 | { 155 | string resourcePath = "Resources/" + resourceSet + "/" + resourceName; 156 | 157 | return ResourceUtils.ReadAllLines("CharacterEditor.Silverlight", resourcePath); 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/Resources/1.13c/AllItems.txt: -------------------------------------------------------------------------------- 1 | elx elixir 2 | hpo Healing Potion 3 | mpo Mana Potion 4 | hpf Full Healing Potion 5 | mpf Full Mana Potion 6 | vps Stamina Potion 7 | yps Antidote Potion 8 | rvs Rejuv Potion 9 | rvl Full Rejuv Potion 10 | wms Thawing Potion 11 | tbk Town Portal Book 12 | ibk Identify Book 13 | amu amulet 14 | vip viper amulet 15 | rin ring 16 | gld gold 17 | bks Bark Scroll 18 | bkd deciphered Bark Scroll 19 | aqv Arrows 20 | tch Torch 21 | cqv Bolts 22 | tsc Town Portal Scroll 23 | isc Identify Scroll 24 | hrt Heart 25 | brz Brain 26 | jaw Jawbone 27 | eyz Eye 28 | hrn Horn 29 | tal Tail 30 | flg Flag 31 | fng Fang 32 | qll Quill 33 | sol Soul 34 | scz Scalp 35 | spe Spleen 36 | key Skeleton Key 37 | luv Mephisto Key 38 | xyz scroll of self resurrect 39 | j34 jade figurine 40 | g34 gold bird 41 | bbb lam esen's tome 42 | box Horadric Cube 43 | tr1 Scroll of Horadric Quest Info 44 | mss Mephisto SoulStone 45 | ass Book of Skill 46 | qey KhalimEye 47 | qhr KhalimHeart 48 | qbr KhalimBrain 49 | ear Player Ear 50 | gcv Chipped Amethyst 51 | gfv Flawed Amethyst 52 | gsv Amethyst 53 | gzv Flawless Amethyst 54 | gpv Perfect Amethyst 55 | gcy Chipped Topaz 56 | gfy Flawed Topaz 57 | gsy Topaz 58 | gly Flawless Topaz 59 | gpy Perfect Topaz 60 | gcb Chipped Saphire 61 | gfb Flawed Saphire 62 | gsb Saphire 63 | glb Flawless Saphire 64 | gpb Perfect Saphire 65 | gcg Chipped Emerald 66 | gfg Flawed Emerald 67 | gsg Emerald 68 | glg Flawless Emerald 69 | gpg Perfect Emerald 70 | gcr Chipped Ruby 71 | gfr Flawed Ruby 72 | gsr Ruby 73 | glr Flawless Ruby 74 | gpr Perfect Ruby 75 | gcw Chipped Diamond 76 | gfw Flawed Diamond 77 | gsw Diamond 78 | glw Flawless Diamond 79 | gpw Perfect Diamond 80 | hp1 Lesser Healing Potion 81 | hp2 Light Healing Potion 82 | hp3 Healing Potion 83 | hp4 Strong Healing Potion 84 | hp5 Greater Healing Potion 85 | mp1 Lesser Mana Potion 86 | mp2 Light Mana Potion 87 | mp3 Mana Potion 88 | mp4 Strong Mana Potion 89 | mp5 Greater Mana Potion 90 | skc Chipped Skull 91 | skf Flawed Skull 92 | sku Skull 93 | skl Flawless Skull 94 | skz Perfect Skull 95 | hrb herb 96 | cm1 Charm Small 97 | cm2 Charm Medium 98 | cm3 Charm Large 99 | rps Small Red Potion 100 | rpl Large Red Potion 101 | bps Small Blue Potion 102 | bpl Large Blue Potion 103 | r01 El 104 | r02 Eld 105 | r03 Tir 106 | r04 Nef 107 | r05 Eth 108 | r06 Ith 109 | r07 Tal 110 | r08 Ral 111 | r09 Ort 112 | r10 Thul 113 | r11 Amn 114 | r12 Sol 115 | r13 Shael 116 | r14 Dol 117 | r15 Hel 118 | r16 Io 119 | r17 Lum 120 | r18 Ko 121 | r19 Fal 122 | r20 Lem 123 | r21 Pul 124 | r22 Um 125 | r23 Mal 126 | r24 Ist 127 | r25 Gul 128 | r26 Vex 129 | r27 Ohm 130 | r28 Lo 131 | r29 Sur 132 | r30 Ber 133 | r31 Jah 134 | r32 Cham 135 | r33 Zod 136 | jew Jewel 137 | ice ice 138 | 0sc Scroll 139 | tr2 Scroll of Malah - Boost Resistances 140 | pk1 Pandemonium Key 1 141 | pk2 Pandemonium Key 2 142 | pk3 Pandemonium Key 3 143 | dhn Diablo's Horn 144 | bey Baal's Eye 145 | mbr Mephisto's Brain 146 | toa Token of Absolution 147 | tes Twisted Essence of Suffering 148 | ceh Charged Essense of Hatred 149 | bet Burning Essence of Terror 150 | fed Festering Essence of Destruction 151 | std Standard 152 | cap Cap/hat 153 | skp Skull Cap 154 | hlm Helm 155 | fhl Full Helm 156 | ghm Great Helm 157 | crn Crown 158 | msk Mask 159 | qui Quilted Armor 160 | lea Leather Armor 161 | hla Hard Leather Armor 162 | stu Studded Leather 163 | rng Ring Mail 164 | scl Scale Mail 165 | chn Chain Mail 166 | brs Breast Plate 167 | spl Splint Mail 168 | plt Plate Mail 169 | fld Field Plate 170 | gth Gothic Plate 171 | ful Full Plate Mail 172 | aar Ancient Armor 173 | ltp Light Plate 174 | buc Buckler 175 | sml Small Shield 176 | lrg Large Shield 177 | kit Kite Shield 178 | tow Tower Shield 179 | gts Gothic Shield 180 | lgl Gloves(L) 181 | vgl Heavy Gloves 182 | mgl Bracers(M) 183 | tgl Light Gauntlets 184 | hgl Gaunlets(H) 185 | lbt Leather Boots 186 | vbt Heavy Boots 187 | mbt Chain Boots 188 | tbt Light Plate Boots 189 | hbt Plate Boots 190 | lbl Sash(L) 191 | vbl Light Belt 192 | mbl Belt(M) 193 | tbl Heavy Belt 194 | hbl Girdle(H) 195 | bhm Bone Helm 196 | bsh Bone Shield 197 | spk Spiked Shield 198 | xap War Hat 199 | xkp Sallet 200 | xlm Casque 201 | xhl Basinet 202 | xhm Winged Helm 203 | xrn Grand Crown 204 | xsk Death Mask 205 | xui Ghost Armor 206 | xea Serpentskin Armor 207 | xla Demonhide Armor 208 | xtu Trellised Armor 209 | xng Linked Mail 210 | xcl Tigulated Mail 211 | xhn Mesh Armor 212 | xrs Cuirass 213 | xpl Russet Armor 214 | xlt Templar Coat 215 | xld Sharktooth Armor 216 | xth Embossed Plate 217 | xul Chaos Armor 218 | xar Ornate Armor 219 | xtp Mage Plate 220 | xuc Defender 221 | xml Round Shield 222 | xrg Scutum 223 | xit Dragon Shield 224 | xow Pavise 225 | xts Ancient Shield 226 | xlg Demonhide Gloves 227 | xvg Sharkskin Gloves 228 | xmg Heavy Bracers 229 | xtg Battle Gauntlets 230 | xhg War Gauntlets 231 | xlb Demonhide Boots 232 | xvb Sharkskin Boots 233 | xmb Mesh Boots 234 | xtb Battle Boots 235 | xhb War Boots 236 | zlb Demonhide Sash 237 | zvb Sharkskin Belt 238 | zmb Mesh Belt 239 | ztb Battle Belt 240 | zhb War Belt 241 | xh9 Grim Helm 242 | xsh Grim Shield 243 | xpk Barbed Shield 244 | dr1 Wolf Head 245 | dr2 Hawk Helm 246 | dr3 Antlers 247 | dr4 Falcon Mask 248 | dr5 Spirit Mask 249 | ba1 Jawbone Cap 250 | ba2 Fanged Helm 251 | ba3 Horned Helm 252 | ba4 Assault Helmet 253 | ba5 Avenger Guard 254 | pa1 Targe 255 | pa2 Rondache 256 | pa3 Heraldic Shield 257 | pa4 Aerin Shield 258 | pa5 Crown Shield 259 | ne1 Preserved Head 260 | ne2 Zombie Head 261 | ne3 Unraveller Head 262 | ne4 Gargoyle Head 263 | ne5 Demon Head 264 | ci0 Circlet 265 | ci1 Coronet 266 | ci2 Tiara 267 | ci3 Diadem 268 | uap Shako 269 | ukp Hydraskull 270 | ulm Armet 271 | uhl Giant Conch 272 | uhm Spired Helm 273 | urn Corona 274 | usk Demonhead 275 | uui Dusk Shroud 276 | uea Wyrmhide 277 | ula Scarab Husk 278 | utu Wire Fleece 279 | ung Diamond Mail 280 | ucl Loricated Mail 281 | uhn Boneweave 282 | urs Great Hauberk 283 | upl Balrog Skin 284 | ult Hellforged Plate 285 | uld Kraken Shell 286 | uth Lacquered Plate 287 | uul Shadow Plate 288 | uar Sacred Armor 289 | utp Archon Plate 290 | uuc Heater 291 | uml Luna 292 | urg Hyperion 293 | uit Monarch 294 | uow Aegis 295 | uts Ward 296 | ulg Bramble Mitts 297 | uvg Vampirebone Gloves 298 | umg Vambraces 299 | utg Crusader Gauntlets 300 | uhg Ogre Gauntlets 301 | ulb Wyrmhide Boots 302 | uvb Scarabshell Boots 303 | umb Boneweave Boots 304 | utb Mirrored Boots 305 | uhb Myrmidon Greaves 306 | ulc Spiderweb Sash 307 | uvc Vampirefang Belt 308 | umc Mithril Coil 309 | utc Troll Belt 310 | uhc Colossus Girdle 311 | uh9 Bone Visage 312 | ush Troll Nest 313 | upk Blade Barrier 314 | dr6 Alpha Helm 315 | dr7 Griffon Headress 316 | dr8 Hunter's Guise 317 | dr9 Sacred Feathers 318 | dra Totemic Mask 319 | ba6 Jawbone Visor 320 | ba7 Lion Helm 321 | ba8 Rage Mask 322 | ba9 Savage Helmet 323 | baa Slayer Guard 324 | pa6 Akaran Targe 325 | pa7 Akaran Rondache 326 | pa8 Protector Shield 327 | pa9 Guilded Shield 328 | paa Royal Shield 329 | ne6 Mummified Trophy 330 | ne7 Fetish Trophy 331 | ne8 Sexton Trophy 332 | ne9 Cantor Trophy 333 | nea Heirophant Trophy 334 | drb Blood Spirt 335 | drc Sun Spirit 336 | drd Earth Spirit 337 | dre Sky Spirit 338 | drf Dream Spirit 339 | bab Carnage Helm 340 | bac Fury Visor 341 | bad Destroyer Helm 342 | bae Conquerer Crown 343 | baf Guardian Crown 344 | pab Sacred Targe 345 | pac Sacred Rondache 346 | pad Ancient Shield 347 | pae Zakarum Shield 348 | paf Vortex Shield 349 | neb Minion Skull 350 | neg Hellspawn Skull 351 | ned Overseer Skull 352 | nee Succubae Skull 353 | nef Bloodlord Skull 354 | hax Hand Axe 355 | axe Axe 356 | 2ax Double Axe 357 | mpi Military Pick 358 | wax War Axe 359 | lax Large Axe 360 | bax Broad Axe 361 | btx Battle Axe 362 | gax Great Axe 363 | gix Giant Axe 364 | wnd Wand 365 | ywn Yew Wand 366 | bwn Bone Wand 367 | gwn Grim Wand 368 | clb Club 369 | scp Scepter 370 | gsc Grand Scepter 371 | wsp War Scepter 372 | spc Spiked Club 373 | mac Mace 374 | mst Morning Star 375 | fla Flail 376 | whm War Hammer 377 | mau Maul 378 | gma Great Maul 379 | ssd Short Sword 380 | scm Scimitar 381 | sbr Saber 382 | flc Falchion 383 | crs Crystal Sword 384 | bsd Broad Sword 385 | lsd Long Sword 386 | wsd War Sword 387 | 2hs Two-Handed Sword 388 | clm Claymore 389 | gis Giant Sword 390 | bsw Bastard Sword 391 | flb Flamberge 392 | gsd Great Sword 393 | dgr Dagger 394 | dir Dirk 395 | kri Kriss 396 | bld Blade 397 | tkf Throwing Knife 398 | tax Throwing Axe 399 | bkf Balanced Knife 400 | bal Balanced Axe 401 | jav Javelin 402 | pil Pilum 403 | ssp Short Spear 404 | glv Glaive 405 | tsp Throwing Spear 406 | spr Spear 407 | tri Trident 408 | brn Brandistock 409 | spt Spetum 410 | pik Pike 411 | bar Bardiche 412 | vou Voulge 413 | scy Scythe 414 | pax Poleaxe 415 | hal Halberd 416 | wsc War Scythe 417 | sst Short Staff 418 | lst Long Staff 419 | cst Gnarled Staff 420 | bst Battle Staff 421 | wst War Staff 422 | sbw Short Bow 423 | hbw Hunter's Bow 424 | lbw Long Bow 425 | cbw Composite Bow 426 | sbb Short Battle Bow 427 | lbb Long Battle Bow 428 | swb Short War Bow 429 | lwb Long War Bow 430 | lxb Light Crossbow 431 | mxb Crossbow 432 | hxb Heavy Crossbow 433 | rxb Repeating Crossbow 434 | gps Rancid Gas Potion 435 | ops Oil Potion 436 | gpm Choking Gas Potion 437 | opm Exploding Potion 438 | gpl Strangling Gas Potion 439 | opl Fulminating Potion 440 | d33 decoy dagger 441 | g33 Gidbinn 442 | leg Wirt's Leg 443 | hdm Horadric Malus 444 | hfh Hellforge Hammer 445 | hst Horadric Staff 446 | msf Staff of the Kings 447 | 9ha Hatchet 448 | 9ax Cleaver 449 | 92a Twin Axe 450 | 9mp Crowbill 451 | 9wa Naga 452 | 9la Military Axe 453 | 9ba Bearded Axe 454 | 9bt Tabar 455 | 9ga Gothic Axe 456 | 9gi Ancient Axe 457 | 9wn Burnt Wand 458 | 9yw Petrified Wand 459 | 9bw Tomb Wand 460 | 9gw Grave Wand 461 | 9cl Cudgel 462 | 9sc Rune Scepter 463 | 9qs Holy Water Sprinkler 464 | 9ws Divine Scepter 465 | 9sp Barbed Club 466 | 9ma Flanged Mace 467 | 9mt Jagged Star 468 | 9fl Knout 469 | 9wh Battle Hammer 470 | 9m9 War Club 471 | 9gm Martel de Fer 472 | 9ss Gladius 473 | 9sm Cutlass 474 | 9sb Shamshir 475 | 9fc Tulwar 476 | 9cr Dimensional Blade 477 | 9bs Battle Sword 478 | 9ls Rune Sword 479 | 9wd Ancient Sword 480 | 92h Espadon 481 | 9cm Dacian Falx 482 | 9gs Tusk Sword 483 | 9b9 Gothic Sword 484 | 9fb Zweihander 485 | 9gd Executioner Sword 486 | 9dg Poignard 487 | 9di Rondel 488 | 9kr Cinquedeas 489 | 9bl Stilleto 490 | 9tk Battle Dart 491 | 9ta Francisca 492 | 9bk War Dart 493 | 9b8 Hurlbat 494 | 9ja War Javelin 495 | 9pi Great Pilum 496 | 9s9 Simbilan 497 | 9gl Spiculum 498 | 9ts Harpoon 499 | 9sr War Spear 500 | 9tr Fuscina 501 | 9br War Fork 502 | 9st Yari 503 | 9p9 Lance 504 | 9b7 Lochaber Axe 505 | 9vo Bill 506 | 9s8 Battle Scythe 507 | 9pa Partizan 508 | 9h9 Bec-de-Corbin 509 | 9wc Grim Scythe 510 | 8ss Jo Staff 511 | 8ls Quarterstaff 512 | 8cs Cedar Staff 513 | 8bs Gothic Staff 514 | 8ws Rune Staff 515 | 8sb Edge Bow 516 | 8hb Razor Bow 517 | 8lb Cedar Bow 518 | 8cb Double Bow 519 | 8s8 Short Siege Bow 520 | 8l8 Long Siege Bow 521 | 8sw Rune Bow 522 | 8lw Gothic Bow 523 | 8lx Arbalest 524 | 8mx Siege Crossbow 525 | 8hx Balista 526 | 8rx Chu-Ko-Nu 527 | qf1 KhalimFlail 528 | qf2 SuperKhalimFlail 529 | ktr Katar 530 | wrb Wrist Blade 531 | axf Hatchet Hands 532 | ces Cestus 533 | clw Claws 534 | btl Blade Talons 535 | skr Scissors Katar 536 | 9ar Quhab 537 | 9wb Wrist Spike 538 | 9xf Fascia 539 | 9cs Hand Scythe 540 | 9lw Greater Claws 541 | 9tw Greater Talons 542 | 9qr Scissors Quhab 543 | 7ar Suwayyah 544 | 7wb Wrist Sword 545 | 7xf War Fist 546 | 7cs Battle Cestus 547 | 7lw Feral Claws 548 | 7tw Runic Talons 549 | 7qr Scissors Suwayyah 550 | 7ha Tomahawk 551 | 7ax Small Crescent 552 | 72a Ettin Axe 553 | 7mp War Spike 554 | 7wa Berserker Axe 555 | 7la Feral Axe 556 | 7ba Silver Edged Axe 557 | 7bt Decapitator 558 | 7ga Champion Axe 559 | 7gi Glorious Axe 560 | 7wn Polished Wand 561 | 7yw Ghost Wand 562 | 7bw Lich Wand 563 | 7gw Unearthed Wand 564 | 7cl Truncheon 565 | 7sc Mighty Scepter 566 | 7qs Seraph Rod 567 | 7ws Caduceus 568 | 7sp Tyrant Club 569 | 7ma Reinforced Mace 570 | 7mt Devil Star 571 | 7fl Scourge 572 | 7wh Legendary Mallet 573 | 7m7 Ogre Maul 574 | 7gm Thunder Maul 575 | 7ss Falcata 576 | 7sm Ataghan 577 | 7sb Elegant Blade 578 | 7fc Hydra Edge 579 | 7cr Phase Blade 580 | 7bs Conquest Sword 581 | 7ls Cryptic Sword 582 | 7wd Mythical Sword 583 | 72h Legend Sword 584 | 7cm Highland Blade 585 | 7gs Balrog Blade 586 | 7b7 Champion Sword 587 | 7fb Colossal Sword 588 | 7gd Colossus Blade 589 | 7dg Bone Knife 590 | 7di Mithral Point 591 | 7kr Fanged Knife 592 | 7bl Legend Spike 593 | 7tk Flying Knife 594 | 7ta Flying Axe 595 | 7bk Winged Knife 596 | 7b8 Winged Axe 597 | 7ja Hyperion Javelin 598 | 7pi Stygian Pilum 599 | 7s7 Balrog Spear 600 | 7gl Ghost Glaive 601 | 7ts Winged Harpoon 602 | 7sr Hyperion Spear 603 | 7tr Stygian Pike 604 | 7br Mancatcher 605 | 7st Ghost Spear 606 | 7p7 War Pike 607 | 7o7 Ogre Axe 608 | 7vo Colossus Voulge 609 | 7s8 Thresher 610 | 7pa Cryptic Axe 611 | 7h7 Great Poleaxe 612 | 7wc Giant Thresher 613 | 6ss Walking Stick 614 | 6ls Stalagmite 615 | 6cs Elder Staff 616 | 6bs Shillelah 617 | 6ws Archon Staff 618 | 6sb Spider Bow 619 | 6hb Blade Bow 620 | 6lb Shadow Bow 621 | 6cb Great Bow 622 | 6s7 Diamond Bow 623 | 6l7 Crusader Bow 624 | 6sw Ward Bow 625 | 6lw Hydra Bow 626 | 6lx Pellet Bow 627 | 6mx Gorgon Crossbow 628 | 6hx Colossus Crossbow 629 | 6rx Demon Crossbow 630 | ob1 Eagle Orb 631 | ob2 Sacred Globe 632 | ob3 Smoked Sphere 633 | ob4 Clasped Orb 634 | ob5 Dragon Stone 635 | am1 Stag Bow 636 | am2 Reflex Bow 637 | am3 Maiden Spear 638 | am4 Maiden Pike 639 | am5 Maiden Javelin 640 | ob6 Glowing Orb 641 | ob7 Crystalline Globe 642 | ob8 Cloudy Sphere 643 | ob9 Sparkling Ball 644 | oba Swirling Crystal 645 | am6 Ashwood Bow 646 | am7 Ceremonial Bow 647 | am8 Ceremonial Spear 648 | am9 Ceremonial Pike 649 | ama Ceremonial Javelin 650 | obb Heavenly Stone 651 | obc Eldritch Orb 652 | obd Demon Heart 653 | obe Vortex Orb 654 | obf Dimensional Shard 655 | amb Matriarchal Bow 656 | amc Grand Matron Bow 657 | amd Matriarchal Spear 658 | ame Matriarchal Pike 659 | amf MatriarchalJavelin -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/Resources/1.13c/ItemGroups.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarpyWar/d2s-character-editor/6304f41ca3b5973052ea86ace8f06d67824471b9/tools/esCharView/CharacterEditor/Resources/1.13c/ItemGroups.txt -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/Resources/1.13c/ItemStatCost.txt: -------------------------------------------------------------------------------- 1 | Stat ID Send Other Signed Send Bits Send Param Bits UpdateAnimRate Saved CSvSigned CSvBits CSvParam fCallback fMin MinAccr Encode Add Multiply Divide ValShift 1.09-Save Bits 1.09-Save Add Save Bits Save Add Save Param Bits keepzero op op param op base op stat1 op stat2 op stat3 direct maxstat itemspecific damagerelated itemevent1 itemeventfunc1 itemevent2 itemeventfunc2 descpriority descfunc descval descstrpos descstrneg descstr2 dgrp dgrpfunc dgrpval dgrpstrpos dgrpstrneg dgrpstr2 stuff *eol 2 | strength 0 1 11 1 0 10 1 1 125 55 1024 7 32 8 32 67 1 1 ModStr1a ModStr1a 1 1 1 Moditem2allattrib Moditem2allattrib 6 0 3 | energy 1 11 1 0 10 1 1 100 55 1024 7 32 7 32 8 maxmana 61 1 1 ModStr1d ModStr1d 1 1 1 Moditem2allattrib Moditem2allattrib 0 4 | dexterity 2 1 11 1 0 10 1 1 125 55 1024 7 32 7 32 65 1 1 ModStr1b ModStr1b 1 1 1 Moditem2allattrib Moditem2allattrib 0 5 | vitality 3 11 1 0 10 1 1 100 55 1024 7 32 7 32 9 maxhp maxstamina 63 1 1 ModStr1c ModStr1c 1 1 1 Moditem2allattrib Moditem2allattrib 0 6 | statpts 4 9 1 0 10 1024 0 7 | newskills 5 9 1 0 8 1024 0 8 | hitpoints 6 32 1 0 21 1024 8 1 maxhp 0 9 | maxhp 7 32 1 0 21 1 1 1 56 20 1024 8 8 32 9 32 59 1 1 ModStr1u ModStr1u 0 10 | mana 8 32 1 0 21 1024 8 1 1 maxmana 0 11 | maxmana 9 32 1 0 21 1 1 0 81 20 1024 8 8 32 8 32 55 1 1 ModStr1e ModStr1e 0 12 | stamina 10 32 1 0 21 1024 8 1 1 maxstamina 0 13 | maxstamina 11 32 1 0 21 1 1 0 75 20 1024 8 8 32 8 32 51 1 1 ModStr5d ModStr5d 0 14 | level 12 9 1 0 7 1024 0 15 | experience 13 32 1 0 32 1024 0 16 | gold 14 32 1 0 25 1024 0 17 | goldbank 15 32 1 0 25 1024 0 18 | item_armor_percent 16 1 11 47 20 1024 9 0 9 0 13 armorclass 74 4 1 Modstr2v Modstr2v 0 19 | item_maxdamage_percent 17 1 11 45 20 1024 9 0 9 0 13 maxdamage secondary_maxdamage item_throw_maxdamage 1 129 3 0 ModStr2j ModStr2j 0 20 | item_mindamage_percent 18 1 11 45 20 1024 9 0 9 0 13 mindamage secondary_mindamage item_throw_mindamage 1 130 3 0 ModStr2k ModStr2k 0 21 | tohit 19 1 16 15 10 1024 10 10 1 115 1 1 ModStr1h ModStr1h 0 22 | toblock 20 1 10 89 204 1024 6 0 6 0 134 2 1 ModStr3g ModStr3g 0 23 | mindamage 21 1 16 122 25 1024 6 0 6 0 1 127 1 1 ModStr1g ModStr1g 0 24 | maxdamage 22 1 16 94 16 1024 7 0 7 0 1 126 1 1 ModStr1f ModStr1f 0 25 | secondary_mindamage 23 1 16 97 15 1024 6 0 6 0 1 124 1 1 ModStr1g ModStr1g 0 26 | secondary_maxdamage 24 1 16 85 11 1024 7 0 7 0 1 123 1 1 ModStr1f ModStr1f 0 27 | damagepercent 25 1 12 45 40 1024 8 0 8 0 1 0 28 | manarecovery 26 1024 8 0 8 0 0 29 | manarecoverybonus 27 1 16 1024 8 0 8 0 52 2 2 ModStr4g ModStr4g 0 30 | staminarecoverybonus 28 1 16 1024 8 0 8 0 48 2 2 ModStr3v ModStr3v 0 31 | lastexp 29 32 1024 0 32 | nextexp 30 32 1024 0 33 | armorclass 31 1 16 17 10 1024 10 10 11 10 71 1 1 ModStr1i ModStr1i 0 34 | armorclass_vs_missile 32 1 16 11 5 1024 8 0 9 0 69 1 1 ModStr6a ModStr6a 0 35 | armorclass_vs_hth 33 1 16 13 7 1024 8 0 8 0 70 1 1 ModStr6b ModStr6b 0 36 | normal_damage_reduction 34 1 10 188 200 1024 6 0 6 0 22 3 2 ModStr2u ModStr2u 0 37 | magic_damage_reduction 35 1 10 397 340 1024 6 0 6 0 21 3 2 ModStr2t ModStr2t 0 38 | damageresist 36 1 9 152 68 1024 8 0 8 0 22 2 2 ModStr2u ModStr2u 0 39 | magicresist 37 1 9 164 68 1024 8 0 8 0 41 4 2 ModStr1m ModStr1m 0 40 | maxmagicresist 38 1 9 1091 409 1024 5 0 5 0 46 4 1 ModStr5x ModStr5x 0 41 | fireresist 39 1 9 43 20 1024 8 0 8 50 36 4 2 ModStr1j ModStr1j 2 19 strModAllResistances strModAllResistances 0 42 | maxfireresist 40 1 9 584 256 1024 5 0 5 0 42 4 1 ModStr5u ModStr5u 0 43 | lightresist 41 1 9 43 20 1024 8 0 8 50 38 4 2 ModStr1l ModStr1l 2 19 strModAllResistances strModAllResistances 0 44 | maxlightresist 42 1 9 584 256 1024 5 0 5 0 43 4 1 ModStr5w ModStr5w 0 45 | coldresist 43 1 9 43 20 1024 8 0 8 50 40 4 2 ModStr1k ModStr1k 2 19 strModAllResistances strModAllResistances 0 46 | maxcoldresist 44 1 9 584 256 1024 5 0 5 0 44 4 1 ModStr5v ModStr5v 0 47 | poisonresist 45 1 9 43 20 1024 8 0 8 50 34 4 2 ModStr1n ModStr1n 2 19 strModAllResistances strModAllResistances 0 48 | maxpoisonresist 46 1 9 526 256 1024 5 0 5 0 45 4 1 ModStr5y ModStr5y 0 49 | damageaura 47 1 16 1024 0 50 | firemindam 48 1 16 11 10 1024 8 0 8 0 1 102 1 1 ModStr1p ModStr1p 0 51 | firemaxdam 49 1 16 19 10 1024 8 0 9 0 1 101 1 1 ModStr1o ModStr1o 0 52 | lightmindam 50 1 16 12 10 1024 6 0 6 0 1 99 1 1 ModStr1r ModStr1r 0 53 | lightmaxdam 51 1 16 17 10 1024 9 0 10 0 1 98 1 1 ModStr1q ModStr1q 0 54 | magicmindam 52 1 16 196 20 1024 6 0 8 0 1 104 1 1 strModMagicDamage strModMagicDamage 0 55 | magicmaxdam 53 1 16 183 20 1024 7 0 9 0 1 103 1 1 strModMagicDamage strModMagicDamage 0 56 | coldmindam 54 1 16 451 512 1024 6 0 8 0 1 96 1 1 ModStr1t ModStr1t 0 57 | coldmaxdam 55 1 16 128 340 1024 8 0 9 0 1 95 1 1 ModStr1s ModStr1s 0 58 | coldlength 56 1 16 77 4 1024 8 0 8 0 1 0 59 | poisonmindam 57 1 19 12 28 1024 9 0 10 0 1 92 1 1 ModStr4i ModStr4i 0 60 | poisonmaxdam 58 1 19 11 34 1024 9 0 10 0 1 91 1 1 ModStr4h ModStr4h 0 61 | poisonlength 59 1 16 0 4 1024 8 0 9 0 1 0 62 | lifedrainmindam 60 1 16 1044 341 1024 7 0 7 0 1 88 2 1 ModStr2z ModStr2z 0 63 | lifedrainmaxdam 61 1 16 1024 1 0 64 | manadrainmindam 62 1 16 1179 341 1024 7 0 7 0 1 89 2 1 ModStr2y ModStr2y 0 65 | manadrainmaxdam 63 1 16 1024 1 0 66 | stamdrainmindam 64 1 16 1024 1 0 67 | stamdrainmaxdam 65 1 16 1024 1 0 68 | stunlength 66 16 1024 1 0 69 | velocitypercent 67 1 1 10 1 1024 7 30 7 30 0 70 | attackrate 68 1 1 10 1 1024 7 30 7 30 1 0 71 | other_animrate 69 1 1 10 1 1024 0 72 | quantity 70 1 16 1024 1 1 0 73 | value 71 1 9 1024 8 100 8 100 1 0 74 | durability 72 1 9 1024 8 0 9 0 1 maxdurability 1 0 75 | maxdurability 73 1 9 9 4 1024 8 0 8 0 1 0 76 | hpregen 74 451 410 1024 6 30 6 30 56 1 2 ModStr2l ModStr2w 0 77 | item_maxdurability_percent 75 1 7 117 10 1024 7 20 7 20 13 maxdurability 3 2 2 ModStr2i ModStr2i 0 78 | item_maxhp_percent 76 1 16 32093 204 1024 6 10 6 10 11 maxhp 58 2 2 ModStr2g ModStr2g 0 79 | item_maxmana_percent 77 1 16 56452 204 1024 6 10 6 10 11 maxmana 54 2 2 ModStr2h ModStr2h 0 80 | item_attackertakesdamage 78 1 16 1 112 128 1024 7 0 7 0 damagedinmelee 6 13 3 2 ModStr1v ModStr1v 0 81 | item_goldbonus 79 1 10 187 34 1024 9 100 9 100 10 2 1 ModStr1w ModStr1w 0 82 | item_magicbonus 80 1 9 577 102 1024 8 100 8 100 8 2 1 ModStr1x ModStr1x 0 83 | item_knockback 81 1 8 1 105 0 1024 7 0 7 0 1 domeleedamage 7 domissiledamage 7 76 3 0 ModStr1y ModStr1y 0 84 | item_timeduration 82 1 10 1024 9 20 9 20 0 85 | item_addclassskills 83 1 4 3 1 49523 1560 1024 3 0 3 0 3 150 13 1 ModStr3a ModStr3a 0 86 | unsentparam1 84 1024 3 0 0 87 | item_addexperience 85 1 9 36015 519 1024 3 0 9 50 11 4 1 Moditem2ExpG Moditem2ExpG 0 88 | item_healafterkill 86 1 7 1 30 101 1024 3 0 7 0 kill 28 16 1 1 ModitemHPaK ModitemHPaK 0 89 | item_reducedprices 87 8 18957 203 1024 3 0 7 0 8 2 2 ModitemRedVendP ModitemRedVendP 0 90 | item_doubleherbduration 88 1 2 1024 1 0 1 0 0 91 | item_lightradius 89 1 5 1 15 51 1024 4 4 4 4 6 1 1 ModStr3f ModStr3f 0 92 | item_lightcolor 90 1 24 1 155 0 1024 5 0 24 0 0 93 | item_req_percent 91 1 8 26 -34 1024 8 100 8 100 0 4 2 ModStr3h ModStr3h 0 94 | item_levelreq 92 1024 6 20 7 0 95 | item_fasterattackrate 93 1 1 9 1042 156 1024 7 20 7 20 1 145 4 1 ModStr4m ModStr4m 0 96 | item_levelreqpct 94 1024 7 20 7 64 13 item_levelreq 0 97 | lastblockframe 95 1024 6 20 0 98 | item_fastermovevelocity 96 1 1 9 4083 156 1024 7 20 7 20 148 4 1 ModStr4s ModStr4s 0 99 | item_nonclassskill 97 1 15 9 1 1 181 327 1024 7 20 6 0 9 81 28 0 100 | state 98 1 1 8 1 415 64 1024 6 20 1 8 0 101 | item_fastergethitrate 99 1 1 9 1065 72 1024 7 20 7 20 139 4 1 ModStr4p ModStr4p 0 102 | monster_playercount 100 1024 7 20 0 103 | skill_poison_override_length 101 8 1024 6 20 0 104 | item_fasterblockrate 102 1 1 9 1484 72 1024 7 20 7 20 136 4 1 ModStr4y ModStr4y 0 105 | skill_bypass_undead 103 1 1024 7 20 0 106 | skill_bypass_demons 104 1 1024 6 20 0 107 | item_fastercastrate 105 1 1 9 3876 156 1024 7 20 7 20 142 4 1 ModStr4v ModStr4v 0 108 | skill_bypass_beasts 106 1 1024 7 20 0 109 | item_singleskill 107 1 15 9 1 1 181 256 1024 14 0 3 0 9 81 27 0 110 | item_restinpeace 108 1 1 1987 0 1024 14 0 1 0 1 kill 29 81 3 0 ModitemSMRIP ModitemSMRIP 0 111 | curse_resistance 109 9 159 33 1024 14 0 9 0 0 112 | item_poisonlengthresist 110 1 9 27 10 1024 8 20 8 20 18 2 2 ModStr3r ModStr3r 0 113 | item_normaldamage 111 1 8 94 100 1024 7 20 9 20 1 122 1 2 ModStr5b ModStr5b 0 114 | item_howl 112 1 8 1 55 10 1024 7 -1 7 -1 1 domeleedamage 8 domissiledamage 8 79 5 2 ModStr3u ModStr3u 0 115 | item_stupidity 113 1 8 1 332 1024 1024 7 0 7 0 1 domeleedamage 9 domissiledamage 9 80 12 2 ModStr6d ModStr6d 0 116 | item_damagetomana 114 1 7 1 43 20 1024 6 0 6 0 damagedinmelee 13 damagedbymissile 13 11 2 1 ModStr3w ModStr3w 0 117 | item_ignoretargetac 115 1 2 1088 1024 1024 1 0 1 0 1 119 3 0 ModStr3y ModStr3y 0 118 | item_fractionaltargetac 116 1 8 67 20 1024 7 0 7 0 1 118 20 1 ModStr5o ModStr5o 0 119 | item_preventheal 117 1 8 48 50 1024 7 0 7 0 1 81 3 0 ModStr4a ModStr4a 0 120 | item_halffreezeduration 118 1 2 5096 988 1024 1 0 1 0 19 3 0 ModStr4b ModStr4b 0 121 | item_tohit_percent 119 1 12 981 40 1024 9 20 9 20 1 117 2 1 ModStr4c ModStr4c 0 122 | item_damagetargetac 120 1 8 24 -20 1024 7 128 7 128 1 75 1 1 ModStr4d ModStr4d 0 123 | item_demondamage_percent 121 1 12 19 12 1024 9 20 9 20 1 112 4 1 ModStr4e ModStr4e 0 124 | item_undeaddamage_percent 122 1 12 13 12 1024 9 20 9 20 1 108 4 1 ModStr4f ModStr4f 0 125 | item_demon_tohit 123 1 13 15 7 1024 10 128 10 128 1 110 1 1 ModStr4j ModStr4j 0 126 | item_undead_tohit 124 1 13 11 7 1024 10 128 10 128 1 106 1 1 ModStr4k ModStr4k 0 127 | item_throwable 125 1 2 82 1024 1024 1 0 1 0 5 3 0 ModStr5a ModStr5a 0 128 | item_elemskill 126 1 3 3 1 76 1024 1024 4 0 3 0 3 157 1 1 ModStr3i ModStr3i 0 129 | item_allskills 127 1 8 1 15123 4096 1024 3 0 3 0 158 1 1 ModStr3k ModStr3k 0 130 | item_attackertakeslightdamage 128 1 6 1 4 102 1024 5 0 5 0 damagedinmelee 10 14 3 2 ModStr3j ModStr3j 0 131 | ironmaiden_level 129 1 10 1024 0 132 | lifetap_level 130 1 10 1024 0 133 | thorns_percent 131 12 1024 0 134 | bonearmor 132 1 32 1024 0 135 | bonearmormax 133 1 32 1024 0 136 | item_freeze 134 1 5 1 666 12 1024 5 0 5 0 1 domeleedamage 14 domissiledamage 14 78 12 2 ModStr3l ModStr3l 0 137 | item_openwounds 135 1 7 1 23 10 1024 7 0 7 0 1 domeleedamage 15 domissiledamage 15 83 2 1 ModStr3m ModStr3m 0 138 | item_crushingblow 136 1 7 1 98 40 1024 7 0 7 0 1 domeleedamage 16 domissiledamage 16 87 2 1 ModStr5c ModStr5c 0 139 | item_kickdamage 137 1 7 77 51 1024 7 0 7 0 121 1 1 ModStr5e ModStr5e 0 140 | item_manaafterkill 138 1 7 1 17 102 1024 7 0 7 0 kill 17 16 1 1 ModStr5f ModStr5f 0 141 | item_healafterdemonkill 139 1 7 1 18 102 1024 7 0 7 0 kill 18 15 1 1 ModStr6c ModStr6c 0 142 | item_extrablood 140 1 7 15 10 1024 7 0 7 0 1 0 143 | item_deadlystrike 141 1 7 31 25 1024 7 0 7 0 1 85 2 1 ModStr5q ModStr5q 0 144 | item_absorbfire_percent 142 1 7 5486 102 1024 7 0 7 0 23 2 2 ModStr5g ModStr5g 0 145 | item_absorbfire 143 1 7 1739 204 1024 7 0 7 0 27 1 1 ModStr5h ModStr5h 0 146 | item_absorblight_percent 144 1 7 5486 102 1024 7 0 7 0 24 2 2 ModStr5i ModStr5i 0 147 | item_absorblight 145 1 7 1739 204 1024 7 0 7 0 29 1 1 ModStr5j ModStr5j 0 148 | item_absorbmagic_percent 146 1 7 5486 102 1024 7 0 7 0 26 2 2 ModStr5k ModStr5k 0 149 | item_absorbmagic 147 1 7 1739 204 1024 7 0 7 0 33 1 1 ModStr5l ModStr5l 0 150 | item_absorbcold_percent 148 1 7 5486 102 1024 7 0 7 0 25 2 2 ModStr5m ModStr5m 0 151 | item_absorbcold 149 1 7 1739 204 1024 7 0 7 0 31 1 1 ModStr5n ModStr5n 0 152 | item_slow 150 1 7 1 101 40 1024 7 0 7 0 1 domeleedamage 19 domissiledamage 19 77 2 2 ModStr5r ModStr5r 0 153 | item_aura 151 1 1 1024 7 0 5 0 9 159 16 0 ModitemAura ModitemAura 0 154 | item_indesctructible 152 1 1 1024 7 0 1 160 3 0 ModStre9s ModStre9s 0 155 | item_cannotbefrozen 153 1 2 15011 2048 1024 1 1 20 3 0 ModStr5z ModStr5z 0 156 | item_staminadrainpct 154 1 7 102 20 1024 7 20 7 20 1 49 2 1 ModStr6e ModStr6e 0 157 | item_reanimate 155 7 10 1 1024 7 0 7 0 10 1 kill 31 17 23 1 Moditemreanimas Moditemreanimas 0 158 | item_pierce 156 1 7 1924 2048 1024 7 0 7 0 1 132 3 0 ModStr6g ModStr6g 0 159 | item_magicarrow 157 1 7 511 1024 1024 7 0 7 0 131 3 0 ModStr6h ModStr6h 0 160 | item_explosivearrow 158 1 7 492 1536 1024 7 0 7 0 133 3 0 ModStr6i ModStr6i 0 161 | item_throw_mindamage 159 1 6 76 128 1024 6 0 6 0 1 0 162 | item_throw_maxdamage 160 1 7 88 128 1024 7 0 7 0 1 0 163 | skill_handofathena 161 1 12 1024 0 164 | skill_staminapercent 162 1 13 1024 1 maxstamina 0 165 | skill_passive_staminapercent 163 1 12 1024 1 maxstamina 0 166 | skill_concentration 164 1 12 1024 0 167 | skill_enchant 165 1 12 1024 0 168 | skill_pierce 166 1 12 1024 0 169 | skill_conviction 167 1 12 1024 0 170 | skill_chillingarmor 168 1 12 1024 0 171 | skill_frenzy 169 1 12 1024 0 172 | skill_decrepify 170 1 12 1024 0 173 | skill_armor_percent 171 1 16 1024 0 174 | alignment 172 1 2 1024 0 175 | target0 173 32 1024 0 176 | target1 174 32 1024 0 177 | goldlost 175 24 1024 0 178 | conversion_level 176 8 1024 0 179 | conversion_maxhp 177 16 1024 0 180 | unit_dooverlay 178 16 1024 0 181 | attack_vs_montype 179 9 10 19 14 1024 3 0 9 10 1 108 22 1 ModitemAttratvsM ModitemAttratvsM 0 182 | damage_vs_montype 180 9 10 27 17 1024 3 0 9 10 1 106 22 1 Moditemdamvsm Moditemdamvsm 0 183 | fade 181 1 7 14 0 3 0 184 | armor_override_percent 182 1 1 8 14 0 0 185 | unused183 183 14 0 0 186 | unused184 184 14 0 0 187 | unused185 185 14 0 0 188 | unused186 186 14 0 0 189 | unused187 187 14 0 0 190 | item_addskill_tab 188 1 3 6 1 11042 768 1024 10 0 3 0 16 151 14 StrSklTabItem1 StrSklTabItem1 0 191 | unused189 189 1024 10 0 0 192 | unused190 190 1024 10 0 0 193 | unused191 191 1024 10 0 0 194 | unused192 192 1024 10 0 0 195 | unused193 193 1024 10 0 0 196 | item_numsockets 194 1 4 38 170 1024 4 0 4 0 1 0 197 | item_skillonattack 195 1 7 16 1 2 190 256 1024 21 0 7 0 16 1 domeleeattack 20 domissileattack 20 160 15 ItemExpansiveChancX ItemExpansiveChancX 0 198 | item_skillonkill 196 1 7 16 1 2 85 19 1024 21 0 7 0 16 1 kill 20 160 15 ModitemskonKill ModitemskonKill 0 199 | item_skillondeath 197 1 7 16 1 2 11 9 1024 21 0 7 0 16 killed 30 160 15 Moditemskondeath Moditemskondeath 0 200 | item_skillonhit 198 1 7 16 1 2 190 256 1024 21 0 7 0 16 1 domeleedamage 20 domissiledamage 20 160 15 ItemExpansiveChanc1 ItemExpansiveChanc1 0 201 | item_skillonlevelup 199 1 7 16 1 2 7 6 1024 21 0 7 0 16 levelup 30 160 15 ModitemskonLevel ModitemskonLevel 0 202 | unused200 200 1024 21 0 0 203 | item_skillongethit 201 1 7 16 1 2 190 256 1024 21 0 7 0 16 damagedinmelee 21 damagedbymissile 21 160 15 ItemExpansiveChanc2 ItemExpansiveChanc2 0 204 | unused202 202 1024 21 0 0 205 | unused203 203 1024 21 0 0 206 | item_charged_skill 204 1 30 1 3 401 256 1024 30 0 16 0 16 1 24 ModStre10d ModStre10d 0 207 | unused204 205 1 30 3 401 256 1024 30 0 0 208 | unused205 206 1 30 3 401 256 1024 30 0 0 209 | unused206 207 1 30 3 401 256 1024 30 0 0 210 | unused207 208 1 30 3 401 256 1024 30 0 0 211 | unused208 209 1 30 3 401 256 1024 30 0 0 212 | unused209 210 1 30 3 401 256 1024 30 0 0 213 | unused210 211 1 30 3 401 256 1024 30 0 0 214 | unused211 212 1 30 3 401 256 1024 30 0 0 215 | unused212 213 1 30 3 401 256 1024 30 0 0 216 | item_armor_perlevel 214 1 6 43 42 1024 6 0 6 0 4 3 level armorclass 72 6 1 ModStr1i ModStr1i increaseswithplaylevelX 0 217 | item_armorpercent_perlevel 215 1 6 87 100 1024 6 0 6 0 5 3 level armorclass 73 8 1 Modstr2v Modstr2v increaseswithplaylevelX 0 218 | item_hp_perlevel 216 1 6 92 64 1024 8 6 0 6 0 2 3 level maxhp 57 6 1 ModStr1u ModStr1u increaseswithplaylevelX 0 219 | item_mana_perlevel 217 1 6 90 128 1024 8 6 0 6 0 2 3 level maxmana 53 6 1 ModStr1e ModStr1e increaseswithplaylevelX 0 220 | item_maxdamage_perlevel 218 1 6 54 204 1024 6 0 6 0 4 3 level maxdamage secondary_maxdamage item_throw_maxdamage 1 125 6 1 ModStr1f ModStr1f increaseswithplaylevelX 0 221 | item_maxdamage_percent_perlevel 219 1 6 86 100 1024 6 0 6 0 5 3 level maxdamage secondary_maxdamage item_throw_maxdamage 1 128 8 1 ModStr2j ModStr2j increaseswithplaylevelX 0 222 | item_strength_perlevel 220 1 6 132 128 1024 6 0 6 0 2 3 level strength 66 6 1 ModStr1a ModStr1a increaseswithplaylevelX 0 223 | item_dexterity_perlevel 221 1 6 132 128 1024 6 0 6 0 2 3 level dexterity 64 6 1 ModStr1b ModStr1b increaseswithplaylevelX 0 224 | item_energy_perlevel 222 1 6 105 128 1024 6 0 6 0 2 3 level energy 60 6 1 ModStr1d ModStr1d increaseswithplaylevelX 0 225 | item_vitality_perlevel 223 1 6 105 128 1024 6 0 6 0 2 3 level vitality 62 6 1 ModStr1c ModStr1c increaseswithplaylevelX 0 226 | item_tohit_perlevel 224 1 6 53 20 1024 6 0 6 0 2 1 level tohit 1 114 6 1 ModStr1h ModStr1h increaseswithplaylevelX 0 227 | item_tohitpercent_perlevel 225 1 6 10 256 1024 6 0 6 0 2 1 level item_tohit_percent 1 116 7 1 ModStr4c ModStr4c increaseswithplaylevelX 0 228 | item_cold_damagemax_perlevel 226 1 6 1058 340 1024 6 0 6 0 2 3 level coldmaxdam 1 94 6 1 ModStr1s ModStr1s increaseswithplaylevelX 0 229 | item_fire_damagemax_perlevel 227 1 6 49 128 1024 6 0 6 0 2 3 level firemaxdam 1 100 6 1 ModStr1o ModStr1o increaseswithplaylevelX 0 230 | item_ltng_damagemax_perlevel 228 1 6 49 128 1024 6 0 6 0 2 3 level lightmaxdam 1 97 6 1 ModStr1q ModStr1q increaseswithplaylevelX 0 231 | item_pois_damagemax_perlevel 229 1 6 49 128 1024 6 0 6 0 2 3 level poisonmaxdam 1 90 6 1 ModStr4h ModStr4h increaseswithplaylevelX 0 232 | item_resist_cold_perlevel 230 1 6 101 128 1024 6 0 6 0 2 3 level coldresist 39 7 2 ModStr1k ModStr1k increaseswithplaylevelX 0 233 | item_resist_fire_perlevel 231 1 6 101 128 1024 6 0 6 0 2 3 level fireresist 35 7 2 ModStr1j ModStr1j increaseswithplaylevelX 0 234 | item_resist_ltng_perlevel 232 1 6 101 128 1024 6 0 6 0 2 3 level lightresist 37 7 2 ModStr1l ModStr1l increaseswithplaylevelX 0 235 | item_resist_pois_perlevel 233 1 6 101 128 1024 6 0 6 0 2 3 level poisonresist 33 7 2 ModStr1n ModStr1n increaseswithplaylevelX 0 236 | item_absorb_cold_perlevel 234 1 6 207 340 1024 6 0 6 0 2 3 level item_absorbcold 32 6 1 ModStre9p ModStre9p increaseswithplaylevelX 0 237 | item_absorb_fire_perlevel 235 1 6 207 340 1024 6 0 6 0 2 3 level item_absorbfire 28 6 1 ModStre9o ModStre9o increaseswithplaylevelX 0 238 | item_absorb_ltng_perlevel 236 1 6 207 340 1024 6 0 6 0 2 3 level item_absorblight 30 6 1 ModStre9q ModStre9q increaseswithplaylevelX 0 239 | item_absorb_pois_perlevel 237 1 6 207 340 1024 6 0 6 0 2 3 level item_absorbmagic 0 240 | item_thorns_perlevel 238 1 6 55 256 1024 6 0 5 0 2 3 level item_attackertakesdamage 12 9 2 ModStr1v ModStr1v increaseswithplaylevelX 0 241 | item_find_gold_perlevel 239 1 6 42 256 1024 6 0 6 0 2 3 level item_goldbonus 9 7 1 ModStr1w ModStr1w increaseswithplaylevelX 0 242 | item_find_magic_perlevel 240 1 6 814 1024 1024 6 0 6 0 2 3 level item_magicbonus 7 7 1 ModStr1x ModStr1x increaseswithplaylevelX 0 243 | item_regenstamina_perlevel 241 1 6 79 256 1024 6 0 6 0 2 3 level staminarecoverybonus 47 8 2 ModStr3v ModStr3v increaseswithplaylevelX 0 244 | item_stamina_perlevel 242 1 6 104 64 1024 6 0 6 0 2 3 level maxstamina 50 6 1 ModStr5d ModStr5d increaseswithplaylevelX 0 245 | item_damage_demon_perlevel 243 1 6 56 10 1024 6 0 6 0 2 3 level item_demondamage_percent 1 111 8 1 ModStr4e ModStr4e increaseswithplaylevelX 0 246 | item_damage_undead_perlevel 244 1 6 91 10 1024 6 0 6 0 2 3 level item_undeaddamage_percent 1 107 8 1 ModStr4f ModStr4f increaseswithplaylevelX 0 247 | item_tohit_demon_perlevel 245 1 6 55 10 1024 6 0 6 0 2 1 level item_demon_tohit 1 109 6 1 ModStr4j ModStr4j increaseswithplaylevelX 0 248 | item_tohit_undead_perlevel 246 1 6 12 10 1024 6 0 6 0 2 1 level item_undead_tohit 1 105 6 1 ModStr4k ModStr4k increaseswithplaylevelX 0 249 | item_crushingblow_perlevel 247 1 6 213 1024 1024 6 0 6 0 2 3 level item_crushingblow 1 86 7 1 ModStr5c ModStr5c increaseswithplaylevelX 0 250 | item_openwounds_perlevel 248 1 6 181 128 1024 6 0 6 0 2 3 level item_openwounds 1 82 7 1 ModStr3m ModStr3m increaseswithplaylevelX 0 251 | item_kick_damage_perlevel 249 1 6 104 128 1024 6 0 6 0 2 3 level item_kickdamage 1 120 6 1 ModStr5e ModStr5e increaseswithplaylevelX 0 252 | item_deadlystrike_perlevel 250 1 6 118 512 1024 6 0 6 0 2 3 level item_deadlystrike 1 84 7 1 ModStr5q ModStr5q increaseswithplaylevelX 0 253 | item_find_gems_perlevel 251 1 1024 0 254 | item_replenish_durability 252 1 5 106 256 1024 5 0 6 0 1 11 0 ModStre9t ModStre9t 0 255 | item_replenish_quantity 253 1 5 106 256 1024 5 0 6 0 2 3 0 ModStre9v ModStre9v 0 256 | item_extra_stack 254 1 99 10 1024 8 0 8 0 4 3 0 ModStre9i ModStre9i 0 257 | item_find_item 255 1 1024 0 258 | item_slash_damage 256 1 1024 1 0 259 | item_slash_damage_percent 257 1 1024 1 0 260 | item_crush_damage 258 1 1024 1 0 261 | item_crush_damage_percent 259 1 1024 1 0 262 | item_thrust_damage 260 1 1024 1 0 263 | item_thrust_damage_percent 261 1 1024 1 0 264 | item_absorb_slash 262 1 1024 0 265 | item_absorb_crush 263 1 1024 0 266 | item_absorb_thrust 264 1 1024 0 267 | item_absorb_slash_percent 265 1 1024 0 268 | item_absorb_crush_percent 266 1 1024 0 269 | item_absorb_thrust_percent 267 1 1024 0 270 | item_armor_bytime 268 1 22 4 0 1024 22 0 22 0 6 armorclass 180 17 1 ModStr1i ModStr1i 0 271 | item_armorpercent_bytime 269 1 22 4 0 1024 22 0 22 0 7 armorclass 180 18 1 Modstr2v Modstr2v 0 272 | item_hp_bytime 270 1 22 4 0 1024 22 0 22 0 6 maxhp 180 17 1 ModStr1u ModStr1u 0 273 | item_mana_bytime 271 1 22 4 0 1024 22 0 22 0 6 maxmana 180 17 1 ModStr1e ModStr1e 0 274 | item_maxdamage_bytime 272 1 22 4 0 1024 22 0 22 0 6 maxdamage secondary_maxdamage item_throw_maxdamage 1 180 17 1 ModStr1f ModStr1f 0 275 | item_maxdamage_percent_bytime 273 1 22 4 0 1024 22 0 22 0 7 maxdamage secondary_mindamage item_throw_mindamage 1 180 18 1 ModStr2j ModStr2j 0 276 | item_strength_bytime 274 1 22 4 0 1024 22 0 22 0 6 strength 180 17 1 ModStr1a ModStr1a 0 277 | item_dexterity_bytime 275 1 22 4 0 1024 22 0 22 0 6 dexterity 180 17 1 ModStr1b ModStr1b 0 278 | item_energy_bytime 276 1 22 4 0 1024 22 0 22 0 6 energy 180 17 1 ModStr1d ModStr1d 0 279 | item_vitality_bytime 277 1 22 4 0 1024 22 0 22 0 6 vitality 180 17 1 ModStr1c ModStr1c 0 280 | item_tohit_bytime 278 1 22 4 0 1024 22 0 22 0 6 tohit 1 180 17 1 ModStr1h ModStr1h 0 281 | item_tohitpercent_bytime 279 1 22 4 0 1024 22 0 22 0 6 item_tohit_percent 1 180 18 1 ModStr4c ModStr4c 0 282 | item_cold_damagemax_bytime 280 1 22 4 0 1024 22 0 22 0 6 coldmaxdam 1 180 17 1 ModStr1s ModStr1s 0 283 | item_fire_damagemax_bytime 281 1 22 4 0 1024 22 0 22 0 6 firemaxdam 1 180 17 1 ModStr1o ModStr1o 0 284 | item_ltng_damagemax_bytime 282 1 22 4 0 1024 22 0 22 0 6 lightmaxdam 1 180 17 1 ModStr1q ModStr1q 0 285 | item_pois_damagemax_bytime 283 1 22 4 0 1024 22 0 22 0 6 poisonmaxdam 1 180 17 1 ModStr4h ModStr4h 0 286 | item_resist_cold_bytime 284 1 22 4 0 1024 22 0 22 0 6 coldresist 180 18 2 ModStr1k ModStr1k 0 287 | item_resist_fire_bytime 285 1 22 4 0 1024 22 0 22 0 6 fireresist 180 18 2 ModStr1j ModStr1j 0 288 | item_resist_ltng_bytime 286 1 22 4 0 1024 22 0 22 0 6 lightresist 180 18 2 ModStr1l ModStr1l 0 289 | item_resist_pois_bytime 287 1 22 4 0 1024 22 0 22 0 6 poisonresist 180 18 2 ModStr1n ModStr1n 0 290 | item_absorb_cold_bytime 288 1 22 4 0 1024 22 0 22 0 6 item_absorbcold 180 18 1 ModStre9p ModStre9p 0 291 | item_absorb_fire_bytime 289 1 22 4 0 1024 22 0 22 0 6 item_absorbfire 180 18 1 ModStre9o ModStre9o 0 292 | item_absorb_ltng_bytime 290 1 22 4 0 1024 22 0 22 0 6 item_absorblight 180 18 1 ModStre9q ModStre9q 0 293 | item_absorb_pois_bytime 291 1 22 4 0 1024 22 0 22 0 6 item_absorbmagic 0 294 | item_find_gold_bytime 292 1 22 4 0 1024 22 0 22 0 6 item_goldbonus 180 18 2 ModStr1w ModStr1w 0 295 | item_find_magic_bytime 293 1 22 4 0 1024 22 0 22 0 6 item_magicbonus 180 18 1 ModStr1x ModStr1x 0 296 | item_regenstamina_bytime 294 1 22 4 0 1024 22 0 22 0 6 staminarecoverybonus 180 18 2 ModStr3v ModStr3v 0 297 | item_stamina_bytime 295 1 22 4 0 1024 22 0 22 0 6 maxstamina 180 17 1 ModStr5d ModStr5d 0 298 | item_damage_demon_bytime 296 1 22 4 0 1024 22 0 22 0 6 item_demondamage_percent 1 180 18 1 ModStr4e ModStr4e 0 299 | item_damage_undead_bytime 297 1 22 4 0 1024 22 0 22 0 6 item_undeaddamage_percent 1 180 18 1 ModStr4f ModStr4f 0 300 | item_tohit_demon_bytime 298 1 22 4 0 1024 22 0 22 0 6 item_demon_tohit 1 180 17 1 ModStr4j ModStr4j 0 301 | item_tohit_undead_bytime 299 1 22 4 0 1024 22 0 22 0 6 item_undead_tohit 1 180 17 1 ModStr4k ModStr4k 0 302 | item_crushingblow_bytime 300 1 22 4 0 1024 22 0 22 0 6 item_crushingblow 1 180 18 1 ModStr5c ModStr5c 0 303 | item_openwounds_bytime 301 1 22 4 0 1024 22 0 22 0 6 item_openwounds 1 180 18 1 ModStr3m ModStr3m 0 304 | item_kick_damage_bytime 302 1 22 4 0 1024 22 0 22 0 6 item_kickdamage 1 180 17 1 ModStr5e ModStr5e 0 305 | item_deadlystrike_bytime 303 1 22 4 0 1024 22 0 22 0 6 item_deadlystrike 1 180 18 1 ModStr5q ModStr5q 0 306 | item_find_gems_bytime 304 1 4 0 1024 0 307 | item_pierce_cold 305 1 9 1432 513 1024 8 50 88 20 1 Moditemenrescoldsk Moditemenrescoldsk 0 308 | item_pierce_fire 306 1 9 1240 497 1024 8 50 88 20 1 Moditemenresfiresk Moditemenresfiresk 0 309 | item_pierce_ltng 307 1 9 1187 481 1024 8 50 88 20 1 Moditemenresltngsk Moditemenresltngsk 0 310 | item_pierce_pois 308 1 9 1322 506 1024 8 50 88 20 1 Moditemenrespoissk Moditemenrespoissk 0 311 | item_damage_vs_monster 309 1 1024 1 0 312 | item_damage_percent_vs_monster 310 1 1024 1 0 313 | item_tohit_vs_monster 311 1 1024 1 0 314 | item_tohit_percent_vs_monster 312 1 1024 1 0 315 | item_ac_vs_monster 313 1 1024 0 316 | item_ac_percent_vs_monster 314 1 1024 0 317 | firelength 315 1 16 1024 0 318 | burningmin 316 1 16 1024 0 319 | burningmax 317 1 16 1024 0 320 | progressive_damage 318 1 3 1024 0 321 | progressive_steal 319 1 3 1024 0 322 | progressive_other 320 1 3 1024 0 323 | progressive_fire 321 1 3 1024 0 324 | progressive_cold 322 1 3 1024 0 325 | progressive_lightning 323 1 3 1024 0 326 | item_extra_charges 324 1 6 1024 6 0 6 0 1 1 0 327 | progressive_tohit 325 1 16 1024 0 328 | poison_count 326 1 5 1024 1 0 329 | damage_framerate 327 1 8 1024 0 330 | pierce_idx 328 1 6 1024 0 331 | passive_fire_mastery 329 1 12 1117 415 1024 8 0 9 50 88 4 1 ModitemdamFiresk ModitemdamFiresk 0 332 | passive_ltng_mastery 330 1 12 1054 408 1024 8 0 9 50 88 4 1 ModitemdamLtngsk ModitemdamLtngsk 0 333 | passive_cold_mastery 331 1 12 1295 379 1024 8 0 9 50 88 4 1 ModitemdamColdsk ModitemdamColdsk 0 334 | passive_pois_mastery 332 1 12 978 394 1024 8 0 9 50 88 4 1 ModitemdamPoissk ModitemdamPoissk 0 335 | passive_fire_pierce 333 1 9 2578 1024 8 0 8 0 88 20 1 Moditemenresfiresk Moditemenresfiresk 0 336 | passive_ltng_pierce 334 1 9 2493 1024 8 0 8 0 88 20 1 Moditemenresltngsk Moditemenresltngsk 0 337 | passive_cold_pierce 335 1 9 1984 1024 8 0 8 0 88 20 1 Moditemenrescoldsk Moditemenrescoldsk 0 338 | passive_pois_pierce 336 1 9 2345 1024 8 0 8 0 88 20 1 Moditemenrespoissk Moditemenrespoissk 0 339 | passive_critical_strike 337 1 9 1024 8 0 8 0 0 340 | passive_dodge 338 1 9 1024 7 0 7 0 0 341 | passive_avoid 339 1 9 1024 7 0 7 0 0 342 | passive_evade 340 1 9 1024 7 0 7 0 0 343 | passive_warmth 341 1 9 1024 8 0 8 0 0 344 | passive_mastery_melee_th 342 1 11 8 1024 8 0 8 0 0 345 | passive_mastery_melee_dmg 343 1 11 8 1024 8 0 8 0 0 346 | passive_mastery_melee_crit 344 1 9 8 1024 8 0 8 0 0 347 | passive_mastery_throw_th 345 1 11 8 1024 8 0 8 0 0 348 | passive_mastery_throw_dmg 346 1 11 8 1024 8 0 8 0 0 349 | passive_mastery_throw_crit 347 1 9 8 1024 8 0 8 0 0 350 | passive_weaponblock 348 1 9 8 1024 8 0 8 0 0 351 | passive_summon_resist 349 1 9 1024 8 0 8 0 0 352 | modifierlist_skill 350 9 1024 0 353 | modifierlist_level 351 8 1024 0 354 | last_sent_hp_pct 352 1 8 1024 0 355 | source_unit_type 353 5 1024 0 356 | source_unit_id 354 32 1024 0 357 | shortparam1 355 16 1024 0 358 | questitemdifficulty 356 1024 2 0 0 359 | passive_mag_mastery 357 1 12 1211 431 1024 8 0 9 50 0 360 | passive_mag_pierce 358 1 9 2812 1024 8 0 8 0 0 361 | -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/Resources/1.13c/Sets.txt: -------------------------------------------------------------------------------- 1 | lrg Civerb's Ward 2 | 2ax Berserker's Hatchet 3 | 6cs Naj's Puzzler 4 | 7gd Bul-Kathos' Sacred Charge 5 | 7ls Sazabi's Cobalt Redeemer 6 | 7m7 Immortal King's Stone Crusher 7 | 7ma Dangoon's Teaching 8 | 7qr Natalya's Mark 9 | 7wd Bul-Kathos' Tribal Guardian 10 | 7ws Griswolds's Redemption 11 | 9mt Aldur's Gauntlet 12 | 9vo Hwanin's Justice 13 | aar Milabrega's Robe 14 | amc M'avina's Caster 15 | amu Set Amulet 16 | ba5 Immortal King's Will 17 | bhm Tancred's Skull 18 | brs Isenhart's Case 19 | bsd Isenhart's Lightbrand 20 | bst Cathan's Rule 21 | buc Hsarus' Iron Fist 22 | bwn McAuley's Superstition 23 | cap Set cap 24 | chn Cathan's Mesh 25 | ci0 Naj's Circlet 26 | ci3 M'avina's True Sight 27 | crn Iratha's Coil 28 | crn Milabrega's Diadem 29 | dr8 Aldur's Stony Gaze 30 | fhl Isenhart's Horns 31 | ful Tancred's Spine 32 | ghm Sigon's Visor 33 | gsc Civerb's Cudgel 34 | gth Sigon's Shelter 35 | gts Isenhart's Parry 36 | gwn Infernal Torch 37 | hbl Sigon's Wrap 38 | hbt Sigon's Sabot 39 | hgl Sigon's Gage 40 | hlm Berserker's Headgear 41 | kit Milabrega's Orb 42 | lbb Vidala's Barb 43 | lbl Death's Guard 44 | lbt Tancred's Hobnails 45 | lea Vidala's Ambush 46 | lgl Death's Hand 47 | lsd Cleglaw's Tooth 48 | ltp Arcanna's Flesh 49 | mbl Set buckler 50 | mbt Hsarus' Iron Heel 51 | mgl Cleglaw's Pincers 52 | mpi Tancred's Crowbill 53 | msk Cathan's Visage 54 | ne9 Trang-Oul's Wing 55 | oba Tal Rasha's Lidless Eye 56 | paf Griswold's Honor 57 | qui Arctic Furs 58 | rin Set ring 59 | rng Angelic Mantle 60 | sbr Angelic Sickle 61 | skp Arcanna's Head 62 | sml Cleglaw's Claw 63 | spl Berserker's Hauberk 64 | stu Cow King's Hide 65 | swb Arctic Horn 66 | tbl Set belt 67 | tbt Vidala's Fetlock 68 | tgl Set gloves 69 | tow Sigon's Guard 70 | uar Immortal King's Soul Cage 71 | ucl Natalya's Shadow 72 | uh9 Trang-Oul's Guise 73 | uhm Ondal's Almighty 74 | uld M'avina's Embrace 75 | ulg Laying of Hands 76 | ult Naj's Light Plate 77 | umc Credendum 78 | upl Sazabi's Ghost Liberator 79 | urn Griswold's Valor 80 | utc Trang-Oul's Girth 81 | uth Tal Rasha's Howling Wind 82 | uts Heaven's Taebaek 83 | uui Spiritual Custodian 84 | uul Aldur's Deception 85 | vbl Arctic Binding 86 | vbt Set boots 87 | vgl McAuley's Taboo 88 | wsd Death's Touch 89 | wsp Milabrega's Rod 90 | wst Arcanna's Deathwand 91 | xap Cow King's Horns 92 | xar Griswold's Heart 93 | xcl Hwanin's Refuge 94 | xh9 Natalya's Totem 95 | xhb Immortal King's Pillar 96 | xhg Immortal King's Forge 97 | xhl Sazabi's Mental Sheath 98 | xhm Guillaume's Face 99 | xlb Rite of Passage 100 | xmb Natalya's Soul 101 | xmg Trang-Oul's Claws 102 | xml Wihtstan's Guard 103 | xrn Hwanin's Splendor 104 | xrs Haemosu's Adament 105 | xsk Tal Rasha's Horadric Crest 106 | xtb Aldur's Advance 107 | xtg M'avina's Icy Clutch 108 | xul Trang-Oul's Scales 109 | xvg Magnus' Skin 110 | zhb Immortal King's Detail 111 | zmb Tal Rasha's Fire-Spun Cloth 112 | ztb Wilhelm's Pride 113 | zvb M'avina's Tenet 114 | -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/Resources/AllItems.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarpyWar/d2s-character-editor/6304f41ca3b5973052ea86ace8f06d67824471b9/tools/esCharView/CharacterEditor/Resources/AllItems.txt -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/Resources/ItemGroups.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarpyWar/d2s-character-editor/6304f41ca3b5973052ea86ace8f06d67824471b9/tools/esCharView/CharacterEditor/Resources/ItemGroups.txt -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/Resources/RoT_1.A3.1/AllItems.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarpyWar/d2s-character-editor/6304f41ca3b5973052ea86ace8f06d67824471b9/tools/esCharView/CharacterEditor/Resources/RoT_1.A3.1/AllItems.txt -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/Resources/RoT_1.A3.1/ItemGroups.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarpyWar/d2s-character-editor/6304f41ca3b5973052ea86ace8f06d67824471b9/tools/esCharView/CharacterEditor/Resources/RoT_1.A3.1/ItemGroups.txt -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/Resources/RoT_1.A3.1/Sets.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarpyWar/d2s-character-editor/6304f41ca3b5973052ea86ace8f06d67824471b9/tools/esCharView/CharacterEditor/Resources/RoT_1.A3.1/Sets.txt -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/Resources/Sets.txt: -------------------------------------------------------------------------------- 1 | qui Autolycus' Robes 2 | 0b0 Barbarian's Point-H 3 | 0bs Iron Wolf's Broad Sword-H 4 | 0gd Sarevok's Master 5 | 0nn Borik's Nightblade 6 | 0sb Griffith's Parry 7 | 0st Mercenary's Pike-H 8 | 0sy Reaver of the Angel of A 9 | 1cs Crook of the Darkest Valley 10 | 1ls Branch of the Ents 11 | 1sb Rogue's Bow-H 12 | 7wb Sascha's Deadly Point 13 | 8lw Lixo's Harp 14 | 8sb Rogue's Bow-NM 15 | 8ws The Staff of the Magi 16 | 9b9 Barbarian's Point-NM 17 | 9be Lancelot's Prediction 18 | 9bs Iron Wolf's Broad Sword-NM 19 | 9cr Keeper of the Fire 20 | 9cs Alora's Silent Assault 21 | 9fc Krakerag's Point 22 | 9fl Darkshade's Evisceration 23 | 9gm Fyrre's Deception 24 | 9gs Krakerag's Slay 25 | 9gw Lim-Dul's Hex 26 | 9mt Darkshade's Skewer 27 | 9p9 Aanna's Touch 28 | 9pb Noir's Focus 29 | 9qr Sascha's Jagged Strike 30 | 9rd Nupraptor's Eradicator 31 | 9st Mercenary's Pike-NM 32 | 9yw The Lich's Curse 33 | aar Kain's Fear 34 | ahb Darien's Band 35 | alb Lim-Dul's Vault 36 | am3 Gabrielle's Pointed Staff 37 | ama Alaziel's Point 38 | amd Milea's Maim 39 | amu Amulet 40 | ba3 Barbarian's Visage-N 41 | ba8 Barbarian's Visage-NM 42 | ba9 Darkshade's Haze 43 | bad Barbarian's Visage-H 44 | bae Sarevok's Malicious Stare 45 | baf Crown of the Northern Tribes 46 | bhm Wrathamon's Skull 47 | bmb Tasselhoff's Pouches 48 | brg Worldstone Shard 49 | brs Tyrael's Halo 50 | bsd Iron Wolf's Broad Sword-N 51 | bsw Barbarian's Point-N 52 | btl Power 53 | btx Messerschmidt's Reaver 54 | buc Hsarus' Iron Fist-Dru 55 | cap Infernal Cranium-Nec 56 | cap Berserker's Headgear-Bar 57 | chn Iron Wolf's Robes-N 58 | ci0 Tara's Vision 59 | ci1 Divada's Tiara 60 | ci2 Raistlin's Glance 61 | ci3 Milea's Crown 62 | ci4 Lixo's Flight 63 | cix Vega's Beauty 64 | clb Hsaru's Iron Arm 65 | clk Death's Guard-Ass 66 | clw Grace 67 | crn Anduin's Vision 68 | crs Tyrael's Virtue 69 | dam Jaheira's Force 70 | dbt Wanderer's Waltz 71 | dr2 Blackwing's Will 72 | dr4 Danarak's Spirit 73 | dr8 Horns of Power 74 | drb Terra's Clairvoyance 75 | eht Gabrielle's Revelation 76 | elv Malek's Pride 77 | fhl Helm of Spirits 78 | ful Armor of Gloom 79 | ghm Iron Wolf's Coif-N 80 | gis Blackwing's Force 81 | gsd Blood Baron 82 | gts Kain's Sentinel 83 | hax Berserker's Hatchet-Bar 84 | hbl Achilles' Girdle 85 | hbt Achilles' Heel 86 | hbw Arctic Horn-Ama 87 | hgl Achilles' Force 88 | hla Berserker's Hauberk-Bar 89 | hlm Mercenary's Shiek-N 90 | irg Kain's Wings 91 | kit Iron Wolf's Guard-N 92 | ktr Death's Touch-Ass 93 | lbl Arcanna's Flesh-Sor 94 | lbt Boots of Sneaking/Hsarus' Iron Heel-Dru 95 | lea Rogue's Suit-N 96 | lgl Death's Hand-Ass 97 | lrg Tara's Mirror 98 | ltp Naj's Light Plate 99 | mau Danarak's Mallet 100 | mbl Infernal Sign-Nec 101 | mbt Blackwing's Icewoven Strap 102 | mgl Messerschmidt's Burning Palm 103 | mnb Divada's Focus 104 | msb Malek's Grip 105 | msk Rathol's Gaze 106 | nea Nupraptor's Servant 107 | neb Lim-Dul's High Guardian 108 | njt Motoko's Power 109 | nsy Rathol's Touch 110 | ob3 Tara's Eye 111 | obc Arion's Eye 112 | pa4 Anduin's Protector 113 | paa Lancelot's Heraldic Lion 114 | pam Griffith's Charm 115 | pbe Malek's Strike 116 | rin Ring 117 | rng Rathol's Fear 118 | rob Tara's Gown 119 | sam Jade Star 120 | sbw Rogue's Bow-N 121 | scl Gabrielle's Defense 122 | scp Milabrega's Rod-Pal 123 | shl Motoko's Kimono 124 | skp Rogue's Cap-N 125 | sml Milabrega's Orb-Pal 126 | spk Tyrael's Faith 127 | spl Barbarian's Shelter-N 128 | spt Mercenary's Pike-N 129 | srn Jade Crest 130 | sst Arcanna's Deathwand-Sor 131 | stu Mercenary's Wraps-N 132 | syn Wrathamon's Scythe of Doom 133 | tbl Vega's Sash 134 | tbt Messerschmidt's Brace 135 | tgl Blackwing's Clench 136 | tow Stormshield 137 | vbl Autolycus' Thieving Tools/Arctic Binding 138 | vbt Milabrega's Ironsoles-Pal 139 | vgl Arctic Mitts-Ama 140 | whm Anduin's Vanquisher 141 | wnd Infernal Torch-Nec 142 | wst Naj's Puzzler 143 | xap Alora's Veil 144 | xar Darkshade's Spined Sheath 145 | xbt Noir's Stride 146 | xcl Robes of Earth and Water 147 | xea Rogue's Suit-NM 148 | xh9 Nupraptor's Skull 149 | xhb Persephone's Heel 150 | xhg Ihsan's Clutch 151 | xhh Sascha's Bosom 152 | xhl Tharn's Ward 153 | xhm Iron Wolf's Coif-NM 154 | xhn Iron Wolf's Robes-NM 155 | xig Noir's Tenacity 156 | xit Iron Wolf's Guard-NM 157 | xkp Rogue's Cap-NM 158 | xla Lixo's Coat 159 | xlb Naj's Fireward 160 | xld Krakerag's Chestplate 161 | xlg The Hands of Wier 162 | xlk Alora's Silhouette 163 | xlm Mercenary's Shiek-NM 164 | xlt Aanna's Dress 165 | xlv Alaziel's Protector 166 | xmb Alora's Vestige 167 | xmg The Lich's Clutch 168 | xms Ihsan's Coil 169 | xng Wrathamon's Cloak of Night 170 | xpk Malek's Defense 171 | xpl Barbarian's Shelter-NM 172 | xrb Divada's Shawl 173 | xrn Malek's Sight 174 | xrs The Lich's Cage 175 | xsk The Lich's Pate 176 | xtb Aanna's March 177 | xtg Aanna's Skill 178 | xth Griffith's Protection 179 | xtp Robes of the Arch-Magi 180 | xtu Mercenary's Wraps-NM 181 | xuc Divada's Screen 182 | xui Danarak's Wrap 183 | xul Lancelot's Security 184 | xvb Sandals of the Wind 185 | xvg Bard's Graceful Hand 186 | yap Ihsan's Gaze 187 | yar Cow King's Steel Hide 188 | ybt Cow King's Steel Hoofs 189 | ycl Darien's Faith 190 | ydw Cow King's Steel Horns 191 | yea Rogue's Suit-H 192 | yh9 Mask of the Angel of Death 193 | yhg Jaheira's Caress 194 | yhh Borik's Dark Binding 195 | yhl Darien's Wit 196 | yhm Iron Wolf's Coif-H 197 | yhn Iron Wolf's Robes-H 198 | yht Noir's Anticipation 199 | yig Claws of the Dragon 200 | yit Iron Wolf's Guard-H 201 | ykp Rogue's Cap-H 202 | yla Jade Wrap 203 | ylb Sascha's Black Slipper 204 | yld Sarevok's Savage Heart 205 | ylg Arion's Hand 206 | ylk Ihsan's Spine 207 | ylm Mercenary's Shiek-H 208 | ylt Jaheira's Volition 209 | ylv Tasselhoff's Jerkin 210 | ymb Lim-Dul's Oath 211 | ymg Alaziel's Grip 212 | yml Alaziel's Buckler 213 | yng Milea's Mail 214 | ypl Barbarian's Shelter-H 215 | yrb Arion's Heart 216 | yrs Shepherd's Invocation 217 | ysk Griffith's Stare 218 | ytb Tasselhoff's Breaches 219 | ytg Shepherd's Blessing 220 | yth Flesh of the Angel of Chaos 221 | ytp Hide of the Tarrasque 222 | ytu Mercenary's Wraps-H 223 | yui Gaia's Carapace 224 | yul Harrogath's Heirloom 225 | yvb Jade Slipper 226 | yvg Borik's Black Hand 227 | zhb Darkshade's Binding 228 | zlb Root's Banded Restraint 229 | zmb Band of Famine 230 | ztb Jaheira's Girdle 231 | zvb The Lich's Sole 232 | -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/Resources/es300_R6D/AllItems.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarpyWar/d2s-character-editor/6304f41ca3b5973052ea86ace8f06d67824471b9/tools/esCharView/CharacterEditor/Resources/es300_R6D/AllItems.txt -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/Resources/es300_R6D/ItemGroups.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarpyWar/d2s-character-editor/6304f41ca3b5973052ea86ace8f06d67824471b9/tools/esCharView/CharacterEditor/Resources/es300_R6D/ItemGroups.txt -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/Resources/es300_R6D/Sets.txt: -------------------------------------------------------------------------------- 1 | qui Autolycus' Robes 2 | 0b0 Barbarian's Point-H 3 | 0bs Iron Wolf's Broad Sword-H 4 | 0gd Sarevok's Master 5 | 0nn Borik's Nightblade 6 | 0sb Griffith's Parry 7 | 0st Mercenary's Pike-H 8 | 0sy Reaver of the Angel of A 9 | 1cs Crook of the Darkest Valley 10 | 1ls Branch of the Ents 11 | 1sb Rogue's Bow-H 12 | 7wb Sascha's Deadly Point 13 | 8lw Lixo's Harp 14 | 8sb Rogue's Bow-NM 15 | 8ws The Staff of the Magi 16 | 9b9 Barbarian's Point-NM 17 | 9be Lancelot's Prediction 18 | 9bs Iron Wolf's Broad Sword-NM 19 | 9cr Keeper of the Fire 20 | 9cs Alora's Silent Assault 21 | 9fc Krakerag's Point 22 | 9fl Darkshade's Evisceration 23 | 9gm Fyrre's Deception 24 | 9gs Krakerag's Slay 25 | 9gw Lim-Dul's Hex 26 | 9mt Darkshade's Skewer 27 | 9p9 Aanna's Touch 28 | 9pb Noir's Focus 29 | 9qr Sascha's Jagged Strike 30 | 9rd Nupraptor's Eradicator 31 | 9st Mercenary's Pike-NM 32 | 9yw The Lich's Curse 33 | aar Kain's Fear 34 | ahb Darien's Band 35 | alb Lim-Dul's Vault 36 | am3 Gabrielle's Pointed Staff 37 | ama Alaziel's Point 38 | amd Milea's Maim 39 | amu Amulet 40 | ba3 Barbarian's Visage-N 41 | ba8 Barbarian's Visage-NM 42 | ba9 Darkshade's Haze 43 | bad Barbarian's Visage-H 44 | bae Sarevok's Malicious Stare 45 | baf Crown of the Northern Tribes 46 | bhm Wrathamon's Skull 47 | bmb Tasselhoff's Pouches 48 | brg Worldstone Shard 49 | brs Tyrael's Halo 50 | bsd Iron Wolf's Broad Sword-N 51 | bsw Barbarian's Point-N 52 | btl Power 53 | btx Messerschmidt's Reaver 54 | buc Hsarus' Iron Fist-Dru 55 | cap Infernal Cranium-Nec 56 | cap Berserker's Headgear-Bar 57 | chn Iron Wolf's Robes-N 58 | ci0 Tara's Vision 59 | ci1 Divada's Tiara 60 | ci2 Raistlin's Glance 61 | ci3 Milea's Crown 62 | ci4 Lixo's Flight 63 | cix Vega's Beauty 64 | clb Hsaru's Iron Arm 65 | clk Death's Guard-Ass 66 | clw Grace 67 | crn Anduin's Vision 68 | crs Tyrael's Virtue 69 | dam Jaheira's Force 70 | dbt Wanderer's Waltz 71 | dr2 Blackwing's Will 72 | dr4 Danarak's Spirit 73 | dr8 Horns of Power 74 | drb Terra's Clairvoyance 75 | eht Gabrielle's Revelation 76 | elv Malek's Pride 77 | fhl Helm of Spirits 78 | ful Armor of Gloom 79 | ghm Iron Wolf's Coif-N 80 | gis Blackwing's Force 81 | gsd Blood Baron 82 | gts Kain's Sentinel 83 | hax Berserker's Hatchet-Bar 84 | hbl Achilles' Girdle 85 | hbt Achilles' Heel 86 | hbw Arctic Horn-Ama 87 | hgl Achilles' Force 88 | hla Berserker's Hauberk-Bar 89 | hlm Mercenary's Shiek-N 90 | irg Kain's Wings 91 | kit Iron Wolf's Guard-N 92 | ktr Death's Touch-Ass 93 | lbl Arcanna's Flesh-Sor 94 | lbt Boots of Sneaking/Hsarus' Iron Heel-Dru 95 | lea Rogue's Suit-N 96 | lgl Death's Hand-Ass 97 | lrg Tara's Mirror 98 | ltp Naj's Light Plate 99 | mau Danarak's Mallet 100 | mbl Infernal Sign-Nec 101 | mbt Blackwing's Icewoven Strap 102 | mgl Messerschmidt's Burning Palm 103 | mnb Divada's Focus 104 | msb Malek's Grip 105 | msk Rathol's Gaze 106 | nea Nupraptor's Servant 107 | neb Lim-Dul's High Guardian 108 | njt Motoko's Power 109 | nsy Rathol's Touch 110 | ob3 Tara's Eye 111 | obc Arion's Eye 112 | pa4 Anduin's Protector 113 | paa Lancelot's Heraldic Lion 114 | pam Griffith's Charm 115 | pbe Malek's Strike 116 | rin Ring 117 | rng Rathol's Fear 118 | rob Tara's Gown 119 | sam Jade Star 120 | sbw Rogue's Bow-N 121 | scl Gabrielle's Defense 122 | scp Milabrega's Rod-Pal 123 | shl Motoko's Kimono 124 | skp Rogue's Cap-N 125 | sml Milabrega's Orb-Pal 126 | spk Tyrael's Faith 127 | spl Barbarian's Shelter-N 128 | spt Mercenary's Pike-N 129 | srn Jade Crest 130 | sst Arcanna's Deathwand-Sor 131 | stu Mercenary's Wraps-N 132 | syn Wrathamon's Scythe of Doom 133 | tbl Vega's Sash 134 | tbt Messerschmidt's Brace 135 | tgl Blackwing's Clench 136 | tow Stormshield 137 | vbl Autolycus' Thieving Tools/Arctic Binding 138 | vbt Milabrega's Ironsoles-Pal 139 | vgl Arctic Mitts-Ama 140 | whm Anduin's Vanquisher 141 | wnd Infernal Torch-Nec 142 | wst Naj's Puzzler 143 | xap Alora's Veil 144 | xar Darkshade's Spined Sheath 145 | xbt Noir's Stride 146 | xcl Robes of Earth and Water 147 | xea Rogue's Suit-NM 148 | xh9 Nupraptor's Skull 149 | xhb Persephone's Heel 150 | xhg Ihsan's Clutch 151 | xhh Sascha's Bosom 152 | xhl Tharn's Ward 153 | xhm Iron Wolf's Coif-NM 154 | xhn Iron Wolf's Robes-NM 155 | xig Noir's Tenacity 156 | xit Iron Wolf's Guard-NM 157 | xkp Rogue's Cap-NM 158 | xla Lixo's Coat 159 | xlb Naj's Fireward 160 | xld Krakerag's Chestplate 161 | xlg The Hands of Wier 162 | xlk Alora's Silhouette 163 | xlm Mercenary's Shiek-NM 164 | xlt Aanna's Dress 165 | xlv Alaziel's Protector 166 | xmb Alora's Vestige 167 | xmg The Lich's Clutch 168 | xms Ihsan's Coil 169 | xng Wrathamon's Cloak of Night 170 | xpk Malek's Defense 171 | xpl Barbarian's Shelter-NM 172 | xrb Divada's Shawl 173 | xrn Malek's Sight 174 | xrs The Lich's Cage 175 | xsk The Lich's Pate 176 | xtb Aanna's March 177 | xtg Aanna's Skill 178 | xth Griffith's Protection 179 | xtp Robes of the Arch-Magi 180 | xtu Mercenary's Wraps-NM 181 | xuc Divada's Screen 182 | xui Danarak's Wrap 183 | xul Lancelot's Security 184 | xvb Sandals of the Wind 185 | xvg Bard's Graceful Hand 186 | yap Ihsan's Gaze 187 | yar Cow King's Steel Hide 188 | ybt Cow King's Steel Hoofs 189 | ycl Darien's Faith 190 | ydw Cow King's Steel Horns 191 | yea Rogue's Suit-H 192 | yh9 Mask of the Angel of Death 193 | yhg Jaheira's Caress 194 | yhh Borik's Dark Binding 195 | yhl Darien's Wit 196 | yhm Iron Wolf's Coif-H 197 | yhn Iron Wolf's Robes-H 198 | yht Noir's Anticipation 199 | yig Claws of the Dragon 200 | yit Iron Wolf's Guard-H 201 | ykp Rogue's Cap-H 202 | yla Jade Wrap 203 | ylb Sascha's Black Slipper 204 | yld Sarevok's Savage Heart 205 | ylg Arion's Hand 206 | ylk Ihsan's Spine 207 | ylm Mercenary's Shiek-H 208 | ylt Jaheira's Volition 209 | ylv Tasselhoff's Jerkin 210 | ymb Lim-Dul's Oath 211 | ymg Alaziel's Grip 212 | yml Alaziel's Buckler 213 | yng Milea's Mail 214 | ypl Barbarian's Shelter-H 215 | yrb Arion's Heart 216 | yrs Shepherd's Invocation 217 | ysk Griffith's Stare 218 | ytb Tasselhoff's Breaches 219 | ytg Shepherd's Blessing 220 | yth Flesh of the Angel of Chaos 221 | ytp Hide of the Tarrasque 222 | ytu Mercenary's Wraps-H 223 | yui Gaia's Carapace 224 | yul Harrogath's Heirloom 225 | yvb Jade Slipper 226 | yvg Borik's Black Hand 227 | zhb Darkshade's Binding 228 | zlb Root's Banded Restraint 229 | zmb Band of Famine 230 | ztb Jaheira's Girdle 231 | zvb The Lich's Sole 232 | -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/SaveReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace CharacterEditor 8 | { 9 | public class SaveReader : ICloneable 10 | { 11 | /// 12 | /// Character's inventory 13 | /// 14 | private Inventory inventory; 15 | /// 16 | /// General character information 17 | /// 18 | private Character character; 19 | /// 20 | /// Character's stats 21 | /// 22 | private Stat stat; 23 | /// 24 | /// Character's skills 25 | /// 26 | private Skill skill; 27 | /// 28 | /// Unmodified character data from save file 29 | /// 30 | private byte[] OriginalCharacterBytes; 31 | /// 32 | /// Unmodified skill data from save file 33 | /// 34 | private byte[] OriginalSkillBytes; 35 | /// 36 | /// Unmodified inventory data from save file 37 | /// 38 | private byte[] OriginalInventoryBytes; 39 | 40 | /// 41 | /// Character's inventory 42 | /// 43 | public Inventory Inventory 44 | { 45 | get { return inventory; } 46 | } 47 | 48 | /// 49 | /// General character information 50 | /// 51 | public Character Character 52 | { 53 | get { return character; } 54 | } 55 | 56 | /// 57 | /// Character's stats (Str, dex, etc) 58 | /// 59 | public Stat Stat 60 | { 61 | get { return stat; } 62 | } 63 | 64 | /// 65 | /// Character's skills 66 | /// 67 | public Skill Skill 68 | { 69 | get { return skill; } 70 | } 71 | 72 | /// 73 | /// Position of the start of stat information (This should be hardcoded in all save files) 74 | /// 75 | private static int StatListBegin 76 | { 77 | get { return 765; } 78 | } 79 | 80 | /// 81 | /// Failed to decode character data 82 | /// 83 | public bool FailedCharacterDecoding { get; protected set; } 84 | 85 | /// 86 | /// Failed to decode skill data 87 | /// 88 | public bool FailedSkillDecoding { get; protected set; } 89 | 90 | /// 91 | /// Failed to decode inventory data 92 | /// 93 | public bool FailedInventoryDecoding { get; protected set; } 94 | 95 | /// 96 | /// Creates a new SaveReader 97 | /// 98 | public SaveReader(string resourceSet) 99 | { 100 | Resources.Instance.ResourceSet = resourceSet; 101 | ItemDefs.LoadItemDefs(); 102 | } 103 | 104 | /// 105 | /// Creates a new SaveReader with specified save file bytes and begins to process the data 106 | /// 107 | /// Raw bytes from save file 108 | public SaveReader(byte[] rawCharacterData) 109 | { 110 | Read(rawCharacterData); 111 | } 112 | 113 | /// 114 | /// Read character save from disk 115 | /// 116 | /// Path of save file 117 | public void Read(byte[] rawCharacterData) 118 | { 119 | ReadHeaders(rawCharacterData); 120 | } 121 | 122 | public byte[] GetBytes(bool skipFailedData = false) 123 | { 124 | byte[] characterBytes = (skipFailedData && FailedCharacterDecoding) ? OriginalCharacterBytes : Character.GetCharacterBytes(); 125 | byte[] statsBytes = Stat.GetStatBytes(); 126 | byte[] skillBytes = (skipFailedData && FailedSkillDecoding) ? OriginalSkillBytes : Skill.GetSkillBytes(); 127 | byte[] inventoryBytes = (skipFailedData && FailedInventoryDecoding) ? OriginalInventoryBytes : Inventory.GetInventoryBytes(Character.HasMercenary); 128 | byte[] rawCharacterData = new byte[characterBytes.Length + statsBytes.Length + skillBytes.Length + inventoryBytes.Length]; 129 | 130 | Array.Copy(characterBytes, rawCharacterData, characterBytes.Length); 131 | Array.Copy(statsBytes, 0, rawCharacterData, characterBytes.Length, statsBytes.Length); 132 | Array.Copy(skillBytes, 0, rawCharacterData, characterBytes.Length + statsBytes.Length, skillBytes.Length); 133 | Array.Copy(inventoryBytes, 0, rawCharacterData, characterBytes.Length + statsBytes.Length + skillBytes.Length, inventoryBytes.Length); 134 | 135 | FixHeaders(ref rawCharacterData); 136 | 137 | return rawCharacterData; 138 | } 139 | 140 | /// 141 | /// Saves player data to specified path 142 | /// 143 | /// Path to save character data as 144 | public void Write(Stream saveFile, bool skipFailedData = false) 145 | { 146 | var rawCharacterData = GetBytes(); 147 | 148 | using (BinaryWriter bw = new BinaryWriter(saveFile)) 149 | { 150 | bw.Write(rawCharacterData); 151 | } 152 | 153 | //File.WriteAllBytes(filePath, rawCharacterData); 154 | } 155 | 156 | /// 157 | /// Splits character data into several sections for easier parsing 158 | /// 159 | private void ReadHeaders(byte[] rawCharacterData) 160 | { 161 | if (rawCharacterData[0] != 0x55 || rawCharacterData[1] != 0xAA || rawCharacterData[2] != 0x55 || rawCharacterData[3] != 0xAA) 162 | { 163 | throw new Exception("Not a Diablo II Save file"); 164 | } 165 | 166 | OriginalCharacterBytes = null; 167 | OriginalSkillBytes = null; 168 | OriginalInventoryBytes = null; 169 | 170 | byte[] statBytes = GetStatBytes(rawCharacterData); 171 | byte[] characterBytes = GetCharacterBytes(rawCharacterData); 172 | byte[] skillBytes = GetSkillBytes(rawCharacterData); 173 | byte[] inventoryBytes = GetInventoryBytes(rawCharacterData); 174 | 175 | inventory = new Inventory(inventoryBytes); 176 | character = new Character(characterBytes); 177 | stat = new Stat(statBytes); 178 | skill = new Skill(skillBytes); 179 | 180 | // Stats will always be different, we're not reading the fractional portion of hp/mana/stamina 181 | FailedCharacterDecoding = !characterBytes.SequenceEqual(character.GetCharacterBytes()); 182 | FailedSkillDecoding = !skillBytes.SequenceEqual(skill.GetSkillBytes()); 183 | FailedInventoryDecoding = !inventoryBytes.SequenceEqual(inventory.GetInventoryBytes(character.HasMercenary)); 184 | 185 | if (FailedCharacterDecoding) 186 | { 187 | OriginalCharacterBytes = characterBytes; 188 | } 189 | if (FailedInventoryDecoding) 190 | { 191 | OriginalInventoryBytes = inventoryBytes; 192 | } 193 | if (FailedSkillDecoding) 194 | { 195 | OriginalSkillBytes = skillBytes; 196 | } 197 | } 198 | 199 | /// 200 | /// Obtains general character information from raw save file data 201 | /// 202 | /// 203 | /// 204 | private static byte[] GetCharacterBytes(byte[] rawCharacterData) 205 | { 206 | byte[] characterBytes = new byte[StatListBegin]; 207 | Array.Copy(rawCharacterData, characterBytes, characterBytes.Length); 208 | 209 | return characterBytes; 210 | } 211 | 212 | /// 213 | /// Copies character's raw stat data from raw header bytes 214 | /// 215 | /// Raw header data from save file 216 | /// Raw stat data 217 | private static byte[] GetStatBytes(byte[] rawCharacterBytes) 218 | { 219 | byte[] statsSection; 220 | int statsSectionLength = FindStatListEnd(rawCharacterBytes) - StatListBegin; 221 | 222 | statsSection = new byte[statsSectionLength]; 223 | Array.Copy(rawCharacterBytes, StatListBegin, statsSection, 0, statsSection.Length); 224 | 225 | return statsSection; 226 | } 227 | 228 | /// 229 | /// Obtains skill information from raw save file data 230 | /// 231 | /// 232 | /// 233 | private static byte[] GetSkillBytes(byte[] rawCharacterData) 234 | { 235 | int itemListBegin = FindItemListBegin(rawCharacterData); 236 | int statListEnd = FindStatListEnd(rawCharacterData); 237 | 238 | byte[] skillBytes = new byte[itemListBegin - statListEnd]; 239 | Array.Copy(rawCharacterData, statListEnd, skillBytes, 0, skillBytes.Length); 240 | 241 | return skillBytes; 242 | } 243 | 244 | /// 245 | /// Obtains inventory information from raw save file data 246 | /// 247 | /// 248 | /// 249 | private static byte[] GetInventoryBytes(byte[] rawCharacterData) 250 | { 251 | int itemListBegin = FindItemListBegin(rawCharacterData); 252 | 253 | byte[] inventoryBytes = new byte[rawCharacterData.Length - itemListBegin]; 254 | Array.Copy(rawCharacterData, itemListBegin, inventoryBytes, 0, inventoryBytes.Length); 255 | 256 | return inventoryBytes; 257 | } 258 | 259 | /// 260 | /// Corrects checksum of new player data 261 | /// 262 | /// Raw player save data 263 | private void FixHeaders(ref byte[] rawCharacterData) 264 | { 265 | byte[] fileSizeBytes = BitConverter.GetBytes(rawCharacterData.Length); 266 | Array.Copy(fileSizeBytes, 0, rawCharacterData, 8, 4); 267 | 268 | uint checksum = CalculateChecksum(rawCharacterData); 269 | 270 | byte[] checksumBytes = BitConverter.GetBytes(checksum); 271 | Array.Copy(checksumBytes, 0, rawCharacterData, 12, 4); 272 | } 273 | 274 | // 275 | /// 276 | /// Calculates a new checksum for specified data 277 | /// 278 | /// Raw character data 279 | /// Checksum for specified data 280 | /// Source: ehertlein ( http://forums.diii.net/showthread.php?t=532037&page=41 ) 281 | private static uint CalculateChecksum(byte[] fileBytes) 282 | { 283 | uint hexTest = 0x80000000; 284 | uint checksum = 0; 285 | 286 | // clear out the old checksum 287 | fileBytes[12] = 0; 288 | fileBytes[13] = 0; 289 | fileBytes[14] = 0; 290 | fileBytes[15] = 0; 291 | 292 | foreach (byte currentByte in fileBytes) 293 | { 294 | if ((checksum & hexTest) == hexTest) 295 | { 296 | checksum = checksum << 1; 297 | checksum = checksum + 1; 298 | } 299 | else 300 | { 301 | checksum = checksum << 1; 302 | } 303 | 304 | checksum += currentByte; 305 | } 306 | 307 | return checksum; 308 | } 309 | 310 | /// 311 | /// Returns the location of the inventory data 312 | /// 313 | /// Raw bytes from save file 314 | /// Location of inventory data 315 | private static int FindItemListBegin(byte[] rawCharacterData) 316 | { 317 | for (int i = StatListBegin; i < rawCharacterData.Length - 5; i++) 318 | { 319 | if (rawCharacterData[i] == 'J' && rawCharacterData[i + 1] == 'M') 320 | { 321 | // JM..JM is the pattern we're looking for 322 | if (rawCharacterData[i + 4] == 'J' && rawCharacterData[i + 5] == 'M') 323 | { 324 | return i; 325 | } 326 | } 327 | } 328 | 329 | return 0; 330 | } 331 | 332 | /// 333 | /// Returns location right after the end of the character's stat list 334 | /// 335 | /// Raw data from save file 336 | /// Location of the end of character's stat list 337 | private static int FindStatListEnd(byte[] rawCharacterBytes) 338 | { 339 | int itemListBegin = FindItemListBegin(rawCharacterBytes); 340 | 341 | if (rawCharacterBytes[itemListBegin - 37] == 'i' && rawCharacterBytes[itemListBegin - 36] == 'f') 342 | { 343 | return itemListBegin - 37; 344 | } 345 | 346 | for (int i = FindItemListBegin(rawCharacterBytes); i > StatListBegin; i--) 347 | { 348 | if (rawCharacterBytes[i] == 'i' && rawCharacterBytes[i + 1] == 'f') 349 | { 350 | return i; 351 | } 352 | } 353 | 354 | throw new Exception("End of stat list not found!"); 355 | } 356 | 357 | /// 358 | /// Move all allocated skill and stat points to pool of unspent points. Fails if golem exists 359 | /// 360 | /// TODO: Untested! 361 | public void Respec() 362 | { 363 | if (Inventory.GolemItems.Count > 0) 364 | { 365 | throw new Exception("Respec: Cannot respec with an active golem"); 366 | } 367 | 368 | int totalSkillPoints = Stat.SkillPoints; 369 | int totalStatPoints = stat.StatPoints; 370 | 371 | for (int i = 0; i < Skill.Length; i++) 372 | { 373 | totalSkillPoints += Skill[i]; 374 | Skill[i] = 0; 375 | } 376 | 377 | totalStatPoints += Stat.Strength; 378 | totalStatPoints += Stat.Dexterity; 379 | totalStatPoints += Stat.Vitality; 380 | totalStatPoints += Stat.Energy; 381 | 382 | Stat.Strength = 0; 383 | Stat.Dexterity = 0; 384 | Stat.Vitality = 0; 385 | Stat.Energy = 0; 386 | 387 | Stat.SkillPoints = totalSkillPoints; 388 | Stat.StatPoints = totalStatPoints; 389 | } 390 | 391 | public object Clone() 392 | { 393 | return this.MemberwiseClone(); 394 | } 395 | } 396 | } 397 | -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/Skill.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Collections; 6 | 7 | namespace CharacterEditor 8 | { 9 | /// 10 | /// Skill class for accessing raw skill data 11 | /// 12 | public class Skill : IEnumerable 13 | { 14 | /// 15 | /// Each byte in skillBytes represents level of a skill 16 | /// 17 | private byte[] skillBytes; 18 | 19 | /// 20 | /// Gets or sets the skill at specified index 21 | /// 22 | /// Index of skill to access 23 | /// Value of skill at specified index 24 | public byte this[int index] 25 | { 26 | get 27 | { 28 | return skillBytes[index+2]; 29 | } 30 | set 31 | { 32 | skillBytes[index+2] = value; 33 | } 34 | } 35 | 36 | /// 37 | /// Number of skills 38 | /// 39 | public int Length 40 | { 41 | get { return skillBytes.Length - 2; } 42 | } 43 | 44 | /// 45 | /// Creates a new skill class for accessing raw skill data 46 | /// 47 | /// Raw skill data from save file 48 | /// Skill data exists between the end of stat data and beginning of item data 49 | public Skill(byte[] skillBytes) 50 | { 51 | if (skillBytes[0] != 'i' || skillBytes[1] != 'f') 52 | { 53 | throw new Exception("SkillByte data missing if header"); 54 | } 55 | 56 | this.skillBytes = skillBytes; 57 | } 58 | 59 | /// 60 | /// Converts all skill data into raw data for save file 61 | /// 62 | /// Raw skill data ready for insertion into save file 63 | public byte[] GetSkillBytes() 64 | { 65 | return skillBytes; 66 | } 67 | 68 | #region IEnumerable Members 69 | 70 | public IEnumerator GetEnumerator() 71 | { 72 | for (int i = 0; i < skillBytes.Length; i++) 73 | { 74 | yield return skillBytes[i]; 75 | } 76 | } 77 | 78 | #endregion 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/Stat.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Collections; 6 | using BKSystem.IO; 7 | using System.ComponentModel; 8 | 9 | namespace CharacterEditor 10 | { 11 | /// 12 | /// Controls access to character's stats 13 | /// 14 | public class Stat : INotifyPropertyChanged 15 | { 16 | /// 17 | /// Raw stat data from save file 18 | /// 19 | private byte[] statsBytes; 20 | /// 21 | /// Decoded stat values 22 | /// 23 | private Dictionary statValues = new Dictionary(); 24 | 25 | public Stat(byte[] statsBytes) 26 | { 27 | if (statsBytes[0] != 'g' || statsBytes[1] != 'f') 28 | { 29 | throw new Exception("StatsByte data missing gf header"); 30 | } 31 | 32 | this.statsBytes = statsBytes; 33 | ReadStats(); 34 | } 35 | 36 | // TODO: Get rid of all of these individual properties? 37 | /// 38 | /// Base value of strength 39 | /// 40 | public int Strength 41 | { 42 | get { return GetStatValue("strength"); } 43 | set 44 | { 45 | SetStatValue("strength", value); 46 | OnPropertyChange("Strength"); 47 | } 48 | } 49 | /// 50 | /// Base value of energy 51 | /// 52 | public int Energy 53 | { 54 | get { return GetStatValue("energy"); } 55 | set 56 | { 57 | SetStatValue("energy", value); 58 | OnPropertyChange("Energy"); 59 | } 60 | } 61 | /// 62 | /// Base value of dexterity 63 | /// 64 | public int Dexterity 65 | { 66 | get { return GetStatValue("dexterity"); } 67 | set 68 | { 69 | SetStatValue("dexterity", value); 70 | OnPropertyChange("Dexterity"); 71 | } 72 | } 73 | /// 74 | /// Base value of vitality 75 | /// 76 | public int Vitality 77 | { 78 | get { return GetStatValue("vitality"); } 79 | set 80 | { 81 | SetStatValue("vitality", value); 82 | OnPropertyChange("Vitality"); 83 | } 84 | } 85 | /// 86 | /// Number of unallocated stat points 87 | /// 88 | public int StatPoints 89 | { 90 | get { return GetStatValue("statpts"); } 91 | set 92 | { 93 | SetStatValue("statpts", value); 94 | OnPropertyChange("StatPoints"); 95 | } 96 | } 97 | /// 98 | /// Number of unallocated skill points 99 | /// 100 | public int SkillPoints 101 | { 102 | get { return GetStatValue("newskills"); } 103 | set 104 | { 105 | SetStatValue("newskills", value); 106 | OnPropertyChange("SkillPoints"); 107 | } 108 | } 109 | /// 110 | /// Current value of hitpoints 111 | /// 112 | /// Current value is usually higher than base value 113 | public int Hitpoints 114 | { 115 | get { return GetStatValue("hitpoints"); } 116 | set 117 | { 118 | SetStatValue("hitpoints", value); 119 | OnPropertyChange("Hitpoints"); 120 | } 121 | } 122 | /// 123 | /// Base value of hitpoints 124 | /// 125 | public int BaseHitpoints 126 | { 127 | get { return GetStatValue("maxhp"); } 128 | set 129 | { 130 | SetStatValue("maxhp", value); 131 | OnPropertyChange("BaseHitpoints"); 132 | } 133 | } 134 | /// 135 | /// Current value of mana 136 | /// 137 | /// Current value is usually higher than base value 138 | public int Mana 139 | { 140 | get { return GetStatValue("mana"); } 141 | set 142 | { 143 | SetStatValue("mana", value); 144 | OnPropertyChange("Mana"); 145 | } 146 | } 147 | /// 148 | /// Base value of mana 149 | /// 150 | public int BaseMana 151 | { 152 | get { return GetStatValue("maxmana"); } 153 | set 154 | { 155 | SetStatValue("maxmana", value); 156 | OnPropertyChange("BaseMana"); 157 | } 158 | } 159 | /// 160 | /// Current value of stamina. 161 | /// 162 | /// Current value is usually higher than base value 163 | public int Stamina 164 | { 165 | get { return GetStatValue("stamina"); } 166 | set 167 | { 168 | SetStatValue("stamina", value); 169 | OnPropertyChange("Stamina"); 170 | } 171 | } 172 | /// 173 | /// Base value of stamina 174 | /// 175 | public int BaseStamina 176 | { 177 | get { return GetStatValue("maxstamina"); } 178 | set 179 | { 180 | SetStatValue("maxstamina", value); 181 | OnPropertyChange("BaseStamina"); 182 | } 183 | } 184 | /// 185 | /// Character's level 186 | /// 187 | public int Level 188 | { 189 | get { return GetStatValue("level"); } 190 | set 191 | { 192 | SetStatValue("level", value); 193 | OnPropertyChange("Level"); 194 | } 195 | } 196 | /// 197 | /// Number of experience points character has 198 | /// 199 | public uint Experience 200 | { 201 | get { return (uint)GetStatValue("experience"); } 202 | set 203 | { 204 | SetStatValue("experience", (int)value); 205 | OnPropertyChange("Experience"); 206 | } 207 | } 208 | /// 209 | /// Amount of gold character has in inventory 210 | /// 211 | public uint Gold 212 | { 213 | get { return (uint)GetStatValue("gold"); } 214 | set 215 | { 216 | SetStatValue("gold", (int)value); 217 | OnPropertyChange("Gold"); 218 | } 219 | } 220 | /// 221 | /// Amount of gold character has in the bank 222 | /// 223 | public uint GoldBank 224 | { 225 | get { return (uint)GetStatValue("goldbank"); } 226 | set 227 | { 228 | SetStatValue("goldbank", (int)value); 229 | OnPropertyChange("GoldBank"); 230 | } 231 | } 232 | /// 233 | /// Number of kills (Eastern sun only!) 234 | /// 235 | public int KillCount 236 | { 237 | get 238 | { 239 | if (Resources.Instance.ResourceSet == "es300_r6d") 240 | { 241 | return GetStatValue("kill_counter"); 242 | } 243 | return 0; 244 | } 245 | set 246 | { 247 | if (Resources.Instance.ResourceSet == "es300_r6d") 248 | { 249 | SetStatValue("kill_counter", value); 250 | OnPropertyChange("KillCount"); 251 | } 252 | } 253 | } 254 | /// 255 | /// Times character has died (Eastern sun only!) 256 | /// 257 | public int DeathCount 258 | { 259 | get 260 | { 261 | if (Resources.Instance.ResourceSet == "es300_r6d") 262 | { 263 | return GetStatValue("death_counter"); 264 | } 265 | return 0; 266 | } 267 | set 268 | { 269 | if (Resources.Instance.ResourceSet == "es300_r6d") 270 | { 271 | SetStatValue("death_counter", value); 272 | OnPropertyChange("DeathCount"); 273 | } 274 | } 275 | } 276 | 277 | /// 278 | /// Gets the specified stat's value 279 | /// 280 | /// Name of stat 281 | /// Value of stat or 0 if stat is not present 282 | private int GetStatValue(string name) 283 | { 284 | int statId = ItemDefs.ItemStatCostsByName[name].ID; 285 | 286 | if (!statValues.ContainsKey(statId)) 287 | { 288 | return 0; 289 | } 290 | 291 | return statValues[statId]; 292 | } 293 | 294 | /// 295 | /// Sets the specified stat to a given value or do nothing if stat is not present 296 | /// 297 | /// Name of stat 298 | /// Value to set stat to 299 | private void SetStatValue(string name, int value) 300 | { 301 | if (!ItemDefs.ItemStatCostsByName.ContainsKey(name)) 302 | { 303 | throw new Exception("SetStatValue: Invalid stat name"); 304 | } 305 | 306 | int statId = ItemDefs.ItemStatCostsByName[name].ID; 307 | 308 | // If value is 0, we assume user wants to delete the entry for that stat. 0 should 309 | // be default if no record exists when diablo loads the save so it should work out? 310 | if (value == 0) 311 | { 312 | statValues.Remove(statId); 313 | } 314 | else 315 | { 316 | statValues[statId] = value; 317 | } 318 | } 319 | 320 | /// 321 | /// Parses raw character stat data 322 | /// 323 | /// Raw characte stat data found between "gf" and "if" near offset 765 in save file 324 | /// Bit lengths of stat types are found in the CSvBits column of ItemStatCost.txt 325 | /// Source: http://phrozenkeep.hugelaser.com/forum/viewtopic.php?f=8&t=9011&start=50 326 | private void ReadStats() 327 | { 328 | BitStream bs = new BitStream(statsBytes); 329 | 330 | // Skip header bytes 331 | bs.SkipBits(8); 332 | bs.SkipBits(8); 333 | 334 | while (bs.RemainingBits >= 9) 335 | { 336 | // ID of stat (See ItemStatCost.txt) 337 | int statIndex = (int)bs.ReadReversed(9); 338 | // Value contains this many bits (See CSvBits in ItemStatCost.txt) 339 | int statValueBits = 0; 340 | // Value needs to be shifted by this amount 341 | int valShift = 0; 342 | 343 | // Terminating stat index 344 | if (statIndex == 0x1ff) 345 | { 346 | break; 347 | } 348 | 349 | statValueBits = ItemDefs.ItemStatCostsById[statIndex].CSvBits; 350 | if (statValueBits == 0) 351 | { 352 | break; 353 | } 354 | 355 | valShift = ItemDefs.ItemStatCostsById[(int)statIndex].ValShift; 356 | 357 | int statValue = (int)bs.ReadReversed(statValueBits); 358 | if (!statValues.ContainsKey(statIndex)) 359 | { 360 | statValues.Add(statIndex, (statValue >> valShift)); 361 | } 362 | } 363 | } 364 | 365 | /// 366 | /// Converts all stat data into raw data for save file 367 | /// 368 | /// Raw stat data ready for insertion into save file 369 | public byte[] GetStatBytes() 370 | { 371 | BitStream bits = new BitStream(); 372 | 373 | bits.WriteReversed('g', 8); 374 | bits.WriteReversed('f', 8); 375 | 376 | var sortedValues = from n in statValues where true orderby n.Key select n; 377 | 378 | foreach (var stat in sortedValues) 379 | { 380 | bits.WriteReversed(stat.Key, 9); 381 | 382 | int valShift = 0; 383 | int bitCount = ItemDefs.ItemStatCostsById[stat.Key].CSvBits; 384 | 385 | if (ItemDefs.ItemStatCostsById.ContainsKey(stat.Key)) 386 | { 387 | valShift = ItemDefs.ItemStatCostsById[stat.Key].ValShift; 388 | } 389 | 390 | bits.WriteReversed((uint)((stat.Value << valShift)), bitCount); 391 | } 392 | 393 | // Write termining stat index 394 | bits.WriteReversed(0x1ff, 9); 395 | 396 | // Add 0 padding to align to byte, if needed 397 | int remainingBitsForAlignment = 8 - (int)(bits.Position % 8); 398 | if (remainingBitsForAlignment > 0) 399 | { 400 | bits.WriteReversed(0, remainingBitsForAlignment); 401 | } 402 | 403 | return bits.ToReversedByteArray(); 404 | } 405 | 406 | #region INotifyPropertyChanged Members 407 | 408 | public event PropertyChangedEventHandler PropertyChanged; 409 | 410 | /// 411 | /// Used to notify listeners that a property has changed. Mainly for data binding 412 | /// on the GUI 413 | /// 414 | /// Name of property that was changed 415 | private void OnPropertyChange(string propertyName) 416 | { 417 | if (PropertyChanged != null) 418 | { 419 | PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 420 | } 421 | } 422 | 423 | #endregion 424 | } 425 | } 426 | -------------------------------------------------------------------------------- /tools/esCharView/CharacterEditor/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | #if SILVERLIGHT 8 | using System.Windows.Resources; 9 | using System.Windows; 10 | #endif 11 | 12 | // Utils for the silverlight app 13 | namespace CharacterEditor 14 | { 15 | public class StringUtils 16 | { 17 | /// 18 | /// Convert a string value into specified type. If string is null or empty, 19 | /// default value of type is returned (null for non-value types) 20 | /// 21 | /// String to parse for value 22 | /// Type of value 23 | /// Value of specified string for a given type 24 | public static object ConvertFromString(string value, Type t) 25 | { 26 | if (value.Length == 0) 27 | { 28 | if (t.IsValueType) 29 | { 30 | return Activator.CreateInstance(t); 31 | } 32 | else 33 | { 34 | if (t == typeof(string)) 35 | { 36 | return ""; 37 | } 38 | 39 | return null; 40 | } 41 | } 42 | 43 | if (t == typeof(bool) && value.Length == 1) 44 | { 45 | return value == "1"; 46 | } 47 | 48 | return Convert.ChangeType(value, t, null); 49 | } 50 | } 51 | 52 | public class ResourceUtils 53 | { 54 | public static Stream OpenResource(string assemblyName, string resourcePath) 55 | { 56 | #if SILVERLIGHT 57 | Uri resourceUri = new Uri(string.Format("{0};component/{1}", assemblyName, resourcePath), UriKind.Relative); 58 | StreamResourceInfo sri = Application.GetResourceStream(resourceUri); 59 | 60 | if (sri == null) 61 | { 62 | throw new FileNotFoundException("Failed to read file: " + resourceUri); 63 | } 64 | 65 | return sri.Stream; 66 | #else 67 | return File.Open(resourcePath, FileMode.Open); 68 | #endif 69 | } 70 | 71 | public static StreamReader OpenResourceText(string assemblyName, string resourcePath) 72 | { 73 | return new StreamReader(OpenResource(assemblyName, resourcePath)); 74 | } 75 | 76 | public static BinaryReader OpenResourceBinary(string assemblyName, string resourcePath) 77 | { 78 | return new BinaryReader(OpenResource(assemblyName, resourcePath)); 79 | } 80 | 81 | public static List ReadAllLines(string assemblyName, string resourcePath) 82 | { 83 | List lines = new List(); 84 | 85 | using (StreamReader sr = OpenResourceText(assemblyName, resourcePath)) 86 | { 87 | while (!sr.EndOfStream) 88 | { 89 | lines.Add(sr.ReadLine()); 90 | } 91 | } 92 | 93 | return lines; 94 | } 95 | 96 | public static string ReadToEnd(string assemblyName, string resourcePath) 97 | { 98 | using (StreamReader sr = OpenResourceText(assemblyName, resourcePath)) 99 | { 100 | return sr.ReadToEnd(); 101 | } 102 | } 103 | 104 | public static byte[] ReadAllBytes(string assemblyName, string resourcePath) 105 | { 106 | using (BinaryReader br = OpenResourceBinary(assemblyName, resourcePath)) 107 | { 108 | return br.ReadBytes((int)br.BaseStream.Length); 109 | } 110 | } 111 | } 112 | } --------------------------------------------------------------------------------