diff --git a/mods/Albion/Meta/3DFLOOR0.json b/mods/Albion/Meta/3DFLOOR0.json index c5f2e16be..4961569d5 100644 --- a/mods/Albion/Meta/3DFLOOR0.json +++ b/mods/Albion/Meta/3DFLOOR0.json @@ -1,101 +1,101 @@ { - "000": { "Id": "floor.1", "PaletteId": 3 }, - "001": { "PaletteId": 3 }, - "002": { "PaletteId": 3 }, - "003": { "PaletteId": 3 }, - "004": { "PaletteId": 3 }, - "005": { "PaletteId": 3 }, - "006": { "PaletteId": 3 }, - "007": { "PaletteId": 3 }, - "008": { "PaletteId": 3 }, - "009": { "PaletteId": 3 }, - "010": { "PaletteId": 3 }, - "011": { "PaletteId": 3 }, - "012": { "PaletteId": 3 }, - "013": { "PaletteId": 3 }, - "014": { "PaletteId": 3 }, - "015": { "PaletteId": 3 }, - "016": { "PaletteId": 3 }, - "017": { "PaletteId": 3 }, - "018": { "PaletteId": 3 }, - "019": { "PaletteId": 3 }, - "020": { "PaletteId": 3 }, - "021": { "PaletteId": 3 }, - "022": { "PaletteId": 3 }, - "023": { "PaletteId": 3 }, - "024": { "PaletteId": 3 }, - "025": { "PaletteId": 3 }, - "026": { "PaletteId": 3 }, - "027": { "PaletteId": 3 }, - "028": { "PaletteId": 3 }, - "029": { "PaletteId": 3 }, - "030": { "PaletteId": 3 }, - "031": { "PaletteId": 7 }, - "032": { "PaletteId": 7 }, - "033": { "PaletteId": 7 }, - "034": { "PaletteId": 7 }, - "035": { "PaletteId": 7 }, - "036": { "PaletteId": 7 }, - "037": { "PaletteId": 7 }, - "038": { "PaletteId": 7 }, - "039": { "PaletteId": 7 }, - "040": { "PaletteId": 8 }, - "041": { "PaletteId": 8 }, - "042": { "PaletteId": 8 }, - "043": { "PaletteId": 8 }, - "044": { "PaletteId": 8 }, - "045": { "PaletteId": 8 }, - "046": { "PaletteId": 8 }, - "047": { "PaletteId": 8 }, - "048": { "PaletteId": 8 }, - "049": { "PaletteId": 8 }, - "050": { "PaletteId": 13 }, - "051": { "PaletteId": 13 }, - "052": { "PaletteId": 13 }, - "053": { "PaletteId": 13 }, - "054": { "PaletteId": 13 }, - "055": { "PaletteId": 13 }, - "056": { "PaletteId": 13 }, - "057": { "PaletteId": 13 }, - "058": { "PaletteId": 29 }, - "059": { "PaletteId": 29 }, - "060": { "PaletteId": 29 }, - "061": { "PaletteId": 29 }, - "062": { "PaletteId": 29 }, - "063": { "PaletteId": 29 }, - "064": { "PaletteId": 29 }, - "065": { "PaletteId": 29 }, - "066": { "PaletteId": 15 }, - "067": { "PaletteId": 15 }, - "068": { "PaletteId": 15 }, - "069": { "PaletteId": 15 }, - "070": { "PaletteId": 15 }, - "071": { "PaletteId": 15 }, - "072": { "PaletteId": 15 }, - "073": { "PaletteId": 15 }, - "074": { "PaletteId": 15 }, - "075": { "PaletteId": 15 }, - "076": { "PaletteId": 15 }, - "077": { "PaletteId": 15 }, - "078": { "PaletteId": 15 }, - "079": { "PaletteId": 15 }, - "080": { "PaletteId": 18 }, - "081": { "PaletteId": 18 }, - "082": { "PaletteId": 18 }, - "083": { "PaletteId": 18 }, - "084": { "PaletteId": 18 }, - "085": { "PaletteId": 18 }, - "086": { "PaletteId": 15 }, - "087": { "PaletteId": 15 }, - "088": { "PaletteId": 15 }, - "089": { "PaletteId": 15 }, - "090": { "PaletteId": 18 }, - "091": { "PaletteId": 18 }, - "092": { "PaletteId": 18 }, - "093": { "PaletteId": 22 }, - "094": { "PaletteId": 22 }, - "095": { "PaletteId": 22 }, - "096": { "PaletteId": 22 }, - "097": { "PaletteId": 22 }, - "098": { "PaletteId": 22 } + "floor.1": { "Palette": "pal.3" }, + "floor.2": { "Palette": "pal.3" }, + "floor.3": { "Palette": "pal.3" }, + "floor.4": { "Palette": "pal.3" }, + "floor.5": { "Palette": "pal.3" }, + "floor.6": { "Palette": "pal.3" }, + "floor.7": { "Palette": "pal.3" }, + "floor.8": { "Palette": "pal.3" }, + "floor.9": { "Palette": "pal.3" }, + "floor.10": { "Palette": "pal.3" }, + "floor.11": { "Palette": "pal.3" }, + "floor.12": { "Palette": "pal.3" }, + "floor.13": { "Palette": "pal.3" }, + "floor.14": { "Palette": "pal.3" }, + "floor.15": { "Palette": "pal.3" }, + "floor.16": { "Palette": "pal.3" }, + "floor.17": { "Palette": "pal.3" }, + "floor.18": { "Palette": "pal.3" }, + "floor.19": { "Palette": "pal.3" }, + "floor.20": { "Palette": "pal.3" }, + "floor.21": { "Palette": "pal.3" }, + "floor.22": { "Palette": "pal.3" }, + "floor.23": { "Palette": "pal.3" }, + "floor.24": { "Palette": "pal.3" }, + "floor.25": { "Palette": "pal.3" }, + "floor.26": { "Palette": "pal.3" }, + "floor.27": { "Palette": "pal.3" }, + "floor.28": { "Palette": "pal.3" }, + "floor.29": { "Palette": "pal.3" }, + "floor.30": { "Palette": "pal.3" }, + "floor.31": { "Palette": "pal.3" }, + "floor.32": { "Palette": "pal.7" }, + "floor.33": { "Palette": "pal.7" }, + "floor.34": { "Palette": "pal.7" }, + "floor.35": { "Palette": "pal.7" }, + "floor.36": { "Palette": "pal.7" }, + "floor.37": { "Palette": "pal.7" }, + "floor.38": { "Palette": "pal.7" }, + "floor.39": { "Palette": "pal.7" }, + "floor.40": { "Palette": "pal.7" }, + "floor.41": { "Palette": "pal.8" }, + "floor.42": { "Palette": "pal.8" }, + "floor.43": { "Palette": "pal.8" }, + "floor.44": { "Palette": "pal.8" }, + "floor.45": { "Palette": "pal.8" }, + "floor.46": { "Palette": "pal.8" }, + "floor.47": { "Palette": "pal.8" }, + "floor.48": { "Palette": "pal.8" }, + "floor.49": { "Palette": "pal.8" }, + "floor.50": { "Palette": "pal.8" }, + "floor.51": { "Palette": "pal.13" }, + "floor.52": { "Palette": "pal.13" }, + "floor.53": { "Palette": "pal.13" }, + "floor.54": { "Palette": "pal.13" }, + "floor.55": { "Palette": "pal.13" }, + "floor.56": { "Palette": "pal.13" }, + "floor.57": { "Palette": "pal.13" }, + "floor.58": { "Palette": "pal.13" }, + "floor.59": { "Palette": "pal.29" }, + "floor.60": { "Palette": "pal.29" }, + "floor.61": { "Palette": "pal.29" }, + "floor.62": { "Palette": "pal.29" }, + "floor.63": { "Palette": "pal.29" }, + "floor.64": { "Palette": "pal.29" }, + "floor.65": { "Palette": "pal.29" }, + "floor.66": { "Palette": "pal.29" }, + "floor.67": { "Palette": "pal.15" }, + "floor.68": { "Palette": "pal.15" }, + "floor.69": { "Palette": "pal.15" }, + "floor.70": { "Palette": "pal.15" }, + "floor.71": { "Palette": "pal.15" }, + "floor.72": { "Palette": "pal.15" }, + "floor.73": { "Palette": "pal.15" }, + "floor.74": { "Palette": "pal.15" }, + "floor.75": { "Palette": "pal.15" }, + "floor.76": { "Palette": "pal.15" }, + "floor.77": { "Palette": "pal.15" }, + "floor.78": { "Palette": "pal.15" }, + "floor.79": { "Palette": "pal.15" }, + "floor.80": { "Palette": "pal.15" }, + "floor.81": { "Palette": "pal.18" }, + "floor.82": { "Palette": "pal.18" }, + "floor.83": { "Palette": "pal.18" }, + "floor.84": { "Palette": "pal.18" }, + "floor.85": { "Palette": "pal.18" }, + "floor.86": { "Palette": "pal.18" }, + "floor.87": { "Palette": "pal.15" }, + "floor.88": { "Palette": "pal.15" }, + "floor.89": { "Palette": "pal.15" }, + "floor.90": { "Palette": "pal.15" }, + "floor.91": { "Palette": "pal.18" }, + "floor.92": { "Palette": "pal.18" }, + "floor.93": { "Palette": "pal.18" }, + "floor.94": { "Palette": "pal.22" }, + "floor.95": { "Palette": "pal.22" }, + "floor.96": { "Palette": "pal.22" }, + "floor.97": { "Palette": "pal.22" }, + "floor.98": { "Palette": "pal.22" }, + "floor.99": { "Palette": "pal.22" } } diff --git a/mods/Albion/Meta/3DFLOOR1.json b/mods/Albion/Meta/3DFLOOR1.json index ffc86f6a3..be3389287 100644 --- a/mods/Albion/Meta/3DFLOOR1.json +++ b/mods/Albion/Meta/3DFLOOR1.json @@ -1,102 +1,102 @@ { - "000": { "Id": "floor.100", "PaletteId": 22 }, - "001": { "PaletteId": 22 }, - "002": { "PaletteId": 22 }, - "003": { "PaletteId": 22 }, - "004": { "PaletteId": 22 }, - "005": { "PaletteId": 22 }, - "006": { "PaletteId": 22 }, - "007": { "PaletteId": 22 }, - "008": { "PaletteId": 22 }, - "009": { "PaletteId": 22 }, - "010": { "PaletteId": 25 }, - "011": { "PaletteId": 25 }, - "012": { "PaletteId": 25 }, - "013": { "PaletteId": 25 }, - "014": { "PaletteId": 25 }, - "015": { "PaletteId": 25 }, - "016": { "PaletteId": 25 }, - "017": { "PaletteId": 25 }, - "018": { "PaletteId": 25 }, - "019": { "PaletteId": 25 }, - "020": { "PaletteId": 25 }, - "021": { "PaletteId": 25 }, - "022": { "PaletteId": 25 }, - "023": { "PaletteId": 25 }, - "024": { "PaletteId": 25 }, - "025": { "PaletteId": 25 }, - "026": { "PaletteId": 25 }, - "027": { "PaletteId": 25 }, - "028": { "PaletteId": 25 }, - "029": { "PaletteId": 25 }, - "030": { "PaletteId": 25 }, - "031": { "PaletteId": 25 }, - "032": { "PaletteId": 25 }, - "033": { "PaletteId": 25 }, - "034": { "PaletteId": 25 }, - "035": { "PaletteId": 25 }, - "036": { "PaletteId": 25 }, - "037": { "PaletteId": 25 }, - "038": { "PaletteId": 25 }, - "039": { "PaletteId": 25 }, - "040": { "PaletteId": 25 }, - "041": { "PaletteId": 25 }, - "042": { "PaletteId": 25 }, - "043": { "PaletteId": 25 }, - "044": { "PaletteId": 25 }, - "045": { "PaletteId": 25 }, - "046": { "PaletteId": 25 }, - "047": { "PaletteId": 25 }, - "048": { "PaletteId": 25 }, - "049": { "PaletteId": 25 }, - "050": { "PaletteId": 25 }, - "051": { "PaletteId": 25 }, - "052": { "PaletteId": 25 }, - "053": { "PaletteId": 25 }, - "054": { "PaletteId": 25 }, - "055": { "PaletteId": 22 }, - "056": { "PaletteId": 22 }, - "057": { "PaletteId": 22 }, - "058": { "PaletteId": 22 }, - "059": { "PaletteId": 22 }, - "060": { "PaletteId": 22 }, - "061": { "PaletteId": 22 }, - "062": { "PaletteId": 22 }, - "063": { "PaletteId": 22 }, - "064": { "PaletteId": 22 }, - "065": { "PaletteId": 26 }, - "066": { "PaletteId": 26 }, - "067": { "PaletteId": 26 }, - "068": { "PaletteId": 26 }, - "069": { "PaletteId": 26 }, - "070": { "PaletteId": 26 }, - "071": { "PaletteId": 26 }, - "072": { "PaletteId": 26 }, - "073": { "PaletteId": 26 }, - "074": { "PaletteId": 26 }, - "075": { "PaletteId": 26 }, - "076": { "PaletteId": 26 }, - "077": { "PaletteId": 7 }, - "078": { "PaletteId": 7 }, - "079": { "PaletteId": 7 }, - "080": { "PaletteId": 7 }, - "081": { "PaletteId": 7 }, - "082": { "PaletteId": 7 }, - "083": { "PaletteId": 7 }, - "084": { "PaletteId": 7 }, - "085": { "PaletteId": 7 }, - "086": { "PaletteId": 7 }, - "087": { "PaletteId": 7 }, - "088": { "PaletteId": 7 }, - "089": { "PaletteId": 7 }, - "090": { "PaletteId": 7 }, - "091": { "PaletteId": 7 }, - "092": { "PaletteId": 7 }, - "093": { "PaletteId": 8 }, - "094": { "PaletteId": 8 }, - "095": { "PaletteId": 8 }, - "096": { "PaletteId": 8 }, - "097": { "PaletteId": 8 }, - "098": { "PaletteId": 8 }, - "099": { "PaletteId": 25 } + "floor.100": { "Palette": "pal.22" }, + "floor.101": { "Palette": "pal.22" }, + "floor.102": { "Palette": "pal.22" }, + "floor.103": { "Palette": "pal.22" }, + "floor.104": { "Palette": "pal.22" }, + "floor.105": { "Palette": "pal.22" }, + "floor.106": { "Palette": "pal.22" }, + "floor.107": { "Palette": "pal.22" }, + "floor.108": { "Palette": "pal.22" }, + "floor.109": { "Palette": "pal.22" }, + "floor.110": { "Palette": "pal.25" }, + "floor.111": { "Palette": "pal.25" }, + "floor.112": { "Palette": "pal.25" }, + "floor.113": { "Palette": "pal.25" }, + "floor.114": { "Palette": "pal.25" }, + "floor.115": { "Palette": "pal.25" }, + "floor.116": { "Palette": "pal.25" }, + "floor.117": { "Palette": "pal.25" }, + "floor.118": { "Palette": "pal.25" }, + "floor.119": { "Palette": "pal.25" }, + "floor.120": { "Palette": "pal.25" }, + "floor.121": { "Palette": "pal.25" }, + "floor.122": { "Palette": "pal.25" }, + "floor.123": { "Palette": "pal.25" }, + "floor.124": { "Palette": "pal.25" }, + "floor.125": { "Palette": "pal.25" }, + "floor.126": { "Palette": "pal.25" }, + "floor.127": { "Palette": "pal.25" }, + "floor.128": { "Palette": "pal.25" }, + "floor.129": { "Palette": "pal.25" }, + "floor.130": { "Palette": "pal.25" }, + "floor.131": { "Palette": "pal.25" }, + "floor.132": { "Palette": "pal.25" }, + "floor.133": { "Palette": "pal.25" }, + "floor.134": { "Palette": "pal.25" }, + "floor.135": { "Palette": "pal.25" }, + "floor.136": { "Palette": "pal.25" }, + "floor.137": { "Palette": "pal.25" }, + "floor.138": { "Palette": "pal.25" }, + "floor.139": { "Palette": "pal.25" }, + "floor.140": { "Palette": "pal.25" }, + "floor.141": { "Palette": "pal.25" }, + "floor.142": { "Palette": "pal.25" }, + "floor.143": { "Palette": "pal.25" }, + "floor.144": { "Palette": "pal.25" }, + "floor.145": { "Palette": "pal.25" }, + "floor.146": { "Palette": "pal.25" }, + "floor.147": { "Palette": "pal.25" }, + "floor.148": { "Palette": "pal.25" }, + "floor.149": { "Palette": "pal.25" }, + "floor.150": { "Palette": "pal.25" }, + "floor.151": { "Palette": "pal.25" }, + "floor.152": { "Palette": "pal.25" }, + "floor.153": { "Palette": "pal.25" }, + "floor.154": { "Palette": "pal.25" }, + "floor.155": { "Palette": "pal.22" }, + "floor.156": { "Palette": "pal.22" }, + "floor.157": { "Palette": "pal.22" }, + "floor.158": { "Palette": "pal.22" }, + "floor.159": { "Palette": "pal.22" }, + "floor.160": { "Palette": "pal.22" }, + "floor.161": { "Palette": "pal.22" }, + "floor.162": { "Palette": "pal.22" }, + "floor.163": { "Palette": "pal.22" }, + "floor.164": { "Palette": "pal.22" }, + "floor.165": { "Palette": "pal.26" }, + "floor.166": { "Palette": "pal.26" }, + "floor.167": { "Palette": "pal.26" }, + "floor.168": { "Palette": "pal.26" }, + "floor.169": { "Palette": "pal.26" }, + "floor.170": { "Palette": "pal.26" }, + "floor.171": { "Palette": "pal.26" }, + "floor.172": { "Palette": "pal.26" }, + "floor.173": { "Palette": "pal.26" }, + "floor.174": { "Palette": "pal.26" }, + "floor.175": { "Palette": "pal.26" }, + "floor.176": { "Palette": "pal.26" }, + "floor.177": { "Palette": "pal.7" }, + "floor.178": { "Palette": "pal.7" }, + "floor.179": { "Palette": "pal.7" }, + "floor.180": { "Palette": "pal.7" }, + "floor.181": { "Palette": "pal.7" }, + "floor.182": { "Palette": "pal.7" }, + "floor.183": { "Palette": "pal.7" }, + "floor.184": { "Palette": "pal.7" }, + "floor.185": { "Palette": "pal.7" }, + "floor.186": { "Palette": "pal.7" }, + "floor.187": { "Palette": "pal.7" }, + "floor.188": { "Palette": "pal.7" }, + "floor.189": { "Palette": "pal.7" }, + "floor.190": { "Palette": "pal.7" }, + "floor.191": { "Palette": "pal.7" }, + "floor.192": { "Palette": "pal.7" }, + "floor.193": { "Palette": "pal.8" }, + "floor.194": { "Palette": "pal.8" }, + "floor.195": { "Palette": "pal.8" }, + "floor.196": { "Palette": "pal.8" }, + "floor.197": { "Palette": "pal.8" }, + "floor.198": { "Palette": "pal.8" }, + "floor.199": { "Palette": "pal.25" } } diff --git a/mods/Albion/Meta/3DFLOOR2.json b/mods/Albion/Meta/3DFLOOR2.json index 6ae1981cf..b3710371d 100644 --- a/mods/Albion/Meta/3DFLOOR2.json +++ b/mods/Albion/Meta/3DFLOOR2.json @@ -1,85 +1,85 @@ { - "000": { "Id": "floor.200", "PaletteId": 25 }, - "001": { "PaletteId": 25 }, - "002": { "PaletteId": 25 }, - "003": { "PaletteId": 25 }, - "004": { "PaletteId": 25 }, - "005": { "PaletteId": 25 }, - "006": { "PaletteId": 25 }, - "007": { "PaletteId": 25 }, - "008": { "PaletteId": 25 }, - "009": { "PaletteId": 25 }, - "010": { "PaletteId": 25 }, - "011": { "PaletteId": 25 }, - "012": { "PaletteId": 25 }, - "013": { "PaletteId": 25 }, - "014": { "PaletteId": 25 }, - "015": { "PaletteId": 25 }, - "016": { "PaletteId": 25 }, - "017": { "PaletteId": 25 }, - "018": { "PaletteId": 25 }, - "019": { "PaletteId": 25 }, - "020": { "PaletteId": 25 }, - "021": { "PaletteId": 25 }, - "022": { "PaletteId": 25 }, - "023": { "PaletteId": 25 }, - "024": { "PaletteId": 25 }, - "025": { "PaletteId": 25 }, - "026": { "PaletteId": 25 }, - "027": { "PaletteId": 25 }, - "028": { "PaletteId": 25 }, - "029": { "PaletteId": 25 }, - "030": { "PaletteId": 25 }, - "031": { "PaletteId": 25 }, - "032": { "PaletteId": 25 }, - "033": { "PaletteId": 25 }, - "034": { "PaletteId": 25 }, - "035": { "PaletteId": 7 }, - "036": { "PaletteId": 7 }, - "037": { "PaletteId": 7 }, - "038": { "PaletteId": 7 }, - "039": { "PaletteId": 7 }, - "040": { "PaletteId": 7 }, - "041": { "PaletteId": 7 }, - "042": { "PaletteId": 18 }, - "043": { "PaletteId": 18 }, - "044": { "PaletteId": 18 }, - "045": { "PaletteId": 18 }, - "046": { "PaletteId": 18 }, - "047": { "PaletteId": 18 }, - "048": { "PaletteId": 18 }, - "049": { "PaletteId": 18 }, - "050": { "PaletteId": 13 }, - "051": { "PaletteId": 13 }, - "052": { "PaletteId": 13 }, - "053": { "PaletteId": 13 }, - "054": { "PaletteId": 13 }, - "055": { "PaletteId": 13 }, - "056": { "PaletteId": 13 }, - "057": { "PaletteId": 13 }, - "058": { "PaletteId": 13 }, - "059": { "PaletteId": 13 }, - "060": { "PaletteId": 13 }, - "061": { "PaletteId": 13 }, - "062": { "PaletteId": 13 }, - "063": { "PaletteId": 13 }, - "064": { "PaletteId": 13 }, - "065": { "PaletteId": 13 }, - "066": { "PaletteId": 13 }, - "067": { "PaletteId": 13 }, - "068": { "PaletteId": 13 }, - "069": { "PaletteId": 13 }, - "070": { "PaletteId": 22 }, - "071": { "PaletteId": 22 }, - "072": { "PaletteId": 22 }, - "073": { "PaletteId": 22 }, - "074": { "PaletteId": 22 }, - "075": { "PaletteId": 22 }, - "076": { "PaletteId": 22 }, - "077": { "PaletteId": 22 }, - "078": { "PaletteId": 22 }, - "079": { "PaletteId": 22 }, - "080": { "PaletteId": 22 }, - "081": { "PaletteId": 22 }, - "082": { "PaletteId": 22 } + "floor.200": { "Palette": "pal.25" }, + "floor.201": { "Palette": "pal.25" }, + "floor.202": { "Palette": "pal.25" }, + "floor.203": { "Palette": "pal.25" }, + "floor.204": { "Palette": "pal.25" }, + "floor.205": { "Palette": "pal.25" }, + "floor.206": { "Palette": "pal.25" }, + "floor.207": { "Palette": "pal.25" }, + "floor.208": { "Palette": "pal.25" }, + "floor.209": { "Palette": "pal.25" }, + "floor.210": { "Palette": "pal.25" }, + "floor.211": { "Palette": "pal.25" }, + "floor.212": { "Palette": "pal.25" }, + "floor.213": { "Palette": "pal.25" }, + "floor.214": { "Palette": "pal.25" }, + "floor.215": { "Palette": "pal.25" }, + "floor.216": { "Palette": "pal.25" }, + "floor.217": { "Palette": "pal.25" }, + "floor.218": { "Palette": "pal.25" }, + "floor.219": { "Palette": "pal.25" }, + "floor.220": { "Palette": "pal.25" }, + "floor.221": { "Palette": "pal.25" }, + "floor.222": { "Palette": "pal.25" }, + "floor.223": { "Palette": "pal.25" }, + "floor.224": { "Palette": "pal.25" }, + "floor.225": { "Palette": "pal.25" }, + "floor.226": { "Palette": "pal.25" }, + "floor.227": { "Palette": "pal.25" }, + "floor.228": { "Palette": "pal.25" }, + "floor.229": { "Palette": "pal.25" }, + "floor.230": { "Palette": "pal.25" }, + "floor.231": { "Palette": "pal.25" }, + "floor.232": { "Palette": "pal.25" }, + "floor.233": { "Palette": "pal.25" }, + "floor.234": { "Palette": "pal.25" }, + "floor.235": { "Palette": "pal.7" }, + "floor.236": { "Palette": "pal.7" }, + "floor.237": { "Palette": "pal.7" }, + "floor.238": { "Palette": "pal.7" }, + "floor.239": { "Palette": "pal.7" }, + "floor.240": { "Palette": "pal.7" }, + "floor.241": { "Palette": "pal.7" }, + "floor.242": { "Palette": "pal.18" }, + "floor.243": { "Palette": "pal.18" }, + "floor.244": { "Palette": "pal.18" }, + "floor.245": { "Palette": "pal.18" }, + "floor.246": { "Palette": "pal.18" }, + "floor.247": { "Palette": "pal.18" }, + "floor.248": { "Palette": "pal.18" }, + "floor.249": { "Palette": "pal.18" }, + "floor.250": { "Palette": "pal.13" }, + "floor.251": { "Palette": "pal.13" }, + "floor.252": { "Palette": "pal.13" }, + "floor.253": { "Palette": "pal.13" }, + "floor.254": { "Palette": "pal.13" }, + "floor.255": { "Palette": "pal.13" }, + "floor.256": { "Palette": "pal.13" }, + "floor.257": { "Palette": "pal.13" }, + "floor.258": { "Palette": "pal.13" }, + "floor.259": { "Palette": "pal.13" }, + "floor.260": { "Palette": "pal.13" }, + "floor.261": { "Palette": "pal.13" }, + "floor.262": { "Palette": "pal.13" }, + "floor.263": { "Palette": "pal.13" }, + "floor.264": { "Palette": "pal.13" }, + "floor.265": { "Palette": "pal.13" }, + "floor.266": { "Palette": "pal.13" }, + "floor.267": { "Palette": "pal.13" }, + "floor.268": { "Palette": "pal.13" }, + "floor.269": { "Palette": "pal.13" }, + "floor.270": { "Palette": "pal.22" }, + "floor.271": { "Palette": "pal.22" }, + "floor.272": { "Palette": "pal.22" }, + "floor.273": { "Palette": "pal.22" }, + "floor.274": { "Palette": "pal.22" }, + "floor.275": { "Palette": "pal.22" }, + "floor.276": { "Palette": "pal.22" }, + "floor.277": { "Palette": "pal.22" }, + "floor.278": { "Palette": "pal.22" }, + "floor.279": { "Palette": "pal.22" }, + "floor.280": { "Palette": "pal.22" }, + "floor.281": { "Palette": "pal.22" }, + "floor.282": { "Palette": "pal.22" } } diff --git a/mods/Albion/Meta/3DOBJEC0.json b/mods/Albion/Meta/3DOBJEC0.json index c13433fbb..8ab16fd47 100644 --- a/mods/Albion/Meta/3DOBJEC0.json +++ b/mods/Albion/Meta/3DOBJEC0.json @@ -1,101 +1,101 @@ { - "000": { "Id": "3dobj.1", "Width": 32, "PaletteId": 3 }, - "001": { "PaletteId": 3, "Width": 16 }, - "002": { "PaletteId": 3, "Width": 64 }, - "003": { "PaletteId": 3, "Width": 31 }, - "004": { "PaletteId": 3, "Width": 31 }, - "005": { "PaletteId": 3, "Width": 61 }, - "006": { "PaletteId": 3, "Width": 61 }, - "007": { "PaletteId": 3, "Width": 36 }, - "008": { "PaletteId": 3, "Width": 25 }, - "009": { "PaletteId": 3, "Width": 23 }, - "010": { "PaletteId": 3, "Width": 27 }, - "011": { "PaletteId": 3, "Width": 66 }, - "012": { "PaletteId": 3, "Width": 63 }, - "013": { "PaletteId": 3, "Width": 74 }, - "014": { "PaletteId": 3, "Width": 39 }, - "015": { "PaletteId": 3, "Width": 63 }, - "016": { "PaletteId": 3, "Width": 66 }, - "017": { "PaletteId": 3, "Width": 119 }, - "018": { "PaletteId": 3, "Width": 60 }, - "019": { "PaletteId": 3, "Width": 89 }, - "020": { "PaletteId": 3, "Width": 20 }, - "021": { "PaletteId": 3, "Width": 19 }, - "022": { "PaletteId": 3, "Width": 19 }, - "023": { "PaletteId": 3, "Width": 15 }, - "024": { "PaletteId": 3, "Width": 24 }, - "025": { "PaletteId": 3, "Width": 17 }, - "026": { "PaletteId": 3, "Width": 50, "Height": 128 }, - "027": { "PaletteId": 3, "Width": 51, "Height": 129 }, - "028": { "PaletteId": 3, "Width": 51, "Height": 129 }, - "029": { "PaletteId": 3, "Width": 42, "Height": 111 }, - "030": { "PaletteId": 3, "Width": 42, "Height": 111 }, - "031": { "PaletteId": 3, "Width": 50, "Height": 129 }, - "032": { "PaletteId": 3, "Width": 51, "Height": 130 }, - "033": { "PaletteId": 3, "Width": 51, "Height": 129 }, - "034": { "PaletteId": 3, "Width": 50 }, - "035": { "PaletteId": 3, "Width": 51 }, - "036": { "PaletteId": 3, "Width": 51 }, - "037": { "PaletteId": 3, "Width": 42 }, - "038": { "PaletteId": 3, "Width": 42 }, - "039": { "PaletteId": 3, "Width": 50 }, - "040": { "PaletteId": 3, "Width": 51 }, - "041": { "PaletteId": 3, "Width": 51 }, - "042": { "PaletteId": 7, "Width": 32 }, - "043": { "PaletteId": 7, "Width": 26 }, - "044": { "PaletteId": 7, "Width": 26 }, - "045": { "PaletteId": 13, "Width": 152 }, - "046": { "PaletteId": 13, "Width": 81 }, - "047": { "PaletteId": 13, "Width": 116 }, - "048": { "PaletteId": 13, "Width": 57 }, - "049": { "PaletteId": 13, "Width": 96 }, - "050": { "PaletteId": 13, "Width": 123 }, - "051": { "PaletteId": 13, "Width": 81 }, - "052": { "PaletteId": 15, "Width": 53 }, - "053": { "PaletteId": 15, "Width": 57, "Height": 135 }, - "054": { "PaletteId": 15, "Width": 28, "Height": 24 }, - "055": { "PaletteId": 15, "Width": 28, "Height": 24 }, - "056": { "PaletteId": 15, "Width": 32, "Height": 52 }, - "057": { "PaletteId": 15, "Width": 21, "Height": 49 }, - "058": { "PaletteId": 15, "Width": 25, "Height": 40 }, - "059": { "PaletteId": 15, "Width": 25, "Height": 40 }, - "060": { "PaletteId": 15, "Width": 21, "Height": 49 }, - "061": { "PaletteId": 15, "Width": 32, "Height": 52 }, - "062": { "PaletteId": 15, "Width": 43 }, - "063": { "PaletteId": 15, "Width": 39 }, - "064": { "PaletteId": 15, "Width": 53 }, - "065": { "PaletteId": 15, "Width": 43 }, - "066": { "PaletteId": 15, "Width": 39 }, - "067": { "PaletteId": 15, "Width": 42 }, - "068": { "PaletteId": 15, "Width": 42 }, - "069": { "PaletteId": 15, "Width": 26 }, - "070": { "PaletteId": 15, "Width": 63 }, - "071": { "Width": 26 }, - "072": { "Width": 17 }, - "073": { "PaletteId": 15, "Width": 26 }, - "074": { "PaletteId": 15, "Width": 31 }, - "075": { "PaletteId": 15, "Width": 20 }, - "076": { "PaletteId": 15, "Width": 19 }, - "077": { "PaletteId": 15, "Width": 54 }, - "078": { "PaletteId": 15, "Width": 33 }, - "079": { "PaletteId": 15, "Width": 54 }, - "080": { "PaletteId": 15, "Width": 33 }, - "081": { "PaletteId": 15, "Width": 100, "Height": 58 }, - "082": { "PaletteId": 15, "Width": 116, "Height": 105 }, - "083": { "PaletteId": 15, "Width": 53 }, - "084": { "PaletteId": 15, "Width": 53 }, - "085": { "PaletteId": 3, "Width": 106 }, - "086": { "PaletteId": 3, "Width": 106 }, - "087": { "PaletteId": 15, "Width": 78 }, - "088": { "PaletteId": 15, "Width": 78 }, - "089": { "PaletteId": 15, "Width": 87 }, - "090": { "PaletteId": 15, "Width": 87, "Height": 88 }, - "091": { "PaletteId": 15, "Width": 100, "Height": 177 }, - "092": { "PaletteId": 15, "Width": 47 }, - "093": { "PaletteId": 15, "Width": 39 }, - "094": { "PaletteId": 15, "Width": 51 }, - "095": { "PaletteId": 15, "Width": 48 }, - "096": { "PaletteId": 15, "Width": 34 }, - "097": { "PaletteId": 15, "Width": 40 }, - "098": { "PaletteId": 11, "Width": 24, "Height": 78 } + "3dobj.1": { "Palette": "pal.3", "Width": 32 }, + "3dobj.2": { "Palette": "pal.3", "Width": 16 }, + "3dobj.3": { "Palette": "pal.3", "Width": 64 }, + "3dobj.4": { "Palette": "pal.3", "Width": 31 }, + "3dobj.5": { "Palette": "pal.3", "Width": 31 }, + "3dobj.6": { "Palette": "pal.3", "Width": 61 }, + "3dobj.7": { "Palette": "pal.3", "Width": 61 }, + "3dobj.8": { "Palette": "pal.3", "Width": 36 }, + "3dobj.9": { "Palette": "pal.3", "Width": 25 }, + "3dobj.10": { "Palette": "pal.3", "Width": 23 }, + "3dobj.11": { "Palette": "pal.3", "Width": 27 }, + "3dobj.12": { "Palette": "pal.3", "Width": 66 }, + "3dobj.13": { "Palette": "pal.3", "Width": 63 }, + "3dobj.14": { "Palette": "pal.3", "Width": 74 }, + "3dobj.15": { "Palette": "pal.3", "Width": 39 }, + "3dobj.16": { "Palette": "pal.3", "Width": 63 }, + "3dobj.17": { "Palette": "pal.3", "Width": 66 }, + "3dobj.18": { "Palette": "pal.3", "Width": 119 }, + "3dobj.19": { "Palette": "pal.3", "Width": 60 }, + "3dobj.20": { "Palette": "pal.3", "Width": 89 }, + "3dobj.21": { "Palette": "pal.3", "Width": 20 }, + "3dobj.22": { "Palette": "pal.3", "Width": 19 }, + "3dobj.23": { "Palette": "pal.3", "Width": 19 }, + "3dobj.24": { "Palette": "pal.3", "Width": 15 }, + "3dobj.25": { "Palette": "pal.3", "Width": 24 }, + "3dobj.26": { "Palette": "pal.3", "Width": 17 }, + "3dobj.27": { "Palette": "pal.3", "Width": 50, "Height": 128 }, + "3dobj.28": { "Palette": "pal.3", "Width": 51, "Height": 129 }, + "3dobj.29": { "Palette": "pal.3", "Width": 51, "Height": 129 }, + "3dobj.30": { "Palette": "pal.3", "Width": 42, "Height": 111 }, + "3dobj.31": { "Palette": "pal.3", "Width": 42, "Height": 111 }, + "3dobj.32": { "Palette": "pal.3", "Width": 50, "Height": 129 }, + "3dobj.33": { "Palette": "pal.3", "Width": 51, "Height": 130 }, + "3dobj.34": { "Palette": "pal.3", "Width": 51, "Height": 129 }, + "3dobj.35": { "Palette": "pal.3", "Width": 50 }, + "3dobj.36": { "Palette": "pal.3", "Width": 51 }, + "3dobj.37": { "Palette": "pal.3", "Width": 51 }, + "3dobj.38": { "Palette": "pal.3", "Width": 42 }, + "3dobj.39": { "Palette": "pal.3", "Width": 42 }, + "3dobj.40": { "Palette": "pal.3", "Width": 50 }, + "3dobj.41": { "Palette": "pal.3", "Width": 51 }, + "3dobj.42": { "Palette": "pal.3", "Width": 51 }, + "3dobj.43": { "Palette": "pal.7", "Width": 32 }, + "3dobj.44": { "Palette": "pal.7", "Width": 26 }, + "3dobj.45": { "Palette": "pal.7", "Width": 26 }, + "3dobj.46": { "Palette": "pal.13", "Width": 152 }, + "3dobj.47": { "Palette": "pal.13", "Width": 81 }, + "3dobj.48": { "Palette": "pal.13", "Width": 116 }, + "3dobj.49": { "Palette": "pal.13", "Width": 57 }, + "3dobj.50": { "Palette": "pal.13", "Width": 96 }, + "3dobj.51": { "Palette": "pal.13", "Width": 123 }, + "3dobj.52": { "Palette": "pal.13", "Width": 81 }, + "3dobj.53": { "Palette": "pal.15", "Width": 53 }, + "3dobj.54": { "Palette": "pal.15", "Width": 57, "Height": 135 }, + "3dobj.55": { "Palette": "pal.15", "Width": 28, "Height": 24 }, + "3dobj.56": { "Palette": "pal.15", "Width": 28, "Height": 24 }, + "3dobj.57": { "Palette": "pal.15", "Width": 32, "Height": 52 }, + "3dobj.58": { "Palette": "pal.15", "Width": 21, "Height": 49 }, + "3dobj.59": { "Palette": "pal.15", "Width": 25, "Height": 40 }, + "3dobj.60": { "Palette": "pal.15", "Width": 25, "Height": 40 }, + "3dobj.61": { "Palette": "pal.15", "Width": 21, "Height": 49 }, + "3dobj.62": { "Palette": "pal.15", "Width": 32, "Height": 52 }, + "3dobj.63": { "Palette": "pal.15", "Width": 43 }, + "3dobj.64": { "Palette": "pal.15", "Width": 39 }, + "3dobj.65": { "Palette": "pal.15", "Width": 53 }, + "3dobj.66": { "Palette": "pal.15", "Width": 43 }, + "3dobj.67": { "Palette": "pal.15", "Width": 39 }, + "3dobj.68": { "Palette": "pal.15", "Width": 42 }, + "3dobj.69": { "Palette": "pal.15", "Width": 42 }, + "3dobj.70": { "Palette": "pal.15", "Width": 26 }, + "3dobj.71": { "Palette": "pal.15", "Width": 63 }, + "3dobj.72": { "Palette": "pal.15", "Width": 26 }, + "3dobj.73": { "Palette": "pal.15", "Width": 17 }, + "3dobj.74": { "Palette": "pal.15", "Width": 26 }, + "3dobj.75": { "Palette": "pal.15", "Width": 31 }, + "3dobj.76": { "Palette": "pal.15", "Width": 20 }, + "3dobj.77": { "Palette": "pal.15", "Width": 19 }, + "3dobj.78": { "Palette": "pal.15", "Width": 54 }, + "3dobj.79": { "Palette": "pal.15", "Width": 33 }, + "3dobj.80": { "Palette": "pal.15", "Width": 54 }, + "3dobj.81": { "Palette": "pal.15", "Width": 33 }, + "3dobj.82": { "Palette": "pal.15", "Width": 100, "Height": 58 }, + "3dobj.83": { "Palette": "pal.15", "Width": 116, "Height": 105 }, + "3dobj.84": { "Palette": "pal.15", "Width": 53 }, + "3dobj.85": { "Palette": "pal.15", "Width": 53 }, + "3dobj.86": { "Palette": "pal.3", "Width": 106 }, + "3dobj.87": { "Palette": "pal.3", "Width": 106 }, + "3dobj.88": { "Palette": "pal.15", "Width": 78 }, + "3dobj.89": { "Palette": "pal.15", "Width": 78 }, + "3dobj.90": { "Palette": "pal.15", "Width": 87 }, + "3dobj.91": { "Palette": "pal.15", "Width": 87, "Height": 88 }, + "3dobj.92": { "Palette": "pal.15", "Width": 100, "Height": 177 }, + "3dobj.93": { "Palette": "pal.15", "Width": 47 }, + "3dobj.94": { "Palette": "pal.15", "Width": 39 }, + "3dobj.95": { "Palette": "pal.15", "Width": 51 }, + "3dobj.96": { "Palette": "pal.15", "Width": 48 }, + "3dobj.97": { "Palette": "pal.15", "Width": 34 }, + "3dobj.98": { "Palette": "pal.15", "Width": 40 }, + "3dobj.99": { "Palette": "pal.11", "Width": 24, "Height": 78 } } diff --git a/mods/Albion/Meta/3DOBJEC1.json b/mods/Albion/Meta/3DOBJEC1.json index 496b70921..6d0b7d133 100644 --- a/mods/Albion/Meta/3DOBJEC1.json +++ b/mods/Albion/Meta/3DOBJEC1.json @@ -1,102 +1,102 @@ { - "000": { "Id": "3dobj.100", "Width": 24, "Height": 70, "PaletteId": 11 }, - "001": { "PaletteId": 11, "Width": 35, "Height": 82 }, - "002": { "PaletteId": 18, "Width": 74 }, - "003": { "PaletteId": 18, "Width": 74 }, - "004": { "PaletteId": 18, "Width": 74 }, - "005": { "PaletteId": 18, "Width": 74 }, - "006": { "PaletteId": 18, "Width": 24 }, - "007": { "PaletteId": 18, "Width": 79 }, - "008": { "PaletteId": 18, "Width": 79 }, - "009": { "PaletteId": 18, "Width": 79, "Height": 178 }, - "010": { "PaletteId": 18, "Width": 79, "Height": 178 }, - "011": { "PaletteId": 18, "Width": 24 }, - "012": { "PaletteId": 18, "Width": 24 }, - "013": { "PaletteId": 3, "Width": 64 }, - "014": { "PaletteId": 3, "Width": 119 }, - "015": { "PaletteId": 18, "Width": 90 }, - "016": { "PaletteId": 18, "Width": 89 }, - "017": { "PaletteId": 18, "Width": 35 }, - "018": { "PaletteId": 18, "Width": 31 }, - "019": { "PaletteId": 18, "Width": 33 }, - "020": { "PaletteId": 18, "Width": 36 }, - "021": { "PaletteId": 18, "Width": 33 }, - "022": { "PaletteId": 18, "Width": 31 }, - "023": { "PaletteId": 18, "Width": 46 }, - "024": { "PaletteId": 18, "Width": 22 }, - "025": { "PaletteId": 18, "Width": 16 }, - "026": { "PaletteId": 18, "Width": 16 }, - "027": { "PaletteId": 18, "Width": 23 }, - "028": { "PaletteId": 18, "Width": 24 }, - "029": { "PaletteId": 18, "Width": 18 }, - "030": { "PaletteId": 18, "Width": 14 }, - "031": { "PaletteId": 18, "Width": 24 }, - "032": { "PaletteId": 22, "Width": 31 }, - "033": { "PaletteId": 22, "Width": 26 }, - "034": { "PaletteId": 22, "Width": 19 }, - "035": { "PaletteId": 22, "Width": 20 }, - "036": { "PaletteId": 22, "Width": 14 }, - "037": { "PaletteId": 22, "Width": 10 }, - "038": { "PaletteId": 22, "Width": 67 }, - "039": { "PaletteId": 22, "Width": 54 }, - "040": { "PaletteId": 22, "Width": 33 }, - "041": { "PaletteId": 22, "Width": 18 }, - "042": { "PaletteId": 22, "Width": 21 }, - "043": { "PaletteId": 22, "Width": 20 }, - "044": { "PaletteId": 22, "Width": 19 }, - "045": { "PaletteId": 22, "Width": 21 }, - "046": { "PaletteId": 22, "Width": 42 }, - "047": { "PaletteId": 22, "Width": 31 }, - "048": { "PaletteId": 22, "Width": 57 }, - "049": { "PaletteId": 22, "Width": 50 }, - "050": { "PaletteId": 22, "Width": 44 }, - "051": { "PaletteId": 22, "Width": 31 }, - "052": { "PaletteId": 22, "Width": 33 }, - "053": { "PaletteId": 22, "Width": 66, "Height": 74 }, - "054": { "PaletteId": 22, "Width": 67, "Height": 94 }, - "055": { "PaletteId": 22, "Width": 40, "Height": 58 }, - "056": { "PaletteId": 22, "Width": 33, "Height": 48 }, - "057": { "PaletteId": 22, "Width": 64 }, - "058": { "PaletteId": 22, "Width": 64 }, - "059": { "PaletteId": 22, "Width": 63 }, - "060": { "PaletteId": 3, "Width": 58 }, - "061": { "PaletteId": 25, "Width": 141, "Height": 80 }, - "062": { "PaletteId": 25, "Width": 75 }, - "063": { "PaletteId": 25, "Width": 75 }, - "064": { "PaletteId": 25, "Width": 92 }, - "065": { "PaletteId": 25, "Width": 53, "ExtraBytes": 27 }, - "066": { "PaletteId": 25, "Width": 99 }, - "067": { "PaletteId": 25, "Width": 66 }, - "068": { "PaletteId": 25, "Width": 51 }, - "069": { "PaletteId": 25, "Width": 51 }, - "070": { "PaletteId": 25, "Width": 51 }, - "071": { "PaletteId": 25, "Width": 23 }, - "072": { "PaletteId": 25, "Width": 23 }, - "073": { "PaletteId": 25, "Width": 60 }, - "074": { "PaletteId": 25, "Width": 60 }, - "075": { "PaletteId": 25, "Width": 98 }, - "076": { "PaletteId": 25, "Width": 98 }, - "077": { "PaletteId": 25, "Width": 85 }, - "078": { "PaletteId": 25, "Width": 56 }, - "079": { "PaletteId": 25, "Width": 56 }, - "080": { "PaletteId": 25, "Width": 48 }, - "081": { "PaletteId": 25, "Width": 126 }, - "082": { "PaletteId": 25, "Width": 126 }, - "083": { "PaletteId": 25, "Width": 62 }, - "084": { "PaletteId": 25, "Width": 81 }, - "085": { "PaletteId": 25, "Width": 78 }, - "086": { "PaletteId": 25, "Width": 37 }, - "087": { "PaletteId": 25, "Width": 65 }, - "088": { "PaletteId": 25, "Width": 65 }, - "089": { "PaletteId": 25, "Width": 139 }, - "090": { "PaletteId": 25, "Width": 255 }, - "091": { "PaletteId": 25, "Width": 61 }, - "092": { "PaletteId": 25, "Width": 61 }, - "093": { "PaletteId": 25, "Width": 23 }, - "094": { "PaletteId": 25, "Width": 27 }, - "095": { "PaletteId": 25, "Width": 63 }, - "096": { "PaletteId": 25, "Width": 74 }, - "097": { "PaletteId": 25, "Width": 25 }, - "098": { "PaletteId": 25, "Width": 36 }, - "099": { "PaletteId": 22, "Width": 42 } + "3dobj.100": { "Palette": "pal.11", "Width": 24, "Height": 70 }, + "3dobj.101": { "Palette": "pal.11", "Width": 35, "Height": 82 }, + "3dobj.102": { "Palette": "pal.18", "Width": 74 }, + "3dobj.103": { "Palette": "pal.18", "Width": 74 }, + "3dobj.104": { "Palette": "pal.18", "Width": 74 }, + "3dobj.105": { "Palette": "pal.18", "Width": 74 }, + "3dobj.106": { "Palette": "pal.18", "Width": 24 }, + "3dobj.107": { "Palette": "pal.18", "Width": 79 }, + "3dobj.108": { "Palette": "pal.18", "Width": 79 }, + "3dobj.109": { "Palette": "pal.18", "Width": 79, "Height": 178 }, + "3dobj.110": { "Palette": "pal.18", "Width": 79, "Height": 178 }, + "3dobj.111": { "Palette": "pal.18", "Width": 24 }, + "3dobj.112": { "Palette": "pal.18", "Width": 24 }, + "3dobj.113": { "Palette": "pal.3", "Width": 64 }, + "3dobj.114": { "Palette": "pal.3", "Width": 119 }, + "3dobj.115": { "Palette": "pal.18", "Width": 90 }, + "3dobj.116": { "Palette": "pal.18", "Width": 89 }, + "3dobj.117": { "Palette": "pal.18", "Width": 35 }, + "3dobj.118": { "Palette": "pal.18", "Width": 31 }, + "3dobj.119": { "Palette": "pal.18", "Width": 33 }, + "3dobj.120": { "Palette": "pal.18", "Width": 36 }, + "3dobj.121": { "Palette": "pal.18", "Width": 33 }, + "3dobj.122": { "Palette": "pal.18", "Width": 31 }, + "3dobj.123": { "Palette": "pal.18", "Width": 46 }, + "3dobj.124": { "Palette": "pal.18", "Width": 22 }, + "3dobj.125": { "Palette": "pal.18", "Width": 16 }, + "3dobj.126": { "Palette": "pal.18", "Width": 16 }, + "3dobj.127": { "Palette": "pal.18", "Width": 23 }, + "3dobj.128": { "Palette": "pal.18", "Width": 24 }, + "3dobj.129": { "Palette": "pal.18", "Width": 18 }, + "3dobj.130": { "Palette": "pal.18", "Width": 14 }, + "3dobj.131": { "Palette": "pal.18", "Width": 24 }, + "3dobj.132": { "Palette": "pal.22", "Width": 31 }, + "3dobj.133": { "Palette": "pal.22", "Width": 26 }, + "3dobj.134": { "Palette": "pal.22", "Width": 19 }, + "3dobj.135": { "Palette": "pal.22", "Width": 20 }, + "3dobj.136": { "Palette": "pal.22", "Width": 14 }, + "3dobj.137": { "Palette": "pal.22", "Width": 10 }, + "3dobj.138": { "Palette": "pal.22", "Width": 67 }, + "3dobj.139": { "Palette": "pal.22", "Width": 54 }, + "3dobj.140": { "Palette": "pal.22", "Width": 33 }, + "3dobj.141": { "Palette": "pal.22", "Width": 18 }, + "3dobj.142": { "Palette": "pal.22", "Width": 21 }, + "3dobj.143": { "Palette": "pal.22", "Width": 20 }, + "3dobj.144": { "Palette": "pal.22", "Width": 19 }, + "3dobj.145": { "Palette": "pal.22", "Width": 21 }, + "3dobj.146": { "Palette": "pal.22", "Width": 42 }, + "3dobj.147": { "Palette": "pal.22", "Width": 31 }, + "3dobj.148": { "Palette": "pal.22", "Width": 57 }, + "3dobj.149": { "Palette": "pal.22", "Width": 50 }, + "3dobj.150": { "Palette": "pal.22", "Width": 44 }, + "3dobj.151": { "Palette": "pal.22", "Width": 31 }, + "3dobj.152": { "Palette": "pal.22", "Width": 33 }, + "3dobj.153": { "Palette": "pal.22", "Width": 66, "Height": 74 }, + "3dobj.154": { "Palette": "pal.22", "Width": 67, "Height": 94 }, + "3dobj.155": { "Palette": "pal.22", "Width": 40, "Height": 58 }, + "3dobj.156": { "Palette": "pal.22", "Width": 33, "Height": 48 }, + "3dobj.157": { "Palette": "pal.22", "Width": 64 }, + "3dobj.158": { "Palette": "pal.22", "Width": 64 }, + "3dobj.159": { "Palette": "pal.22", "Width": 63 }, + "3dobj.160": { "Palette": "pal.3", "Width": 58 }, + "3dobj.161": { "Palette": "pal.25", "Width": 141, "Height": 80 }, + "3dobj.162": { "Palette": "pal.25", "Width": 75 }, + "3dobj.163": { "Palette": "pal.25", "Width": 75 }, + "3dobj.164": { "Palette": "pal.25", "Width": 92 }, + "3dobj.165": { "Palette": "pal.25", "Width": 53, "ExtraBytes": 27 }, + "3dobj.166": { "Palette": "pal.25", "Width": 99 }, + "3dobj.167": { "Palette": "pal.25", "Width": 66 }, + "3dobj.168": { "Palette": "pal.25", "Width": 51 }, + "3dobj.169": { "Palette": "pal.25", "Width": 51 }, + "3dobj.170": { "Palette": "pal.25", "Width": 51 }, + "3dobj.171": { "Palette": "pal.25", "Width": 23 }, + "3dobj.172": { "Palette": "pal.25", "Width": 23 }, + "3dobj.173": { "Palette": "pal.25", "Width": 60 }, + "3dobj.174": { "Palette": "pal.25", "Width": 60 }, + "3dobj.175": { "Palette": "pal.25", "Width": 98 }, + "3dobj.176": { "Palette": "pal.25", "Width": 98 }, + "3dobj.177": { "Palette": "pal.25", "Width": 85 }, + "3dobj.178": { "Palette": "pal.25", "Width": 56 }, + "3dobj.179": { "Palette": "pal.25", "Width": 56 }, + "3dobj.180": { "Palette": "pal.25", "Width": 48 }, + "3dobj.181": { "Palette": "pal.25", "Width": 126 }, + "3dobj.182": { "Palette": "pal.25", "Width": 126 }, + "3dobj.183": { "Palette": "pal.25", "Width": 62 }, + "3dobj.184": { "Palette": "pal.25", "Width": 81 }, + "3dobj.185": { "Palette": "pal.25", "Width": 78 }, + "3dobj.186": { "Palette": "pal.25", "Width": 37 }, + "3dobj.187": { "Palette": "pal.25", "Width": 65 }, + "3dobj.188": { "Palette": "pal.25", "Width": 65 }, + "3dobj.189": { "Palette": "pal.25", "Width": 139 }, + "3dobj.190": { "Palette": "pal.25", "Width": 255 }, + "3dobj.191": { "Palette": "pal.25", "Width": 61 }, + "3dobj.192": { "Palette": "pal.25", "Width": 61 }, + "3dobj.193": { "Palette": "pal.25", "Width": 23 }, + "3dobj.194": { "Palette": "pal.25", "Width": 27 }, + "3dobj.195": { "Palette": "pal.25", "Width": 63 }, + "3dobj.196": { "Palette": "pal.25", "Width": 74 }, + "3dobj.197": { "Palette": "pal.25", "Width": 25 }, + "3dobj.198": { "Palette": "pal.25", "Width": 36 }, + "3dobj.199": { "Palette": "pal.22", "Width": 42 } } diff --git a/mods/Albion/Meta/3DOBJEC2.json b/mods/Albion/Meta/3DOBJEC2.json index 13662ceca..459a7858d 100644 --- a/mods/Albion/Meta/3DOBJEC2.json +++ b/mods/Albion/Meta/3DOBJEC2.json @@ -1,102 +1,102 @@ { - "000": { "Id": "3dobj.200", "Width": 57, "PaletteId": 22 }, - "001": { "PaletteId": 22, "Width": 48 }, - "002": { "PaletteId": 22, "Width": 32 }, - "003": { "PaletteId": 22, "Width": 18 }, - "004": { "PaletteId": 22, "Width": 21 }, - "005": { "PaletteId": 22, "Width": 67 }, - "006": { "PaletteId": 22, "Width": 68 }, - "007": { "PaletteId": 22, "Width": 50, "ExtraBytes": 21 }, - "008": { "PaletteId": 22, "Width": 28 }, - "009": { "PaletteId": 22, "Width": 25 }, - "010": { "PaletteId": 22, "Width": 21, "ExtraBytes": 11 }, - "011": { "PaletteId": 22, "Width": 15 }, - "012": { "PaletteId": 15, "Width": 106 }, - "013": { "PaletteId": 15, "Width": 140 }, - "014": { "PaletteId": 15, "Width": 100 }, - "015": { "PaletteId": 15, "Width": 96 }, - "016": { "PaletteId": 15, "Width": 56 }, - "017": { "PaletteId": 15, "Width": 63 }, - "018": { "PaletteId": 15, "Width": 63 }, - "019": { "PaletteId": 15, "Width": 63 }, - "020": { "PaletteId": 15, "Width": 65 }, - "021": { "PaletteId": 15, "Width": 122 }, - "022": { "PaletteId": 15, "Width": 38 }, - "023": { "PaletteId": 15, "Width": 57 }, - "024": { "PaletteId": 15, "Width": 33, "ExtraBytes": 24 }, - "025": { "PaletteId": 15, "Width": 42 }, - "026": { "PaletteId": 15, "Width": 30 }, - "027": { "PaletteId": 15, "Width": 17 }, - "028": { "PaletteId": 15, "Width": 12 }, - "029": { "PaletteId": 15, "Width": 16 }, - "030": { "PaletteId": 15, "Width": 8 }, - "031": { "PaletteId": 15, "Width": 7 }, - "032": { "PaletteId": 15, "Width": 68 }, - "033": { "PaletteId": 15, "Width": 71 }, - "034": { "PaletteId": 15, "Width": 85 }, - "035": { "PaletteId": 3, "Width": 51, "Height": 130 }, - "036": { "PaletteId": 3, "Width": 51, "Height": 129 }, - "037": { "PaletteId": 3, "Width": 50, "Height": 128 }, - "038": { "PaletteId": 3, "Width": 50, "Height": 129 }, - "039": { "PaletteId": 3, "Width": 56, "Height": 56 }, - "040": { "PaletteId": 3, "Width": 27, "Height": 74 }, - "041": { "PaletteId": 3, "Width": 55 }, - "042": { "PaletteId": 3, "Width": 55 }, - "043": { "PaletteId": 3, "Width": 55 }, - "044": { "PaletteId": 3, "Width": 100, "Height": 100 }, - "045": { "PaletteId": 9, "Width": 64 }, - "046": { "PaletteId": 9, "Width": 64 }, - "047": { "PaletteId": 7, "Width": 10 }, - "048": { "PaletteId": 7, "Width": 63, "Height": 101 }, - "049": { "PaletteId": 7, "Width": 31 }, - "050": { "PaletteId": 25, "Width": 39 }, - "051": { "PaletteId": 25, "Width": 63 }, - "052": { "PaletteId": 25, "Width": 60 }, - "053": { "PaletteId": 25, "Width": 119 }, - "054": { "PaletteId": 25, "Width": 89 }, - "055": { "PaletteId": 25, "Width": 66 }, - "056": { "PaletteId": 25, "Width": 42, "Height": 111 }, - "057": { "PaletteId": 25, "Width": 51, "Height": 129 }, - "058": { "PaletteId": 25, "Width": 51, "Height": 129 }, - "059": { "PaletteId": 25, "Width": 50, "Height": 128 }, - "060": { "PaletteId": 25, "Width": 50, "Height": 129 }, - "061": { "PaletteId": 8, "Width": 32 }, - "062": { "PaletteId": 8, "Width": 26 }, - "063": { "PaletteId": 8, "Width": 26 }, - "064": { "PaletteId": 25, "Width": 56, "Height": 129 }, - "065": { "PaletteId": 25, "Width": 56, "Height": 125 }, - "066": { "PaletteId": 25, "Width": 55, "Height": 129 }, - "067": { "PaletteId": 25, "Width": 56, "Height": 125 }, - "068": { "PaletteId": 25, "Width": 73, "Height": 130 }, - "069": { "PaletteId": 25, "Width": 60, "Height": 109 }, - "070": { "PaletteId": 25, "Width": 60, "Height": 109 }, - "071": { "PaletteId": 25, "Width": 45, "Height": 141 }, - "072": { "PaletteId": 25, "Width": 53, "Height": 141 }, - "073": { "PaletteId": 25, "Width": 47, "Height": 102 }, - "074": { "PaletteId": 25, "Width": 47, "Height": 103 }, - "075": { "PaletteId": 25, "Width": 61 }, - "076": { "PaletteId": 7, "Width": 64 }, - "077": { "PaletteId": 8, "Width": 26 }, - "078": { "PaletteId": 22, "Width": 64 }, - "079": { "PaletteId": 22, "Width": 64 }, - "080": { "PaletteId": 22, "Width": 64 }, - "081": { "PaletteId": 3, "Width": 145, "Height": 165 }, - "082": { "PaletteId": 3, "Width": 66 }, - "083": { "PaletteId": 3, "Width": 66 }, - "084": { "PaletteId": 9, "Width": 64 }, - "085": { "PaletteId": 9, "Width": 64 }, - "086": { "PaletteId": 3, "Width": 27, "Height": 74 }, - "087": { "PaletteId": 3, "Width": 27, "Height": 74 }, - "088": { "PaletteId": 15, "Width": 116 }, - "089": { "PaletteId": 3, "Width": 145, "Height": 165 }, - "090": { "PaletteId": 3, "Width": 81, "Height": 118 }, - "091": { "PaletteId": 3, "Width": 56, "Height": 56 }, - "092": { "PaletteId": 15, "Width": 85, "Height": 122 }, - "093": { "PaletteId": 3, "Width": 81, "Height": 119 }, - "094": { "PaletteId": 3, "Width": 155, "Height": 172 }, - "095": { "PaletteId": 15, "Width": 165, "Height": 147 }, - "096": { "PaletteId": 3, "Width": 24, "Height": 78 }, - "097": { "PaletteId": 3, "Width": 24, "Height": 70 }, - "098": { "PaletteId": 30, "Width": 51, "Height": 130 }, - "099": { "PaletteId": 30, "Width": 56, "Height": 129 } + "3dobj.200": { "Palette": "pal.22", "Width": 57 }, + "3dobj.201": { "Palette": "pal.22", "Width": 48 }, + "3dobj.202": { "Palette": "pal.22", "Width": 32 }, + "3dobj.203": { "Palette": "pal.22", "Width": 18 }, + "3dobj.204": { "Palette": "pal.22", "Width": 21 }, + "3dobj.205": { "Palette": "pal.22", "Width": 67 }, + "3dobj.206": { "Palette": "pal.22", "Width": 68 }, + "3dobj.207": { "Palette": "pal.22", "Width": 50, "ExtraBytes": 21 }, + "3dobj.208": { "Palette": "pal.22", "Width": 28 }, + "3dobj.209": { "Palette": "pal.22", "Width": 25 }, + "3dobj.210": { "Palette": "pal.22", "Width": 21, "ExtraBytes": 11 }, + "3dobj.211": { "Palette": "pal.22", "Width": 15 }, + "3dobj.212": { "Palette": "pal.15", "Width": 106 }, + "3dobj.213": { "Palette": "pal.15", "Width": 140 }, + "3dobj.214": { "Palette": "pal.15", "Width": 100 }, + "3dobj.215": { "Palette": "pal.15", "Width": 96 }, + "3dobj.216": { "Palette": "pal.15", "Width": 56 }, + "3dobj.217": { "Palette": "pal.15", "Width": 63 }, + "3dobj.218": { "Palette": "pal.15", "Width": 63 }, + "3dobj.219": { "Palette": "pal.15", "Width": 63 }, + "3dobj.220": { "Palette": "pal.15", "Width": 65 }, + "3dobj.221": { "Palette": "pal.15", "Width": 122 }, + "3dobj.222": { "Palette": "pal.15", "Width": 38 }, + "3dobj.223": { "Palette": "pal.15", "Width": 57 }, + "3dobj.224": { "Palette": "pal.15", "Width": 33, "ExtraBytes": 24 }, + "3dobj.225": { "Palette": "pal.15", "Width": 42 }, + "3dobj.226": { "Palette": "pal.15", "Width": 30 }, + "3dobj.227": { "Palette": "pal.15", "Width": 17 }, + "3dobj.228": { "Palette": "pal.15", "Width": 12 }, + "3dobj.229": { "Palette": "pal.15", "Width": 16 }, + "3dobj.230": { "Palette": "pal.15", "Width": 8 }, + "3dobj.231": { "Palette": "pal.15", "Width": 7 }, + "3dobj.232": { "Palette": "pal.15", "Width": 68 }, + "3dobj.233": { "Palette": "pal.15", "Width": 71 }, + "3dobj.234": { "Palette": "pal.15", "Width": 85 }, + "3dobj.235": { "Palette": "pal.3", "Width": 51, "Height": 130 }, + "3dobj.236": { "Palette": "pal.3", "Width": 51, "Height": 129 }, + "3dobj.237": { "Palette": "pal.3", "Width": 50, "Height": 128 }, + "3dobj.238": { "Palette": "pal.3", "Width": 50, "Height": 129 }, + "3dobj.239": { "Palette": "pal.3", "Width": 56, "Height": 56 }, + "3dobj.240": { "Palette": "pal.3", "Width": 27, "Height": 74 }, + "3dobj.241": { "Palette": "pal.3", "Width": 55 }, + "3dobj.242": { "Palette": "pal.3", "Width": 55 }, + "3dobj.243": { "Palette": "pal.3", "Width": 55 }, + "3dobj.244": { "Palette": "pal.3", "Width": 100, "Height": 100 }, + "3dobj.245": { "Palette": "pal.9", "Width": 64 }, + "3dobj.246": { "Palette": "pal.9", "Width": 64 }, + "3dobj.247": { "Palette": "pal.7", "Width": 10 }, + "3dobj.248": { "Palette": "pal.7", "Width": 63, "Height": 101 }, + "3dobj.249": { "Palette": "pal.7", "Width": 31 }, + "3dobj.250": { "Palette": "pal.25", "Width": 39 }, + "3dobj.251": { "Palette": "pal.25", "Width": 63 }, + "3dobj.252": { "Palette": "pal.25", "Width": 60 }, + "3dobj.253": { "Palette": "pal.25", "Width": 119 }, + "3dobj.254": { "Palette": "pal.25", "Width": 89 }, + "3dobj.255": { "Palette": "pal.25", "Width": 66 }, + "3dobj.256": { "Palette": "pal.25", "Width": 42, "Height": 111 }, + "3dobj.257": { "Palette": "pal.25", "Width": 51, "Height": 129 }, + "3dobj.258": { "Palette": "pal.25", "Width": 51, "Height": 129 }, + "3dobj.259": { "Palette": "pal.25", "Width": 50, "Height": 128 }, + "3dobj.260": { "Palette": "pal.25", "Width": 50, "Height": 129 }, + "3dobj.261": { "Palette": "pal.8", "Width": 32 }, + "3dobj.262": { "Palette": "pal.8", "Width": 26 }, + "3dobj.263": { "Palette": "pal.8", "Width": 26 }, + "3dobj.264": { "Palette": "pal.25", "Width": 56, "Height": 129 }, + "3dobj.265": { "Palette": "pal.25", "Width": 56, "Height": 125 }, + "3dobj.266": { "Palette": "pal.25", "Width": 55, "Height": 129 }, + "3dobj.267": { "Palette": "pal.25", "Width": 56, "Height": 125 }, + "3dobj.268": { "Palette": "pal.25", "Width": 73, "Height": 130 }, + "3dobj.269": { "Palette": "pal.25", "Width": 60, "Height": 109 }, + "3dobj.270": { "Palette": "pal.25", "Width": 60, "Height": 109 }, + "3dobj.271": { "Palette": "pal.25", "Width": 45, "Height": 141 }, + "3dobj.272": { "Palette": "pal.25", "Width": 53, "Height": 141 }, + "3dobj.273": { "Palette": "pal.25", "Width": 47, "Height": 102 }, + "3dobj.274": { "Palette": "pal.25", "Width": 47, "Height": 103 }, + "3dobj.275": { "Palette": "pal.25", "Width": 61 }, + "3dobj.276": { "Palette": "pal.7", "Width": 64 }, + "3dobj.277": { "Palette": "pal.8", "Width": 26 }, + "3dobj.278": { "Palette": "pal.22", "Width": 64 }, + "3dobj.279": { "Palette": "pal.22", "Width": 64 }, + "3dobj.280": { "Palette": "pal.22", "Width": 64 }, + "3dobj.281": { "Palette": "pal.3", "Width": 145, "Height": 165 }, + "3dobj.282": { "Palette": "pal.3", "Width": 66 }, + "3dobj.283": { "Palette": "pal.3", "Width": 66 }, + "3dobj.284": { "Palette": "pal.9", "Width": 64 }, + "3dobj.285": { "Palette": "pal.9", "Width": 64 }, + "3dobj.286": { "Palette": "pal.3", "Width": 27, "Height": 74 }, + "3dobj.287": { "Palette": "pal.3", "Width": 27, "Height": 74 }, + "3dobj.288": { "Palette": "pal.15", "Width": 116 }, + "3dobj.289": { "Palette": "pal.3", "Width": 145, "Height": 165 }, + "3dobj.290": { "Palette": "pal.3", "Width": 81, "Height": 118 }, + "3dobj.291": { "Palette": "pal.3", "Width": 56, "Height": 56 }, + "3dobj.292": { "Palette": "pal.15", "Width": 85, "Height": 122 }, + "3dobj.293": { "Palette": "pal.3", "Width": 81, "Height": 119 }, + "3dobj.294": { "Palette": "pal.3", "Width": 155, "Height": 172 }, + "3dobj.295": { "Palette": "pal.15", "Width": 165, "Height": 147 }, + "3dobj.296": { "Palette": "pal.3", "Width": 24, "Height": 78 }, + "3dobj.297": { "Palette": "pal.3", "Width": 24, "Height": 70 }, + "3dobj.298": { "Palette": "pal.30", "Width": 51, "Height": 130 }, + "3dobj.299": { "Palette": "pal.30", "Width": 56, "Height": 129 } } diff --git a/mods/Albion/Meta/3DOBJEC3.json b/mods/Albion/Meta/3DOBJEC3.json index fb8d4314c..0044db2b6 100644 --- a/mods/Albion/Meta/3DOBJEC3.json +++ b/mods/Albion/Meta/3DOBJEC3.json @@ -1,28 +1,28 @@ { - "000": { "Id": "3dobj.300", "Width": 56, "Height": 125, "PaletteId": 30 }, - "001": { "PaletteId": 30, "Width": 53, "Height": 141 }, - "002": { "PaletteId": 13, "Width": 129, "Height": 164 }, - "003": { "PaletteId": 13, "Width": 165, "Height": 147 }, - "004": { "PaletteId": 13, "Width": 165, "Height": 147 }, - "005": { "PaletteId": 56, "Width": 165, "Height": 147 }, - "006": { "PaletteId": 13, "Width": 90, "Height": 136 }, - "007": { "PaletteId": 16, "Width": 85, "Height": 122 }, - "008": { "PaletteId": 16, "Width": 129, "Height": 164 }, - "009": { "PaletteId": 18, "Width": 183, "Height": 152 }, - "010": { "PaletteId": 18, "Width": 155, "Height": 172 }, - "011": { "PaletteId": 18, "Width": 106, "Height": 112 }, - "012": { "PaletteId": 22, "Width": 49, "Height": 107 }, - "013": { "PaletteId": 22, "Width": 90, "Height": 136 }, - "014": { "PaletteId": 22, "Width": 77, "Height": 147 }, - "015": { "PaletteId": 22, "Width": 123 }, - "016": { "PaletteId": 22, "Width": 77, "Height": 147 }, - "017": { "PaletteId": 56, "Width": 183, "Height": 152 }, - "018": { "PaletteId": 22, "Width": 83, "Height": 84 }, - "019": { "PaletteId": 22, "Width": 54, "Height": 116 }, - "020": { "PaletteId": 22, "Width": 54, "Height": 116 }, - "021": { "PaletteId": 11, "Width": 50, "Height": 120 }, - "022": { "PaletteId": 13, "Width": 75, "Height": 71 }, - "023": { "PaletteId": 13, "Width": 85, "Height": 122 }, - "024": { "PaletteId": 13, "Width": 106, "Height": 112 }, - "025": { "PaletteId": 13, "Width": 83, "Height": 84 } + "3dobj.300": { "Palette": "pal.30", "Width": 56, "Height": 125 }, + "3dobj.301": { "Palette": "pal.30", "Width": 53, "Height": 141 }, + "3dobj.302": { "Palette": "pal.13", "Width": 129, "Height": 164 }, + "3dobj.303": { "Palette": "pal.13", "Width": 165, "Height": 147 }, + "3dobj.304": { "Palette": "pal.13", "Width": 165, "Height": 147 }, + "3dobj.305": { "Palette": "pal.56", "Width": 165, "Height": 147 }, + "3dobj.306": { "Palette": "pal.13", "Width": 90, "Height": 136 }, + "3dobj.307": { "Palette": "pal.16", "Width": 85, "Height": 122 }, + "3dobj.308": { "Palette": "pal.16", "Width": 129, "Height": 164 }, + "3dobj.309": { "Palette": "pal.18", "Width": 183, "Height": 152 }, + "3dobj.310": { "Palette": "pal.18", "Width": 155, "Height": 172 }, + "3dobj.311": { "Palette": "pal.18", "Width": 106, "Height": 112 }, + "3dobj.312": { "Palette": "pal.22", "Width": 49, "Height": 107 }, + "3dobj.313": { "Palette": "pal.22", "Width": 90, "Height": 136 }, + "3dobj.314": { "Palette": "pal.22", "Width": 77, "Height": 147 }, + "3dobj.315": { "Palette": "pal.22", "Width": 123 }, + "3dobj.316": { "Palette": "pal.22", "Width": 77, "Height": 147 }, + "3dobj.317": { "Palette": "pal.56", "Width": 183, "Height": 152 }, + "3dobj.318": { "Palette": "pal.22", "Width": 83, "Height": 84 }, + "3dobj.319": { "Palette": "pal.22", "Width": 54, "Height": 116 }, + "3dobj.320": { "Palette": "pal.22", "Width": 54, "Height": 116 }, + "3dobj.321": { "Palette": "pal.11", "Width": 50, "Height": 120 }, + "3dobj.322": { "Palette": "pal.13", "Width": 75, "Height": 71 }, + "3dobj.323": { "Palette": "pal.13", "Width": 85, "Height": 122 }, + "3dobj.324": { "Palette": "pal.13", "Width": 106, "Height": 112 }, + "3dobj.325": { "Palette": "pal.13", "Width": 83, "Height": 84 } } diff --git a/mods/Albion/Meta/3DOVERL0.json b/mods/Albion/Meta/3DOVERL0.json index 6ece4c268..bb0d808d5 100644 --- a/mods/Albion/Meta/3DOVERL0.json +++ b/mods/Albion/Meta/3DOVERL0.json @@ -1,101 +1,101 @@ { - "000": { "Id": "overlay.1", "Width": 51, "PaletteId": 3 }, - "001": { "PaletteId": 3, "Width": 44 }, - "002": { "PaletteId": 3, "Width": 44 }, - "003": { "PaletteId": 3, "Width": 50 }, - "004": { "PaletteId": 3, "Width": 50 }, - "005": { "PaletteId": 3, "Width": 47 }, - "006": { "PaletteId": 3, "Width": 83 }, - "007": { "PaletteId": 3, "Width": 6 }, - "008": { "PaletteId": 3, "Width": 20 }, - "009": { "PaletteId": 3, "Width": 26 }, - "010": { "PaletteId": 3, "Width": 44 }, - "011": { "PaletteId": 3, "Width": 39 }, - "012": { "PaletteId": 3, "Width": 31 }, - "013": { "PaletteId": 3, "Width": 20 }, - "014": { "PaletteId": 3, "Width": 36 }, - "015": { "PaletteId": 3, "Width": 46 }, - "016": { "PaletteId": 3, "Width": 37 }, - "017": { "PaletteId": 3, "Width": 112 }, - "018": { "PaletteId": 3, "Width": 112 }, - "019": { "PaletteId": 3, "Width": 50 }, - "020": { "PaletteId": 3, "Width": 76 }, - "021": { "Width": 47 }, - "022": { "PaletteId": 8, "Width": 62, "Height": 42 }, - "023": { "PaletteId": 13, "Width": 24 }, - "024": { "PaletteId": 13, "Width": 65 }, - "025": { "PaletteId": 13, "Width": 58 }, - "026": { "PaletteId": 13, "Width": 26 }, - "027": { "PaletteId": 13, "Width": 26 }, - "028": { "PaletteId": 13, "Width": 63 }, - "029": { "PaletteId": 13, "Width": 70 }, - "030": { "PaletteId": 13, "Width": 16 }, - "031": { "PaletteId": 13, "Width": 13 }, - "032": { "PaletteId": 13, "Width": 9 }, - "033": { "PaletteId": 13, "Width": 7 }, - "034": { "PaletteId": 13, "Width": 33 }, - "035": { "PaletteId": 13, "Width": 42 }, - "036": { "PaletteId": 13, "Width": 41 }, - "037": { "PaletteId": 13, "Width": 41 }, - "038": { "PaletteId": 13, "Width": 52 }, - "039": { "PaletteId": 13, "Width": 41 }, - "040": { "PaletteId": 13, "Width": 40 }, - "041": { "PaletteId": 13, "Width": 23 }, - "042": { "PaletteId": 13, "Width": 52 }, - "043": { "PaletteId": 13, "Width": 48 }, - "044": { "PaletteId": 13, "Width": 128 }, - "045": { "PaletteId": 13, "Width": 128 }, - "046": { "PaletteId": 13, "Width": 30 }, - "047": { "PaletteId": 13, "Width": 30 }, - "048": { "PaletteId": 13, "Width": 128 }, - "049": { "PaletteId": 13, "Width": 128 }, - "050": { "PaletteId": 13, "Width": 36 }, - "051": { "PaletteId": 13, "Width": 56 }, - "052": { "PaletteId": 13, "Width": 25 }, - "053": { "PaletteId": 13, "Width": 36 }, - "054": { "PaletteId": 13, "Width": 25 }, - "055": { "PaletteId": 13, "Width": 24, "ExtraBytes": 7 }, - "056": { "PaletteId": 13, "Width": 29 }, - "057": { "PaletteId": 13, "Width": 128 }, - "058": { "PaletteId": 13, "Width": 128 }, - "059": { "PaletteId": 13, "Width": 96 }, - "060": { "PaletteId": 13, "Width": 29 }, - "061": { "PaletteId": 13, "Width": 62 }, - "062": { "PaletteId": 15, "Width": 130 }, - "063": { "PaletteId": 15, "Width": 130 }, - "064": { "PaletteId": 15, "Width": 130 }, - "065": { "PaletteId": 15, "Width": 130 }, - "066": { "PaletteId": 15, "Width": 37 }, - "067": { "PaletteId": 15, "Width": 34 }, - "068": { "PaletteId": 15, "Width": 39 }, - "069": { "PaletteId": 15, "Width": 38 }, - "070": { "PaletteId": 15, "Width": 39 }, - "071": { "PaletteId": 15, "Width": 41 }, - "072": { "PaletteId": 11, "Width": 134 }, - "073": { "PaletteId": 11, "Width": 134 }, - "074": { "PaletteId": 11, "Width": 134 }, - "075": { "PaletteId": 11, "Width": 134 }, - "076": { "PaletteId": 11, "Width": 134 }, - "077": { "PaletteId": 11, "Width": 134 }, - "078": { "PaletteId": 11, "Width": 59 }, - "079": { "PaletteId": 11, "Width": 86 }, - "080": { "PaletteId": 15, "Width": 55 }, - "081": { "PaletteId": 18, "Width": 134 }, - "082": { "PaletteId": 18, "Width": 115 }, - "083": { "PaletteId": 18, "Width": 116 }, - "084": { "PaletteId": 18, "Width": 114 }, - "085": { "PaletteId": 18, "Width": 116 }, - "086": { "PaletteId": 18, "Width": 115 }, - "087": { "PaletteId": 18, "Width": 18 }, - "088": { "PaletteId": 18, "Width": 30 }, - "089": { "PaletteId": 18, "Width": 36 }, - "090": { "PaletteId": 18, "Width": 36 }, - "091": { "PaletteId": 18, "Width": 30 }, - "092": { "PaletteId": 18, "Width": 36 }, - "093": { "PaletteId": 18, "Width": 29 }, - "094": { "PaletteId": 18, "Width": 38 }, - "095": { "PaletteId": 18, "Width": 87 }, - "096": { "PaletteId": 18, "Width": 42 }, - "097": { "PaletteId": 18, "Width": 134 }, - "098": { "PaletteId": 18, "Width": 134 } + "overlay.1": { "Palette": "pal.3", "Width": 51}, + "overlay.2": { "Palette": "pal.3", "Width": 44 }, + "overlay.3": { "Palette": "pal.3", "Width": 44 }, + "overlay.4": { "Palette": "pal.3", "Width": 50 }, + "overlay.5": { "Palette": "pal.3", "Width": 50 }, + "overlay.6": { "Palette": "pal.3", "Width": 47 }, + "overlay.7": { "Palette": "pal.3", "Width": 83 }, + "overlay.8": { "Palette": "pal.3", "Width": 6 }, + "overlay.9": { "Palette": "pal.3", "Width": 20 }, + "overlay.10": { "Palette": "pal.3", "Width": 26 }, + "overlay.11": { "Palette": "pal.3", "Width": 44 }, + "overlay.12": { "Palette": "pal.3", "Width": 39 }, + "overlay.13": { "Palette": "pal.3", "Width": 31 }, + "overlay.14": { "Palette": "pal.3", "Width": 20 }, + "overlay.15": { "Palette": "pal.3", "Width": 36 }, + "overlay.16": { "Palette": "pal.3", "Width": 46 }, + "overlay.17": { "Palette": "pal.3", "Width": 37 }, + "overlay.18": { "Palette": "pal.3", "Width": 112 }, + "overlay.19": { "Palette": "pal.3", "Width": 112 }, + "overlay.20": { "Palette": "pal.3", "Width": 50 }, + "overlay.21": { "Palette": "pal.3", "Width": 76 }, + "overlay.22": { "Palette": "pal.8", "Width": 47 }, + "overlay.23": { "Palette": "pal.8", "Width": 62, "Height": 42 }, + "overlay.24": { "Palette": "pal.13", "Width": 24 }, + "overlay.25": { "Palette": "pal.13", "Width": 65 }, + "overlay.26": { "Palette": "pal.13", "Width": 58 }, + "overlay.27": { "Palette": "pal.13", "Width": 26 }, + "overlay.28": { "Palette": "pal.13", "Width": 26 }, + "overlay.29": { "Palette": "pal.13", "Width": 63 }, + "overlay.30": { "Palette": "pal.13", "Width": 70 }, + "overlay.31": { "Palette": "pal.13", "Width": 16 }, + "overlay.32": { "Palette": "pal.13", "Width": 13 }, + "overlay.33": { "Palette": "pal.13", "Width": 9 }, + "overlay.34": { "Palette": "pal.13", "Width": 7 }, + "overlay.35": { "Palette": "pal.13", "Width": 33 }, + "overlay.36": { "Palette": "pal.13", "Width": 42 }, + "overlay.37": { "Palette": "pal.13", "Width": 41 }, + "overlay.38": { "Palette": "pal.13", "Width": 41 }, + "overlay.39": { "Palette": "pal.13", "Width": 52 }, + "overlay.40": { "Palette": "pal.13", "Width": 41 }, + "overlay.41": { "Palette": "pal.13", "Width": 40 }, + "overlay.42": { "Palette": "pal.13", "Width": 23 }, + "overlay.43": { "Palette": "pal.13", "Width": 52 }, + "overlay.44": { "Palette": "pal.13", "Width": 48 }, + "overlay.45": { "Palette": "pal.13", "Width": 128 }, + "overlay.46": { "Palette": "pal.13", "Width": 128 }, + "overlay.47": { "Palette": "pal.13", "Width": 30 }, + "overlay.48": { "Palette": "pal.13", "Width": 30 }, + "overlay.49": { "Palette": "pal.13", "Width": 128 }, + "overlay.50": { "Palette": "pal.13", "Width": 128 }, + "overlay.51": { "Palette": "pal.13", "Width": 36 }, + "overlay.52": { "Palette": "pal.13", "Width": 56 }, + "overlay.53": { "Palette": "pal.13", "Width": 25 }, + "overlay.54": { "Palette": "pal.13", "Width": 36 }, + "overlay.55": { "Palette": "pal.13", "Width": 25 }, + "overlay.56": { "Palette": "pal.13", "Width": 24, "ExtraBytes": 7 }, + "overlay.57": { "Palette": "pal.13", "Width": 29 }, + "overlay.58": { "Palette": "pal.13", "Width": 128 }, + "overlay.59": { "Palette": "pal.13", "Width": 128 }, + "overlay.60": { "Palette": "pal.13", "Width": 96 }, + "overlay.61": { "Palette": "pal.13", "Width": 29 }, + "overlay.62": { "Palette": "pal.13", "Width": 62 }, + "overlay.63": { "Palette": "pal.15", "Width": 130 }, + "overlay.64": { "Palette": "pal.15", "Width": 130 }, + "overlay.65": { "Palette": "pal.15", "Width": 130 }, + "overlay.66": { "Palette": "pal.15", "Width": 130 }, + "overlay.67": { "Palette": "pal.15", "Width": 37 }, + "overlay.68": { "Palette": "pal.15", "Width": 34 }, + "overlay.69": { "Palette": "pal.15", "Width": 39 }, + "overlay.70": { "Palette": "pal.15", "Width": 38 }, + "overlay.71": { "Palette": "pal.15", "Width": 39 }, + "overlay.72": { "Palette": "pal.15", "Width": 41 }, + "overlay.73": { "Palette": "pal.11", "Width": 134 }, + "overlay.74": { "Palette": "pal.11", "Width": 134 }, + "overlay.75": { "Palette": "pal.11", "Width": 134 }, + "overlay.76": { "Palette": "pal.11", "Width": 134 }, + "overlay.77": { "Palette": "pal.11", "Width": 134 }, + "overlay.78": { "Palette": "pal.11", "Width": 134 }, + "overlay.79": { "Palette": "pal.11", "Width": 59 }, + "overlay.80": { "Palette": "pal.11", "Width": 86 }, + "overlay.81": { "Palette": "pal.15", "Width": 55 }, + "overlay.82": { "Palette": "pal.18", "Width": 134 }, + "overlay.83": { "Palette": "pal.18", "Width": 115 }, + "overlay.84": { "Palette": "pal.18", "Width": 116 }, + "overlay.85": { "Palette": "pal.18", "Width": 114 }, + "overlay.86": { "Palette": "pal.18", "Width": 116 }, + "overlay.87": { "Palette": "pal.18", "Width": 115 }, + "overlay.88": { "Palette": "pal.18", "Width": 18 }, + "overlay.89": { "Palette": "pal.18", "Width": 30 }, + "overlay.90": { "Palette": "pal.18", "Width": 36 }, + "overlay.91": { "Palette": "pal.18", "Width": 36 }, + "overlay.92": { "Palette": "pal.18", "Width": 30 }, + "overlay.93": { "Palette": "pal.18", "Width": 36 }, + "overlay.94": { "Palette": "pal.18", "Width": 29 }, + "overlay.95": { "Palette": "pal.18", "Width": 38 }, + "overlay.96": { "Palette": "pal.18", "Width": 87 }, + "overlay.97": { "Palette": "pal.18", "Width": 42 }, + "overlay.98": { "Palette": "pal.18", "Width": 134 }, + "overlay.99": { "Palette": "pal.18", "Width": 134 } } diff --git a/mods/Albion/Meta/3DOVERL1.json b/mods/Albion/Meta/3DOVERL1.json index 5b63dd719..4f41e09ed 100644 --- a/mods/Albion/Meta/3DOVERL1.json +++ b/mods/Albion/Meta/3DOVERL1.json @@ -1,102 +1,102 @@ { - "000": { "Id": "overlay.100", "Width": 35, "PaletteId": 18 }, - "001": { "PaletteId": 18, "Width": 82 }, - "002": { "PaletteId": 18, "Width": 38 }, - "003": { "PaletteId": 18, "Width": 48 }, - "004": { "PaletteId": 18, "Width": 74 }, - "005": { "PaletteId": 18, "Width": 27 }, - "006": { "PaletteId": 18, "Width": 31 }, - "007": { "PaletteId": 18, "Width": 21 }, - "008": { "PaletteId": 18, "Width": 21 }, - "009": { "PaletteId": 18, "Width": 30 }, - "010": { "PaletteId": 18, "Width": 33 }, - "011": { "PaletteId": 18, "Width": 24 }, - "012": { "PaletteId": 18, "Width": 21 }, - "013": { "PaletteId": 18, "Width": 40 }, - "014": { "PaletteId": 3, "Width": 38 }, - "015": { "PaletteId": 25, "Width": 36 }, - "016": { "PaletteId": 25, "Width": 36 }, - "017": { "PaletteId": 25, "Width": 52 }, - "018": { "PaletteId": 25, "Width": 29, "ExtraBytes": 10 }, - "019": { "PaletteId": 25, "Width": 30 }, - "020": { "PaletteId": 25, "Width": 52 }, - "021": { "PaletteId": 25, "Width": 51 }, - "022": { "PaletteId": 25, "Width": 52 }, - "023": { "PaletteId": 25, "Width": 51 }, - "024": { "PaletteId": 25, "Width": 49 }, - "025": { "PaletteId": 25, "Width": 18 }, - "026": { "PaletteId": 25, "Width": 16 }, - "027": { "PaletteId": 25, "Width": 50 }, - "028": { "PaletteId": 25, "Width": 22 }, - "029": { "PaletteId": 25, "Width": 43 }, - "030": { "PaletteId": 25, "Width": 42 }, - "031": { "PaletteId": 25, "Width": 24 }, - "032": { "PaletteId": 25, "Width": 53 }, - "033": { "PaletteId": 25, "Width": 53 }, - "034": { "PaletteId": 25, "Width": 68 }, - "035": { "PaletteId": 25, "Width": 58 }, - "036": { "PaletteId": 25, "Width": 57 }, - "037": { "PaletteId": 25, "Width": 29 }, - "038": { "PaletteId": 25, "Width": 29 }, - "039": { "PaletteId": 25, "Width": 42 }, - "040": { "PaletteId": 25, "Width": 50 }, - "041": { "PaletteId": 25, "Width": 50 }, - "042": { "PaletteId": 25, "Width": 53 }, - "043": { "PaletteId": 25, "Width": 53 }, - "044": { "PaletteId": 25, "Width": 64 }, - "045": { "PaletteId": 25, "Width": 64 }, - "046": { "PaletteId": 25, "Width": 29 }, - "047": { "PaletteId": 25, "Width": 29 }, - "048": { "PaletteId": 25, "Width": 68 }, - "049": { "PaletteId": 25, "Width": 68 }, - "050": { "PaletteId": 25, "Width": 75 }, - "051": { "PaletteId": 25, "Width": 75 }, - "052": { "PaletteId": 25, "Width": 76 }, - "053": { "PaletteId": 25, "Width": 76 }, - "054": { "PaletteId": 25, "Width": 76 }, - "055": { "PaletteId": 25, "Width": 76 }, - "056": { "PaletteId": 25, "Width": 80 }, - "057": { "PaletteId": 25, "Width": 80 }, - "058": { "PaletteId": 25, "Width": 27 }, - "059": { "PaletteId": 25, "Width": 27 }, - "060": { "PaletteId": 25, "Width": 27 }, - "061": { "PaletteId": 25, "Width": 27 }, - "062": { "PaletteId": 25, "Width": 27 }, - "063": { "PaletteId": 25, "Width": 27 }, - "064": { "PaletteId": 25, "Width": 27 }, - "065": { "PaletteId": 25, "Width": 27 }, - "066": { "PaletteId": 25, "Width": 95 }, - "067": { "PaletteId": 25, "Width": 95 }, - "068": { "PaletteId": 25, "Width": 109 }, - "069": { "PaletteId": 25, "Width": 95 }, - "070": { "PaletteId": 25, "Width": 94 }, - "071": { "PaletteId": 25, "Width": 91 }, - "072": { "PaletteId": 25, "Width": 55 }, - "073": { "PaletteId": 25, "Width": 57 }, - "074": { "PaletteId": 3, "Width": 40 }, - "075": { "PaletteId": 13, "Width": 128 }, - "076": { "PaletteId": 13, "Width": 128 }, - "077": { "PaletteId": 22, "Width": 11 }, - "078": { "PaletteId": 22, "Width": 14 }, - "079": { "PaletteId": 22, "Width": 30 }, - "080": { "PaletteId": 22, "Width": 25 }, - "081": { "PaletteId": 22, "Width": 55 }, - "082": { "PaletteId": 6, "Width": 52 }, - "083": { "PaletteId": 6, "Width": 52 }, - "084": { "PaletteId": 22, "Width": 7 }, - "085": { "PaletteId": 22, "Width": 7 }, - "086": { "PaletteId": 22, "Width": 12 }, - "087": { "PaletteId": 22, "Width": 12 }, - "088": { "PaletteId": 3, "Width": 128 }, - "089": { "PaletteId": 7, "Width": 27 }, - "090": { "PaletteId": 7, "Width": 27 }, - "091": { "PaletteId": 7, "Width": 21 }, - "092": { "PaletteId": 7, "Width": 21 }, - "093": { "PaletteId": 7, "Width": 21 }, - "094": { "PaletteId": 7, "Width": 21 }, - "095": { "PaletteId": 7, "Width": 21 }, - "096": { "PaletteId": 7, "Width": 21 }, - "097": { "PaletteId": 7, "Width": 21 }, - "098": { "PaletteId": 7, "Width": 21 }, - "099": { "PaletteId": 7, "Width": 26 } + "overlay.100": { "Palette": "pal.18", "Width": 35 }, + "overlay.101": { "Palette": "pal.18", "Width": 82 }, + "overlay.102": { "Palette": "pal.18", "Width": 38 }, + "overlay.103": { "Palette": "pal.18", "Width": 48 }, + "overlay.104": { "Palette": "pal.18", "Width": 74 }, + "overlay.105": { "Palette": "pal.18", "Width": 27 }, + "overlay.106": { "Palette": "pal.18", "Width": 31 }, + "overlay.107": { "Palette": "pal.18", "Width": 21 }, + "overlay.108": { "Palette": "pal.18", "Width": 21 }, + "overlay.109": { "Palette": "pal.18", "Width": 30 }, + "overlay.110": { "Palette": "pal.18", "Width": 33 }, + "overlay.111": { "Palette": "pal.18", "Width": 24 }, + "overlay.112": { "Palette": "pal.18", "Width": 21 }, + "overlay.113": { "Palette": "pal.18", "Width": 40 }, + "overlay.114": { "Palette": "pal.3", "Width": 38 }, + "overlay.115": { "Palette": "pal.25", "Width": 36 }, + "overlay.116": { "Palette": "pal.25", "Width": 36 }, + "overlay.117": { "Palette": "pal.25", "Width": 52 }, + "overlay.118": { "Palette": "pal.25", "Width": 29, "ExtraBytes": 10 }, + "overlay.119": { "Palette": "pal.25", "Width": 30 }, + "overlay.120": { "Palette": "pal.25", "Width": 52 }, + "overlay.121": { "Palette": "pal.25", "Width": 51 }, + "overlay.122": { "Palette": "pal.25", "Width": 52 }, + "overlay.123": { "Palette": "pal.25", "Width": 51 }, + "overlay.124": { "Palette": "pal.25", "Width": 49 }, + "overlay.125": { "Palette": "pal.25", "Width": 18 }, + "overlay.126": { "Palette": "pal.25", "Width": 16 }, + "overlay.127": { "Palette": "pal.25", "Width": 50 }, + "overlay.128": { "Palette": "pal.25", "Width": 22 }, + "overlay.129": { "Palette": "pal.25", "Width": 43 }, + "overlay.130": { "Palette": "pal.25", "Width": 42 }, + "overlay.131": { "Palette": "pal.25", "Width": 24 }, + "overlay.132": { "Palette": "pal.25", "Width": 53 }, + "overlay.133": { "Palette": "pal.25", "Width": 53 }, + "overlay.134": { "Palette": "pal.25", "Width": 68 }, + "overlay.135": { "Palette": "pal.25", "Width": 58 }, + "overlay.136": { "Palette": "pal.25", "Width": 57 }, + "overlay.137": { "Palette": "pal.25", "Width": 29 }, + "overlay.138": { "Palette": "pal.25", "Width": 29 }, + "overlay.139": { "Palette": "pal.25", "Width": 42 }, + "overlay.140": { "Palette": "pal.25", "Width": 50 }, + "overlay.141": { "Palette": "pal.25", "Width": 50 }, + "overlay.142": { "Palette": "pal.25", "Width": 53 }, + "overlay.143": { "Palette": "pal.25", "Width": 53 }, + "overlay.144": { "Palette": "pal.25", "Width": 64 }, + "overlay.145": { "Palette": "pal.25", "Width": 64 }, + "overlay.146": { "Palette": "pal.25", "Width": 29 }, + "overlay.147": { "Palette": "pal.25", "Width": 29 }, + "overlay.148": { "Palette": "pal.25", "Width": 68 }, + "overlay.149": { "Palette": "pal.25", "Width": 68 }, + "overlay.150": { "Palette": "pal.25", "Width": 75 }, + "overlay.151": { "Palette": "pal.25", "Width": 75 }, + "overlay.152": { "Palette": "pal.25", "Width": 76 }, + "overlay.153": { "Palette": "pal.25", "Width": 76 }, + "overlay.154": { "Palette": "pal.25", "Width": 76 }, + "overlay.155": { "Palette": "pal.25", "Width": 76 }, + "overlay.156": { "Palette": "pal.25", "Width": 80 }, + "overlay.157": { "Palette": "pal.25", "Width": 80 }, + "overlay.158": { "Palette": "pal.25", "Width": 27 }, + "overlay.159": { "Palette": "pal.25", "Width": 27 }, + "overlay.160": { "Palette": "pal.25", "Width": 27 }, + "overlay.161": { "Palette": "pal.25", "Width": 27 }, + "overlay.162": { "Palette": "pal.25", "Width": 27 }, + "overlay.163": { "Palette": "pal.25", "Width": 27 }, + "overlay.164": { "Palette": "pal.25", "Width": 27 }, + "overlay.165": { "Palette": "pal.25", "Width": 27 }, + "overlay.166": { "Palette": "pal.25", "Width": 95 }, + "overlay.167": { "Palette": "pal.25", "Width": 95 }, + "overlay.168": { "Palette": "pal.25", "Width": 109 }, + "overlay.169": { "Palette": "pal.25", "Width": 95 }, + "overlay.170": { "Palette": "pal.25", "Width": 94 }, + "overlay.171": { "Palette": "pal.25", "Width": 91 }, + "overlay.172": { "Palette": "pal.25", "Width": 55 }, + "overlay.173": { "Palette": "pal.25", "Width": 57 }, + "overlay.174": { "Palette": "pal.3", "Width": 40 }, + "overlay.175": { "Palette": "pal.13", "Width": 128 }, + "overlay.176": { "Palette": "pal.13", "Width": 128 }, + "overlay.177": { "Palette": "pal.22", "Width": 11 }, + "overlay.178": { "Palette": "pal.22", "Width": 14 }, + "overlay.179": { "Palette": "pal.22", "Width": 30 }, + "overlay.180": { "Palette": "pal.22", "Width": 25 }, + "overlay.181": { "Palette": "pal.22", "Width": 55 }, + "overlay.182": { "Palette": "pal.6", "Width": 52 }, + "overlay.183": { "Palette": "pal.6", "Width": 52 }, + "overlay.184": { "Palette": "pal.22", "Width": 7 }, + "overlay.185": { "Palette": "pal.22", "Width": 7 }, + "overlay.186": { "Palette": "pal.22", "Width": 12 }, + "overlay.187": { "Palette": "pal.22", "Width": 12 }, + "overlay.188": { "Palette": "pal.3", "Width": 128 }, + "overlay.189": { "Palette": "pal.7", "Width": 27 }, + "overlay.190": { "Palette": "pal.7", "Width": 27 }, + "overlay.191": { "Palette": "pal.7", "Width": 21 }, + "overlay.192": { "Palette": "pal.7", "Width": 21 }, + "overlay.193": { "Palette": "pal.7", "Width": 21 }, + "overlay.194": { "Palette": "pal.7", "Width": 21 }, + "overlay.195": { "Palette": "pal.7", "Width": 21 }, + "overlay.196": { "Palette": "pal.7", "Width": 21 }, + "overlay.197": { "Palette": "pal.7", "Width": 21 }, + "overlay.198": { "Palette": "pal.7", "Width": 21 }, + "overlay.199": { "Palette": "pal.7", "Width": 26 } } diff --git a/mods/Albion/Meta/3DOVERL2.json b/mods/Albion/Meta/3DOVERL2.json index 3c7ebc8b0..618b7369e 100644 --- a/mods/Albion/Meta/3DOVERL2.json +++ b/mods/Albion/Meta/3DOVERL2.json @@ -1,43 +1,43 @@ { - "000": { "Id": "overlay.200", "Width": 26, "PaletteId": 7 }, - "001": { "PaletteId": 7, "Width": 20, "Height": 6 }, - "002": { "PaletteId": 7, "Width": 20, "Height": 6 }, - "003": { "PaletteId": 7, "Width": 20, "Height": 6 }, - "004": { "PaletteId": 7, "Width": 20, "Height": 6 }, - "005": { "PaletteId": 7, "Width": 31 }, - "006": { "PaletteId": 7, "Width": 31 }, - "007": { "PaletteId": 7, "Width": 64 }, - "008": { "PaletteId": 7, "Width": 64 }, - "009": { "PaletteId": 25, "Width": 112 }, - "010": { "PaletteId": 25, "Width": 112 }, - "011": { "PaletteId": 25, "Width": 47 }, - "012": { "PaletteId": 25, "Width": 83 }, - "013": { "PaletteId": 25, "Width": 76 }, - "014": { "PaletteId": 25, "Width": 50 }, - "015": { "PaletteId": 7, "Width": 17 }, - "016": { "PaletteId": 7, "Width": 17 }, - "017": { "PaletteId": 7, "Width": 17 }, - "018": { "PaletteId": 7, "Width": 17 }, - "019": { "PaletteId": 7, "Width": 17 }, - "020": { "PaletteId": 7, "Width": 17 }, - "021": { "PaletteId": 7, "Width": 17 }, - "022": { "PaletteId": 7, "Width": 17 }, - "023": { "PaletteId": 7, "Width": 17 }, - "024": { "PaletteId": 7, "Width": 17 }, - "025": { "PaletteId": 7, "Width": 33 }, - "026": { "PaletteId": 7, "Width": 33 }, - "027": { "PaletteId": 7, "Width": 33, "Height": 35 }, - "028": { "PaletteId": 7, "Width": 14, "Height": 14 }, - "029": { "PaletteId": 7, "Width": 12, "Height": 12 }, - "030": { "PaletteId": 7, "Width": 12, "Height": 12 }, - "031": { "PaletteId": 7, "Width": 12, "Height": 11 }, - "032": { "PaletteId": 7, "Width": 12, "Height": 11 }, - "033": { "PaletteId": 18, "Width": 84 }, - "034": { "PaletteId": 18, "Width": 84 }, - "035": { "PaletteId": 18, "Width": 84 }, - "036": { "PaletteId": 18, "Width": 84 }, - "037": { "PaletteId": 18, "Width": 52 }, - "038": { "PaletteId": 18, "Width": 52 }, - "039": { "PaletteId": 18, "Width": 18 }, - "040": { "PaletteId": 18, "Width": 18 } + "overlay.200": { "Palette": "pal.7", "Width": 26 }, + "overlay.201": { "Palette": "pal.7", "Width": 20, "Height": 6 }, + "overlay.202": { "Palette": "pal.7", "Width": 20, "Height": 6 }, + "overlay.203": { "Palette": "pal.7", "Width": 20, "Height": 6 }, + "overlay.204": { "Palette": "pal.7", "Width": 20, "Height": 6 }, + "overlay.205": { "Palette": "pal.7", "Width": 31 }, + "overlay.206": { "Palette": "pal.7", "Width": 31 }, + "overlay.207": { "Palette": "pal.7", "Width": 64 }, + "overlay.208": { "Palette": "pal.7", "Width": 64 }, + "overlay.209": { "Palette": "pal.25", "Width": 112 }, + "overlay.210": { "Palette": "pal.25", "Width": 112 }, + "overlay.211": { "Palette": "pal.25", "Width": 47 }, + "overlay.212": { "Palette": "pal.25", "Width": 83 }, + "overlay.213": { "Palette": "pal.25", "Width": 76 }, + "overlay.214": { "Palette": "pal.25", "Width": 50 }, + "overlay.215": { "Palette": "pal.7", "Width": 17 }, + "overlay.216": { "Palette": "pal.7", "Width": 17 }, + "overlay.217": { "Palette": "pal.7", "Width": 17 }, + "overlay.218": { "Palette": "pal.7", "Width": 17 }, + "overlay.219": { "Palette": "pal.7", "Width": 17 }, + "overlay.220": { "Palette": "pal.7", "Width": 17 }, + "overlay.221": { "Palette": "pal.7", "Width": 17 }, + "overlay.222": { "Palette": "pal.7", "Width": 17 }, + "overlay.223": { "Palette": "pal.7", "Width": 17 }, + "overlay.224": { "Palette": "pal.7", "Width": 17 }, + "overlay.225": { "Palette": "pal.7", "Width": 33 }, + "overlay.226": { "Palette": "pal.7", "Width": 33 }, + "overlay.227": { "Palette": "pal.7", "Width": 33, "Height": 35 }, + "overlay.228": { "Palette": "pal.7", "Width": 14, "Height": 14 }, + "overlay.229": { "Palette": "pal.7", "Width": 12, "Height": 12 }, + "overlay.230": { "Palette": "pal.7", "Width": 12, "Height": 12 }, + "overlay.231": { "Palette": "pal.7", "Width": 12, "Height": 11 }, + "overlay.232": { "Palette": "pal.7", "Width": 12, "Height": 11 }, + "overlay.233": { "Palette": "pal.18", "Width": 84 }, + "overlay.234": { "Palette": "pal.18", "Width": 84 }, + "overlay.235": { "Palette": "pal.18", "Width": 84 }, + "overlay.236": { "Palette": "pal.18", "Width": 84 }, + "overlay.237": { "Palette": "pal.18", "Width": 52 }, + "overlay.238": { "Palette": "pal.18", "Width": 52 }, + "overlay.239": { "Palette": "pal.18", "Width": 18 }, + "overlay.240": { "Palette": "pal.18", "Width": 18 } } diff --git a/mods/Albion/Meta/3DWALLS0.json b/mods/Albion/Meta/3DWALLS0.json index 757e43dd6..1458aa5c0 100644 --- a/mods/Albion/Meta/3DWALLS0.json +++ b/mods/Albion/Meta/3DWALLS0.json @@ -1,101 +1,101 @@ { - "000": { "Id": "wall.1", "Width": 112, "PaletteId": 3 }, - "001": { "PaletteId": 3, "Width": 112 }, - "002": { "PaletteId": 3, "Width": 112 }, - "003": { "PaletteId": 3, "Width": 112 }, - "004": { "PaletteId": 3, "Width": 112 }, - "005": { "PaletteId": 3, "Width": 112 }, - "006": { "PaletteId": 3, "Width": 112 }, - "007": { "PaletteId": 3, "Width": 112 }, - "008": { "PaletteId": 3, "Width": 112 }, - "009": { "PaletteId": 3, "Width": 112 }, - "010": { "PaletteId": 7, "Width": 80 }, - "011": { "PaletteId": 7, "Width": 80 }, - "012": { "PaletteId": 7, "Width": 80 }, - "013": { "PaletteId": 7, "Width": 80 }, - "014": { "PaletteId": 7, "Width": 80 }, - "015": { "PaletteId": 7, "Width": 80 }, - "016": { "PaletteId": 8, "Width": 107 }, - "017": { "PaletteId": 8, "Width": 107 }, - "018": { "PaletteId": 8, "Width": 107 }, - "019": { "PaletteId": 8, "Width": 107 }, - "020": { "PaletteId": 8, "Width": 107 }, - "021": { "PaletteId": 8, "Width": 107 }, - "022": { "PaletteId": 8, "Width": 107 }, - "023": { "PaletteId": 8, "Width": 107 }, - "024": { "PaletteId": 8, "Width": 107 }, - "025": { "PaletteId": 8, "Width": 107 }, - "026": { "PaletteId": 13, "Width": 128 }, - "027": { "PaletteId": 13, "Width": 128 }, - "028": { "PaletteId": 13, "Width": 128 }, - "029": { "PaletteId": 13, "Width": 128 }, - "030": { "PaletteId": 13, "Width": 128 }, - "031": { "PaletteId": 13, "Width": 128 }, - "032": { "PaletteId": 13, "Width": 128 }, - "033": { "PaletteId": 13, "Width": 128 }, - "034": { "PaletteId": 13, "Width": 128 }, - "035": { "PaletteId": 13, "Width": 128 }, - "036": { "PaletteId": 13, "Width": 128 }, - "037": { "PaletteId": 13, "Width": 128 }, - "038": { "PaletteId": 13, "Width": 198 }, - "039": { "PaletteId": 13, "Width": 198 }, - "040": { "PaletteId": 13, "Width": 198 }, - "041": { "PaletteId": 13, "Width": 198 }, - "042": { "PaletteId": 13, "Width": 128 }, - "043": { "PaletteId": 13, "Width": 128 }, - "044": { "PaletteId": 13, "Width": 32 }, - "045": { "PaletteId": 15, "Width": 130 }, - "046": { "PaletteId": 15, "Width": 130 }, - "047": { "PaletteId": 15, "Width": 130 }, - "048": { "PaletteId": 15, "Width": 130 }, - "049": { "Width": 128 }, - "050": { "Width": 156 }, - "051": { "PaletteId": 15, "Width": 130, "Height": 106 }, - "052": { "PaletteId": 3, "Width": 130 }, - "053": { "PaletteId": 3, "Width": 130 }, - "054": { "PaletteId": 11, "Width": 134 }, - "055": { "PaletteId": 11, "Width": 134 }, - "056": { "PaletteId": 11, "Width": 134 }, - "057": { "PaletteId": 11, "Width": 134 }, - "058": { "PaletteId": 11, "Width": 134 }, - "059": { "PaletteId": 11, "Width": 134 }, - "060": { "PaletteId": 11, "Width": 134 }, - "061": { "PaletteId": 15, "Width": 112 }, - "062": { "PaletteId": 15, "Width": 112 }, - "063": { "PaletteId": 15, "Width": 112 }, - "064": { "Width": 8 }, - "065": { "PaletteId": 18, "Width": 134 }, - "066": { "PaletteId": 18, "Width": 134 }, - "067": { "PaletteId": 18, "Width": 84 }, - "068": { "PaletteId": 18, "Width": 84 }, - "069": { "PaletteId": 18, "Width": 84 }, - "070": { "PaletteId": 18, "Width": 84 }, - "071": { "PaletteId": 22, "Width": 144 }, - "072": { "PaletteId": 22, "Width": 144 }, - "073": { "PaletteId": 22, "Width": 144 }, - "074": { "PaletteId": 25, "Width": 196 }, - "075": { "PaletteId": 25, "Width": 53 }, - "076": { "PaletteId": 25, "Width": 81 }, - "077": { "PaletteId": 25, "Width": 196 }, - "078": { "PaletteId": 25, "Width": 196 }, - "079": { "PaletteId": 25, "Width": 196 }, - "080": { "PaletteId": 25, "Width": 196 }, - "081": { "PaletteId": 25, "Width": 196 }, - "082": { "PaletteId": 25, "Width": 196 }, - "083": { "PaletteId": 25, "Width": 196 }, - "084": { "PaletteId": 25, "Width": 196 }, - "085": { "PaletteId": 25, "Width": 196 }, - "086": { "PaletteId": 25, "Width": 196 }, - "087": { "PaletteId": 25, "Width": 196 }, - "088": { "PaletteId": 25, "Width": 196 }, - "089": { "PaletteId": 25, "Width": 196 }, - "090": { "PaletteId": 25, "Width": 196 }, - "091": { "PaletteId": 25, "Width": 196 }, - "092": { "PaletteId": 25, "Width": 196 }, - "093": { "PaletteId": 25, "Width": 98 }, - "094": { "PaletteId": 25, "Width": 98 }, - "095": { "PaletteId": 25, "Width": 98 }, - "096": { "PaletteId": 25, "Width": 98 }, - "097": { "PaletteId": 25, "Width": 98 }, - "098": { "PaletteId": 25, "Width": 98 } + "wall.1": { "Palette": "pal.3", "Width": 112 }, + "wall.2": { "Palette": "pal.3", "Width": 112 }, + "wall.3": { "Palette": "pal.3", "Width": 112 }, + "wall.4": { "Palette": "pal.3", "Width": 112 }, + "wall.5": { "Palette": "pal.3", "Width": 112 }, + "wall.6": { "Palette": "pal.3", "Width": 112 }, + "wall.7": { "Palette": "pal.3", "Width": 112 }, + "wall.8": { "Palette": "pal.3", "Width": 112 }, + "wall.9": { "Palette": "pal.3", "Width": 112 }, + "wall.10": { "Palette": "pal.3", "Width": 112 }, + "wall.11": { "Palette": "pal.7", "Width": 80 }, + "wall.12": { "Palette": "pal.7", "Width": 80 }, + "wall.13": { "Palette": "pal.7", "Width": 80 }, + "wall.14": { "Palette": "pal.7", "Width": 80 }, + "wall.15": { "Palette": "pal.7", "Width": 80 }, + "wall.16": { "Palette": "pal.7", "Width": 80 }, + "wall.17": { "Palette": "pal.8", "Width": 107 }, + "wall.18": { "Palette": "pal.8", "Width": 107 }, + "wall.19": { "Palette": "pal.8", "Width": 107 }, + "wall.20": { "Palette": "pal.8", "Width": 107 }, + "wall.21": { "Palette": "pal.8", "Width": 107 }, + "wall.22": { "Palette": "pal.8", "Width": 107 }, + "wall.23": { "Palette": "pal.8", "Width": 107 }, + "wall.24": { "Palette": "pal.8", "Width": 107 }, + "wall.25": { "Palette": "pal.8", "Width": 107 }, + "wall.26": { "Palette": "pal.8", "Width": 107 }, + "wall.27": { "Palette": "pal.13", "Width": 128 }, + "wall.28": { "Palette": "pal.13", "Width": 128 }, + "wall.29": { "Palette": "pal.13", "Width": 128 }, + "wall.30": { "Palette": "pal.13", "Width": 128 }, + "wall.31": { "Palette": "pal.13", "Width": 128 }, + "wall.32": { "Palette": "pal.13", "Width": 128 }, + "wall.33": { "Palette": "pal.13", "Width": 128 }, + "wall.34": { "Palette": "pal.13", "Width": 128 }, + "wall.35": { "Palette": "pal.13", "Width": 128 }, + "wall.36": { "Palette": "pal.13", "Width": 128 }, + "wall.37": { "Palette": "pal.13", "Width": 128 }, + "wall.38": { "Palette": "pal.13", "Width": 128 }, + "wall.39": { "Palette": "pal.13", "Width": 198 }, + "wall.40": { "Palette": "pal.13", "Width": 198 }, + "wall.41": { "Palette": "pal.13", "Width": 198 }, + "wall.42": { "Palette": "pal.13", "Width": 198 }, + "wall.43": { "Palette": "pal.13", "Width": 128 }, + "wall.44": { "Palette": "pal.13", "Width": 128 }, + "wall.45": { "Palette": "pal.13", "Width": 32 }, + "wall.46": { "Palette": "pal.15", "Width": 130 }, + "wall.47": { "Palette": "pal.15", "Width": 130 }, + "wall.48": { "Palette": "pal.15", "Width": 130 }, + "wall.49": { "Palette": "pal.15", "Width": 130 }, + "wall.50": { "Palette": "pal.0", "Width": 128 }, + "wall.51": { "Palette": "pal.0", "Width": 156 }, + "wall.52": { "Palette": "pal.15", "Width": 130, "Height": 106 }, + "wall.53": { "Palette": "pal.3", "Width": 130 }, + "wall.54": { "Palette": "pal.3", "Width": 130 }, + "wall.55": { "Palette": "pal.11", "Width": 134 }, + "wall.56": { "Palette": "pal.11", "Width": 134 }, + "wall.57": { "Palette": "pal.11", "Width": 134 }, + "wall.58": { "Palette": "pal.11", "Width": 134 }, + "wall.59": { "Palette": "pal.11", "Width": 134 }, + "wall.60": { "Palette": "pal.11", "Width": 134 }, + "wall.61": { "Palette": "pal.11", "Width": 134 }, + "wall.62": { "Palette": "pal.15", "Width": 112 }, + "wall.63": { "Palette": "pal.15", "Width": 112 }, + "wall.64": { "Palette": "pal.15", "Width": 112 }, + "wall.65": { "Palette": "pal.0", "Width": 8 }, + "wall.66": { "Palette": "pal.18", "Width": 134 }, + "wall.67": { "Palette": "pal.18", "Width": 134 }, + "wall.68": { "Palette": "pal.18", "Width": 84 }, + "wall.69": { "Palette": "pal.18", "Width": 84 }, + "wall.70": { "Palette": "pal.18", "Width": 84 }, + "wall.71": { "Palette": "pal.18", "Width": 84 }, + "wall.72": { "Palette": "pal.22", "Width": 144 }, + "wall.73": { "Palette": "pal.22", "Width": 144 }, + "wall.74": { "Palette": "pal.22", "Width": 144 }, + "wall.75": { "Palette": "pal.25", "Width": 196 }, + "wall.76": { "Palette": "pal.25", "Width": 53 }, + "wall.77": { "Palette": "pal.25", "Width": 81 }, + "wall.78": { "Palette": "pal.25", "Width": 196 }, + "wall.79": { "Palette": "pal.25", "Width": 196 }, + "wall.80": { "Palette": "pal.25", "Width": 196 }, + "wall.81": { "Palette": "pal.25", "Width": 196 }, + "wall.82": { "Palette": "pal.25", "Width": 196 }, + "wall.83": { "Palette": "pal.25", "Width": 196 }, + "wall.84": { "Palette": "pal.25", "Width": 196 }, + "wall.85": { "Palette": "pal.25", "Width": 196 }, + "wall.86": { "Palette": "pal.25", "Width": 196 }, + "wall.87": { "Palette": "pal.25", "Width": 196 }, + "wall.88": { "Palette": "pal.25", "Width": 196 }, + "wall.89": { "Palette": "pal.25", "Width": 196 }, + "wall.90": { "Palette": "pal.25", "Width": 196 }, + "wall.91": { "Palette": "pal.25", "Width": 196 }, + "wall.92": { "Palette": "pal.25", "Width": 196 }, + "wall.93": { "Palette": "pal.25", "Width": 196 }, + "wall.94": { "Palette": "pal.25", "Width": 98 }, + "wall.95": { "Palette": "pal.25", "Width": 98 }, + "wall.96": { "Palette": "pal.25", "Width": 98 }, + "wall.97": { "Palette": "pal.25", "Width": 98 }, + "wall.98": { "Palette": "pal.25", "Width": 98 }, + "wall.99": { "Palette": "pal.25", "Width": 98 } } diff --git a/mods/Albion/Meta/3DWALLS1.json b/mods/Albion/Meta/3DWALLS1.json index ed18c4d46..853df3c33 100644 --- a/mods/Albion/Meta/3DWALLS1.json +++ b/mods/Albion/Meta/3DWALLS1.json @@ -1,40 +1,40 @@ { - "000": { "Id": "wall.100", "Width": 98, "PaletteId": 25 }, - "001": { "PaletteId": 25, "Width": 98 }, - "002": { "PaletteId": 25, "Width": 98 }, - "003": { "PaletteId": 25, "Width": 98 }, - "004": { "PaletteId": 25, "Width": 98 }, - "005": { "PaletteId": 25, "Width": 98 }, - "006": { "PaletteId": 13, "Width": 128 }, - "007": { "PaletteId": 13, "Width": 128 }, - "008": { "PaletteId": 13, "Width": 128 }, - "009": { "PaletteId": 22, "Width": 144 }, - "010": { "PaletteId": 22, "Width": 144 }, - "011": { "PaletteId": 22, "Width": 144 }, - "012": { "PaletteId": 15, "Width": 130 }, - "013": { "PaletteId": 15, "Width": 130 }, - "014": { "PaletteId": 13, "Width": 128 }, - "015": { "PaletteId": 13, "Width": 128 }, - "016": { "PaletteId": 13, "Width": 128 }, - "017": { "PaletteId": 13, "Width": 128 }, - "018": { "PaletteId": 13, "Width": 128 }, - "019": { "PaletteId": 13, "Width": 100, "Height": 100 }, - "020": { "PaletteId": 13, "Width": 128 }, - "021": { "PaletteId": 7, "Width": 80, "Height": 128 }, - "022": { "PaletteId": 7, "Width": 80 }, - "023": { "PaletteId": 7, "Width": 79, "ExtraBytes": 49 }, - "024": { "PaletteId": 25, "Width": 197 }, - "025": { "PaletteId": 25, "Width": 197 }, - "026": { "PaletteId": 25, "Width": 197 }, - "027": { "PaletteId": 25, "Width": 140, "ExtraBytes": 40 }, - "028": { "PaletteId": 25, "Width": 64 }, - "029": { "PaletteId": 16, "Width": 134 }, - "030": { "PaletteId": 22, "Width": 144 }, - "031": { "PaletteId": 22, "Width": 144 }, - "032": { "PaletteId": 22, "Width": 144 }, - "033": { "PaletteId": 22, "Width": 144 }, - "034": { "PaletteId": 22, "Width": 144 }, - "035": { "PaletteId": 18, "Width": 134 }, - "036": { "PaletteId": 18, "Width": 134 }, - "037": { "PaletteId": 18, "Width": 100, "Height": 100 } + "wall.100": { "Palette": "pal.25", "Width": 98 }, + "wall.101": { "Palette": "pal.25", "Width": 98 }, + "wall.102": { "Palette": "pal.25", "Width": 98 }, + "wall.103": { "Palette": "pal.25", "Width": 98 }, + "wall.104": { "Palette": "pal.25", "Width": 98 }, + "wall.105": { "Palette": "pal.25", "Width": 98 }, + "wall.106": { "Palette": "pal.13", "Width": 128 }, + "wall.107": { "Palette": "pal.13", "Width": 128 }, + "wall.108": { "Palette": "pal.13", "Width": 128 }, + "wall.109": { "Palette": "pal.22", "Width": 144 }, + "wall.110": { "Palette": "pal.22", "Width": 144 }, + "wall.111": { "Palette": "pal.22", "Width": 144 }, + "wall.112": { "Palette": "pal.15", "Width": 130 }, + "wall.113": { "Palette": "pal.15", "Width": 130 }, + "wall.114": { "Palette": "pal.13", "Width": 128 }, + "wall.115": { "Palette": "pal.13", "Width": 128 }, + "wall.116": { "Palette": "pal.13", "Width": 128 }, + "wall.117": { "Palette": "pal.13", "Width": 128 }, + "wall.118": { "Palette": "pal.13", "Width": 128 }, + "wall.119": { "Palette": "pal.13", "Width": 100, "Height": 100 }, + "wall.120": { "Palette": "pal.13", "Width": 128 }, + "wall.121": { "Palette": "pal.7", "Width": 80, "Height": 128 }, + "wall.122": { "Palette": "pal.7", "Width": 80 }, + "wall.123": { "Palette": "pal.7", "Width": 79, "ExtraBytes": 49 }, + "wall.124": { "Palette": "pal.25", "Width": 197 }, + "wall.125": { "Palette": "pal.25", "Width": 197 }, + "wall.126": { "Palette": "pal.25", "Width": 197 }, + "wall.127": { "Palette": "pal.25", "Width": 140, "ExtraBytes": 40 }, + "wall.128": { "Palette": "pal.25", "Width": 64 }, + "wall.129": { "Palette": "pal.16", "Width": 134 }, + "wall.130": { "Palette": "pal.22", "Width": 144 }, + "wall.131": { "Palette": "pal.22", "Width": 144 }, + "wall.132": { "Palette": "pal.22", "Width": 144 }, + "wall.133": { "Palette": "pal.22", "Width": 144 }, + "wall.134": { "Palette": "pal.22", "Width": 144 }, + "wall.135": { "Palette": "pal.18", "Width": 134 }, + "wall.136": { "Palette": "pal.18", "Width": 134 }, + "wall.137": { "Palette": "pal.18", "Width": 100, "Height": 100 } } diff --git a/mods/Albion/Meta/COMBACK0.json b/mods/Albion/Meta/COMBACK0.json index e501ff560..0191587f3 100644 --- a/mods/Albion/Meta/COMBACK0.json +++ b/mods/Albion/Meta/COMBACK0.json @@ -1,21 +1,21 @@ { - "000": { "Id": "combg.1", "PaletteId": 23 }, - "001": { "PaletteId": 24 }, - "002": { "PaletteId": 27 }, - "003": { "PaletteId": 32 }, - "004": { "PaletteId": 33 }, - "005": { "PaletteId": 34 }, - "006": { "PaletteId": 35 }, - "007": { "PaletteId": 36 }, - "008": { "PaletteId": 37 }, - "009": { "PaletteId": 38 }, - "010": { "PaletteId": 39 }, - "011": { "PaletteId": 40 }, - "012": { "PaletteId": 41 }, - "013": { "PaletteId": 42 }, - "014": { "PaletteId": 43 }, - "015": { "PaletteId": 44 }, - "016": { "PaletteId": 52 }, - "017": { "PaletteId": 53 }, - "018": { "PaletteId": 54 } + "combg.1": { "Palette": "pal.23" }, + "combg.2": { "Palette": "pal.24" }, + "combg.3": { "Palette": "pal.27" }, + "combg.4": { "Palette": "pal.32" }, + "combg.5": { "Palette": "pal.33" }, + "combg.6": { "Palette": "pal.34" }, + "combg.7": { "Palette": "pal.35" }, + "combg.8": { "Palette": "pal.36" }, + "combg.9": { "Palette": "pal.37" }, + "combg.10": { "Palette": "pal.38" }, + "combg.11": { "Palette": "pal.39" }, + "combg.12": { "Palette": "pal.40" }, + "combg.13": { "Palette": "pal.41" }, + "combg.14": { "Palette": "pal.42" }, + "combg.15": { "Palette": "pal.43" }, + "combg.16": { "Palette": "pal.44" }, + "combg.17": { "Palette": "pal.52" }, + "combg.18": { "Palette": "pal.53" }, + "combg.19": { "Palette": "pal.54" } } diff --git a/mods/Albion/Meta/LABDATA0.json b/mods/Albion/Meta/LABDATA0.json index a8a4c5dac..590a3e882 100644 --- a/mods/Albion/Meta/LABDATA0.json +++ b/mods/Albion/Meta/LABDATA0.json @@ -1,11 +1,11 @@ { - "0": { "Id": "lab.1", "PaletteId": 3 }, // Test1 - "1": { "PaletteId": 7 }, // Test2 - "2": { "PaletteId": 8 }, // Test3 - "3": { "PaletteId": 13 }, // Test4 - "4": { "PaletteId": 14}, // Test5 - "5": { "PaletteId": 15 }, // TestArgim - "6": { "PaletteId": 15 }, // Unknown7 - "7": { "PaletteId": 11 }, // Unknown8 - "8": { "PaletteId": 3 } // Unknown9 + "lab.1": { "Palette": "pal.3" }, // Test1 + "lab.2": { "Palette": "pal.7" }, // Test2 + "lab.3": { "Palette": "pal.8" }, // Test3 + "lab.4": { "Palette": "pal.13" }, // Test4 + "lab.5": { "Palette": "pal.14"}, // Test5 + "lab.6": { "Palette": "pal.15" }, // TestArgim + "lab.7": { "Palette": "pal.15" }, // Unknown7 + "lab.8": { "Palette": "pal.11" }, // Unknown8 + "lab.9": { "Palette": "pal.3" } // Unknown9 } diff --git a/mods/Albion/Meta/LABDATA1.json b/mods/Albion/Meta/LABDATA1.json index b3b9beb83..dbe7c6413 100644 --- a/mods/Albion/Meta/LABDATA1.json +++ b/mods/Albion/Meta/LABDATA1.json @@ -1,27 +1,27 @@ { - "1": { "Id": "lab.101", "PaletteId": 3 }, // TestSrimalinar - "2": { "PaletteId": 7 }, // Toronto1 - "3": { "PaletteId": 8 }, // Toronto2 - "4": { "PaletteId": 13 }, // TestDrinno - "5": { "PaletteId": 13 }, // Unknown105 - "6": { "PaletteId": 15 }, // Argim - "7": { "PaletteId": 15 }, // Unknown107 - "8": { "PaletteId": 18 }, // TestKenget - "9": { "PaletteId": 3 }, // Jirinaar - "10": { "PaletteId": 25 }, // Test110 - "11": { "PaletteId": 15 }, // HunterCellar - "12": { "PaletteId": 13 }, // Drinno - "13": { "PaletteId": 18 }, // Unknown113 - "14": { "PaletteId": 29 }, // ArgimDead - "15": { "PaletteId": 18 }, // Unknown115 - "16": { "PaletteId": 18 }, // Unknown116 - "17": { "PaletteId": 22 }, // TransportCaves - "18": { "PaletteId": 18 }, // Kenget1 - "19": { "PaletteId": 18 }, // Kenget2 - "20": { "PaletteId": 18 }, // Kenget3 - "21": { "PaletteId": 18 }, // Kenget4 - "22": { "PaletteId": 18 }, // Kenget5 - "23": { "PaletteId": 18 }, // Kenget6 - "24": { "PaletteId": 18 }, // Kenget7 - "25": { "PaletteId": 18 } // Kenget8 + "lab.101": { "Palette": "pal.3" }, // TestSrimalinar + "lab.102": { "Palette": "pal.7" }, // Toronto1 + "lab.103": { "Palette": "pal.8" }, // Toronto2 + "lab.104": { "Palette": "pal.13" }, // TestDrinno + "lab.105": { "Palette": "pal.13" }, // Unknown105 + "lab.106": { "Palette": "pal.15" }, // Argim + "lab.107": { "Palette": "pal.15" }, // Unknown107 + "lab.108": { "Palette": "pal.18" }, // TestKenget + "lab.109": { "Palette": "pal.3" }, // Jirinaar + "lab.110": { "Palette": "pal.25" }, // Test110 + "lab.111": { "Palette": "pal.15" }, // HunterCellar + "lab.112": { "Palette": "pal.13" }, // Drinno + "lab.113": { "Palette": "pal.18" }, // Unknown113 + "lab.114": { "Palette": "pal.29" }, // ArgimDead + "lab.115": { "Palette": "pal.18" }, // Unknown115 + "lab.116": { "Palette": "pal.18" }, // Unknown116 + "lab.117": { "Palette": "pal.22" }, // TransportCaves + "lab.118": { "Palette": "pal.18" }, // Kenget1 + "lab.119": { "Palette": "pal.18" }, // Kenget2 + "lab.120": { "Palette": "pal.18" }, // Kenget3 + "lab.121": { "Palette": "pal.18" }, // Kenget4 + "lab.122": { "Palette": "pal.18" }, // Kenget5 + "lab.123": { "Palette": "pal.18" }, // Kenget6 + "lab.124": { "Palette": "pal.18" }, // Kenget7 + "lab.125": { "Palette": "pal.18" } // Kenget8 } diff --git a/mods/Albion/Meta/LABDATA2.json b/mods/Albion/Meta/LABDATA2.json index 2d0f7cd36..558e1abbf 100644 --- a/mods/Albion/Meta/LABDATA2.json +++ b/mods/Albion/Meta/LABDATA2.json @@ -1,13 +1,13 @@ { - "0": { "Id": "lab.200", "PaletteId": 22 }, // KounosCave - "1": { "PaletteId": 25 }, // Unknown201 - "2": { "PaletteId": 13 }, // Kontos - "3": { "PaletteId": 25 }, // Beloveno - "4": { "PaletteId": 3 }, // Srimalinar - "5": { "PaletteId": 25 }, // Unknown205 - "6": { "PaletteId": 25 }, // UmajoKenta - "7": { "PaletteId": 30 }, // UmajoPrison - "8": { "PaletteId": 13 }, // DeviceMaker - "9": { "PaletteId": 13 }, // Unknown209 - "10": { "PaletteId": 30 } // MountainPass + "lab.200": { "Palette": "pal.22" }, // KounosCave + "lab.201": { "Palette": "pal.25" }, // Unknown201 + "lab.202": { "Palette": "pal.13" }, // Kontos + "lab.203": { "Palette": "pal.25" }, // Beloveno + "lab.204": { "Palette": "pal.3" }, // Srimalinar + "lab.205": { "Palette": "pal.25" }, // Unknown205 + "lab.206": { "Palette": "pal.25" }, // UmajoKenta + "lab.207": { "Palette": "pal.30" }, // UmajoPrison + "lab.208": { "Palette": "pal.13" }, // DeviceMaker + "lab.209": { "Palette": "pal.13" }, // Unknown209 + "lab.210": { "Palette": "pal.30" } // MountainPass } diff --git a/mods/Albion/Meta/MONGFX0.json b/mods/Albion/Meta/MONGFX0.json index d2e810d73..357067034 100644 --- a/mods/Albion/Meta/MONGFX0.json +++ b/mods/Albion/Meta/MONGFX0.json @@ -1,8 +1,7 @@ { - "000": { "Id": "mongfx.1" }, - "027": { "PaletteId": 38 }, - "045": { "PaletteId": 38 }, - "046": { "PaletteId": 38 }, - "058": { "PaletteId": 39 }, - "059": { "PaletteId": 38 } + "mongfx.28": { "Palette": "pal.38" }, + "mongfx.46": { "Palette": "pal.38" }, + "mongfx.47": { "Palette": "pal.38" }, + "mongfx.59": { "Palette": "pal.39" }, + "mongfx.60": { "Palette": "pal.38" } } diff --git a/mods/Albion/Meta/MainExe.json b/mods/Albion/Meta/MainExe.json index 4246681d3..65fd90de0 100644 --- a/mods/Albion/Meta/MainExe.json +++ b/mods/Albion/Meta/MainExe.json @@ -1,94 +1,92 @@ { - "000": { "Id": "coregfx.0", - "Offset": 1031768, "Width": 14, "Height": 14, "Hotspot": " 1 1" }, // Cursor - "001": { "Offset": 1031964, "Width": 16, "Height": 16, "Hotspot": " 7 3" }, // Cursor3dUp - "002": { "Offset": 1032220, "Width": 16, "Height": 16, "Hotspot": " 7 10" }, // Cursor3dDown - "003": { "Offset": 1032476, "Width": 16, "Height": 16, "Hotspot": " 2 6" }, // Cursor3dLeft - "004": { "Offset": 1032732, "Width": 16, "Height": 16, "Hotspot": "13 6" }, // Cursor3dRight - "005": { "Offset": 1032988, "Width": 16, "Height": 16, "Hotspot": " 1 7" }, // Cursor3dTurnLeft90 - "006": { "Offset": 1033244, "Width": 16, "Height": 16, "Hotspot": "14 7" }, // Cursor3dTurnRight90 - "007": { "Offset": 1033500, "Width": 16, "Height": 16, "Hotspot": " 5 13" }, // Cursor3dTurnLeft180 - "008": { "Offset": 1033756, "Width": 16, "Height": 16, "Hotspot": "10 13" }, // Cursor3dTurnRight180 - "009": { "Offset": 1034012, "Width": 16, "Height": 16, "Hotspot": " 7 7" }, // Cursor2dUp - "010": { "Offset": 1034268, "Width": 16, "Height": 16, "Hotspot": " 7 7" }, // Cursor2dDown - "011": { "Offset": 1034524, "Width": 16, "Height": 16, "Hotspot": " 7 7" }, // Cursor2dLeft - "012": { "Offset": 1034780, "Width": 16, "Height": 16, "Hotspot": " 7 7" }, // Cursor2dRight - "013": { "Offset": 1035036, "Width": 16, "Height": 16, "Hotspot": " 7 7" }, // Cursor2dUpLeft - "014": { "Offset": 1035292, "Width": 16, "Height": 16, "Hotspot": " 7 7" }, // Cursor2dUpRight - "015": { "Offset": 1035548, "Width": 16, "Height": 16, "Hotspot": " 7 7" }, // Cursor2dDownRight - "016": { "Offset": 1035804, "Width": 16, "Height": 16, "Hotspot": " 7 7" }, // Cursor2dDownLeft - "017": { "Offset": 1036060, "Width": 14, "Height": 12, "Hotspot": " 1 1" }, // CursorSelected - "018": { "Offset": 1036216, "Width": 24, "Height": 15, "Hotspot": "12 7" }, // CursorCdLoad - "019": { "Offset": 1036576, "Width": 16, "Height": 19, "Hotspot": " 8 9" }, // CursorWait - "020": { "Offset": 1036880, "Width": 18, "Height": 25, "Hotspot": " 8 12" }, // CursorMouseClick - "021": { "Offset": 1037330, "Width": 8, "Height": 8, "Hotspot": " 1 1" }, // CursorSmall - "022": { "Offset": 1037394, "Width": 20, "Height": 19, "Hotspot": " 9 9" }, // CursorCrossSelected - "023": { "Offset": 1037774, "Width": 22, "Height": 21, "Hotspot": "10 10" }, // CursorCrossUnselected - "024": { "Offset": 1038236, "Width": 28, "Height": 20, "Hotspot": "14 10" }, // CursorMemoryLoad - "025": { "Offset": 1038826, "Width": 16, "Height": 16, "Hotspot": " 3 3" }, // CursorUpLeft - "026": { "Offset": 1039084, "Width": 16, "Height": 14, "Hotspot": "13 3" }, // CursorUpRight - "027": { "Offset": 1039632, "Width": 32, "Height": 64 }, // UiBackground - "028": { "Offset": 1041680, "Width": 16, "Height": 12 }, // UiBackgroundStriped - "029": { "Offset": 1041872, "Width": 16, "Height": 3 }, // UiBackgroundLines1 - "030": { "Offset": 1041920, "Width": 16, "Height": 3 }, // UiBackgroundLines2 - "031": { "Offset": 1041968, "Width": 16, "Height": 3 }, // UiBackgroundLines3 - "032": { "Offset": 1042016, "Width": 16, "Height": 3 }, // UiBackgroundLines4 - "033": { "Offset": 1042064, "Width": 16, "Height": 16 }, // UiWindowTopLeft - "034": { "Offset": 1042320, "Width": 16, "Height": 16 }, // UiWindowTopRight - "035": { "Offset": 1042576, "Width": 16, "Height": 16 }, // UiWindowBottomLeft - "036": { "Offset": 1042832, "Width": 16, "Height": 16 }, // UiWindowBottomRight - "037": { "Offset": 1043088, "Width": 56, "Height": 16 }, // UiExitButton - "038": { "Offset": 1043984, "Width": 56, "Height": 16 }, // UiExitButtonPressed - "039": { "Offset": 1044880, "Width": 56, "Height": 16 }, // UiExitButtonHover - "040": { "Offset": 1045776, "Width": 8, "Height": 8 }, // UiOffensiveValue - "041": { "Offset": 1045840, "Width": 6, "Height": 8 }, // UiDefensiveValue - "042": { "Offset": 1045888, "Width": 12, "Height": 10 }, // UiGold - "043": { "Offset": 1046008, "Width": 20, "Height": 10 }, // UiFood - "044": { "Offset": 1046208, "Width": 16, "Height": 16 }, // UiNa - "045": { "Offset": 1046464, "Width": 16, "Height": 16 }, // UiBroken - "046": { "Offset": 1046720, "Width": 50, "Height": 8 }, // UiSpellAdvance - "047": { "Offset": 1047120, "Width": 16, "Height": 16 }, // CombatMove - "048": { "Offset": 1047376, "Width": 16, "Height": 16 }, // CombatAttackMelee - "049": { "Offset": 1047632, "Width": 16, "Height": 16 }, // CombatAttackRange - "050": { "Offset": 1047888, "Width": 16, "Height": 16 }, // CombatRetreat - "051": { "Offset": 1048144, "Width": 16, "Height": 16 }, // CombatMagic - "052": { "Offset": 1048400, "Width": 16, "Height": 16 }, // CombatMagicItem - "053": { "Offset": 1048656, "Width": 32, "Height": 27 }, // MonsterEyeOff - "054": { "Offset": 1049520, "Width": 32, "Height": 27 }, // MonsterEyeOn - "055": { "Offset": 1050384, "Width": 32, "Height": 25 }, // Clock - "056": { "Offset": 1051184, "Width": 6, "Height": 7 }, // ClockNum0 - "057": { "Offset": 1051226, "Width": 6, "Height": 7 }, // ClockNum1 - "058": { "Offset": 1051268, "Width": 6, "Height": 7 }, // ClockNum2 - "059": { "Offset": 1051310, "Width": 6, "Height": 7 }, // ClockNum3 - "060": { "Offset": 1051352, "Width": 6, "Height": 7 }, // ClockNum4 - "061": { "Offset": 1051394, "Width": 6, "Height": 7 }, // ClockNum5 - "062": { "Offset": 1051436, "Width": 6, "Height": 7 }, // ClockNum6 - "063": { "Offset": 1051478, "Width": 6, "Height": 7 }, // ClockNum7 - "064": { "Offset": 1051520, "Width": 6, "Height": 7 }, // ClockNum8 - "065": { "Offset": 1051562, "Width": 6, "Height": 7 }, // ClockNum9 - "066": { "Offset": 1051604, "Width": 30, "Height": 29 }, // CompassDe - "067": { "Offset": 1052474, "Width": 30, "Height": 29 }, // CompassEn - "068": { "Offset": 1053344, "Width": 30, "Height": 29 }, // CompassFr - "069": { "Offset": 1054214, "Width": 6, "Height": 6 }, // CompassDot0 - "070": { "Offset": 1054250, "Width": 6, "Height": 6 }, // CompassDot1 - "071": { "Offset": 1054286, "Width": 6, "Height": 6 }, // CompassDot2 - "072": { "Offset": 1054322, "Width": 6, "Height": 6 }, // CompassDot3 - "073": { "Offset": 1054358, "Width": 6, "Height": 6 }, // CompassDot4 - "074": { "Offset": 1054394, "Width": 6, "Height": 6 }, // CompassDot5 - "075": { "Offset": 1054430, "Width": 6, "Height": 6 }, // CompassDot6 - "076": { "Offset": 1054466, "Width": 6, "Height": 6 }, // CompassDot7 - "077": { "Offset": 1054502, "Width": 18, "Height": 18 }, // Select - "078": { "Offset": 1054826, "Width": 22, "Height": 22 }, // Lock - "079": { "Offset": 1055310, "Width": 34, "Height": 48 }, // Producer - "080": { "Offset": 1056942, "Width": 32, "Height": 32 }, // CharEffect1 - "081": { "Offset": 1057966, "Width": 32, "Height": 32 }, // CharEffect2 - "082": { "Offset": 1058989, "Width": 14, "Height": 13 }, // CharEffect3 - "083": { "Offset": 1059171, "Width": 16, "Height": 16 }, // ArrowTurnLeft90 - "084": { "Offset": 1059427, "Width": 16, "Height": 16 }, // ArrowTurnRight90 - "085": { "Offset": 1059683, "Width": 16, "Height": 16 }, // ArrowTurnLeft180 - "086": { "Offset": 1059939, "Width": 16, "Height": 16 }, // ArrowTurnRight180 - "087": { "Offset": 1060195, "Width": 16, "Height": 16 }, // ArrowLookUp - "088": { "Offset": 1060451, "Width": 16, "Height": 16 } // ArrowLookDown + "coregfx.0": { "Offset": 1031768, "Width": 14, "Height": 14, "Hotspot": " 1 1" }, // Cursor + "coregfx.1": { "Offset": 1031964, "Width": 16, "Height": 16, "Hotspot": " 7 3" }, // Cursor3dUp + "coregfx.2": { "Offset": 1032220, "Width": 16, "Height": 16, "Hotspot": " 7 10" }, // Cursor3dDown + "coregfx.3": { "Offset": 1032476, "Width": 16, "Height": 16, "Hotspot": " 2 6" }, // Cursor3dLeft + "coregfx.4": { "Offset": 1032732, "Width": 16, "Height": 16, "Hotspot": "13 6" }, // Cursor3dRight + "coregfx.5": { "Offset": 1032988, "Width": 16, "Height": 16, "Hotspot": " 1 7" }, // Cursor3dTurnLeft90 + "coregfx.6": { "Offset": 1033244, "Width": 16, "Height": 16, "Hotspot": "14 7" }, // Cursor3dTurnRight90 + "coregfx.7": { "Offset": 1033500, "Width": 16, "Height": 16, "Hotspot": " 5 13" }, // Cursor3dTurnLeft180 + "coregfx.8": { "Offset": 1033756, "Width": 16, "Height": 16, "Hotspot": "10 13" }, // Cursor3dTurnRight180 + "coregfx.9": { "Offset": 1034012, "Width": 16, "Height": 16, "Hotspot": " 7 7" }, // Cursor2dUp + "coregfx.10": { "Offset": 1034268, "Width": 16, "Height": 16, "Hotspot": " 7 7" }, // Cursor2dDown + "coregfx.11": { "Offset": 1034524, "Width": 16, "Height": 16, "Hotspot": " 7 7" }, // Cursor2dLeft + "coregfx.12": { "Offset": 1034780, "Width": 16, "Height": 16, "Hotspot": " 7 7" }, // Cursor2dRight + "coregfx.13": { "Offset": 1035036, "Width": 16, "Height": 16, "Hotspot": " 7 7" }, // Cursor2dUpLeft + "coregfx.14": { "Offset": 1035292, "Width": 16, "Height": 16, "Hotspot": " 7 7" }, // Cursor2dUpRight + "coregfx.15": { "Offset": 1035548, "Width": 16, "Height": 16, "Hotspot": " 7 7" }, // Cursor2dDownRight + "coregfx.16": { "Offset": 1035804, "Width": 16, "Height": 16, "Hotspot": " 7 7" }, // Cursor2dDownLeft + "coregfx.17": { "Offset": 1036060, "Width": 14, "Height": 12, "Hotspot": " 1 1" }, // CursorSelected + "coregfx.18": { "Offset": 1036216, "Width": 24, "Height": 15, "Hotspot": "12 7" }, // CursorCdLoad + "coregfx.19": { "Offset": 1036576, "Width": 16, "Height": 19, "Hotspot": " 8 9" }, // CursorWait + "coregfx.20": { "Offset": 1036880, "Width": 18, "Height": 25, "Hotspot": " 8 12" }, // CursorMouseClick + "coregfx.21": { "Offset": 1037330, "Width": 8, "Height": 8, "Hotspot": " 1 1" }, // CursorSmall + "coregfx.22": { "Offset": 1037394, "Width": 20, "Height": 19, "Hotspot": " 9 9" }, // CursorCrossSelected + "coregfx.23": { "Offset": 1037774, "Width": 22, "Height": 21, "Hotspot": "10 10" }, // CursorCrossUnselected + "coregfx.24": { "Offset": 1038236, "Width": 28, "Height": 20, "Hotspot": "14 10" }, // CursorMemoryLoad + "coregfx.25": { "Offset": 1038826, "Width": 16, "Height": 16, "Hotspot": " 3 3" }, // CursorUpLeft + "coregfx.26": { "Offset": 1039084, "Width": 16, "Height": 14, "Hotspot": "13 3" }, // CursorUpRight + "coregfx.27": { "Offset": 1039632, "Width": 32, "Height": 64 }, // UiBackground + "coregfx.28": { "Offset": 1041680, "Width": 16, "Height": 12 }, // UiBackgroundStriped + "coregfx.29": { "Offset": 1041872, "Width": 16, "Height": 3 }, // UiBackgroundLines1 + "coregfx.30": { "Offset": 1041920, "Width": 16, "Height": 3 }, // UiBackgroundLines2 + "coregfx.31": { "Offset": 1041968, "Width": 16, "Height": 3 }, // UiBackgroundLines3 + "coregfx.32": { "Offset": 1042016, "Width": 16, "Height": 3 }, // UiBackgroundLines4 + "coregfx.33": { "Offset": 1042064, "Width": 16, "Height": 16 }, // UiWindowTopLeft + "coregfx.34": { "Offset": 1042320, "Width": 16, "Height": 16 }, // UiWindowTopRight + "coregfx.35": { "Offset": 1042576, "Width": 16, "Height": 16 }, // UiWindowBottomLeft + "coregfx.36": { "Offset": 1042832, "Width": 16, "Height": 16 }, // UiWindowBottomRight + "coregfx.37": { "Offset": 1043088, "Width": 56, "Height": 16 }, // UiExitButton + "coregfx.38": { "Offset": 1043984, "Width": 56, "Height": 16 }, // UiExitButtonPressed + "coregfx.39": { "Offset": 1044880, "Width": 56, "Height": 16 }, // UiExitButtonHover + "coregfx.40": { "Offset": 1045776, "Width": 8, "Height": 8 }, // UiOffensiveValue + "coregfx.41": { "Offset": 1045840, "Width": 6, "Height": 8 }, // UiDefensiveValue + "coregfx.42": { "Offset": 1045888, "Width": 12, "Height": 10 }, // UiGold + "coregfx.43": { "Offset": 1046008, "Width": 20, "Height": 10 }, // UiFood + "coregfx.44": { "Offset": 1046208, "Width": 16, "Height": 16 }, // UiNa + "coregfx.45": { "Offset": 1046464, "Width": 16, "Height": 16 }, // UiBroken + "coregfx.46": { "Offset": 1046720, "Width": 50, "Height": 8 }, // UiSpellAdvance + "coregfx.47": { "Offset": 1047120, "Width": 16, "Height": 16 }, // CombatMove + "coregfx.48": { "Offset": 1047376, "Width": 16, "Height": 16 }, // CombatAttackMelee + "coregfx.49": { "Offset": 1047632, "Width": 16, "Height": 16 }, // CombatAttackRange + "coregfx.50": { "Offset": 1047888, "Width": 16, "Height": 16 }, // CombatRetreat + "coregfx.51": { "Offset": 1048144, "Width": 16, "Height": 16 }, // CombatMagic + "coregfx.52": { "Offset": 1048400, "Width": 16, "Height": 16 }, // CombatMagicItem + "coregfx.53": { "Offset": 1048656, "Width": 32, "Height": 27 }, // MonsterEyeOff + "coregfx.54": { "Offset": 1049520, "Width": 32, "Height": 27 }, // MonsterEyeOn + "coregfx.55": { "Offset": 1050384, "Width": 32, "Height": 25 }, // Clock + "coregfx.56": { "Offset": 1051184, "Width": 6, "Height": 7 }, // ClockNum0 + "coregfx.57": { "Offset": 1051226, "Width": 6, "Height": 7 }, // ClockNum1 + "coregfx.58": { "Offset": 1051268, "Width": 6, "Height": 7 }, // ClockNum2 + "coregfx.59": { "Offset": 1051310, "Width": 6, "Height": 7 }, // ClockNum3 + "coregfx.60": { "Offset": 1051352, "Width": 6, "Height": 7 }, // ClockNum4 + "coregfx.61": { "Offset": 1051394, "Width": 6, "Height": 7 }, // ClockNum5 + "coregfx.62": { "Offset": 1051436, "Width": 6, "Height": 7 }, // ClockNum6 + "coregfx.63": { "Offset": 1051478, "Width": 6, "Height": 7 }, // ClockNum7 + "coregfx.64": { "Offset": 1051520, "Width": 6, "Height": 7 }, // ClockNum8 + "coregfx.65": { "Offset": 1051562, "Width": 6, "Height": 7 }, // ClockNum9 + "coregfx.66": { "Offset": 1051604, "Width": 30, "Height": 29 }, // CompassDe + "coregfx.67": { "Offset": 1052474, "Width": 30, "Height": 29 }, // CompassEn + "coregfx.68": { "Offset": 1053344, "Width": 30, "Height": 29 }, // CompassFr + "coregfx.69": { "Offset": 1054214, "Width": 6, "Height": 6 }, // CompassDot0 + "coregfx.70": { "Offset": 1054250, "Width": 6, "Height": 6 }, // CompassDot1 + "coregfx.71": { "Offset": 1054286, "Width": 6, "Height": 6 }, // CompassDot2 + "coregfx.72": { "Offset": 1054322, "Width": 6, "Height": 6 }, // CompassDot3 + "coregfx.73": { "Offset": 1054358, "Width": 6, "Height": 6 }, // CompassDot4 + "coregfx.74": { "Offset": 1054394, "Width": 6, "Height": 6 }, // CompassDot5 + "coregfx.75": { "Offset": 1054430, "Width": 6, "Height": 6 }, // CompassDot6 + "coregfx.76": { "Offset": 1054466, "Width": 6, "Height": 6 }, // CompassDot7 + "coregfx.77": { "Offset": 1054502, "Width": 18, "Height": 18 }, // Select + "coregfx.78": { "Offset": 1054826, "Width": 22, "Height": 22 }, // Lock + "coregfx.79": { "Offset": 1055310, "Width": 34, "Height": 48 }, // Producer + "coregfx.80": { "Offset": 1056942, "Width": 32, "Height": 32 }, // CharEffect1 + "coregfx.81": { "Offset": 1057966, "Width": 32, "Height": 32 }, // CharEffect2 + "coregfx.82": { "Offset": 1058989, "Width": 14, "Height": 13 }, // CharEffect3 + "coregfx.83": { "Offset": 1059171, "Width": 16, "Height": 16 }, // ArrowTurnLeft90 + "coregfx.84": { "Offset": 1059427, "Width": 16, "Height": 16 }, // ArrowTurnRight90 + "coregfx.85": { "Offset": 1059683, "Width": 16, "Height": 16 }, // ArrowTurnLeft180 + "coregfx.86": { "Offset": 1059939, "Width": 16, "Height": 16 }, // ArrowTurnRight180 + "coregfx.87": { "Offset": 1060195, "Width": 16, "Height": 16 }, // ArrowLookUp + "coregfx.88": { "Offset": 1060451, "Width": 16, "Height": 16 } // ArrowLookDown } - diff --git a/mods/Albion/Meta/MainExe_DE.json b/mods/Albion/Meta/MainExe_DE.json index 83f389494..53fc008c7 100644 --- a/mods/Albion/Meta/MainExe_DE.json +++ b/mods/Albion/Meta/MainExe_DE.json @@ -1,94 +1,93 @@ { - "000": { "Id": "coregfx.0", - "Offset": 1025980, "Width": 14, "Height": 14, "Hotspot": " 1 1" } , // Cursor - "001": { "Offset": 1026176, "Width": 16, "Height": 16, "Hotspot": " 7 3" } , // Cursor3dUp - "002": { "Offset": 1026432, "Width": 16, "Height": 16, "Hotspot": " 7 10" } , // Cursor3dDown - "003": { "Offset": 1026688, "Width": 16, "Height": 16, "Hotspot": " 2 6" } , // Cursor3dLeft - "004": { "Offset": 1026944, "Width": 16, "Height": 16, "Hotspot": "13 6" } , // Cursor3dRight - "005": { "Offset": 1027200, "Width": 16, "Height": 16, "Hotspot": " 1 7" } , // Cursor3dTurnLeft90 - "006": { "Offset": 1027456, "Width": 16, "Height": 16, "Hotspot": "14 7" } , // Cursor3dTurnRight90 - "007": { "Offset": 1027712, "Width": 16, "Height": 16, "Hotspot": " 5 13" } , // Cursor3dTurnLeft180 - "008": { "Offset": 1027968, "Width": 16, "Height": 16, "Hotspot": "10 13" } , // Cursor3dTurnRight180 - "009": { "Offset": 1028224, "Width": 16, "Height": 16, "Hotspot": " 7 7" } , // Cursor2dUp - "010": { "Offset": 1028480, "Width": 16, "Height": 16, "Hotspot": " 7 7" } , // Cursor2dDown - "011": { "Offset": 1028736, "Width": 16, "Height": 16, "Hotspot": " 7 7" } , // Cursor2dLeft - "012": { "Offset": 1028992, "Width": 16, "Height": 16, "Hotspot": " 7 7" } , // Cursor2dRight - "013": { "Offset": 1029248, "Width": 16, "Height": 16, "Hotspot": " 7 7" } , // Cursor2dUpLeft - "014": { "Offset": 1029504, "Width": 16, "Height": 16, "Hotspot": " 7 7" } , // Cursor2dUpRight - "015": { "Offset": 1029760, "Width": 16, "Height": 16, "Hotspot": " 7 7" } , // Cursor2dDownRight - "016": { "Offset": 1030016, "Width": 16, "Height": 16, "Hotspot": " 7 7" } , // Cursor2dDownLeft - "017": { "Offset": 1030272, "Width": 14, "Height": 12, "Hotspot": " 1 1" } , // CursorSelected - "018": { "Offset": 1030452, "Width": 24, "Height": 15, "Hotspot": "12 7" } , // CursorCdLoad - "019": { "Offset": 1030788, "Width": 16, "Height": 19, "Hotspot": " 8 9" } , // CursorWait - "020": { "Offset": 1031092, "Width": 18, "Height": 25, "Hotspot": " 8 12" } , // CursorMouseClick - "021": { "Offset": 1031542, "Width": 8, "Height": 8, "Hotspot": " 1 1" } , // CursorSmall - "022": { "Offset": 1031606, "Width": 20, "Height": 19, "Hotspot": " 9 9" } , // CursorCrossSelected - "023": { "Offset": 1031986, "Width": 22, "Height": 21, "Hotspot": "10 10" } , // CursorCrossUnselected - "024": { "Offset": 1032448, "Width": 28, "Height": 20, "Hotspot": "14 10" } , // CursorMemoryLoad - "025": { "Offset": 1033038, "Width": 16, "Height": 16, "Hotspot": " 3 3" } , // CursorUpLeft - "026": { "Offset": 1033296, "Width": 16, "Height": 14, "Hotspot": "13 3" } , // CursorUpRight - "027": { "Offset": 1033844, "Width": 32, "Height": 64 }, // UiBackground - "028": { "Offset": 1035892, "Width": 16, "Height": 12 }, // UiBackgroundStriped - "029": { "Offset": 1036084, "Width": 16, "Height": 3 }, // UiBackgroundLines1 - "030": { "Offset": 1036132, "Width": 16, "Height": 3 }, // UiBackgroundLines2 - "031": { "Offset": 1036180, "Width": 16, "Height": 3 }, // UiBackgroundLines3 - "032": { "Offset": 1036228, "Width": 16, "Height": 3 }, // UiBackgroundLines4 - "033": { "Offset": 1036276, "Width": 16, "Height": 16 }, // UiWindowTopLeft - "034": { "Offset": 1036532, "Width": 16, "Height": 16 }, // UiWindowTopRight - "035": { "Offset": 1036788, "Width": 16, "Height": 16 }, // UiWindowBottomLeft - "036": { "Offset": 1037044, "Width": 16, "Height": 16 }, // UiWindowBottomRight - "037": { "Offset": 1037300, "Width": 56, "Height": 16 }, // UiExitButton - "038": { "Offset": 1038196, "Width": 56, "Height": 16 }, // UiExitButtonPressed - "039": { "Offset": 1039092, "Width": 56, "Height": 16 }, // UiExitButtonHover - "040": { "Offset": 1039988, "Width": 8, "Height": 8 }, // UiOffensiveValue - "041": { "Offset": 1040052, "Width": 6, "Height": 8 }, // UiDefensiveValue - "042": { "Offset": 1040100, "Width": 12, "Height": 10 }, // UiGold - "043": { "Offset": 1040220, "Width": 20, "Height": 10 }, // UiFood - "044": { "Offset": 1040420, "Width": 16, "Height": 16 }, // UiNa - "045": { "Offset": 1040676, "Width": 16, "Height": 16 }, // UiBroken - "046": { "Offset": 1040932, "Width": 50, "Height": 8 }, // UiSpellAdvance - "047": { "Offset": 1041332, "Width": 16, "Height": 16 }, // CombatMove - "048": { "Offset": 1041588, "Width": 16, "Height": 16 }, // CombatAttackMelee - "049": { "Offset": 1041844, "Width": 16, "Height": 16 }, // CombatAttackRange - "050": { "Offset": 1042100, "Width": 16, "Height": 16 }, // CombatRetreat - "051": { "Offset": 1042356, "Width": 16, "Height": 16 }, // CombatMagic - "052": { "Offset": 1042612, "Width": 16, "Height": 16 }, // CombatMagicItem - "053": { "Offset": 1042868, "Width": 32, "Height": 27 }, // MonsterEyeOff - "054": { "Offset": 1043732, "Width": 32, "Height": 27 }, // MonsterEyeOn - "055": { "Offset": 1044596, "Width": 32, "Height": 25 }, // Clock - "056": { "Offset": 1045396, "Width": 6, "Height": 7 }, // ClockNum0 - "057": { "Offset": 1045438, "Width": 6, "Height": 7 }, // ClockNum1 - "058": { "Offset": 1045480, "Width": 6, "Height": 7 }, // ClockNum2 - "059": { "Offset": 1045522, "Width": 6, "Height": 7 }, // ClockNum3 - "060": { "Offset": 1045564, "Width": 6, "Height": 7 }, // ClockNum4 - "061": { "Offset": 1045606, "Width": 6, "Height": 7 }, // ClockNum5 - "062": { "Offset": 1045648, "Width": 6, "Height": 7 }, // ClockNum6 - "063": { "Offset": 1045690, "Width": 6, "Height": 7 }, // ClockNum7 - "064": { "Offset": 1045732, "Width": 6, "Height": 7 }, // ClockNum8 - "065": { "Offset": 1045774, "Width": 6, "Height": 7 }, // ClockNum9 - "066": { "Offset": 1045816, "Width": 30, "Height": 29 }, // CompassDe - // "067": { "Offset": 1046686, "Width": 30, "Height": 29 }, // CompassEn - // "068": { "Offset": 1047556, "Width": 30, "Height": 29 }, // CompassFr - "069": { "Offset": 1046686, "Width": 6, "Height": 6 }, // CompassDot0 - "070": { "Offset": 1046722, "Width": 6, "Height": 6 }, // CompassDot1 - "071": { "Offset": 1046758, "Width": 6, "Height": 6 }, // CompassDot2 - "072": { "Offset": 1046794, "Width": 6, "Height": 6 }, // CompassDot3 - "073": { "Offset": 1046830, "Width": 6, "Height": 6 }, // CompassDot4 - "074": { "Offset": 1046866, "Width": 6, "Height": 6 }, // CompassDot5 - "075": { "Offset": 1046902, "Width": 6, "Height": 6 }, // CompassDot6 - "076": { "Offset": 1046938, "Width": 6, "Height": 6 }, // CompassDot7 - "077": { "Offset": 1046974, "Width": 18, "Height": 18 }, // Select - "078": { "Offset": 1047298, "Width": 22, "Height": 22 }, // Lock - "079": { "Offset": 1047782, "Width": 34, "Height": 48 }, // Producer - "080": { "Offset": 1049414, "Width": 32, "Height": 32 }, // CharEffect1 - "081": { "Offset": 1050438, "Width": 32, "Height": 32 }, // CharEffect2 - "082": { "Offset": 1051461, "Width": 14, "Height": 13 }, // CharEffect3 - "083": { "Offset": 1051643, "Width": 16, "Height": 16 }, // ArrowTurnLeft90 - "084": { "Offset": 1051899, "Width": 16, "Height": 16 }, // ArrowTurnRight90 - "085": { "Offset": 1052155, "Width": 16, "Height": 16 }, // ArrowTurnLeft180 - "086": { "Offset": 1052411, "Width": 16, "Height": 16 }, // ArrowTurnRight180 - "087": { "Offset": 1052667, "Width": 16, "Height": 16 }, // ArrowLookUp - "088": { "Offset": 1052923, "Width": 16, "Height": 16 } // ArrowLookDown - // Strange cursor at 1067914 (0x104b8a), width 11 - // Circular thingy at 1068065 (0x104c21), width 16 + "coregfx.0": { "Offset": 1025980, "Width": 14, "Height": 14, "Hotspot": " 1 1" } , // Cursor + "coregfx.1": { "Offset": 1026176, "Width": 16, "Height": 16, "Hotspot": " 7 3" } , // Cursor3dUp + "coregfx.2": { "Offset": 1026432, "Width": 16, "Height": 16, "Hotspot": " 7 10" } , // Cursor3dDown + "coregfx.3": { "Offset": 1026688, "Width": 16, "Height": 16, "Hotspot": " 2 6" } , // Cursor3dLeft + "coregfx.4": { "Offset": 1026944, "Width": 16, "Height": 16, "Hotspot": "13 6" } , // Cursor3dRight + "coregfx.5": { "Offset": 1027200, "Width": 16, "Height": 16, "Hotspot": " 1 7" } , // Cursor3dTurnLeft90 + "coregfx.6": { "Offset": 1027456, "Width": 16, "Height": 16, "Hotspot": "14 7" } , // Cursor3dTurnRight90 + "coregfx.7": { "Offset": 1027712, "Width": 16, "Height": 16, "Hotspot": " 5 13" } , // Cursor3dTurnLeft180 + "coregfx.8": { "Offset": 1027968, "Width": 16, "Height": 16, "Hotspot": "10 13" } , // Cursor3dTurnRight180 + "coregfx.9": { "Offset": 1028224, "Width": 16, "Height": 16, "Hotspot": " 7 7" } , // Cursor2dUp + "coregfx.10": { "Offset": 1028480, "Width": 16, "Height": 16, "Hotspot": " 7 7" } , // Cursor2dDown + "coregfx.11": { "Offset": 1028736, "Width": 16, "Height": 16, "Hotspot": " 7 7" } , // Cursor2dLeft + "coregfx.12": { "Offset": 1028992, "Width": 16, "Height": 16, "Hotspot": " 7 7" } , // Cursor2dRight + "coregfx.13": { "Offset": 1029248, "Width": 16, "Height": 16, "Hotspot": " 7 7" } , // Cursor2dUpLeft + "coregfx.14": { "Offset": 1029504, "Width": 16, "Height": 16, "Hotspot": " 7 7" } , // Cursor2dUpRight + "coregfx.15": { "Offset": 1029760, "Width": 16, "Height": 16, "Hotspot": " 7 7" } , // Cursor2dDownRight + "coregfx.16": { "Offset": 1030016, "Width": 16, "Height": 16, "Hotspot": " 7 7" } , // Cursor2dDownLeft + "coregfx.17": { "Offset": 1030272, "Width": 14, "Height": 12, "Hotspot": " 1 1" } , // CursorSelected + "coregfx.18": { "Offset": 1030452, "Width": 24, "Height": 15, "Hotspot": "12 7" } , // CursorCdLoad + "coregfx.19": { "Offset": 1030788, "Width": 16, "Height": 19, "Hotspot": " 8 9" } , // CursorWait + "coregfx.20": { "Offset": 1031092, "Width": 18, "Height": 25, "Hotspot": " 8 12" } , // CursorMouseClick + "coregfx.21": { "Offset": 1031542, "Width": 8, "Height": 8, "Hotspot": " 1 1" } , // CursorSmall + "coregfx.22": { "Offset": 1031606, "Width": 20, "Height": 19, "Hotspot": " 9 9" } , // CursorCrossSelected + "coregfx.23": { "Offset": 1031986, "Width": 22, "Height": 21, "Hotspot": "10 10" } , // CursorCrossUnselected + "coregfx.24": { "Offset": 1032448, "Width": 28, "Height": 20, "Hotspot": "14 10" } , // CursorMemoryLoad + "coregfx.25": { "Offset": 1033038, "Width": 16, "Height": 16, "Hotspot": " 3 3" } , // CursorUpLeft + "coregfx.26": { "Offset": 1033296, "Width": 16, "Height": 14, "Hotspot": "13 3" } , // CursorUpRight + "coregfx.27": { "Offset": 1033844, "Width": 32, "Height": 64 }, // UiBackground + "coregfx.28": { "Offset": 1035892, "Width": 16, "Height": 12 }, // UiBackgroundStriped + "coregfx.29": { "Offset": 1036084, "Width": 16, "Height": 3 }, // UiBackgroundLines1 + "coregfx.30": { "Offset": 1036132, "Width": 16, "Height": 3 }, // UiBackgroundLines2 + "coregfx.31": { "Offset": 1036180, "Width": 16, "Height": 3 }, // UiBackgroundLines3 + "coregfx.32": { "Offset": 1036228, "Width": 16, "Height": 3 }, // UiBackgroundLines4 + "coregfx.33": { "Offset": 1036276, "Width": 16, "Height": 16 }, // UiWindowTopLeft + "coregfx.34": { "Offset": 1036532, "Width": 16, "Height": 16 }, // UiWindowTopRight + "coregfx.35": { "Offset": 1036788, "Width": 16, "Height": 16 }, // UiWindowBottomLeft + "coregfx.36": { "Offset": 1037044, "Width": 16, "Height": 16 }, // UiWindowBottomRight + "coregfx.37": { "Offset": 1037300, "Width": 56, "Height": 16 }, // UiExitButton + "coregfx.38": { "Offset": 1038196, "Width": 56, "Height": 16 }, // UiExitButtonPressed + "coregfx.39": { "Offset": 1039092, "Width": 56, "Height": 16 }, // UiExitButtonHover + "coregfx.40": { "Offset": 1039988, "Width": 8, "Height": 8 }, // UiOffensiveValue + "coregfx.41": { "Offset": 1040052, "Width": 6, "Height": 8 }, // UiDefensiveValue + "coregfx.42": { "Offset": 1040100, "Width": 12, "Height": 10 }, // UiGold + "coregfx.43": { "Offset": 1040220, "Width": 20, "Height": 10 }, // UiFood + "coregfx.44": { "Offset": 1040420, "Width": 16, "Height": 16 }, // UiNa + "coregfx.45": { "Offset": 1040676, "Width": 16, "Height": 16 }, // UiBroken + "coregfx.46": { "Offset": 1040932, "Width": 50, "Height": 8 }, // UiSpellAdvance + "coregfx.47": { "Offset": 1041332, "Width": 16, "Height": 16 }, // CombatMove + "coregfx.48": { "Offset": 1041588, "Width": 16, "Height": 16 }, // CombatAttackMelee + "coregfx.49": { "Offset": 1041844, "Width": 16, "Height": 16 }, // CombatAttackRange + "coregfx.50": { "Offset": 1042100, "Width": 16, "Height": 16 }, // CombatRetreat + "coregfx.51": { "Offset": 1042356, "Width": 16, "Height": 16 }, // CombatMagic + "coregfx.52": { "Offset": 1042612, "Width": 16, "Height": 16 }, // CombatMagicItem + "coregfx.53": { "Offset": 1042868, "Width": 32, "Height": 27 }, // MonsterEyeOff + "coregfx.54": { "Offset": 1043732, "Width": 32, "Height": 27 }, // MonsterEyeOn + "coregfx.55": { "Offset": 1044596, "Width": 32, "Height": 25 }, // Clock + "coregfx.56": { "Offset": 1045396, "Width": 6, "Height": 7 }, // ClockNum0 + "coregfx.57": { "Offset": 1045438, "Width": 6, "Height": 7 }, // ClockNum1 + "coregfx.58": { "Offset": 1045480, "Width": 6, "Height": 7 }, // ClockNum2 + "coregfx.59": { "Offset": 1045522, "Width": 6, "Height": 7 }, // ClockNum3 + "coregfx.60": { "Offset": 1045564, "Width": 6, "Height": 7 }, // ClockNum4 + "coregfx.61": { "Offset": 1045606, "Width": 6, "Height": 7 }, // ClockNum5 + "coregfx.62": { "Offset": 1045648, "Width": 6, "Height": 7 }, // ClockNum6 + "coregfx.63": { "Offset": 1045690, "Width": 6, "Height": 7 }, // ClockNum7 + "coregfx.64": { "Offset": 1045732, "Width": 6, "Height": 7 }, // ClockNum8 + "coregfx.65": { "Offset": 1045774, "Width": 6, "Height": 7 }, // ClockNum9 + "coregfx.66": { "Offset": 1045816, "Width": 30, "Height": 29 }, // CompassDe + // "coregfx.67": { "Offset": 1046686, "Width": 30, "Height": 29 }, // CompassEn + // "coregfx.68": { "Offset": 1047556, "Width": 30, "Height": 29 }, // CompassFr + "coregfx.69": { "Offset": 1046686, "Width": 6, "Height": 6 }, // CompassDot0 + "coregfx.70": { "Offset": 1046722, "Width": 6, "Height": 6 }, // CompassDot1 + "coregfx.71": { "Offset": 1046758, "Width": 6, "Height": 6 }, // CompassDot2 + "coregfx.72": { "Offset": 1046794, "Width": 6, "Height": 6 }, // CompassDot3 + "coregfx.73": { "Offset": 1046830, "Width": 6, "Height": 6 }, // CompassDot4 + "coregfx.74": { "Offset": 1046866, "Width": 6, "Height": 6 }, // CompassDot5 + "coregfx.75": { "Offset": 1046902, "Width": 6, "Height": 6 }, // CompassDot6 + "coregfx.76": { "Offset": 1046938, "Width": 6, "Height": 6 }, // CompassDot7 + "coregfx.77": { "Offset": 1046974, "Width": 18, "Height": 18 }, // Select + "coregfx.78": { "Offset": 1047298, "Width": 22, "Height": 22 }, // Lock + "coregfx.79": { "Offset": 1047782, "Width": 34, "Height": 48 }, // Producer + "coregfx.80": { "Offset": 1049414, "Width": 32, "Height": 32 }, // CharEffect1 + "coregfx.81": { "Offset": 1050438, "Width": 32, "Height": 32 }, // CharEffect2 + "coregfx.82": { "Offset": 1051461, "Width": 14, "Height": 13 }, // CharEffect3 + "coregfx.83": { "Offset": 1051643, "Width": 16, "Height": 16 }, // ArrowTurnLeft90 + "coregfx.84": { "Offset": 1051899, "Width": 16, "Height": 16 }, // ArrowTurnRight90 + "coregfx.85": { "Offset": 1052155, "Width": 16, "Height": 16 }, // ArrowTurnLeft180 + "coregfx.86": { "Offset": 1052411, "Width": 16, "Height": 16 }, // ArrowTurnRight180 + "coregfx.87": { "Offset": 1052667, "Width": 16, "Height": 16 }, // ArrowLookUp + "coregfx.88": { "Offset": 1052923, "Width": 16, "Height": 16 } // ArrowLookDown + // Strange cursor at 1067914 (0x104b8a), width 11 + // Circular thingy at 1068065 (0x104c21), width 16 } diff --git a/mods/Albion/Meta/NPCGR0.json b/mods/Albion/Meta/NPCGR0.json index 773811034..558d368f6 100644 --- a/mods/Albion/Meta/NPCGR0.json +++ b/mods/Albion/Meta/NPCGR0.json @@ -1,101 +1,101 @@ { - "000": { "Id": "npclarge.1", "PaletteId": 6 }, - "001": { "PaletteId": 6 }, - "002": { "PaletteId": 6 }, - "003": { "PaletteId": 6 }, - "004": { "PaletteId": 6 }, - "005": { "PaletteId": 6 }, - "006": { "PaletteId": 6 }, - "007": { "PaletteId": 6 }, - "008": { "PaletteId": 6 }, - "009": { "PaletteId": 6 }, - "010": { "PaletteId": 6 }, - "011": { "PaletteId": 6 }, - "012": { "PaletteId": 6 }, - "013": { "PaletteId": 6 }, - "014": { "PaletteId": 6 }, - "015": { "PaletteId": 6 }, - "016": { "PaletteId": 6 }, - "017": { "PaletteId": 6 }, - "018": { "PaletteId": 26 }, - "019": { "PaletteId": 26 }, - "020": { "PaletteId": 26 }, - "021": { "PaletteId": 26 }, - "022": { "PaletteId": 26 }, - "023": { "PaletteId": 26 }, - "024": { "PaletteId": 26 }, - "025": { "PaletteId": 26 }, - "026": { "PaletteId": 6 }, - "027": { "PaletteId": 6 }, - "028": { "PaletteId": 6 }, - "029": { "PaletteId": 6 }, - "030": { "PaletteId": 6 }, - "031": { "PaletteId": 6 }, - "032": { "PaletteId": 6 }, - "033": { "PaletteId": 6 }, - "034": { "PaletteId": 6 }, - "035": { "PaletteId": 6 }, - "036": { "PaletteId": 6 }, - "037": { "PaletteId": 6 }, - "038": { "PaletteId": 9 }, - "039": { "PaletteId": 9 }, - "040": { "PaletteId": 9 }, - "041": { "PaletteId": 9 }, - "042": { "PaletteId": 9 }, - "043": { "PaletteId": 9 }, - "044": { "PaletteId": 9 }, - "045": { "PaletteId": 9 }, - "046": { "PaletteId": 9 }, - "047": { "PaletteId": 9 }, - "048": { "PaletteId": 9 }, - "049": { "PaletteId": 9 }, - "050": { "PaletteId": 9 }, - "051": { "PaletteId": 9 }, - "052": { "PaletteId": 9 }, - "053": { "PaletteId": 9 }, - "054": { "PaletteId": 9 }, - "055": { "PaletteId": 9 }, - "056": { "PaletteId": 9 }, - "057": { "PaletteId": 9 }, - "058": { "PaletteId": 9 }, - "059": { "PaletteId": 9 }, - "060": { "PaletteId": 9 }, - "061": { "PaletteId": 9 }, - "062": { "PaletteId": 9 }, - "063": { "PaletteId": 9 }, - "064": { "PaletteId": 9 }, - "065": { "PaletteId": 9 }, - "066": { "PaletteId": 9 }, - "067": { "PaletteId": 9 }, - "068": { "PaletteId": 9 }, - "069": { "PaletteId": 9 }, - "070": { "PaletteId": 9 }, - "071": { "PaletteId": 9 }, - "072": { "PaletteId": 9 }, - "073": { "PaletteId": 9 }, - "074": { "PaletteId": 9 }, - "075": { "PaletteId": 9 }, - "076": { "PaletteId": 9 }, - "077": { "PaletteId": 9 }, - "078": { "PaletteId": 9 }, - "079": { "PaletteId": 9 }, - "080": { "PaletteId": 9 }, - "081": { "PaletteId": 9 }, - "082": { "PaletteId": 9 }, - "083": { "PaletteId": 9 }, - "084": { "PaletteId": 9 }, - "085": { "PaletteId": 9 }, - "086": { "PaletteId": 9 }, - "087": { "PaletteId": 9 }, - "088": { "PaletteId": 9 }, - "089": { "PaletteId": 9 }, - "090": { "PaletteId": 9 }, - "091": { "PaletteId": 9 }, - "092": { "PaletteId": 9 }, - "093": { "PaletteId": 26 }, - "094": { "PaletteId": 9 }, - "095": { "PaletteId": 9 }, - "096": { "PaletteId": 26 }, - "097": { "PaletteId": 26 }, - "098": { "PaletteId": 26 } + "npclarge.1": { "Palette": "pal.6" }, + "npclarge.2": { "Palette": "pal.6" }, + "npclarge.3": { "Palette": "pal.6" }, + "npclarge.4": { "Palette": "pal.6" }, + "npclarge.5": { "Palette": "pal.6" }, + "npclarge.6": { "Palette": "pal.6" }, + "npclarge.7": { "Palette": "pal.6" }, + "npclarge.8": { "Palette": "pal.6" }, + "npclarge.9": { "Palette": "pal.6" }, + "npclarge.10": { "Palette": "pal.6" }, + "npclarge.11": { "Palette": "pal.6" }, + "npclarge.12": { "Palette": "pal.6" }, + "npclarge.13": { "Palette": "pal.6" }, + "npclarge.14": { "Palette": "pal.6" }, + "npclarge.15": { "Palette": "pal.6" }, + "npclarge.16": { "Palette": "pal.6" }, + "npclarge.17": { "Palette": "pal.6" }, + "npclarge.18": { "Palette": "pal.6" }, + "npclarge.19": { "Palette": "pal.26" }, + "npclarge.20": { "Palette": "pal.26" }, + "npclarge.21": { "Palette": "pal.26" }, + "npclarge.22": { "Palette": "pal.26" }, + "npclarge.23": { "Palette": "pal.26" }, + "npclarge.24": { "Palette": "pal.26" }, + "npclarge.25": { "Palette": "pal.26" }, + "npclarge.26": { "Palette": "pal.26" }, + "npclarge.27": { "Palette": "pal.6" }, + "npclarge.28": { "Palette": "pal.6" }, + "npclarge.29": { "Palette": "pal.6" }, + "npclarge.30": { "Palette": "pal.6" }, + "npclarge.31": { "Palette": "pal.6" }, + "npclarge.32": { "Palette": "pal.6" }, + "npclarge.33": { "Palette": "pal.6" }, + "npclarge.34": { "Palette": "pal.6" }, + "npclarge.35": { "Palette": "pal.6" }, + "npclarge.36": { "Palette": "pal.6" }, + "npclarge.37": { "Palette": "pal.6" }, + "npclarge.38": { "Palette": "pal.6" }, + "npclarge.39": { "Palette": "pal.9" }, + "npclarge.40": { "Palette": "pal.9" }, + "npclarge.41": { "Palette": "pal.9" }, + "npclarge.42": { "Palette": "pal.9" }, + "npclarge.43": { "Palette": "pal.9" }, + "npclarge.44": { "Palette": "pal.9" }, + "npclarge.45": { "Palette": "pal.9" }, + "npclarge.46": { "Palette": "pal.9" }, + "npclarge.47": { "Palette": "pal.9" }, + "npclarge.48": { "Palette": "pal.9" }, + "npclarge.49": { "Palette": "pal.9" }, + "npclarge.50": { "Palette": "pal.9" }, + "npclarge.51": { "Palette": "pal.9" }, + "npclarge.52": { "Palette": "pal.9" }, + "npclarge.53": { "Palette": "pal.9" }, + "npclarge.54": { "Palette": "pal.9" }, + "npclarge.55": { "Palette": "pal.9" }, + "npclarge.56": { "Palette": "pal.9" }, + "npclarge.57": { "Palette": "pal.9" }, + "npclarge.58": { "Palette": "pal.9" }, + "npclarge.59": { "Palette": "pal.9" }, + "npclarge.60": { "Palette": "pal.9" }, + "npclarge.61": { "Palette": "pal.9" }, + "npclarge.62": { "Palette": "pal.9" }, + "npclarge.63": { "Palette": "pal.9" }, + "npclarge.64": { "Palette": "pal.9" }, + "npclarge.65": { "Palette": "pal.9" }, + "npclarge.66": { "Palette": "pal.9" }, + "npclarge.67": { "Palette": "pal.9" }, + "npclarge.68": { "Palette": "pal.9" }, + "npclarge.69": { "Palette": "pal.9" }, + "npclarge.70": { "Palette": "pal.9" }, + "npclarge.71": { "Palette": "pal.9" }, + "npclarge.72": { "Palette": "pal.9" }, + "npclarge.73": { "Palette": "pal.9" }, + "npclarge.74": { "Palette": "pal.9" }, + "npclarge.75": { "Palette": "pal.9" }, + "npclarge.76": { "Palette": "pal.9" }, + "npclarge.77": { "Palette": "pal.9" }, + "npclarge.78": { "Palette": "pal.9" }, + "npclarge.79": { "Palette": "pal.9" }, + "npclarge.80": { "Palette": "pal.9" }, + "npclarge.81": { "Palette": "pal.9" }, + "npclarge.82": { "Palette": "pal.9" }, + "npclarge.83": { "Palette": "pal.9" }, + "npclarge.84": { "Palette": "pal.9" }, + "npclarge.85": { "Palette": "pal.9" }, + "npclarge.86": { "Palette": "pal.9" }, + "npclarge.87": { "Palette": "pal.9" }, + "npclarge.88": { "Palette": "pal.9" }, + "npclarge.89": { "Palette": "pal.9" }, + "npclarge.90": { "Palette": "pal.9" }, + "npclarge.91": { "Palette": "pal.9" }, + "npclarge.92": { "Palette": "pal.9" }, + "npclarge.93": { "Palette": "pal.9" }, + "npclarge.94": { "Palette": "pal.26" }, + "npclarge.95": { "Palette": "pal.9" }, + "npclarge.96": { "Palette": "pal.9" }, + "npclarge.97": { "Palette": "pal.26" }, + "npclarge.98": { "Palette": "pal.26" }, + "npclarge.99": { "Palette": "pal.26" } } diff --git a/mods/Albion/Meta/NPCGR1.json b/mods/Albion/Meta/NPCGR1.json index 46592b897..19e498528 100644 --- a/mods/Albion/Meta/NPCGR1.json +++ b/mods/Albion/Meta/NPCGR1.json @@ -1,80 +1,80 @@ { - "000": { "Id": "npclarge.100", "PaletteId": 26 }, - "001": { "PaletteId": 26 }, - "002": { "PaletteId": 26 }, - "003": { "PaletteId": 26 }, - "004": { "PaletteId": 9 }, - "005": { "PaletteId": 9 }, - "006": { "PaletteId": 9 }, - "007": { "PaletteId": 9 }, - "008": { "PaletteId": 26 }, - "009": { "PaletteId": 26 }, - "010": { "PaletteId": 26 }, - "011": { "PaletteId": 9 }, - "012": { "PaletteId": 9 }, - "013": { "PaletteId": 9 }, - "014": { "PaletteId": 9 }, - "015": { "PaletteId": 9 }, - "016": { "PaletteId": 9 }, - "017": { "PaletteId": 9 }, - "018": { "PaletteId": 9 }, - "019": { "PaletteId": 6 }, - "020": { "PaletteId": 6 }, - "021": { "PaletteId": 16 }, - "022": { "PaletteId": 16 }, - "023": { "PaletteId": 16 }, - "024": { "PaletteId": 16 }, - "025": { "PaletteId": 16 }, - "026": { "PaletteId": 16 }, - "027": { "PaletteId": 16 }, - "028": { "PaletteId": 16 }, - "029": { "PaletteId": 16 }, - "030": { "PaletteId": 16 }, - "031": { "PaletteId": 16 }, - "032": { "PaletteId": 16 }, - "033": { "PaletteId": 16 }, - "034": { "PaletteId": 16 }, - "035": { "PaletteId": 16 }, - "036": { "PaletteId": 16 }, - "037": { "PaletteId": 16 }, - "038": { "PaletteId": 16 }, - "039": { "PaletteId": 16 }, - "040": { "PaletteId": 16 }, - "041": { "PaletteId": 16 }, - "042": { "PaletteId": 16 }, - "043": { "PaletteId": 16 }, - "044": { "PaletteId": 16 }, - "045": { "PaletteId": 16 }, - "046": { "PaletteId": 16 }, - "047": { "PaletteId": 16 }, - "048": { "PaletteId": 16 }, - "049": { "PaletteId": 16 }, - "050": { "PaletteId": 16 }, - "051": { "PaletteId": 16 }, - "052": { "PaletteId": 16 }, - "053": { "PaletteId": 16 }, - "054": { "PaletteId": 9 }, - "055": { "PaletteId": 16 }, - "056": { "PaletteId": 16 }, - "057": { "PaletteId": 16 }, - "058": { "PaletteId": 16 }, - "059": { "PaletteId": 16 }, - "060": { "PaletteId": 16 }, - "061": { "PaletteId": 16 }, - "062": { "PaletteId": 16 }, - "063": { "PaletteId": 16 }, - "064": { "PaletteId": 16 }, - "065": { "PaletteId": 16 }, - "066": { "PaletteId": 16 }, - "067": { "PaletteId": 26 }, - "068": { "PaletteId": 45 }, - "069": { "PaletteId": 45 }, - "070": { "PaletteId": 45 }, - "071": { "PaletteId": 5 }, - "072": { "PaletteId": 5 }, - "073": { "PaletteId": 5 }, - "074": { "PaletteId": 5 }, - "075": { "PaletteId": 5 }, - "076": { "PaletteId": 6 }, - "077": { "PaletteId": 6 } + "npclarge.100": { "Palette": "pal.26" }, + "npclarge.101": { "Palette": "pal.26" }, + "npclarge.102": { "Palette": "pal.26" }, + "npclarge.103": { "Palette": "pal.26" }, + "npclarge.104": { "Palette": "pal.9" }, + "npclarge.105": { "Palette": "pal.9" }, + "npclarge.106": { "Palette": "pal.9" }, + "npclarge.107": { "Palette": "pal.9" }, + "npclarge.108": { "Palette": "pal.26" }, + "npclarge.109": { "Palette": "pal.26" }, + "npclarge.110": { "Palette": "pal.26" }, + "npclarge.111": { "Palette": "pal.9" }, + "npclarge.112": { "Palette": "pal.9" }, + "npclarge.113": { "Palette": "pal.9" }, + "npclarge.114": { "Palette": "pal.9" }, + "npclarge.115": { "Palette": "pal.9" }, + "npclarge.116": { "Palette": "pal.9" }, + "npclarge.117": { "Palette": "pal.9" }, + "npclarge.118": { "Palette": "pal.9" }, + "npclarge.119": { "Palette": "pal.6" }, + "npclarge.120": { "Palette": "pal.6" }, + "npclarge.121": { "Palette": "pal.16" }, + "npclarge.122": { "Palette": "pal.16" }, + "npclarge.123": { "Palette": "pal.16" }, + "npclarge.124": { "Palette": "pal.16" }, + "npclarge.125": { "Palette": "pal.16" }, + "npclarge.126": { "Palette": "pal.16" }, + "npclarge.127": { "Palette": "pal.16" }, + "npclarge.128": { "Palette": "pal.16" }, + "npclarge.129": { "Palette": "pal.16" }, + "npclarge.130": { "Palette": "pal.16" }, + "npclarge.131": { "Palette": "pal.16" }, + "npclarge.132": { "Palette": "pal.16" }, + "npclarge.133": { "Palette": "pal.16" }, + "npclarge.134": { "Palette": "pal.16" }, + "npclarge.135": { "Palette": "pal.16" }, + "npclarge.136": { "Palette": "pal.16" }, + "npclarge.137": { "Palette": "pal.16" }, + "npclarge.138": { "Palette": "pal.16" }, + "npclarge.139": { "Palette": "pal.16" }, + "npclarge.140": { "Palette": "pal.16" }, + "npclarge.141": { "Palette": "pal.16" }, + "npclarge.142": { "Palette": "pal.16" }, + "npclarge.143": { "Palette": "pal.16" }, + "npclarge.144": { "Palette": "pal.16" }, + "npclarge.145": { "Palette": "pal.16" }, + "npclarge.146": { "Palette": "pal.16" }, + "npclarge.147": { "Palette": "pal.16" }, + "npclarge.148": { "Palette": "pal.16" }, + "npclarge.149": { "Palette": "pal.16" }, + "npclarge.150": { "Palette": "pal.16" }, + "npclarge.151": { "Palette": "pal.16" }, + "npclarge.152": { "Palette": "pal.16" }, + "npclarge.153": { "Palette": "pal.16" }, + "npclarge.154": { "Palette": "pal.9" }, + "npclarge.155": { "Palette": "pal.16" }, + "npclarge.156": { "Palette": "pal.16" }, + "npclarge.157": { "Palette": "pal.16" }, + "npclarge.158": { "Palette": "pal.16" }, + "npclarge.159": { "Palette": "pal.16" }, + "npclarge.160": { "Palette": "pal.16" }, + "npclarge.161": { "Palette": "pal.16" }, + "npclarge.162": { "Palette": "pal.16" }, + "npclarge.163": { "Palette": "pal.16" }, + "npclarge.164": { "Palette": "pal.16" }, + "npclarge.165": { "Palette": "pal.16" }, + "npclarge.166": { "Palette": "pal.16" }, + "npclarge.167": { "Palette": "pal.26" }, + "npclarge.168": { "Palette": "pal.45" }, + "npclarge.169": { "Palette": "pal.45" }, + "npclarge.170": { "Palette": "pal.45" }, + "npclarge.171": { "Palette": "pal.5" }, + "npclarge.172": { "Palette": "pal.5" }, + "npclarge.173": { "Palette": "pal.5" }, + "npclarge.174": { "Palette": "pal.5" }, + "npclarge.175": { "Palette": "pal.5" }, + "npclarge.176": { "Palette": "pal.6" }, + "npclarge.177": { "Palette": "pal.6" } } diff --git a/mods/Albion/Meta/SPELLDAT.json b/mods/Albion/Meta/SPELLDAT.json index 2ed9fc1d4..4ba8342e7 100644 --- a/mods/Albion/Meta/SPELLDAT.json +++ b/mods/Albion/Meta/SPELLDAT.json @@ -1,213 +1,213 @@ { - "0": { "Id": "spell.1", "School": "DjiKas", "SpellNumber": 0, "Name": "stext.203" }, - "1": { "School": "DjiKas", "SpellNumber": 1, "Name": "stext.204" }, - "2": { "School": "DjiKas", "SpellNumber": 2, "Name": "stext.205" }, - "3": { "School": "DjiKas", "SpellNumber": 3, "Name": "stext.206" }, - "4": { "School": "DjiKas", "SpellNumber": 4, "Name": "stext.207" }, - "5": { "School": "DjiKas", "SpellNumber": 5, "Name": "stext.208" }, - "6": { "School": "DjiKas", "SpellNumber": 6, "Name": "stext.209" }, - "7": { "School": "DjiKas", "SpellNumber": 7, "Name": "stext.210" }, - "8": { "School": "DjiKas", "SpellNumber": 8, "Name": "stext.211" }, - "9": { "School": "DjiKas", "SpellNumber": 9, "Name": "stext.212" }, - "10": { "School": "DjiKas", "SpellNumber": 10, "Name": "stext.213" }, - "11": { "School": "DjiKas", "SpellNumber": 11, "Name": "stext.214" }, - "12": { "School": "DjiKas", "SpellNumber": 12, "Name": "stext.215" }, - "13": { "School": "DjiKas", "SpellNumber": 13, "Name": "stext.216" }, - "14": { "School": "DjiKas", "SpellNumber": 14, "Name": "stext.217" }, - "15": { "School": "DjiKas", "SpellNumber": 15, "Name": "stext.218" }, - "16": { "School": "DjiKas", "SpellNumber": 16, "Name": "stext.219" }, - "17": { "School": "DjiKas", "SpellNumber": 17, "Name": "stext.220" }, - "18": { "School": "DjiKas", "SpellNumber": 18, "Name": "stext.221" }, - "19": { "School": "DjiKas", "SpellNumber": 19, "Name": "stext.222" }, - "20": { "School": "DjiKas", "SpellNumber": 20, "Name": "stext.223" }, - "21": { "School": "DjiKas", "SpellNumber": 21, "Name": "stext.224" }, - "22": { "School": "DjiKas", "SpellNumber": 22, "Name": "stext.225" }, - "23": { "School": "DjiKas", "SpellNumber": 23, "Name": "stext.226" }, - "24": { "School": "DjiKas", "SpellNumber": 24, "Name": "stext.227" }, - "25": { "School": "DjiKas", "SpellNumber": 25, "Name": "stext.228" }, - "26": { "School": "DjiKas", "SpellNumber": 26, "Name": "stext.229" }, - "27": { "School": "DjiKas", "SpellNumber": 27, "Name": "stext.230" }, - "28": { "School": "DjiKas", "SpellNumber": 28, "Name": "stext.231" }, - "29": { "School": "DjiKas", "SpellNumber": 29, "Name": "stext.232" }, - "30": { "School": "DjiKantos", "SpellNumber": 0, "Name": "stext.233" }, - "31": { "School": "DjiKantos", "SpellNumber": 1, "Name": "stext.234" }, - "32": { "School": "DjiKantos", "SpellNumber": 2, "Name": "stext.235" }, - "33": { "School": "DjiKantos", "SpellNumber": 3, "Name": "stext.236" }, - "34": { "School": "DjiKantos", "SpellNumber": 4, "Name": "stext.237" }, - "35": { "School": "DjiKantos", "SpellNumber": 5, "Name": "stext.238" }, - "36": { "School": "DjiKantos", "SpellNumber": 6, "Name": "stext.239" }, - "37": { "School": "DjiKantos", "SpellNumber": 7, "Name": "stext.240" }, - "38": { "School": "DjiKantos", "SpellNumber": 8, "Name": "stext.241" }, - "39": { "School": "DjiKantos", "SpellNumber": 9, "Name": "stext.242" }, - "40": { "School": "DjiKantos", "SpellNumber": 10, "Name": "stext.243" }, - "41": { "School": "DjiKantos", "SpellNumber": 11, "Name": "stext.244" }, - "42": { "School": "DjiKantos", "SpellNumber": 12, "Name": "stext.245" }, - "43": { "School": "DjiKantos", "SpellNumber": 13, "Name": "stext.246" }, - "44": { "School": "DjiKantos", "SpellNumber": 14, "Name": "stext.247" }, - "45": { "School": "DjiKantos", "SpellNumber": 15, "Name": "stext.248" }, - "46": { "School": "DjiKantos", "SpellNumber": 16, "Name": "stext.249" }, - "47": { "School": "DjiKantos", "SpellNumber": 17, "Name": "stext.250" }, - "48": { "School": "DjiKantos", "SpellNumber": 18, "Name": "stext.251" }, - "49": { "School": "DjiKantos", "SpellNumber": 19, "Name": "stext.252" }, - "50": { "School": "DjiKantos", "SpellNumber": 20, "Name": "stext.253" }, - "51": { "School": "DjiKantos", "SpellNumber": 21, "Name": "stext.254" }, - "52": { "School": "DjiKantos", "SpellNumber": 22, "Name": "stext.255" }, - "53": { "School": "DjiKantos", "SpellNumber": 23, "Name": "stext.256" }, - "54": { "School": "DjiKantos", "SpellNumber": 24, "Name": "stext.257" }, - "55": { "School": "DjiKantos", "SpellNumber": 25, "Name": "stext.258" }, - "56": { "School": "DjiKantos", "SpellNumber": 26, "Name": "stext.259" }, - "57": { "School": "DjiKantos", "SpellNumber": 27, "Name": "stext.260" }, - "58": { "School": "DjiKantos", "SpellNumber": 28, "Name": "stext.261" }, - "59": { "School": "DjiKantos", "SpellNumber": 29, "Name": "stext.262" }, - "60": { "School": "Druid", "SpellNumber": 0, "Name": "stext.263" }, - "61": { "School": "Druid", "SpellNumber": 1, "Name": "stext.264" }, - "62": { "School": "Druid", "SpellNumber": 2, "Name": "stext.265" }, - "63": { "School": "Druid", "SpellNumber": 3, "Name": "stext.266" }, - "64": { "School": "Druid", "SpellNumber": 4, "Name": "stext.267" }, - "65": { "School": "Druid", "SpellNumber": 5, "Name": "stext.268" }, - "66": { "School": "Druid", "SpellNumber": 6, "Name": "stext.269" }, - "67": { "School": "Druid", "SpellNumber": 7, "Name": "stext.270" }, - "68": { "School": "Druid", "SpellNumber": 8, "Name": "stext.271" }, - "69": { "School": "Druid", "SpellNumber": 9, "Name": "stext.272" }, - "70": { "School": "Druid", "SpellNumber": 10, "Name": "stext.273" }, - "71": { "School": "Druid", "SpellNumber": 11, "Name": "stext.274" }, - "72": { "School": "Druid", "SpellNumber": 12, "Name": "stext.275" }, - "73": { "School": "Druid", "SpellNumber": 13, "Name": "stext.276" }, - "74": { "School": "Druid", "SpellNumber": 14, "Name": "stext.277" }, - "75": { "School": "Druid", "SpellNumber": 15, "Name": "stext.278" }, - "76": { "School": "Druid", "SpellNumber": 16, "Name": "stext.279" }, - "77": { "School": "Druid", "SpellNumber": 17, "Name": "stext.280" }, - "78": { "School": "Druid", "SpellNumber": 18, "Name": "stext.281" }, - "79": { "School": "Druid", "SpellNumber": 19, "Name": "stext.282" }, - "80": { "School": "Druid", "SpellNumber": 20, "Name": "stext.283" }, - "81": { "School": "Druid", "SpellNumber": 21, "Name": "stext.284" }, - "82": { "School": "Druid", "SpellNumber": 22, "Name": "stext.285" }, - "83": { "School": "Druid", "SpellNumber": 23, "Name": "stext.286" }, - "84": { "School": "Druid", "SpellNumber": 24, "Name": "stext.287" }, - "85": { "School": "Druid", "SpellNumber": 25, "Name": "stext.288" }, - "86": { "School": "Druid", "SpellNumber": 26, "Name": "stext.289" }, - "87": { "School": "Druid", "SpellNumber": 27, "Name": "stext.290" }, - "88": { "School": "Druid", "SpellNumber": 28, "Name": "stext.291" }, - "89": { "School": "Druid", "SpellNumber": 29, "Name": "stext.292" }, - "90": { "School": "OquloKamulos", "SpellNumber": 0, "Name": "stext.293" }, - "91": { "School": "OquloKamulos", "SpellNumber": 1, "Name": "stext.294" }, - "92": { "School": "OquloKamulos", "SpellNumber": 2, "Name": "stext.295" }, - "93": { "School": "OquloKamulos", "SpellNumber": 3, "Name": "stext.296" }, - "94": { "School": "OquloKamulos", "SpellNumber": 4, "Name": "stext.297" }, - "95": { "School": "OquloKamulos", "SpellNumber": 5, "Name": "stext.298" }, - "96": { "School": "OquloKamulos", "SpellNumber": 6, "Name": "stext.299" }, - "97": { "School": "OquloKamulos", "SpellNumber": 7, "Name": "stext.300" }, - "98": { "School": "OquloKamulos", "SpellNumber": 8, "Name": "stext.301" }, - "99": { "School": "OquloKamulos", "SpellNumber": 9, "Name": "stext.302" }, - "100": { "School": "OquloKamulos", "SpellNumber": 10, "Name": "stext.303" }, - "101": { "School": "OquloKamulos", "SpellNumber": 11, "Name": "stext.304" }, - "102": { "School": "OquloKamulos", "SpellNumber": 12, "Name": "stext.305" }, - "103": { "School": "OquloKamulos", "SpellNumber": 13, "Name": "stext.306" }, - "104": { "School": "OquloKamulos", "SpellNumber": 14, "Name": "stext.307" }, - "105": { "School": "OquloKamulos", "SpellNumber": 15, "Name": "stext.308" }, - "106": { "School": "OquloKamulos", "SpellNumber": 16, "Name": "stext.309" }, - "107": { "School": "OquloKamulos", "SpellNumber": 17, "Name": "stext.310" }, - "108": { "School": "OquloKamulos", "SpellNumber": 18, "Name": "stext.311" }, - "109": { "School": "OquloKamulos", "SpellNumber": 19, "Name": "stext.312" }, - "110": { "School": "OquloKamulos", "SpellNumber": 20, "Name": "stext.313" }, - "111": { "School": "OquloKamulos", "SpellNumber": 21, "Name": "stext.314" }, - "112": { "School": "OquloKamulos", "SpellNumber": 22, "Name": "stext.315" }, - "113": { "School": "OquloKamulos", "SpellNumber": 23, "Name": "stext.316" }, - "114": { "School": "OquloKamulos", "SpellNumber": 24, "Name": "stext.317" }, - "115": { "School": "OquloKamulos", "SpellNumber": 25, "Name": "stext.318" }, - "116": { "School": "OquloKamulos", "SpellNumber": 26, "Name": "stext.319" }, - "117": { "School": "OquloKamulos", "SpellNumber": 27, "Name": "stext.320" }, - "118": { "School": "OquloKamulos", "SpellNumber": 28, "Name": "stext.321" }, - "119": { "School": "OquloKamulos", "SpellNumber": 29, "Name": "stext.322" }, - "120": { "School": "Unk4", "SpellNumber": 0, "Name": "stext.323" }, - "121": { "School": "Unk4", "SpellNumber": 1, "Name": "stext.324" }, - "122": { "School": "Unk4", "SpellNumber": 2, "Name": "stext.325" }, - "123": { "School": "Unk4", "SpellNumber": 3, "Name": "stext.326" }, - "124": { "School": "Unk4", "SpellNumber": 4, "Name": "stext.327" }, - "125": { "School": "Unk4", "SpellNumber": 5, "Name": "stext.328" }, - "126": { "School": "Unk4", "SpellNumber": 6, "Name": "stext.329" }, - "127": { "School": "Unk4", "SpellNumber": 7, "Name": "stext.330" }, - "128": { "School": "Unk4", "SpellNumber": 8, "Name": "stext.331" }, - "129": { "School": "Unk4", "SpellNumber": 9, "Name": "stext.332" }, - "130": { "School": "Unk4", "SpellNumber": 10, "Name": "stext.333" }, - "131": { "School": "Unk4", "SpellNumber": 11, "Name": "stext.334" }, - "132": { "School": "Unk4", "SpellNumber": 12, "Name": "stext.335" }, - "133": { "School": "Unk4", "SpellNumber": 13, "Name": "stext.336" }, - "134": { "School": "Unk4", "SpellNumber": 14, "Name": "stext.337" }, - "135": { "School": "Unk4", "SpellNumber": 15, "Name": "stext.338" }, - "136": { "School": "Unk4", "SpellNumber": 16, "Name": "stext.339" }, - "137": { "School": "Unk4", "SpellNumber": 17, "Name": "stext.340" }, - "138": { "School": "Unk4", "SpellNumber": 18, "Name": "stext.341" }, - "139": { "School": "Unk4", "SpellNumber": 19, "Name": "stext.342" }, - "140": { "School": "Unk4", "SpellNumber": 20, "Name": "stext.343" }, - "141": { "School": "Unk4", "SpellNumber": 21, "Name": "stext.344" }, - "142": { "School": "Unk4", "SpellNumber": 22, "Name": "stext.345" }, - "143": { "School": "Unk4", "SpellNumber": 23, "Name": "stext.346" }, - "144": { "School": "Unk4", "SpellNumber": 24, "Name": "stext.347" }, - "145": { "School": "Unk4", "SpellNumber": 25, "Name": "stext.348" }, - "146": { "School": "Unk4", "SpellNumber": 26, "Name": "stext.349" }, - "147": { "School": "Unk4", "SpellNumber": 27, "Name": "stext.350" }, - "148": { "School": "Unk4", "SpellNumber": 28, "Name": "stext.351" }, - "149": { "School": "Unk4", "SpellNumber": 29, "Name": "stext.352" }, - "150": { "School": "ZombieMagic", "SpellNumber": 0, "Name": "stext.353" }, - "151": { "School": "ZombieMagic", "SpellNumber": 1, "Name": "stext.354" }, - "152": { "School": "ZombieMagic", "SpellNumber": 2, "Name": "stext.355" }, - "153": { "School": "ZombieMagic", "SpellNumber": 3, "Name": "stext.356" }, - "154": { "School": "ZombieMagic", "SpellNumber": 4, "Name": "stext.357" }, - "155": { "School": "ZombieMagic", "SpellNumber": 5, "Name": "stext.358" }, - "156": { "School": "ZombieMagic", "SpellNumber": 6, "Name": "stext.359" }, - "157": { "School": "ZombieMagic", "SpellNumber": 7, "Name": "stext.360" }, - "158": { "School": "ZombieMagic", "SpellNumber": 8, "Name": "stext.361" }, - "159": { "School": "ZombieMagic", "SpellNumber": 9, "Name": "stext.362" }, - "160": { "School": "ZombieMagic", "SpellNumber": 10, "Name": "stext.363" }, - "161": { "School": "ZombieMagic", "SpellNumber": 11, "Name": "stext.364" }, - "162": { "School": "ZombieMagic", "SpellNumber": 12, "Name": "stext.365" }, - "163": { "School": "ZombieMagic", "SpellNumber": 13, "Name": "stext.366" }, - "164": { "School": "ZombieMagic", "SpellNumber": 14, "Name": "stext.367" }, - "165": { "School": "ZombieMagic", "SpellNumber": 15, "Name": "stext.368" }, - "166": { "School": "ZombieMagic", "SpellNumber": 16, "Name": "stext.369" }, - "167": { "School": "ZombieMagic", "SpellNumber": 17, "Name": "stext.370" }, - "168": { "School": "ZombieMagic", "SpellNumber": 18, "Name": "stext.371" }, - "169": { "School": "ZombieMagic", "SpellNumber": 19, "Name": "stext.372" }, - "170": { "School": "ZombieMagic", "SpellNumber": 20, "Name": "stext.373" }, - "171": { "School": "ZombieMagic", "SpellNumber": 21, "Name": "stext.374" }, - "172": { "School": "ZombieMagic", "SpellNumber": 22, "Name": "stext.375" }, - "173": { "School": "ZombieMagic", "SpellNumber": 23, "Name": "stext.376" }, - "174": { "School": "ZombieMagic", "SpellNumber": 24, "Name": "stext.377" }, - "175": { "School": "ZombieMagic", "SpellNumber": 25, "Name": "stext.378" }, - "176": { "School": "ZombieMagic", "SpellNumber": 26, "Name": "stext.379" }, - "177": { "School": "ZombieMagic", "SpellNumber": 27, "Name": "stext.380" }, - "178": { "School": "ZombieMagic", "SpellNumber": 28, "Name": "stext.381" }, - "179": { "School": "ZombieMagic", "SpellNumber": 29, "Name": "stext.382" }, - "180": { "School": "Unk6", "SpellNumber": 0, "Name": "stext.383" }, - "181": { "School": "Unk6", "SpellNumber": 1, "Name": "stext.384" }, - "182": { "School": "Unk6", "SpellNumber": 2, "Name": "stext.385" }, - "183": { "School": "Unk6", "SpellNumber": 3, "Name": "stext.386" }, - "184": { "School": "Unk6", "SpellNumber": 4, "Name": "stext.387" }, - "185": { "School": "Unk6", "SpellNumber": 5, "Name": "stext.388" }, - "186": { "School": "Unk6", "SpellNumber": 6, "Name": "stext.389" }, - "187": { "School": "Unk6", "SpellNumber": 7, "Name": "stext.390" }, - "188": { "School": "Unk6", "SpellNumber": 8, "Name": "stext.391" }, - "189": { "School": "Unk6", "SpellNumber": 9, "Name": "stext.392" }, - "190": { "School": "Unk6", "SpellNumber": 10, "Name": "stext.393" }, - "191": { "School": "Unk6", "SpellNumber": 11, "Name": "stext.394" }, - "192": { "School": "Unk6", "SpellNumber": 12, "Name": "stext.395" }, - "193": { "School": "Unk6", "SpellNumber": 13, "Name": "stext.396" }, - "194": { "School": "Unk6", "SpellNumber": 14, "Name": "stext.397" }, - "195": { "School": "Unk6", "SpellNumber": 15, "Name": "stext.398" }, - "196": { "School": "Unk6", "SpellNumber": 16, "Name": "stext.399" }, - "197": { "School": "Unk6", "SpellNumber": 17, "Name": "stext.400" }, - "198": { "School": "Unk6", "SpellNumber": 18, "Name": "stext.401" }, - "199": { "School": "Unk6", "SpellNumber": 19, "Name": "stext.402" }, - "200": { "School": "Unk6", "SpellNumber": 20, "Name": "stext.403" }, - "201": { "School": "Unk6", "SpellNumber": 21, "Name": "stext.404" }, - "202": { "School": "Unk6", "SpellNumber": 22, "Name": "stext.405" }, - "203": { "School": "Unk6", "SpellNumber": 23, "Name": "stext.406" }, - "204": { "School": "Unk6", "SpellNumber": 24, "Name": "stext.407" }, - "205": { "School": "Unk6", "SpellNumber": 25, "Name": "stext.408" }, - "206": { "School": "Unk6", "SpellNumber": 26, "Name": "stext.409" }, - "207": { "School": "Unk6", "SpellNumber": 27, "Name": "stext.410" }, - "208": { "School": "Unk6", "SpellNumber": 28, "Name": "stext.411" }, - "209": { "School": "Unk6", "SpellNumber": 29, "Name": "stext.412" } + "spell.1": { "School": "DjiKas", "SpellNumber": 0, "Name": "stext.203" }, + "spell.2": { "School": "DjiKas", "SpellNumber": 1, "Name": "stext.204" }, + "spell.3": { "School": "DjiKas", "SpellNumber": 2, "Name": "stext.205" }, + "spell.4": { "School": "DjiKas", "SpellNumber": 3, "Name": "stext.206" }, + "spell.5": { "School": "DjiKas", "SpellNumber": 4, "Name": "stext.207" }, + "spell.6": { "School": "DjiKas", "SpellNumber": 5, "Name": "stext.208" }, + "spell.7": { "School": "DjiKas", "SpellNumber": 6, "Name": "stext.209" }, + "spell.8": { "School": "DjiKas", "SpellNumber": 7, "Name": "stext.210" }, + "spell.9": { "School": "DjiKas", "SpellNumber": 8, "Name": "stext.211" }, + "spell.10": { "School": "DjiKas", "SpellNumber": 9, "Name": "stext.212" }, + "spell.11": { "School": "DjiKas", "SpellNumber": 10, "Name": "stext.213" }, + "spell.12": { "School": "DjiKas", "SpellNumber": 11, "Name": "stext.214" }, + "spell.13": { "School": "DjiKas", "SpellNumber": 12, "Name": "stext.215" }, + "spell.14": { "School": "DjiKas", "SpellNumber": 13, "Name": "stext.216" }, + "spell.15": { "School": "DjiKas", "SpellNumber": 14, "Name": "stext.217" }, + "spell.16": { "School": "DjiKas", "SpellNumber": 15, "Name": "stext.218" }, + "spell.17": { "School": "DjiKas", "SpellNumber": 16, "Name": "stext.219" }, + "spell.18": { "School": "DjiKas", "SpellNumber": 17, "Name": "stext.220" }, + "spell.19": { "School": "DjiKas", "SpellNumber": 18, "Name": "stext.221" }, + "spell.20": { "School": "DjiKas", "SpellNumber": 19, "Name": "stext.222" }, + "spell.21": { "School": "DjiKas", "SpellNumber": 20, "Name": "stext.223" }, + "spell.22": { "School": "DjiKas", "SpellNumber": 21, "Name": "stext.224" }, + "spell.23": { "School": "DjiKas", "SpellNumber": 22, "Name": "stext.225" }, + "spell.24": { "School": "DjiKas", "SpellNumber": 23, "Name": "stext.226" }, + "spell.25": { "School": "DjiKas", "SpellNumber": 24, "Name": "stext.227" }, + "spell.26": { "School": "DjiKas", "SpellNumber": 25, "Name": "stext.228" }, + "spell.27": { "School": "DjiKas", "SpellNumber": 26, "Name": "stext.229" }, + "spell.28": { "School": "DjiKas", "SpellNumber": 27, "Name": "stext.230" }, + "spell.29": { "School": "DjiKas", "SpellNumber": 28, "Name": "stext.231" }, + "spell.30": { "School": "DjiKas", "SpellNumber": 29, "Name": "stext.232" }, + "spell.31": { "School": "DjiKantos", "SpellNumber": 0, "Name": "stext.233" }, + "spell.32": { "School": "DjiKantos", "SpellNumber": 1, "Name": "stext.234" }, + "spell.33": { "School": "DjiKantos", "SpellNumber": 2, "Name": "stext.235" }, + "spell.34": { "School": "DjiKantos", "SpellNumber": 3, "Name": "stext.236" }, + "spell.35": { "School": "DjiKantos", "SpellNumber": 4, "Name": "stext.237" }, + "spell.36": { "School": "DjiKantos", "SpellNumber": 5, "Name": "stext.238" }, + "spell.37": { "School": "DjiKantos", "SpellNumber": 6, "Name": "stext.239" }, + "spell.38": { "School": "DjiKantos", "SpellNumber": 7, "Name": "stext.240" }, + "spell.39": { "School": "DjiKantos", "SpellNumber": 8, "Name": "stext.241" }, + "spell.40": { "School": "DjiKantos", "SpellNumber": 9, "Name": "stext.242" }, + "spell.41": { "School": "DjiKantos", "SpellNumber": 10, "Name": "stext.243" }, + "spell.42": { "School": "DjiKantos", "SpellNumber": 11, "Name": "stext.244" }, + "spell.43": { "School": "DjiKantos", "SpellNumber": 12, "Name": "stext.245" }, + "spell.44": { "School": "DjiKantos", "SpellNumber": 13, "Name": "stext.246" }, + "spell.45": { "School": "DjiKantos", "SpellNumber": 14, "Name": "stext.247" }, + "spell.46": { "School": "DjiKantos", "SpellNumber": 15, "Name": "stext.248" }, + "spell.47": { "School": "DjiKantos", "SpellNumber": 16, "Name": "stext.249" }, + "spell.48": { "School": "DjiKantos", "SpellNumber": 17, "Name": "stext.250" }, + "spell.49": { "School": "DjiKantos", "SpellNumber": 18, "Name": "stext.251" }, + "spell.50": { "School": "DjiKantos", "SpellNumber": 19, "Name": "stext.252" }, + "spell.51": { "School": "DjiKantos", "SpellNumber": 20, "Name": "stext.253" }, + "spell.52": { "School": "DjiKantos", "SpellNumber": 21, "Name": "stext.254" }, + "spell.53": { "School": "DjiKantos", "SpellNumber": 22, "Name": "stext.255" }, + "spell.54": { "School": "DjiKantos", "SpellNumber": 23, "Name": "stext.256" }, + "spell.55": { "School": "DjiKantos", "SpellNumber": 24, "Name": "stext.257" }, + "spell.56": { "School": "DjiKantos", "SpellNumber": 25, "Name": "stext.258" }, + "spell.57": { "School": "DjiKantos", "SpellNumber": 26, "Name": "stext.259" }, + "spell.58": { "School": "DjiKantos", "SpellNumber": 27, "Name": "stext.260" }, + "spell.59": { "School": "DjiKantos", "SpellNumber": 28, "Name": "stext.261" }, + "spell.60": { "School": "DjiKantos", "SpellNumber": 29, "Name": "stext.262" }, + "spell.61": { "School": "Druid", "SpellNumber": 0, "Name": "stext.263" }, + "spell.62": { "School": "Druid", "SpellNumber": 1, "Name": "stext.264" }, + "spell.63": { "School": "Druid", "SpellNumber": 2, "Name": "stext.265" }, + "spell.64": { "School": "Druid", "SpellNumber": 3, "Name": "stext.266" }, + "spell.65": { "School": "Druid", "SpellNumber": 4, "Name": "stext.267" }, + "spell.66": { "School": "Druid", "SpellNumber": 5, "Name": "stext.268" }, + "spell.67": { "School": "Druid", "SpellNumber": 6, "Name": "stext.269" }, + "spell.68": { "School": "Druid", "SpellNumber": 7, "Name": "stext.270" }, + "spell.69": { "School": "Druid", "SpellNumber": 8, "Name": "stext.271" }, + "spell.70": { "School": "Druid", "SpellNumber": 9, "Name": "stext.272" }, + "spell.71": { "School": "Druid", "SpellNumber": 10, "Name": "stext.273" }, + "spell.72": { "School": "Druid", "SpellNumber": 11, "Name": "stext.274" }, + "spell.73": { "School": "Druid", "SpellNumber": 12, "Name": "stext.275" }, + "spell.74": { "School": "Druid", "SpellNumber": 13, "Name": "stext.276" }, + "spell.75": { "School": "Druid", "SpellNumber": 14, "Name": "stext.277" }, + "spell.76": { "School": "Druid", "SpellNumber": 15, "Name": "stext.278" }, + "spell.77": { "School": "Druid", "SpellNumber": 16, "Name": "stext.279" }, + "spell.78": { "School": "Druid", "SpellNumber": 17, "Name": "stext.280" }, + "spell.79": { "School": "Druid", "SpellNumber": 18, "Name": "stext.281" }, + "spell.80": { "School": "Druid", "SpellNumber": 19, "Name": "stext.282" }, + "spell.81": { "School": "Druid", "SpellNumber": 20, "Name": "stext.283" }, + "spell.82": { "School": "Druid", "SpellNumber": 21, "Name": "stext.284" }, + "spell.83": { "School": "Druid", "SpellNumber": 22, "Name": "stext.285" }, + "spell.84": { "School": "Druid", "SpellNumber": 23, "Name": "stext.286" }, + "spell.85": { "School": "Druid", "SpellNumber": 24, "Name": "stext.287" }, + "spell.86": { "School": "Druid", "SpellNumber": 25, "Name": "stext.288" }, + "spell.87": { "School": "Druid", "SpellNumber": 26, "Name": "stext.289" }, + "spell.88": { "School": "Druid", "SpellNumber": 27, "Name": "stext.290" }, + "spell.89": { "School": "Druid", "SpellNumber": 28, "Name": "stext.291" }, + "spell.90": { "School": "Druid", "SpellNumber": 29, "Name": "stext.292" }, + "spell.91": { "School": "OquloKamulos", "SpellNumber": 0, "Name": "stext.293" }, + "spell.92": { "School": "OquloKamulos", "SpellNumber": 1, "Name": "stext.294" }, + "spell.93": { "School": "OquloKamulos", "SpellNumber": 2, "Name": "stext.295" }, + "spell.94": { "School": "OquloKamulos", "SpellNumber": 3, "Name": "stext.296" }, + "spell.95": { "School": "OquloKamulos", "SpellNumber": 4, "Name": "stext.297" }, + "spell.96": { "School": "OquloKamulos", "SpellNumber": 5, "Name": "stext.298" }, + "spell.97": { "School": "OquloKamulos", "SpellNumber": 6, "Name": "stext.299" }, + "spell.98": { "School": "OquloKamulos", "SpellNumber": 7, "Name": "stext.300" }, + "spell.99": { "School": "OquloKamulos", "SpellNumber": 8, "Name": "stext.301" }, + "spell.100": { "School": "OquloKamulos", "SpellNumber": 9, "Name": "stext.302" }, + "spell.101": { "School": "OquloKamulos", "SpellNumber": 10, "Name": "stext.303" }, + "spell.102": { "School": "OquloKamulos", "SpellNumber": 11, "Name": "stext.304" }, + "spell.103": { "School": "OquloKamulos", "SpellNumber": 12, "Name": "stext.305" }, + "spell.104": { "School": "OquloKamulos", "SpellNumber": 13, "Name": "stext.306" }, + "spell.105": { "School": "OquloKamulos", "SpellNumber": 14, "Name": "stext.307" }, + "spell.106": { "School": "OquloKamulos", "SpellNumber": 15, "Name": "stext.308" }, + "spell.107": { "School": "OquloKamulos", "SpellNumber": 16, "Name": "stext.309" }, + "spell.108": { "School": "OquloKamulos", "SpellNumber": 17, "Name": "stext.310" }, + "spell.109": { "School": "OquloKamulos", "SpellNumber": 18, "Name": "stext.311" }, + "spell.110": { "School": "OquloKamulos", "SpellNumber": 19, "Name": "stext.312" }, + "spell.111": { "School": "OquloKamulos", "SpellNumber": 20, "Name": "stext.313" }, + "spell.112": { "School": "OquloKamulos", "SpellNumber": 21, "Name": "stext.314" }, + "spell.113": { "School": "OquloKamulos", "SpellNumber": 22, "Name": "stext.315" }, + "spell.114": { "School": "OquloKamulos", "SpellNumber": 23, "Name": "stext.316" }, + "spell.115": { "School": "OquloKamulos", "SpellNumber": 24, "Name": "stext.317" }, + "spell.116": { "School": "OquloKamulos", "SpellNumber": 25, "Name": "stext.318" }, + "spell.117": { "School": "OquloKamulos", "SpellNumber": 26, "Name": "stext.319" }, + "spell.118": { "School": "OquloKamulos", "SpellNumber": 27, "Name": "stext.320" }, + "spell.119": { "School": "OquloKamulos", "SpellNumber": 28, "Name": "stext.321" }, + "spell.120": { "School": "OquloKamulos", "SpellNumber": 29, "Name": "stext.322" }, + "spell.121": { "School": "Unk4", "SpellNumber": 0, "Name": "stext.323" }, + "spell.122": { "School": "Unk4", "SpellNumber": 1, "Name": "stext.324" }, + "spell.123": { "School": "Unk4", "SpellNumber": 2, "Name": "stext.325" }, + "spell.124": { "School": "Unk4", "SpellNumber": 3, "Name": "stext.326" }, + "spell.125": { "School": "Unk4", "SpellNumber": 4, "Name": "stext.327" }, + "spell.126": { "School": "Unk4", "SpellNumber": 5, "Name": "stext.328" }, + "spell.127": { "School": "Unk4", "SpellNumber": 6, "Name": "stext.329" }, + "spell.128": { "School": "Unk4", "SpellNumber": 7, "Name": "stext.330" }, + "spell.129": { "School": "Unk4", "SpellNumber": 8, "Name": "stext.331" }, + "spell.130": { "School": "Unk4", "SpellNumber": 9, "Name": "stext.332" }, + "spell.131": { "School": "Unk4", "SpellNumber": 10, "Name": "stext.333" }, + "spell.132": { "School": "Unk4", "SpellNumber": 11, "Name": "stext.334" }, + "spell.133": { "School": "Unk4", "SpellNumber": 12, "Name": "stext.335" }, + "spell.134": { "School": "Unk4", "SpellNumber": 13, "Name": "stext.336" }, + "spell.135": { "School": "Unk4", "SpellNumber": 14, "Name": "stext.337" }, + "spell.136": { "School": "Unk4", "SpellNumber": 15, "Name": "stext.338" }, + "spell.137": { "School": "Unk4", "SpellNumber": 16, "Name": "stext.339" }, + "spell.138": { "School": "Unk4", "SpellNumber": 17, "Name": "stext.340" }, + "spell.139": { "School": "Unk4", "SpellNumber": 18, "Name": "stext.341" }, + "spell.140": { "School": "Unk4", "SpellNumber": 19, "Name": "stext.342" }, + "spell.141": { "School": "Unk4", "SpellNumber": 20, "Name": "stext.343" }, + "spell.142": { "School": "Unk4", "SpellNumber": 21, "Name": "stext.344" }, + "spell.143": { "School": "Unk4", "SpellNumber": 22, "Name": "stext.345" }, + "spell.144": { "School": "Unk4", "SpellNumber": 23, "Name": "stext.346" }, + "spell.145": { "School": "Unk4", "SpellNumber": 24, "Name": "stext.347" }, + "spell.146": { "School": "Unk4", "SpellNumber": 25, "Name": "stext.348" }, + "spell.147": { "School": "Unk4", "SpellNumber": 26, "Name": "stext.349" }, + "spell.148": { "School": "Unk4", "SpellNumber": 27, "Name": "stext.350" }, + "spell.149": { "School": "Unk4", "SpellNumber": 28, "Name": "stext.351" }, + "spell.150": { "School": "Unk4", "SpellNumber": 29, "Name": "stext.352" }, + "spell.151": { "School": "ZombieMagic", "SpellNumber": 0, "Name": "stext.353" }, + "spell.152": { "School": "ZombieMagic", "SpellNumber": 1, "Name": "stext.354" }, + "spell.153": { "School": "ZombieMagic", "SpellNumber": 2, "Name": "stext.355" }, + "spell.154": { "School": "ZombieMagic", "SpellNumber": 3, "Name": "stext.356" }, + "spell.155": { "School": "ZombieMagic", "SpellNumber": 4, "Name": "stext.357" }, + "spell.156": { "School": "ZombieMagic", "SpellNumber": 5, "Name": "stext.358" }, + "spell.157": { "School": "ZombieMagic", "SpellNumber": 6, "Name": "stext.359" }, + "spell.158": { "School": "ZombieMagic", "SpellNumber": 7, "Name": "stext.360" }, + "spell.159": { "School": "ZombieMagic", "SpellNumber": 8, "Name": "stext.361" }, + "spell.160": { "School": "ZombieMagic", "SpellNumber": 9, "Name": "stext.362" }, + "spell.161": { "School": "ZombieMagic", "SpellNumber": 10, "Name": "stext.363" }, + "spell.162": { "School": "ZombieMagic", "SpellNumber": 11, "Name": "stext.364" }, + "spell.163": { "School": "ZombieMagic", "SpellNumber": 12, "Name": "stext.365" }, + "spell.164": { "School": "ZombieMagic", "SpellNumber": 13, "Name": "stext.366" }, + "spell.165": { "School": "ZombieMagic", "SpellNumber": 14, "Name": "stext.367" }, + "spell.166": { "School": "ZombieMagic", "SpellNumber": 15, "Name": "stext.368" }, + "spell.167": { "School": "ZombieMagic", "SpellNumber": 16, "Name": "stext.369" }, + "spell.168": { "School": "ZombieMagic", "SpellNumber": 17, "Name": "stext.370" }, + "spell.169": { "School": "ZombieMagic", "SpellNumber": 18, "Name": "stext.371" }, + "spell.170": { "School": "ZombieMagic", "SpellNumber": 19, "Name": "stext.372" }, + "spell.171": { "School": "ZombieMagic", "SpellNumber": 20, "Name": "stext.373" }, + "spell.172": { "School": "ZombieMagic", "SpellNumber": 21, "Name": "stext.374" }, + "spell.173": { "School": "ZombieMagic", "SpellNumber": 22, "Name": "stext.375" }, + "spell.174": { "School": "ZombieMagic", "SpellNumber": 23, "Name": "stext.376" }, + "spell.175": { "School": "ZombieMagic", "SpellNumber": 24, "Name": "stext.377" }, + "spell.176": { "School": "ZombieMagic", "SpellNumber": 25, "Name": "stext.378" }, + "spell.177": { "School": "ZombieMagic", "SpellNumber": 26, "Name": "stext.379" }, + "spell.178": { "School": "ZombieMagic", "SpellNumber": 27, "Name": "stext.380" }, + "spell.179": { "School": "ZombieMagic", "SpellNumber": 28, "Name": "stext.381" }, + "spell.180": { "School": "ZombieMagic", "SpellNumber": 29, "Name": "stext.382" }, + "spell.181": { "School": "Unk6", "SpellNumber": 0, "Name": "stext.383" }, + "spell.182": { "School": "Unk6", "SpellNumber": 1, "Name": "stext.384" }, + "spell.183": { "School": "Unk6", "SpellNumber": 2, "Name": "stext.385" }, + "spell.184": { "School": "Unk6", "SpellNumber": 3, "Name": "stext.386" }, + "spell.185": { "School": "Unk6", "SpellNumber": 4, "Name": "stext.387" }, + "spell.186": { "School": "Unk6", "SpellNumber": 5, "Name": "stext.388" }, + "spell.187": { "School": "Unk6", "SpellNumber": 6, "Name": "stext.389" }, + "spell.188": { "School": "Unk6", "SpellNumber": 7, "Name": "stext.390" }, + "spell.189": { "School": "Unk6", "SpellNumber": 8, "Name": "stext.391" }, + "spell.190": { "School": "Unk6", "SpellNumber": 9, "Name": "stext.392" }, + "spell.191": { "School": "Unk6", "SpellNumber": 10, "Name": "stext.393" }, + "spell.192": { "School": "Unk6", "SpellNumber": 11, "Name": "stext.394" }, + "spell.193": { "School": "Unk6", "SpellNumber": 12, "Name": "stext.395" }, + "spell.194": { "School": "Unk6", "SpellNumber": 13, "Name": "stext.396" }, + "spell.195": { "School": "Unk6", "SpellNumber": 14, "Name": "stext.397" }, + "spell.196": { "School": "Unk6", "SpellNumber": 15, "Name": "stext.398" }, + "spell.197": { "School": "Unk6", "SpellNumber": 16, "Name": "stext.399" }, + "spell.198": { "School": "Unk6", "SpellNumber": 17, "Name": "stext.400" }, + "spell.199": { "School": "Unk6", "SpellNumber": 18, "Name": "stext.401" }, + "spell.200": { "School": "Unk6", "SpellNumber": 19, "Name": "stext.402" }, + "spell.201": { "School": "Unk6", "SpellNumber": 20, "Name": "stext.403" }, + "spell.202": { "School": "Unk6", "SpellNumber": 21, "Name": "stext.404" }, + "spell.203": { "School": "Unk6", "SpellNumber": 22, "Name": "stext.405" }, + "spell.204": { "School": "Unk6", "SpellNumber": 23, "Name": "stext.406" }, + "spell.205": { "School": "Unk6", "SpellNumber": 24, "Name": "stext.407" }, + "spell.206": { "School": "Unk6", "SpellNumber": 25, "Name": "stext.408" }, + "spell.207": { "School": "Unk6", "SpellNumber": 26, "Name": "stext.409" }, + "spell.208": { "School": "Unk6", "SpellNumber": 27, "Name": "stext.410" }, + "spell.209": { "School": "Unk6", "SpellNumber": 28, "Name": "stext.411" }, + "spell.210": { "School": "Unk6", "SpellNumber": 29, "Name": "stext.412" } } diff --git a/mods/Albion/alb_assets.json b/mods/Albion/alb_assets.json index db0b7caab..b6117eb0d 100644 --- a/mods/Albion/alb_assets.json +++ b/mods/Albion/alb_assets.json @@ -1,348 +1,433 @@ { - "StringMappings": { - "itemname.*": "special.ItemNames", - "stext.*": "special.SystemStrings", - "utext.*": "special.UAlbionStrings", - "word.0-499": "special.Words1:0", - "word.500-999": "special.Words2:500", - "word.1000-1499": "special.Words3:1000" + "special.SoundBank": { + "Files": { + "Albion/DRIVERS/ALBISND.OPL": { + "Container": "raw", + "Loader": "soundbank", + "IsReadOnly": true + } + } }, - "Files": { - // Misc - "Albion/DRIVERS/ALBISND.OPL": { - "Container": "raw", - "Loader": "soundbank", - "Max": 0, - "Map": { "0": { "Id": "special.SoundBank" } } + "special.ItemNamesSingleLang": { + "Files": { + "!GERMAN": { "Language": "GERMAN" }, + "!ENGLISH": { "Language": "ENGLISH" }, + "!FRENCH": { "Language": "FRENCH" } }, + "Loader": "itemnameMeta", + "Target": "special.ItemNamesMultiLang", + "IsReadOnly": true + }, - // Dummy item for some dodgy edge cases - "dummy": { "Loader": "dummy", "Container": "dummy", "Max": 0, "Map": { "0": {"Id": "special.DummyObject" } } }, + "special.ItemNamesMultiLang": { + "Files": { + "Albion/CD/XLDLIBS/ITEMNAME.DAT": { + "Loader": "itemname", + "Container": "raw" + } + } + }, - // Items - "Albion/CD/XLDLIBS/ITEMLIST.DAT": { "Loader": "itemdata", "Container": "items", "Map": { "0": { "Id": "item.1" } } }, - "Albion/CD/XLDLIBS/ITEMNAME.DAT": { "Loader": "itemname", "Container": "raw", "Max": 0, "Map": { "0": { "Id": "special.ItemNames" } } }, + "special.SystemStrings": { + "Files": { + "Albion/CD/XLDLIBS/GERMAN/SYSTEXTS": { "Language": "GERMAN" }, + "Albion/CD/XLDLIBS/ENGLISH/SYSTEXTS": { "Language": "ENGLISH" }, + "Albion/CD/XLDLIBS/FRENCH/SYSTEXTS": { "Language": "FRENCH" } + }, + "Loader": "stext", + "Container": "raw", + "IsReadOnly": true + }, - // Events - "Albion/CD/XLDLIBS/EVNTSET0.XLD": { "Loader": "eset", "Max": 98, "Map": { "0": { "Id": "eset.1" } } }, - "Albion/CD/XLDLIBS/EVNTSET1.XLD": { "Loader": "eset", "Map": { "0": { "Id": "eset.100" } } }, - "Albion/CD/XLDLIBS/EVNTSET2.XLD": { "Loader": "eset", "Map": { "0": { "Id": "eset.200" } } }, - "Albion/CD/XLDLIBS/EVNTSET3.XLD": { "Loader": "eset", "Map": { "1": { "Id": "eset.301" } } }, - "Albion/CD/XLDLIBS/EVNTSET9.XLD": { "Loader": "eset", "Max": 98, "Map": { "81": { "Id": "eset.981" } } }, - "Albion/CD/XLDLIBS/SCRIPT0.XLD": { "Loader": "script", "Map": { "1": { "Id": "script.2" } } }, - "Albion/CD/XLDLIBS/SCRIPT2.XLD": { "Loader": "script", "MinimumCount": 100, "Map": { "0": { "Id": "script.200" } } }, + "special.Words1-Words3": { + "Files": { + "Albion/CD/XLDLIBS/GERMAN/WORDLIS0.XLD": { "Language": "GERMAN" }, + "Albion/CD/XLDLIBS/ENGLISH/WORDLIS0.XLD": { "Language": "ENGLISH" }, + "Albion/CD/XLDLIBS/FRENCH/WORDLIS0.XLD": { "Language": "FRENCH" } + }, + "Loader": "wordlist" + }, - // Maps - "Albion/CD/XLDLIBS/BLKLIST0.XLD": { "Loader": "block", "Map": { "0": {"Id": "block.1" } } }, - "Albion/CD/XLDLIBS/LABDATA0.XLD": { "Loader": "lab", "MapFile": "Meta/LABDATA0.json" }, - "Albion/CD/XLDLIBS/LABDATA1.XLD": { "Loader": "lab", "MinimumCount": 100, "MapFile": "Meta/LABDATA1.json" }, - "Albion/CD/XLDLIBS/LABDATA2.XLD": { "Loader": "lab", "MinimumCount": 100, "MapFile": "Meta/LABDATA2.json" }, - "Albion/CD/XLDLIBS/MAPDATA1.XLD": { "Loader": "map", "Map": { "0": { "Id": "map.100" } } }, - "Albion/CD/XLDLIBS/MAPDATA2.XLD": { "Loader": "map", "Map": { "0": { "Id": "map.200" } } }, - "Albion/CD/XLDLIBS/MAPDATA3.XLD": { "Loader": "map", "Map": { "0": { "Id": "map.300" } } }, - "Albion/CD/XLDLIBS/ICONDAT0.XLD": { - "Loader": "tileset", - "Map": { - "0": { "Id": "tiledata.1", "UseSmallGraphics": true }, - "1": { "UseSmallGraphics": true }, - "3": { "UseSmallGraphics": true } - } + "itemname.1-462": { + "Files": { // Dummy files used for ensuring the language is available on the AssetLoadContext + "!GERMAN": { "Language": "GERMAN" }, + "!ENGLISH": { "Language": "ENGLISH" }, + "!FRENCH": { "Language": "FRENCH" } }, + "Loader": "stringset", + "FirstId": "itemname.1", + "Target": "special.ItemNamesSingleLang", + "IsReadOnly": true + }, - // Monster info - "Albion/CD/XLDLIBS/MONCHAR0.XLD": { "Loader": "sheet", "Map": { "0": { "Id": "monster.1" } } }, - "Albion/CD/XLDLIBS/MONGRP0.XLD": { "Loader": "mongrp", "Max": 98, "Map": { "1": { "Id": "mongrp.2" } } }, - "Albion/CD/XLDLIBS/MONGRP1.XLD": { "Loader": "mongrp", "MinimumCount": 100, "Map": { "0": { "Id": "mongrp.100" } } }, - "Albion/CD/XLDLIBS/MONGRP2.XLD": { "Loader": "mongrp", "MinimumCount": 100, "Map": { "0": { "Id": "mongrp.200" } } }, + "stext.0-777": { + "Files": { // Dummy files used for ensuring the language is available on the AssetLoadContext + "!GERMAN": { "Language": "GERMAN" }, + "!ENGLISH": { "Language": "ENGLISH" }, + "!FRENCH": { "Language": "FRENCH" } + }, + "Loader": "stringset", + "FirstId": "stext.0", + "Target": "special.SystemStrings", + "IsReadOnly": true + }, - // Palettes - "Albion/CD/XLDLIBS/PALETTE.000": { - "Loader": "pal", - "Container": "raw", - "Max": 0, - "Map": { "0": { "Id": "pal.0", "IsCommon": true } } + "word.0-499": { + "Files": { // Dummy files used for ensuring the language is available on the AssetLoadContext + "!GERMAN": { "Language": "GERMAN" }, + "!ENGLISH": { "Language": "ENGLISH" }, + "!FRENCH": { "Language": "FRENCH" } }, - "Albion/CD/XLDLIBS/PALETTE0.XLD": { - "Loader": "pal", - "Map": { - "0": { "Id": "pal.1", "NightPaletteId": "pal.47", "AnimatedRanges": "0x99-0x9f, 0xb0-0xb4, 0xb5-0xbf" }, - "1": { "NightPaletteId": "pal.47", "AnimatedRanges": "0x99-0x9f, 0xb0-0xb4, 0xb5-0xbf" }, - "2": { "NightPaletteId": "pal.55", "AnimatedRanges": "0x40-0x43, 0x44-0x4f" }, - "3": { "NightPaletteId": "pal.48" }, - "5": { "AnimatedRanges": "0xb0-0xb4, 0xb5-0xbf" }, - "13": { "NightPaletteId": "pal.49", "AnimatedRanges": "0xb0-0xb3, 0xb4-0xbf" }, - "14": { "AnimatedRanges": "0x58-0x5f" }, - "24": { "NightPaletteId": "pal.49", "AnimatedRanges": "0xb0-0xb3, 0xb4-0xbf" }, - "25": { "AnimatedRanges": "0xb4-0xb7, 0xb8-0xbb, 0xbc-0xbf" }, - "30": { "AnimatedRanges": "0x10-0x4f" }, - "46": { "AnimatedRanges": "0x99-0x9f, 0xb0-0xb4, 0xb5-0xbf" }, - "48": { "AnimatedRanges": "0xb0-0xb3, 0xb4-0xbf" }, - "50": { "NightPaletteId": "pal.49", "AnimatedRanges": "0xb0-0xb3, 0xb4-0xbf" }, - "54": { "AnimatedRanges": "0x40-0x43, 0x44-0x4f" } - } + "Loader": "stringset", + "FirstId": "word.0", + "Target": "special.Words1", + "IsReadOnly": true + }, + + "word.500-999": { + "Files": { // Dummy files used for ensuring the language is available on the AssetLoadContext + "!GERMAN": { "Language": "GERMAN" }, + "!ENGLISH": { "Language": "ENGLISH" }, + "!FRENCH": { "Language": "FRENCH" } }, + "Loader": "stringset", + "FirstId": "word.0", + "Target": "special.Words2", + "IsReadOnly": true + }, + + "word.1000-1499": { + "Files": { // Dummy files used for ensuring the language is available on the AssetLoadContext + "!GERMAN": { "Language": "GERMAN" }, + "!ENGLISH": { "Language": "ENGLISH" }, + "!FRENCH": { "Language": "FRENCH" } + }, + "Loader": "stringset", + "FirstId": "word.0", + "Target": "special.Words3", + "IsReadOnly": true + }, + + // Items + "item.1-462": { "Files": { "Albion/CD/XLDLIBS/ITEMLIST.DAT": { "Loader": "itemdata", "Container": "items" } } }, - // Fonts - "Albion/CD/XLDLIBS/FONTS0.XLD#33906F62": { // EN+FR - "Optional": true, - "Loader": "fixedsize", - "Width": 8, - "Height": 8, - "Max": 1, - "Map": { - "0": { "Id": "fontgfx.Regular" }, - "1": { "Id": "fontgfx.Bold" } + // Events + "eset.1-32": { "Files": { "Albion/CD/XLDLIBS/EVNTSET0.XLD": { "Loader": "eset" } } }, + "eset.33-99": { "Files": { "Albion/CD/XLDLIBS/EVNTSET0.XLD": { "Loader": "eset" } }, "Optional": true }, // not in original game + "eset.100-199": { "Files": { "Albion/CD/XLDLIBS/EVNTSET1.XLD": { "Loader": "eset" } } }, + "eset.200-299": { "Files": { "Albion/CD/XLDLIBS/EVNTSET2.XLD": { "Loader": "eset" } } }, + "eset.300-324": { "Files": { "Albion/CD/XLDLIBS/EVNTSET3.XLD": { "Loader": "eset" } } }, + "eset.900-999": { "Files": { "Albion/CD/XLDLIBS/EVNTSET9.XLD": { "Loader": "eset" } } }, + "script.1-62": { "Files": { "Albion/CD/XLDLIBS/SCRIPT0.XLD": { "Loader": "script" } } }, + "script.200-211": { "Files": { "Albion/CD/XLDLIBS/SCRIPT2.XLD": { "Loader": "script" } } }, + + // Maps + "block.1-11": { "Files": { "Albion/CD/XLDLIBS/BLKLIST0.XLD": { "Loader": "block" } } }, + "lab.1-9": { "Files": { "Albion/CD/XLDLIBS/LABDATA0.XLD": { "Loader": "lab", "MapFile": "Meta/LABDATA0.json" } } }, + "lab.10-99": { "Files": { "Albion/CD/XLDLIBS/LABDATA0.XLD": { "Loader": "lab", "MapFile": "Meta/LABDATA0.json" } }, "Optional": true }, // not in original game + "lab.100-199": { "Files": { "Albion/CD/XLDLIBS/LABDATA1.XLD": { "Loader": "lab", "MapFile": "Meta/LABDATA1.json" } } }, + "lab.200-210": { "Files": { "Albion/CD/XLDLIBS/LABDATA2.XLD": { "Loader": "lab", "MapFile": "Meta/LABDATA2.json" } } }, + "map.100-199": { "Files": { "Albion/CD/XLDLIBS/MAPDATA1.XLD": { "Loader": "map" } } }, + "map.200-299": { "Files": { "Albion/CD/XLDLIBS/MAPDATA2.XLD": { "Loader": "map" } } }, + "map.300-399": { "Files": { "Albion/CD/XLDLIBS/MAPDATA3.XLD": { "Loader": "map" } } }, + + "tiledata.1-11": { + "Files": { + "Albion/CD/XLDLIBS/ICONDAT0.XLD": { + "Loader": "tileset", + "Map": { + "tiledata.1": { "UseSmallGraphics": true }, + "tiledata.2": { "UseSmallGraphics": true }, + "tiledata.4": { "UseSmallGraphics": true } + } } - }, - "Albion/CD/XLDLIBS/FONTS0.XLD#A70E9601": { // DE - "Optional": true, - "Loader": "fixedsize", - "Width": 8, - "Height": 8, - "Max": 1, - "Map": { - "0": { "Id": "fontgfx.GermanRegular" }, - "1": { "Id": "fontgfx.GermanBold" } + } + }, + + // Monster info + "monster.1-59": { "Files": { "Albion/CD/XLDLIBS/MONCHAR0.XLD": { "Loader": "sheet" } } }, + "mongrp.1-99": { "Files": { "Albion/CD/XLDLIBS/MONGRP0.XLD": { "Loader": "mongrp" } } }, + "mongrp.100-199": { "Files": { "Albion/CD/XLDLIBS/MONGRP1.XLD": { "Loader": "mongrp" } } }, + "mongrp.200-251": { "Files": { "Albion/CD/XLDLIBS/MONGRP2.XLD": { "Loader": "mongrp" } } }, + + // Palettes + "pal.0": { + "Files": { + "Albion/CD/XLDLIBS/PALETTE.000": { + "IsCommon": true, + "Container": "raw", + "Loader": "pal" } - }, + } + }, - // Audio - "Albion/CD/XLDLIBS/SAMPLES0.XLD": { "Loader": "sample", "Max": 98, "Map": { "0": { "Id": "sample.1" } } }, // TODO: Add mapping for NPC sound id -> sample id - "Albion/CD/XLDLIBS/SAMPLES1.XLD": { "Loader": "sample", "MinimumCount": 100, "Map": { "0": { "Id": "sample.100" } } }, - "Albion/CD/XLDLIBS/SAMPLES2.XLD": { "Loader": "sample", "MinimumCount": 100, "Map": { "1": { "Id": "sample.201" } } }, - "Albion/CD/XLDLIBS/SONGS0.XLD": { "Loader": "song", "Map": { "0": { "Id": "song.1" } } }, // TODO: Add mapping for song -> ambient song id - "Albion/CD/XLDLIBS/WAVELIB0.XLD": { "Loader": "wavlib", "Map": { "4": { "Id": "wavlib.5" } } }, + "pal.1-56": { + "Files": { + "Albion/CD/XLDLIBS/PALETTE0.XLD": { + "Loader": "pal", + "Map": { + "pal.1": { "NightPalette": "pal.47", "AnimatedRanges": "0x99-0x9f, 0xb0-0xb4, 0xb5-0xbf" }, + "pal.2": { "NightPalette": "pal.47", "AnimatedRanges": "0x99-0x9f, 0xb0-0xb4, 0xb5-0xbf" }, + "pal.3": { "NightPalette": "pal.55", "AnimatedRanges": "0x40-0x43, 0x44-0x4f" }, + "pal.4": { "NightPalette": "pal.48" }, + "pal.6": { "AnimatedRanges": "0xb0-0xb4, 0xb5-0xbf" }, + "pal.14": { "NightPalette": "pal.49", "AnimatedRanges": "0xb0-0xb3, 0xb4-0xbf" }, + "pal.15": { "AnimatedRanges": "0x58-0x5f" }, + "pal.25": { "NightPalette": "pal.49", "AnimatedRanges": "0xb0-0xb3, 0xb4-0xbf" }, + "pal.26": { "AnimatedRanges": "0xb4-0xb7, 0xb8-0xbb, 0xbc-0xbf" }, + "pal.31": { "AnimatedRanges": "0x10-0x4f" }, + "pal.47": { "AnimatedRanges": "0x99-0x9f, 0xb0-0xb4, 0xb5-0xbf" }, + "pal.49": { "AnimatedRanges": "0xb0-0xb3, 0xb4-0xbf" }, + "pal.51": { "NightPalette": "pal.49", "AnimatedRanges": "0xb0-0xb3, 0xb4-0xbf" }, + "pal.55": { "AnimatedRanges": "0x40-0x43, 0x44-0x4f" } + } + } + } + }, - // Videos - "Albion/CD/XLDLIBS/GERMAN/FLICS0.XLD": { "Language": "GERMAN", "Loader": "flic", "Map": { "0": { "Id": "vid.1" } } }, - "Albion/CD/XLDLIBS/ENGLISH/FLICS0.XLD": { "Language": "ENGLISH", "Loader": "flic", "Map": { "0": { "Id": "vid.1" } } }, - "Albion/CD/XLDLIBS/FRENCH/FLICS0.XLD": { "Language": "FRENCH", "Loader": "flic", "Map": { "0": { "Id": "vid.1" } } }, + // Fonts + "fontgfx.1-2": { // English/French fonts, regular & bold + "Files": { + "Albion/CD/XLDLIBS/FONTS0.XLD#33906F62": { // EN+FR + "Loader": "fixedsize", + "Optional": true, + "Width": 8, + "Height": 8 + } + } + }, + "fontgfx.4-5": { // German fonts + "Files": { + "Albion/CD/XLDLIBS/FONTS0.XLD#A70E9601": { // DE + "Loader": "fixedsize", + "Optional": true, + "Width": 8, + "Height": 8 + } + } + }, - // Text - "Albion/CD/XLDLIBS/GERMAN/EVNTTXT0.XLD": { "Language": "GERMAN", "Loader": "stringtable", "Max": 98, "Map": { "0": { "Id": "etext.1" } } }, - "Albion/CD/XLDLIBS/GERMAN/EVNTTXT1.XLD": { "Language": "GERMAN", "Loader": "stringtable", "MinimumCount": 100, "Map": { "0": { "Id": "etext.100" } } }, - "Albion/CD/XLDLIBS/GERMAN/EVNTTXT2.XLD": { "Language": "GERMAN", "Loader": "stringtable", "MinimumCount": 100, "Map": { "0": { "Id": "etext.200" } } }, - "Albion/CD/XLDLIBS/GERMAN/EVNTTXT3.XLD": { "Language": "GERMAN", "Loader": "stringtable", "MinimumCount": 100, "Map": { "0": { "Id": "etext.300" } } }, - "Albion/CD/XLDLIBS/GERMAN/EVNTTXT9.XLD": { "Language": "GERMAN", "Loader": "stringtable", "MinimumCount": 100, "Map": { "0": { "Id": "etext.900" } } }, - "Albion/CD/XLDLIBS/GERMAN/MAPTEXT1.XLD": { "Language": "GERMAN", "Loader": "stringtable", "MinimumCount": 100, "Map": { "0": { "Id": "mtext.100" } } }, - "Albion/CD/XLDLIBS/GERMAN/MAPTEXT2.XLD": { "Language": "GERMAN", "Loader": "stringtable", "MinimumCount": 100, "Map": { "0": { "Id": "mtext.200" } } }, - "Albion/CD/XLDLIBS/GERMAN/MAPTEXT3.XLD": { "Language": "GERMAN", "Loader": "stringtable", "MinimumCount": 100, "Map": { "0": { "Id": "mtext.300" } } }, + // Audio + "sample.1-99": { "Files": { "Albion/CD/XLDLIBS/SAMPLES0.XLD": { "Loader": "sample" } } }, // TODO: Add mapping for NPC sound id -> sample id + "sample.100-199": { "Files": { "Albion/CD/XLDLIBS/SAMPLES1.XLD": { "Loader": "sample" } } }, + "sample.200-299": { "Files": { "Albion/CD/XLDLIBS/SAMPLES2.XLD": { "Loader": "sample" } } }, + "song.1-99": { "Files": { "Albion/CD/XLDLIBS/SONGS0.XLD": { "Loader": "song" } } }, // TODO: Add mapping for song -> ambient song id + "wavlib.1-99": { "Files": { "Albion/CD/XLDLIBS/WAVELIB0.XLD": { "Loader": "wavlib" } } }, - "Albion/CD/XLDLIBS/ENGLISH/EVNTTXT0.XLD": { "Language": "ENGLISH", "Loader": "stringtable", "Max": 98, "Map": { "0": { "Id": "etext.1" } } }, - "Albion/CD/XLDLIBS/ENGLISH/EVNTTXT1.XLD": { "Language": "ENGLISH", "Loader": "stringtable", "MinimumCount": 100, "Map": { "0": { "Id": "etext.100" } } }, - "Albion/CD/XLDLIBS/ENGLISH/EVNTTXT2.XLD": { "Language": "ENGLISH", "Loader": "stringtable", "MinimumCount": 100, "Map": { "0": { "Id": "etext.200" } } }, - "Albion/CD/XLDLIBS/ENGLISH/EVNTTXT3.XLD": { "Language": "ENGLISH", "Loader": "stringtable", "MinimumCount": 100, "Map": { "0": { "Id": "etext.300" } } }, - "Albion/CD/XLDLIBS/ENGLISH/EVNTTXT9.XLD": { "Language": "ENGLISH", "Loader": "stringtable", "MinimumCount": 100, "Map": { "0": { "Id": "etext.900" } } }, - "Albion/CD/XLDLIBS/ENGLISH/MAPTEXT1.XLD": { "Language": "ENGLISH", "Loader": "stringtable", "MinimumCount": 100, "Map": { "0": { "Id": "mtext.100" } } }, - "Albion/CD/XLDLIBS/ENGLISH/MAPTEXT2.XLD": { "Language": "ENGLISH", "Loader": "stringtable", "MinimumCount": 100, "Map": { "0": { "Id": "mtext.200" } } }, - "Albion/CD/XLDLIBS/ENGLISH/MAPTEXT3.XLD": { "Language": "ENGLISH", "Loader": "stringtable", "MinimumCount": 100, "Map": { "0": { "Id": "mtext.300" } } }, + // Videos + "vid.1-99": { + "Loader": "flic", + "IsReadOnly": true, + "Files": { + "Albion/CD/XLDLIBS/GERMAN/FLICS0.XLD": { "Language": "GERMAN" }, + "Albion/CD/XLDLIBS/ENGLISH/FLICS0.XLD": { "Language": "ENGLISH" }, + "Albion/CD/XLDLIBS/FRENCH/FLICS0.XLD": { "Language": "FRENCH" } + } + }, + + // Event Text + "etext.1-32": { + "Loader": "stringtable", + "Files": { + "Albion/CD/XLDLIBS/ENGLISH/EVNTTXT0.XLD": { "Language": "ENGLISH" }, + "Albion/CD/XLDLIBS/FRENCH/EVNTTXT0.XLD": { "Language": "FRENCH" }, + "Albion/CD/XLDLIBS/GERMAN/EVNTTXT0.XLD": { "Language": "GERMAN" } + } + }, + "etext.33-99": { // Not in original game + "Loader": "stringtable", + "Optional": true, + "Files": { + "Albion/CD/XLDLIBS/ENGLISH/EVNTTXT0.XLD": { "Language": "ENGLISH" }, + "Albion/CD/XLDLIBS/FRENCH/EVNTTXT0.XLD": { "Language": "FRENCH" }, + "Albion/CD/XLDLIBS/GERMAN/EVNTTXT0.XLD": { "Language": "GERMAN" } + } + }, - "Albion/CD/XLDLIBS/FRENCH/EVNTTXT0.XLD": { "Language": "FRENCH", "Loader": "stringtable", "Max": 98, "Map": { "0": { "Id": "etext.1" } } }, - "Albion/CD/XLDLIBS/FRENCH/EVNTTXT1.XLD": { "Language": "FRENCH", "Loader": "stringtable", "MinimumCount": 100, "Map": { "0": { "Id": "etext.100" } } }, - "Albion/CD/XLDLIBS/FRENCH/EVNTTXT2.XLD": { "Language": "FRENCH", "Loader": "stringtable", "MinimumCount": 100, "Map": { "0": { "Id": "etext.200" } } }, - "Albion/CD/XLDLIBS/FRENCH/EVNTTXT3.XLD": { "Language": "FRENCH", "Loader": "stringtable", "MinimumCount": 100, "Map": { "0": { "Id": "etext.300" } } }, - "Albion/CD/XLDLIBS/FRENCH/EVNTTXT9.XLD": { "Language": "FRENCH", "Loader": "stringtable", "MinimumCount": 100, "Map": { "0": { "Id": "etext.900" } } }, - "Albion/CD/XLDLIBS/FRENCH/MAPTEXT1.XLD": { "Language": "FRENCH", "Loader": "stringtable", "MinimumCount": 100, "Map": { "0": { "Id": "mtext.100" } } }, - "Albion/CD/XLDLIBS/FRENCH/MAPTEXT2.XLD": { "Language": "FRENCH", "Loader": "stringtable", "MinimumCount": 100, "Map": { "0": { "Id": "mtext.200" } } }, - "Albion/CD/XLDLIBS/FRENCH/MAPTEXT3.XLD": { "Language": "FRENCH", "Loader": "stringtable", "MinimumCount": 100, "Map": { "0": { "Id": "mtext.300" } } }, + "etext.100-199": { + "Loader": "stringtable", + "Files": { + "Albion/CD/XLDLIBS/ENGLISH/EVNTTXT1.XLD": { "Language": "ENGLISH" }, + "Albion/CD/XLDLIBS/FRENCH/EVNTTXT1.XLD": { "Language": "FRENCH" }, + "Albion/CD/XLDLIBS/GERMAN/EVNTTXT1.XLD": { "Language": "GERMAN" } + } + }, + "etext.200-299": { + "Loader": "stringtable", + "Files": { + "Albion/CD/XLDLIBS/ENGLISH/EVNTTXT2.XLD": { "Language": "ENGLISH" }, + "Albion/CD/XLDLIBS/FRENCH/EVNTTXT2.XLD": { "Language": "FRENCH" }, + "Albion/CD/XLDLIBS/GERMAN/EVNTTXT2.XLD": { "Language": "GERMAN" } + } + }, + "etext.300-399": { + "Loader": "stringtable", + "Files": { + "Albion/CD/XLDLIBS/ENGLISH/EVNTTXT3.XLD": { "Language": "ENGLISH" }, + "Albion/CD/XLDLIBS/FRENCH/EVNTTXT3.XLD": { "Language": "FRENCH" }, + "Albion/CD/XLDLIBS/GERMAN/EVNTTXT3.XLD": { "Language": "GERMAN" } + } + }, + "etext.900-999": { + "Loader": "stringtable", + "Files": { + "Albion/CD/XLDLIBS/ENGLISH/EVNTTXT9.XLD": { "Language": "ENGLISH" }, + "Albion/CD/XLDLIBS/FRENCH/EVNTTXT9.XLD": { "Language": "FRENCH" }, + "Albion/CD/XLDLIBS/GERMAN/EVNTTXT9.XLD": { "Language": "GERMAN" } + } + }, - "GERMAN/strings.json": { - "Language": "GERMAN", - "Loader": "json", - "Container": "raw", - "Max": 0, - "Map": { "0": { "Id": "special.UAlbionStrings" } } + // Map Text + "mtext.100-199": { + "Loader": "stringtable", + "Files": { + "Albion/CD/XLDLIBS/ENGLISH/MAPTEXT1.XLD": { "Language": "ENGLISH" }, + "Albion/CD/XLDLIBS/FRENCH/MAPTEXT1.XLD": { "Language": "FRENCH" }, + "Albion/CD/XLDLIBS/GERMAN/MAPTEXT1.XLD": { "Language": "GERMAN" } }, - "ENGLISH/strings.json": { - "Language": "ENGLISH", - "Loader": "json", - "Container": "raw", - "Max": 0, - "Map": { "0": { "Id": "special.UAlbionStrings" } } + "Optional": true // test maps etc don't have any text so don't be as strict + }, + "mtext.200-299": { + "Loader": "stringtable", + "Files": { + "Albion/CD/XLDLIBS/ENGLISH/MAPTEXT2.XLD": { "Language": "ENGLISH" }, + "Albion/CD/XLDLIBS/FRENCH/MAPTEXT2.XLD": { "Language": "FRENCH" }, + "Albion/CD/XLDLIBS/GERMAN/MAPTEXT2.XLD": { "Language": "GERMAN" } }, - "FRENCH/strings.json": { - "Language": "FRENCH", - "Loader": "json", - "Container": "raw", - "Max": 0, - "Map": { "0": { "Id": "special.UAlbionStrings" } } + "Optional": true + }, + "mtext.300-399": { + "Loader": "stringtable", + "Files": { + "Albion/CD/XLDLIBS/ENGLISH/MAPTEXT3.XLD": { "Language": "ENGLISH" }, + "Albion/CD/XLDLIBS/FRENCH/MAPTEXT3.XLD": { "Language": "FRENCH" }, + "Albion/CD/XLDLIBS/GERMAN/MAPTEXT3.XLD": { "Language": "GERMAN" } }, + "Optional": true + }, - "Albion/CD/XLDLIBS/GERMAN/SYSTEXTS": { - "Language": "GERMAN", - "Loader": "stext", - "Container": "raw", - "Max": 0, - "Map": { "0": { "Id": "special.SystemStrings" } } - }, - "Albion/CD/XLDLIBS/ENGLISH/SYSTEXTS": { - "Language": "ENGLISH", - "Loader": "stext", - "Container": "raw", - "Max": 0, - "Map": { "0": { "Id": "special.SystemStrings" } } - }, - "Albion/CD/XLDLIBS/FRENCH/SYSTEXTS": { - "Language": "FRENCH", - "Loader": "stext", - "Container": "raw", - "Max": 0, - "Map": { "0": { "Id": "special.SystemStrings" } } - }, + // Spells + "spell.1-210": { "Files": { "Albion/CD/XLDLIBS/SPELLDAT.DAT": { "Loader": "spell", "Container": "spells", "MapFile": "Meta/SPELLDAT.json" } } }, - "Albion/CD/XLDLIBS/GERMAN/WORDLIS0.XLD": { - "Language": "GERMAN", - "Loader": "wordlist", - "Max": 2, - "Map": { - "0": { "Id": "special.Words1" }, - "1": { "Id": "special.Words2" }, - "2": { "Id": "special.Words3" } - } - }, - "Albion/CD/XLDLIBS/ENGLISH/WORDLIS0.XLD": { - "Language": "ENGLISH", - "Loader": "wordlist", - "Max": 2, - "Map": { - "0": { "Id": "special.Words1" }, - "1": { "Id": "special.Words2" }, - "2": { "Id": "special.Words3" } - } - }, - "Albion/CD/XLDLIBS/FRENCH/WORDLIS0.XLD": { - "Language": "FRENCH", - "Loader": "wordlist", - "Max": 2, - "Map": { - "0": { "Id": "special.Words1" }, - "1": { "Id": "special.Words2" }, - "2": { "Id": "special.Words3" } - } - }, + // Initial game state data + "automap.100-199": { "Files": { "Albion/CD/XLDLIBS/INITIAL/AUTOMAP1.XLD": { "Loader": "automap" } }, "Optional": true }, // Marked as optional as 2D maps won't have automaps and we don't want to spam errors when unpacking + "automap.200-299": { "Files": { "Albion/CD/XLDLIBS/INITIAL/AUTOMAP2.XLD": { "Loader": "automap" } }, "Optional": true }, + "automap.300-399": { "Files": { "Albion/CD/XLDLIBS/INITIAL/AUTOMAP3.XLD": { "Loader": "automap" } }, "Optional": true }, + "chest.1-2": { "Files": { "Albion/CD/XLDLIBS/INITIAL/CHESTDT0.XLD": { "Loader": "chest" } } }, + "chest.3-99": { "Files": { "Albion/CD/XLDLIBS/INITIAL/CHESTDT0.XLD": { "Loader": "chest" } }, "Optional": true }, // Not in original game but defined here to allow extension + "chest.100-199": { "Files": { "Albion/CD/XLDLIBS/INITIAL/CHESTDT1.XLD": { "Loader": "chest" } } }, + "chest.200-299": { "Files": { "Albion/CD/XLDLIBS/INITIAL/CHESTDT2.XLD": { "Loader": "chest" } } }, + "chest.500-574": { "Files": { "Albion/CD/XLDLIBS/INITIAL/CHESTDT5.XLD": { "Loader": "chest" } } }, + "merchant.1": { "Files": { "Albion/CD/XLDLIBS/INITIAL/MERCHDT0.XLD": { "Loader": "merchant" } } }, + "merchant.100-109": { "Files": { "Albion/CD/XLDLIBS/INITIAL/MERCHDT1.XLD": { "Loader": "merchant" } } }, + "merchant.200-210": { "Files": { "Albion/CD/XLDLIBS/INITIAL/MERCHDT2.XLD": { "Loader": "merchant" } } }, + "npc.1-99": { "Files": { "Albion/CD/XLDLIBS/INITIAL/NPCCHAR0.XLD": { "Loader": "sheet" } } }, + "npc.100-199": { "Files": { "Albion/CD/XLDLIBS/INITIAL/NPCCHAR1.XLD": { "Loader": "sheet" } } }, + "npc.200-299": { "Files": { "Albion/CD/XLDLIBS/INITIAL/NPCCHAR2.XLD": { "Loader": "sheet" } } }, + "psheet.1-59": { "Files": { "Albion/CD/XLDLIBS/INITIAL/PRTCHAR0.XLD": { "Loader": "sheet" } } }, + "psheet.100-199": { "Files": { "Albion/CD/XLDLIBS/INITIAL/PRTCHAR1.XLD": { "Loader": "sheet" } } }, + "psheet.200-299": { "Files": { "Albion/CD/XLDLIBS/INITIAL/PRTCHAR2.XLD": { "Loader": "sheet" } } }, - // Spells - "Albion/CD/XLDLIBS/SPELLDAT.DAT": { "Loader": "spell", "Container": "spells", "MapFile": "Meta/SPELLDAT.json" }, + // Graphics + "floor.1-99": { "Files": { "Albion/CD/XLDLIBS/3DFLOOR0.XLD": { "Loader": "fixedsize", "Width": 64, "Height": 64, "MapFile": "Meta/3DFLOOR0.json" } } }, + "floor.100-199": { "Files": { "Albion/CD/XLDLIBS/3DFLOOR1.XLD": { "Loader": "fixedsize", "Width": 64, "Height": 64, "MapFile": "Meta/3DFLOOR1.json" } } }, + "floor.200-299": { "Files": { "Albion/CD/XLDLIBS/3DFLOOR2.XLD": { "Loader": "fixedsize", "Width": 64, "Height": 64, "MapFile": "Meta/3DFLOOR2.json" } } }, + "combg.1-19": { "Files": { "Albion/CD/XLDLIBS/COMBACK0.XLD": { "Loader": "fixedsize", "Width": 360, "MapFile": "Meta/COMBACK0.json" } } }, + "comgfx.1-85": { "Files": { "Albion/CD/XLDLIBS/COMGFX0.XLD": { "Loader": "multiheader", "Palette": "pal.23" } } }, + "pinvgfx.1-10": { "Files": { "Albion/CD/XLDLIBS/FBODPIX0.XLD": { "Loader": "header", "Palette": "pal.19" } } }, + "mongfx.1-60": { "Files": { "Albion/CD/XLDLIBS/MONGFX0.XLD": { "Loader": "multiheader", "Palette": "pal.24", "MapFile": "Meta/MONGFX0.json" } } }, + "npclarge.1-99": { "Files": { "Albion/CD/XLDLIBS/NPCGR0.XLD": { "Loader": "header", "MapFile": "Meta/NPCGR0.json" } } }, + "npclarge.100-199": { "Files": { "Albion/CD/XLDLIBS/NPCGR1.XLD": { "Loader": "header", "MapFile": "Meta/NPCGR1.json" } } }, + "npcsmall.1-29": { "Files": { "Albion/CD/XLDLIBS/NPCKL0.XLD": { "Loader": "header", "Palette": "pal.1", "Map": { "npcsmall.28": { "Palette": "pal.4" } } } } }, + "plarge.1-10": { "Files": { "Albion/CD/XLDLIBS/PARTGR0.XLD": { "Loader": "header", "Palette": "pal.20" } } }, + "psmall.1-10": { "Files": { "Albion/CD/XLDLIBS/PARTKL0.XLD": { "Loader": "header", "Palette": "pal.1" } } }, + "portrait.1-99": { "Files": { "Albion/CD/XLDLIBS/SMLPORT0.XLD": { "Loader": "fixedsize", "Width": 34 } }, "Palette": "pal.0" }, + "portrait.100-175": { "Files": { "Albion/CD/XLDLIBS/SMLPORT1.XLD": { "Loader": "fixedsize", "Width": 34 } } }, + "tacgfx.1-72": { "Files": { "Albion/CD/XLDLIBS/TACTICO0.XLD": { "Loader": "fixedsize", "Width": 32 } }, "Palette": "pal.0" }, + "pic.1-45": { "Files": { "Albion/CD/XLDLIBS/PICTURE0.XLD": { "Loader": "interlaced", "IsReadOnly": true } } }, + "3dobj.1-99": { "Files": { "Albion/CD/XLDLIBS/3DOBJEC0.XLD": { "Loader": "fixedsize", "MapFile": "Meta/3DOBJEC0.json" } } }, + "3dobj.100-199": { "Files": { "Albion/CD/XLDLIBS/3DOBJEC1.XLD": { "Loader": "fixedsize", "MapFile": "Meta/3DOBJEC1.json" } } }, + "3dobj.200-299": { "Files": { "Albion/CD/XLDLIBS/3DOBJEC2.XLD": { "Loader": "fixedsize", "MapFile": "Meta/3DOBJEC2.json" } } }, + "3dobj.300-399": { "Files": { "Albion/CD/XLDLIBS/3DOBJEC3.XLD": { "Loader": "fixedsize", "MapFile": "Meta/3DOBJEC3.json" } } }, + "overlay.1-99": { "Files": { "Albion/CD/XLDLIBS/3DOVERL0.XLD": { "Loader": "fixedsize", "Transposed": true, "MapFile": "Meta/3DOVERL0.json" } } }, + "overlay.100-199": { "Files": { "Albion/CD/XLDLIBS/3DOVERL1.XLD": { "Loader": "fixedsize", "Transposed": true, "MapFile": "Meta/3DOVERL1.json" } } }, + "overlay.200-299": { "Files": { "Albion/CD/XLDLIBS/3DOVERL2.XLD": { "Loader": "fixedsize", "Transposed": true, "MapFile": "Meta/3DOVERL2.json" } } }, + "wall.1-99": { "Files": { "Albion/CD/XLDLIBS/3DWALLS0.XLD": { "Loader": "fixedsize", "Transposed": true, "MapFile": "Meta/3DWALLS0.json" } } }, + "wall.100-199": { "Files": { "Albion/CD/XLDLIBS/3DWALLS1.XLD": { "Loader": "fixedsize", "Transposed": true, "MapFile": "Meta/3DWALLS1.json" } } }, - // Initial game state data - "Albion/CD/XLDLIBS/INITIAL/AUTOMAP1.XLD": { "Loader": "automap", "MinimumCount": 100, "Map": { "0": { "Id": "automap.100" } } }, - "Albion/CD/XLDLIBS/INITIAL/AUTOMAP2.XLD": { "Loader": "automap", "MinimumCount": 100, "Map": { "0": { "Id": "automap.200" } } }, - "Albion/CD/XLDLIBS/INITIAL/AUTOMAP3.XLD": { "Loader": "automap", "MinimumCount": 100, "Map": { "0": { "Id": "automap.300" } } }, - "Albion/CD/XLDLIBS/INITIAL/CHESTDT0.XLD": { "Loader": "chest", "Max": 98, "Map": { "0": { "Id": "chest.1" } } }, - "Albion/CD/XLDLIBS/INITIAL/CHESTDT1.XLD": { "Loader": "chest", "MinimumCount": 100, "Map": { "0": { "Id": "chest.100" } } }, - "Albion/CD/XLDLIBS/INITIAL/CHESTDT2.XLD": { "Loader": "chest", "MinimumCount": 100, "Map": { "0": { "Id": "chest.200" } } }, - "Albion/CD/XLDLIBS/INITIAL/CHESTDT5.XLD": { "Loader": "chest", "MinimumCount": 100, "Map": { "0": { "Id": "chest.500" } } }, - "Albion/CD/XLDLIBS/INITIAL/MERCHDT0.XLD": { "Loader": "merchant", "Max": 98, "Map": { "0": { "Id": "merchant.1" } } }, - "Albion/CD/XLDLIBS/INITIAL/MERCHDT1.XLD": { "Loader": "merchant", "MinimumCount": 100, "Map": { "0": { "Id": "merchant.100" } } }, - "Albion/CD/XLDLIBS/INITIAL/MERCHDT2.XLD": { "Loader": "merchant", "MinimumCount": 100, "Map": { "0": { "Id": "merchant.200" } } }, - "Albion/CD/XLDLIBS/INITIAL/NPCCHAR0.XLD": { "Loader": "sheet", "Max": 98, "Map": { "0": { "Id": "npc.1" } } }, - "Albion/CD/XLDLIBS/INITIAL/NPCCHAR1.XLD": { "Loader": "sheet", "MinimumCount": 100, "Map": { "0": { "Id": "npc.100" } } }, - "Albion/CD/XLDLIBS/INITIAL/NPCCHAR2.XLD": { "Loader": "sheet", "MinimumCount": 100, "Map": { "0": { "Id": "npc.200" } } }, - "Albion/CD/XLDLIBS/INITIAL/PRTCHAR0.XLD": { "Loader": "sheet", "Max": 98, "MinimumCount": 58, "Map": { "0": { "Id": "psheet.1" } } }, - "Albion/CD/XLDLIBS/INITIAL/PRTCHAR1.XLD": { "Loader": "sheet", "Map": { "0": { "Id": "psheet.100" } } }, - "Albion/CD/XLDLIBS/INITIAL/PRTCHAR2.XLD": { "Loader": "sheet", "Map": { "0": { "Id": "psheet.200" } } }, + "3dbg.1-3": { + "Files": { + "Albion/CD/XLDLIBS/3DBCKGR0.XLD": { + "Loader": "header", + "Map": { + "3dbg.1": { "Palette": "pal.3" }, + "3dbg.2": { "Palette": "pal.3" }, + "3dbg.3": { "Palette": "pal.25" } + } + } + } + }, - // Graphics - "Albion/CD/XLDLIBS/3DFLOOR0.XLD": { "Loader": "fixedsize", "Width": 64, "Height": 64, "Max": 98, "MapFile": "Meta/3DFLOOR0.json" }, - "Albion/CD/XLDLIBS/3DFLOOR1.XLD": { "Loader": "fixedsize", "MinimumCount": 100, "Width": 64, "Height": 64, "MapFile": "Meta/3DFLOOR1.json" }, - "Albion/CD/XLDLIBS/3DFLOOR2.XLD": { "Loader": "fixedsize", "MinimumCount": 100, "Width": 64, "Height": 64, "MapFile": "Meta/3DFLOOR2.json" }, - "Albion/CD/XLDLIBS/3DOBJEC0.XLD": { "Loader": "fixedsize", "Max": 98, "MapFile": "Meta/3DOBJEC0.json" }, - "Albion/CD/XLDLIBS/3DOBJEC1.XLD": { "Loader": "fixedsize", "MapFile": "Meta/3DOBJEC1.json" }, - "Albion/CD/XLDLIBS/3DOBJEC2.XLD": { "Loader": "fixedsize", "MapFile": "Meta/3DOBJEC2.json" }, - "Albion/CD/XLDLIBS/3DOBJEC3.XLD": { "Loader": "fixedsize", "MinimumCount": 100, "MapFile": "Meta/3DOBJEC3.json" }, - "Albion/CD/XLDLIBS/3DOVERL0.XLD": { "Transposed": true, "Loader": "fixedsize", "Max": 98, "MapFile": "Meta/3DOVERL0.json" }, - "Albion/CD/XLDLIBS/3DOVERL1.XLD": { "Transposed": true, "Loader": "fixedsize", "MapFile": "Meta/3DOVERL1.json" }, - "Albion/CD/XLDLIBS/3DOVERL2.XLD": { "Transposed": true, "Loader": "fixedsize", "MinimumCount": 100, "MapFile": "Meta/3DOVERL2.json" }, - "Albion/CD/XLDLIBS/3DWALLS0.XLD": { "Transposed": true, "Loader": "fixedsize", "Max": 98, "MapFile": "Meta/3DWALLS0.json" }, - "Albion/CD/XLDLIBS/3DWALLS1.XLD": { "Transposed": true, "Loader": "fixedsize", "MinimumCount": 100, "MapFile": "Meta/3DWALLS1.json" }, - "Albion/CD/XLDLIBS/COMBACK0.XLD": { "Loader": "fixedsize", "Width": 360, "MapFile": "Meta/COMBACK0.json" }, - "Albion/CD/XLDLIBS/COMGFX0.XLD": { "Loader": "multiheader", "PaletteId": 23, "Map": { "0": { "Id": "comgfx.1"} } }, - "Albion/CD/XLDLIBS/FBODPIX0.XLD": { "Loader": "header", "PaletteId": 19, "Map": { "0": { "Id": "pinvgfx.1" } } }, - "Albion/CD/XLDLIBS/MONGFX0.XLD": { "Loader": "multiheader", "PaletteId": 24, "MapFile": "Meta/MONGFX0.json" }, - "Albion/CD/XLDLIBS/NPCGR0.XLD": { "Loader": "header", "Max": 98, "MapFile": "Meta/NPCGR0.json" }, - "Albion/CD/XLDLIBS/NPCGR1.XLD": { "Loader": "header", "MinimumCount": 100, "MapFile": "Meta/NPCGR1.json" }, - "Albion/CD/XLDLIBS/NPCKL0.XLD": { "Loader": "header", "PaletteId": 1, "Map": { "0": { "Id": "npcsmall.1" }, "27": { "PaletteId": 4 } } }, - "Albion/CD/XLDLIBS/PARTGR0.XLD": { "Loader": "header", "PaletteId": 20, "Map": { "0": { "Id": "plarge.1" } } }, - "Albion/CD/XLDLIBS/PARTKL0.XLD": { "Loader": "header", "PaletteId": 1, "Map": { "0": { "Id": "psmall.1" } } }, - "Albion/CD/XLDLIBS/PICTURE0.XLD": { "Loader": "interlaced", "IsReadOnly": true, "Map": { "0": { "Id": "pic.1" } } }, - "Albion/CD/XLDLIBS/SMLPORT0.XLD": { "Loader": "fixedsize", "Width": 34, "Max": 98, "Map": { "0": { "Id": "portrait.1" } } }, - "Albion/CD/XLDLIBS/SMLPORT1.XLD": { "Loader": "fixedsize", "Width": 34, "MinimumCount": 100, "Map": { "0": { "Id": "portrait.100" } } }, - "Albion/CD/XLDLIBS/TACTICO0.XLD": { "Loader": "fixedsize", "Width": 32, "Map": { "0": { "Id": "tacgfx.1" } } }, + "autotile.1-2": { + "Files": { + "Albion/CD/XLDLIBS/AUTOGFX0.XLD": { + "Post": "atlas", + "Loader": "amorphous", + "Map": { + "autotile.1": { "SubSprites": "(8,8,576) (16,16)", "Palette": "pal.11" }, + "autotile.2": { "SubSprites": "(8,8,576) (16,16)", "Palette": "pal.30" } + } + } + } + }, - "Albion/CD/XLDLIBS/3DBCKGR0.XLD": { - "Loader": "header", - "Map": { - "0": { "Id": "3dbg.1", "PaletteId": 3 }, - "1": { "PaletteId": 3 }, - "2": { "PaletteId": 25 } - } - }, - "Albion/CD/XLDLIBS/AUTOGFX0.XLD": { - "Post": "atlas", - "Loader": "amorphous", - "Map": { - "0": { "Id": "autotile.1", "SubSprites": "(8,8,576) (16,16)", "PaletteId": 11 }, - "1": { "SubSprites": "(8,8,576) (16,16)", "PaletteId": 30 } + "tilegfx.1-11": { + "Files": { + "Albion/CD/XLDLIBS/ICONGFX0.XLD": { + "Loader": "tilegfx", + "Width": 16, + "Height": 16, + "Map": { + "tilegfx.1": { "Palette": "pal.1" }, + "tilegfx.2": { "Palette": "pal.2" }, + "tilegfx.3": { "Palette": "pal.6" }, + "tilegfx.4": { "Palette": "pal.4" }, + "tilegfx.5": { "Palette": "pal.5" }, + "tilegfx.6": { "Palette": "pal.16" }, + "tilegfx.7": { "Palette": "pal.9" }, + "tilegfx.8": { "Palette": "pal.26" }, + "tilegfx.9": { "Palette": "pal.28" }, + "tilegfx.10": { "Palette": "pal.45" }, + "tilegfx.11": { "Palette": "pal.56" } + } } - }, - "Albion/CD/XLDLIBS/ICONGFX0.XLD": { - "Loader": "tilegfx", - "Width": 16, - "Height": 16, - "Map": { - "0": { "Id": "tilegfx.1", "PaletteId": 1 }, - "1": { "PaletteId": 2 }, - "2": { "PaletteId": 6 }, - "3": { "PaletteId": 4 }, - "4": { "PaletteId": 5 }, - "5": { "PaletteId": 16 }, - "6": { "PaletteId": 9 }, - "7": { "PaletteId": 26 }, - "8": { "PaletteId": 28 }, - "9": { "PaletteId": 45 }, - "10": { "PaletteId": 56 } + } + }, + + "itemgfx.1": { + "Files": { + "Albion/CD/XLDLIBS/ITEMGFX": { + "Loader": "fixedsize", + "Container": "raw", + "Palette": "pal.0", + "Width": 16, + "Height": 16 } - }, - "Albion/CD/XLDLIBS/ITEMGFX": { - "Loader": "fixedsize", - "Container": "raw", - "Width": 16, - "Height": 16, - "Max": 0, - "Map": { "0": { "Id": "itemgfx.1" } } - }, - "Albion/CD/XLDLIBS/SLAB": { - "Loader": "slab", - "Container": "raw", - "Width": 360, - "Max": 0, - "Map": { "0": { "Id": "uibg.1" } } - }, - "Albion/MAIN.EXE#476227B0": { // EN GOG Installed Aug 22 1996 - "IsReadOnly": true, - "Loader": "fixedsize", - "Container": "binaryoffsets", - "MapFile": "Meta/MainExe.json" - }, - "Albion/MAIN.EXE#9FC7ABCF": { // EN Jul 25 1996 - "IsReadOnly": true, - "Loader": "fixedsize", - "Container": "binaryoffsets", - "MapFile": "Meta/MainExe.json" - }, - "Albion/MAIN.EXE#487DA334": { // FR - "IsReadOnly": true, - "Loader": "fixedsize", - "Container": "binaryoffsets", - "MapFile": "Meta/MainExe.json" - }, - "Albion/MAIN.EXE#EC6D6389": { // DE GOG ISO Dec 14 1995 - "IsReadOnly": true, - "Loader": "fixedsize", - "Container": "binaryoffsets", - "MapFile": "Meta/MainExe_DE.json" } + }, + "uibg.1": { "Files": { "Albion/CD/XLDLIBS/SLAB": { "Loader": "slab", "Container": "raw", "Width": 360 } } }, + + "coregfx.0-88": { + "Files": { + "Albion/MAIN.EXE#476227B0": { "MapFile": "Meta/MainExe.json" }, // EN GOG, built Aug 22 1996 + "Albion/MAIN.EXE#9FC7ABCF": { "MapFile": "Meta/MainExe.json" }, // EN, built Jul 25 1996 + "Albion/MAIN.EXE#487DA334": { "MapFile": "Meta/MainExe.json" }, // FR + "Albion/MAIN.EXE#EC6D6389": { "MapFile": "Meta/MainExe_DE.json" } // DE GOG ISO, built Dec 14 1995 + }, + "Container": "binaryoffsets", + "Loader": "fixedsize", + "IsReadOnly": true } } // vim: tabstop=2 shiftwidth=2 expandtab diff --git a/mods/Albion/modinfo.json b/mods/Albion/modinfo.json index 967afbbe9..9e9201731 100644 --- a/mods/Albion/modinfo.json +++ b/mods/Albion/modinfo.json @@ -5,7 +5,7 @@ "Author": "Cam Sinclair et al", "Version": "0.1.0", "Dependencies": [ "Base", "Shaders", "Common" ], - "InheritAssetConfigFrom": "Base", + "InheritTypeConfigFrom": "Base", "AssetConfig": "alb_assets.json", "SymLinks": { "Albion": "$(ALBION)" } } diff --git a/mods/Base/Base.shproj b/mods/Base/Base.shproj index ae36cb23c..18f2e9d6d 100644 --- a/mods/Base/Base.shproj +++ b/mods/Base/Base.shproj @@ -3,7 +3,8 @@ - + + diff --git a/mods/Base/modinfo.json b/mods/Base/modinfo.json index 83c442418..cbc069b14 100644 --- a/mods/Base/modinfo.json +++ b/mods/Base/modinfo.json @@ -5,5 +5,5 @@ "Author": "Cam Sinclair et al", "Version": "0.1.0", "Dependencies": [], - "AssetConfig": "base_assets.json" + "TypeConfig": "types.json" } diff --git a/mods/Base/base_assets.json b/mods/Base/types.json similarity index 55% rename from mods/Base/base_assets.json rename to mods/Base/types.json index aaa5273c0..3a1c91d28 100644 --- a/mods/Base/base_assets.json +++ b/mods/Base/types.json @@ -1,61 +1,61 @@ { "IdTypes": { - "3dbg": { "AssetType": "BackgroundGfx", "EnumType": "UAlbion.Base.DungeonBackground, UAlbion.Base" }, - "3dobj": { "AssetType": "Object3D", "EnumType": "UAlbion.Base.DungeonObject, UAlbion.Base" }, - "automap": { "AssetType": "Automap", "EnumType": "UAlbion.Base.Automap, UAlbion.Base" }, - "autotile": { "AssetType": "AutomapGfx", "EnumType": "UAlbion.Base.AutomapTiles, UAlbion.Base" }, - "block": { "AssetType": "BlockList", "EnumType": "UAlbion.Base.BlockList, UAlbion.Base" }, - "chest": { "AssetType": "Chest", "EnumType": "UAlbion.Base.Chest, UAlbion.Base" }, - "combg": { "AssetType": "CombatBackground", "EnumType": "UAlbion.Base.CombatBackground, UAlbion.Base" }, - "comgfx": { "AssetType": "CombatGfx", "EnumType": "UAlbion.Base.CombatGfx, UAlbion.Base" }, - "coregfx": { "AssetType": "CoreGfx", "EnumType": "UAlbion.Base.CoreGfx, UAlbion.Base" }, - "door": { "AssetType": "Door", "EnumType": "UAlbion.Base.Door, UAlbion.Base" }, - "eset": { "AssetType": "EventSet", "EnumType": "UAlbion.Base.EventSet, UAlbion.Base" }, - "etext": { "AssetType": "EventText", "EnumType": "UAlbion.Base.EventText, UAlbion.Base" }, - "floor": { "AssetType": "Floor", "EnumType": "UAlbion.Base.Floor, UAlbion.Base" }, - "font": { "AssetType": "FontDefinition", "EnumType": "UAlbion.Base.Font, UAlbion.Base" }, - "fontgfx": { "AssetType": "FontGfx", "EnumType": "UAlbion.Base.FontGfx, UAlbion.Base" }, - "ink": { "AssetType": "Ink", "EnumType": "UAlbion.Base.Ink, UAlbion.Base" }, - "item": { "AssetType": "Item", "EnumType": "UAlbion.Base.Item, UAlbion.Base" }, - "itemgfx": { "AssetType": "ItemGfx", "EnumType": "UAlbion.Base.ItemGfx, UAlbion.Base" }, - "itemname": { "AssetType": "ItemName", "EnumType": "UAlbion.Base.ItemName, UAlbion.Base" }, - "lab": { "AssetType": "Labyrinth", "EnumType": "UAlbion.Base.Labyrinth, UAlbion.Base" }, - "map": { "AssetType": "Map", "EnumType": "UAlbion.Base.Map, UAlbion.Base" }, - "merchant": { "AssetType": "Merchant", "EnumType": "UAlbion.Base.Merchant, UAlbion.Base" }, - "mongfx": { "AssetType": "MonsterGfx", "EnumType": "UAlbion.Base.MonsterGfx, UAlbion.Base" }, - "mongrp": { "AssetType": "MonsterGroup", "EnumType": "UAlbion.Base.MonsterGroup, UAlbion.Base" }, - "monster": { "AssetType": "MonsterSheet", "EnumType": "UAlbion.Base.MonsterSheet, UAlbion.Base" }, - "mtext": { "AssetType": "MapText", "EnumType": "UAlbion.Base.MapText, UAlbion.Base" }, - "npc": { "AssetType": "NpcSheet", "EnumType": "UAlbion.Base.NpcSheet, UAlbion.Base" }, - "npclarge": { "AssetType": "NpcLargeGfx", "EnumType": "UAlbion.Base.NpcLargeGfx, UAlbion.Base" }, - "npcsmall": { "AssetType": "NpcSmallGfx", "EnumType": "UAlbion.Base.NpcSmallGfx, UAlbion.Base" }, - "overlay": { "AssetType": "WallOverlay", "EnumType": "UAlbion.Base.WallOverlay, UAlbion.Base" }, - "pal": { "AssetType": "Palette", "EnumType": "UAlbion.Base.Palette, UAlbion.Base" }, - "pic": { "AssetType": "Picture", "EnumType": "UAlbion.Base.Picture, UAlbion.Base" }, - "party": { "AssetType": "PartyMember", "EnumType": "UAlbion.Base.PartyMember, UAlbion.Base" }, - "pinvgfx": { "AssetType": "PartyInventoryGfx", "EnumType": "UAlbion.Base.PartyInventoryGfx, UAlbion.Base" }, - "plarge": { "AssetType": "PartyLargeGfx", "EnumType": "UAlbion.Base.PartyLargeGfx, UAlbion.Base" }, - "psheet": { "AssetType": "PartySheet", "EnumType": "UAlbion.Base.PartySheet, UAlbion.Base" }, - "portrait": { "AssetType": "Portrait", "EnumType": "UAlbion.Base.Portrait, UAlbion.Base" }, - "psmall": { "AssetType": "PartySmallGfx", "EnumType": "UAlbion.Base.PartySmallGfx, UAlbion.Base" }, - "sample": { "AssetType": "Sample", "EnumType": "UAlbion.Base.Sample, UAlbion.Base" }, - "script": { "AssetType": "Script", "EnumType": "UAlbion.Base.Script, UAlbion.Base" }, - "song": { "AssetType": "Song", "EnumType": "UAlbion.Base.Song, UAlbion.Base" }, - "special": { "AssetType": "Special", "EnumType": "UAlbion.Base.Special, UAlbion.Base" }, - "spell": { "AssetType": "Spell", "EnumType": "UAlbion.Base.Spell, UAlbion.Base" }, - "stext": { "AssetType": "Text", "EnumType": "UAlbion.Base.SystemText, UAlbion.Base" }, - "switch": { "AssetType": "Switch", "EnumType": "UAlbion.Base.Switch, UAlbion.Base" }, - "tacgfx": { "AssetType": "TacticalGfx", "EnumType": "UAlbion.Base.TacticalGfx, UAlbion.Base" }, - "target": { "AssetType": "Target", "EnumType": "UAlbion.Base.Target, UAlbion.Base" }, - "ticker": { "AssetType": "Ticker", "EnumType": "UAlbion.Base.Ticker, UAlbion.Base" }, - "tiledata": { "AssetType": "Tileset", "EnumType": "UAlbion.Base.Tileset, UAlbion.Base" }, - "tilegfx": { "AssetType": "TilesetGfx", "EnumType": "UAlbion.Base.TilesetGfx, UAlbion.Base" }, - "uibg": { "AssetType": "Slab", "EnumType": "UAlbion.Base.UiBackground, UAlbion.Base" }, - "utext": { "AssetType": "Text", "EnumType": "UAlbion.Base.UAlbionString, UAlbion.Base" }, - "vid": { "AssetType": "Video", "EnumType": "UAlbion.Base.Video, UAlbion.Base" }, - "wall": { "AssetType": "Wall", "EnumType": "UAlbion.Base.Wall, UAlbion.Base" }, - "wavlib": { "AssetType": "WaveLibrary", "EnumType": "UAlbion.Base.WaveLibrary, UAlbion.Base" }, - "word": { "AssetType": "Word", "EnumType": "UAlbion.Base.Word, UAlbion.Base" } + "3dbg": { "AssetType": "BackgroundGfx", "EnumType": "UAlbion.Base.DungeonBackground, UAlbion.Base" }, + "3dobj": { "AssetType": "Object3D", "EnumType": "UAlbion.Base.DungeonObject, UAlbion.Base" }, + "automap": { "AssetType": "Automap", "EnumType": "UAlbion.Base.Automap, UAlbion.Base" }, + "autotile": { "AssetType": "AutomapGfx", "EnumType": "UAlbion.Base.AutomapTiles, UAlbion.Base" }, + "block": { "AssetType": "BlockList", "EnumType": "UAlbion.Base.BlockList, UAlbion.Base" }, + "chest": { "AssetType": "Chest", "EnumType": "UAlbion.Base.Chest, UAlbion.Base" }, + "combg": { "AssetType": "CombatBackground", "EnumType": "UAlbion.Base.CombatBackground, UAlbion.Base" }, + "comgfx": { "AssetType": "CombatGfx", "EnumType": "UAlbion.Base.CombatGfx, UAlbion.Base" }, + "coregfx": { "AssetType": "CoreGfx", "EnumType": "UAlbion.Base.CoreGfx, UAlbion.Base" }, + "door": { "AssetType": "Door", "EnumType": "UAlbion.Base.Door, UAlbion.Base" }, + "eset": { "AssetType": "EventSet", "EnumType": "UAlbion.Base.EventSet, UAlbion.Base" }, + "etext": { "AssetType": "EventText", "EnumType": "UAlbion.Base.EventText, UAlbion.Base" }, + "floor": { "AssetType": "Floor", "EnumType": "UAlbion.Base.Floor, UAlbion.Base" }, + "font": { "AssetType": "FontDefinition", "EnumType": "UAlbion.Base.Font, UAlbion.Base" }, + "fontgfx": { "AssetType": "FontGfx", "EnumType": "UAlbion.Base.FontGfx, UAlbion.Base" }, + "ink": { "AssetType": "Ink", "EnumType": "UAlbion.Base.Ink, UAlbion.Base" }, + "item": { "AssetType": "Item", "EnumType": "UAlbion.Base.Item, UAlbion.Base" }, + "itemgfx": { "AssetType": "ItemGfx", "EnumType": "UAlbion.Base.ItemGfx, UAlbion.Base" }, + "itemname": { "AssetType": "ItemName", "EnumType": "UAlbion.Base.ItemName, UAlbion.Base" }, + "lab": { "AssetType": "Labyrinth", "EnumType": "UAlbion.Base.Labyrinth, UAlbion.Base" }, + "map": { "AssetType": "Map", "EnumType": "UAlbion.Base.Map, UAlbion.Base" }, + "merchant": { "AssetType": "Merchant", "EnumType": "UAlbion.Base.Merchant, UAlbion.Base" }, + "mongfx": { "AssetType": "MonsterGfx", "EnumType": "UAlbion.Base.MonsterGfx, UAlbion.Base" }, + "mongrp": { "AssetType": "MonsterGroup", "EnumType": "UAlbion.Base.MonsterGroup, UAlbion.Base" }, + "monster": { "AssetType": "MonsterSheet", "EnumType": "UAlbion.Base.MonsterSheet, UAlbion.Base" }, + "mtext": { "AssetType": "MapText", "EnumType": "UAlbion.Base.MapText, UAlbion.Base" }, + "npc": { "AssetType": "NpcSheet", "EnumType": "UAlbion.Base.NpcSheet, UAlbion.Base" }, + "npclarge": { "AssetType": "NpcLargeGfx", "EnumType": "UAlbion.Base.NpcLargeGfx, UAlbion.Base" }, + "npcsmall": { "AssetType": "NpcSmallGfx", "EnumType": "UAlbion.Base.NpcSmallGfx, UAlbion.Base" }, + "overlay": { "AssetType": "WallOverlay", "EnumType": "UAlbion.Base.WallOverlay, UAlbion.Base" }, + "pal": { "AssetType": "Palette", "EnumType": "UAlbion.Base.Palette, UAlbion.Base" }, + "pic": { "AssetType": "Picture", "EnumType": "UAlbion.Base.Picture, UAlbion.Base" }, + "party": { "AssetType": "PartyMember", "EnumType": "UAlbion.Base.PartyMember, UAlbion.Base" }, + "pinvgfx": { "AssetType": "PartyInventoryGfx", "EnumType": "UAlbion.Base.PartyInventoryGfx, UAlbion.Base" }, + "plarge": { "AssetType": "PartyLargeGfx", "EnumType": "UAlbion.Base.PartyLargeGfx, UAlbion.Base" }, + "psheet": { "AssetType": "PartySheet", "EnumType": "UAlbion.Base.PartySheet, UAlbion.Base" }, + "portrait": { "AssetType": "Portrait", "EnumType": "UAlbion.Base.Portrait, UAlbion.Base" }, + "psmall": { "AssetType": "PartySmallGfx", "EnumType": "UAlbion.Base.PartySmallGfx, UAlbion.Base" }, + "sample": { "AssetType": "Sample", "EnumType": "UAlbion.Base.Sample, UAlbion.Base" }, + "script": { "AssetType": "Script", "EnumType": "UAlbion.Base.Script, UAlbion.Base" }, + "song": { "AssetType": "Song", "EnumType": "UAlbion.Base.Song, UAlbion.Base" }, + "special": { "AssetType": "Special", "EnumType": "UAlbion.Base.Special, UAlbion.Base" }, + "spell": { "AssetType": "Spell", "EnumType": "UAlbion.Base.Spell, UAlbion.Base" }, + "stext": { "AssetType": "Text", "EnumType": "UAlbion.Base.SystemText, UAlbion.Base" }, + "switch": { "AssetType": "Switch", "EnumType": "UAlbion.Base.Switch, UAlbion.Base" }, + "tacgfx": { "AssetType": "TacticalGfx", "EnumType": "UAlbion.Base.TacticalGfx, UAlbion.Base" }, + "target": { "AssetType": "Target", "EnumType": "UAlbion.Base.Target, UAlbion.Base" }, + "ticker": { "AssetType": "Ticker", "EnumType": "UAlbion.Base.Ticker, UAlbion.Base" }, + "tiledata": { "AssetType": "Tileset", "EnumType": "UAlbion.Base.Tileset, UAlbion.Base" }, + "tilegfx": { "AssetType": "TilesetGfx", "EnumType": "UAlbion.Base.TilesetGfx, UAlbion.Base" }, + "uibg": { "AssetType": "Slab", "EnumType": "UAlbion.Base.UiBackground, UAlbion.Base" }, + "utext": { "AssetType": "Text", "EnumType": "UAlbion.Base.UAlbionString, UAlbion.Base" }, + "vid": { "AssetType": "Video", "EnumType": "UAlbion.Base.Video, UAlbion.Base" }, + "wall": { "AssetType": "Wall", "EnumType": "UAlbion.Base.Wall, UAlbion.Base" }, + "wavlib": { "AssetType": "WaveLibrary", "EnumType": "UAlbion.Base.WaveLibrary, UAlbion.Base" }, + "word": { "AssetType": "Word", "EnumType": "UAlbion.Base.Word, UAlbion.Base" } }, "Loaders": { @@ -73,9 +73,10 @@ "ink": "UAlbion.Formats.Parsers.JsonLoader`1[[UAlbion.Formats.Assets.Ink, UAlbion.Formats]], UAlbion.Formats", "inputconfig": "UAlbion.Formats.Parsers.InputConfigLoader, UAlbion.Formats", "interlaced": "UAlbion.Formats.Parsers.InterlacedBitmapLoader, UAlbion.Formats", - "itemNameCollector": "UAlbion.Formats.Parsers.ItemNameCollector, UAlbion.Formats", "itemdata": "UAlbion.Formats.Parsers.ItemDataLoader, UAlbion.Formats", "itemname": "UAlbion.Formats.Parsers.ItemNameLoader, UAlbion.Formats", + "itemnameCollector": "UAlbion.Formats.Parsers.ItemNameCollectorLoader, UAlbion.Formats", + "itemnameMeta": "UAlbion.Formats.Parsers.ItemNameMetaLoader, UAlbion.Formats", "json": "UAlbion.Formats.Parsers.JsonStringLoader, UAlbion.Formats", "jsonEventSet": "UAlbion.Formats.Parsers.JsonLoader`1[[UAlbion.Formats.Assets.EventSet, UAlbion.Formats]], UAlbion.Formats", "jsonInv": "UAlbion.Formats.Parsers.JsonLoader`1[[UAlbion.Formats.Assets.Inventory, UAlbion.Formats]], UAlbion.Formats", @@ -102,6 +103,8 @@ "song": "UAlbion.Formats.Parsers.SongLoader, UAlbion.Formats", "spell": "UAlbion.Formats.Parsers.SpellLoader, UAlbion.Formats", "stext": "UAlbion.Formats.Parsers.SystemTextLoader, UAlbion.Formats", + "stringset": "UAlbion.Formats.Parsers.StringSetStringLoader, UAlbion.Formats", + "stringcollector": "UAlbion.Formats.Parsers.StringSetCollectorLoader, UAlbion.Formats", "stringtable": "UAlbion.Formats.Parsers.AlbionStringTableLoader, UAlbion.Formats", "tiledMap": "UAlbion.Formats.Exporters.Tiled.TiledMapLoader, UAlbion.Formats", "tiledStamp": "UAlbion.Formats.Exporters.Tiled.StampLoader, UAlbion.Formats", @@ -113,7 +116,6 @@ "wav": "UAlbion.Formats.Parsers.WavLoader, UAlbion.Formats", "wavlib": "UAlbion.Formats.Parsers.WaveLibLoader, UAlbion.Formats", "wavlib2": "UAlbion.Formats.Parsers.WaveLibWavLoader, UAlbion.Formats", - "wordCollector": "UAlbion.Formats.Parsers.WordCollector, UAlbion.Formats", "wordlist": "UAlbion.Formats.Parsers.WordListLoader, UAlbion.Formats", // These are in Game.Veldrid due to a dependency on ImageSharp @@ -149,6 +151,11 @@ "atlas": "UAlbion.Formats.Parsers.AtlasPostProcessor, UAlbion.Formats" }, + "GlobalPropertyTypes": [ + "UAlbion.Config.Properties.AssetProps, UAlbion.Config", + "UAlbion.Game.Veldrid.Input.CursorManager, UAlbion.Game.Veldrid" + ], + "VarTypes": [ "UAlbion.Formats.Config.GameVars, UAlbion.Formats", "UAlbion.Game.Settings.UserVars, UAlbion.Game", diff --git a/mods/Albion/ENGLISH/strings.json b/mods/Common/ENGLISH/strings.json similarity index 100% rename from mods/Albion/ENGLISH/strings.json rename to mods/Common/ENGLISH/strings.json diff --git a/mods/Albion/FRENCH/strings.json b/mods/Common/FRENCH/strings.json similarity index 100% rename from mods/Albion/FRENCH/strings.json rename to mods/Common/FRENCH/strings.json diff --git a/mods/Common/Fonts/0_Regular.json b/mods/Common/Fonts/1_Regular.json similarity index 100% rename from mods/Common/Fonts/0_Regular.json rename to mods/Common/Fonts/1_Regular.json diff --git a/mods/Common/Fonts/1_Bold.json b/mods/Common/Fonts/2_Bold.json similarity index 100% rename from mods/Common/Fonts/1_Bold.json rename to mods/Common/Fonts/2_Bold.json diff --git a/mods/Common/Fonts/2_DebugFont6.json b/mods/Common/Fonts/3_DebugFont6.json similarity index 100% rename from mods/Common/Fonts/2_DebugFont6.json rename to mods/Common/Fonts/3_DebugFont6.json diff --git a/mods/Common/Fonts/3_DebugFont7.json b/mods/Common/Fonts/4_DebugFont7.json similarity index 100% rename from mods/Common/Fonts/3_DebugFont7.json rename to mods/Common/Fonts/4_DebugFont7.json diff --git a/mods/Common/Fonts/4_DebugFont10.json b/mods/Common/Fonts/5_DebugFont10.json similarity index 100% rename from mods/Common/Fonts/4_DebugFont10.json rename to mods/Common/Fonts/5_DebugFont10.json diff --git a/mods/Common/Fonts/5_DebugFont11.json b/mods/Common/Fonts/6_DebugFont11.json similarity index 100% rename from mods/Common/Fonts/5_DebugFont11.json rename to mods/Common/Fonts/6_DebugFont11.json diff --git a/mods/Common/Fonts/6_DebugFont14.json b/mods/Common/Fonts/7_DebugFont14.json similarity index 100% rename from mods/Common/Fonts/6_DebugFont14.json rename to mods/Common/Fonts/7_DebugFont14.json diff --git a/mods/Common/Fonts/7_DebugFont17.json b/mods/Common/Fonts/8_DebugFont17.json similarity index 100% rename from mods/Common/Fonts/7_DebugFont17.json rename to mods/Common/Fonts/8_DebugFont17.json diff --git a/mods/Albion/GERMAN/strings.json b/mods/Common/GERMAN/strings.json similarity index 100% rename from mods/Albion/GERMAN/strings.json rename to mods/Common/GERMAN/strings.json diff --git a/mods/Common/assets.json b/mods/Common/assets.json index fadf52e74..1cb81b486 100644 --- a/mods/Common/assets.json +++ b/mods/Common/assets.json @@ -1,39 +1,61 @@ { - "Files": { - "config.json": { - "Container": "dummy", - "Loader": "varset", - "Max": 0, - "Map": { "0": { "Id": "special.GameConfig" } } + "special.GameConfig": { + "Files": { "config.json": {} }, + "Container": "dummy", + "Loader": "varset" + }, + + "special.InputConfig": { + "Files": { "input.json": {} }, + "Container": "dummy", + "Loader": "inputconfig" + }, + + "special.UAlbionStrings": { + "Files": { + "GERMAN/strings.json": { "Language": "GERMAN" }, + "ENGLISH/strings.json": { "Language": "ENGLISH" }, + "FRENCH/strings.json": { "Language": "FRENCH" } }, - "input.json": { - "Container": "dummy", - "Loader": "inputconfig", - "Max": 0, - "Map": { "0": { "Id": "special.InputConfig" } } + "Loader": "json", + "Container": "raw" + }, + + "utext.0-*": { + "Files": { // Dummy files used for ensuring the language is available on the AssetLoadContext + "!GERMAN": { "Language": "GERMAN" }, + "!ENGLISH": { "Language": "ENGLISH" }, + "!FRENCH": { "Language": "FRENCH" } }, - "PartyMembers": { - "Loader": "jsonParty", - "Pattern": "{id:00}_{name}.json", - "Map": { "1": { "Id": "party.1" } } - }, - "Fonts": { - "Loader": "font", - "Pattern": "{id}_{name}.json", - "Map": { "0": { "Id": "font.Regular" } } - }, - "Fonts/DebugFonts.png": { - "Container": "raw", - "Loader": "png8", - "Max": 0, - "Map": { "0": { "Id": "fontgfx.Debug" } } - }, - "Inks.json": { - "Container": "jsonObj", - "Loader": "ink", - "Max": 99, - "Map": { "1": { "Id": "ink.White" } } - } + "Loader": "stringset", + "FirstId": "utext.0", + "Target": "special.UAlbionStrings", + "IsReadOnly": true + }, + + "party.1-*": { + "Files": { "PartyMembers": {} }, + "Loader": "jsonParty", + "Pattern": "{id:00}_{name}.json" + }, + + "font.1-*": { + "Files": { "Fonts": {} }, + "Loader": "font", + "Pattern": "{id}_{name}.json" + }, + + "fontgfx.Debug": { + "Files": { "Fonts/DebugFonts.png": {} }, + "Container": "raw", + "Loader": "png8", + "Palette": "pal.0" + }, + + "ink.1-*": { + "Files": { "Inks.json": {} }, + "Container": "jsonObj", + "Loader": "ink" } } // vim: tabstop=2 shiftwidth=2 expandtab diff --git a/mods/Common/modinfo.json b/mods/Common/modinfo.json index 2d84c7194..c2e3ef02f 100644 --- a/mods/Common/modinfo.json +++ b/mods/Common/modinfo.json @@ -5,5 +5,6 @@ "Author": "Cam Sinclair et al", "Version": "0.1.0", "Dependencies": [ "Base" ], - "InheritAssetConfigFrom": "Base" + "InheritTypeConfigFrom": "Base", + "AssetConfig": "assets.json" } diff --git a/mods/ExportPalettes/modinfo.json b/mods/ExportPalettes/modinfo.json index 965cbc14e..697a21535 100644 --- a/mods/ExportPalettes/modinfo.json +++ b/mods/ExportPalettes/modinfo.json @@ -2,6 +2,7 @@ "Name": "UAlbion-ExportPalettes", "Description": "A simple mod spec for exporting palettes to png", "Version": "0.1.0", - "Dependencies": [ "Base", "Shaders", "Common" ], - "InheritAssetConfigFrom": "Base" + "Dependencies": [ "Albion" ], + "InheritTypeConfigFrom": "Base", + "AssetConfig": "assets.json" } diff --git a/mods/Repacked/assets.json b/mods/Repacked/assets.json deleted file mode 100644 index be5b337e1..000000000 --- a/mods/Repacked/assets.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - /* - Uses identical asset config to 'Albion', but without the symlink - pointing it to the installed game files. This ensures that when - exporting to 'Repacked', the original game won't be affected. - Only the files in the mods/Repacked directory will be updated. - */ -} - diff --git a/mods/Repacked/modinfo.json b/mods/Repacked/modinfo.json index a4710b183..b82dd16d5 100644 --- a/mods/Repacked/modinfo.json +++ b/mods/Repacked/modinfo.json @@ -3,5 +3,6 @@ "Description": "Local location for repacking modified assets for testing with original game", "Version": "0.1.0", "Dependencies": [ "Albion" ], - "InheritAssetConfigFrom": "Albion" + "InheritAssetConfigFrom": "Albion", + "InheritTypeConfigFrom": "Albion" } diff --git a/mods/Shaders/assets.json b/mods/Shaders/assets.json deleted file mode 100644 index ffcd4415b..000000000 --- a/mods/Shaders/assets.json +++ /dev/null @@ -1 +0,0 @@ -{ } diff --git a/mods/Unpacked/assets.json b/mods/Unpacked/assets.json index db9a68915..adf906f26 100644 --- a/mods/Unpacked/assets.json +++ b/mods/Unpacked/assets.json @@ -1,219 +1,242 @@ { - "Languages": { // Just used for the options screen - "ENGLISH": { "DisplayName": "English", "ShortName": "EN" }, - "GERMAN": { "DisplayName": "Deutsch", "ShortName": "DE" }, - "FRENCH": { "DisplayName": "Français", "ShortName": "FR" } + // Pattern Variables: 0=SubId in container 1=frame/sub-asset number 2=Asset name 3=PaletteId. Patterns must begin with {id}_ when writing. + "special.SoundBank": { "Files": { "Assets/Soundbank.json": { "Container": "raw", "Loader": "jsonSoundbank" } } }, + + "special.TiledNpcsLarge": { + "Sequence": 1, // Needs to happen before maps are exported + "Loader": "tiledNpcTileset", + "Container": "raw", + "UseDummyRead": true, + "Files": { "Assets/Tilesets/LargeNPCs.tsx": { "IsSmall": false, "GraphicsPattern": "Assets/Tilesets/LargeNPCs/{id}.png" } } + }, + "special.TiledNpcsSmall": { + "Sequence": 1, // Needs to happen before maps are exported + "Loader": "tiledNpcTileset", + "Container": "raw", + "UseDummyRead": true, + "Files": { "Assets/Tilesets/SmallNPCs.tsx": { "IsSmall": true, "GraphicsPattern": "Assets/Tilesets/SmallNPCs/{id}.png" } } }, - "Files": { // Pattern Variables: 0=SubId in container 1=frame/sub-asset number 2=Asset name 3=PaletteId. Patterns must begin with {id}_ when writing. - "Assets/3dBackgrounds": { "Loader": "png8", "Container": "dir", "Pattern": "{id}_p{pal}_{name}.png", "Map": { "1": { "Id": "3dbg.1" } } }, - "Assets/3dFloors": { "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png", "Map": { "1": { "Id": "floor.1" } } }, - "Assets/3dObjects": { "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png", "Map": { "1": { "Id": "3dobj.1" } } }, - "Assets/3dOverlays": { "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png", "Map": { "1": { "Id": "overlay.1" } } }, - "Assets/3dWalls": { "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png", "Map": { "1": { "Id": "wall.1" } } }, - "Assets/CombatBackground": { "Loader": "png8", "Container": "dir", "Pattern": "{id}_p{pal}_{name}.png", "Map": { "1": { "Id": "combg.1" } } }, - "Assets/CombatGraphics": { "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png", "Map": { "1": { "Id": "comgfx.1" } } }, - "Assets/InvBackgrounds": { "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png", "Map": { "1": { "Id": "pinvgfx.1" } } }, - "Assets/MonsterGraphics": { "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png", "Map": { "1": { "Id": "mongfx.1" } } }, - "Assets/NpcGraphicsLarge": { "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png", "Map": { "1": { "Id": "npclarge.1" } } }, - "Assets/NpcGraphicsSmall": { "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png", "Map": { "1": { "Id": "npcsmall.1" } } }, - "Assets/PartyGraphicsLarge": { "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png", "Map": { "1": { "Id": "plarge.1" } } }, - "Assets/PartyGraphicsSmall": { "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png", "Map": { "1": { "Id": "psmall.1" } } }, - "Assets/Pictures": { "Loader": "png32", "Container": "dir", "Pattern": "{id}_{name}.png", "Map": { "1": { "Id": "pic.1" } } }, - "Assets/Portraits": { "Loader": "png8", "Container": "dir", "Pattern": "{id}_{name}.png", "Map": { "1": { "Id": "portrait.1" } } }, - "Assets/Slab": { "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_{name}.png", "Map": { "1": { "Id": "uibg.1" } } }, - "Assets/TacticalIcons": { "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_{name}.png", "Map": { "1": { "Id": "tacgfx.1" } } }, - "Assets/CoreGraphics": { "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_{name}.png", "Map": { "0": { "Id": "coregfx.0" } } }, - - "Assets/Soundbank.json": { - "Container": "raw", - "Loader": "jsonSoundbank", - "Max": 0, - "Map": { "0": { "Id": "special.SoundBank" } } - }, - - "Assets/Tilesets/LargeNPCs.tsx": { - "Loader": "tiledNpcTileset", - "Container": "raw", - "IsSmall": false, - "GraphicsPattern": "Assets/Tilesets/LargeNPCs/{id}.png", - "Max": 0, - "Map": { "0": { "Id": "special.DummyObject" } } - }, - "Assets/Tilesets/SmallNPCs.tsx": { - "Loader": "tiledNpcTileset", - "Container": "raw", - "IsSmall": true, - "GraphicsPattern": "Assets/Tilesets/SmallNPCs/{id}.png", - "Max": 0, - "Map": { "0": { "Id": "special.DummyObject" } } - }, + "special.ItemNamesMultiLang": { // This is just for repacking back to the binary formats + "Loader": "itemnameCollector", + "TargetRange": "itemname.1-*", + "TargetLanguages": [ "GERMAN", "ENGLISH", "FRENCH" ], + "IsReadOnly": true + }, - "Assets/Chests": { "Loader": "jsonInv", "Container": "dir", "Pattern": "{id:000}_{name}.json", "Map": { "1": { "Id": "chest.1" } } }, - "Assets/Events": { "Loader": "eventSetScript", "Container": "dir", "Pattern": "{id:000}_{name}.ua", "Map": { "1": { "Id": "eset.1" } } }, - "Assets/Merchants": { "Loader": "jsonInv", "Container": "dir", "Pattern": "{id:000}_{name}.json", "Map": { "1": { "Id": "merchant.1" } } }, - "Assets/MonsterGroups": { "Loader": "jsonMonsterGroup", "Container": "dir", "Pattern": "{id}_{name}.json", "Map": { "2": { "Id": "mongrp.2" } } }, - "Assets/Monsters": { "Loader": "jsonSheet", "Container": "dir", "Pattern": "{id}_{name}.json", "Map": { "1": { "Id": "monster.1" } } }, - "Assets/Npcs": { "Loader": "jsonSheet", "Container": "dir", "Pattern": "{id}_{name}.json", "Map": { "1": { "Id": "npc.1" } } }, - "Assets/Palettes": { "Loader": "jsonPal", "Container": "dir", "Pattern": "{id}_{name}.json", "Map": { "0": { "Id": "pal.0" } } }, - "Assets/PartyMembers": { "Loader": "jsonSheet", "Container": "dir", "Pattern": "{id}_{name}.json", "Map": { "1": { "Id": "psheet.1" } } }, - "Assets/Samples": { "Loader": "wav", "Container": "dir", "Pattern": "{id}_{name}.wav", "Map": { "1": { "Id": "sample.1" } } }, - "Assets/Scripts": { "Loader": "script", "Container": "dir", "Pattern": "{id}_{name}.ua", "Map": { "2": { "Id": "script.2" } } }, - "Assets/Songs": { "Loader": "song", "Container": "dir", "Pattern": "{id}_{name}.xmi", "Map": { "1": { "Id": "song.1" } } }, - "Assets/WaveLib": { "Loader": "wavlib2", "Container": "dir", "Pattern": "{id}_{frame}_{name}.wav", "Map": { "5": { "Id": "wavlib.5" } } }, - "Assets/Items.json": { "Loader": "jsonItems", "Container": "jsonObj", "Map": { "1": { "Id": "item.1" } } }, - "Assets/Spells.json": { "Loader": "jsonSpell", "Container": "jsonObj", "Map": { "1": { "Id": "spell.1" } } }, - - "dummy_item_names": { "Loader": "itemNameCollector", "Container": "dummy", "Max": 0, "Map": { "0": { "Id": "special.ItemNames" } } }, - "Assets/ItemNamesDE.json": { "Language": "GERMAN", "Loader": "utf8", "Container": "jsonStrings", "Map": { "1": { "Id": "itemname.1" } } }, - "Assets/ItemNamesEN.json": { "Language": "ENGLISH", "Loader": "utf8", "Container": "jsonStrings", "Map": { "1": { "Id": "itemname.1" } } }, - "Assets/ItemNamesFR.json": { "Language": "FRENCH", "Loader": "utf8", "Container": "jsonStrings", "Map": { "1": { "Id": "itemname.1" } } }, - - "Assets/SystemTextDE.json": { "Language": "GERMAN", "Loader": "utf8", "Container": "jsonStrings", "Map": { "0": { "Id": "stext.0" } } }, - "Assets/SystemTextEN.json": { "Language": "ENGLISH", "Loader": "utf8", "Container": "jsonStrings", "Map": { "0": { "Id": "stext.0" } } }, - "Assets/SystemTextFR.json": { "Language": "FRENCH", "Loader": "utf8", "Container": "jsonStrings", "Map": { "0": { "Id": "stext.0" } } }, - - "Assets/EventTextDE": { "Language": "GERMAN", "Loader": "jsonText", "Container": "dir", "Pattern": "{id}_{name}.json", "Map": { "1": { "Id": "etext.1" } } }, - "Assets/EventTextEN": { "Language": "ENGLISH", "Loader": "jsonText", "Container": "dir", "Pattern": "{id}_{name}.json", "Map": { "1": { "Id": "etext.1" } } }, - "Assets/EventTextFR": { "Language": "FRENCH", "Loader": "jsonText", "Container": "dir", "Pattern": "{id}_{name}.json", "Map": { "1": { "Id": "etext.1" } } }, - - "Assets/MapTextDE": { "Language": "GERMAN", "Loader": "jsonText", "Container": "dir", "Pattern": "{id}_{name}.json", "Map": { "100": { "Id": "mtext.100" } } }, - "Assets/MapTextEN": { "Language": "ENGLISH", "Loader": "jsonText", "Container": "dir", "Pattern": "{id}_{name}.json", "Map": { "100": { "Id": "mtext.100" } } }, - "Assets/MapTextFR": { "Language": "FRENCH", "Loader": "jsonText", "Container": "dir", "Pattern": "{id}_{name}.json", "Map": { "100": { "Id": "mtext.100" } } }, - - "dummy_words_de": { - "Language": "GERMAN", - "Loader": "wordCollector", - "Container": "dummy", - "Max": 2, - "Map": { - "0": { "Id": "special.Words1" }, - "1": { "Id": "special.Words2" }, - "2": { "Id": "special.Words3" } - } - }, - "dummy_words_en": { - "Language": "ENGLISH", - "Loader": "wordCollector", - "Container": "dummy", - "Max": 2, - "Map": { - "0": { "Id": "special.Words1" }, - "1": { "Id": "special.Words2" }, - "2": { "Id": "special.Words3" } - } + "special.Words1": { + "Files": { + "!GERMAN": { "Language": "GERMAN" }, + "!ENGLISH": { "Language": "ENGLISH" }, + "!FRENCH": { "Language": "FRENCH" } }, - "dummy_words_fr": { - "Language": "FRENCH", - "Loader": "wordCollector", - "Container": "dummy", - "Max": 2, - "Map": { - "0": { "Id": "special.Words1" }, - "1": { "Id": "special.Words2" }, - "2": { "Id": "special.Words3" } - } - }, - "Assets/WordsDE.json": { "Language": "GERMAN", "Loader": "utf8", "Container": "jsonStrings", "Map": { "0": { "Id": "word.0" } } }, - "Assets/WordsEN.json": { "Language": "ENGLISH", "Loader": "utf8", "Container": "jsonStrings", "Map": { "0": { "Id": "word.0" } } }, - "Assets/WordsFR.json": { "Language": "FRENCH", "Loader": "utf8", "Container": "jsonStrings", "Map": { "0": { "Id": "word.0" } } }, - - "Assets/Automap/1": { "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame:000}_p{pal}_{name}.png", "Max": 1, "Map": { "1": { "Id": "autotile.1" } } }, - "Assets/Automap/2": { "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame:000}_p{pal}_{name}.png", "Max": 2, "Map": { "2": { "Id": "autotile.2" } } }, - "Assets/Automap": { "Loader": "automap", "Container": "dir", "Pattern": "{id}_{frame}_{name}.dat", "Map": { "100": { "Id": "automap.100" } } }, - "Assets/FontGfx": { - "Container": "dir", - "Loader": "pngsheet", - "Width": 8, - "Height": 8, - "Pattern": "{id}_{name}.png", - "Map": { "1": { "Id": "fontgfx.1" } } + "IsReadOnly": true, + "Loader": "stringcollector", + "FileRange": "word.0-1499", + "TargetRange": "word.0-499" + }, + "special.Words2": { + "Files": { + "!GERMAN": { "Language": "GERMAN" }, + "!ENGLISH": { "Language": "ENGLISH" }, + "!FRENCH": { "Language": "FRENCH" } }, - "Assets/Items.png": { - "Loader": "pngsheet", - "Container": "raw", - "Pattern": "{id}_{name}.png", - "Width": 16, - "Height": 16, - "Map": { "0": { "Id": "itemgfx.1" } } + "IsReadOnly": true, + "Loader": "stringcollector", + "FileRange": "word.0-1499", + "TargetRange": "word.500-999" + }, + "special.Words3": { + "Files": { + "!GERMAN": { "Language": "GERMAN" }, + "!ENGLISH": { "Language": "ENGLISH" }, + "!FRENCH": { "Language": "FRENCH" } }, + "IsReadOnly": true, + "Loader": "stringcollector", + "FileRange": "word.0-1499", + "TargetRange": "word.1000-1499" + }, - "Assets/Tilesets/1": { "Loader": "pngtile8", "Container": "dir", "PaletteId": 1, "Pattern": "{id}_{frame}.png", "Max": 1, "Map": { "1": { "Id": "tilegfx.1" } } }, - "Assets/Tilesets/2": { "Loader": "pngtile8", "Container": "dir", "PaletteId": 2, "Pattern": "{id}_{frame}.png", "Max": 2, "Map": { "2": { "Id": "tilegfx.2" } } }, - "Assets/Tilesets/3": { "Loader": "pngtile8", "Container": "dir", "PaletteId": 6, "Pattern": "{id}_{frame}.png", "Max": 3, "Map": { "3": { "Id": "tilegfx.3" } } }, - "Assets/Tilesets/4": { "Loader": "pngtile8", "Container": "dir", "PaletteId": 4, "Pattern": "{id}_{frame}.png", "Max": 4, "Map": { "4": { "Id": "tilegfx.4" } } }, - "Assets/Tilesets/5": { "Loader": "pngtile8", "Container": "dir", "PaletteId": 5, "Pattern": "{id}_{frame}.png", "Max": 5, "Map": { "5": { "Id": "tilegfx.5" } } }, - "Assets/Tilesets/6": { "Loader": "pngtile8", "Container": "dir", "PaletteId": 16, "Pattern": "{id}_{frame}.png", "Max": 6, "Map": { "6": { "Id": "tilegfx.6" } } }, - "Assets/Tilesets/7": { "Loader": "pngtile8", "Container": "dir", "PaletteId": 9, "Pattern": "{id}_{frame}.png", "Max": 7, "Map": { "7": { "Id": "tilegfx.7" } } }, - "Assets/Tilesets/8": { "Loader": "pngtile8", "Container": "dir", "PaletteId": 26, "Pattern": "{id}_{frame}.png", "Max": 8, "Map": { "8": { "Id": "tilegfx.8" } } }, - "Assets/Tilesets/9": { "Loader": "pngtile8", "Container": "dir", "PaletteId": 28, "Pattern": "{id}_{frame}.png", "Max": 9, "Map": { "9": { "Id": "tilegfx.9" } } }, - "Assets/Tilesets/10": { "Loader": "pngtile8", "Container": "dir", "PaletteId": 45, "Pattern": "{id}_{frame}.png", "Max": 10, "Map": { "10": { "Id": "tilegfx.10" } } }, - "Assets/Tilesets/11": { "Loader": "pngtile8", "Container": "dir", "PaletteId": 56, "Pattern": "{id}_{frame}.png", "Max": 11, "Map": { "11": { "Id": "tilegfx.11" } } }, - - "Assets/Tilesets/1/Stamps": { "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx", "Max": 1, "Map": { "1": { "Id": "block.1" } } }, - "Assets/Tilesets/2/Stamps": { "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx", "Max": 2, "Map": { "2": { "Id": "block.2" } } }, - "Assets/Tilesets/3/Stamps": { "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx", "Max": 3, "Map": { "3": { "Id": "block.3" } } }, - "Assets/Tilesets/4/Stamps": { "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx", "Max": 4, "Map": { "4": { "Id": "block.4" } } }, - "Assets/Tilesets/5/Stamps": { "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx", "Max": 5, "Map": { "5": { "Id": "block.5" } } }, - "Assets/Tilesets/6/Stamps": { "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx", "Max": 6, "Map": { "6": { "Id": "block.6" } } }, - "Assets/Tilesets/7/Stamps": { "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx", "Max": 7, "Map": { "7": { "Id": "block.7" } } }, - "Assets/Tilesets/8/Stamps": { "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx", "Max": 8, "Map": { "8": { "Id": "block.8" } } }, - "Assets/Tilesets/9/Stamps": { "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx", "Max": 9, "Map": { "9": { "Id": "block.9" } } }, - "Assets/Tilesets/10/Stamps": { "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx", "Max": 10, "Map": { "10": { "Id": "block.10" } } }, - "Assets/Tilesets/11/Stamps": { "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx", "Max": 11, "Map": { "11": { "Id": "block.11" } } }, - - "Assets/3dTilesets": { - // "Loader": "jsonLab", - "Loader": "tiledLabyrinth", - "Container": "dir", - - "Pattern": "{id}_{name}.json", - "TileWidth": 96, - "TileHeight": 128, - "BaseHeight": 68, - "TilesPerRow": 16, - "TiledFloorPattern": "Tiled/{id}_{name}_Floors.tsx", - "TiledCeilingPattern": "Tiled/{id}_{name}_Ceilings.tsx", - "TiledWallPattern": "Tiled/{id}_{name}_Walls.tsx", - "TiledContentsPattern": "Tiled/{id}_{name}_Contents.tsx", - - "FloorPngPattern": "Tiled/Gfx/{id}_{name}_Floors.png", - "CeilingPngPattern": "Tiled/Gfx/{id}_{name}_Ceilings.png", - "WallPngPattern": "Tiled/Gfx/{id}_{name}_Walls.png", - "ContentsPngPattern": "Tiled/Gfx/{id}_{name}_Contents.png", - - "Map": { "1": { "Id": "lab.1" } } - }, + "3dbg.1-*": { "Files": { "Assets/3dBackgrounds": {} }, "Loader": "png8", "Container": "dir", "Pattern": "{id}_p{pal}_{name}.png" }, + "floor.1-*": { "Files": { "Assets/3dFloors": {} }, "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png" }, + "3dobj.1-*": { "Files": { "Assets/3dObjects": {} }, "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png" }, + "overlay.1-*": { "Files": { "Assets/3dOverlays": {} }, "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png" }, + "wall.1-*": { "Files": { "Assets/3dWalls": {} }, "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png" }, + "combg.1-*": { "Files": { "Assets/CombatBackground": {} }, "Loader": "png8", "Container": "dir", "Pattern": "{id}_p{pal}_{name}.png" }, + "comgfx.1-*": { "Files": { "Assets/CombatGraphics": {} }, "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png" }, + "pinvgfx.1-*": { "Files": { "Assets/InvBackgrounds": {} }, "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png" }, + "mongfx.1-*": { "Files": { "Assets/MonsterGraphics": {} }, "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png" }, + "npclarge.1-*": { "Files": { "Assets/NpcGraphicsLarge": {} }, "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png" }, + "npcsmall.1-*": { "Files": { "Assets/NpcGraphicsSmall": {} }, "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png" }, + "plarge.1-*": { "Files": { "Assets/PartyGraphicsLarge": {} }, "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png" }, + "psmall.1-*": { "Files": { "Assets/PartyGraphicsSmall": {} }, "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_p{pal}_{name}.png" }, + "pic.1-*": { "Files": { "Assets/Pictures": {} }, "Loader": "png32", "Container": "dir", "Pattern": "{id}_{name}.png" }, + "portrait.1-*": { "Files": { "Assets/Portraits": {} }, "Loader": "png8", "Container": "dir", "Pattern": "{id}_{name}.png", "Palette": "pal.0" }, + "uibg.1-*": { "Files": { "Assets/Slab": {} }, "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_{name}.png", "Palette": "pal.0" }, + "tacgfx.1-*": { "Files": { "Assets/TacticalIcons": {} }, "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_{name}.png", "Palette": "pal.0" }, + "coregfx.0-*": { "Files": { "Assets/CoreGraphics": {} }, "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame}_{name}.png", "Palette": "pal.0" }, + + "chest.1-*": { "Files": { "Assets/Chests": {} }, "Loader": "jsonInv", "Container": "dir", "Pattern": "{id:000}_{name}.json" }, + "eset.1-*": { "Files": { "Assets/Events": {} }, "Loader": "eventSetScript", "Container": "dir", "Pattern": "{id:000}_{name}.ua" }, + "merchant.1-*": { "Files": { "Assets/Merchants": {} }, "Loader": "jsonInv", "Container": "dir", "Pattern": "{id:000}_{name}.json" }, + "mongrp.2-*": { "Files": { "Assets/MonsterGroups": {} }, "Loader": "jsonMonsterGroup", "Container": "dir", "Pattern": "{id}_{name}.json" }, + "monster.1-*": { "Files": { "Assets/Monsters": {} }, "Loader": "jsonSheet", "Container": "dir", "Pattern": "{id}_{name}.json" }, + "npc.1-*": { "Files": { "Assets/Npcs": {} }, "Loader": "jsonSheet", "Container": "dir", "Pattern": "{id}_{name}.json" }, + "pal.0-*": { "Files": { "Assets/Palettes": {} }, "Loader": "jsonPal", "Container": "dir", "Pattern": "{id}_{name}.json" }, + "psheet.1-*": { "Files": { "Assets/PartyMembers": {} }, "Loader": "jsonSheet", "Container": "dir", "Pattern": "{id}_{name}.json" }, + "sample.1-*": { "Files": { "Assets/Samples": {} }, "Loader": "wav", "Container": "dir", "Pattern": "{id}_{name}.wav" }, + "script.2-*": { "Files": { "Assets/Scripts": {} }, "Loader": "script", "Container": "dir", "Pattern": "{id}_{name}.ua" }, + "song.1-*": { "Files": { "Assets/Songs": {} }, "Loader": "song", "Container": "dir", "Pattern": "{id}_{name}.xmi" }, + "wavlib.5-*": { "Files": { "Assets/WaveLib": {} }, "Loader": "wavlib2", "Container": "dir", "Pattern": "{id}_{frame}_{name}.wav" }, + "item.1-*": { "Files": { "Assets/Items.json": {} }, "Loader": "jsonItems", "Container": "jsonObj" }, + "spell.1-*": { "Files": { "Assets/Spells.json": {} }, "Loader": "jsonSpell", "Container": "jsonObj" }, + + "itemname.1-*": { + "Loader": "utf8", + "Container": "jsonStrings", + "Files": { + "Assets/ItemNamesDE.json": { "Language": "GERMAN" }, + "Assets/ItemNamesEN.json": { "Language": "ENGLISH" }, + "Assets/ItemNamesFR.json": { "Language": "FRENCH" } + } + }, - "Assets/Tilesets": { - "Loader": "tiledTileset", // "jsonTileset" - "Container": "dir", - "Pattern": "{id}_{name}.tsx", // "{id}_{name}.json", - "GraphicsPattern": "{id}/{id}_{frame}.png", - "BlankTilePath": "../../Blank.png", - "Map": { "1": { "Id": "tiledata.1" } } - }, + "stext.0-*": { + "Loader": "utf8", + "Container": "jsonStrings", + "Files": { + "Assets/SystemTextDE.json": { "Language": "GERMAN" }, + "Assets/SystemTextEN.json": { "Language": "ENGLISH" }, + "Assets/SystemTextFR.json": { "Language": "FRENCH" } + } + }, - "Assets/Maps": { - "Loader": "tiledMap", - "Container": "dir", - "Pattern": "{id}_{name}.tmx", - "ScriptPattern": "{id}_{name}.ua", - - "TilesetPattern": "../Tilesets/{id}_{name}.tsx", - "SmallNpcs": "../Tilesets/SmallNPCs.tsx", - "LargeNpcs": "../Tilesets/LargeNPCs.tsx", - - "TileWidth": 96, - "TileHeight": 128, - "BaseHeight": 68, - "TiledFloorPattern": "../3dTilesets/Tiled/{id}_{name}_Floors.tsx", - "TiledCeilingPattern": "../3dTilesets/Tiled/{id}_{name}_Ceilings.tsx", - "TiledWallPattern": "../3dTilesets/Tiled/{id}_{name}_Walls.tsx", - "TiledContentsPattern": "../3dTilesets/Tiled/{id}_{name}_Contents.tsx", - - "Map": { "100": { "Id": "map.100" } } + "etext.1-*": { + "Loader": "jsonText", + "Container": "dir", + "Pattern": "{id}_{name}.json", + "Files": { + "Assets/EventTextDE": { "Language": "GERMAN" }, + "Assets/EventTextEN": { "Language": "ENGLISH" }, + "Assets/EventTextFR": { "Language": "FRENCH" } } + }, + + "mtext.100-*": { + "Loader": "jsonText", + "Container": "dir", + "Optional": true, // Not every map has text + "Pattern": "{id}_{name}.json", + "Files": { + "Assets/MapTextDE": { "Language": "GERMAN" }, + "Assets/MapTextEN": { "Language": "ENGLISH" }, + "Assets/MapTextFR": { "Language": "FRENCH" } + } + }, + + "word.0-*": { + "Loader": "utf8", + "Container": "jsonStrings", + "Files": { + "Assets/WordsDE.json": { "Language": "GERMAN" }, + "Assets/WordsEN.json": { "Language": "ENGLISH" }, + "Assets/WordsFR.json": { "Language": "FRENCH" } + } + }, + + "autotile.1": { "Files": { "Assets/Automap/1": {} }, "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame:000}_p{pal}_{name}.png" }, + "autotile.2": { "Files": { "Assets/Automap/2": {} }, "Loader": "png8", "Container": "dir", "Pattern": "{id}_{frame:000}_p{pal}_{name}.png" }, + "automap.100-*": { "Files": { "Assets/Automap": {} }, "Loader": "automap", "Container": "dir", "Pattern": "{id}_{frame}_{name}.dat", "Optional": true }, + + "fontgfx.1-*": { + "Files": { "Assets/FontGfx": { } }, + "Container": "dir", + "Loader": "pngsheet", + "Width": 8, + "Height": 8, + "Pattern": "{id}_{name}.png" + }, + + "itemgfx.1-*": { + "Files": { "Assets/Items.png": {} }, + "Loader": "pngsheet", + "Container": "raw", + "Pattern": "{id}_{name}.png", + "Width": 16, "Height": 16 + }, + + "tilegfx.1": { "Files": { "Assets/Tilesets/1": {} }, "Loader": "pngtile8", "Container": "dir", "Palette": "pal.1", "Pattern": "{id}_{frame}.png" }, + "tilegfx.2": { "Files": { "Assets/Tilesets/2": {} }, "Loader": "pngtile8", "Container": "dir", "Palette": "pal.2", "Pattern": "{id}_{frame}.png" }, + "tilegfx.3": { "Files": { "Assets/Tilesets/3": {} }, "Loader": "pngtile8", "Container": "dir", "Palette": "pal.6", "Pattern": "{id}_{frame}.png" }, + "tilegfx.4": { "Files": { "Assets/Tilesets/4": {} }, "Loader": "pngtile8", "Container": "dir", "Palette": "pal.4", "Pattern": "{id}_{frame}.png" }, + "tilegfx.5": { "Files": { "Assets/Tilesets/5": {} }, "Loader": "pngtile8", "Container": "dir", "Palette": "pal.5", "Pattern": "{id}_{frame}.png" }, + "tilegfx.6": { "Files": { "Assets/Tilesets/6": {} }, "Loader": "pngtile8", "Container": "dir", "Palette": "pal.16", "Pattern": "{id}_{frame}.png" }, + "tilegfx.7": { "Files": { "Assets/Tilesets/7": {} }, "Loader": "pngtile8", "Container": "dir", "Palette": "pal.9", "Pattern": "{id}_{frame}.png" }, + "tilegfx.8": { "Files": { "Assets/Tilesets/8": {} }, "Loader": "pngtile8", "Container": "dir", "Palette": "pal.26", "Pattern": "{id}_{frame}.png" }, + "tilegfx.9": { "Files": { "Assets/Tilesets/9": {} }, "Loader": "pngtile8", "Container": "dir", "Palette": "pal.28", "Pattern": "{id}_{frame}.png" }, + "tilegfx.10": { "Files": { "Assets/Tilesets/10": {} }, "Loader": "pngtile8", "Container": "dir", "Palette": "pal.45", "Pattern": "{id}_{frame}.png" }, + "tilegfx.11": { "Files": { "Assets/Tilesets/11": {} }, "Loader": "pngtile8", "Container": "dir", "Palette": "pal.56", "Pattern": "{id}_{frame}.png" }, + + "block.1": { "Files": { "Assets/Tilesets/1/Stamps": {} }, "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx" }, + "block.2": { "Files": { "Assets/Tilesets/2/Stamps": {} }, "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx" }, + "block.3": { "Files": { "Assets/Tilesets/3/Stamps": {} }, "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx" }, + "block.4": { "Files": { "Assets/Tilesets/4/Stamps": {} }, "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx" }, + "block.5": { "Files": { "Assets/Tilesets/5/Stamps": {} }, "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx" }, + "block.6": { "Files": { "Assets/Tilesets/6/Stamps": {} }, "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx" }, + "block.7": { "Files": { "Assets/Tilesets/7/Stamps": {} }, "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx" }, + "block.8": { "Files": { "Assets/Tilesets/8/Stamps": {} }, "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx" }, + "block.9": { "Files": { "Assets/Tilesets/9/Stamps": {} }, "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx" }, + "block.10": { "Files": { "Assets/Tilesets/10/Stamps": {} }, "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx" }, + "block.11": { "Files": { "Assets/Tilesets/11/Stamps": {} }, "Loader": "tiledStamp", "Container": "dir", "Pattern": "{id}_{frame:000}.stamp", "TilesetPattern": "../../{id}_{name}.tsx" }, + + "lab.1-*": { + "Files": { "Assets/3dTilesets": {} }, + "Loader": "tiledLabyrinth", + "Container": "dir", + + "Pattern": "{id}_{name}.json", + "TileWidth": 96, + "TileHeight": 128, + "BaseHeight": 68, + "TilesPerRow": 16, + "TiledFloorPattern": "Tiled/{id}_{name}_Floors.tsx", + "TiledCeilingPattern": "Tiled/{id}_{name}_Ceilings.tsx", + "TiledWallPattern": "Tiled/{id}_{name}_Walls.tsx", + "TiledContentsPattern": "Tiled/{id}_{name}_Contents.tsx", + + "FloorPngPattern": "Tiled/Gfx/{id}_{name}_Floors.png", + "CeilingPngPattern": "Tiled/Gfx/{id}_{name}_Ceilings.png", + "WallPngPattern": "Tiled/Gfx/{id}_{name}_Walls.png", + "ContentsPngPattern": "Tiled/Gfx/{id}_{name}_Contents.png" + }, + + "tiledata.1-*": { + "Files": { "Assets/Tilesets": {} }, + "Loader": "tiledTileset", // "jsonTileset" + "Container": "dir", + "Pattern": "{id}_{name}.tsx", // "{id}_{name}.json", + "GraphicsPattern": "{id}/{id}_{frame}.png", + "BlankTilePath": "../../Blank.png" + }, + + "map.100-*": { + "Files": { "Assets/Maps": {} }, + "Loader": "tiledMap", + "Container": "dir", + "Pattern": "{id}_{name}.tmx", + "ScriptPattern": "{id}_{name}.ua", + + "TilesetPattern": "../Tilesets/{id}_{name}.tsx", + "SmallNpcs": "../Tilesets/SmallNPCs.tsx", + "LargeNpcs": "../Tilesets/LargeNPCs.tsx", + + "TileWidth": 96, + "BaseHeight": 68, + "TiledFloorPattern": "../3dTilesets/Tiled/{id}_{name}_Floors.tsx", + "TiledCeilingPattern": "../3dTilesets/Tiled/{id}_{name}_Ceilings.tsx", + "TiledWallPattern": "../3dTilesets/Tiled/{id}_{name}_Walls.tsx", + "TiledContentsPattern": "../3dTilesets/Tiled/{id}_{name}_Contents.tsx" } } // vim: tabstop=2 shiftwidth=2 expandtab diff --git a/mods/Unpacked/modinfo.json b/mods/Unpacked/modinfo.json index a5c234903..b3eb5eaac 100644 --- a/mods/Unpacked/modinfo.json +++ b/mods/Unpacked/modinfo.json @@ -3,5 +3,6 @@ "Description": "Local location for unpacking/modifying and repacking assets", "Version": "0.1.0", "Dependencies": [ "Base", "Shaders", "Common" ], - "InheritAssetConfigFrom": "Base" + "InheritTypeConfigFrom": "Base", + "AssetConfig": "assets.json" } diff --git a/src/Api/Eventing/EventConstructorAttribute.cs b/src/Api/Eventing/EventConstructorAttribute.cs new file mode 100644 index 000000000..ee91f0a54 --- /dev/null +++ b/src/Api/Eventing/EventConstructorAttribute.cs @@ -0,0 +1,11 @@ +using System; + +namespace UAlbion.Api.Eventing; + +/// +/// Used to specify which constructor should be used when parsing an event from text using its reflection metadata. +/// +[AttributeUsage(AttributeTargets.Constructor)] +public sealed class EventConstructorAttribute : Attribute +{ +} \ No newline at end of file diff --git a/src/Api/Eventing/EventMetadata.cs b/src/Api/Eventing/EventMetadata.cs index bfa423d94..7652380ff 100644 --- a/src/Api/Eventing/EventMetadata.cs +++ b/src/Api/Eventing/EventMetadata.cs @@ -143,7 +143,7 @@ public static void SerializePart(IScriptBuilder builder, EventPartMetadata part, Func BuildParser(ParameterExpression partsParameter) { - var constructor = Type.GetConstructors().Single(); + var constructor = GetConstructor(); var parameters = constructor.GetParameters(); if (parameters.Length != Parts.Count) { @@ -157,4 +157,29 @@ Func BuildParser(ParameterExpression partsParameter) Expression.New(constructor, Parts.Select(x => x.Parser)), typeof(IEvent)), partsParameter).Compile(); } + + ConstructorInfo GetConstructor() + { + var constructors = Type.GetConstructors(); + ConstructorInfo constructor = null; + + if (constructors.Length == 1) + return constructors[0]; + + foreach (var x in constructors) + { + if (x.GetCustomAttribute() == null) + continue; + + if (constructor != null) + throw new FormatException($"\"{Type}\" has multiple constructors with the {nameof(EventConstructorAttribute)}!"); + + constructor = x; + } + + if (constructor == null) + throw new FormatException($"\"{Type}\" has multiple constructors, but none of them are annotated with an {nameof(EventConstructorAttribute)} to indicate which should be used for constructing parsed events"); + + return constructor; + } } \ No newline at end of file diff --git a/src/Api/FileSystem.cs b/src/Api/FileSystem.cs index 8a5f67efc..7b5c27677 100644 --- a/src/Api/FileSystem.cs +++ b/src/Api/FileSystem.cs @@ -35,7 +35,8 @@ public string CurrentDirectory public IEnumerable ReadAllLines(string path) => File.ReadAllLines(ToAbsolutePath(path)); public byte[] ReadAllBytes(string path) => File.ReadAllBytes(ToAbsolutePath(path)); public void WriteAllBytes(string path, byte[] bytes) => File.WriteAllBytes(ToAbsolutePath(path), bytes); - public IEnumerable EnumerateDirectory(string path, string filter = null) + public IEnumerable EnumerateFiles(string path, string filter = null) => filter == null ? Directory.EnumerateFiles(ToAbsolutePath(path)) : Directory.EnumerateFiles(ToAbsolutePath(path), filter); + public IEnumerable EnumerateDirectories(string path) => Directory.EnumerateDirectories(ToAbsolutePath(path)); public string ToAbsolutePath(string path) => ApiUtil.CombinePaths(_currentDirectory, path); } \ No newline at end of file diff --git a/src/Api/IFileSystem.cs b/src/Api/IFileSystem.cs index 14a619b5a..fb4f1eba8 100644 --- a/src/Api/IFileSystem.cs +++ b/src/Api/IFileSystem.cs @@ -10,7 +10,8 @@ public interface IFileSystem IFileSystem Duplicate(string currentDirectory); bool FileExists(string path); bool DirectoryExists(string path); - IEnumerable EnumerateDirectory(string path, string filter = null); + IEnumerable EnumerateFiles(string path, string filter = null); + IEnumerable EnumerateDirectories(string path); void CreateDirectory(string path); Stream OpenRead(string path); Stream OpenWriteTruncate(string path); diff --git a/src/Api/Json/DictionaryConverterUtil.cs b/src/Api/Json/DictionaryConverterUtil.cs index 3406d1cee..b7fa6cced 100644 --- a/src/Api/Json/DictionaryConverterUtil.cs +++ b/src/Api/Json/DictionaryConverterUtil.cs @@ -27,7 +27,8 @@ public static Dictionary Read( if (reader.TokenType != JsonTokenType.PropertyName) throw new JsonException($"Expected property name when reading {typeToConvert}, but was {reader.TokenType}"); - var key = parser(reader.GetString()); + var keyString = reader.GetString(); + var key = parser(keyString); TValue v; if (valueConverter != null) diff --git a/src/Api/ReadOnlyFileSystem.cs b/src/Api/ReadOnlyFileSystem.cs index 3acfbfc2a..d6b48f9c3 100644 --- a/src/Api/ReadOnlyFileSystem.cs +++ b/src/Api/ReadOnlyFileSystem.cs @@ -8,8 +8,9 @@ public class ReadOnlyFileSystem : IFileSystem { string _currentDirectory; public ReadOnlyFileSystem(string currentDirectory) => CurrentDirectory = currentDirectory; - public IEnumerable EnumerateDirectory(string path, string filter = null) + public IEnumerable EnumerateFiles(string path, string filter = null) => filter == null ? Directory.EnumerateFiles(path) : Directory.EnumerateFiles(path, filter); + public IEnumerable EnumerateDirectories(string path) => Directory.EnumerateDirectories(path); public bool IsReadOnly => true; public string CurrentDirectory diff --git a/src/Api/RedirectionFileSystemDecorator.cs b/src/Api/RedirectionFileSystemDecorator.cs index 2b3741560..8d7627c0f 100644 --- a/src/Api/RedirectionFileSystemDecorator.cs +++ b/src/Api/RedirectionFileSystemDecorator.cs @@ -24,7 +24,8 @@ public RedirectionFileSystemDecorator(IFileSystem disk, string prefix, string ta public IFileSystem Duplicate(string currentDirectory) => _disk.Duplicate(currentDirectory); public bool FileExists(string path) => _disk.FileExists(ToAbsolutePath(path)); public bool DirectoryExists(string path) => _disk.DirectoryExists(ToAbsolutePath(path)); - public IEnumerable EnumerateDirectory(string path, string filter = null) => _disk.EnumerateDirectory(ToAbsolutePath(path), filter); + public IEnumerable EnumerateFiles(string path, string filter = null) => _disk.EnumerateFiles(ToAbsolutePath(path), filter); + public IEnumerable EnumerateDirectories(string path) => _disk.EnumerateDirectories(ToAbsolutePath(path)); public void CreateDirectory(string path) => _disk.CreateDirectory(ToAbsolutePath(path)); public Stream OpenRead(string path) => _disk.OpenRead(ToAbsolutePath(path)); public Stream OpenWriteTruncate(string path) => _disk.OpenWriteTruncate(ToAbsolutePath(path)); diff --git a/src/Base/EventSet.cs b/src/Base/EventSet.cs index d93c01f36..bec080bd4 100644 --- a/src/Base/EventSet.cs +++ b/src/Base/EventSet.cs @@ -231,4 +231,6 @@ public enum EventSet : ushort Khunag2 = 989, Siobhan2 = 990, + + Empty999 = 999 } diff --git a/src/Base/EventText.cs b/src/Base/EventText.cs index c376ea615..10e9e2dee 100644 --- a/src/Base/EventText.cs +++ b/src/Base/EventText.cs @@ -231,4 +231,6 @@ public enum EventText : ushort Khunag2 = 989, Siobhan2 = 990, + + Empty999 = 999 } diff --git a/src/Base/FontGfx.cs b/src/Base/FontGfx.cs index 032061182..7dc8560ba 100644 --- a/src/Base/FontGfx.cs +++ b/src/Base/FontGfx.cs @@ -1,3 +1,5 @@ +using UAlbion.Config; + namespace UAlbion.Base; public enum FontGfx : byte diff --git a/src/Base/ItemGfx.cs b/src/Base/ItemGfx.cs index 096cb88f8..11b670f0f 100644 --- a/src/Base/ItemGfx.cs +++ b/src/Base/ItemGfx.cs @@ -1,4 +1,4 @@ -// ReSharper disable InconsistentNaming + // ReSharper disable InconsistentNaming // ReSharper disable UnusedMember.Global namespace UAlbion.Base; diff --git a/src/Base/MapText.cs b/src/Base/MapText.cs index 671c31d48..84ba6c138 100644 --- a/src/Base/MapText.cs +++ b/src/Base/MapText.cs @@ -1,5 +1,6 @@ // ReSharper disable InconsistentNaming // ReSharper disable UnusedMember.Global + namespace UAlbion.Base; public enum MapText : ushort diff --git a/src/Base/Song.cs b/src/Base/Song.cs index 2e3ecbd17..ae0f9b49b 100644 --- a/src/Base/Song.cs +++ b/src/Base/Song.cs @@ -117,7 +117,7 @@ public enum Song : byte // Unknown44 = 44, // Unknown45 = 45, // ShortOptimisticGlissando = 46, - Invalid51 = 51, - Invalid53 = 53, - Invalid58 = 58, + // Invalid51 = 51, + // Invalid53 = 53, + // Invalid58 = 58, } diff --git a/src/Base/Special.cs b/src/Base/Special.cs index c89e87140..4bc49baa9 100644 --- a/src/Base/Special.cs +++ b/src/Base/Special.cs @@ -5,13 +5,16 @@ namespace UAlbion.Base; public enum Special : ushort { SoundBank = 0, - ItemNames = 1, - SystemStrings = 2, - UAlbionStrings = 3, - Words1 = 4, - Words2 = 5, - Words3 = 6, - DummyObject = 7, - GameConfig = 8, - InputConfig = 9, + SystemStrings, + UAlbionStrings, + Words1, + Words2, + Words3, + DummyObject, + GameConfig, + InputConfig, + ItemNamesMultiLang, + ItemNamesSingleLang, + TiledNpcsLarge, + TiledNpcsSmall } \ No newline at end of file diff --git a/src/Config/AssetConfig.cs b/src/Config/AssetConfig.cs index fc35f6685..b002e6f72 100644 --- a/src/Config/AssetConfig.cs +++ b/src/Config/AssetConfig.cs @@ -1,9 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text.Json.Serialization; -using UAlbion.Api; namespace UAlbion.Config; @@ -12,274 +8,38 @@ namespace UAlbion.Config; /// public class AssetConfig : IAssetConfig { - /// - /// A set of aliases for enums to be used as asset ids - /// - [JsonIgnore] public IReadOnlyDictionary IdTypes { get; private set; } - /// - /// A set of mappings between asset ids and string ids - /// - [JsonIgnore] public IReadOnlyDictionary StringMappings { get; private set; } - /// - /// A set of aliases for .NET types (deriving from IAssetLoader) to be used for loading assets - /// - [JsonIgnore] public IReadOnlyDictionary Loaders { get; private set; } - /// - /// A set of aliases for .NET types (deriving from IAssetContainer) to be used for extracting individual assets from container formats. - /// - [JsonIgnore] public IReadOnlyDictionary Containers { get; private set; } - /// - /// A set of aliases for .NET types (deriving from IAssetPostProcessor) to be used for additional post-load asset processing. - /// - [JsonIgnore] public IReadOnlyDictionary PostProcessors { get; private set; } - /// - /// A set of supported natural languages that the player can choose to play the game in. - /// - [JsonIgnore] public IReadOnlyDictionary Languages { get; private set; } - /// - /// The collection of files comprising the mod, paths are relative to the mod directory by default. - /// - [JsonIgnore] public IReadOnlyDictionary Files { get; private set; } - - /// - /// The collection of types containing static IVar instances to be registered - /// - [JsonInclude, JsonPropertyName("VarTypes")] public List VarTypes { get; private set; } = new(); - - // Dictionaries containing only the details defined in this particular mod, and not in any that this mod inherits from. - [JsonInclude, JsonPropertyName("IdTypes")] public Dictionary RawIdTypes { get; private set; } = new(); - [JsonInclude, JsonPropertyName("StringMappings")] public Dictionary RawStringMappings { get; private set; } = new(); - [JsonInclude, JsonPropertyName("Loaders")] public Dictionary RawLoaders { get; private set; } = new(); - [JsonInclude, JsonPropertyName("Containers")] public Dictionary RawContainers { get; private set; } = new(); - [JsonInclude, JsonPropertyName("PostProcessors")] public Dictionary RawPostProcessors { get; private set; } = new(); - [JsonInclude, JsonPropertyName("Languages")] public Dictionary RawLanguages { get; private set; } = new(); - [JsonInclude, JsonPropertyName("Files")] public Dictionary RawFiles { get; private set; } = new(); - - Dictionary _assetLookup; - AssetMapping _mapping; - - public static AssetConfig Parse( - byte[] configText, - AssetConfig parent, - AssetMapping mapping, - IJsonUtil jsonUtil) + public AssetConfig(string modName, RangeLookup ranges) { - if (jsonUtil == null) throw new ArgumentNullException(nameof(jsonUtil)); - var config = jsonUtil.Deserialize(configText); - if (config == null) - return null; - - if (parent != null) - { - config.IdTypes = new FallbackDictionary (config.RawIdTypes, parent.IdTypes); - config.StringMappings = new FallbackDictionary (config.RawStringMappings, parent.StringMappings); - config.Loaders = new FallbackDictionary (config.RawLoaders, parent.Loaders); - config.Containers = new FallbackDictionary (config.RawContainers, parent.Containers); - config.PostProcessors = new FallbackDictionary (config.RawPostProcessors, parent.PostProcessors); - config.Languages = new FallbackDictionary(config.RawLanguages, parent.Languages); - config.Files = new FallbackDictionary (config.RawFiles, parent.Files); - } - else - { - config.IdTypes = config.RawIdTypes; - config.StringMappings = config.RawStringMappings; - config.Loaders = config.RawLoaders; - config.Containers = config.RawContainers; - config.PostProcessors = config.RawPostProcessors; - config.Languages = config.RawLanguages; - config.Files = config.RawFiles; - } - - config.PostLoad(mapping); - return config; + ModName = modName; + Ranges = ranges ?? throw new ArgumentNullException(nameof(ranges)); } - public static AssetConfig Load( - string configPath, - AssetConfig parent, - AssetMapping mapping, - IFileSystem disk, - IJsonUtil jsonUtil) - { - if (disk == null) throw new ArgumentNullException(nameof(disk)); - if (jsonUtil == null) throw new ArgumentNullException(nameof(jsonUtil)); - if (!disk.FileExists(configPath)) - throw new FileNotFoundException($"Could not open asset config from {configPath}"); - - var configText = disk.ReadAllBytes(configPath); - var config = Parse(configText, parent, mapping, jsonUtil); - if(config == null) - throw new FileLoadException($"Could not load asset config from \"{configPath}\""); - return config; - } - - public void Save(string configPath, IFileSystem disk, IJsonUtil jsonUtil) - { - if (disk == null) throw new ArgumentNullException(nameof(disk)); - if (jsonUtil == null) throw new ArgumentNullException(nameof(jsonUtil)); - var json = jsonUtil.Serialize(this); - disk.WriteAllText(configPath, json); - } + public string ModName { get; } // The mod name - public AssetInfo[] GetAssetInfo(AssetId id) - => _assetLookup.TryGetValue(id, out var info) - ? info - : Array.Empty(); + /// + /// The collection of asset ranges that are provided by the mod. + /// + public RangeLookup Ranges { get; } - void PostLoad(AssetMapping mapping) + public IEnumerable GetAssetInfo(AssetId id) { - _mapping = mapping ?? throw new ArgumentNullException(nameof(mapping)); - foreach (var kvp in IdTypes) - kvp.Value.Alias = kvp.Key; - - foreach (var kvp in Languages) - kvp.Value.Id = kvp.Key; + var range = Ranges.TryFindAssetRangeInfo(id); + if (range == null) + yield break; - foreach (var kvp in Files) + if (range.Files == null || range.Files.Count == 0) { - kvp.Value.Config = this; - int index = kvp.Key.IndexOf('#', StringComparison.Ordinal); - kvp.Value.Filename = index == -1 ? kvp.Key : kvp.Key[..index]; - if (index != -1) - kvp.Value.Sha256Hash = kvp.Key[(index + 1)..]; - - // Resolve type aliases - if (kvp.Value.Loader != null && Loaders.TryGetValue(kvp.Value.Loader, out var typeName)) kvp.Value.Loader = typeName; - if (kvp.Value.Container != null && Containers.TryGetValue(kvp.Value.Container, out typeName)) kvp.Value.Container = typeName; - if (kvp.Value.Post != null && PostProcessors.TryGetValue(kvp.Value.Post, out typeName)) kvp.Value.Post = typeName; - - foreach (var asset in kvp.Value.Map) - { - asset.Value.Index = asset.Key; - asset.Value.File = kvp.Value; - } + yield return range.Node; } - } - - public delegate IList<(int RangeStart, int RangeLength)> GetSubItemCountMethod(AssetFileInfo info); - public delegate byte[] ReadAllBytesMethod(string path); - public void PopulateAssetIds( - IJsonUtil jsonUtil, - GetSubItemCountMethod getSubItemCountForFile, - ReadAllBytesMethod readAllBytesFunc) - { - if (jsonUtil == null) throw new ArgumentNullException(nameof(jsonUtil)); - - var temp = new Dictionary>(); - foreach (var file in Files) + else { - file.Value.PopulateAssetIds(jsonUtil, getSubItemCountForFile, readAllBytesFunc); - - foreach (var asset in file.Value.Map.Values) + foreach (var file in range.Files) { - if (!temp.TryGetValue(asset.AssetId, out var list)) - { - list = new List(); - temp[asset.AssetId] = list; - } - - list.Add(asset); + if (file.Map != null && file.Map.TryGetValue(id, out var assetInfo)) + yield return assetInfo.Node; + else + yield return file.Node; } } - - _assetLookup = temp.ToDictionary(x => x.Key, x => x.Value.ToArray()); - } - - public void RegisterStringRedirects(AssetMapping mapping) - { - if (mapping == null) throw new ArgumentNullException(nameof(mapping)); - foreach (var kvp in StringMappings) - { - var (type, range) = SplitId(kvp.Key, '.'); - var enumType = ResolveIdType(type); - var (targetStr, offsetStr) = SplitId(kvp.Value, ':'); - if (!int.TryParse(offsetStr, out var offset)) - offset = 0; - - var target = ResolveId(targetStr); - var (min, max) = ParseRange(range); - mapping.RegisterStringRedirect(enumType, target, min, max, offset); - } - } - - static IEnumerable Validate(IEnumerable types, string category, string assetConfigPath) - { - foreach (var type in types) - { - var enumType = Type.GetType(type); - if (enumType == null) - yield return $"Could not load {category} type \"{type}\" defined in \"{assetConfigPath}\""; - } - } - - public void Validate(string assetConfigPath) - { - var errors = new List(); - errors.AddRange(Validate(IdTypes.Values.Select(x => x.EnumType), "enum", assetConfigPath)); - errors.AddRange(Validate(Loaders.Values, "loader", assetConfigPath)); - errors.AddRange(Validate(Containers.Values, "container", assetConfigPath)); - errors.AddRange(Validate(PostProcessors.Values, "post-processor", assetConfigPath)); - if (errors.Count > 0) - { - var combined = string.Join(Environment.NewLine, errors); - throw new InvalidOperationException(combined); - } - } - - static (int, int) ParseRange(string s) - { - if (s == "*") - return (0, int.MaxValue); - - int index = s.IndexOf('-', StringComparison.Ordinal); - if (index == -1) - { - if(!int.TryParse(s, out var asInt)) - throw new FormatException($"Invalid id range \"{s}\""); - - return (asInt, asInt); - } - - var from = s[..index]; - var to = s[(index + 1)..]; - if(!int.TryParse(from, out var min)) - throw new FormatException($"Invalid id range \"{s}\""); - - if(!int.TryParse(to, out var max)) - throw new FormatException($"Invalid id range \"{s}\""); - - return (min, max); - } - - static (string, string) SplitId(string id, char separator) - { - int index = id.IndexOf(separator, StringComparison.Ordinal); - if (index == -1) - return (id, null); - - var type = id[..index]; - var val = id[(index + 1)..]; - return (type, val); - } - - Type ResolveIdType(string type) - { - if (!IdTypes.TryGetValue(type, out var assetType)) - throw new FormatException($"Could not resolve asset id alias \"{type}\""); - - var enumType = Type.GetType(assetType.EnumType); - if (enumType == null) - throw new FormatException($"Could not resolve type \"{assetType.EnumType}\" (alias \"{assetType.Alias}\")"); - - return enumType; - } - - internal AssetId ResolveId(string id) - { - var (type, val) = SplitId(id, '.'); - if (val == null) - throw new FormatException("Asset IDs should consist of an alias type and value, separated by a '.' character"); - var enumType = ResolveIdType(type); - return _mapping.EnumToId(enumType, val); } -} +} \ No newline at end of file diff --git a/src/Config/AssetConfigLoadException.cs b/src/Config/AssetConfigLoadException.cs new file mode 100644 index 000000000..f902fd709 --- /dev/null +++ b/src/Config/AssetConfigLoadException.cs @@ -0,0 +1,13 @@ +using System; +using System.Runtime.Serialization; + +namespace UAlbion.Config; + +[Serializable] +public class AssetConfigLoadException : Exception +{ + public AssetConfigLoadException() { } + public AssetConfigLoadException(string message) : base(message) { } + public AssetConfigLoadException(string message, Exception exception) : base(message, exception) { } + protected AssetConfigLoadException(SerializationInfo info, StreamingContext context) : base(info, context) { } +} \ No newline at end of file diff --git a/src/Config/AssetConfigLoader.cs b/src/Config/AssetConfigLoader.cs new file mode 100644 index 000000000..da6d55a6b --- /dev/null +++ b/src/Config/AssetConfigLoader.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.IO; +using UAlbion.Api; +using UAlbion.Config.Properties; + +namespace UAlbion.Config; + +public class AssetConfigLoader +{ + readonly IJsonUtil _jsonUtil; + readonly IFileSystem _disk; + readonly IPathResolver _pathResolver; + readonly TypeConfig _typeConfig; + + public AssetConfigLoader( + IFileSystem disk, + IJsonUtil jsonUtil, + IPathResolver pathResolver, + TypeConfig typeConfig) + { + _jsonUtil = jsonUtil; + _disk = disk; + _pathResolver = pathResolver; + _typeConfig = typeConfig; + } + + public AssetConfig Load(string configPath, string modName, AssetConfig parent) + { + if (!_disk.FileExists(configPath)) + throw new FileNotFoundException($"Could not open asset config from {configPath}"); + + var configText = _disk.ReadAllBytes(configPath); + try + { + var config = Parse(configText, modName, parent); + if (config == null) + throw new FileLoadException($"Could not load asset config from \"{configPath}\""); + + return config; + } + catch (Exception ex) + { + var message = $@"Error loading asset config ""{configPath}"": + {ex.Message}"; + throw new AssetConfigLoadException(message, ex); + } + } + + public AssetConfig Parse(byte[] configText, string modName, AssetConfig parent) + { + var raw = _jsonUtil.Deserialize>(configText); + if (raw == null) + return null; + + var ranges = BuildRanges(raw, parent); + var assetConfig = new AssetConfig(modName, ranges); + return assetConfig; + } + + RangeLookup BuildRanges(Dictionary raw, AssetConfig parent) + { + var rangeInfos = new List(); + foreach (var kvp in raw) + rangeInfos.Add(BuildRange(kvp.Key, kvp.Value)); + + return new RangeLookup(parent?.Ranges, rangeInfos); + } + + AssetRangeInfo BuildRange(string key, LoadAssetRangeInfo load) + { + AssetRange range = default; + try + { + range = _typeConfig.ParseIdRange(key); + var rangeInfo = new AssetRangeInfo(range, load.Sequence ?? int.MaxValue); + + if (load.Properties != null) + rangeInfo.Node.SetProperties(load.Properties, _typeConfig, "range", range); + + foreach (var fileKvp in load.Files) + rangeInfo.Files.Add(BuildFile(rangeInfo, fileKvp.Key, fileKvp.Value)); + + return rangeInfo; + } + catch (Exception ex) + { + var message = $@"Error loading info for range {key} ({range}): + {ex.Message}"; + throw new AssetConfigLoadException(message, ex); + } + } + + AssetFileInfo BuildFile(AssetRangeInfo rangeInfo, string key, LoadAssetFileInfo load) + { + try + { + int index = key.IndexOf('#', StringComparison.Ordinal); + var filename = index == -1 ? key : key[..index]; + var hash = index != -1 + ? key[(index + 1)..] + : null; + + var fileInfo = new AssetFileInfo(rangeInfo); + + fileInfo.Node.SetProperty(AssetProps.Filename, filename); + if (hash != null) + fileInfo.Node.SetProperty(AssetProps.Sha256Hash, hash); + + if (load.Properties != null) // Do this before loading any asset mapping or the loader/container might not be set + fileInfo.Node.SetProperties(load.Properties, _typeConfig, "file", key); + + if (load.Map != null) + { + foreach (var kvp in load.Map) + { + var info = BuildAssetInfo(_typeConfig, kvp.Key, fileInfo, kvp.Value); + fileInfo.Map.Add(info.Id, info); + } + } + + if (load.MapFile != null) + { + var fullPath = _pathResolver.ResolvePath(load.MapFile); + if (!_disk.FileExists(fullPath)) + throw new FileNotFoundException($"Could not find mapping file \"{load.MapFile}\" ({fullPath}) specified in mod \"{_typeConfig.ModName}\""); + + var mappingJson = _disk.ReadAllBytes(fullPath); + var mapping = _jsonUtil.Deserialize>(mappingJson); + foreach (var kvp in mapping) + { + var info = BuildAssetInfo(_typeConfig, kvp.Key, fileInfo, kvp.Value); + fileInfo.Map.Add(info.Id, info); + } + } + + return fileInfo; + } + catch (Exception ex) + { + var message = $@"Error loading info for file {key}: + {ex.Message}"; + throw new AssetConfigLoadException(message, ex); + } + } + + static AssetInfo BuildAssetInfo(TypeConfig typeConfig, string key, AssetFileInfo parent, LoadAssetInfo load) + { + AssetId id = AssetId.None; + try + { + id = typeConfig.ResolveId(key); + var result = new AssetInfo(id, parent); + + if (load.Properties != null) + result.Node.SetProperties(load.Properties, typeConfig, "asset", id); + + return result; + } + catch (Exception ex) + { + var message = $@"Error loading info for asset {key} ({id}): + {ex.Message}"; + throw new AssetConfigLoadException(message, ex); + } + } +} \ No newline at end of file diff --git a/src/Config/AssetFileInfo.cs b/src/Config/AssetFileInfo.cs index 279f4839d..e180fa5f4 100644 --- a/src/Config/AssetFileInfo.cs +++ b/src/Config/AssetFileInfo.cs @@ -1,219 +1,20 @@ using System; using System.Collections.Generic; -using System.Text.Json; -using System.Text.Json.Serialization; -using UAlbion.Api; namespace UAlbion.Config; -/// -/// Represents all information about a given file on disk that is required by the asset loading / saving system. -/// public class AssetFileInfo { - /// The filename, relative to the mod directory - [JsonIgnore] public string Filename { get; set; } // Just mirrors the dictionary key + public AssetRangeInfo Range { get; } + public AssetNode Node { get; } - /// The first 32-bits of the file's SHA256 hash (if specified) - [JsonIgnore] public string Sha256Hash { get; set; } - - /// The alias of the IAssetContainer to use for loading the file - public string Container { get; set; } - - /// The alias of the IAssetLoad to use for loading assets in this file - public string Loader { get; set; } - /// The alias of the IAssetPostProcessor to run on assets in this file after loading - public string Post { get; set; } - - /// The maximum container-index to load from this file. - [JsonInclude] public int? Max { get; private set; } - - /// The mapping from container indices to assets - [JsonInclude] public Dictionary Map { get; private set; } = new(); - - /// Path to an optional JSON file containing the mapping (for cases where the mapping is complicated and would clutter the main assets.json file) - public string MapFile { get; set; } - - /// - /// The set of additional properties that relate to assets in the file - /// - [JsonInclude] - [JsonExtensionData] - public Dictionary Properties { get; private set; } - - /// - /// The default width, in pixels, of frames in images inside the file - /// - public int? Width // For sprites only - { - get => Get(AssetProperty.Width, (int?)null); - set => Set(AssetProperty.Width, value); - } - - /// - /// The default height, in pixels, of frames in images inside the file - /// - public int? Height // For sprites only - { - get => Get(AssetProperty.Height, (int?)null); - set => Set(AssetProperty.Height, value); - } - - internal AssetConfig Config { get; set; } - - public override string ToString() => $"AssetFile: {Filename}{(string.IsNullOrEmpty(Sha256Hash) ? "" : $"#{Sha256Hash}")} ({Map.Count})"; - - /// - /// Retrieve a property's value by name - /// - /// The type to interpret the property value as - /// The property name - /// The value to use if the property does not exist, or cannot be parsed as the requested type - /// The parsed value, or defaultValue if no value existed or could be parsed. - public T Get(string property, T defaultValue) - { - if (Properties == null || !Properties.TryGetValue(property, out var token)) - return defaultValue; - - if (token is JsonElement elem) - { - if (typeof(T) == typeof(string)) return (T)(object)elem.GetString(); - if (typeof(T) == typeof(int)) return (T)(object)elem.GetInt32(); - if (typeof(T) == typeof(long)) return (T)(object)elem.GetInt64(); - if (typeof(T) == typeof(bool)) return (T)(object)elem.GetBoolean(); - } - //if (token is double asDouble) - //{ - // if (typeof(T) == typeof(int)) - // return (T)(object)Convert.ToInt32(asDouble); - - // if (typeof(T) == typeof(int?)) - // return (T)(object)Convert.ToInt32(asDouble); - //} - - if (typeof(T).IsAssignableFrom(typeof(AssetId))) - { - var id = (string)token; - return CastHelper.Cast(ResolveId(id)); - } - - if (typeof(T).IsEnum) - return (T)Enum.Parse(typeof(T), (string)token); - - return (T)token; - } - - /// - /// Set's a property by name - /// - /// The type of value to set - /// The property name - /// The value to set the property to - public void Set(string property, T value) - { - if (value == null) - { - if (Properties == null) - return; - - Properties.Remove(property); - if (Properties.Count == 0) - Properties = null; - } - else - { - Properties ??= new Dictionary(); - Properties[property] = value; - } - } - - void MergeMapping(Dictionary mapping) + public AssetFileInfo(AssetRangeInfo range) { - foreach (var kvp in mapping) - { - if (Map.TryGetValue(kvp.Key, out var info)) - { - kvp.Value.File = this; - info.Id ??= kvp.Value.Id; - if (kvp.Value.Properties != null) - foreach (var prop in kvp.Value.Properties) - info.Set(prop.Key, prop.Value); - } - else - { - info = kvp.Value; - Map[kvp.Key] = info; - } - - info.Index = kvp.Key; - info.File = this; - } + Range = range ?? throw new ArgumentNullException(nameof(range)); + Node = new AssetNode(range.Node); } - /// - /// Uses the given callbacks to determine which assets are actually present in the file in the current file system. - /// - /// The JSON parsing utility - /// A method which takes an AssetFileInfo and returns the list of container id ranges (offsets + lengths) that the file contains. - /// A method which takes a relative filename and returns the file contents as a byte array - /// - /// - public void PopulateAssetIds( - IJsonUtil jsonUtil, - AssetConfig.GetSubItemCountMethod getSubItemCountForFile, - AssetConfig.ReadAllBytesMethod readAllBytesFunc) - { - if (jsonUtil == null) throw new ArgumentNullException(nameof(jsonUtil)); - if (getSubItemCountForFile == null) throw new ArgumentNullException(nameof(getSubItemCountForFile)); - if (readAllBytesFunc == null) throw new ArgumentNullException(nameof(readAllBytesFunc)); - - if (!string.IsNullOrEmpty(MapFile)) - { - var mappingJson = readAllBytesFunc(MapFile); - var mapping = jsonUtil.Deserialize>(mappingJson); - MergeMapping(mapping); - MapFile = null; - } - - AssetInfo last = null; - var ranges = getSubItemCountForFile(this); - if (ranges == null) - return; - - foreach (var asset in Map.Values) - { - last ??= asset; // Let last start off as the first mapped info, in case the range doesn't overlap with the mapped ids. - if (asset.Id == null || !asset.AssetId.IsNone) continue; - asset.AssetId = ResolveId(asset.Id); - } - - foreach (var range in ranges) - { - for (int i = range.RangeStart; i < range.RangeStart + range.RangeLength; i++) - { - if (last != null && i < last.Index) // Don't add any assets below the mapped range - break; - - if (!Map.TryGetValue(i, out var asset)) - { - if (last == null) - continue; - - asset = new AssetInfo(); - Map[i] = asset; - } - - if(last == null && asset.Id == null) - throw new FormatException("The first sub-item in a file's asset mapping must have its Id property set"); - - asset.File = this; - asset.Index = i; - if (asset.Id == null && last != null) - asset.AssetId = new AssetId(last.AssetId.Type, i - last.Index + last.AssetId.Id); - last = asset; - } - } - } + public Dictionary Map { get; } = new(); - internal AssetId ResolveId(string id) => Config.ResolveId(id); + public override string ToString() => $"AssetFile: {Node.Filename}{(string.IsNullOrEmpty(Node.Sha256Hash) ? "" : $"#{Node.Sha256Hash}")}"; } \ No newline at end of file diff --git a/src/Config/AssetInfo.cs b/src/Config/AssetInfo.cs index 996e93d71..c7ba6083a 100644 --- a/src/Config/AssetInfo.cs +++ b/src/Config/AssetInfo.cs @@ -1,98 +1,17 @@ using System; -using System.Collections.Generic; -using System.Text.Json; -using System.Text.Json.Serialization; -using UAlbion.Api; -#pragma warning disable CA2227 // Collection properties should be read only namespace UAlbion.Config; public class AssetInfo { - public string Id { get; set; } // Id of this asset in the mapped enum type. - [JsonInclude, JsonExtensionData] public Dictionary Properties { get; private set; } // Supported: string, bool, int - [JsonIgnore] public AssetId AssetId { get; set; } - [JsonIgnore] public int Index { get; set; } // Sub-asset offset in the container file (or 0 if not inside a container) - [JsonIgnore] public AssetFileInfo File { get; set; } + public AssetId Id { get; } + public AssetFileInfo File { get; } + public AssetNode Node { get; } - public int Width // For sprites only + public AssetInfo(AssetId id, AssetFileInfo file) { - get => Get(AssetProperty.Width, File?.Width ?? 0); - set => Set(AssetProperty.Width, value == 0 ? (object)null : value); + Id = id; + File = file ?? throw new ArgumentNullException(nameof(file)); + Node = new AssetNode(file.Node); } - - public int Height // For sprites only - { - get => Get(AssetProperty.Height, File?.Height ?? 0); - set => Set(AssetProperty.Height, value == 0 ? (object)null : value); - } - - public AssetInfo() { } - public AssetInfo(Dictionary properties) - { - if (properties == null) throw new ArgumentNullException(nameof(properties)); - foreach (var property in properties) - Set(property.Key, property.Value); - } - - public override string ToString() - { - var hashPart = (string.IsNullOrEmpty(File.Sha256Hash) ? "" : $"#{File.Sha256Hash}"); - return $"I:{AssetId} ({File.Filename}{hashPart}.{Index})"; - } - - public AssetPathPattern GetPattern(string property, string defaultValue) - { - var pattern = Get(property, defaultValue); - return AssetPathPattern.Build(pattern); - } - - public T Get(string property, T defaultValue) - { - if (Properties == null || !Properties.TryGetValue(property, out var token)) - return File != null ? File.Get(property, defaultValue) : defaultValue; - - if (token is JsonElement elem) - { - if (typeof(T) == typeof(string)) return (T)(object)elem.GetString(); - if (typeof(T) == typeof(int)) return (T)(object)elem.GetInt32(); - if (typeof(T) == typeof(long)) return (T)(object)elem.GetInt64(); - if (typeof(T) == typeof(bool)) return (T)(object)elem.GetBoolean(); - - if (typeof(T).IsAssignableTo(typeof(IAssetId))) - { - var id = elem.GetString(); - return CastHelper.Cast(File.ResolveId(id)); - } - - if (typeof(T).IsEnum) - return (T)Enum.Parse(typeof(T), elem.GetString() ?? ""); - } - //else if (token is double asDouble) - //{ - // if (typeof(T) == typeof(int)) return (T)(object)Convert.ToInt32(asDouble); - // if (typeof(T) == typeof(int?)) return (T)(object)Convert.ToInt32(asDouble); - //} - - return (T)token; - } - - public void Set(string property, T value) - { - if (value == null) - { - if (Properties == null) - return; - - Properties.Remove(property); - if (Properties.Count == 0) - Properties = null; - } - else - { - Properties ??= new Dictionary(); - Properties[property] = value; - } - } -} -#pragma warning restore CA2227 // Collection properties should be read only \ No newline at end of file +} \ No newline at end of file diff --git a/src/Config/AssetLoadContext.cs b/src/Config/AssetLoadContext.cs new file mode 100644 index 000000000..9d178ce19 --- /dev/null +++ b/src/Config/AssetLoadContext.cs @@ -0,0 +1,39 @@ +using UAlbion.Api; +using UAlbion.Config.Properties; + +namespace UAlbion.Config; + +public record AssetLoadContext( + AssetId AssetId, + AssetNode Node, + ModContext ModContext, + string Language = null) +{ + public T GetProperty(IAssetProperty assetProperty) + => Node.GetProperty(assetProperty); + public T GetProperty(IAssetProperty assetProperty, T defaultValue) + => Node.GetProperty(assetProperty, defaultValue); + public void SetProperty(IAssetProperty assetProperty, T value) + => Node.SetProperty(assetProperty, value); + + public int Index => Node.GetIndex(AssetId); + public string Filename => Node.Filename; + public string Sha256Hash => Node.Sha256Hash; + public string ModName => ModContext.ModName; + public IJsonUtil Json => ModContext.Json; + public IFileSystem Disk => ModContext.Disk; + public AssetMapping Mapping => ModContext.Mapping; + public AssetPath BuildAssetPath(int subAsset = 0, string overrideName = null) + => new(AssetId, subAsset, Node.GetProperty(AssetProps.Palette).Id, overrideName); + + // Common loader / container specific properties + public int Width => Node.Width; // The default width, in pixels, of frames in images inside the file. For sprites only. + public int Height => Node.Height; // The default height, in pixels, of frames in images inside the file. For sprites only. + public AssetId PaletteId => Node.PaletteId; //for providing context when exporting 8-bit images to true-colour PNGs + + public override string ToString() + { + var hashPart = (string.IsNullOrEmpty(Sha256Hash) ? "" : $"#{Sha256Hash}"); + return $"I:{AssetId} ({Filename}{hashPart}.{Index})"; + } +} \ No newline at end of file diff --git a/src/Config/AssetMapping.cs b/src/Config/AssetMapping.cs index d6d8ffabe..7287303f5 100644 --- a/src/Config/AssetMapping.cs +++ b/src/Config/AssetMapping.cs @@ -46,16 +46,13 @@ static T GetAttribute(object x) where T : Attribute // Always built dynamically based on the current set of active mods public static AssetMapping Global => GlobalIsThreadLocal ? ThreadLocalGlobal.Value : TrueGlobal; public static bool GlobalIsThreadLocal { get; set; } // Set to true for unit tests. - readonly Dictionary _nameCache = new(); + readonly Dictionary _idToNameCache = new(); readonly struct Range { - [JsonConstructor] - public Range(int from, int to) { From = from; To = to; } - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public int From { get; } - [JsonIgnore(Condition = JsonIgnoreCondition.Never)] - public int To { get; } + [JsonConstructor] public Range(int from, int to) { From = from; To = to; } + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] public int From { get; } + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] public int To { get; } public override string ToString() => $"{From}:{To}"; } @@ -122,8 +119,7 @@ public EnumInfo(string typeString, AssetType assetType, int? lastMax) .ToArray(); // Keyed by AssetType, a byte enum readonly Dictionary _byEnumType = new(); - readonly Dictionary> _byName = new(); - readonly Dictionary _stringLookup = new(); + readonly Dictionary> _byName = new(); public AssetMapping() { } AssetMapping(Dictionary byEnumType) @@ -217,7 +213,7 @@ public string IdToName(AssetId id) if (id == AssetId.None) // Special case to keep things tidy return "None"; - if (_nameCache.TryGetValue(id, out var name)) + if (_idToNameCache.TryGetValue(id, out var name)) return name; var (enumType, enumValue) = IdToEnum(id); @@ -233,7 +229,7 @@ public string IdToName(AssetId id) : enumType.Name + "." + enumName; } - _nameCache[id] = name; + _idToNameCache[id] = name; return name; } @@ -263,7 +259,7 @@ public AssetId EnumToId(T id) where T : unmanaged, Enum } } - public AssetId EnumToId((Type, int) value) => EnumToId(value.Item1, value.Item2); + public AssetId EnumToId((Type Type, int Value) value) => EnumToId(value.Type, value.Value); public AssetId EnumToId(Type enumType, int enumValue) { if (enumType == null) @@ -297,16 +293,16 @@ public AssetId EnumToId(Type enumType, string value) throw new FormatException($"Could not parse a value of type \"{enumType}\", as it has not been registered in the asset mapping"); if (int.TryParse(value, out int intValue)) - return new AssetId(enumInfo.AssetType, intValue); + return new AssetId(enumInfo.AssetType, enumInfo.Offset + intValue); if (!_byName.TryGetValue(value.ToUpperInvariant(), out var matches)) throw new FormatException($"Could not parse id \"{value}\" as \"{enumType}\": no such value"); - var match = matches.FirstOrDefault(x => x.Item1 == enumInfo); + var match = matches.FirstOrDefault(x => x.Info == enumInfo); if (match == (null, 0)) throw new FormatException($"Could not parse id \"{value}\" as \"{enumType}\": no such value"); - return new AssetId(match.Item1.AssetType, match.Item2); + return new AssetId(match.Info.AssetType, match.Value); } public AssetMapping Clear() @@ -358,26 +354,13 @@ void RegisterNames(EnumInfo info) var key = value.Item1.ToUpperInvariant(); if (!_byName.TryGetValue(key, out var entries)) { - entries = new List<(EnumInfo, int)>(); + entries = new List<(EnumInfo Info, int Value)>(); _byName[key] = entries; } entries.Add((info, value.Item2 + info.Offset)); } } - public AssetMapping RegisterStringRedirect(Type enumType, AssetId target, int min, int max, int offset) - { - foreach (var id in EnumerateAssetsOfType(enumType)) - { - var (_, numeric) = IdToEnum(id); - if (numeric < min || numeric > max) - continue; - - _stringLookup[id] = (target, (ushort)(offset + numeric - min)); - } - return this; - } - public void ConsistencyCheck() { (string, string) Describe(AssetType assetType) @@ -421,11 +404,6 @@ public void ConsistencyCheck() } } - public (AssetId, ushort)? TextIdToStringId(AssetId id) - => _stringLookup.ContainsKey(id) - ? _stringLookup[id] - : ((AssetId, ushort)?)null; - public IEnumerable EnumerateAssetsOfType(AssetType type) { foreach (var info in _byAssetType[(byte)type]) // Nested for-loops go brrr @@ -475,9 +453,6 @@ public void MergeFrom(AssetMapping other) if (!_byEnumType.ContainsKey(info.EnumType)) RegisterAssetType(info.EnumTypeString, info.AssetType); } - - foreach (var redirect in other._stringLookup) - _stringLookup[redirect.Key] = redirect.Value; } public AssetId Parse(string s, AssetType[] validTypes) // pass null for validTypes to allow any type @@ -499,17 +474,17 @@ public void MergeFrom(AssetMapping other) : ParseNumeric(s, typeName, valueName, validTypes); } - static AssetId ParseTextual(string s, string typeName, IList<(EnumInfo, int)> matches, AssetType[] validTypes) + static AssetId ParseTextual(string s, string typeName, IList<(EnumInfo Info, int Value)> matches, AssetType[] validTypes) { AssetId result = new AssetId(AssetType.Unknown); foreach (var match in matches) { - if (validTypes != null && !validTypes.Contains(match.Item1.AssetType)) + if (validTypes != null && !validTypes.Contains(match.Info.AssetType)) continue; if (!string.IsNullOrEmpty(typeName) - && !match.Item1.EnumType.Name.Equals(typeName, StringComparison.OrdinalIgnoreCase) - && !match.Item1.AssetType.ToString().Equals(typeName, StringComparison.OrdinalIgnoreCase)) + && !match.Info.EnumType.Name.Equals(typeName, StringComparison.OrdinalIgnoreCase) + && !match.Info.AssetType.ToString().Equals(typeName, StringComparison.OrdinalIgnoreCase)) { continue; } @@ -518,13 +493,13 @@ static AssetId ParseTextual(string s, string typeName, IList<(EnumInfo, int)> ma { var candidates = string.Join(", ", matches - .Where(x => validTypes == null || validTypes.Contains(x.Item1.AssetType)) - .Select(x => x.Item1.EnumType.Name)); + .Where(x => validTypes == null || validTypes.Contains(x.Info.AssetType)) + .Select(x => x.Info.EnumType.Name)); throw new FormatException($"Could not unambiguously parse \"{s}\" as an asset id. Candidate types: {candidates}"); } - result = new AssetId(match.Item1.AssetType, match.Item2); + result = new AssetId(match.Info.AssetType, match.Value); } if (result.Type == AssetType.Unknown) @@ -562,4 +537,14 @@ static AssetId ParseTextual(string s, string typeName, IList<(EnumInfo, int)> ma return result; } + + public AssetId MaxIdForType(Type type) + { + if (type == null) throw new ArgumentNullException(nameof(type)); + if (!_byEnumType.TryGetValue(type, out var result)) + throw new FormatException($"Could not get max id for unmapped type \"{type.FullName}\""); + + var max = result.EnumMax; + return EnumToId(type, max); + } } \ No newline at end of file diff --git a/src/Config/AssetNode.cs b/src/Config/AssetNode.cs new file mode 100644 index 000000000..c2423d896 --- /dev/null +++ b/src/Config/AssetNode.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; +using UAlbion.Config.Properties; + +namespace UAlbion.Config; + +public class AssetNode +{ + readonly AssetId _startOfRangeId; + readonly AssetNode _parent; + bool _frozen; + + public AssetNode(AssetId startOfRangeId) => _startOfRangeId = startOfRangeId; + public AssetNode(AssetNode parent) + { + _parent = parent ?? throw new ArgumentNullException(nameof(parent)); + _startOfRangeId = parent._startOfRangeId; + } + + /// + /// The set of additional properties that relate to assets in the file + /// + [JsonInclude] + [JsonExtensionData] + public Dictionary Properties { get; private set; } + + public int GetIndex(AssetId id) + { + if (id.Type != _startOfRangeId.Type) + throw new InvalidOperationException($"AssetId ({id}) of incorrect type passed to AssetRangeInfo.GetIndex (range {this})"); + + return id.Id - _startOfRangeId.Id; + } + + /// + /// Retrieve a property's value + /// + /// The type to interpret the property value as + /// The property to retrieve + /// The parsed value, or defaultValue if no value existed or could be parsed. + public T GetProperty(IAssetProperty assetProperty) + { + if (assetProperty == null) throw new ArgumentNullException(nameof(assetProperty)); + return GetProperty(assetProperty, assetProperty.DefaultValue); + } + + /// + /// Retrieve a property's value + /// + /// The type to interpret the property value as + /// The property to retrieve + /// The default value to use when the property is not set + /// The parsed value, or defaultValue if no value existed or could be parsed. + public T GetProperty(IAssetProperty assetProperty, T defaultValue) + { + if (assetProperty == null) throw new ArgumentNullException(nameof(assetProperty)); + var name = assetProperty.Name; + + object token = null; + Properties?.TryGetValue(name, out token); + + if (token == null) + { + return _parent != null + ? _parent.GetProperty(assetProperty) + : defaultValue; + } + + return (T)token; + } + + /// + /// Set's a property by name + /// + /// The property + /// The value to set the property to + /// The type config + public void SetProperty(IAssetProperty assetProperty, JsonElement value, TypeConfig typeConfig) + { + if (assetProperty == null) throw new ArgumentNullException(nameof(assetProperty)); + if (_frozen) throw new InvalidOperationException("Tried to modify asset node after it was frozen. Asset nodes are immutable after mods have been loaded."); + + try + { + Properties ??= new Dictionary(); + Properties[assetProperty.Name] = assetProperty.FromJson(value, typeConfig); + } + catch (Exception ex) + { + var message = $@"Error setting property {assetProperty.Name} to {value} ({assetProperty.GetType().Name}): + {ex.Message}"; + throw new AssetConfigLoadException(message, ex); + } + } + + public void SetProperty(IAssetProperty assetProperty, T value) + { + if (assetProperty == null) throw new ArgumentNullException(nameof(assetProperty)); + if (_frozen) throw new InvalidOperationException("Tried to modify asset node after it was frozen. Asset nodes are immutable after mods have been loaded."); + Properties ??= new Dictionary(); + Properties[assetProperty.Name] = value; + } + + public void SetProperties(IReadOnlyDictionary dict, TypeConfig typeConfig, string contextName, object context) + { + if (dict == null) throw new ArgumentNullException(nameof(dict)); + if (typeConfig == null) throw new ArgumentNullException(nameof(typeConfig)); + + // The loader and container can change the set of valid properties, so we have to make sure those are set first. + foreach (var kvp in dict) + { + if (kvp.Key != AssetProps.Loader.Name && kvp.Key != AssetProps.Container.Name) + continue; + + var assetProperty = typeConfig.GetAssetProperty(kvp.Key, this); + if (assetProperty == null) + throw new AssetConfigLoadException($"Unexpected property \"{kvp.Key}\" encountered when loading {contextName} \"{context}\" - did not match any registered global property or any exposed by the asset's loader/container"); + + SetProperty(assetProperty, kvp.Value, typeConfig); + } + + foreach (var kvp in dict) + { + if (kvp.Key == AssetProps.Loader.Name || kvp.Key == AssetProps.Container.Name) + continue; + + var assetProperty = typeConfig.GetAssetProperty(kvp.Key, this); + if (assetProperty == null) + throw new AssetConfigLoadException($"Unexpected property \"{kvp.Key}\" encountered when loading {contextName} \"{context}\" - did not match any registered global property or any exposed by the asset's loader/container"); + + SetProperty(assetProperty, kvp.Value, typeConfig); + } + + _frozen = true; + } + + // Asset loading control props + public string Filename => GetProperty(AssetProps.Filename); + public string Sha256Hash => GetProperty(AssetProps.Sha256Hash); + public Type Container => GetProperty(AssetProps.Container); // The alias of the IAssetContainer to use for loading the file + public Type Loader => GetProperty(AssetProps.Loader); // The alias of the IAssetLoader to use for loading assets in this file + public Type PostProcessor => GetProperty(AssetProps.Post); // The alias of the IAssetPostProcessor to use for loading assets in this file + public bool IsReadOnly => GetProperty(AssetProps.IsReadOnly); // To prevent zeroing out files when repacking formats that don't have writing code yet, e.g. ILBM images + + // Common loader / container specific properties + public int Width => GetProperty(AssetProps.Width); // The default width, in pixels, of frames in images inside the file. For sprites only. + public int Height => GetProperty(AssetProps.Height); // The default height, in pixels, of frames in images inside the file. For sprites only. + public AssetId PaletteId => GetProperty(AssetProps.Palette); // for providing context when exporting 8-bit images to true-colour PNGs +} \ No newline at end of file diff --git a/src/Config/AssetPath.cs b/src/Config/AssetPath.cs index 38e8d9426..87bcda409 100644 --- a/src/Config/AssetPath.cs +++ b/src/Config/AssetPath.cs @@ -4,38 +4,26 @@ namespace UAlbion.Config; public readonly struct AssetPath : IEquatable { - public int Index { get; } + public AssetId AssetId { get; } public int SubAsset { get; } public int? PaletteId { get; } public int? PaletteFrame { get; } public string Name { get; } - public override string ToString() => $"{Index}:{SubAsset}{(PaletteId==null ? "" : " P"+PaletteId)} {Name}"; - public AssetPath(int index, int subAsset = 0, int? paletteId = null, string overrideName = null, int? paletteFrame = null) + public override string ToString() => $"{AssetId.Id}:{SubAsset}{(PaletteId == null ? "" : " P" + PaletteId)} {Name}"; + + public AssetPath(AssetId id, int subAsset = 0, int? paletteId = null, string overrideName = null, int? paletteFrame = null) { - Index = index; + AssetId = id; + Name = overrideName ?? ConfigUtil.AssetName(id); SubAsset = subAsset; PaletteId = paletteId; - Name = overrideName; PaletteFrame = paletteFrame; } - public AssetPath(AssetInfo info, int subAsset = 0, string overrideName = null) - { - if (info == null) throw new ArgumentNullException(nameof(info)); - Index = info.Index; - SubAsset = subAsset; - Name = string.IsNullOrEmpty(overrideName) ? ConfigUtil.AssetName(info.AssetId) : overrideName; - PaletteId = info.Get(AssetProperty.PaletteId, -1); - if (PaletteId == -1) - PaletteId = null; - - PaletteFrame = null; - } - public override bool Equals(object obj) => obj is AssetPath other && Equals(other); - public bool Equals(AssetPath other) => Index == other.Index && SubAsset == other.SubAsset && PaletteId == other.PaletteId && Name == other.Name; + public bool Equals(AssetPath other) => AssetId == other.AssetId && SubAsset == other.SubAsset && PaletteId == other.PaletteId && Name == other.Name; public static bool operator ==(AssetPath left, AssetPath right) => left.Equals(right); public static bool operator !=(AssetPath left, AssetPath right) => !(left == right); - public override int GetHashCode() => HashCode.Combine(Index, SubAsset, PaletteId, Name); + public override int GetHashCode() => HashCode.Combine(AssetId, SubAsset, PaletteId, Name); } \ No newline at end of file diff --git a/src/Config/AssetPathPattern.cs b/src/Config/AssetPathPattern.cs index 253dd6ae0..dd15fae21 100644 --- a/src/Config/AssetPathPattern.cs +++ b/src/Config/AssetPathPattern.cs @@ -16,8 +16,8 @@ public class AssetPathPattern enum PartType { + Id, Text, - Index, SubAsset, Name, Palette, @@ -38,9 +38,8 @@ public Part(string name, string value) Type = name.ToUpperInvariant() switch { - "0" => PartType.Index, - "ID" => PartType.Index, - "INDEX" => PartType.Index, + "0" => PartType.Id, + "ID" => PartType.Id, "1" => PartType.SubAsset, "FRAME" => PartType.SubAsset, @@ -144,13 +143,13 @@ public static AssetPathPattern Build(string pattern) { switch (part.Type) { - case PartType.Text: sb.Append(Regex.Escape(part.Value)); break; - case PartType.Index: sb.Append(@"(?\d+)"); break; - case PartType.SubAsset: sb.Append(@"(?\d+)"); break; - case PartType.Name: sb.Append(@"(?\w+)"); break; - case PartType.Palette: sb.Append(@"(?\d+)"); break; + case PartType.Id: sb.Append(@"(?\d+)"); break; + case PartType.Text: sb.Append(Regex.Escape(part.Value)); break; + case PartType.SubAsset: sb.Append(@"(?\d+)"); break; + case PartType.Name: sb.Append(@"(?\w+)"); break; + case PartType.Palette: sb.Append(@"(?\d+)"); break; case PartType.PaletteFrame: sb.Append(@"(?\d+)"); break; - case PartType.IgnoreNum: sb.Append(@"\d+"); break; + case PartType.IgnoreNum: sb.Append(@"\d+"); break; } } @@ -162,7 +161,6 @@ static string FormatInt(int v, string format) => ? v.ToString(format, CultureInfo.InvariantCulture) : v.ToString(CultureInfo.InvariantCulture); - public string Format(AssetInfo info) => Format(new AssetPath(info)); public string Format(in AssetPath path) { var sb = new StringBuilder(); @@ -170,9 +168,9 @@ public string Format(in AssetPath path) { switch (part.Type) { - case PartType.Text: sb.Append(part.Value); break; - case PartType.Index: sb.Append(FormatInt(path.Index, part.Value)); break; + case PartType.Id: sb.Append(FormatInt(path.AssetId.Id, part.Value)); break; case PartType.SubAsset: sb.Append(FormatInt(path.SubAsset, part.Value)); break; + case PartType.Text: sb.Append(part.Value); break; case PartType.Name: sb.Append(path.Name); break; case PartType.IgnoreNum: sb.Append('0'); break; case PartType.Palette: @@ -189,7 +187,7 @@ public string Format(in AssetPath path) return sb.ToString(); } - public bool TryParse(string filename, out AssetPath path) + public bool TryParse(string filename, AssetType type, out AssetPath path) { var m = _regex.Match(filename); if (!m.Success) @@ -198,28 +196,28 @@ public bool TryParse(string filename, out AssetPath path) return false; } - var indexGroup = m.Groups["Index"]; + var idGroup = m.Groups["Id"]; var subAssetGroup = m.Groups["SubAsset"]; var paletteGroup = m.Groups["Palette"]; var pframeGroup = m.Groups["PFrame"]; - int index = indexGroup.Success ? int.Parse(indexGroup.Value) : -1; + int id = idGroup.Success ? int.Parse(idGroup.Value) : -1; int subAsset = subAssetGroup.Success ? int.Parse(subAssetGroup.Value) : 0; int? paletteId = paletteGroup.Success ? (int?)int.Parse(paletteGroup.Value) : null; int? paletteFrame = pframeGroup.Success ? (int?)int.Parse(pframeGroup.Value) : null; - path = new AssetPath(index, subAsset, paletteId, m.Groups["Name"].Value, paletteFrame); + path = new AssetPath(new AssetId(type, id), subAsset, paletteId, m.Groups["Name"].Value, paletteFrame); return true; } - public string WilcardForIndex(int index) + public string WilcardForId(AssetId id) { var sb = new StringBuilder(); foreach (var part in _parts) { switch (part.Type) { + case PartType.Id: sb.Append(FormatInt(id.Id, part.Value)); break; case PartType.Text: sb.Append(part.Value); break; - case PartType.Index: sb.Append(FormatInt(index, part.Value)); break; case PartType.SubAsset: sb.Append('*'); break; case PartType.Name: sb.Append('*'); break; case PartType.IgnoreNum: sb.Append('*'); break; diff --git a/src/Config/AssetProperties.cs b/src/Config/AssetProperties.cs new file mode 100644 index 000000000..e4ca1a3f8 --- /dev/null +++ b/src/Config/AssetProperties.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace UAlbion.Config; + +public class AssetProperties +{ + readonly Dictionary> _properties = new(); // loader/container specific properties + readonly Dictionary _globalProperties = new(); + readonly AssetProperties _parent; + + public AssetProperties(AssetProperties parent) + => _parent = parent; + + public IAssetProperty GetGlobalProperty(string name) + { + _globalProperties.TryGetValue(name, out var globalProperty); + return globalProperty; + } + + public IAssetProperty GetProperty(string name, Type context) + { + if (context == null) + return null; + + _properties.TryGetValue(context, out var dict); + if (dict == null) + return null; + + dict.TryGetValue(name, out var assetProperty); + return assetProperty; + } + + public void LoadAssetPropertiesFromType(bool loadGlobal, Type type) + { + if (type == null) throw new ArgumentNullException(nameof(type)); + var properties = type.GetProperties(BindingFlags.Static | BindingFlags.Public); + foreach (var property in properties) + { + if (!property.PropertyType.IsAssignableTo(typeof(IAssetProperty))) continue; + var instance = (IAssetProperty)property.GetValue(null); + if (instance == null) continue; + + if (loadGlobal) + AddGlobalProperty(instance); + else + AddProperty(type, instance); + } + + var fields = type.GetFields(BindingFlags.Static | BindingFlags.Public); + foreach (var field in fields) + { + if (!field.FieldType.IsAssignableTo(typeof(IAssetProperty))) continue; + var instance = (IAssetProperty)field.GetValue(null); + if (instance == null) continue; + + if (loadGlobal) + AddGlobalProperty(instance); + else + AddProperty(type, instance); + } + } + + void AddProperty(Type type, IAssetProperty assetProperty) + { + _properties.TryGetValue(type, out var dict); + if (dict == null) + { + dict = new Dictionary(); + _properties[type] = dict; + } + + if (dict.ContainsKey(assetProperty.Name)) + throw new InvalidOperationException($"Tried to registry property \"{assetProperty.Name}\" for type {type}, but another property is already registered with that name"); + + if (_parent?.GetProperty(assetProperty.Name, type) != null) + throw new InvalidOperationException($"Tried to registry property \"{assetProperty.Name}\" for type {type}, but another property is already registered with that name in an inherited mod's type config"); + + dict[assetProperty.Name] = assetProperty; + } + + void AddGlobalProperty(IAssetProperty assetProperty) + { + if (_globalProperties.ContainsKey(assetProperty.Name)) + throw new InvalidOperationException($"Tried to registry property \"{assetProperty.Name}\" globally, but another property is already registered with that name"); + + if (_parent?._globalProperties.ContainsKey(assetProperty.Name) == true) + throw new InvalidOperationException($"Tried to registry property \"{assetProperty.Name}\" globally, but another property is already registered with that name in an inherited mod's type config"); + + _globalProperties[assetProperty.Name] = assetProperty; + } +} \ No newline at end of file diff --git a/src/Config/AssetProperty.cs b/src/Config/AssetProperty.cs deleted file mode 100644 index 9c65914d0..000000000 --- a/src/Config/AssetProperty.cs +++ /dev/null @@ -1,64 +0,0 @@ -namespace UAlbion.Config; - -public static class AssetProperty -{ - // General - public const string Language = "Language"; // string - public const string Offset = "Offset"; // int, used for BinaryOffsetContainer, e.g. MAIN.EXE - public const string Pattern = "Pattern"; // string, mostly for DirectoryContainer - public const string MinimumCount = "MinimumCount"; // Just used to get closer to 1:1 round-tripping of XLDs - public const string IsReadOnly = "IsReadOnly"; // To prevent zeroing out files when repacking formats that don't have writing code yet, e.g. ILBM images - public const string Optional = "Optional"; // bool, will suppress missing-asset warnings when true - - // Textures - public const string Width = "Width"; // int - public const string Height = "Height"; // int - public const string PaletteId = "PaletteId"; // int, for providing context when exporting 8-bit images to true-colour PNGs - public const string SubSprites = "SubSprites"; // string - public const string Transposed = "Transposed"; // bool, for various textures in the 3D world that are stored with rows/columns flipped - public const string ExtraBytes = "ExtraBytes"; // int, used to suppress assertions when loading original assets that have incorrect sizes - - // Palette - public const string IsCommon = "IsCommon"; // bool - public const string AnimatedRanges = "AnimatedRanges"; // string (e.g. "0x1-0xf, 0x12-0x1a") - - // 32-bit tileset gfx - public const string DayPath = "DayPath"; // string - public const string NightPath = "NightPath"; // string - - // 2D Tilesets - public const string BlankTilePath = "BlankTilePath"; // string - public const string GraphicsPattern = "GraphicsPattern"; // string - public const string UseSmallGraphics = "UseSmallGraphics"; // bool - - // Maps - public const string LargeNpcs = "LargeNpcs"; // string - public const string SmallNpcs = "SmallNpcs"; // string - public const string TilesetPattern = "TilesetPattern"; // string - public const string ScriptPattern = "ScriptPattern"; // string - - // NPC tileset - public const string IsSmall = "IsSmall"; // bool - - // Isometric tileset/map properties - public const string BaseHeight = "BaseHeight"; // int - public const string CeilingPngPattern = "CeilingPngPattern"; // string - public const string ContentsPngPattern = "ContentsPngPattern"; // string - public const string FloorPngPattern = "FloorPngPattern"; // string - public const string TileHeight = "TileHeight"; // int - public const string TileWidth = "TileWidth"; // int - public const string TiledCeilingPattern = "TiledCeilingPattern"; // string - public const string TiledContentsPattern = "TiledContentsPattern"; // string - public const string TiledFloorPattern = "TiledFloorPattern"; // string - public const string TiledWallPattern = "TiledWallPattern"; // string - public const string TilesPerRow = "TilesPerRow"; // int - public const string WallPngPattern = "WallPngPattern"; // string - - // Spells - public const string Name = "Name"; // StringId to use in game - public const string MagicSchool = "School"; // SpellClass enum - public const string SpellNumber = "SpellNumber"; // offset into school, used for save-game serialization - - // Songs - public const string WaveLib = "WaveLib"; -} \ No newline at end of file diff --git a/src/Config/AssetRange.cs b/src/Config/AssetRange.cs new file mode 100644 index 000000000..c0219d2e6 --- /dev/null +++ b/src/Config/AssetRange.cs @@ -0,0 +1,37 @@ +using System.Collections; +using System.Collections.Generic; + +namespace UAlbion.Config; + +public readonly record struct AssetRange(AssetId From, AssetId To) : IEnumerable +{ + public override string ToString() => $"{From}-{To} [{From.Id}-{To.Id}]"; + public IEnumerator GetEnumerator() => new RangeEnumerator(this); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + class RangeEnumerator : IEnumerator + { + readonly AssetRange _range; + public RangeEnumerator(AssetRange range) => _range = range; + + public void Reset() => Current = AssetId.None; + public AssetId Current { get; private set; } + object IEnumerator.Current => Current; + public void Dispose() { } + + public bool MoveNext() + { + if (Current >= _range.To) + return false; + + if (Current.IsNone) + { + Current = _range.From; + return true; + } + + Current = new AssetId(Current.Type, Current.Id + 1); + return true; + } + } +}; \ No newline at end of file diff --git a/src/Config/AssetRangeInfo.cs b/src/Config/AssetRangeInfo.cs new file mode 100644 index 000000000..560eac4dc --- /dev/null +++ b/src/Config/AssetRangeInfo.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace UAlbion.Config; + +public class AssetRangeInfo +{ + public AssetRange Range { get; } + public AssetNode Node { get; } + public int Sequence { get; } + public List Files { get; } = new(); + public AssetRangeInfo(AssetRange range, int sequence) + { + Range = range; + Sequence = sequence; + Node = new AssetNode(range.From); + } + + public override string ToString() => $"{Range} ({Files.Count} files)"; +} \ No newline at end of file diff --git a/src/Config/AssetType.cs b/src/Config/AssetType.cs index 103de3e4c..aa2b31046 100644 --- a/src/Config/AssetType.cs +++ b/src/Config/AssetType.cs @@ -13,6 +13,7 @@ public enum AssetType : byte { [Unmapped] None = 0, // Must be 0 so default(AssetId) will equate to AssetId.None + // Various kinds of SpriteId AutomapGfx, BackgroundGfx, CombatBackground, @@ -36,31 +37,39 @@ public enum AssetType : byte Wall, WallOverlay, + // Text rendering Ink, FontDefinition, [Unmapped] MetaFont, + // Environmental objects that are setup on new game and have their state tracked in the save files Chest, Merchant, Door, + // Scripts EventSet, Script, + // Inventory items [Unmapped] Gold, [Unmapped] Rations, Item, + // 3D template Labyrinth, + // Actual map data Map, [IsomorphicTo(Map)] Automap, + // Character definitions PartyMember, [IsomorphicTo(PartyMember)] PartySheet, MonsterSheet, NpcSheet, + // Misc MonsterGroup, [Unmapped] ObjectGroup, Palette, @@ -74,16 +83,21 @@ public enum AssetType : byte [IsomorphicTo(Tileset)] BlockList, [Localised] Video, - [Localised] [IsomorphicTo(EventSet)] EventText, - [Localised] [IsomorphicTo(Map)] MapText, + // Individual strings [Localised] [IsomorphicTo(Item)] ItemName, [Localised] Text, [Localised] Word, - [Unmapped] MapTextIndex = 250, // Used for NPCs with the SimpleMsg flag - [Unmapped] PromptNumber = 251, // Used for DialogueLine actions - [Unmapped] LocalNpc = 252, // For identifying NPCs in a map by their slot number - Target = 253, // For targeting DataChangeEvents etc - Special = 254, // For various types that only have a single value + // String sets + [Localised] [IsomorphicTo(EventSet)] EventText, + [Localised] [IsomorphicTo(Map)] MapText, + + Special, // For various types that only have a single value, can be a string set + + [Unmapped] MapTextIndex, // Used for NPCs with the SimpleMsg flag + [Unmapped] PromptNumber, // Used for DialogueLine actions + [Unmapped] LocalNpc, // For identifying NPCs in a map by their slot number + Target, // For targeting DataChangeEvents etc + [Unmapped] Unknown = 255 } \ No newline at end of file diff --git a/src/Config/ConfigUtil.cs b/src/Config/ConfigUtil.cs index 553ba2902..9f0c7ea34 100644 --- a/src/Config/ConfigUtil.cs +++ b/src/Config/ConfigUtil.cs @@ -8,7 +8,7 @@ namespace UAlbion.Config; public static class ConfigUtil { - static readonly string ProbePath = Path.Combine("mods", "Base", "base_assets.json"); + static readonly string ProbePath = Path.Combine("mods", "Base", "types.json"); public static string FindBasePath(IFileSystem disk) // Should roughly match UAlbion.Scripting.Tests.TestUtil { if (disk == null) throw new ArgumentNullException(nameof(disk)); diff --git a/src/Config/IAssetConfig.cs b/src/Config/IAssetConfig.cs index 83f2f065e..d251bd0e4 100644 --- a/src/Config/IAssetConfig.cs +++ b/src/Config/IAssetConfig.cs @@ -1,6 +1,8 @@ -namespace UAlbion.Config; +using System.Collections.Generic; + +namespace UAlbion.Config; public interface IAssetConfig { - AssetInfo[] GetAssetInfo(AssetId id); + IEnumerable GetAssetInfo(AssetId id); } \ No newline at end of file diff --git a/src/Formats/Containers/IAssetContainer.cs b/src/Config/IAssetContainer.cs similarity index 54% rename from src/Formats/Containers/IAssetContainer.cs rename to src/Config/IAssetContainer.cs index b03b3cdad..55a9c6b4f 100644 --- a/src/Formats/Containers/IAssetContainer.cs +++ b/src/Config/IAssetContainer.cs @@ -1,8 +1,7 @@ using System.Collections.Generic; using SerdesNet; -using UAlbion.Config; -namespace UAlbion.Formats.Containers; +namespace UAlbion.Config; /// /// Abstraction over container files/directories containing assets @@ -15,10 +14,9 @@ public interface IAssetContainer /// Open a serializer for one of the assets inside a container /// /// The path to the container file/directory - /// The config metadata for the asset /// The loader context, containing the JSON serialiser, file system access object etc /// - ISerializer Read(string path, AssetInfo info, SerdesContext context); + ISerializer Read(string path, AssetLoadContext context); /// /// Write all assets inside a container @@ -26,14 +24,5 @@ public interface IAssetContainer /// The path to the container file/directory /// A list of pairs containing asset metadata and the corresponding raw bytes of the asset /// The loader context, containing the JSON serialiser, file system access object etc - void Write(string path, IList<(AssetInfo, byte[])> assets, SerdesContext context); - - /// - /// Open the container and return the sub-item ranges that are present inside it. - /// - /// The path to the container file/directory - /// The config metadata for the container - /// The loader context, containing the JSON serialiser, file system access object etc - /// A list of range pairs: (subItemId of start of range, count of ids in range) - List<(int num, int count)> GetSubItemRanges(string path, AssetFileInfo info, SerdesContext context); + void Write(string path, IList<(AssetLoadContext, byte[])> assets, ModContext context); } \ No newline at end of file diff --git a/src/Config/IAssetLoader.cs b/src/Config/IAssetLoader.cs new file mode 100644 index 000000000..6afa33627 --- /dev/null +++ b/src/Config/IAssetLoader.cs @@ -0,0 +1,13 @@ +using SerdesNet; + +namespace UAlbion.Config; + +public interface IAssetLoader +{ + object Serdes(object existing, ISerializer s, AssetLoadContext context); +} + +public interface IAssetLoader : IAssetLoader where T : class +{ + T Serdes(T existing, ISerializer s, AssetLoadContext context); // SerDes = Serialise / Deserialise. +} \ No newline at end of file diff --git a/src/Config/IAssetPostProcessor.cs b/src/Config/IAssetPostProcessor.cs new file mode 100644 index 000000000..6d0e3f852 --- /dev/null +++ b/src/Config/IAssetPostProcessor.cs @@ -0,0 +1,6 @@ +namespace UAlbion.Config; + +public interface IAssetPostProcessor +{ + object Process(object asset, AssetLoadContext context); +} \ No newline at end of file diff --git a/src/Config/IAssetProperty.cs b/src/Config/IAssetProperty.cs new file mode 100644 index 000000000..4e654b92d --- /dev/null +++ b/src/Config/IAssetProperty.cs @@ -0,0 +1,14 @@ +using System.Text.Json; + +namespace UAlbion.Config; + +public interface IAssetProperty +{ + string Name { get; } + object FromJson(JsonElement elem, TypeConfig config); +} + +public interface IAssetProperty : IAssetProperty +{ + T DefaultValue { get; } +} \ No newline at end of file diff --git a/src/Config/LoadAssetFileInfo.cs b/src/Config/LoadAssetFileInfo.cs new file mode 100644 index 000000000..cd3b1bc37 --- /dev/null +++ b/src/Config/LoadAssetFileInfo.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace UAlbion.Config; + +public class LoadAssetFileInfo +{ + public string MapFile { get; set; } + public Dictionary Map { get; set; } + + [JsonInclude, JsonExtensionData] + public Dictionary Properties { get; private set; } +} \ No newline at end of file diff --git a/src/Config/LoadAssetInfo.cs b/src/Config/LoadAssetInfo.cs new file mode 100644 index 000000000..433b3c09d --- /dev/null +++ b/src/Config/LoadAssetInfo.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace UAlbion.Config; + +public class LoadAssetInfo +{ + [JsonInclude, JsonExtensionData] + public Dictionary Properties { get; private set; } +} \ No newline at end of file diff --git a/src/Config/LoadAssetRangeInfo.cs b/src/Config/LoadAssetRangeInfo.cs new file mode 100644 index 000000000..e8d1ca3f5 --- /dev/null +++ b/src/Config/LoadAssetRangeInfo.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace UAlbion.Config; + +public class LoadAssetRangeInfo +{ + public int? Sequence { get; set; } + public Dictionary Files { get; set; } = new(); + [JsonInclude] [JsonExtensionData] public Dictionary Properties { get; private set; } +} \ No newline at end of file diff --git a/src/Config/ModConfig.cs b/src/Config/ModConfig.cs index 8bb463907..3171e6bb9 100644 --- a/src/Config/ModConfig.cs +++ b/src/Config/ModConfig.cs @@ -15,8 +15,10 @@ public class ModConfig public string Author { get; set; } public Version Version { get; set; } public string ShaderPath { get; set; } - public string AssetConfig { get; set; } = "assets.json"; + public string AssetConfig { get; set; } + public string TypeConfig { get; set; } public string InheritAssetConfigFrom { get; set; } + public string InheritTypeConfigFrom { get; set; } [JsonInclude] public List Dependencies { get; private set; } = new(); [JsonInclude] public Dictionary SymLinks { get; private set; } = new(); diff --git a/src/Formats/SerdesContext.cs b/src/Config/ModContext.cs similarity index 72% rename from src/Formats/SerdesContext.cs rename to src/Config/ModContext.cs index 7e6b86cb4..d0d94bc58 100644 --- a/src/Formats/SerdesContext.cs +++ b/src/Config/ModContext.cs @@ -1,17 +1,16 @@ using System; using UAlbion.Api; -using UAlbion.Config; -namespace UAlbion.Formats; +namespace UAlbion.Config; -public class SerdesContext +public class ModContext { - public SerdesContext(string modName, IJsonUtil json, AssetMapping mapping, IFileSystem disk) + public ModContext(string modName, IJsonUtil json, IFileSystem disk, AssetMapping mapping) { ModName = modName; Json = json ?? throw new ArgumentNullException(nameof(json)); - Mapping = mapping ?? throw new ArgumentNullException(nameof(mapping)); Disk = disk ?? throw new ArgumentNullException(nameof(disk)); + Mapping = mapping ?? throw new ArgumentNullException(nameof(mapping)); } public string ModName { get; } diff --git a/src/Config/PathResolver.cs b/src/Config/PathResolver.cs index 1cfd3fcaa..7d3ea0ea3 100644 --- a/src/Config/PathResolver.cs +++ b/src/Config/PathResolver.cs @@ -78,14 +78,13 @@ static string GetConfigBaseDir() if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { var configHome = Environment.GetEnvironmentVariable("XDG_CONFIG_HOME"); if (configHome == null) { - var home = Environment.GetEnvironmentVariable("HOME"); - if (home != null) - configHome = Path.Combine(home, ".config"); + var home = Environment.GetFolderPath(Environment.SpecialFolder.Personal); + configHome = Path.Combine(home, ".config"); } if (configHome == null) @@ -97,6 +96,12 @@ static string GetConfigBaseDir() return configHome; } + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + var home = Environment.GetFolderPath(Environment.SpecialFolder.Personal); + return Path.Combine(home, "Library", "Application Support"); + } + throw new NotSupportedException(); } @@ -105,7 +110,7 @@ static string GetCacheBaseDir() if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { var cacheHome = Environment.GetEnvironmentVariable("XDG_CACHE_HOME"); if (cacheHome == null) diff --git a/src/Config/Properties/AssetIdAssetProperty.cs b/src/Config/Properties/AssetIdAssetProperty.cs new file mode 100644 index 000000000..8d01442b8 --- /dev/null +++ b/src/Config/Properties/AssetIdAssetProperty.cs @@ -0,0 +1,40 @@ +using System; +using System.Text.Json; +using UAlbion.Api; + +namespace UAlbion.Config.Properties; + +public class AssetIdAssetProperty : IAssetProperty +{ + public AssetIdAssetProperty(string name) => Name = name; + public string Name { get; } + public AssetId DefaultValue => AssetId.None; + public object FromJson(JsonElement elem, TypeConfig config) + { + if (config == null) throw new ArgumentNullException(nameof(config)); + var asString = elem.GetString(); + return config.ResolveId(asString); + } +} + +public class AssetIdAssetProperty : IAssetProperty where T : IAssetId +{ + readonly Func _converter; + + public AssetIdAssetProperty(string name, T defaultValue, Func converter) + { + _converter = converter; + Name = name; + DefaultValue = defaultValue; + } + + public string Name { get; } + public T DefaultValue { get; } + public object FromJson(JsonElement elem, TypeConfig config) + { + if (config == null) throw new ArgumentNullException(nameof(config)); + var asString = elem.GetString(); + AssetId id = config.ResolveId(asString); + return _converter(id); + } +} \ No newline at end of file diff --git a/src/Config/Properties/AssetProps.cs b/src/Config/Properties/AssetProps.cs new file mode 100644 index 000000000..43724c9ce --- /dev/null +++ b/src/Config/Properties/AssetProps.cs @@ -0,0 +1,30 @@ +namespace UAlbion.Config.Properties; + +public static class AssetProps +{ + // General + public static readonly StringAssetProperty Filename = new("Filename"); // Not specified explicitly - this is loaded from the dictionary key of the file + public static readonly StringAssetProperty Sha256Hash = new("Sha256Hash"); // Not specified explicitly - this is loaded from the dictionary key of the file + public static readonly PathPatternProperty Pattern = new("Pattern"); // Mostly for DirectoryContainer - used to specify complex file names based on the asset's id and metadata + + // How to load the asset. Container is used to pull one asset out of a file containing many, loader + // is used to actually load the asset and the post-processor can perform any extra steps required after + // loading (to maintain simplicity, use of post-processors should be minimised wherever possible) + public static readonly TypeAliasAssetProperty Container = new("Container", "container", x => x.Containers); + public static readonly TypeAliasAssetProperty Loader = new("Loader", "loader", x => x.Loaders); + public static readonly TypeAliasAssetProperty Post = new("Post", "post-processor", x => x.PostProcessors); + + // When to load the asset. Language will cause the asset to only be loaded in that language's context, + // IsReadOnly will cause the asset to be ignored when converting / saving, UseDummyRead will skip the + // loading step when converting and Optional will suppress error messages about missing assets (e.g. when some languages are not available etc). + public static readonly StringAssetProperty Language = new("Language"); + public static readonly BoolAssetProperty IsReadOnly = new("IsReadOnly"); // To prevent zeroing out files when repacking formats that don't have writing code yet, e.g. ILBM images + public static readonly BoolAssetProperty UseDummyRead = new("UseDummyRead"); // For asset conversion, indicates that no asset should be loaded from the source mod, instead the target mod loader should be called directly with a dummy object + public static readonly BoolAssetProperty Optional = new("Optional"); // Will suppress missing-asset warnings when true + + // Common texture-specific properties + public static readonly IntAssetProperty Width = new("Width"); + public static readonly IntAssetProperty Height = new("Height"); + public static readonly AssetIdAssetProperty Palette = new("Palette"); // For providing context when exporting 8-bit images to true-colour PNGs and reimporting the true-colour PNGs back to 8-bit images. +} + diff --git a/src/Config/Properties/AssetRangeAssetProperty.cs b/src/Config/Properties/AssetRangeAssetProperty.cs new file mode 100644 index 000000000..afde32455 --- /dev/null +++ b/src/Config/Properties/AssetRangeAssetProperty.cs @@ -0,0 +1,17 @@ +using System; +using System.Text.Json; + +namespace UAlbion.Config.Properties; + +public class AssetRangeAssetProperty : IAssetProperty +{ + public AssetRangeAssetProperty(string name) => Name = name; + public string Name { get; } + public AssetRange DefaultValue => new(AssetId.None, AssetId.None); + public object FromJson(JsonElement elem, TypeConfig config) + { + if (config == null) throw new ArgumentNullException(nameof(config)); + var asString = elem.GetString(); + return config.ParseIdRange(asString); + } +} \ No newline at end of file diff --git a/src/Config/Properties/BoolAssetProperty.cs b/src/Config/Properties/BoolAssetProperty.cs new file mode 100644 index 000000000..b709176b8 --- /dev/null +++ b/src/Config/Properties/BoolAssetProperty.cs @@ -0,0 +1,11 @@ +using System.Text.Json; + +namespace UAlbion.Config.Properties; + +public class BoolAssetProperty : IAssetProperty +{ + public BoolAssetProperty(string name) => Name = name; + public string Name { get; } + public bool DefaultValue => false; + public object FromJson(JsonElement elem, TypeConfig config) => elem.GetBoolean(); +} \ No newline at end of file diff --git a/src/Config/Properties/EnumAssetProperty.cs b/src/Config/Properties/EnumAssetProperty.cs new file mode 100644 index 000000000..4382a4066 --- /dev/null +++ b/src/Config/Properties/EnumAssetProperty.cs @@ -0,0 +1,24 @@ +using System; +using System.Text.Json; + +namespace UAlbion.Config.Properties; + +public class EnumAssetProperty : IAssetProperty where T : struct, Enum +{ + public EnumAssetProperty(string name, T defaultValue) + { + Name = name; + DefaultValue = defaultValue; + } + + public string Name { get; } + public T DefaultValue { get; } + public object FromJson(JsonElement elem, TypeConfig config) + { + var asString = elem.GetString(); + if (asString == null) + throw new FormatException($"Null is an invalid value for the \"{Name}\" property (must be {typeof(T).Name})"); + + return Enum.Parse(asString); + } +} \ No newline at end of file diff --git a/src/Config/Properties/IntAssetProperty.cs b/src/Config/Properties/IntAssetProperty.cs new file mode 100644 index 000000000..41fd28d97 --- /dev/null +++ b/src/Config/Properties/IntAssetProperty.cs @@ -0,0 +1,11 @@ +using System.Text.Json; + +namespace UAlbion.Config.Properties; + +public class IntAssetProperty : IAssetProperty +{ + public IntAssetProperty(string name) => Name = name; + public string Name { get; } + public int DefaultValue => 0; + public object FromJson(JsonElement elem, TypeConfig config) => elem.GetInt32(); +} \ No newline at end of file diff --git a/src/Config/Properties/PathPatternProperty.cs b/src/Config/Properties/PathPatternProperty.cs new file mode 100644 index 000000000..fd1690a13 --- /dev/null +++ b/src/Config/Properties/PathPatternProperty.cs @@ -0,0 +1,26 @@ +using System.Text.Json; + +namespace UAlbion.Config.Properties; + +public class PathPatternProperty : IAssetProperty +{ + public PathPatternProperty(string name) + { + Name = name; + DefaultValue = AssetPathPattern.Build(""); + } + + public PathPatternProperty(string name, string defaultPath) + { + Name = name; + DefaultValue = AssetPathPattern.Build(defaultPath); + } + + public string Name { get; } + public AssetPathPattern DefaultValue { get; } + public object FromJson(JsonElement elem, TypeConfig config) + { + var asString = elem.GetString(); + return AssetPathPattern.Build(asString); + } +} \ No newline at end of file diff --git a/src/Config/Properties/StringAssetProperty.cs b/src/Config/Properties/StringAssetProperty.cs new file mode 100644 index 000000000..766022646 --- /dev/null +++ b/src/Config/Properties/StringAssetProperty.cs @@ -0,0 +1,11 @@ +using System.Text.Json; + +namespace UAlbion.Config.Properties; + +public class StringAssetProperty : IAssetProperty +{ + public StringAssetProperty(string name) => Name = name; + public string Name { get; } + public string DefaultValue => null; + public object FromJson(JsonElement elem, TypeConfig config) => elem.GetString(); +} \ No newline at end of file diff --git a/src/Config/Properties/StringListAssetProperty.cs b/src/Config/Properties/StringListAssetProperty.cs new file mode 100644 index 000000000..e59cb6093 --- /dev/null +++ b/src/Config/Properties/StringListAssetProperty.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; + +namespace UAlbion.Config.Properties; + +public class StringListAssetProperty : IAssetProperty> +{ + public StringListAssetProperty(string name) => Name = name; + public string Name { get; } + public List DefaultValue => new(); + public object FromJson(JsonElement elem, TypeConfig config) + { + if (elem.ValueKind != JsonValueKind.Array) + throw new FormatException($"Property \"{Name}\" expects an array of strings"); + + var list = new List(); + foreach (var s in elem.EnumerateArray()) + list.Add(s.GetString()); + + return list; + } + +} \ No newline at end of file diff --git a/src/Config/Properties/TypeAliasAssetProperty.cs b/src/Config/Properties/TypeAliasAssetProperty.cs new file mode 100644 index 000000000..a180b6c39 --- /dev/null +++ b/src/Config/Properties/TypeAliasAssetProperty.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; + +namespace UAlbion.Config.Properties; + +public class TypeAliasAssetProperty : IAssetProperty +{ + readonly string _description; + readonly Func> _dictSelector; + + public TypeAliasAssetProperty(string name, string description, Func> dictSelector) + { + if (string.IsNullOrEmpty(name)) throw new ArgumentException("Value cannot be null or empty.", nameof(name)); + if (string.IsNullOrEmpty(description)) throw new ArgumentException("Value cannot be null or empty.", nameof(description)); + + Name = name; + _description = description; + _dictSelector = dictSelector ?? throw new ArgumentNullException(nameof(dictSelector)); + } + + public string Name { get; } + public Type DefaultValue => null; + public object FromJson(JsonElement elem, TypeConfig config) + { + var dict = _dictSelector(config); + var s = elem.GetString(); + if (s != null && dict.TryGetValue(s, out var type)) + return type; + + throw new FormatException($"Could not resolve {_description} alias \"{s}\" to a type name"); + } +} \ No newline at end of file diff --git a/src/Config/RangeLookup.cs b/src/Config/RangeLookup.cs new file mode 100644 index 000000000..5c95eb4e5 --- /dev/null +++ b/src/Config/RangeLookup.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using System.Linq; + +namespace UAlbion.Config; + +public class RangeLookup // Note: it is assumed that id ranges in a given config are disjoint, i.e. there should be no AssetId which satisfies multiple ranges in a single assets.json +{ + readonly AssetRangeInfo[][] _byType = new AssetRangeInfo[256][]; + readonly RangeLookup _parent; + + public RangeLookup() { } // Empty range for mods without an asset config + public RangeLookup(RangeLookup parent, IEnumerable ranges) + { + _parent = parent; + + var groups = ranges + .GroupBy(x => x.Range.From.Type) + .Select(x => (x.Key, x.OrderBy(x => x.Range.From).ToArray())); + + foreach (var (type, group) in groups) + _byType[(int)type] = group; + } + + public IEnumerable AllRanges => + _byType + .Where(x => x != null) + .SelectMany(x => x) + .OrderBy(x => x.Sequence) + .ThenBy(x => x.Range.From.ToString()); + + public AssetRangeInfo TryFindAssetRangeInfo(AssetId id) + { + var rangesForType = _byType[(int)id.Type]; + if (rangesForType == null) + return _parent?.TryFindAssetRangeInfo(id); + + int index = FindNearest(rangesForType, id); + if (index < 0 || index >= rangesForType.Length) + return _parent?.TryFindAssetRangeInfo(id); + + var rangeInfo = rangesForType[index]; + var range = rangeInfo.Range; + return range.From <= id && range.To >= id + ? rangeInfo + : _parent?.TryFindAssetRangeInfo(id); + } + + static int FindNearest(AssetRangeInfo[] collection, AssetId id) // Binary search + { + int first = 0; + int last = collection.Length - 1; + int mid; + + do + { + mid = first + (last - first) / 2; + if (id > collection[mid].Range.To) + first = mid + 1; + else + last = mid - 1; + + if (collection[mid].Range.From <= id && collection[mid].Range.To >= id) + return mid; + } while (first <= last); + + if (collection[mid].Range.From > id && mid != 0) + mid--; + + return mid; + } +} \ No newline at end of file diff --git a/src/Config/TypeConfig.cs b/src/Config/TypeConfig.cs new file mode 100644 index 000000000..63a0bd7dd --- /dev/null +++ b/src/Config/TypeConfig.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; + +namespace UAlbion.Config; + +public class TypeConfig // Defines the vocabulary to be used in assets.json files / AssetConfig +{ + readonly AssetMapping _mapping; + + public TypeConfig(string modName, AssetMapping mapping) + { + ModName = modName; + _mapping = mapping ?? throw new ArgumentNullException(nameof(mapping)); + } + + /// + /// The name of the mod that this type config came from + /// + public string ModName { get; } + + /// + /// A set of aliases for enums to be used as asset ids + /// + public IReadOnlyDictionary IdTypes { get; init; } + + /// + /// A set of supported natural languages that the player can choose to play the game in. + /// + public IReadOnlyDictionary Languages { get; init; } + + /// + /// A set of aliases for .NET types (deriving from IAssetLoader) to be used for loading assets + /// + public IReadOnlyDictionary Loaders { get; init; } + + /// + /// A set of aliases for .NET types (deriving from IAssetContainer) to be used for extracting individual assets from container formats. + /// + public IReadOnlyDictionary Containers { get; init; } + + /// + /// A set of aliases for .NET types (deriving from IAssetPostProcessor) to be used for additional post-load asset processing. + /// + public IReadOnlyDictionary PostProcessors { get; init; } + + /// + /// The collection of types containing static IAssetProperty members to be registered + /// + public AssetProperties PropertyTypes { get; init; } + + /// + /// The collection of types containing vars + /// + public List VarTypes { get; init; } = new(); + + public IAssetProperty GetAssetProperty(string name, AssetNode node) + { + var loader = node?.Loader; + if (loader != null) + { + var loaderProperty = PropertyTypes.GetProperty(name, loader); + if (loaderProperty != null) + return loaderProperty; + } + + var container = node?.Container; + if (container != null) + { + var containerProperty = PropertyTypes.GetProperty(name, container); + if (containerProperty != null) + return containerProperty; + } + + return PropertyTypes.GetGlobalProperty(name); + } + + public AssetId ResolveId(string id) + { + if (string.IsNullOrEmpty(id)) + return AssetId.None; + + var (type, val) = SplitId(id, '.'); + if (val == null) + throw new FormatException($"Asset IDs should consist of an alias type and value, separated by a '.' character (string was \"{id}\")"); + + var enumType = ResolveIdType(type); + return _mapping.EnumToId(enumType, val); + } + + static (string, string) SplitId(string id, char separator) + { + int index = id.IndexOf(separator, StringComparison.Ordinal); + if (index == -1) + return (id, null); + + var type = id[..index]; + var val = id[(index + 1)..]; + return (type, val); + } + + Type ResolveIdType(string type) + { + if (!IdTypes.TryGetValue(type, out var assetType)) + throw new FormatException($"Could not resolve asset id alias \"{type}\""); + + var enumType = Type.GetType(assetType.EnumType); + if (enumType == null) + throw new FormatException($"Could not resolve type \"{assetType.EnumType}\" (alias \"{assetType.Alias}\")"); + + return enumType; + } + + public AssetRange ParseIdRange(string s) + { + if (string.IsNullOrWhiteSpace(s)) + throw new ArgumentNullException(nameof(s)); + + int hyphenIndex = s.IndexOf('-', StringComparison.Ordinal); + if (hyphenIndex == -1) + { + var id = ResolveId(s); + return new AssetRange(id, id); + } + + var first = s[..hyphenIndex]; + var dotIndex = first.IndexOf('.', StringComparison.Ordinal); + if (dotIndex == -1) + throw new FormatException("Expected '.' in first section of range specifier \"{s}\""); + + var firstId = ResolveId(first); + + var typePart = first[..dotIndex]; + var secondPart = s[(hyphenIndex + 1)..]; + AssetId secondId; + if (secondPart == "*") + { + var (type, _) = _mapping.IdToEnum(firstId); + secondId = _mapping.MaxIdForType(type); + } + else + { + var second = $"{typePart}.{secondPart}"; + secondId = ResolveId(second); + } + + return new AssetRange(firstId, secondId); + } +} \ No newline at end of file diff --git a/src/Config/TypeConfigLoader.cs b/src/Config/TypeConfigLoader.cs new file mode 100644 index 000000000..bec647201 --- /dev/null +++ b/src/Config/TypeConfigLoader.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json.Serialization; +using UAlbion.Api; + +namespace UAlbion.Config; + +public class TypeConfigLoader +{ + readonly IJsonUtil _jsonUtil; + public TypeConfigLoader(IJsonUtil jsonUtil) + => _jsonUtil = jsonUtil ?? throw new ArgumentNullException(nameof(jsonUtil)); + +#pragma warning disable CA1812 // 'AssetConfigLoader.RawTypeConfig' is an internal class that is apparently never instantiated. If so, remove the code from the assembly. If this class is intended to contain only static members, make it 'static' (Module in Visual Basic). + class RawTypeConfig + { + [JsonInclude, JsonPropertyName("Languages")] public Dictionary Languages { get; private set; } = new(); + [JsonInclude, JsonPropertyName("IdTypes")] public Dictionary IdTypes { get; private set; } = new(); + [JsonInclude, JsonPropertyName("Containers")] public Dictionary Containers { get; private set; } = new(); + [JsonInclude, JsonPropertyName("Loaders")] public Dictionary Loaders { get; private set; } = new(); + [JsonInclude, JsonPropertyName("PostProcessors")] public Dictionary PostProcessors { get; private set; } = new(); + [JsonInclude, JsonPropertyName("GlobalPropertyTypes")] public List GlobalPropertyTypes { get; private set; } = new(); + [JsonInclude, JsonPropertyName("VarTypes")] public List VarTypes { get; private set; } = new(); + } +#pragma warning restore CA1812 // 'AssetConfigLoader.RawTypeConfig' is an internal class that is apparently never instantiated. If so, remove the code from the assembly. If this class is intended to contain only static members, make it 'static' (Module in Visual Basic). + + public TypeConfig Load(string configPath, string modName, TypeConfig parent, AssetMapping mapping, IFileSystem disk) + { + if (disk == null) throw new ArgumentNullException(nameof(disk)); + if (!disk.FileExists(configPath)) + throw new FileNotFoundException($"Could not open asset config from {configPath}"); + + var configText = disk.ReadAllBytes(configPath); + var config = Parse(configText, modName, parent, mapping); + if (config == null) + throw new FileLoadException($"Could not load asset config from \"{configPath}\""); + + return config; + } + + public IReadOnlyDictionary LoadIdTypesOnly(string configPath, IFileSystem disk) + { + if (disk == null) throw new ArgumentNullException(nameof(disk)); + if (!disk.FileExists(configPath)) + throw new FileNotFoundException($"Could not open asset config from {configPath}"); + + var configText = disk.ReadAllBytes(configPath); + var raw = _jsonUtil.Deserialize(configText); + if (raw == null) + return null; + + foreach (var kvp in raw.IdTypes) + kvp.Value.Alias = kvp.Key; + + return raw.IdTypes; + } + + public TypeConfig Parse(byte[] configText, string modName, TypeConfig parent, AssetMapping mapping) + { + var raw = _jsonUtil.Deserialize(configText); + if (raw == null) + return null; + + foreach (var kvp in raw.IdTypes) + kvp.Value.Alias = kvp.Key; + + foreach (var kvp in raw.Languages) + kvp.Value.Id = kvp.Key; + + var loaders = GetTypes(raw.Loaders); + var containers = GetTypes(raw.Containers); + var postProcessors = GetTypes(raw.PostProcessors); + + var config = new TypeConfig(modName, mapping) + { + IdTypes = parent != null ? new FallbackDictionary(raw.IdTypes, parent.IdTypes) : raw.IdTypes, + Loaders = parent != null ? new FallbackDictionary(loaders, parent.Loaders) : loaders, + Containers = parent != null ? new FallbackDictionary(containers, parent.Containers) : containers, + PostProcessors = parent != null ? new FallbackDictionary(postProcessors, parent.PostProcessors) : postProcessors, + Languages = parent != null ? new FallbackDictionary(raw.Languages, parent.Languages) : raw.Languages, + PropertyTypes = LoadPropertyTypes(modName, parent, raw), + VarTypes = raw.VarTypes + }; + + return config; + } + + static AssetProperties LoadPropertyTypes(string modName, TypeConfig parent, RawTypeConfig raw) + { + var properties = new AssetProperties(parent?.PropertyTypes); + + foreach (var kvp in raw.Loaders) + { + var type = Type.GetType(kvp.Value); + if (type == null) + throw new InvalidOperationException($"Could not load type \"{kvp.Value}\" as property container from mod {modName}"); + + properties.LoadAssetPropertiesFromType(false, type); + } + + foreach (var kvp in raw.Containers) + { + var type = Type.GetType(kvp.Value); + if (type == null) + throw new InvalidOperationException($"Could not load type \"{kvp.Value}\" as property container from mod {modName}"); + + properties.LoadAssetPropertiesFromType(false, type); + } + + foreach (var typeName in raw.GlobalPropertyTypes) + { + var type = Type.GetType(typeName); + if (type == null) + throw new InvalidOperationException($"Could not load type \"{typeName}\" as property container from mod {modName}"); + + properties.LoadAssetPropertiesFromType(true, type); + } + + return properties; + } + + static Dictionary GetTypes(Dictionary typeNames) + { + var results = new Dictionary(); + foreach (var kvp in typeNames) + { + var type = Type.GetType(kvp.Value); + results[kvp.Key] = type; + } + + return results; + } +} \ No newline at end of file diff --git a/src/Core.Veldrid/ShaderCache.cs b/src/Core.Veldrid/ShaderCache.cs index 8d6940502..0842d1c1d 100644 --- a/src/Core.Veldrid/ShaderCache.cs +++ b/src/Core.Veldrid/ShaderCache.cs @@ -152,7 +152,7 @@ CacheEntry BuildShaderPair(GraphicsBackend backend, ShaderInfo vertex, ShaderInf } void RemoveOldFiles(string name, string goodHash) { - foreach (var path in _disk.EnumerateDirectory(_shaderCachePath, $"{name}.*")) + foreach (var path in _disk.EnumerateFiles(_shaderCachePath, $"{name}.*")) { var filename = Path.GetFileName(path); var parts = filename[(name.Length + 1)..].Split('.'); diff --git a/src/Formats/AssetConversionOptions.cs b/src/Formats/AssetConversionOptions.cs new file mode 100644 index 000000000..bea3baf07 --- /dev/null +++ b/src/Formats/AssetConversionOptions.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using UAlbion.Config; + +namespace UAlbion.Formats; + +public record AssetConversionOptions( + AssetLoaderMethod LoaderFunc, + Action FlushCacheFunc, + ISet Ids, + ISet AssetTypes, + string[] Languages, + Regex FilePattern +); \ No newline at end of file diff --git a/src/Formats/AssetIdTypes.json b/src/Formats/AssetIdTypes.json index 5047805f8..6cb44f14c 100644 --- a/src/Formats/AssetIdTypes.json +++ b/src/Formats/AssetIdTypes.json @@ -73,22 +73,23 @@ "VideoId": [ "Video" ], "WaveLibraryId": [ "WaveLibrary" ], - "EventTextId": [ "EventText" ], - "MapTextId": [ "MapText" ], "WordId": [ "Word" ], - "TextId": [ + "StringSetId": [ "EventText", - "ItemName", "MapText", - "Special", + "Special" + ], + + "TextId": [ + "ItemName", "Text", "Word" ] }, "Extras": { - "EventSetId": [ " public readonly TextId ToEventText() => new TextId(AssetType.EventText, Id);" ], - "MapId": [ " public readonly TextId ToMapText() => new TextId(AssetType.MapText, Id);" ], + "EventSetId": [ " public readonly StringSetId ToEventText() => new StringSetId(AssetType.EventText, Id);" ], + "MapId": [ " public readonly StringSetId ToMapText() => new StringSetId(AssetType.MapText, Id);" ], "ItemId": [ " public readonly TextId ToName() => new TextId(AssetType.ItemName, Id);" ], "TilesetId": [ " public readonly BlockListId ToBlockList() => new BlockListId(AssetType.BlockList, Id);", diff --git a/src/Formats/AssetLoadResult.cs b/src/Formats/AssetLoadResult.cs new file mode 100644 index 000000000..3721bc18d --- /dev/null +++ b/src/Formats/AssetLoadResult.cs @@ -0,0 +1,5 @@ +using UAlbion.Config; + +namespace UAlbion.Formats; + +public record AssetLoadResult(AssetId AssetId, object Asset, AssetNode Node); \ No newline at end of file diff --git a/src/Formats/AssetLoaderMethod.cs b/src/Formats/AssetLoaderMethod.cs new file mode 100644 index 000000000..9badbf19e --- /dev/null +++ b/src/Formats/AssetLoaderMethod.cs @@ -0,0 +1,5 @@ +using UAlbion.Config; + +namespace UAlbion.Formats; + +public delegate AssetLoadResult AssetLoaderMethod(AssetId id, string language); \ No newline at end of file diff --git a/src/Formats/Assets/AlbionPalette.cs b/src/Formats/Assets/AlbionPalette.cs index d89bf350e..86806a587 100644 --- a/src/Formats/Assets/AlbionPalette.cs +++ b/src/Formats/Assets/AlbionPalette.cs @@ -7,6 +7,7 @@ using UAlbion.Api; using UAlbion.Api.Visual; using UAlbion.Config; +using UAlbion.Formats.Parsers; namespace UAlbion.Formats.Assets; @@ -178,12 +179,12 @@ public uint[] GetUnambiguousPalette() public ReadOnlySpan GetPaletteAtTime(int i) => _texture.GetLayerBuffer(0).GetRow(i % _texture.Height); public override string ToString() { return string.IsNullOrEmpty(Name) ? $"Palette {Id}" : $"{Name} ({Id})"; } - public static AlbionPalette Serdes(AlbionPalette p, AssetInfo info, ISerializer s) + public static AlbionPalette Serdes(AlbionPalette p, AssetLoadContext context, ISerializer s) { if (s == null) throw new ArgumentNullException(nameof(s)); - if (info == null) throw new ArgumentNullException(nameof(info)); + if (context == null) throw new ArgumentNullException(nameof(context)); - bool isCommon = info.Get(AssetProperty.IsCommon, false); + bool isCommon = context.GetProperty(PaletteLoader.IsCommon); long entryCount = isCommon ? CommonEntries : VariableEntries; if (p == null) @@ -191,12 +192,13 @@ public static AlbionPalette Serdes(AlbionPalette p, AssetInfo info, ISerializer if (s.IsWriting()) throw new ArgumentNullException(nameof(p)); // AssetId is None when loading palettes from raw data in ImageReverser + int index = context.Index; p = new AlbionPalette { - Id = info.AssetId.IsNone ? (uint)info.Index : info.AssetId.ToUInt32(), - Name = info.AssetId.IsNone - ? info.Index.ToString() - : info.AssetId.ToString() + Id = context.AssetId.IsNone ? (uint)index : context.AssetId.ToUInt32(), + Name = context.AssetId.IsNone + ? index.ToString() + : context.AssetId.ToString() }; } @@ -216,13 +218,13 @@ public static AlbionPalette Serdes(AlbionPalette p, AssetInfo info, ISerializer } if (s.IsReading()) - p.Ranges = ParseRanges(info); + p.Ranges = ParseRanges(context); return p; } - static IEnumerable<(byte, byte)> ParseRanges(AssetInfo info) => - info.Get(AssetProperty.AnimatedRanges, null)?.Split(',').Select(x => + static IEnumerable<(byte, byte)> ParseRanges(AssetLoadContext context) => + context.GetProperty(PaletteLoader.AnimatedRanges)?.Split(',').Select(x => { var parts = x.Trim().Split('-'); if (parts.Length != 2) diff --git a/src/Formats/Assets/AssetIdStringDictionary.cs b/src/Formats/Assets/AssetIdStringDictionary.cs deleted file mode 100644 index 05942321d..000000000 --- a/src/Formats/Assets/AssetIdStringDictionary.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; -using UAlbion.Config; - -namespace UAlbion.Formats.Assets; - -public class AssetIdStringDictionary : Dictionary, IStringSet -{ - public string GetString(StringId id, string language) => this.GetValueOrDefault(id.Id); -} \ No newline at end of file diff --git a/src/Formats/Assets/EventSet.cs b/src/Formats/Assets/EventSet.cs index 91c281cde..9581c84df 100644 --- a/src/Formats/Assets/EventSet.cs +++ b/src/Formats/Assets/EventSet.cs @@ -16,7 +16,7 @@ public class EventSet : IEventSet { ushort[] _chainMapping; AssetId IEventSet.Id => Id; - [JsonIgnore] public TextId TextId => Id.ToEventText(); + [JsonIgnore] public StringSetId StringSetId => Id.ToEventText(); [JsonInclude] public EventSetId Id { get; private init; } [JsonInclude] public IList Chains { get; private set; } [JsonIgnore] public IList ExtraEntryPoints => null; @@ -70,18 +70,23 @@ public static EventSet Serdes(EventSetId id, EventSet set, AssetMapping mapping, foreach (var e in set.Events) e.Unswizzle(set.Events); - // TODO: Unify this with the code in BaseMapData - var sortedChains = set.Chains + set.BuildChainMapping(); + return set; + } + + // TODO: Unify this with the code in BaseMapData + void BuildChainMapping() + { + var sortedChains = Chains .Select((eventId, chainId) => (eventId, chainId)) .OrderBy(x => x) .ToArray(); - set._chainMapping = new ushort[set.Events.Count]; - for (int i = 0, j = 0; i < set.Events.Count; i++) + _chainMapping = new ushort[Events.Count]; + for (int i = 0, j = 0; i < Events.Count; i++) { while (sortedChains.Length > j + 1 && sortedChains[j].eventId < i) j++; - set._chainMapping[i] = (ushort)sortedChains[j].chainId; + _chainMapping[i] = (ushort)sortedChains[j].chainId; } - return set; } } \ No newline at end of file diff --git a/src/Formats/Assets/IEventSet.cs b/src/Formats/Assets/IEventSet.cs index f8833390d..4aad12d5c 100644 --- a/src/Formats/Assets/IEventSet.cs +++ b/src/Formats/Assets/IEventSet.cs @@ -9,7 +9,7 @@ namespace UAlbion.Formats.Assets; public interface IEventSet { AssetId Id { get; } - TextId TextId { get; } + StringSetId StringSetId { get; } IList Events { get; } IList Chains { get; } IList ExtraEntryPoints { get; } diff --git a/src/Formats/Assets/IFFChunkType.cs b/src/Formats/Assets/IFFChunkType.cs index f9bc3300c..0df3e54ae 100644 --- a/src/Formats/Assets/IFFChunkType.cs +++ b/src/Formats/Assets/IFFChunkType.cs @@ -1,6 +1,6 @@ namespace UAlbion.Formats.Assets; -public static class IFFChunkType +public static class IffChunkType { public const string Format = "FORM"; public const string PackedBitmap = "PBM "; diff --git a/src/Formats/Assets/IStringSet.cs b/src/Formats/Assets/IStringSet.cs index 712fffc5a..51a6522e1 100644 --- a/src/Formats/Assets/IStringSet.cs +++ b/src/Formats/Assets/IStringSet.cs @@ -3,5 +3,6 @@ public interface IStringSet { int Count { get; } - string GetString(StringId id, string language = null); + string GetString(StringId id); + void SetString(StringId id, string value); } \ No newline at end of file diff --git a/src/Formats/Assets/IntStringDictionary.cs b/src/Formats/Assets/IntStringDictionary.cs index dc4872ea7..6eb29f686 100644 --- a/src/Formats/Assets/IntStringDictionary.cs +++ b/src/Formats/Assets/IntStringDictionary.cs @@ -4,5 +4,6 @@ namespace UAlbion.Formats.Assets; public class IntStringDictionary : Dictionary, IStringSet { - public string GetString(StringId id, string language) => this.GetValueOrDefault(id.SubId); + public string GetString(StringId id) => this.GetValueOrDefault(id.SubId); + public void SetString(StringId id, string value) => this[id.SubId] = value; } \ No newline at end of file diff --git a/src/Formats/Assets/InterlacedBitmap.cs b/src/Formats/Assets/InterlacedBitmap.cs index 089c9a4fd..b98f25674 100644 --- a/src/Formats/Assets/InterlacedBitmap.cs +++ b/src/Formats/Assets/InterlacedBitmap.cs @@ -33,12 +33,12 @@ public static InterlacedBitmap Serdes(InterlacedBitmap img, ISerializer s) if (s == null) throw new ArgumentNullException(nameof(s)); img ??= new InterlacedBitmap(); - var formatChunk = IffChunk.Serdes(0, new IffChunk(IFFChunkType.Format, 0), s); - if (formatChunk.TypeId != IFFChunkType.Format) + var formatChunk = IffChunk.Serdes(0, new IffChunk(IffChunkType.Format, 0), s); + if (formatChunk.TypeId != IffChunkType.Format) throw new NotSupportedException($"Invalid IFF header, expected \"FORM\", found \"{formatChunk.TypeId}\""); - var formatId = s.FixedLengthString("FormatId", IFFChunkType.PackedBitmap, 4); - if (formatId != IFFChunkType.PackedBitmap) + var formatId = s.FixedLengthString("FormatId", IffChunkType.PackedBitmap, 4); + if (formatId != IffChunkType.PackedBitmap) throw new NotSupportedException($"Invalid IFF header, expected \"PBM \", found \"{formatId}\""); if (s.IsReading()) @@ -49,17 +49,17 @@ public static InterlacedBitmap Serdes(InterlacedBitmap img, ISerializer s) var chunk = IffChunk.Serdes(i, null, s); switch (chunk.TypeId) { - case IFFChunkType.BitmapHeader: img.SerdesHeader(s, chunk.Length); break; - case IFFChunkType.ColorMapping: img.SerdesPalette(s, chunk.Length); break; - case IFFChunkType.Hotspot: img.SerdesHotspot(s, chunk.Length); break; + case IffChunkType.BitmapHeader: img.SerdesHeader(s, chunk.Length); break; + case IffChunkType.ColorMapping: img.SerdesPalette(s, chunk.Length); break; + case IffChunkType.Hotspot: img.SerdesHotspot(s, chunk.Length); break; - case IFFChunkType.ColorRanges: + case IffChunkType.ColorRanges: img.ColorRanges ??= new List(); img.ColorRanges.Add(ColorRange.Serdes(img.ColorRanges.Count, null, s)); break; - case IFFChunkType.Thumbnail: img.SerdesThumbnail(s, chunk.Length); break; - case IFFChunkType.Body: img.SerdesPixels(s, chunk.Length); break; + case IffChunkType.Thumbnail: img.SerdesThumbnail(s, chunk.Length); break; + case IffChunkType.Body: img.SerdesPixels(s, chunk.Length); break; default: s.Bytes("Unk", null, chunk.Length); break; @@ -70,12 +70,12 @@ public static InterlacedBitmap Serdes(InterlacedBitmap img, ISerializer s) } else { - WriteChunk(s, IFFChunkType.BitmapHeader, (x, n) => img.SerdesHeader(x, n)); - WriteChunk(s, IFFChunkType.ColorMapping, (x, n) => img.SerdesPalette(x, n)); - WriteChunk(s, IFFChunkType.Hotspot, (x, n) => img.SerdesHotspot(x, n)); + WriteChunk(s, IffChunkType.BitmapHeader, (x, n) => img.SerdesHeader(x, n)); + WriteChunk(s, IffChunkType.ColorMapping, (x, n) => img.SerdesPalette(x, n)); + WriteChunk(s, IffChunkType.Hotspot, (x, n) => img.SerdesHotspot(x, n)); s.List(nameof(img.ColorRanges), img.ColorRanges, img.ColorRanges.Count, ColorRange.Serdes); - WriteChunk(s, IFFChunkType.Thumbnail, (x, n) => img.SerdesThumbnail(x, n)); - WriteChunk(s, IFFChunkType.Body, (x, n) => img.SerdesPixels(x, n)); + WriteChunk(s, IffChunkType.Thumbnail, (x, n) => img.SerdesThumbnail(x, n)); + WriteChunk(s, IffChunkType.Body, (x, n) => img.SerdesPixels(x, n)); } formatChunk.WriteLength(s); diff --git a/src/Formats/Assets/ItemData.cs b/src/Formats/Assets/ItemData.cs index 0611180c5..aae3b5234 100644 --- a/src/Formats/Assets/ItemData.cs +++ b/src/Formats/Assets/ItemData.cs @@ -12,7 +12,7 @@ public sealed class ItemData { public const int SizeOnDisk = 0x28; public ItemData(ItemId id) => Id = id; - [JsonIgnore] public StringId Name => Id.ToName(); + [JsonIgnore] public StringId Name => new(Id.ToName()); public ItemId Id { get; } public byte Unknown { get; set; } // 0 Always 0 public ItemType TypeId { get; set; } // 1 Item type @@ -132,13 +132,12 @@ public override string ToString() return sb.ToString(); } - public static ItemData Serdes(AssetInfo info, ItemData item, ISerializer s, ISpellManager spellManager) + public static ItemData Serdes(AssetId id, ItemData item, ISerializer s, ISpellManager spellManager) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (s == null) throw new ArgumentNullException(nameof(s)); if (spellManager == null) throw new ArgumentNullException(nameof(spellManager)); - item ??= new ItemData(info.AssetId); + item ??= new ItemData(id); item.Unknown = s.UInt8(nameof(item.Unknown), item.Unknown); // 0 item.TypeId = s.EnumU8(nameof(item.TypeId), item.TypeId); // 1 item.SlotType = ((PersistedItemSlotId)s.UInt8(nameof(item.SlotType), (byte)item.SlotType.ToPersisted())).ToMemory(); // 2 diff --git a/src/Formats/Assets/Labyrinth/LabyrinthData.cs b/src/Formats/Assets/Labyrinth/LabyrinthData.cs index 96cc0e6e0..d4285d994 100644 --- a/src/Formats/Assets/Labyrinth/LabyrinthData.cs +++ b/src/Formats/Assets/Labyrinth/LabyrinthData.cs @@ -71,11 +71,10 @@ public int FrameCountForObjectGroup(int i) => .Where(x => x is { ObjectInfoNumber: >= 0 } && x.ObjectInfoNumber < Objects.Count) .Select(x => (long)Objects[x.ObjectInfoNumber].FrameCount)); - public static LabyrinthData Serdes(LabyrinthData d, AssetInfo info, AssetMapping mapping, ISerializer s) + public static LabyrinthData Serdes(LabyrinthData d, AssetId id, AssetMapping mapping, ISerializer s) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (s == null) throw new ArgumentNullException(nameof(s)); - d ??= new LabyrinthData { Id = info.AssetId }; + d ??= new LabyrinthData { Id = id }; if (d.ObjectGroups.Count > WallOffset) throw new InvalidOperationException($"A labyrinth specification can only contain a maximum of {WallOffset} object groups, but {d.Id} contains {d.ObjectGroups.Count}"); if (d.Walls.Count > MaxWalls) throw new InvalidOperationException($"A labyrinth specification can only contain a maximum of {WallOffset} walls, but {d.Id} contains {d.Walls.Count}"); diff --git a/src/Formats/Assets/ListStringSet.cs b/src/Formats/Assets/ListStringSet.cs index 0eaa777df..455d0d3b5 100644 --- a/src/Formats/Assets/ListStringSet.cs +++ b/src/Formats/Assets/ListStringSet.cs @@ -5,15 +5,26 @@ namespace UAlbion.Formats.Assets; public class ListStringSet : List, IStringSet { public ListStringSet() { } + public ListStringSet(int capacity) : base(capacity) { } public ListStringSet(IList existing) { Clear(); if (existing != null) + { + Capacity = existing.Count; foreach (var s in existing) Add(s); + } } - public string GetString(StringId id, string language) => Count > id.SubId ? this[id.SubId] : null; + public string GetString(StringId id) => Count > id.SubId ? this[id.SubId] : null; + public void SetString(StringId id, string value) + { + while (Count <= id.SubId) + Add(null); + + this[id.SubId] = value; + } public int FindOrAdd(string text) { diff --git a/src/Formats/Assets/Maps/BaseMapData.cs b/src/Formats/Assets/Maps/BaseMapData.cs index 0aaaefbac..5df2c57c3 100644 --- a/src/Formats/Assets/Maps/BaseMapData.cs +++ b/src/Formats/Assets/Maps/BaseMapData.cs @@ -22,7 +22,7 @@ public abstract class BaseMapData : IMapData, IJsonPostDeserialise AssetId IEventSet.Id => Id; [JsonInclude] public MapId Id { get; protected set; } - [JsonIgnore] public TextId TextId => Id.ToMapText(); + [JsonIgnore] public StringSetId StringSetId => Id.ToMapText(); public abstract MapType MapType { get; } [JsonInclude] public MapFlags Flags { get; set; } // Wait/Rest, Light-Environment, NPC converge range [JsonInclude] public int Width { get; protected set; } @@ -269,7 +269,7 @@ protected void SerdesNpcWaypoints(ISerializer s) } } - public static IMapData Serdes(AssetInfo info, IMapData existing, AssetMapping mapping, ISerializer s) + public static IMapData Serdes(AssetId id, IMapData existing, AssetMapping mapping, ISerializer s) { if (s == null) throw new ArgumentNullException(nameof(s)); if (s.BytesRemaining == 0) @@ -290,8 +290,8 @@ public static IMapData Serdes(AssetInfo info, IMapData existing, AssetMapping ma return mapType switch { // Indoor/outdoor maps aren't distinguished on disk - it has to be inferred from the tileset - MapType.TwoD => MapData2D.Serdes(info, (MapData2D)existing, mapping, s), - MapType.ThreeD => MapData3D.Serdes(info, (MapData3D)existing, mapping, s), + MapType.TwoD => MapData2D.Serdes(id, (MapData2D)existing, mapping, s), + MapType.ThreeD => MapData3D.Serdes(id, (MapData3D)existing, mapping, s), _ => throw new NotImplementedException($"Unrecognised map type {mapType} found.") }; } diff --git a/src/Formats/Assets/Maps/MapData2D.cs b/src/Formats/Assets/Maps/MapData2D.cs index 38f452a1d..d5c8a01f5 100644 --- a/src/Formats/Assets/Maps/MapData2D.cs +++ b/src/Formats/Assets/Maps/MapData2D.cs @@ -69,14 +69,13 @@ public MapData2D(MapId id, PaletteId paletteId, TilesetId tilesetId, byte width, Tiles = new MapTile[width * height]; } - public static MapData2D Serdes(AssetInfo info, MapData2D existing, AssetMapping mapping, ISerializer s) + public static MapData2D Serdes(AssetId id, MapData2D existing, AssetMapping mapping, ISerializer s) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (mapping == null) throw new ArgumentNullException(nameof(mapping)); if (s == null) throw new ArgumentNullException(nameof(s)); var startOffset = s.Offset; - var map = existing ?? new MapData2D { Id = info.AssetId }; + var map = existing ?? new MapData2D { Id = id }; map.Flags = s.EnumU16(nameof(Flags), map.Flags); // 0 _ = s.UInt8("MapType", (byte)MapType.TwoD); // 2 (always Map2D to start with, may shift to outdoors once we assign the tileset) diff --git a/src/Formats/Assets/Maps/MapData3D.cs b/src/Formats/Assets/Maps/MapData3D.cs index ff9594cb2..905b528da 100644 --- a/src/Formats/Assets/Maps/MapData3D.cs +++ b/src/Formats/Assets/Maps/MapData3D.cs @@ -70,12 +70,11 @@ public MapData3D(MapId id, PaletteId paletteId, LabyrinthId labyrinthId, int wid AutomapGraphics = new byte[AutomapGraphicsSize]; } - public static MapData3D Serdes(AssetInfo info, MapData3D existing, AssetMapping mapping, ISerializer s) + public static MapData3D Serdes(AssetId id, MapData3D existing, AssetMapping mapping, ISerializer s) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (s == null) throw new ArgumentNullException(nameof(s)); - var map = existing ?? new MapData3D { Id = info.AssetId }; + var map = existing ?? new MapData3D { Id = id }; map.Flags = s.EnumU16(nameof(Flags), map.Flags); // 0 var _ = s.UInt8("MapType", (byte)map.MapType); // 2 diff --git a/src/Formats/Assets/Maps/TilesetData.cs b/src/Formats/Assets/Maps/TilesetData.cs index ba40ab806..e68666343 100644 --- a/src/Formats/Assets/Maps/TilesetData.cs +++ b/src/Formats/Assets/Maps/TilesetData.cs @@ -4,6 +4,7 @@ using SerdesNet; using UAlbion.Config; using UAlbion.Formats.Ids; +using UAlbion.Formats.Parsers; namespace UAlbion.Formats.Assets.Maps; @@ -16,15 +17,15 @@ public TilesetData() { } public bool UseSmallGraphics { get; set; } // Careful if renaming: needs to match up to asset property in assets.json [JsonInclude] public List Tiles { get; private set; } = new(); - public static TilesetData Serdes(TilesetData td, ISerializer s, AssetInfo info) + public static TilesetData Serdes(TilesetData td, ISerializer s, AssetLoadContext context) { const int dummyTileCount = 1; if (s == null) throw new ArgumentNullException(nameof(s)); - if (info == null) throw new ArgumentNullException(nameof(info)); + if (context == null) throw new ArgumentNullException(nameof(context)); int tileCount = td?.Tiles.Count ?? (int)(s.BytesRemaining / 8) + dummyTileCount; - td ??= new TilesetData(info.AssetId); - td.UseSmallGraphics = info.Get(AssetProperty.UseSmallGraphics, td.UseSmallGraphics); + td ??= new TilesetData(context.AssetId); + td.UseSmallGraphics = context.GetProperty(TilesetLoader.UseSmallGraphicsProperty, td.UseSmallGraphics); if (td.Tiles.Count == 0) { diff --git a/src/Formats/Assets/MultiLanguageStringDictionary.cs b/src/Formats/Assets/MultiLanguageStringDictionary.cs deleted file mode 100644 index 838ceaa50..000000000 --- a/src/Formats/Assets/MultiLanguageStringDictionary.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; - -namespace UAlbion.Formats.Assets; - -public class MultiLanguageStringDictionary : Dictionary, IStringSet -{ - public string GetString(StringId id, string language) - => TryGetValue(language, out var collection) ? collection.GetString(id, language) : null; -} \ No newline at end of file diff --git a/src/Formats/Assets/ScriptEventSet.cs b/src/Formats/Assets/ScriptEventSet.cs index 034c07c57..3b04bb181 100644 --- a/src/Formats/Assets/ScriptEventSet.cs +++ b/src/Formats/Assets/ScriptEventSet.cs @@ -10,15 +10,15 @@ namespace UAlbion.Formats.Assets; public class ScriptEventSet : IEventSet { - public ScriptEventSet(AssetId id, TextId textId, IList events) + public ScriptEventSet(AssetId id, StringSetId stringSetId, IList events) { Id = id; - TextId = textId; + StringSetId = stringSetId; Events = events?.ToArray() ?? throw new ArgumentNullException(nameof(events)); } public AssetId Id { get; } - public TextId TextId { get; } + public StringSetId StringSetId { get; } public IList Events { get; } public IList Chains { get; } = Array.Empty(); public IList ExtraEntryPoints { get; } = Array.Empty(); diff --git a/src/Formats/Assets/SpellData.cs b/src/Formats/Assets/SpellData.cs index e1e7b0052..b4a8813f7 100644 --- a/src/Formats/Assets/SpellData.cs +++ b/src/Formats/Assets/SpellData.cs @@ -3,6 +3,7 @@ using SerdesNet; using UAlbion.Config; using UAlbion.Formats.Ids; +using UAlbion.Formats.Parsers; namespace UAlbion.Formats.Assets; @@ -20,7 +21,7 @@ public SpellData(SpellId id, SpellClass school, byte number) // For tests } [JsonIgnore] public SpellId Id { get; } // Setters needed for JSON - public StringId Name { get; set; } + public AssetId Name { get; set; } public SpellClass Class { get; set; } public byte OffsetInClass { get; set; } public SpellEnvironments Environments { get; set; } @@ -28,21 +29,20 @@ public SpellData(SpellId id, SpellClass school, byte number) // For tests public byte LevelRequirement { get; set; } public SpellTargets Targets { get; set; } public byte Unused { get; set; } // Always 0 except for unused spells in school 6 - - public static SpellData Serdes(SpellData d, AssetInfo info, ISerializer s) + public static SpellData Serdes(SpellData d, AssetLoadContext context, ISerializer s) { - if (info == null) throw new ArgumentNullException(nameof(info)); + if (context == null) throw new ArgumentNullException(nameof(context)); if (s == null) throw new ArgumentNullException(nameof(s)); - d ??= new SpellData(info.AssetId); + d ??= new SpellData(context.AssetId); d.Environments = s.EnumU8(nameof(Environments), d.Environments); d.Cost = s.UInt8(nameof(Cost), d.Cost); d.LevelRequirement = s.UInt8(nameof(LevelRequirement), d.LevelRequirement); d.Targets = s.EnumU8(nameof(Targets), d.Targets); d.Unused = s.UInt8(nameof(Unused), d.Unused); - d.Name = info.Get(AssetProperty.Name, (TextId)AssetId.None); - d.Class = info.Get(AssetProperty.MagicSchool, SpellClass.DjiKas); - d.OffsetInClass = (byte)info.Get(AssetProperty.SpellNumber, 0); + d.Name = context.GetProperty(SpellLoader.SpellName); + d.Class = context.GetProperty(SpellLoader.MagicSchool); + d.OffsetInClass = (byte)context.GetProperty(SpellLoader.SpellNumber); return d; } } \ No newline at end of file diff --git a/src/Formats/Assets/StringId.cs b/src/Formats/Assets/StringId.cs index f6b1d53a2..9a2af6065 100644 --- a/src/Formats/Assets/StringId.cs +++ b/src/Formats/Assets/StringId.cs @@ -4,16 +4,14 @@ namespace UAlbion.Formats.Assets; -public struct StringId : System.IEquatable +public struct StringId : IEquatable { - public StringId(TextId id, ushort subId) { Id = id; SubId = subId; } + public StringId(TextId id) { Id = id; SubId = 0; } + public StringId(StringSetId id, ushort subId) { Id = id; SubId = subId; } public override string ToString() => $"{Id}:{SubId}"; public AssetId Id { get; } public ushort SubId { get; } - public static implicit operator StringId(TextId id) => ToStringId(id); - public static StringId ToStringId(TextId id) => FormatUtil.ResolveTextId(id); - public override bool Equals(object obj) => obj is StringId other && Equals(other); public bool Equals(StringId other) => Id == other.Id && SubId == other.SubId; public static bool operator ==(StringId left, StringId right) => left.Equals(right); @@ -26,11 +24,17 @@ public static StringId Parse(string s) throw new ArgumentNullException(nameof(s)); int index = s.IndexOf(':', StringComparison.Ordinal); - if (index == -1) - throw new FormatException($"Expected ':' in StringId: \"{s}\""); - var textId = TextId.Parse(s[..index]); - var subId = ushort.Parse(s[(index + 1)..]); - return new StringId(textId, subId); + if (index == -1) + { + var id = TextId.Parse(s); + return new StringId(id); + } + else + { + var id = StringSetId.Parse(s[..index]); + var subId = ushort.Parse(s[(index + 1)..]); + return new StringId(id, subId); + } } } \ No newline at end of file diff --git a/src/Formats/Assets/TextIdStringDictionary.cs b/src/Formats/Assets/TextIdStringDictionary.cs deleted file mode 100644 index a474795b9..000000000 --- a/src/Formats/Assets/TextIdStringDictionary.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; -using UAlbion.Formats.Ids; - -namespace UAlbion.Formats.Assets; - -public class TextIdStringDictionary : Dictionary, IStringSet -{ - public string GetString(StringId id, string language) => this.GetValueOrDefault(id.Id); -} \ No newline at end of file diff --git a/src/Formats/Assets/WaveLibSample.cs b/src/Formats/Assets/WaveLibSample.cs index e25ab9cb5..98e5ae8a6 100644 --- a/src/Formats/Assets/WaveLibSample.cs +++ b/src/Formats/Assets/WaveLibSample.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics.CodeAnalysis; using System.Linq; using SerdesNet; using UAlbion.Api; diff --git a/src/Formats/Containers/BinaryOffsetContainer.cs b/src/Formats/Containers/BinaryOffsetContainer.cs index 102721e0a..42aca3358 100644 --- a/src/Formats/Containers/BinaryOffsetContainer.cs +++ b/src/Formats/Containers/BinaryOffsetContainer.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using SerdesNet; using UAlbion.Api; using UAlbion.Config; +using UAlbion.Config.Properties; namespace UAlbion.Formats.Containers; @@ -13,22 +13,21 @@ namespace UAlbion.Formats.Containers; /// public class BinaryOffsetContainer : IAssetContainer { - public ISerializer Read(string path, AssetInfo info, SerdesContext context) + public static readonly IntAssetProperty Offset = new("Offset"); // int, used for BinaryOffsetContainer, e.g. MAIN.EXE + public static readonly StringAssetProperty Hotspot = new("Hotspot"); // for cursors, formatted like "5 -2" + + public ISerializer Read(string path, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (context == null) throw new ArgumentNullException(nameof(context)); using var stream = context.Disk.OpenRead(path); using var br = new BinaryReader(stream); - stream.Position = info.Get(AssetProperty.Offset, 0); - var bytes = br.ReadBytes(info.Width * info.Height); + stream.Position = context.GetProperty(Offset); + var bytes = br.ReadBytes(context.Node.Width * context.Node.Height); var ms = new MemoryStream(bytes); return new AlbionReader(new BinaryReader(ms)); } - public void Write(string path, IList<(AssetInfo, byte[])> assets, SerdesContext context) + public void Write(string path, IList<(AssetLoadContext, byte[])> assets, ModContext context) => ApiUtil.Assert("Binary offset containers do not currently support saving"); - - public List<(int, int)> GetSubItemRanges(string path, AssetFileInfo info, SerdesContext context) // All sub-items must be given explicitly for binary offset containers - => FormatUtil.SortedIntsToRanges(info?.Map.Keys.OrderBy(x => x)); } \ No newline at end of file diff --git a/src/Formats/Containers/DirectoryContainer.cs b/src/Formats/Containers/DirectoryContainer.cs index 73c83d964..e5759001f 100644 --- a/src/Formats/Containers/DirectoryContainer.cs +++ b/src/Formats/Containers/DirectoryContainer.cs @@ -5,6 +5,8 @@ using System.Text; using SerdesNet; using UAlbion.Config; +using UAlbion.Config.Properties; +using UAlbion.Formats.Ids; using UAlbion.Formats.Parsers; namespace UAlbion.Formats.Containers; @@ -14,27 +16,31 @@ namespace UAlbion.Formats.Containers; /// public class DirectoryContainer : IAssetContainer { - public ISerializer Read(string path, AssetInfo info, SerdesContext context) + static readonly AssetPathPattern DefaultPattern = AssetPathPattern.Build("{0}_{1}_{2}.dat"); + public ISerializer Read(string path, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (context == null) throw new ArgumentNullException(nameof(context)); var subAssets = new Dictionary(); // path and name // Pattern vars: 0=Index 1=SubItem 2=Name 3=Palette - var pattern = info.GetPattern(AssetProperty.Pattern, "{0}_{1}_{2}.dat"); + var pattern = context.GetProperty(AssetProps.Pattern, DefaultPattern); if (context.Disk.DirectoryExists(path)) { - foreach (var filePath in context.Disk.EnumerateDirectory(path, pattern.WilcardForIndex(info.Index))) + var wildcardPattern = pattern.WilcardForId(context.AssetId); + foreach (var filePath in context.Disk.EnumerateFiles(path, wildcardPattern)) { var filename = Path.GetFileName(filePath); - if (!pattern.TryParse(filename, out var assetPath)) + if (!pattern.TryParse(filename, context.AssetId.Type, out var assetPath)) continue; if (assetPath.PaletteId.HasValue) - info.Set(AssetProperty.PaletteId, assetPath.PaletteId); + { + var palId = new PaletteId(assetPath.PaletteId.Value); + context.SetProperty(AssetProps.Palette, palId); + } - if (assetPath.Index != info.Index) + if (assetPath.AssetId != context.AssetId) continue; subAssets[assetPath.SubAsset] = (filePath, assetPath.Name); @@ -61,10 +67,11 @@ public ISerializer Read(string path, AssetInfo info, SerdesContext context) }); } - public void Write(string path, IList<(AssetInfo, byte[])> assets, SerdesContext context) + public void Write(string path, IList<(AssetLoadContext, byte[])> assets, ModContext context) { if (assets == null) throw new ArgumentNullException(nameof(assets)); if (context == null) throw new ArgumentNullException(nameof(context)); + if (context.Disk.FileExists(path)) throw new InvalidOperationException($"Cannot save directory container at \"{path}\", as there is already a file with that name."); @@ -83,12 +90,12 @@ public void Write(string path, IList<(AssetInfo, byte[])> assets, SerdesContext using var s = new AlbionReader(br); var subAssets = PackedChunks.Unpack(s).ToList(); - var pattern = info.GetPattern(AssetProperty.Pattern, "{0}_{1}_{2}.dat"); + var pattern = info.GetProperty(AssetProps.Pattern, AssetPathPattern.Build("{0}_{1}_{2}.dat")); if (subAssets.Count == 1) { var (subAssetBytes, name) = subAssets[0]; - var filename = name ?? pattern.Format(new AssetPath(info)); + var filename = name ?? pattern.Format(info.BuildAssetPath()); context.Disk.WriteAllBytes(Path.Combine(path, filename), subAssetBytes); } else @@ -102,7 +109,7 @@ public void Write(string path, IList<(AssetInfo, byte[])> assets, SerdesContext if (string.IsNullOrWhiteSpace(name)) name = null; - var filename = name ?? pattern.Format(new AssetPath(info, i)); + var filename = name ?? pattern.Format(info.BuildAssetPath(i)); var fullPath = Path.Combine(path, filename); var dir = Path.GetDirectoryName(fullPath); @@ -114,26 +121,4 @@ public void Write(string path, IList<(AssetInfo, byte[])> assets, SerdesContext } } } - - public List<(int, int)> GetSubItemRanges(string path, AssetFileInfo info, SerdesContext context) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - var subIds = new List(); - if (!context.Disk.DirectoryExists(path)) - return new List<(int, int)> { (0, 1) }; - - foreach (var filePath in context.Disk.EnumerateDirectory(path)) - { - var file = Path.GetFileName(filePath); - int index = file.IndexOf('_', StringComparison.Ordinal); - var part = index == -1 ? file : file.Substring(0, index); - if (!int.TryParse(part, out var asInt)) - continue; - - subIds.Add(asInt); - } - - subIds.Sort(); - return FormatUtil.SortedIntsToRanges(subIds); - } } diff --git a/src/Formats/Containers/DummyContainer.cs b/src/Formats/Containers/DummyContainer.cs index 50c030d36..aacfcbefb 100644 --- a/src/Formats/Containers/DummyContainer.cs +++ b/src/Formats/Containers/DummyContainer.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Generic; using SerdesNet; using UAlbion.Config; @@ -9,11 +7,6 @@ namespace UAlbion.Formats.Containers; public class DummyContainer : IAssetContainer { static readonly EmptySerializer Empty = new(); - public ISerializer Read(string path, AssetInfo info, SerdesContext context) => Empty; - public void Write(string path, IList<(AssetInfo, byte[])> assets, SerdesContext context) { } - public List<(int num, int count)> GetSubItemRanges(string path, AssetFileInfo info, SerdesContext context) - { - if (info == null) throw new ArgumentNullException(nameof(info)); - return FormatUtil.SortedIntsToRanges(info.Map.Keys.OrderBy(x => x)); - } + public ISerializer Read(string path, AssetLoadContext context) => Empty; + public void Write(string path, IList<(AssetLoadContext, byte[])> assets, ModContext context) { } } \ No newline at end of file diff --git a/src/Formats/Containers/ItemListContainer.cs b/src/Formats/Containers/ItemListContainer.cs index 476651c6b..2c160ace3 100644 --- a/src/Formats/Containers/ItemListContainer.cs +++ b/src/Formats/Containers/ItemListContainer.cs @@ -14,17 +14,16 @@ namespace UAlbion.Formats.Containers; /// public class ItemListContainer : IAssetContainer { - public ISerializer Read(string path, AssetInfo info, SerdesContext context) + public ISerializer Read(string path, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (context == null) throw new ArgumentNullException(nameof(context)); var stream = context.Disk.OpenRead(path); var br = new BinaryReader(stream); - stream.Position = info.Index * ItemData.SizeOnDisk; + stream.Position = context.Index * ItemData.SizeOnDisk; return new AlbionReader(br, ItemData.SizeOnDisk); } - public void Write(string path, IList<(AssetInfo, byte[])> assets, SerdesContext context) + public void Write(string path, IList<(AssetLoadContext, byte[])> assets, ModContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); @@ -41,14 +40,4 @@ public void Write(string path, IList<(AssetInfo, byte[])> assets, SerdesContext bw.Write(bytes); } } - - public List<(int, int)> GetSubItemRanges(string path, AssetFileInfo info, SerdesContext context) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - if (!context.Disk.FileExists(path)) - return new List<(int, int)>(); - - using var f = context.Disk.OpenRead(path); - return new List<(int, int)> { (0, (int)f.Length / ItemData.SizeOnDisk) }; - } } \ No newline at end of file diff --git a/src/Formats/Containers/JsonObjectContainer.cs b/src/Formats/Containers/JsonObjectContainer.cs index b6f2310c4..3c67d4026 100644 --- a/src/Formats/Containers/JsonObjectContainer.cs +++ b/src/Formats/Containers/JsonObjectContainer.cs @@ -16,15 +16,14 @@ namespace UAlbion.Formats.Containers; public class JsonObjectContainer : IAssetContainer { [SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "The serializer will handle it")] - public ISerializer Read(string path, AssetInfo info, SerdesContext context) + public ISerializer Read(string path, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (context == null) throw new ArgumentNullException(nameof(context)); if (!context.Disk.FileExists(path)) return null; var dict = Load(path, context); - if (!dict.TryGetValue(info.AssetId, out var token)) + if (!dict.TryGetValue(context.AssetId, out var token)) return null; var ms = new MemoryStream(Encoding.UTF8.GetBytes(context.Json.Serialize(token))); @@ -37,7 +36,7 @@ public ISerializer Read(string path, AssetInfo info, SerdesContext context) () => { br.Dispose(); ms.Dispose(); }); } - public void Write(string path, IList<(AssetInfo, byte[])> assets, SerdesContext context) + public void Write(string path, IList<(AssetLoadContext, byte[])> assets, ModContext context) { if (assets == null) throw new ArgumentNullException(nameof(assets)); if (context == null) throw new ArgumentNullException(nameof(context)); @@ -57,18 +56,7 @@ public void Write(string path, IList<(AssetInfo, byte[])> assets, SerdesContext context.Disk.WriteAllText(path, fullText); } - public List<(int, int)> GetSubItemRanges(string path, AssetFileInfo info, SerdesContext context) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - - if (!context.Disk.FileExists(path)) - return null; - - var dict = Load(path, context); - return FormatUtil.SortedIntsToRanges(dict.Keys.Select(x => x.Id).OrderBy(x => x)); - } - - static IDictionary Load(string path, SerdesContext context) + static IDictionary Load(string path, AssetLoadContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); diff --git a/src/Formats/Containers/JsonStringContainer.cs b/src/Formats/Containers/JsonStringContainer.cs index 7b190e326..d3dc99f74 100644 --- a/src/Formats/Containers/JsonStringContainer.cs +++ b/src/Formats/Containers/JsonStringContainer.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Linq; using System.Text; using SerdesNet; using UAlbion.Api; @@ -16,16 +15,15 @@ namespace UAlbion.Formats.Containers; public class JsonStringContainer : IAssetContainer { [SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "The serializer will handle it")] - public ISerializer Read(string path, AssetInfo info, SerdesContext context) + public ISerializer Read(string path, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (context == null) throw new ArgumentNullException(nameof(context)); if (!context.Disk.FileExists(path)) return null; var dict = Load(path, context); - if (!dict.TryGetValue(info.AssetId, out var value)) + if (!dict.TryGetValue(context.AssetId, out var value)) return null; var ms = new MemoryStream(Encoding.UTF8.GetBytes(value)); @@ -38,7 +36,7 @@ public ISerializer Read(string path, AssetInfo info, SerdesContext context) () => { br.Dispose(); ms.Dispose(); }); } - public void Write(string path, IList<(AssetInfo, byte[])> assets, SerdesContext context) + public void Write(string path, IList<(AssetLoadContext, byte[])> assets, ModContext context) { if (assets == null) throw new ArgumentNullException(nameof(assets)); if (context == null) throw new ArgumentNullException(nameof(context)); @@ -55,24 +53,16 @@ public void Write(string path, IList<(AssetInfo, byte[])> assets, SerdesContext context.Disk.WriteAllText(path, fullText); } - public List<(int, int)> GetSubItemRanges(string path, AssetFileInfo info, SerdesContext context) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - - if (!context.Disk.FileExists(path)) - return null; - - var dict = Load(path, context); - return FormatUtil.SortedIntsToRanges(dict.Keys.Select(x => x.Id).OrderBy(x => x)); - } - - static IDictionary Load(string path, SerdesContext context) + static IDictionary Load(string path, AssetLoadContext context) { var text = context.Disk.ReadAllBytes(path); var dict = context.Json.Deserialize>(text); if (dict == null) throw new FileLoadException($"Could not deserialize \"{path}\""); - return dict.ToDictionary(x => AssetId.Parse(x.Key), x => x.Value); + var dictionary = new Dictionary(); + foreach (var pair in dict) + dictionary.Add(AssetId.Parse(pair.Key), pair.Value); + return dictionary; } } \ No newline at end of file diff --git a/src/Formats/Containers/RawContainer.cs b/src/Formats/Containers/RawContainer.cs index 396f3dbf4..5b27fd2d9 100644 --- a/src/Formats/Containers/RawContainer.cs +++ b/src/Formats/Containers/RawContainer.cs @@ -13,11 +13,10 @@ namespace UAlbion.Formats.Containers; /// public class RawContainer : IAssetContainer { - public ISerializer Read(string path, AssetInfo info, SerdesContext context) + public ISerializer Read(string path, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (context == null) throw new ArgumentNullException(nameof(context)); - ApiUtil.Assert(info.Index == 0, "SubItem should always be 0 when accessing a non-container file"); + ApiUtil.Assert(context.Index == 0, "SubItem should always be 0 when accessing a non-container file"); if (!context.Disk.FileExists(path)) return null; @@ -27,7 +26,7 @@ public ISerializer Read(string path, AssetInfo info, SerdesContext context) return new AlbionReader(br); } - public void Write(string path, IList<(AssetInfo, byte[])> assets, SerdesContext context) + public void Write(string path, IList<(AssetLoadContext, byte[])> assets, ModContext context) { if (assets == null) throw new ArgumentNullException(nameof(assets)); if (context == null) throw new ArgumentNullException(nameof(context)); @@ -48,7 +47,4 @@ public void Write(string path, IList<(AssetInfo, byte[])> assets, SerdesContext var (_, bytes) = assets.Single(); context.Disk.WriteAllBytes(path, bytes); } - - public List<(int, int)> GetSubItemRanges(string path, AssetFileInfo info, SerdesContext context) - => new() { (0, 1) }; } \ No newline at end of file diff --git a/src/Formats/Containers/SpellListContainer.cs b/src/Formats/Containers/SpellListContainer.cs index 66cd64b53..012d60564 100644 --- a/src/Formats/Containers/SpellListContainer.cs +++ b/src/Formats/Containers/SpellListContainer.cs @@ -15,18 +15,17 @@ namespace UAlbion.Formats.Containers; public class SpellListContainer : IAssetContainer { static readonly byte[] Blank = { 0, 0, 0, 0, 0 }; - public ISerializer Read(string path, AssetInfo info, SerdesContext context) + public ISerializer Read(string path, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (context == null) throw new ArgumentNullException(nameof(context)); var stream = context.Disk.OpenRead(path); var br = new BinaryReader(stream); - stream.Position = info.Index * SpellData.SizeOnDisk; + stream.Position = context.Index * SpellData.SizeOnDisk; return new AlbionReader(br, SpellData.SizeOnDisk); } - public void Write(string path, IList<(AssetInfo, byte[])> assets, SerdesContext context) + public void Write(string path, IList<(AssetLoadContext, byte[])> assets, ModContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); @@ -51,15 +50,4 @@ public void Write(string path, IList<(AssetInfo, byte[])> assets, SerdesContext bw.Write(bytes); } } - - public List<(int, int)> GetSubItemRanges(string path, AssetFileInfo info, SerdesContext context) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - - if (!context.Disk.FileExists(path)) - return new List<(int, int)>(); - - using var f = context.Disk.OpenRead(path); - return new List<(int, int)> { (0, (int)f.Length / SpellData.SizeOnDisk) }; - } } \ No newline at end of file diff --git a/src/Formats/Containers/XldContainer.cs b/src/Formats/Containers/XldContainer.cs index 7471f6979..d3f42aa91 100644 --- a/src/Formats/Containers/XldContainer.cs +++ b/src/Formats/Containers/XldContainer.cs @@ -5,6 +5,7 @@ using SerdesNet; using UAlbion.Api; using UAlbion.Config; +using UAlbion.Config.Properties; using UAlbion.Formats.Assets.Save; namespace UAlbion.Formats.Containers; @@ -15,19 +16,25 @@ namespace UAlbion.Formats.Containers; public class XldContainer : IAssetContainer { const string MagicString = "XLD0I"; + static readonly IntAssetProperty MinimumCount = new("MinimumCount"); static int HeaderSize(int itemCount) => MagicString.Length + 3 + 4 * itemCount; - public ISerializer Read(string path, AssetInfo info, SerdesContext context) + public ISerializer Read(string path, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (context == null) throw new ArgumentNullException(nameof(context)); + if (!context.Disk.FileExists(path)) + return null; + using var s = new AlbionReader(new BinaryReader(context.Disk.OpenRead(path))); - var bytes = LoadAsset(info.Index, s); + var bytes = LoadAsset(context.Index, s); + if (bytes == null) + return null; + var ms = new MemoryStream(bytes); return new AlbionReader(new BinaryReader(ms)); } - public void Write(string path, IList<(AssetInfo, byte[])> assets, SerdesContext context) + public void Write(string path, IList<(AssetLoadContext, byte[])> assets, ModContext context) { if (assets == null) throw new ArgumentNullException(nameof(assets)); if (context == null) throw new ArgumentNullException(nameof(context)); @@ -43,7 +50,7 @@ public void Write(string path, IList<(AssetInfo, byte[])> assets, SerdesContext foreach (var (info, bytes) in assets) byIndex[info.Index] = bytes; - int minCount = assets[0].Item1.File.Get(AssetProperty.MinimumCount, 0); + int minCount = assets[0].Item1.GetProperty(MinimumCount); int count = byIndex .OrderBy(x => x.Key) .Last(x => x.Value.Length > 0) @@ -66,28 +73,11 @@ public void Write(string path, IList<(AssetInfo, byte[])> assets, SerdesContext s.Bytes(null, buffer, buffer.Length); } - public List<(int num, int count)> GetSubItemRanges(string path, AssetFileInfo info, SerdesContext context) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - - if (!context.Disk.FileExists(path)) - return new List<(int, int)>(); - - using var s = new AlbionReader(new BinaryReader(context.Disk.OpenRead(path))); - var lengths = HeaderSerdes(null, s); - int i = 0; - for (; i < lengths.Length; i++) - if (lengths[i] > 0) - break; - - return new List<(int, int)> { (i, lengths.Length - i) }; - } - static byte[] LoadAsset(int subItem, ISerializer s) { var lengths = HeaderSerdes(null, s); if (subItem >= lengths.Length) - throw new ArgumentOutOfRangeException($"Tried to load subItem {subItem} from XLD, but it only contains {lengths.Length} items."); + return null; long offset = s.Offset; offset += lengths.Where((_, i) => i < subItem).Sum(); diff --git a/src/Formats/Containers/ZipContainer.cs b/src/Formats/Containers/ZipContainer.cs index ba08cb420..bf5b4f56c 100644 --- a/src/Formats/Containers/ZipContainer.cs +++ b/src/Formats/Containers/ZipContainer.cs @@ -9,7 +9,7 @@ namespace UAlbion.Formats.Containers; /// public class ZipContainer : IAssetContainer { - public ISerializer Read(string path, AssetInfo info, SerdesContext context) => throw new System.NotImplementedException(); - public void Write(string path, IList<(AssetInfo, byte[])> assets, SerdesContext context) => throw new System.NotImplementedException(); - public List<(int, int)> GetSubItemRanges(string path, AssetFileInfo info, SerdesContext context) => throw new System.NotImplementedException(); + public ISerializer Read(string path, AssetLoadContext context) => throw new System.NotImplementedException(); + public void Write(string path, IList<(AssetLoadContext, byte[])> assets, ModContext context) + => throw new System.NotImplementedException(); } \ No newline at end of file diff --git a/src/Formats/EventFormatter.cs b/src/Formats/EventFormatter.cs index 494740a13..dd819e1b4 100644 --- a/src/Formats/EventFormatter.cs +++ b/src/Formats/EventFormatter.cs @@ -12,10 +12,10 @@ namespace UAlbion.Formats; public class EventFormatter : IEventFormatter { - readonly Func _stringLoadFunc; + readonly Func _stringLoadFunc; readonly AssetId _textSourceId; - public EventFormatter(Func stringLoadFunc, AssetId textSourceId) + public EventFormatter(Func stringLoadFunc, AssetId textSourceId) { _stringLoadFunc = stringLoadFunc; _textSourceId = textSourceId; @@ -30,7 +30,7 @@ public void Format(IScriptBuilder builder, IEventNode e, int idOffset = 0) if (e.Event is TextEvent textEvent && _stringLoadFunc != null) { var text = - _stringLoadFunc(textEvent.ToId(_textSourceId)) + _stringLoadFunc(textEvent.ToId(_textSourceId), null) .Replace("\"", "\\\"", StringComparison.Ordinal); builder.Add(ScriptPartType.Comment, $" ; \"{text}\""); @@ -46,7 +46,7 @@ public void Format(IScriptBuilder builder, IEvent e) if (e is TextEvent textEvent && _stringLoadFunc != null) { var text = - _stringLoadFunc(textEvent.ToId(_textSourceId)) + _stringLoadFunc(textEvent.ToId(_textSourceId), null) .Replace("\"", "\\\"", StringComparison.Ordinal); builder.Add(ScriptPartType.Comment, $" ; \"{text}\""); @@ -160,7 +160,7 @@ public void FormatChain(IScriptBuilder builder, IEventNode firstEvent, int inden } } - public void FormatEventSet(IScriptBuilder builder, IList events, int indent = 0) where T : IEventNode + void FormatEventSet(IScriptBuilder builder, IList events, int indent = 0) where T : IEventNode { if (builder == null) throw new ArgumentNullException(nameof(builder)); if (events == null) return; diff --git a/src/Formats/Exporters/Tiled/MapImport.cs b/src/Formats/Exporters/Tiled/MapImport.cs index 191f004cd..e9435a636 100644 --- a/src/Formats/Exporters/Tiled/MapImport.cs +++ b/src/Formats/Exporters/Tiled/MapImport.cs @@ -8,10 +8,9 @@ namespace UAlbion.Formats.Exporters.Tiled; public static class MapImport { - public static BaseMapData ToAlbion(this Map map, AssetInfo info, string script) + public static BaseMapData ToAlbion(this Map map, AssetId assetId, string script) { if (map == null) throw new ArgumentNullException(nameof(map)); - if (info == null) throw new ArgumentNullException(nameof(info)); // Check width/height <= 255 if (map.Width > 256) throw new FormatException($"Map widths above 256 are not currently supported (was {map.Width})"); @@ -26,7 +25,8 @@ public static BaseMapData ToAlbion(this Map map, AssetInfo info, string script) List markers = new(); List markerTiles = new(); ObjectGroupMapping.LoadObjectGroups( - info, map, + assetId, + map, is3d ? map.TileHeight : map.TileWidth, map.TileHeight, eventLayout, triggers, npcs, zones, @@ -38,7 +38,7 @@ public static BaseMapData ToAlbion(this Map map, AssetInfo info, string script) if (is3d) { var labId = MapperUtil.PropId(map, MapMapping.Prop.Labyrinth, true); - var map3d = new MapData3D(info.AssetId, paletteId, labId, (byte)map.Width, (byte)map.Height, eventLayout.Events, eventLayout.Chains, npcs, zones); + var map3d = new MapData3D(assetId, paletteId, labId, (byte)map.Width, (byte)map.Height, eventLayout.Events, eventLayout.Chains, npcs, zones); LayerMapping3D.ReadLayers(map3d, map.Layers); for (int i = 0; i < markerTiles.Count; i++) @@ -53,7 +53,7 @@ public static BaseMapData ToAlbion(this Map map, AssetInfo info, string script) { var tilesetId = MapperUtil.PropId(map, MapMapping.Prop.Tileset, true); var tiles = LayerMapping2D.ReadMapLayers(map); - var albionMap2d = new MapData2D(info.AssetId, paletteId, tilesetId, map.Width, map.Height, eventLayout.Events, eventLayout.Chains, npcs, zones); + var albionMap2d = new MapData2D(assetId, paletteId, tilesetId, map.Width, map.Height, eventLayout.Events, eventLayout.Chains, npcs, zones); Array.Copy(tiles, albionMap2d.Tiles, albionMap2d.Tiles.Length); albionMap = albionMap2d; } diff --git a/src/Formats/Exporters/Tiled/ObjectGroupMapping.cs b/src/Formats/Exporters/Tiled/ObjectGroupMapping.cs index eb3adb190..c3104355a 100644 --- a/src/Formats/Exporters/Tiled/ObjectGroupMapping.cs +++ b/src/Formats/Exporters/Tiled/ObjectGroupMapping.cs @@ -39,7 +39,8 @@ public static List BuildObjectGroups( } - public static void LoadObjectGroups(AssetInfo info, + public static void LoadObjectGroups( + AssetId assetId, Map map, int tileWidth, int tileHeight, @@ -50,7 +51,6 @@ public static void LoadObjectGroups(AssetInfo info, List markers, List markerTiles) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (map == null) throw new ArgumentNullException(nameof(map)); if (triggers == null) throw new ArgumentNullException(nameof(triggers)); if (npcs == null) throw new ArgumentNullException(nameof(npcs)); @@ -86,6 +86,6 @@ ushort ResolveEntryPoint(string name) } } - TriggerMapping.LoadZones(zones, info.AssetId, triggers, map); + TriggerMapping.LoadZones(zones, assetId, triggers, map); } } \ No newline at end of file diff --git a/src/Formats/Exporters/Tiled/StampLoader.cs b/src/Formats/Exporters/Tiled/StampLoader.cs index f4f7159ba..4a8ade8f4 100644 --- a/src/Formats/Exporters/Tiled/StampLoader.cs +++ b/src/Formats/Exporters/Tiled/StampLoader.cs @@ -2,6 +2,7 @@ using System.Text; using SerdesNet; using UAlbion.Config; +using UAlbion.Config.Properties; using UAlbion.Formats.Assets; using UAlbion.Formats.Ids; using UAlbion.Formats.Parsers; @@ -10,9 +11,9 @@ namespace UAlbion.Formats.Exporters.Tiled; public class StampLoader : IAssetLoader { - public BlockList Serdes(BlockList existing, AssetInfo info, ISerializer s, SerdesContext context) + public static readonly PathPatternProperty TilesetPattern = new("TilesetPattern", "../Tilesets/{0}_{2}.tsx"); + public BlockList Serdes(BlockList existing, ISerializer s, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (s == null) throw new ArgumentNullException(nameof(s)); if (context == null) throw new ArgumentNullException(nameof(context)); @@ -20,11 +21,11 @@ public BlockList Serdes(BlockList existing, AssetInfo info, ISerializer s, Serde { if (existing == null) throw new ArgumentNullException(nameof(existing)); - var blockListId = (BlockListId)info.AssetId; + var blockListId = (BlockListId)context.AssetId; var tilesetId = blockListId.ToTileset(); - var tilesetPattern = info.GetPattern(AssetProperty.TilesetPattern, "../Tilesets/{0}_{2}.tsx"); - var tilesetPath = tilesetPattern.Format(new AssetPath(tilesetId.Id, 0, null, ConfigUtil.AssetName(tilesetId))); + var tilesetPattern = context.GetProperty(TilesetPattern); + var tilesetPath = tilesetPattern.Format(new AssetPath(tilesetId, 0, null, ConfigUtil.AssetName(tilesetId))); var tileset = new Tileset { @@ -58,8 +59,8 @@ public BlockList Serdes(BlockList existing, AssetInfo info, ISerializer s, Serde return list; } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((BlockList) existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((BlockList) existing, s, context); /* .stamp file { diff --git a/src/Formats/Exporters/Tiled/TileMapping.cs b/src/Formats/Exporters/Tiled/TileMapping.cs index fc7ba385d..e6014db4e 100644 --- a/src/Formats/Exporters/Tiled/TileMapping.cs +++ b/src/Formats/Exporters/Tiled/TileMapping.cs @@ -4,6 +4,7 @@ using UAlbion.Config; using UAlbion.Formats.Assets.Labyrinth; using UAlbion.Formats.Assets.Maps; +using UAlbion.Formats.Ids; using static UAlbion.Formats.Exporters.Tiled.MapperUtil; namespace UAlbion.Formats.Exporters.Tiled; @@ -33,7 +34,7 @@ static class Prop public const string UseUnderlayFlags = "UseUnderlayFlags"; } - public static Tile BuildTile(int id, int index, ushort? imageNumber, List tileProperties, Tilemap2DProperties properties, AssetPathPattern graphicsPattern) + public static Tile BuildTile(TilesetId id, int index, ushort? imageNumber, List tileProperties, Tilemap2DProperties properties, AssetPathPattern graphicsPattern) { if (properties == null) throw new ArgumentNullException(nameof(properties)); if (graphicsPattern == null) throw new ArgumentNullException(nameof(graphicsPattern)); @@ -116,7 +117,7 @@ static ushort SourceStringToImageNumber(string source, Tilemap2DProperties prope { if (string.IsNullOrEmpty(source)) return 0; if (source == properties.BlankTilePath) return 0xffff; - if (!graphicsPattern.TryParse(source, out var assetPath)) return 0xffff; + if (!graphicsPattern.TryParse(source, AssetType.Unknown, out var assetPath)) return 0xffff; return (ushort)assetPath.SubAsset; } diff --git a/src/Formats/Exporters/Tiled/TiledMapLoader.cs b/src/Formats/Exporters/Tiled/TiledMapLoader.cs index 56e8a23e8..d0a23664a 100644 --- a/src/Formats/Exporters/Tiled/TiledMapLoader.cs +++ b/src/Formats/Exporters/Tiled/TiledMapLoader.cs @@ -3,72 +3,84 @@ using SerdesNet; using UAlbion.Api.Eventing; using UAlbion.Config; +using UAlbion.Config.Properties; using UAlbion.Formats.Assets.Maps; namespace UAlbion.Formats.Exporters.Tiled; public class TiledMapLoader : Component, IAssetLoader { - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((BaseMapData) existing, info, s, context); - - public BaseMapData Serdes(BaseMapData existing, AssetInfo info, ISerializer s, SerdesContext context) + // Isometric tileset/map properties + public static readonly IntAssetProperty BaseHeight = new("BaseHeight"); + public static readonly IntAssetProperty TileWidth = new("TileWidth"); + public static readonly PathPatternProperty TiledCeilingPattern = new("TiledCeilingPattern"); + public static readonly PathPatternProperty TiledContentsPattern = new("TiledContentsPattern"); + public static readonly PathPatternProperty TiledFloorPattern = new("TiledFloorPattern"); + public static readonly PathPatternProperty TiledWallPattern = new("TiledWallPattern"); + public static readonly StringAssetProperty LargeNpcs = new("LargeNpcs"); // string + public static readonly StringAssetProperty SmallNpcs = new("SmallNpcs"); // string + public static readonly PathPatternProperty TilesetPattern = new("TilesetPattern", "../Tilesets/{0}_{2}.tsx"); // string + public static readonly PathPatternProperty ScriptPattern = new("ScriptPattern"); // string + + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((BaseMapData) existing, s, context); + + public BaseMapData Serdes(BaseMapData existing, ISerializer s, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (s == null) throw new ArgumentNullException(nameof(s)); if (context == null) throw new ArgumentNullException(nameof(context)); if (!s.IsWriting()) - return Read(info, s, context); + return Read(s, context); - Write(existing, info, s, context); + Write(existing, s, context); return existing; } - static string GetScriptFilename(AssetInfo info) + static string GetScriptFilename(AssetLoadContext context) { - var scriptPattern = info.GetPattern(AssetProperty.ScriptPattern, ""); - return scriptPattern.Format(info); + var scriptPattern = context.GetProperty(ScriptPattern); + return scriptPattern.Format(context.BuildAssetPath()); } - void Write(BaseMapData existing, AssetInfo info, ISerializer s, SerdesContext context) + void Write(BaseMapData existing, ISerializer s, AssetLoadContext context) { (byte[] bytes, string script) = existing switch { - MapData2D map2d => Write2D(map2d, info, context), - MapData3D map3d => Write3D(map3d, info), + MapData2D map2d => Write2D(map2d, context), + MapData3D map3d => Write3D(map3d, context), _ => (null, null) }; if (bytes != null) s.Bytes(null, bytes, bytes.Length); else - Warn($"No bytes were generated when saving map {info.Id}"); + Warn($"No bytes were generated when saving map {context.AssetId}"); if (script == null) { - Warn($"No script for map {info.Id}, aborting script output"); + Warn($"No script for map {context.AssetId}, aborting script output"); return; } - var scriptPath = GetScriptFilename(info); + var scriptPath = GetScriptFilename(context); if (string.IsNullOrEmpty(scriptPath)) { - Warn($"No script path was set for map {info.Id}, aborting script output"); + Warn($"No script path was set for map {context.AssetId}, aborting script output"); return; } - var assetDir = GetAssetDir(info); + var assetDir = GetAssetDir(context); if (!context.Disk.DirectoryExists(assetDir)) context.Disk.CreateDirectory(assetDir); context.Disk.WriteAllText(Path.Combine(assetDir, scriptPath), script); } - BaseMapData Read(AssetInfo info, ISerializer s, SerdesContext context) + BaseMapData Read(ISerializer s, AssetLoadContext context) { - var assetDir = GetAssetDir(info); - var scriptPath = Path.Combine(assetDir, GetScriptFilename(info)); + var assetDir = GetAssetDir(context); + var scriptPath = Path.Combine(assetDir, GetScriptFilename(context)); string script = null; if (!string.IsNullOrEmpty(scriptPath) && context.Disk.FileExists(scriptPath)) script = context.Disk.ReadAllText(scriptPath); @@ -76,10 +88,10 @@ BaseMapData Read(AssetInfo info, ISerializer s, SerdesContext context) var bytes = s.Bytes(null, null, (int)s.BytesRemaining); using var ms = new MemoryStream(bytes); var map = Map.Parse(ms); - return map.ToAlbion(info, script); + return map.ToAlbion(context.AssetId, script); } - (byte[], string) Write2D(MapData2D map, AssetInfo info, SerdesContext context) + (byte[], string) Write2D(MapData2D map, AssetLoadContext context) { var assets = Resolve(); TilesetData tileset = assets.LoadTileData(map.TilesetId); @@ -89,66 +101,66 @@ BaseMapData Read(AssetInfo info, ISerializer s, SerdesContext context) return (null, null); } - var tilesetPattern = info.GetPattern(AssetProperty.TilesetPattern, "../Tilesets/{0}_{2}.tsx"); - var tilesetPath = tilesetPattern.Format(new AssetPath(map.TilesetId.Id, 0, null, ConfigUtil.AssetName(map.TilesetId))); + var tilesetPattern = context.GetProperty(TilesetPattern); + var tilesetPath = tilesetPattern.Format(new AssetPath(map.TilesetId, 0, null, ConfigUtil.AssetName(map.TilesetId))); var npcTilesetPath = map.MapType == MapType.TwoDOutdoors - ? info.Get(AssetProperty.SmallNpcs, "../Tilesets/SmallNPCs.tsx") - : info.Get(AssetProperty.LargeNpcs, "../Tilesets/LargeNPCs.tsx"); + ? context.GetProperty(SmallNpcs, "../Tilesets/SmallNPCs.tsx") + : context.GetProperty(LargeNpcs, "../Tilesets/LargeNPCs.tsx"); - var assetDir = GetAssetDir(info); + var assetDir = GetAssetDir(context); var npcTileset = Tileset.Load(Path.Combine(assetDir, npcTilesetPath), context.Disk); npcTileset.Filename = npcTilesetPath; // The path in the map file should be relative to the map path, not to the mod dir so replace it here. var properties = new Tilemap2DProperties { TileWidth = 16, TileHeight = 16 }; - var formatter = new EventFormatter(assets.LoadString, map.Id.ToMapText()); + var formatter = new EventFormatter(assets.LoadStringSafe, map.Id.ToMapText()); var (tiledMap, script) = MapExport.FromAlbionMap2D(map, tileset, properties, tilesetPath, npcTileset, formatter); var mapBytes = FormatUtil.BytesFromTextWriter(tiledMap.Serialize); return (mapBytes, script); } - (byte[], string) Write3D(MapData3D map, AssetInfo info) + (byte[], string) Write3D(MapData3D map, AssetLoadContext context) { var assets = Resolve(); var destModApplier = Resolve(); - var floorPattern = info.GetPattern(AssetProperty.TiledFloorPattern, ""); - var ceilingPattern = info.GetPattern(AssetProperty.TiledCeilingPattern, ""); - var wallPattern = info.GetPattern(AssetProperty.TiledWallPattern, ""); - var contentsPattern = info.GetPattern(AssetProperty.TiledContentsPattern, ""); + var floorPattern = context.GetProperty(TiledFloorPattern); + var ceilingPattern = context.GetProperty(TiledCeilingPattern); + var wallPattern = context.GetProperty(TiledWallPattern); + var contentsPattern = context.GetProperty(TiledContentsPattern); if (floorPattern.IsEmpty || ceilingPattern.IsEmpty || wallPattern.IsEmpty || contentsPattern.IsEmpty) return (Array.Empty(), null); - var labInfo = destModApplier.GetAssetInfo(map.LabDataId, null); + var labInfo = destModApplier.GetAssetInfo(map.LabDataId); if (labInfo == null) { Error($"Could not load asset info for lab {map.LabDataId} in map {map.Id}"); return (Array.Empty(), null); } - var assetPath = new AssetPath(labInfo); + var assetPath = new AssetPath(map.LabDataId, 0, labInfo.PaletteId.Id); var properties = new Tilemap3DProperties { - TileWidth = info.Get(AssetProperty.TileWidth, 0), - TileHeight = info.Get(AssetProperty.BaseHeight, 0), + TileWidth = context.GetProperty(TileWidth, 0), + TileHeight = context.GetProperty(BaseHeight), FloorPath = floorPattern.Format(assetPath), CeilingPath = ceilingPattern.Format(assetPath), WallPath = wallPattern.Format(assetPath), ContentsPath = contentsPattern.Format(assetPath), }; - var formatter = new EventFormatter(assets.LoadString, map.Id.ToMapText()); + var formatter = new EventFormatter(assets.LoadStringSafe, map.Id.ToMapText()); var (tiledMap, script) = MapExport.FromAlbionMap3D(map, properties, formatter); var mapBytes = FormatUtil.BytesFromTextWriter(tiledMap.Serialize); return (mapBytes, script); } - string GetAssetDir(AssetInfo info) + string GetAssetDir(AssetLoadContext context) { var pathResolver = Resolve(); - var destPath = pathResolver.ResolvePath(info.File.Filename); + var destPath = pathResolver.ResolvePath(context.Filename); return destPath; } } diff --git a/src/Formats/Exporters/Tiled/TiledTilesetLoader.cs b/src/Formats/Exporters/Tiled/TiledTilesetLoader.cs index 7641ce705..0ba0d575b 100644 --- a/src/Formats/Exporters/Tiled/TiledTilesetLoader.cs +++ b/src/Formats/Exporters/Tiled/TiledTilesetLoader.cs @@ -3,22 +3,25 @@ using SerdesNet; using UAlbion.Api.Eventing; using UAlbion.Config; +using UAlbion.Config.Properties; using UAlbion.Formats.Assets.Maps; namespace UAlbion.Formats.Exporters.Tiled; public class TiledTilesetLoader : Component, IAssetLoader { - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((TilesetData)existing, info, s, context); + public static readonly StringAssetProperty BlankTilePathProperty = new("BlankTilePath"); + public static readonly PathPatternProperty GraphicsPattern = new("GraphicsPattern"); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((TilesetData)existing, s, context); - public TilesetData Serdes(TilesetData existing, AssetInfo info, ISerializer s, SerdesContext context) + public TilesetData Serdes(TilesetData existing, ISerializer s, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (s == null) throw new ArgumentNullException(nameof(s)); + if (context == null) throw new ArgumentNullException(nameof(context)); - var graphicsTemplate = info.Get(AssetProperty.GraphicsPattern, "{0}/{0}_{1}.png"); - var blankTilePath = info.Get(AssetProperty.BlankTilePath, "Blank.png"); + var graphicsTemplate = context.GetProperty(GraphicsPattern, AssetPathPattern.Build("{0}/{0}_{1}.png")); + var blankTilePath = context.GetProperty(BlankTilePathProperty, "Blank.png"); var properties = new Tilemap2DProperties { @@ -28,15 +31,17 @@ public TilesetData Serdes(TilesetData existing, AssetInfo info, ISerializer s, S TileHeight = 16 }; - return s.IsWriting() ? Save(existing, properties, s) : Load(info, properties, s); + return s.IsWriting() + ? Save(existing, properties, s) + : Load(context, properties, s); } - static TilesetData Load(AssetInfo info, Tilemap2DProperties properties, ISerializer serializer) + static TilesetData Load(AssetLoadContext context, Tilemap2DProperties properties, ISerializer serializer) { var xmlBytes = serializer.Bytes(null, null, (int)serializer.BytesRemaining); using var ms = new MemoryStream(xmlBytes); var tileset = Tileset.Parse(ms); - return TilesetMapping.ToAlbion(tileset, info.AssetId, properties); + return TilesetMapping.ToAlbion(tileset, context.AssetId, properties); } static TilesetData Save(TilesetData tileset, Tilemap2DProperties properties, ISerializer s) diff --git a/src/Formats/Exporters/Tiled/TilemapProperties.cs b/src/Formats/Exporters/Tiled/TilemapProperties.cs index b2ee6cb76..ba565c6ec 100644 --- a/src/Formats/Exporters/Tiled/TilemapProperties.cs +++ b/src/Formats/Exporters/Tiled/TilemapProperties.cs @@ -1,4 +1,5 @@ -using UAlbion.Formats.Assets.Labyrinth; +using UAlbion.Config; +using UAlbion.Formats.Assets.Labyrinth; namespace UAlbion.Formats.Exporters.Tiled; @@ -12,7 +13,7 @@ public class TilemapProperties public class Tilemap2DProperties : TilemapProperties { - public string GraphicsTemplate { get; set; } + public AssetPathPattern GraphicsTemplate { get; set; } } public class Tilemap3DProperties : TilemapProperties diff --git a/src/Formats/Exporters/Tiled/TilesetMapping.cs b/src/Formats/Exporters/Tiled/TilesetMapping.cs index 552940638..dccbaa229 100644 --- a/src/Formats/Exporters/Tiled/TilesetMapping.cs +++ b/src/Formats/Exporters/Tiled/TilesetMapping.cs @@ -22,13 +22,13 @@ public static Tileset FromAlbion(TilesetData tileset, Tilemap2DProperties proper if (tileset == null) throw new ArgumentNullException(nameof(tileset)); if (properties == null) throw new ArgumentNullException(nameof(properties)); - var graphicsPattern = AssetPathPattern.Build(properties.GraphicsTemplate); + var graphicsPattern = properties.GraphicsTemplate; List tiles = tileset.Tiles .Where(x => !x.IsBlank) .Select(x => TileMapping.BuildTile( - tileset.Id.Id, + tileset.Id, x.Index, x.FrameCount > 0 ? x.ImageNumber : null, TileMapping.BuildTileProperties(x), @@ -50,7 +50,7 @@ public static Tileset FromAlbion(TilesetData tileset, Tilemap2DProperties proper for (int f = 1; f < sourceTile.FrameCount; f++) { tiles.Add(TileMapping.BuildTile( - tileset.Id.Id, + tileset.Id, nextId, (ushort)(sourceTile.ImageNumber + f), new List { new(Prop.Frame, true) }, @@ -86,7 +86,7 @@ public static TilesetData ToAlbion(Tileset tileset, TilesetId id, Tilemap2DPrope if (properties == null) throw new ArgumentNullException(nameof(properties)); var t = new TilesetData(id); - var graphicsPattern = AssetPathPattern.Build(properties.GraphicsTemplate); + var graphicsPattern = properties.GraphicsTemplate; var tileLookup = tileset.Tiles .Select(x => TileMapping.InterpretTile(x, properties, graphicsPattern)) diff --git a/src/Formats/Exporters/Tiled/TriggerMapping.cs b/src/Formats/Exporters/Tiled/TriggerMapping.cs index 867cc027b..a49f19fdd 100644 --- a/src/Formats/Exporters/Tiled/TriggerMapping.cs +++ b/src/Formats/Exporters/Tiled/TriggerMapping.cs @@ -94,6 +94,19 @@ static List BuildTriggerProperties(ZoneKey zone, Dictionary $"#{GetHashForString(s) & 0x00ffffff:x}"; + static int GetHashForString(string s) + { + if (string.IsNullOrEmpty(s)) return 0; + int result = 0; + foreach (var c in s) + { + result *= 17; + result ^= char.ToUpperInvariant(c); + } + return result; + } + static ObjectGroup BuildTriggerObjectGroup( int objectGroupId, string name, @@ -121,7 +134,7 @@ from r in polygons { Id = objectGroupId, Name = name, - Color = "#" + (name.GetHashCode(StringComparison.InvariantCulture) & 0x00ffffff).ToString("x"), + Color = GetColorForString(name), Opacity = 0.5f, Objects = zonePolygons.ToList(), }; diff --git a/src/Formats/FormatUtil.cs b/src/Formats/FormatUtil.cs index 18d8e4980..f3eb19d7a 100644 --- a/src/Formats/FormatUtil.cs +++ b/src/Formats/FormatUtil.cs @@ -6,9 +6,6 @@ using System.Text; using SerdesNet; using UAlbion.Api; -using UAlbion.Config; -using UAlbion.Formats.Assets; -using UAlbion.Formats.Ids; using UAlbion.Formats.MapEvents; namespace UAlbion.Formats; @@ -121,14 +118,6 @@ public static byte[] HexStringToBytes(string s) return ranges; } - public static StringId ResolveTextId(TextId id) - { - var result = AssetMapping.Global.TextIdToStringId(id); - return result.HasValue - ? new StringId(result.Value.Item1, result.Value.Item2) - : new StringId(id, 0); - } - public static bool Compare(QueryOperation operation, int value, int immediate) => operation switch { diff --git a/src/Formats/IAssetLoader.cs b/src/Formats/IAssetLoader.cs deleted file mode 100644 index bb9032f21..000000000 --- a/src/Formats/IAssetLoader.cs +++ /dev/null @@ -1,14 +0,0 @@ -using SerdesNet; -using UAlbion.Config; - -namespace UAlbion.Formats; - -public interface IAssetLoader -{ - object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context); -} - -public interface IAssetLoader : IAssetLoader where T : class -{ - T Serdes(T existing, AssetInfo info, ISerializer s, SerdesContext context); // SerDes = Serialise / Deserialise. -} \ No newline at end of file diff --git a/src/Formats/IAssetManager.cs b/src/Formats/IAssetManager.cs index 95c74fcd4..7afdffb35 100644 --- a/src/Formats/IAssetManager.cs +++ b/src/Formats/IAssetManager.cs @@ -16,7 +16,7 @@ namespace UAlbion.Formats; public interface IAssetManager : ITextureLoader { - AssetInfo GetAssetInfo(AssetId id, string language = null); + AssetNode GetAssetInfo(AssetId id, string language = null); ITexture LoadTexture(SpriteId id); ITileGraphics LoadTileGraphics(TilesetGfxId id); object LoadMapObject(MapObjectId id); // Might be an ITexture or a Mesh @@ -27,10 +27,11 @@ public interface IAssetManager : ITextureLoader LabyrinthData LoadLabyrinthData(LabyrinthId id); bool IsStringDefined(TextId id, string language); bool IsStringDefined(StringId id, string language); - string LoadString(TextId id); - string LoadString(StringId id); - string LoadString(TextId id, string language); - string LoadString(StringId id, string language); + string LoadStringSafe(TextId id, string language = null); // never returns null - will give a "missing string" string instead + string LoadStringSafe(StringId id, string language = null); // never returns null - will give a "missing string" string instead + string LoadStringRaw(TextId id, string language = null); // can return null + string LoadStringRaw(StringId id, string language = null); // can return null + IStringSet LoadStringSet(StringSetId id, string language); ISample LoadSample(SampleId id); WaveLib LoadWaveLib(WaveLibraryId waveLibraryId); FlicFile LoadVideo(VideoId id); diff --git a/src/Formats/IAssetPostProcessor.cs b/src/Formats/IAssetPostProcessor.cs deleted file mode 100644 index 0f8d80e69..000000000 --- a/src/Formats/IAssetPostProcessor.cs +++ /dev/null @@ -1,8 +0,0 @@ -using UAlbion.Config; - -namespace UAlbion.Formats; - -public interface IAssetPostProcessor -{ - object Process(object asset, AssetInfo info); -} \ No newline at end of file diff --git a/src/Formats/IAssetPostProcessorRegistry.cs b/src/Formats/IAssetPostProcessorRegistry.cs index a83940d41..e922518b1 100644 --- a/src/Formats/IAssetPostProcessorRegistry.cs +++ b/src/Formats/IAssetPostProcessorRegistry.cs @@ -1,6 +1,9 @@ -namespace UAlbion.Formats; +using System; +using UAlbion.Config; + +namespace UAlbion.Formats; public interface IAssetPostProcessorRegistry { - IAssetPostProcessor GetPostProcessor(string postProcessorName); + IAssetPostProcessor GetPostProcessor(Type postProcessorType); } \ No newline at end of file diff --git a/src/Formats/IModApplier.cs b/src/Formats/IModApplier.cs index f78ae0845..ce9f238ff 100644 --- a/src/Formats/IModApplier.cs +++ b/src/Formats/IModApplier.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text.RegularExpressions; +using System.Collections.Generic; using UAlbion.Api.Eventing; using UAlbion.Config; using UAlbion.Formats.Assets.Save; @@ -9,21 +7,17 @@ namespace UAlbion.Formats; public interface IModApplier : IComponent { - void LoadMods(AssetMapping mapping, IPathResolver pathResolver, IReadOnlyList mods); - AssetInfo GetAssetInfo(AssetId key, string language); - object LoadAsset(AssetId id); - object LoadAsset(AssetId id, string language); - object LoadAssetCached(AssetId assetId); - string LoadAssetAnnotated(AssetId id, string language); - SavedGame LoadSavedGame(string path); IReadOnlyDictionary Languages { get; } IEnumerable ShaderPaths { get; } - delegate (object asset, AssetInfo info) AssetLoader(AssetId id, string language); - void SaveAssets( - AssetLoader loaderFunc, - Action flushCacheFunc, - ISet ids, - ISet assetTypes, - Regex filePattern); + void LoadMods(AssetMapping mapping, IPathResolver pathResolver, IReadOnlyList mods); + SavedGame LoadSavedGame(string path); + AssetNode GetAssetInfo(AssetId key, string language = null); + + object LoadAsset(AssetId id, string language = null); + object LoadAssetCached(AssetId id, string language = null); + string LoadAssetAnnotated(AssetId id, string language = null); + + AssetLoadResult LoadAssetAndNode(AssetId assetId, string language = null); + void SaveAssets(AssetConversionOptions options); } diff --git a/src/Formats/Ids/EventSetId.g.cs b/src/Formats/Ids/EventSetId.g.cs index a1da90159..742aa5214 100644 --- a/src/Formats/Ids/EventSetId.g.cs +++ b/src/Formats/Ids/EventSetId.g.cs @@ -131,7 +131,7 @@ public static EventSetId SerdesU16BE(string name, EventSetId id, AssetMapping ma public override bool Equals(object obj) => obj is IAssetId other && other.ToUInt32() == _value; public int CompareTo(object obj) => (obj is IAssetId other) ? _value.CompareTo(other.ToUInt32()) : -1; public override int GetHashCode() => unchecked((int)_value); - public readonly TextId ToEventText() => new TextId(AssetType.EventText, Id); + public readonly StringSetId ToEventText() => new StringSetId(AssetType.EventText, Id); } public class EventSetIdConverter : TypeConverter diff --git a/src/Formats/Ids/MapId.g.cs b/src/Formats/Ids/MapId.g.cs index dc877bf85..97b195361 100644 --- a/src/Formats/Ids/MapId.g.cs +++ b/src/Formats/Ids/MapId.g.cs @@ -131,7 +131,7 @@ public static MapId SerdesU16BE(string name, MapId id, AssetMapping mapping, ISe public override bool Equals(object obj) => obj is IAssetId other && other.ToUInt32() == _value; public int CompareTo(object obj) => (obj is IAssetId other) ? _value.CompareTo(other.ToUInt32()) : -1; public override int GetHashCode() => unchecked((int)_value); - public readonly TextId ToMapText() => new TextId(AssetType.MapText, Id); + public readonly StringSetId ToMapText() => new StringSetId(AssetType.MapText, Id); } public class MapIdConverter : TypeConverter diff --git a/src/Formats/Ids/MapTextId.g.cs b/src/Formats/Ids/MapTextId.g.cs deleted file mode 100644 index 31104e9f2..000000000 --- a/src/Formats/Ids/MapTextId.g.cs +++ /dev/null @@ -1,148 +0,0 @@ -// Note: This file was automatically generated using Tools/CodeGenerator. -// No changes should be made to this file by hand. Instead, the relevant json -// files should be modified and then GenerateEnums should be used to regenerate -// the various types. -using System; -using System.ComponentModel; -using System.Globalization; -using System.Text.Json.Serialization; -using SerdesNet; -using UAlbion.Api; -using UAlbion.Config; - -namespace UAlbion.Formats.Ids; - -[JsonConverter(typeof(ToStringJsonConverter))] -[TypeConverter(typeof(MapTextIdConverter))] -public readonly struct MapTextId : IEquatable, IEquatable, IComparable, IAssetId -{ - readonly uint _value; - public MapTextId(AssetType type, int id = 0) - { - if (!(type == AssetType.None || type == AssetType.MapText)) - throw new ArgumentOutOfRangeException($"Tried to construct a MapTextId with a type of {type}"); -#if DEBUG - if (id < 0 || id > 0xffffff) - throw new ArgumentOutOfRangeException($"Tried to construct a MapTextId with out of range id {id}"); -#endif - _value = (uint)type << 24 | (uint)id; - } - - public MapTextId(int id) - { -#if DEBUG - if (id < 0 || id > 0xffffff) - throw new ArgumentOutOfRangeException($"Tried to construct a MapTextId with out of range id {id}"); -#endif - _value = (uint)AssetType.MapText << 24 | (uint)id; - } - - MapTextId(uint id) - { - _value = id; - if (!(Type == AssetType.None || Type == AssetType.MapText)) - throw new ArgumentOutOfRangeException($"Tried to construct a MapTextId with a type of {Type}"); - } - - public MapTextId(IAssetId id) - { - _value = id.ToUInt32(); - if (!(Type == AssetType.None || Type == AssetType.MapText)) - throw new ArgumentOutOfRangeException($"Tried to construct a MapTextId with a type of {Type}"); - } - - public static MapTextId From(T id) where T : unmanaged, Enum => (MapTextId)AssetMapping.Global.EnumToId(id); - - public int ToDisk(AssetMapping mapping) - { - if (mapping == null) throw new ArgumentNullException(nameof(mapping)); - var (enumType, enumValue) = AssetMapping.Global.IdToEnum(this); - return mapping.EnumToId(enumType, enumValue).Id; - } - - public static MapTextId FromDisk(int disk, AssetMapping mapping) - { - if (mapping == null) throw new ArgumentNullException(nameof(mapping)); - var (enumType, enumValue) = mapping.IdToEnum(new MapTextId(AssetType.MapText, disk)); - return (MapTextId)AssetMapping.Global.EnumToId(enumType, enumValue); - } - - public static MapTextId SerdesU8(string name, MapTextId id, AssetMapping mapping, ISerializer s) - { - if (s == null) throw new ArgumentNullException(nameof(s)); - - byte diskValue = (byte)id.ToDisk(mapping); - diskValue = s.UInt8(name, diskValue); - id = FromDisk(diskValue, mapping); - if (s.IsCommenting()) s.Comment(id.ToString(), true); - return id; - } - - public static MapTextId SerdesU16(string name, MapTextId id, AssetMapping mapping, ISerializer s) - { - if (s == null) throw new ArgumentNullException(nameof(s)); - - ushort diskValue = (ushort)id.ToDisk(mapping); - diskValue = s.UInt16(name, diskValue); - id = FromDisk(diskValue, mapping); - if (s.IsCommenting()) s.Comment(id.ToString(), true); - return id; - } - - public static MapTextId SerdesU16BE(string name, MapTextId id, AssetMapping mapping, ISerializer s) - { - if (s == null) throw new ArgumentNullException(nameof(s)); - - ushort diskValue = (ushort)id.ToDisk(mapping); - diskValue = s.UInt16BE(name, diskValue); - id = FromDisk(diskValue, mapping); - if (s.IsCommenting()) s.Comment(id.ToString(), true); - return id; - } - - public readonly AssetType Type => (AssetType)((_value & 0xff00_0000) >> 24); - public readonly int Id => (int)(_value & 0xffffff); - public static MapTextId None => new MapTextId(AssetType.None); - public bool IsNone => Type == AssetType.None; - - public override string ToString() => AssetMapping.Global.IdToName(this); - public string ToStringNumeric() => Id.ToString(); - public static AssetType[] ValidTypes = { AssetType.MapText }; - public static MapTextId Parse(string s) => AssetMapping.Global.Parse(s, ValidTypes); - - public static implicit operator AssetId(MapTextId id) => AssetId.FromUInt32(id._value); - public static implicit operator MapTextId(AssetId id) => new MapTextId(id.ToUInt32()); - public static implicit operator TextId(MapTextId id) => TextId.FromUInt32(id._value); - public static explicit operator MapTextId(TextId id) => new MapTextId(id.ToUInt32()); - public static implicit operator MapTextId(UAlbion.Base.MapText id) => MapTextId.From(id); - - public readonly int ToInt32() => unchecked((int)_value); - public readonly uint ToUInt32() => _value; - public static MapTextId FromInt32(int id) => new MapTextId(unchecked((uint)id)); - public static MapTextId FromUInt32(uint id) => new MapTextId(id); - public static bool operator ==(MapTextId x, MapTextId y) => x.Equals(y); - public static bool operator !=(MapTextId x, MapTextId y) => !(x == y); - public static bool operator ==(MapTextId x, AssetId y) => x.Equals(y); - public static bool operator !=(MapTextId x, AssetId y) => !(x == y); - public static bool operator <(MapTextId x, MapTextId y) => x.CompareTo(y) == -1; - public static bool operator >(MapTextId x, MapTextId y) => x.CompareTo(y) == 1; - public static bool operator <=(MapTextId x, MapTextId y) => x.CompareTo(y) != 1; - public static bool operator >=(MapTextId x, MapTextId y) => x.CompareTo(y) != -1; - public bool Equals(MapTextId other) => _value == other._value; - public bool Equals(AssetId other) => _value == other.ToUInt32(); - public override bool Equals(object obj) => obj is IAssetId other && other.ToUInt32() == _value; - public int CompareTo(object obj) => (obj is IAssetId other) ? _value.CompareTo(other.ToUInt32()) : -1; - public override int GetHashCode() => unchecked((int)_value); -} - -public class MapTextIdConverter : TypeConverter -{ - public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) - => sourceType == typeof(string) ? true : base.CanConvertFrom(context, sourceType); - - public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) - => value is string s ? MapTextId.Parse(s) : base.ConvertFrom(context, culture, value); - - public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) - => destinationType == typeof(string) ? value.ToString() : base.ConvertTo(context, culture, value, destinationType); -} \ No newline at end of file diff --git a/src/Formats/Ids/SpecialId.g.cs b/src/Formats/Ids/SpecialId.g.cs index d1f024107..23295ffa4 100644 --- a/src/Formats/Ids/SpecialId.g.cs +++ b/src/Formats/Ids/SpecialId.g.cs @@ -112,8 +112,8 @@ public static SpecialId SerdesU16BE(string name, SpecialId id, AssetMapping mapp public static implicit operator AssetId(SpecialId id) => AssetId.FromUInt32(id._value); public static implicit operator SpecialId(AssetId id) => new SpecialId(id.ToUInt32()); - public static implicit operator TextId(SpecialId id) => TextId.FromUInt32(id._value); - public static explicit operator SpecialId(TextId id) => new SpecialId(id.ToUInt32()); + public static implicit operator StringSetId(SpecialId id) => StringSetId.FromUInt32(id._value); + public static explicit operator SpecialId(StringSetId id) => new SpecialId(id.ToUInt32()); public static implicit operator SpecialId(UAlbion.Base.Special id) => SpecialId.From(id); public readonly int ToInt32() => unchecked((int)_value); diff --git a/src/Formats/Ids/EventTextId.g.cs b/src/Formats/Ids/StringSetId.g.cs similarity index 54% rename from src/Formats/Ids/EventTextId.g.cs rename to src/Formats/Ids/StringSetId.g.cs index f929b4334..55b405f06 100644 --- a/src/Formats/Ids/EventTextId.g.cs +++ b/src/Formats/Ids/StringSetId.g.cs @@ -12,46 +12,37 @@ namespace UAlbion.Formats.Ids; -[JsonConverter(typeof(ToStringJsonConverter))] -[TypeConverter(typeof(EventTextIdConverter))] -public readonly struct EventTextId : IEquatable, IEquatable, IComparable, IAssetId +[JsonConverter(typeof(ToStringJsonConverter))] +[TypeConverter(typeof(StringSetIdConverter))] +public readonly struct StringSetId : IEquatable, IEquatable, IComparable, IAssetId { readonly uint _value; - public EventTextId(AssetType type, int id = 0) + public StringSetId(AssetType type, int id = 0) { - if (!(type == AssetType.None || type == AssetType.EventText)) - throw new ArgumentOutOfRangeException($"Tried to construct a EventTextId with a type of {type}"); + if (!(type == AssetType.None || type >= AssetType.EventText && type <= AssetType.Special)) + throw new ArgumentOutOfRangeException($"Tried to construct a StringSetId with a type of {type}"); #if DEBUG if (id < 0 || id > 0xffffff) - throw new ArgumentOutOfRangeException($"Tried to construct a EventTextId with out of range id {id}"); + throw new ArgumentOutOfRangeException($"Tried to construct a StringSetId with out of range id {id}"); #endif _value = (uint)type << 24 | (uint)id; } - public EventTextId(int id) - { -#if DEBUG - if (id < 0 || id > 0xffffff) - throw new ArgumentOutOfRangeException($"Tried to construct a EventTextId with out of range id {id}"); -#endif - _value = (uint)AssetType.EventText << 24 | (uint)id; - } - - EventTextId(uint id) + StringSetId(uint id) { _value = id; - if (!(Type == AssetType.None || Type == AssetType.EventText)) - throw new ArgumentOutOfRangeException($"Tried to construct a EventTextId with a type of {Type}"); + if (!(Type == AssetType.None || Type >= AssetType.EventText && Type <= AssetType.Special)) + throw new ArgumentOutOfRangeException($"Tried to construct a StringSetId with a type of {Type}"); } - public EventTextId(IAssetId id) + public StringSetId(IAssetId id) { _value = id.ToUInt32(); - if (!(Type == AssetType.None || Type == AssetType.EventText)) - throw new ArgumentOutOfRangeException($"Tried to construct a EventTextId with a type of {Type}"); + if (!(Type == AssetType.None || Type >= AssetType.EventText && Type <= AssetType.Special)) + throw new ArgumentOutOfRangeException($"Tried to construct a StringSetId with a type of {Type}"); } - public static EventTextId From(T id) where T : unmanaged, Enum => (EventTextId)AssetMapping.Global.EnumToId(id); + public static StringSetId From(T id) where T : unmanaged, Enum => (StringSetId)AssetMapping.Global.EnumToId(id); public int ToDisk(AssetMapping mapping) { @@ -60,88 +51,91 @@ public int ToDisk(AssetMapping mapping) return mapping.EnumToId(enumType, enumValue).Id; } - public static EventTextId FromDisk(int disk, AssetMapping mapping) + public static StringSetId FromDisk(AssetType type, int disk, AssetMapping mapping) { if (mapping == null) throw new ArgumentNullException(nameof(mapping)); - var (enumType, enumValue) = mapping.IdToEnum(new EventTextId(AssetType.EventText, disk)); - return (EventTextId)AssetMapping.Global.EnumToId(enumType, enumValue); + if (!(type == AssetType.None || type >= AssetType.EventText && type <= AssetType.Special)) + throw new ArgumentOutOfRangeException($"Tried to construct a StringSetId with a type of {type}"); + + var (enumType, enumValue) = mapping.IdToEnum(new StringSetId(type, disk)); + return (StringSetId)AssetMapping.Global.EnumToId(enumType, enumValue); } - public static EventTextId SerdesU8(string name, EventTextId id, AssetMapping mapping, ISerializer s) + public static StringSetId SerdesU8(string name, StringSetId id, AssetType type, AssetMapping mapping, ISerializer s) { if (s == null) throw new ArgumentNullException(nameof(s)); byte diskValue = (byte)id.ToDisk(mapping); diskValue = s.UInt8(name, diskValue); - id = FromDisk(diskValue, mapping); + id = FromDisk(type, diskValue, mapping); if (s.IsCommenting()) s.Comment(id.ToString(), true); return id; } - public static EventTextId SerdesU16(string name, EventTextId id, AssetMapping mapping, ISerializer s) + public static StringSetId SerdesU16(string name, StringSetId id, AssetType type, AssetMapping mapping, ISerializer s) { if (s == null) throw new ArgumentNullException(nameof(s)); ushort diskValue = (ushort)id.ToDisk(mapping); diskValue = s.UInt16(name, diskValue); - id = FromDisk(diskValue, mapping); + id = FromDisk(type, diskValue, mapping); if (s.IsCommenting()) s.Comment(id.ToString(), true); return id; } - public static EventTextId SerdesU16BE(string name, EventTextId id, AssetMapping mapping, ISerializer s) + public static StringSetId SerdesU16BE(string name, StringSetId id, AssetType type, AssetMapping mapping, ISerializer s) { if (s == null) throw new ArgumentNullException(nameof(s)); ushort diskValue = (ushort)id.ToDisk(mapping); diskValue = s.UInt16BE(name, diskValue); - id = FromDisk(diskValue, mapping); + id = FromDisk(type, diskValue, mapping); if (s.IsCommenting()) s.Comment(id.ToString(), true); return id; } public readonly AssetType Type => (AssetType)((_value & 0xff00_0000) >> 24); public readonly int Id => (int)(_value & 0xffffff); - public static EventTextId None => new EventTextId(AssetType.None); + public static StringSetId None => new StringSetId(AssetType.None); public bool IsNone => Type == AssetType.None; public override string ToString() => AssetMapping.Global.IdToName(this); public string ToStringNumeric() => Id.ToString(); - public static AssetType[] ValidTypes = { AssetType.EventText }; - public static EventTextId Parse(string s) => AssetMapping.Global.Parse(s, ValidTypes); + public static AssetType[] ValidTypes = { AssetType.EventText, AssetType.MapText, AssetType.Special }; + public static StringSetId Parse(string s) => AssetMapping.Global.Parse(s, ValidTypes); - public static implicit operator AssetId(EventTextId id) => AssetId.FromUInt32(id._value); - public static implicit operator EventTextId(AssetId id) => new EventTextId(id.ToUInt32()); - public static implicit operator TextId(EventTextId id) => TextId.FromUInt32(id._value); - public static explicit operator EventTextId(TextId id) => new EventTextId(id.ToUInt32()); - public static implicit operator EventTextId(UAlbion.Base.EventText id) => EventTextId.From(id); + public static implicit operator AssetId(StringSetId id) => AssetId.FromUInt32(id._value); + public static implicit operator StringSetId(AssetId id) => new StringSetId(id.ToUInt32()); + public static implicit operator StringSetId(UAlbion.Base.EventText id) => StringSetId.From(id); + public static implicit operator StringSetId(UAlbion.Base.MapText id) => StringSetId.From(id); + public static implicit operator StringSetId(UAlbion.Base.Special id) => StringSetId.From(id); public readonly int ToInt32() => unchecked((int)_value); public readonly uint ToUInt32() => _value; - public static EventTextId FromInt32(int id) => new EventTextId(unchecked((uint)id)); - public static EventTextId FromUInt32(uint id) => new EventTextId(id); - public static bool operator ==(EventTextId x, EventTextId y) => x.Equals(y); - public static bool operator !=(EventTextId x, EventTextId y) => !(x == y); - public static bool operator ==(EventTextId x, AssetId y) => x.Equals(y); - public static bool operator !=(EventTextId x, AssetId y) => !(x == y); - public static bool operator <(EventTextId x, EventTextId y) => x.CompareTo(y) == -1; - public static bool operator >(EventTextId x, EventTextId y) => x.CompareTo(y) == 1; - public static bool operator <=(EventTextId x, EventTextId y) => x.CompareTo(y) != 1; - public static bool operator >=(EventTextId x, EventTextId y) => x.CompareTo(y) != -1; - public bool Equals(EventTextId other) => _value == other._value; + public static StringSetId FromInt32(int id) => new StringSetId(unchecked((uint)id)); + public static StringSetId FromUInt32(uint id) => new StringSetId(id); + public static bool operator ==(StringSetId x, StringSetId y) => x.Equals(y); + public static bool operator !=(StringSetId x, StringSetId y) => !(x == y); + public static bool operator ==(StringSetId x, AssetId y) => x.Equals(y); + public static bool operator !=(StringSetId x, AssetId y) => !(x == y); + public static bool operator <(StringSetId x, StringSetId y) => x.CompareTo(y) == -1; + public static bool operator >(StringSetId x, StringSetId y) => x.CompareTo(y) == 1; + public static bool operator <=(StringSetId x, StringSetId y) => x.CompareTo(y) != 1; + public static bool operator >=(StringSetId x, StringSetId y) => x.CompareTo(y) != -1; + public bool Equals(StringSetId other) => _value == other._value; public bool Equals(AssetId other) => _value == other.ToUInt32(); public override bool Equals(object obj) => obj is IAssetId other && other.ToUInt32() == _value; public int CompareTo(object obj) => (obj is IAssetId other) ? _value.CompareTo(other.ToUInt32()) : -1; public override int GetHashCode() => unchecked((int)_value); } -public class EventTextIdConverter : TypeConverter +public class StringSetIdConverter : TypeConverter { public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) => sourceType == typeof(string) ? true : base.CanConvertFrom(context, sourceType); public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) - => value is string s ? EventTextId.Parse(s) : base.ConvertFrom(context, culture, value); + => value is string s ? StringSetId.Parse(s) : base.ConvertFrom(context, culture, value); public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) => destinationType == typeof(string) ? value.ToString() : base.ConvertTo(context, culture, value, destinationType); diff --git a/src/Formats/Ids/TextId.g.cs b/src/Formats/Ids/TextId.g.cs index dabb98a8f..79f6e6ce7 100644 --- a/src/Formats/Ids/TextId.g.cs +++ b/src/Formats/Ids/TextId.g.cs @@ -19,7 +19,7 @@ namespace UAlbion.Formats.Ids; readonly uint _value; public TextId(AssetType type, int id = 0) { - if (!(type == AssetType.None || type >= AssetType.EventText && type <= AssetType.Word || type == AssetType.Special)) + if (!(type == AssetType.None || type >= AssetType.ItemName && type <= AssetType.Word)) throw new ArgumentOutOfRangeException($"Tried to construct a TextId with a type of {type}"); #if DEBUG if (id < 0 || id > 0xffffff) @@ -31,14 +31,14 @@ public TextId(AssetType type, int id = 0) TextId(uint id) { _value = id; - if (!(Type == AssetType.None || Type >= AssetType.EventText && Type <= AssetType.Word || Type == AssetType.Special)) + if (!(Type == AssetType.None || Type >= AssetType.ItemName && Type <= AssetType.Word)) throw new ArgumentOutOfRangeException($"Tried to construct a TextId with a type of {Type}"); } public TextId(IAssetId id) { _value = id.ToUInt32(); - if (!(Type == AssetType.None || Type >= AssetType.EventText && Type <= AssetType.Word || Type == AssetType.Special)) + if (!(Type == AssetType.None || Type >= AssetType.ItemName && Type <= AssetType.Word)) throw new ArgumentOutOfRangeException($"Tried to construct a TextId with a type of {Type}"); } @@ -54,7 +54,7 @@ public int ToDisk(AssetMapping mapping) public static TextId FromDisk(AssetType type, int disk, AssetMapping mapping) { if (mapping == null) throw new ArgumentNullException(nameof(mapping)); - if (!(type == AssetType.None || type >= AssetType.EventText && type <= AssetType.Word || type == AssetType.Special)) + if (!(type == AssetType.None || type >= AssetType.ItemName && type <= AssetType.Word)) throw new ArgumentOutOfRangeException($"Tried to construct a TextId with a type of {type}"); var (enumType, enumValue) = mapping.IdToEnum(new TextId(type, disk)); @@ -101,17 +101,12 @@ public static TextId SerdesU16BE(string name, TextId id, AssetType type, AssetMa public override string ToString() => AssetMapping.Global.IdToName(this); public string ToStringNumeric() => Id.ToString(); - public static AssetType[] ValidTypes = { - AssetType.EventText, AssetType.ItemName, AssetType.MapText, AssetType.Special, - AssetType.Text, AssetType.Word }; + public static AssetType[] ValidTypes = { AssetType.ItemName, AssetType.Text, AssetType.Word }; public static TextId Parse(string s) => AssetMapping.Global.Parse(s, ValidTypes); public static implicit operator AssetId(TextId id) => AssetId.FromUInt32(id._value); public static implicit operator TextId(AssetId id) => new TextId(id.ToUInt32()); - public static implicit operator TextId(UAlbion.Base.EventText id) => TextId.From(id); public static implicit operator TextId(UAlbion.Base.ItemName id) => TextId.From(id); - public static implicit operator TextId(UAlbion.Base.MapText id) => TextId.From(id); - public static implicit operator TextId(UAlbion.Base.Special id) => TextId.From(id); public static implicit operator TextId(UAlbion.Base.SystemText id) => TextId.From(id); public static implicit operator TextId(UAlbion.Base.UAlbionString id) => TextId.From(id); public static implicit operator TextId(UAlbion.Base.Word id) => TextId.From(id); diff --git a/src/Formats/MapEvents/TextEvent.cs b/src/Formats/MapEvents/TextEvent.cs index 5859680a6..423941802 100644 --- a/src/Formats/MapEvents/TextEvent.cs +++ b/src/Formats/MapEvents/TextEvent.cs @@ -11,27 +11,27 @@ namespace UAlbion.Formats.MapEvents; public class TextEvent : MapEvent, IAsyncEvent // Relies on event chain context to resolve TextId to an enum type / AssetId { TextEvent() { } - public TextEvent(ushort subId, TextLocation location, SheetId SheetId) + public TextEvent(ushort subId, TextLocation location, SheetId sheetId) { SubId = subId; Location = location; var expectedType = AssetTypeForTextLocation(location); - if (SheetId.Type != expectedType && !SheetId.IsNone) + if (sheetId.Type != expectedType && !sheetId.IsNone) { throw new FormatException( "Tried to construct a text event with location " + - $"{location} and a character id of type {SheetId.Type}, but a {expectedType} was expected"); + $"{location} and a character id of type {sheetId.Type}, but a {expectedType} was expected"); } - Speaker = SheetId; + Speaker = sheetId; } [EventPart("id")] public ushort SubId { get; private set; } [EventPart("location", true, TextLocation.NoPortrait)] public TextLocation Location { get; private set; } [EventPart("speaker", true, "None")] public SheetId Speaker { get; private set; } public override MapEventType EventType => MapEventType.Text; - public StringId ToId(TextId textId) => new(textId, SubId); + public StringId ToId(StringSetId textId) => new(textId, SubId); public static TextEvent Parse(string[] parts) { diff --git a/src/Formats/Parsers/AlbionStringTableLoader.cs b/src/Formats/Parsers/AlbionStringTableLoader.cs index f8533e182..dfca7b003 100644 --- a/src/Formats/Parsers/AlbionStringTableLoader.cs +++ b/src/Formats/Parsers/AlbionStringTableLoader.cs @@ -8,7 +8,7 @@ namespace UAlbion.Formats.Parsers; public class AlbionStringTableLoader : IAssetLoader { - public ListStringSet Serdes(ListStringSet existing, AssetInfo info, ISerializer s, SerdesContext context) + public ListStringSet Serdes(ListStringSet existing, ISerializer s, AssetLoadContext context) { if (s == null) throw new ArgumentNullException(nameof(s)); if (s.IsReading()) @@ -41,6 +41,6 @@ public ListStringSet Serdes(ListStringSet existing, AssetInfo info, ISerializer } } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes(existing as ListStringSet, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes(existing as ListStringSet, s, context); } diff --git a/src/Formats/Parsers/AmorphousSpriteLoader.cs b/src/Formats/Parsers/AmorphousSpriteLoader.cs index 94a6a13f2..e9117e6cf 100644 --- a/src/Formats/Parsers/AmorphousSpriteLoader.cs +++ b/src/Formats/Parsers/AmorphousSpriteLoader.cs @@ -4,11 +4,13 @@ using SerdesNet; using UAlbion.Api.Visual; using UAlbion.Config; +using UAlbion.Config.Properties; namespace UAlbion.Formats.Parsers; public class AmorphousSpriteLoader : IAssetLoader> { + public static readonly StringAssetProperty SubSpritesProperty = new("SubSprites"); static readonly Regex SizesRegex = new(@" \(\s* (?'width'\d+),\s* @@ -40,16 +42,16 @@ public class AmorphousSpriteLoader : IAssetLoader> } } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((IReadOnlyTexture)existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((IReadOnlyTexture)existing, s, context); - public IReadOnlyTexture Serdes(IReadOnlyTexture existing, AssetInfo info, ISerializer s, SerdesContext context) + public IReadOnlyTexture Serdes(IReadOnlyTexture existing, ISerializer s, AssetLoadContext context) { if (s == null) throw new ArgumentNullException(nameof(s)); - if (info == null) throw new ArgumentNullException(nameof(info)); + if (context == null) throw new ArgumentNullException(nameof(context)); return s.IsWriting() ? Write(existing, s) - : Read(info, s); + : Read(context, s); } static IReadOnlyTexture Write(IReadOnlyTexture existing, ISerializer s) @@ -78,9 +80,9 @@ static IReadOnlyTexture Write(IReadOnlyTexture existing, ISerializer return existing; } - static IReadOnlyTexture Read(AssetInfo info, ISerializer s) + static IReadOnlyTexture Read(AssetLoadContext context, ISerializer s) { - var sizes = ParseSpriteSizes(info.Get(AssetProperty.SubSprites, null)); + var sizes = ParseSpriteSizes(context.Node.GetProperty(SubSpritesProperty)); int totalWidth = 0; int totalHeight = 0; @@ -101,7 +103,7 @@ static IReadOnlyTexture Read(AssetInfo info, ISerializer s) totalWidth = width; } - var result = new SimpleTexture(info.AssetId, totalWidth, totalHeight); + var result = new SimpleTexture(context.AssetId, totalWidth, totalHeight); for (int n = 0; n < frames.Count; n++) { diff --git a/src/Formats/Parsers/AtlasPostProcessor.cs b/src/Formats/Parsers/AtlasPostProcessor.cs index b26f6246d..94128e9de 100644 --- a/src/Formats/Parsers/AtlasPostProcessor.cs +++ b/src/Formats/Parsers/AtlasPostProcessor.cs @@ -11,18 +11,18 @@ public class AtlasPostProcessor : IAssetPostProcessor { const int MarginPixels = 0; - public object Process(object asset, AssetInfo info) => Process((IReadOnlyTexture)asset, info); - public static SimpleTexture Process(IReadOnlyTexture sprite, AssetInfo info) + public object Process(object asset, AssetLoadContext context) => Process((IReadOnlyTexture)asset, context); + public static SimpleTexture Process(IReadOnlyTexture sprite, AssetLoadContext context) { if (sprite == null) throw new ArgumentNullException(nameof(sprite)); - if (info == null) throw new ArgumentNullException(nameof(info)); + if (context == null) throw new ArgumentNullException(nameof(context)); var layout = SpriteSheetUtil.ArrangeSpriteSheet(sprite.Regions.Count, 1, sprite.GetRegionBuffer); if (layout.Layers > 1) throw new InvalidOperationException("Could not layout atlas onto one layer"); var texture = new SimpleTexture( - info.AssetId, sprite.Id.ToString(), + context.AssetId, sprite.Id.ToString(), layout.Width, layout.Height); var pixelData = texture.GetMutableLayerBuffer(0).Buffer; diff --git a/src/Formats/Parsers/AutomapLoader.cs b/src/Formats/Parsers/AutomapLoader.cs index 299ab8fc0..11dd54703 100644 --- a/src/Formats/Parsers/AutomapLoader.cs +++ b/src/Formats/Parsers/AutomapLoader.cs @@ -6,7 +6,8 @@ namespace UAlbion.Formats.Parsers; public class AutomapLoader : IAssetLoader { - public Automap Serdes(Automap existing, AssetInfo info, ISerializer s, SerdesContext context) => Automap.Serdes(existing, s); - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((Automap)existing, info, s, context); + public Automap Serdes(Automap existing, ISerializer s, AssetLoadContext context) + => Automap.Serdes(existing, s); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((Automap)existing, s, context); } diff --git a/src/Formats/Parsers/BlockListLoader.cs b/src/Formats/Parsers/BlockListLoader.cs index d45414775..79b1af4bf 100644 --- a/src/Formats/Parsers/BlockListLoader.cs +++ b/src/Formats/Parsers/BlockListLoader.cs @@ -6,9 +6,9 @@ namespace UAlbion.Formats.Parsers; public class BlockListLoader : IAssetLoader { - public BlockList Serdes(BlockList existing, AssetInfo info, ISerializer s, SerdesContext context) - => BlockList.Serdes(info?.AssetId.Id ?? 0, existing, s); + public BlockList Serdes(BlockList existing, ISerializer s, AssetLoadContext context) + => BlockList.Serdes(context?.AssetId.Id ?? 0, existing, s); - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes(existing as BlockList, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes(existing as BlockList, s, context); } diff --git a/src/Formats/Parsers/CharacterSheetLoader.cs b/src/Formats/Parsers/CharacterSheetLoader.cs index 1684379e4..03415d9e0 100644 --- a/src/Formats/Parsers/CharacterSheetLoader.cs +++ b/src/Formats/Parsers/CharacterSheetLoader.cs @@ -8,13 +8,12 @@ namespace UAlbion.Formats.Parsers; public class CharacterSheetLoader : Component, IAssetLoader { - public CharacterSheet Serdes(CharacterSheet existing, AssetInfo info, ISerializer s, SerdesContext context) + public CharacterSheet Serdes(CharacterSheet existing, ISerializer s, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (context == null) throw new ArgumentNullException(nameof(context)); - return CharacterSheet.Serdes(info.AssetId, existing, context.Mapping, s, Resolve()); + return CharacterSheet.Serdes(context.AssetId, existing, context.Mapping, s, Resolve()); } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes(existing as CharacterSheet, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes(existing as CharacterSheet, s, context); } \ No newline at end of file diff --git a/src/Formats/Parsers/ChestLoader.cs b/src/Formats/Parsers/ChestLoader.cs index 437a0b820..9ae77459c 100644 --- a/src/Formats/Parsers/ChestLoader.cs +++ b/src/Formats/Parsers/ChestLoader.cs @@ -7,13 +7,12 @@ namespace UAlbion.Formats.Parsers; public class ChestLoader : IAssetLoader { - public Inventory Serdes(Inventory existing, AssetInfo info, ISerializer s, SerdesContext context) + public Inventory Serdes(Inventory existing, ISerializer s, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (context == null) throw new ArgumentNullException(nameof(context)); - return Inventory.SerdesChest(info.AssetId.ToInt32(), existing, context.Mapping, s); + return Inventory.SerdesChest(context.AssetId.ToInt32(), existing, context.Mapping, s); } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes(existing as Inventory, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes(existing as Inventory, s, context); } diff --git a/src/Formats/Parsers/DummyLoader.cs b/src/Formats/Parsers/DummyLoader.cs index 6152ebc3f..f5a6c93bb 100644 --- a/src/Formats/Parsers/DummyLoader.cs +++ b/src/Formats/Parsers/DummyLoader.cs @@ -5,6 +5,6 @@ namespace UAlbion.Formats.Parsers; public class DummyLoader : IAssetLoader { - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) + public object Serdes(object existing, ISerializer s, AssetLoadContext context) => existing ?? new object(); } diff --git a/src/Formats/Parsers/EventSetLoader.cs b/src/Formats/Parsers/EventSetLoader.cs index d45700f51..fde28b457 100644 --- a/src/Formats/Parsers/EventSetLoader.cs +++ b/src/Formats/Parsers/EventSetLoader.cs @@ -7,13 +7,12 @@ namespace UAlbion.Formats.Parsers; public class EventSetLoader : IAssetLoader { - public EventSet Serdes(EventSet existing, AssetInfo info, ISerializer s, SerdesContext context) + public EventSet Serdes(EventSet existing, ISerializer s, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (context == null) throw new ArgumentNullException(nameof(context)); - return EventSet.Serdes(info.AssetId, existing, context.Mapping, s); + return EventSet.Serdes(context.AssetId, existing, context.Mapping, s); } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes(existing as EventSet, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes(existing as EventSet, s, context); } diff --git a/src/Formats/Parsers/EventSetScriptLoader.cs b/src/Formats/Parsers/EventSetScriptLoader.cs index ed2918736..2cf627141 100644 --- a/src/Formats/Parsers/EventSetScriptLoader.cs +++ b/src/Formats/Parsers/EventSetScriptLoader.cs @@ -10,13 +10,12 @@ namespace UAlbion.Formats.Parsers; public class EventSetScriptLoader : Component, IAssetLoader { - public EventSet Serdes(EventSet existing, AssetInfo info, ISerializer s, SerdesContext context) + public EventSet Serdes(EventSet existing, ISerializer s, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (s == null) throw new ArgumentNullException(nameof(s)); if (context == null) throw new ArgumentNullException(nameof(context)); - var id = (EventSetId)info.AssetId; + var id = (EventSetId)context.AssetId; if (s.IsWriting()) { if (existing == null) throw new ArgumentNullException(nameof(existing)); @@ -30,7 +29,7 @@ public EventSet Serdes(EventSet existing, AssetInfo info, ISerializer s, SerdesC var bytes = s.Bytes(null, null, (int)s.BytesRemaining); var script = Encoding.UTF8.GetString(bytes); var eventLayout = AlbionCompiler.Compile(script); - return new EventSet(info.AssetId, eventLayout.Events, eventLayout.Chains); + return new EventSet(context.AssetId, eventLayout.Events, eventLayout.Chains); } return existing; @@ -38,10 +37,10 @@ public EventSet Serdes(EventSet existing, AssetInfo info, ISerializer s, SerdesC static string Decompile(EventSetId id, EventSet set, IAssetManager assets) { - var eventFormatter = new EventFormatter(assets.LoadString, id.ToEventText()); + var eventFormatter = new EventFormatter(assets.LoadStringSafe, id.ToEventText()); return eventFormatter.Decompile(set.Events, set.Chains, null).Script; } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((EventSet)existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((EventSet)existing, s, context); } \ No newline at end of file diff --git a/src/Formats/Parsers/FixedSizeSpriteLoader.cs b/src/Formats/Parsers/FixedSizeSpriteLoader.cs index 587cede13..07b888876 100644 --- a/src/Formats/Parsers/FixedSizeSpriteLoader.cs +++ b/src/Formats/Parsers/FixedSizeSpriteLoader.cs @@ -4,31 +4,35 @@ using UAlbion.Api; using UAlbion.Api.Visual; using UAlbion.Config; +using UAlbion.Config.Properties; namespace UAlbion.Formats.Parsers; public class FixedSizeSpriteLoader : IAssetLoader> { public const string TypeString = "UAlbion.Formats.Parsers.FixedSizeSpriteLoader, UAlbion.Formats"; + public static readonly BoolAssetProperty TransposedProperty = new("Transposed"); // For various textures in the 3D world that are stored with rows/columns flipped + public static readonly IntAssetProperty ExtraBytesProperty = new("ExtraBytes"); // Used to suppress assertions when loading original assets that have incorrect sizes - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((IReadOnlyTexture) existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((IReadOnlyTexture) existing, s, context); - public IReadOnlyTexture Serdes(IReadOnlyTexture existing, AssetInfo info, ISerializer s, SerdesContext context) + public IReadOnlyTexture Serdes(IReadOnlyTexture existing, ISerializer s, AssetLoadContext context) { if (s == null) throw new ArgumentNullException(nameof(s)); - if (info == null) throw new ArgumentNullException(nameof(info)); + if (context == null) throw new ArgumentNullException(nameof(context)); return s.IsWriting() - ? Write(existing, info, s) - : Read(info, s); + ? Write(existing, context, s) + : Read(context, s); } - static IReadOnlyTexture Read(AssetInfo info, ISerializer s) + static IReadOnlyTexture Read(AssetLoadContext context, ISerializer s) { var streamLength = s.BytesRemaining; if (streamLength == 0) return null; + var info = context.Node; int width = info.Width; int height = info.Height; @@ -42,10 +46,10 @@ static IReadOnlyTexture Read(AssetInfo info, ISerializer s) byte[] pixelData = s.Bytes(null, null, (int)streamLength); int expectedPixelCount = width * height * spriteCount; - int extra = info.Get(AssetProperty.ExtraBytes, 0); + int extra = info.GetProperty(ExtraBytesProperty); ApiUtil.Assert((expectedPixelCount + extra) == (int)streamLength, - $"Extra pixels found when loading fixed size sprite {info.AssetId} " + + $"Extra pixels found when loading fixed size sprite {context.AssetId} " + $"({streamLength} bytes for a {width}x{height}x{spriteCount} image, expected {expectedPixelCount}"); var frames = new Region[spriteCount]; @@ -53,17 +57,17 @@ static IReadOnlyTexture Read(AssetInfo info, ISerializer s) frames[n] = new Region(0, height * n, width, height, width, totalHeight, 0); var sprite = new SimpleTexture( - info.AssetId, - info.AssetId.ToString(), + context.AssetId, + context.AssetId.ToString(), width, height * spriteCount, pixelData.AsSpan(0, expectedPixelCount), // May be less than the streamlength frames); - return info.Get(AssetProperty.Transposed, false) ? Transpose(sprite) : sprite; + return info.GetProperty(TransposedProperty) ? Transpose(sprite) : sprite; } - static IReadOnlyTexture Write(IReadOnlyTexture existing, AssetInfo info, ISerializer s) + static IReadOnlyTexture Write(IReadOnlyTexture existing, AssetLoadContext context, ISerializer s) { if (existing == null) throw new ArgumentNullException(nameof(existing)); @@ -74,7 +78,7 @@ static IReadOnlyTexture Write(IReadOnlyTexture existing, AssetInfo i ApiUtil.Assert(f.Height == frame.Height, "FixedSizeSpriteLoader tried to serialise sprite with non-uniform frames"); } - var sprite = info.Get(AssetProperty.Transposed, false) + var sprite = context.GetProperty(TransposedProperty) ? Transpose(existing) : existing; diff --git a/src/Formats/Parsers/FlicLoader.cs b/src/Formats/Parsers/FlicLoader.cs index e55cebf59..4e5676772 100644 --- a/src/Formats/Parsers/FlicLoader.cs +++ b/src/Formats/Parsers/FlicLoader.cs @@ -6,5 +6,5 @@ namespace UAlbion.Formats.Parsers; public class FlicLoader : IAssetLoader { - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) => new FlicFile(s); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) => new FlicFile(s); } diff --git a/src/Formats/Parsers/InputConfigLoader.cs b/src/Formats/Parsers/InputConfigLoader.cs index 3a6f46926..1d3bc82ba 100644 --- a/src/Formats/Parsers/InputConfigLoader.cs +++ b/src/Formats/Parsers/InputConfigLoader.cs @@ -7,20 +7,18 @@ namespace UAlbion.Formats.Parsers; public class InputConfigLoader : IAssetLoader { - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((InputConfig)existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((InputConfig)existing, s, context); - public InputConfig Serdes(InputConfig existing, AssetInfo info, ISerializer s, SerdesContext context) + public InputConfig Serdes(InputConfig existing, ISerializer s, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (context == null) throw new ArgumentNullException(nameof(context)); if (!s.IsWriting()) - return InputConfig.Load(info.File.Filename, context.Disk, context.Json); + return InputConfig.Load(context.Filename, context.Disk, context.Json); if (existing == null) throw new ArgumentNullException(nameof(existing)); - existing.Save(info.File.Filename, context.Disk, context.Json); + existing.Save(context.Filename, context.Disk, context.Json); return existing; - } } \ No newline at end of file diff --git a/src/Formats/Parsers/InterlacedBitmapLoader.cs b/src/Formats/Parsers/InterlacedBitmapLoader.cs index 300aba163..a29b4caf1 100644 --- a/src/Formats/Parsers/InterlacedBitmapLoader.cs +++ b/src/Formats/Parsers/InterlacedBitmapLoader.cs @@ -8,21 +8,21 @@ namespace UAlbion.Formats.Parsers; public class InterlacedBitmapLoader : IAssetLoader> { - public IReadOnlyTexture Serdes(IReadOnlyTexture existing, AssetInfo info, ISerializer s, SerdesContext context) + public IReadOnlyTexture Serdes(IReadOnlyTexture existing, ISerializer s, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); + if (context == null) throw new ArgumentNullException(nameof(context)); if (s.IsWriting()) // TODO: Implement writing if required. May require palette generation, which can get complicated. return existing; var ilbm = InterlacedBitmap.Serdes(null, s); - return s.IsWriting() ? existing : ConvertIlbmToTexture(ilbm, info); + return s.IsWriting() ? existing : ConvertIlbmToTexture(ilbm, context); } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((IReadOnlyTexture)existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((IReadOnlyTexture)existing, s, context); - static IReadOnlyTexture ConvertIlbmToTexture(InterlacedBitmap bitmap, AssetInfo info) + static IReadOnlyTexture ConvertIlbmToTexture(InterlacedBitmap bitmap, AssetLoadContext info) { var texture = new SimpleTexture(info.AssetId, bitmap.Width, bitmap.Height); texture.AddRegion(0, 0, bitmap.Width, bitmap.Height); diff --git a/src/Formats/Parsers/ItemDataLoader.cs b/src/Formats/Parsers/ItemDataLoader.cs index 8aac61429..499c2f400 100644 --- a/src/Formats/Parsers/ItemDataLoader.cs +++ b/src/Formats/Parsers/ItemDataLoader.cs @@ -1,4 +1,5 @@ -using SerdesNet; +using System; +using SerdesNet; using UAlbion.Api.Eventing; using UAlbion.Config; using UAlbion.Formats.Assets; @@ -7,8 +8,12 @@ namespace UAlbion.Formats.Parsers; public class ItemDataLoader : Component, IAssetLoader { - public ItemData Serdes(ItemData existing, AssetInfo info, ISerializer s, SerdesContext context) - => ItemData.Serdes(info, existing, s, Resolve()); - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes(existing as ItemData, info, s, context); + public ItemData Serdes(ItemData existing, ISerializer s, AssetLoadContext context) + { + if (context == null) throw new ArgumentNullException(nameof(context)); + return ItemData.Serdes(context.AssetId, existing, s, Resolve()); + } + + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes(existing as ItemData, s, context); } \ No newline at end of file diff --git a/src/Formats/Parsers/ItemNameCollector.cs b/src/Formats/Parsers/ItemNameCollector.cs deleted file mode 100644 index 892a0ab05..000000000 --- a/src/Formats/Parsers/ItemNameCollector.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using SerdesNet; -using UAlbion.Api.Eventing; -using UAlbion.Config; -using UAlbion.Formats.Assets; - -namespace UAlbion.Formats.Parsers; - -public class ItemNameCollector : Component, IAssetLoader -{ - public MultiLanguageStringDictionary Serdes(MultiLanguageStringDictionary existing, AssetInfo info, ISerializer s, SerdesContext context) - { - if (context == null) throw new ArgumentNullException(nameof(context)); - if (s.IsWriting()) return existing; - - var assets = Resolve(); - var ids = - context.Mapping.EnumerateAssetsOfType(AssetType.ItemName) - // make sure TextId->StringId resolution won't happen - // and get stuck in infinite recursion - .Select(x => new StringId(x, 0)) - .ToArray(); - - var german = ids.ToDictionary(x => x.Id.Id, x => assets.LoadString(x, Base.Language.German)); - var english = ids.ToDictionary(x => x.Id.Id, x => assets.LoadString(x, Base.Language.English)); - var french = ids.ToDictionary(x => x.Id.Id, x => assets.LoadString(x, Base.Language.French)); - - static void Add(ListStringSet collection, int i, string value) - { - while (collection.Count <= i) - collection.Add(null); - collection[i] = value; - } - - static ListStringSet Build(IDictionary dict) - { - var list = new ListStringSet(); - foreach (var kvp in dict) - Add(list, kvp.Key, kvp.Value); - return list; - } - - return new MultiLanguageStringDictionary - { - [Base.Language.German] = Build(german), - [Base.Language.English] = Build(english), - [Base.Language.French] = Build(french) - }; - } - - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((MultiLanguageStringDictionary)existing, info, s, context); -} \ No newline at end of file diff --git a/src/Formats/Parsers/ItemNameCollectorLoader.cs b/src/Formats/Parsers/ItemNameCollectorLoader.cs new file mode 100644 index 000000000..d9852d9be --- /dev/null +++ b/src/Formats/Parsers/ItemNameCollectorLoader.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using SerdesNet; +using UAlbion.Api.Eventing; +using UAlbion.Config; +using UAlbion.Config.Properties; +using UAlbion.Formats.Assets; +using UAlbion.Formats.Ids; + +namespace UAlbion.Formats.Parsers; + +public class ItemNameCollectorLoader : Component, IAssetLoader> +{ + public static readonly AssetRangeAssetProperty TargetRange = new("TargetRange"); + public static readonly StringListAssetProperty TargetLanguages = new("TargetLanguages"); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((Dictionary)existing, s, context); + + public Dictionary Serdes( + Dictionary existing, + ISerializer s, + AssetLoadContext context) + { + if (context == null) throw new ArgumentNullException(nameof(context)); + + if (existing != null) + throw new NotSupportedException($"{nameof(StringSetStringLoader)} is read-only"); + + var range = context.GetProperty(TargetRange); + var languages = context.GetProperty(TargetLanguages); + + var results = new Dictionary(); + var assets = Resolve(); + foreach (var language in languages) + { + if (!results.TryGetValue(language, out var list)) + { + list = new ListStringSet(); + results[language] = list; + } + + var firstId = range.From.Id; + foreach (var id in range) + { + var textId = (TextId)id; + var value = assets.LoadStringSafe(textId, language); + list.SetString(new StringId(context.AssetId, (ushort)(id.Id - firstId)), value); + } + } + + return results; + } +} \ No newline at end of file diff --git a/src/Formats/Parsers/ItemNameLoader.cs b/src/Formats/Parsers/ItemNameLoader.cs index 0cebf7138..c2a270c96 100644 --- a/src/Formats/Parsers/ItemNameLoader.cs +++ b/src/Formats/Parsers/ItemNameLoader.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using SerdesNet; using UAlbion.Api; using UAlbion.Config; @@ -6,56 +8,84 @@ namespace UAlbion.Formats.Parsers; -public class ItemNameLoader : IAssetLoader +public class ItemNameLoader : IAssetLoader> { const int StringSize = 20; - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((MultiLanguageStringDictionary)existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((Dictionary)existing, s, context); - public MultiLanguageStringDictionary Serdes(MultiLanguageStringDictionary existing, AssetInfo info, ISerializer s, SerdesContext context) + public Dictionary Serdes( + Dictionary existing, + ISerializer s, + AssetLoadContext context) { if (s == null) throw new ArgumentNullException(nameof(s)); - if (s.IsWriting() && existing == null) throw new ArgumentNullException(nameof(existing)); + if (context == null) throw new ArgumentNullException(nameof(context)); - existing ??= new MultiLanguageStringDictionary(); - if (!existing.ContainsKey(Base.Language.German)) existing[Base.Language.German] = new ListStringSet(); - if (!existing.ContainsKey(Base.Language.English)) existing[Base.Language.English] = new ListStringSet(); - if (!existing.ContainsKey(Base.Language.French)) existing[Base.Language.French] = new ListStringSet(); + if (s.IsReading()) + return Read(s); + + if (existing == null) + throw new ArgumentNullException(nameof(existing)); + + WriteAll(existing, s); + return existing; + } - static void Inner(IStringSet collection, int i, ISerializer s2) + static Dictionary Read(ISerializer s) + { + var german = new ListStringSet(); + var english = new ListStringSet(); + var french = new ListStringSet(); + + var results = new Dictionary + { + { Base.Language.German, german }, + { Base.Language.English, english }, + { Base.Language.French, french } + }; + + var streamLength = s.BytesRemaining; + ApiUtil.Assert(streamLength % StringSize == 0, "Expected item name file length to be a whole multiple of the string size"); + + long end = s.Offset + streamLength; + while (s.Offset < end) { - var concrete = (ListStringSet)collection; - while (collection.Count <= i) - concrete.Add(null); - concrete[i] = s2.FixedLengthString(null, concrete[i], StringSize); + german.Add(s.FixedLengthString(null, null, StringSize)); + english.Add(s.FixedLengthString(null, null, StringSize)); + french.Add(s.FixedLengthString(null, null, StringSize)); } - if (s.IsReading()) + return results; + } + + static void WriteAll(Dictionary existing, ISerializer s) + { + existing.TryGetValue(Base.Language.German, out var german); + existing.TryGetValue(Base.Language.English, out var english); + existing.TryGetValue(Base.Language.French, out var french); + + var maxCount = new[] { - var streamLength = s.BytesRemaining; - ApiUtil.Assert(streamLength % StringSize == 0, "Expected item name file length to be a whole multiple of the string size"); + german?.Count, + english?.Count, + french?.Count, + }.Max(); - int i = 1; - long end = s.Offset + streamLength; - while (s.Offset < end) - { - Inner(existing[Base.Language.German], i, s); - Inner(existing[Base.Language.English], i, s); - Inner(existing[Base.Language.French], i, s); - i++; - } - } - else + if (maxCount == null) + throw new InvalidOperationException($"No strings for base languages in dictionary given to {nameof(ItemNameLoader)}"); + + for (int i = 0; i < maxCount.Value; i++) { - var stringCount = existing[Base.Language.German].Count; - for (int i = 1; i < stringCount; i++) - { - Inner(existing[Base.Language.German], i, s); - Inner(existing[Base.Language.English], i, s); - Inner(existing[Base.Language.French], i, s); - } + Write(german, s, i); + Write(english, s, i); + Write(french, s, i); } + } - return existing; + static void Write(ListStringSet set, ISerializer s, int i) + { + var text = set.Count <= i ? "" : set[i]; + s.FixedLengthString(null, text, StringSize); } } diff --git a/src/Formats/Parsers/ItemNameMetaLoader.cs b/src/Formats/Parsers/ItemNameMetaLoader.cs new file mode 100644 index 000000000..cbc740894 --- /dev/null +++ b/src/Formats/Parsers/ItemNameMetaLoader.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using SerdesNet; +using UAlbion.Api.Eventing; +using UAlbion.Config; +using UAlbion.Config.Properties; +using UAlbion.Formats.Assets; + +namespace UAlbion.Formats.Parsers; + +public class ItemNameMetaLoader : Component, IAssetLoader +{ + public static readonly AssetIdAssetProperty TargetProperty = new("Target"); + public ListStringSet Serdes(ListStringSet existing, ISerializer s, AssetLoadContext context) + { + if (context == null) throw new ArgumentNullException(nameof(context)); + var targetId = context.GetProperty(TargetProperty); + if (targetId == AssetId.None) + return null; + + if (existing != null) + throw new NotSupportedException($"{nameof(ItemNameMetaLoader)} is read-only"); + + var applier = Resolve(); + var target = applier.LoadAssetCached(targetId, null); + + if (target is not Dictionary dict) + throw new FormatException($"Expected target \"{targetId}\" to be a {nameof(Dictionary)} but it was {target?.GetType()}"); + + return dict.TryGetValue(context.Language, out var list) + ? list + : null; + } + + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((ListStringSet)existing, s, context); +} \ No newline at end of file diff --git a/src/Formats/Parsers/JsonLoader.cs b/src/Formats/Parsers/JsonLoader.cs index 465ea1f1b..b040bbd2f 100644 --- a/src/Formats/Parsers/JsonLoader.cs +++ b/src/Formats/Parsers/JsonLoader.cs @@ -7,7 +7,7 @@ namespace UAlbion.Formats.Parsers; public class JsonLoader : IAssetLoader where T : class { - public T Serdes(T existing, AssetInfo info, ISerializer s, SerdesContext context) + public T Serdes(T existing, ISerializer s, AssetLoadContext context) { if (s == null) throw new ArgumentNullException(nameof(s)); if (context == null) throw new ArgumentNullException(nameof(context)); @@ -29,6 +29,6 @@ public T Serdes(T existing, AssetInfo info, ISerializer s, SerdesContext context } } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((T)existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((T)existing, s, context); } \ No newline at end of file diff --git a/src/Formats/Parsers/JsonStringLoader.cs b/src/Formats/Parsers/JsonStringLoader.cs index 6f508a79e..f43b9f9b8 100644 --- a/src/Formats/Parsers/JsonStringLoader.cs +++ b/src/Formats/Parsers/JsonStringLoader.cs @@ -7,11 +7,11 @@ namespace UAlbion.Formats.Parsers; public class JsonStringLoader : IAssetLoader { - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) + public object Serdes(object existing, ISerializer s, AssetLoadContext context) { if (s == null) throw new ArgumentNullException(nameof(s)); if (context == null) throw new ArgumentNullException(nameof(context)); var bytes = s.Bytes(null, null, (int)s.BytesRemaining); return context.Json.Deserialize(bytes); } -} +} \ No newline at end of file diff --git a/src/Formats/Parsers/LabyrinthDataLoader.cs b/src/Formats/Parsers/LabyrinthDataLoader.cs index f8b325b8a..bae09f055 100644 --- a/src/Formats/Parsers/LabyrinthDataLoader.cs +++ b/src/Formats/Parsers/LabyrinthDataLoader.cs @@ -7,13 +7,12 @@ namespace UAlbion.Formats.Parsers; public class LabyrinthDataLoader : IAssetLoader { - public LabyrinthData Serdes(LabyrinthData existing, AssetInfo info, ISerializer s, SerdesContext context) + public LabyrinthData Serdes(LabyrinthData existing, ISerializer s, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (context == null) throw new ArgumentNullException(nameof(context)); - return LabyrinthData.Serdes(existing, info, context.Mapping, s); + return LabyrinthData.Serdes(existing, context.AssetId, context.Mapping, s); } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes(existing as LabyrinthData, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes(existing as LabyrinthData, s, context); } diff --git a/src/Formats/Parsers/MapLoader.cs b/src/Formats/Parsers/MapLoader.cs index 33833200f..9e1d2d74a 100644 --- a/src/Formats/Parsers/MapLoader.cs +++ b/src/Formats/Parsers/MapLoader.cs @@ -8,13 +8,12 @@ namespace UAlbion.Formats.Parsers; public class MapLoader : IAssetLoader { - public IMapData Serdes(IMapData existing, AssetInfo info, ISerializer s, SerdesContext context) + public IMapData Serdes(IMapData existing, ISerializer s, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (context == null) throw new ArgumentNullException(nameof(context)); - return BaseMapData.Serdes(info, existing, context.Mapping, s); + return BaseMapData.Serdes(context.AssetId, existing, context.Mapping, s); } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes(existing as IMapData, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes(existing as IMapData, s, context); } \ No newline at end of file diff --git a/src/Formats/Parsers/MerchantLoader.cs b/src/Formats/Parsers/MerchantLoader.cs index eebf28346..beb3a7f74 100644 --- a/src/Formats/Parsers/MerchantLoader.cs +++ b/src/Formats/Parsers/MerchantLoader.cs @@ -7,13 +7,12 @@ namespace UAlbion.Formats.Parsers; public class MerchantLoader : IAssetLoader { - public Inventory Serdes(Inventory existing, AssetInfo info, ISerializer s, SerdesContext context) + public Inventory Serdes(Inventory existing, ISerializer s, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (context == null) throw new ArgumentNullException(nameof(context)); - return Inventory.SerdesMerchant(info.AssetId.ToInt32(), existing, context.Mapping, s); + return Inventory.SerdesMerchant(context.AssetId.ToInt32(), existing, context.Mapping, s); } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes(existing as Inventory, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes(existing as Inventory, s, context); } diff --git a/src/Formats/Parsers/MonsterGroupLoader.cs b/src/Formats/Parsers/MonsterGroupLoader.cs index 510d64c0f..1dec2dd5a 100644 --- a/src/Formats/Parsers/MonsterGroupLoader.cs +++ b/src/Formats/Parsers/MonsterGroupLoader.cs @@ -7,12 +7,12 @@ namespace UAlbion.Formats.Parsers; public class MonsterGroupLoader : IAssetLoader { - public MonsterGroup Serdes(MonsterGroup existing, AssetInfo info, ISerializer s, SerdesContext context) + public MonsterGroup Serdes(MonsterGroup existing, ISerializer s, AssetLoadContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); - return MonsterGroup.Serdes(info?.AssetId.Id ?? 0, existing, context.Mapping, s); + return MonsterGroup.Serdes(context.AssetId.Id, existing, context.Mapping, s); } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes(existing as MonsterGroup, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes(existing as MonsterGroup, s, context); } diff --git a/src/Formats/Parsers/MultiHeaderSpriteLoader.cs b/src/Formats/Parsers/MultiHeaderSpriteLoader.cs index a40bc9bff..dfac9ac94 100644 --- a/src/Formats/Parsers/MultiHeaderSpriteLoader.cs +++ b/src/Formats/Parsers/MultiHeaderSpriteLoader.cs @@ -11,13 +11,13 @@ namespace UAlbion.Formats.Parsers; public class MultiHeaderSpriteLoader : IAssetLoader> { - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((IReadOnlyTexture)existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((IReadOnlyTexture)existing, s, context); - public IReadOnlyTexture Serdes(IReadOnlyTexture existing, AssetInfo info, ISerializer s, SerdesContext context) + public IReadOnlyTexture Serdes(IReadOnlyTexture existing, ISerializer s, AssetLoadContext context) { if (s == null) throw new ArgumentNullException(nameof(s)); - if (info == null) throw new ArgumentNullException(nameof(info)); + if (context == null) throw new ArgumentNullException(nameof(context)); if (s.IsWriting()) { if (existing == null) @@ -27,10 +27,10 @@ public IReadOnlyTexture Serdes(IReadOnlyTexture existing, AssetInfo return existing; } - return Read(info, s); + return Read(context, s); } - static IReadOnlyTexture Read(AssetInfo info, ISerializer s) + static IReadOnlyTexture Read(AssetLoadContext context, ISerializer s) { int width = s.UInt16("Width", 0); int height = s.UInt16("Height", 0); @@ -56,7 +56,7 @@ static IReadOnlyTexture Read(AssetInfo info, ISerializer s) frames.Add(new SimpleTexture(null, null, width, height, frameBytes)); } - return BlitUtil.CombineFramesVertically(info.AssetId, frames); + return BlitUtil.CombineFramesVertically(context.AssetId, frames); } static void Write(IReadOnlyTexture existing, ISerializer s) diff --git a/src/Formats/Parsers/PaletteLoader.cs b/src/Formats/Parsers/PaletteLoader.cs index 9488a3b68..4b62ddab3 100644 --- a/src/Formats/Parsers/PaletteLoader.cs +++ b/src/Formats/Parsers/PaletteLoader.cs @@ -1,19 +1,23 @@ using System; using SerdesNet; using UAlbion.Config; +using UAlbion.Config.Properties; using UAlbion.Formats.Assets; namespace UAlbion.Formats.Parsers; public class PaletteLoader : IAssetLoader { - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((AlbionPalette)existing, info, s, context); + public static readonly BoolAssetProperty IsCommon = new("IsCommon"); // bool + public static readonly StringAssetProperty AnimatedRanges = new("AnimatedRanges"); // string (e.g. "0x1-0xf, 0x12-0x1a") + public static AssetIdAssetProperty NightPalette { get; }= new("NightPalette"); - public AlbionPalette Serdes(AlbionPalette existing, AssetInfo info, ISerializer s, SerdesContext context) + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((AlbionPalette)existing, s, context); + + public AlbionPalette Serdes(AlbionPalette existing, ISerializer s, AssetLoadContext context) { if (s == null) throw new ArgumentNullException(nameof(s)); - if (info == null) throw new ArgumentNullException(nameof(info)); - return AlbionPalette.Serdes(existing, info, s); - } + return AlbionPalette.Serdes(existing, context, s); + } } diff --git a/src/Formats/Parsers/RawLoader.cs b/src/Formats/Parsers/RawLoader.cs index a6dbb2355..67c5109e3 100644 --- a/src/Formats/Parsers/RawLoader.cs +++ b/src/Formats/Parsers/RawLoader.cs @@ -6,12 +6,12 @@ namespace UAlbion.Formats.Parsers; public class RawLoader : IAssetLoader { - public byte[] Serdes(byte[] existing, AssetInfo info, ISerializer s, SerdesContext context) + public byte[] Serdes(byte[] existing, ISerializer s, AssetLoadContext context) { if (s == null) throw new ArgumentNullException(nameof(s)); return s.Bytes(null, existing, (int) (existing?.Length ?? s.BytesRemaining)); } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((byte[]) existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((byte[]) existing, s, context); } diff --git a/src/Formats/Parsers/SampleLoader.cs b/src/Formats/Parsers/SampleLoader.cs index 1e3ece2ef..c6a3da185 100644 --- a/src/Formats/Parsers/SampleLoader.cs +++ b/src/Formats/Parsers/SampleLoader.cs @@ -11,13 +11,12 @@ public class SampleLoader : IAssetLoader const int Channels = 1; const int BytesPerSample = 1; - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((AlbionSample) existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((AlbionSample) existing, s, context); - public AlbionSample Serdes(AlbionSample existing, AssetInfo info, ISerializer s, SerdesContext context) + public AlbionSample Serdes(AlbionSample existing, ISerializer s, AssetLoadContext context) { if (s == null) throw new ArgumentNullException(nameof(s)); - if (info == null) throw new ArgumentNullException(nameof(info)); return s.IsWriting() ? Write(existing, s) : Read(s); } diff --git a/src/Formats/Parsers/ScriptLoader.cs b/src/Formats/Parsers/ScriptLoader.cs index cb59589a5..25cf3b292 100644 --- a/src/Formats/Parsers/ScriptLoader.cs +++ b/src/Formats/Parsers/ScriptLoader.cs @@ -19,10 +19,10 @@ static IEnumerable ReadLines(ISerializer s) return ApiUtil.SplitLines(text, StringSplitOptions.None).Select(x => x.Trim()); } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((Script)existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((Script)existing, s, context); - public Script Serdes(Script existing, AssetInfo info, ISerializer s, SerdesContext context) + public Script Serdes(Script existing, ISerializer s, AssetLoadContext context) { if (s == null) throw new ArgumentNullException(nameof(s)); diff --git a/src/Formats/Parsers/SingleHeaderSpriteLoader.cs b/src/Formats/Parsers/SingleHeaderSpriteLoader.cs index 2f277aefa..d68697ad2 100644 --- a/src/Formats/Parsers/SingleHeaderSpriteLoader.cs +++ b/src/Formats/Parsers/SingleHeaderSpriteLoader.cs @@ -9,13 +9,13 @@ namespace UAlbion.Formats.Parsers; public class SingleHeaderSpriteLoader : IAssetLoader> { - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((IReadOnlyTexture)existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((IReadOnlyTexture)existing, s, context); - public IReadOnlyTexture Serdes(IReadOnlyTexture existing, AssetInfo info, ISerializer s, SerdesContext context) + public IReadOnlyTexture Serdes(IReadOnlyTexture existing, ISerializer s, AssetLoadContext context) { if (s == null) throw new ArgumentNullException(nameof(s)); - if (info == null) throw new ArgumentNullException(nameof(info)); + if (context == null) throw new ArgumentNullException(nameof(context)); if (s.IsWriting()) { @@ -24,10 +24,10 @@ public IReadOnlyTexture Serdes(IReadOnlyTexture existing, AssetInfo return existing; } - return Read(info, s); + return Read(context, s); } - static IReadOnlyTexture Read(AssetInfo info, ISerializer s) + static IReadOnlyTexture Read(AssetLoadContext context, ISerializer s) { ushort width = s.UInt16("Width", 0); ushort height = s.UInt16("Height", 0); @@ -35,7 +35,7 @@ static IReadOnlyTexture Read(AssetInfo info, ISerializer s) ApiUtil.Assert(something == 0); byte frameCount = s.UInt8("Frames", 1); - var result = new SimpleTexture(info.AssetId, width, height * frameCount); + var result = new SimpleTexture(context.AssetId, width, height * frameCount); for (int i = 0; i < frameCount; i++) { byte[] frameBytes = s.Bytes("Frame" + i, null, width * height); diff --git a/src/Formats/Parsers/SlabLoader.cs b/src/Formats/Parsers/SlabLoader.cs index b77dab610..80cfbff24 100644 --- a/src/Formats/Parsers/SlabLoader.cs +++ b/src/Formats/Parsers/SlabLoader.cs @@ -8,10 +8,10 @@ namespace UAlbion.Formats.Parsers; public class SlabLoader : IAssetLoader> { const int StatusBarHeight = 48; - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((IReadOnlyTexture)existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((IReadOnlyTexture)existing, s, context); - public IReadOnlyTexture Serdes(IReadOnlyTexture existing, AssetInfo info, ISerializer s, SerdesContext context) + public IReadOnlyTexture Serdes(IReadOnlyTexture existing, ISerializer s, AssetLoadContext context) { IReadOnlyTexture singleFrame = null; if (s.IsWriting()) @@ -28,7 +28,7 @@ public IReadOnlyTexture Serdes(IReadOnlyTexture existing, AssetInfo singleFrame = texture; } - var sprite = new FixedSizeSpriteLoader().Serdes(singleFrame, info, s, context); + var sprite = new FixedSizeSpriteLoader().Serdes(singleFrame, s, context); if (sprite == null) return null; diff --git a/src/Formats/Parsers/SongLoader.cs b/src/Formats/Parsers/SongLoader.cs index 9acda4f8f..24b12d60f 100644 --- a/src/Formats/Parsers/SongLoader.cs +++ b/src/Formats/Parsers/SongLoader.cs @@ -6,10 +6,10 @@ namespace UAlbion.Formats.Parsers; public class SongLoader : IAssetLoader { - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((byte[])existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((byte[])existing, s, context); - public byte[] Serdes(byte[] existing, AssetInfo info, ISerializer s, SerdesContext context) + public byte[] Serdes(byte[] existing, ISerializer s, AssetLoadContext context) { if (s == null) throw new ArgumentNullException(nameof(s)); if (s.IsReading()) diff --git a/src/Formats/Parsers/SpellLoader.cs b/src/Formats/Parsers/SpellLoader.cs index eaf59d1e9..fefc15395 100644 --- a/src/Formats/Parsers/SpellLoader.cs +++ b/src/Formats/Parsers/SpellLoader.cs @@ -1,19 +1,24 @@ using System; using SerdesNet; using UAlbion.Config; +using UAlbion.Config.Properties; using UAlbion.Formats.Assets; namespace UAlbion.Formats.Parsers; public class SpellLoader : IAssetLoader { - public SpellData Serdes(SpellData existing, AssetInfo info, ISerializer s, SerdesContext context) + public static readonly AssetIdAssetProperty SpellName = new("Name"); // StringId to use in game + public static readonly EnumAssetProperty MagicSchool = new("School", SpellClass.DjiKas); // SpellClass enum + public static readonly IntAssetProperty SpellNumber = new("SpellNumber"); // offset into school, used for save-game serialization + + public SpellData Serdes(SpellData existing, ISerializer s, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (s == null) throw new ArgumentNullException(nameof(s)); - return SpellData.Serdes(existing, info, s); + if (context == null) throw new ArgumentNullException(nameof(context)); + return SpellData.Serdes(existing, context, s); } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes(existing as SpellData, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes(existing as SpellData, s, context); } diff --git a/src/Formats/Parsers/StringSetCollectorLoader.cs b/src/Formats/Parsers/StringSetCollectorLoader.cs new file mode 100644 index 000000000..2347c3ad7 --- /dev/null +++ b/src/Formats/Parsers/StringSetCollectorLoader.cs @@ -0,0 +1,53 @@ +using System; +using SerdesNet; +using UAlbion.Api.Eventing; +using UAlbion.Config; +using UAlbion.Config.Properties; +using UAlbion.Formats.Assets; +using UAlbion.Formats.Ids; + +namespace UAlbion.Formats.Parsers; + +public class StringSetCollectorLoader : Component, IAssetLoader +{ + public static readonly AssetRangeAssetProperty FileRange = new("FileRange"); + public static readonly AssetRangeAssetProperty TargetRange = new("TargetRange"); + public ListStringSet Serdes(ListStringSet existing, ISerializer s, AssetLoadContext context) + { + if (context == null) throw new ArgumentNullException(nameof(context)); + var targetRange = context.GetProperty(TargetRange); + var fileRange = context.GetProperty(FileRange); + + if (targetRange.From.Type != fileRange.From.Type) + throw new FormatException($"The asset types of TargetRange and FileRange must match when using {nameof(StringSetCollectorLoader)} (asset {context.AssetId})"); + + if (targetRange.From.Id < fileRange.From.Id) + throw new FormatException($"The TargetRange must match or be a subset of the FileRange when using {nameof(StringSetCollectorLoader)} (asset {context.AssetId})"); + + if (targetRange.To.Id > fileRange.To.Id) + throw new FormatException($"The TargetRange must match or be a subset of the FileRange when using {nameof(StringSetCollectorLoader)} (asset {context.AssetId})"); + + var assets = Resolve(); + if (!assets.IsStringDefined(targetRange.From, context.Language)) + return null; + + var result = new ListStringSet(fileRange.To.Id - fileRange.From.Id + 1); + foreach (var id in fileRange) + { + int index = id.Id - fileRange.From.Id; + while (result.Count <= index) + result.Add(null); + + if (id >= targetRange.From && id <= targetRange.To) + { + string value = assets.LoadStringRaw(id, context.Language); + result[index] = value; + } + } + + return result; + } + + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((ListStringSet)existing, s, context); +} \ No newline at end of file diff --git a/src/Formats/Parsers/StringSetStringLoader.cs b/src/Formats/Parsers/StringSetStringLoader.cs new file mode 100644 index 000000000..2edc16ea5 --- /dev/null +++ b/src/Formats/Parsers/StringSetStringLoader.cs @@ -0,0 +1,44 @@ +using System; +using SerdesNet; +using UAlbion.Api.Eventing; +using UAlbion.Config; +using UAlbion.Config.Properties; +using UAlbion.Formats.Assets; +using UAlbion.Formats.Ids; + +namespace UAlbion.Formats.Parsers; + +public class StringSetStringLoader : Component, IAssetLoader +{ + public static readonly AssetIdAssetProperty TargetProperty = new("Target", StringSetId.None, x => x); + public static readonly AssetIdAssetProperty FirstIdProperty = new("FirstId", TextId.None, x => x); + public string Serdes(string existing, ISerializer s, AssetLoadContext context) + { + if (context == null) throw new ArgumentNullException(nameof(context)); + + if (existing != null) + throw new NotSupportedException($"{nameof(StringSetStringLoader)} is read-only"); + + var target = context.GetProperty(TargetProperty); + var firstId = context.GetProperty(FirstIdProperty); + if (target == AssetId.None) + return null; + + var assets = Resolve(); + var set = assets.LoadStringSet(target, context.Language); + + if (set == null) + return null; + + if (context.AssetId.Type != firstId.Type) + throw new InvalidOperationException($"The FirstId for {context.AssetId} ({firstId}) had a mismatched type"); + + int thisId = context.AssetId.Id; + ushort subId = (ushort)(thisId - firstId.Id); + var stringId = new StringId(target, subId); + return set.GetString(stringId); + } + + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((string)existing, s, context); +} diff --git a/src/Formats/Parsers/SystemTextLoader.cs b/src/Formats/Parsers/SystemTextLoader.cs index c2f31db22..6e1b69561 100644 --- a/src/Formats/Parsers/SystemTextLoader.cs +++ b/src/Formats/Parsers/SystemTextLoader.cs @@ -11,10 +11,10 @@ public class SystemTextLoader : IAssetLoader { static readonly Regex Regex = new(@"\[(\d+):(.*)\]"); - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((IntStringDictionary) existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((IntStringDictionary) existing, s, context); - public IntStringDictionary Serdes(IntStringDictionary existing, AssetInfo info, ISerializer s, SerdesContext context) + public IntStringDictionary Serdes(IntStringDictionary existing, ISerializer s, AssetLoadContext context) { if (s == null) throw new ArgumentNullException(nameof(s)); if (s.IsWriting()) throw new NotImplementedException("Saving of system text not currently supported"); diff --git a/src/Formats/Parsers/TilesetGraphicsLoader.cs b/src/Formats/Parsers/TilesetGraphicsLoader.cs index d22d1ed61..dbc98f2a0 100644 --- a/src/Formats/Parsers/TilesetGraphicsLoader.cs +++ b/src/Formats/Parsers/TilesetGraphicsLoader.cs @@ -9,17 +9,17 @@ public class TilesetGraphicsLoader : IAssetLoader { readonly FixedSizeSpriteLoader _spriteLoader = new(); - public ITileGraphics Serdes(ITileGraphics existing, AssetInfo info, ISerializer s, SerdesContext context) + public ITileGraphics Serdes(ITileGraphics existing, ISerializer s, AssetLoadContext context) { - var asset = _spriteLoader.Serdes((IReadOnlyTexture)existing?.Texture, info, s, context); + var asset = _spriteLoader.Serdes((IReadOnlyTexture)existing?.Texture, s, context); if (s.IsWriting()) return existing; - var texture = AtlasPostProcessor.Process(asset, info); + var texture = AtlasPostProcessor.Process(asset, context); return new SimpleTileGraphics(texture); } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((ITileGraphics)existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((ITileGraphics)existing, s, context); } \ No newline at end of file diff --git a/src/Formats/Parsers/TilesetLoader.cs b/src/Formats/Parsers/TilesetLoader.cs index acc56c836..7a03ad1ef 100644 --- a/src/Formats/Parsers/TilesetLoader.cs +++ b/src/Formats/Parsers/TilesetLoader.cs @@ -1,14 +1,16 @@ using SerdesNet; using UAlbion.Config; +using UAlbion.Config.Properties; using UAlbion.Formats.Assets.Maps; namespace UAlbion.Formats.Parsers; public class TilesetLoader : IAssetLoader { - public TilesetData Serdes(TilesetData existing, AssetInfo info, ISerializer s, SerdesContext context) - => TilesetData.Serdes(existing, s, info); + public static readonly BoolAssetProperty UseSmallGraphicsProperty = new("UseSmallGraphics"); + public TilesetData Serdes(TilesetData existing, ISerializer s, AssetLoadContext context) + => TilesetData.Serdes(existing, s, context); - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes(existing as TilesetData, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes(existing as TilesetData, s, context); } \ No newline at end of file diff --git a/src/Formats/Parsers/Utf8Loader.cs b/src/Formats/Parsers/Utf8Loader.cs index 82660aed5..d7dd8b806 100644 --- a/src/Formats/Parsers/Utf8Loader.cs +++ b/src/Formats/Parsers/Utf8Loader.cs @@ -7,7 +7,7 @@ namespace UAlbion.Formats.Parsers; public class Utf8Loader : IAssetLoader { - public string Serdes(string existing, AssetInfo info, ISerializer s, SerdesContext context) + public string Serdes(string existing, ISerializer s, AssetLoadContext context) { if (s == null) throw new ArgumentNullException(nameof(s)); if(s.IsWriting()) @@ -24,6 +24,6 @@ public string Serdes(string existing, AssetInfo info, ISerializer s, SerdesConte } } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((string) existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((string) existing, s, context); } diff --git a/src/Formats/Parsers/VarSetLoader.cs b/src/Formats/Parsers/VarSetLoader.cs index f2d0aa5ce..1924274eb 100644 --- a/src/Formats/Parsers/VarSetLoader.cs +++ b/src/Formats/Parsers/VarSetLoader.cs @@ -8,24 +8,23 @@ namespace UAlbion.Formats.Parsers; public class VarSetLoader : IAssetLoader { - public VarSet Serdes(VarSet existing, AssetInfo info, ISerializer s, SerdesContext context) + public VarSet Serdes(VarSet existing, ISerializer s, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (context == null) throw new ArgumentNullException(nameof(context)); - if (!context.Disk.FileExists(info.File.Filename)) - throw new FileNotFoundException($"Could not find game config file at expected path {info.File.Filename}"); + if (!context.Disk.FileExists(context.Filename)) + throw new FileNotFoundException($"Could not find game config file at expected path {context.Filename}"); if (s.IsWriting()) - Save(existing, info.File.Filename, context.Disk, context.Json); + Save(existing, context.Filename, context.Disk, context.Json); else - existing = Load(context.ModName, info.File.Filename, context.Disk, context.Json); + existing = Load(context.ModName, context.Filename, context.Disk, context.Json); return existing; } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((VarSet)existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((VarSet)existing, s, context); public static VarSet Load(string name, string path, IFileSystem disk, IJsonUtil json) { diff --git a/src/Formats/Parsers/WavLoader.cs b/src/Formats/Parsers/WavLoader.cs index a13fb87a7..258f8c6f7 100644 --- a/src/Formats/Parsers/WavLoader.cs +++ b/src/Formats/Parsers/WavLoader.cs @@ -8,7 +8,7 @@ namespace UAlbion.Formats.Parsers; public class WavLoader : IAssetLoader { - public ISample Serdes(ISample existing, AssetInfo info, ISerializer s, SerdesContext context) + public ISample Serdes(ISample existing, ISerializer s, AssetLoadContext context) { if (s == null) throw new ArgumentNullException(nameof(s)); if (s.IsWriting() && existing == null) @@ -35,7 +35,7 @@ public ISample Serdes(ISample existing, AssetInfo info, ISerializer s, SerdesCon s.UInt32("TotalSize", (uint)s.Offset - 4); // Write actual length to container format chunk s.Seek(offset); } - else ApiUtil.Assert(fullSize == (int)s.Offset - 4, "Full size of WAV doesn't match bytes read"); + // else ApiUtil.Assert(fullSize == (int)s.Offset - 4, "Full size of WAV doesn't match bytes read"); // Lots of these in the original game's wav libraries return existing; } @@ -65,6 +65,6 @@ static void SerdesDataTag(ISample w, ISerializer s) w.Samples = s.Bytes(nameof(w.Samples), w.Samples, sampleCount); } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((ISample) existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((ISample) existing, s, context); } diff --git a/src/Formats/Parsers/WaveLibLoader.cs b/src/Formats/Parsers/WaveLibLoader.cs index 6413080ba..d741a2ea1 100644 --- a/src/Formats/Parsers/WaveLibLoader.cs +++ b/src/Formats/Parsers/WaveLibLoader.cs @@ -7,12 +7,11 @@ namespace UAlbion.Formats.Parsers; public class WaveLibLoader : IAssetLoader { - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((WaveLib)existing, info, s, context); - public WaveLib Serdes(WaveLib existing, AssetInfo info, ISerializer s, SerdesContext context) + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((WaveLib)existing, s, context); + public WaveLib Serdes(WaveLib existing, ISerializer s, AssetLoadContext context) { if (s == null) throw new ArgumentNullException(nameof(s)); - if (info == null) throw new ArgumentNullException(nameof(info)); return WaveLib.Serdes(existing, s); } } diff --git a/src/Formats/Parsers/WaveLibWavLoader.cs b/src/Formats/Parsers/WaveLibWavLoader.cs index 8ad6f9ef1..5a06b624b 100644 --- a/src/Formats/Parsers/WaveLibWavLoader.cs +++ b/src/Formats/Parsers/WaveLibWavLoader.cs @@ -2,6 +2,7 @@ using System.Text.RegularExpressions; using SerdesNet; using UAlbion.Config; +using UAlbion.Config.Properties; using UAlbion.Formats.Assets; using static System.FormattableString; @@ -11,15 +12,16 @@ public class WaveLibWavLoader : IAssetLoader { static readonly WavLoader WavLoader = new(); static readonly Regex NameRegex = new(@"i(\d+)t(\d+)"); - public WaveLib Serdes(WaveLib existing, AssetInfo info, ISerializer s, SerdesContext context) + public WaveLib Serdes(WaveLib existing, ISerializer s, AssetLoadContext context) { if (s == null) throw new ArgumentNullException(nameof(s)); + if (context == null) throw new ArgumentNullException(nameof(context)); return s.IsWriting() - ? Write(existing, info, s, context) + ? Write(existing, s, context) : Read(s, context); } - static WaveLib Read(ISerializer s, SerdesContext context) + static WaveLib Read(ISerializer s, AssetLoadContext context) { var lib = new WaveLib(); int i = 0; @@ -44,7 +46,7 @@ static WaveLib Read(ISerializer s, SerdesContext context) var instrument = int.Parse(m.Groups[1].Value); var type = int.Parse(m.Groups[2].Value); - var sample = FormatUtil.DeserializeFromBytes(bytes, s2 => WavLoader.Serdes(null, null, s2, context)); + var sample = FormatUtil.DeserializeFromBytes(bytes, s2 => WavLoader.Serdes(null, s2, context)); lib.Samples[i] = new WaveLibSample { Active = true, @@ -64,9 +66,11 @@ static WaveLib Read(ISerializer s, SerdesContext context) return lib; } - static WaveLib Write(WaveLib existing, AssetInfo info, ISerializer s, SerdesContext context) + static readonly AssetPathPattern DefaultPattern = AssetPathPattern.Build("{0}_{1}_{2}.dat"); + static WaveLib Write(WaveLib existing, ISerializer s, AssetLoadContext context) { if (existing == null) throw new ArgumentNullException(nameof(existing)); + if (context == null) throw new ArgumentNullException(nameof(context)); PackedChunks.PackNamed(s, WaveLib.MaxSamples, i => { @@ -75,14 +79,14 @@ static WaveLib Write(WaveLib existing, AssetInfo info, ISerializer s, SerdesCont return (Array.Empty(), null); string extension = Invariant($"i{sample.Instrument}t{sample.Type}"); - var pattern = info.GetPattern(AssetProperty.Pattern, "{0}_{1}_{2}.dat"); - var name = pattern.Format(new AssetPath(info, i, extension)); - var bytes = FormatUtil.SerializeToBytes(s2 => WavLoader.Serdes(sample, null, s2, context)); + var pattern = context.GetProperty(AssetProps.Pattern, DefaultPattern); + var name = pattern.Format(context.BuildAssetPath(i, extension)); + var bytes = FormatUtil.SerializeToBytes(s2 => WavLoader.Serdes(sample, s2, context)); return (bytes, name); }); return existing; } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((WaveLib)existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((WaveLib)existing, s, context); } diff --git a/src/Formats/Parsers/WordCollector.cs b/src/Formats/Parsers/WordCollector.cs deleted file mode 100644 index 4849e1f3e..000000000 --- a/src/Formats/Parsers/WordCollector.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Linq; -using SerdesNet; -using UAlbion.Api.Eventing; -using UAlbion.Config; -using UAlbion.Formats.Assets; - -namespace UAlbion.Formats.Parsers; - -public class WordCollector : Component, IAssetLoader -{ - static readonly AssetId Words1 = AssetId.From(Base.Special.Words1); - static readonly AssetId Words2 = AssetId.From(Base.Special.Words2); - static readonly AssetId Words3 = AssetId.From(Base.Special.Words3); - - public ListStringSet Serdes(ListStringSet existing, AssetInfo info, ISerializer s, SerdesContext context) - { - if (info == null) throw new ArgumentNullException(nameof(info)); - if (context == null) throw new ArgumentNullException(nameof(context)); - if (s.IsWriting()) return existing; - - var predicate = info.AssetId switch - { - { } x when x == Words1 => (Func)(y => y < 500), - { } x when x == Words2 => y => y is >= 500 and < 1000, - { } x when x == Words3 => y => y >= 1000, - _ => throw new ArgumentOutOfRangeException(nameof(info)) - }; - - var assets = Resolve(); - var ids = - context.Mapping.EnumerateAssetsOfType(AssetType.Word) - .Where(x => predicate(x.Id)) - // make sure TextId->StringId resolution won't happen - // and get stuck in infinite recursion - .Select(x => new StringId(x, 0)) - .ToArray(); - - var language = info.Get(AssetProperty.Language, Base.Language.English); - var dict = ids.ToDictionary( - x => x.Id.Id, - x => assets.LoadString(x, language)); - - var list = new ListStringSet(); - for (int i = 0; i < 1500; i++) - list.Add(null); - - foreach (var kvp in dict) - list[kvp.Key] = kvp.Value; - return list; - } - - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((ListStringSet) existing, info, s, context); -} \ No newline at end of file diff --git a/src/Formats/Parsers/WordListLoader.cs b/src/Formats/Parsers/WordListLoader.cs index 9210d7264..d75195efb 100644 --- a/src/Formats/Parsers/WordListLoader.cs +++ b/src/Formats/Parsers/WordListLoader.cs @@ -11,13 +11,13 @@ public class WordListLoader : IAssetLoader { const int WordLength = 21; - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((ListStringSet)existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((ListStringSet)existing, s, context); - public ListStringSet Serdes(ListStringSet existing, AssetInfo info, ISerializer s, SerdesContext context) + public ListStringSet Serdes(ListStringSet existing, ISerializer s, AssetLoadContext context) { if (s == null) throw new ArgumentNullException(nameof(s)); - if (info == null) throw new ArgumentNullException(nameof(info)); + if (context == null) throw new ArgumentNullException(nameof(context)); if (s.IsReading()) { @@ -25,6 +25,7 @@ public ListStringSet Serdes(ListStringSet existing, AssetInfo info, ISerializer var strings = new List(); while (s.BytesRemaining > 0) strings.Add(s.FixedLengthString(null, null, WordLength)); + return new ListStringSet(strings); } else diff --git a/src/Formats/StubAssetManager.cs b/src/Formats/StubAssetManager.cs index b9a7d5b8f..95b48fbe9 100644 --- a/src/Formats/StubAssetManager.cs +++ b/src/Formats/StubAssetManager.cs @@ -20,7 +20,7 @@ public class StubAssetManager : IAssetManager { static readonly AlbionPalette Greyscale = new(0, "Greyscale", Enumerable.Range(0, 256).Select(x => ApiUtil.PackColor((byte)x, (byte)x, (byte)x, 255)).ToArray()); public AlbionPalette LoadPalette(PaletteId id) => Greyscale; - public AssetInfo GetAssetInfo(AssetId id, string language = null) => throw new NotImplementedException(); + public AssetNode GetAssetInfo(AssetId id, string language = null) => throw new NotImplementedException(); public Automap LoadAutomap(AutomapId id) => throw new NotImplementedException(); public PartyMemberInfo LoadPartyMember(PartyMemberId id) => throw new NotImplementedException(); public CharacterSheet LoadSheet(SheetId id) => throw new NotImplementedException(); @@ -51,9 +51,9 @@ public class StubAssetManager : IAssetManager public object LoadSoundBanks() => throw new NotImplementedException(); public IVarSet LoadConfig() => throw new NotImplementedException(); public InputConfig LoadInputConfig() => throw new NotImplementedException(); - - public string LoadString(StringId id) => throw new NotImplementedException(); - public string LoadString(StringId id, string language) => throw new NotImplementedException(); - public string LoadString(TextId id) => throw new NotImplementedException(); - public string LoadString(TextId id, string language) => throw new NotImplementedException(); + public string LoadStringRaw(TextId id, string language = null) => throw new NotImplementedException(); + public string LoadStringRaw(StringId id, string language = null) => throw new NotImplementedException(); + public string LoadStringSafe(TextId id, string language) => throw new NotImplementedException(); + public string LoadStringSafe(StringId id, string language) => throw new NotImplementedException(); + public IStringSet LoadStringSet(StringSetId id, string language) => throw new NotImplementedException(); } diff --git a/src/Game.Veldrid/Assets/IsometricBuilder.cs b/src/Game.Veldrid/Assets/IsometricBuilder.cs index 6eadb5240..a923543fb 100644 --- a/src/Game.Veldrid/Assets/IsometricBuilder.cs +++ b/src/Game.Veldrid/Assets/IsometricBuilder.cs @@ -43,11 +43,11 @@ public class IsometricBuilder : Component public IFramebufferHolder Framebuffer { get; } int ExpansionFactor => _mode == IsometricMode.Contents ? ContentsExpansionFactor : 1; - public IsometricBuilder(IFramebufferHolder framebuffer, ICameraProvider cameraProvider, int width, int height, int diamondHeight, int tilesPerRow) + public IsometricBuilder(ModContext modContext, IFramebufferHolder framebuffer, ICameraProvider cameraProvider, int width, int height, int diamondHeight, int tilesPerRow) { Framebuffer = framebuffer ?? throw new ArgumentNullException(nameof(framebuffer)); _labId = Base.Labyrinth.Test1; - _layout = AttachChild(new IsometricLayout()); + _layout = AttachChild(new IsometricLayout(modContext)); _cameraProvider = cameraProvider ?? throw new ArgumentNullException(nameof(cameraProvider)); _width = width; _height = height; @@ -111,13 +111,13 @@ void ResizeFramebuffer() Framebuffer.Height = (uint)(ExpansionFactor * _height * rows * mag); } - public List[] Build(LabyrinthData labyrinth, AssetInfo info, IsometricMode mode, IAssetManager assets) + public List[] Build(LabyrinthData labyrinth, AssetLoadContext context, IsometricMode mode, IAssetManager assets) { if (labyrinth == null) throw new ArgumentNullException(nameof(labyrinth)); _labId = labyrinth.Id; _mode = mode; - _layout.Load(labyrinth, info, _mode, BuildProperties(), _paletteId, assets); + _layout.Load(labyrinth, context, _mode, BuildProperties(), _paletteId, assets); ResizeFramebuffer(); Update(); diff --git a/src/Game.Veldrid/Assets/IsometricLabyrinthLoader.cs b/src/Game.Veldrid/Assets/IsometricLabyrinthLoader.cs index cba10fbe4..4a9a86351 100644 --- a/src/Game.Veldrid/Assets/IsometricLabyrinthLoader.cs +++ b/src/Game.Veldrid/Assets/IsometricLabyrinthLoader.cs @@ -7,6 +7,7 @@ using SixLabors.ImageSharp.PixelFormats; using UAlbion.Api.Eventing; using UAlbion.Config; +using UAlbion.Config.Properties; using UAlbion.Core.Events; using UAlbion.Core.Veldrid; using UAlbion.Formats; @@ -26,6 +27,20 @@ public sealed class IsometricLabyrinthLoader : Component, IAssetLoader(); AttachChild(new ShaderCache(pathResolver.ResolvePath("$(CACHE)/ShaderCache"))); @@ -45,7 +60,7 @@ void SetupEngine(int tileWidth, int tileHeight, int baseHeight, int tilesPerRow) _shaderLoader.AddShaderDirectory(shaderPath); _engine = new Engine(GraphicsBackend.Vulkan, false, false); - _isoRsm = new IsometricRenderSystem(tileWidth, tileHeight, baseHeight, tilesPerRow); + _isoRsm = new IsometricRenderSystem(modContext, tileWidth, tileHeight, baseHeight, tilesPerRow); AttachChild(_shaderLoader); AttachChild(_engine); @@ -59,18 +74,18 @@ void SetupEngine(int tileWidth, int tileHeight, int baseHeight, int tilesPerRow) // Raise(new EngineFlagEvent(FlagOperation.Set, EngineFlags.ShowBoundingBoxes)); } - IEnumerable<(string, byte[])> Save(LabyrinthData labyrinth, AssetInfo info, IsometricMode mode, string pngPath, string tsxPath) + IEnumerable<(string, byte[])> Save(LabyrinthData labyrinth, AssetLoadContext context, IsometricMode mode, string pngPath, string tsxPath) { var assets = Resolve(); - var tileWidth = info.Get(AssetProperty.TileWidth, DefaultWidth); - var tileHeight = info.Get(AssetProperty.TileHeight, DefaultHeight); - var baseHeight = info.Get(AssetProperty.BaseHeight, DefaultBaseHeight); - var tilesPerRow = info.Get(AssetProperty.TilesPerRow, DefaultTilesPerRow); + var tileWidth = context.GetProperty(TileWidth, DefaultWidth); + var tileHeight = context.GetProperty(TileHeight, DefaultHeight); + var baseHeight = context.GetProperty(BaseHeight, DefaultBaseHeight); + var tilesPerRow = context.GetProperty(TilesPerRow, DefaultTilesPerRow); if (_engine == null) - SetupEngine(tileWidth, tileHeight, baseHeight, tilesPerRow); + SetupEngine(context.ModContext, tileWidth, tileHeight, baseHeight, tilesPerRow); - var frames = _isoRsm.Builder.Build(labyrinth, info, mode, assets); + var frames = _isoRsm.Builder.Build(labyrinth, context, mode, assets); _engine.RenderFrame(false); Image image = _engine.ReadTexture2D(_isoRsm.IsoBuffer.GetColorTexture(0)); @@ -101,17 +116,16 @@ void SetupEngine(int tileWidth, int tileHeight, int baseHeight, int tilesPerRow) yield return (tsxPath, tsxBytes); } - byte[] SaveJson(LabyrinthData labyrinth, AssetInfo info, SerdesContext context) => + byte[] SaveJson(LabyrinthData labyrinth, AssetLoadContext context) => FormatUtil.SerializeToBytes(s => - _jsonLoader.Serdes(labyrinth, info, s, context)); + _jsonLoader.Serdes(labyrinth, s, context)); - public LabyrinthData Serdes(LabyrinthData existing, AssetInfo info, ISerializer s, SerdesContext context) + public LabyrinthData Serdes(LabyrinthData existing, ISerializer s, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (context == null) throw new ArgumentNullException(nameof(context)); - var json = info.GetPattern(AssetProperty.Pattern, "{0}_{2}.json"); - var path = new AssetPath(info); + var json = context.GetProperty(AssetProps.Pattern, AssetPathPattern.Build("{0}_{2}.json")); + var path = context.BuildAssetPath(); string BuildPath(AssetPathPattern pattern) => pattern.Format(path); if (s.IsReading()) @@ -120,31 +134,31 @@ public LabyrinthData Serdes(LabyrinthData existing, AssetInfo info, ISerializer var (chunk, _) = chunks.Single(); return FormatUtil.DeserializeFromBytes(chunk, s2 => - _jsonLoader.Serdes(null, info, s2, context)); + _jsonLoader.Serdes(null, s2, context)); } if (existing == null) throw new ArgumentNullException(nameof(existing)); - var floorTsx = info.GetPattern(AssetProperty.TiledFloorPattern, "Tiled/{0}_{2}_Floors.tsx"); - var ceilingTsx = info.GetPattern(AssetProperty.TiledCeilingPattern, "Tiled/{0}_{2}_Ceilings.tsx"); - var wallTsx = info.GetPattern(AssetProperty.TiledWallPattern, "Tiled/{0}_{2}_Walls.tsx"); - var contentsTsx = info.GetPattern(AssetProperty.TiledContentsPattern, "Tiled/{0}_{2}_Contents.tsx"); - var floorPng = info.GetPattern(AssetProperty.FloorPngPattern, "Tiled/Gfx/{0}_{2}_Floors.png"); - var ceilingPng = info.GetPattern(AssetProperty.CeilingPngPattern, "Tiled/Gfx/{0}_{2}_Ceilings.png"); - var wallPng = info.GetPattern(AssetProperty.WallPngPattern, "Tiled/Gfx/{0}_{2}_Walls.png"); - var contentsPng = info.GetPattern(AssetProperty.ContentsPngPattern, "Tiled/Gfx/{0}_{2}_Contents.png"); - - var files = new List<(string, byte[])> {(json.Format(path), SaveJson(existing, info, context))}; - files.AddRange(Save(existing, info, IsometricMode.Floors, BuildPath(floorPng), BuildPath(floorTsx))); - files.AddRange(Save(existing, info, IsometricMode.Ceilings, BuildPath(ceilingPng), BuildPath(ceilingTsx))); - files.AddRange(Save(existing, info, IsometricMode.Walls, BuildPath(wallPng), BuildPath(wallTsx))); - files.AddRange(Save(existing, info, IsometricMode.Contents, BuildPath(contentsPng), BuildPath(contentsTsx))); + var floorTsx = context.GetProperty(TiledFloorPattern); + var ceilingTsx = context.GetProperty(TiledCeilingPattern); + var wallTsx = context.GetProperty(TiledWallPattern); + var contentsTsx = context.GetProperty(TiledContentsPattern); + var floorPng = context.GetProperty(FloorPngPattern); + var ceilingPng = context.GetProperty(CeilingPngPattern); + var wallPng = context.GetProperty(WallPngPattern); + var contentsPng = context.GetProperty(ContentsPngPattern); + + var files = new List<(string, byte[])> {(json.Format(path), SaveJson(existing, context))}; + files.AddRange(Save(existing, context, IsometricMode.Floors, BuildPath(floorPng), BuildPath(floorTsx))); + files.AddRange(Save(existing, context, IsometricMode.Ceilings, BuildPath(ceilingPng), BuildPath(ceilingTsx))); + files.AddRange(Save(existing, context, IsometricMode.Walls, BuildPath(wallPng), BuildPath(wallTsx))); + files.AddRange(Save(existing, context, IsometricMode.Contents, BuildPath(contentsPng), BuildPath(contentsTsx))); PackedChunks.PackNamed(s, files.Count, i => (files[i].Item2, files[i].Item1)); return existing; } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((LabyrinthData)existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((LabyrinthData)existing, s, context); public void Dispose() { diff --git a/src/Game.Veldrid/Assets/IsometricRenderSystem.cs b/src/Game.Veldrid/Assets/IsometricRenderSystem.cs index b39320e2e..78c1f66b9 100644 --- a/src/Game.Veldrid/Assets/IsometricRenderSystem.cs +++ b/src/Game.Veldrid/Assets/IsometricRenderSystem.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; using UAlbion.Api.Eventing; +using UAlbion.Config; using UAlbion.Core; using UAlbion.Core.Veldrid; using UAlbion.Core.Veldrid.Etm; @@ -26,7 +27,7 @@ public sealed class IsometricRenderSystem : Component, IDisposable public IsometricBuilder Builder { get; } public IFramebufferHolder IsoBuffer { get; } - public IsometricRenderSystem(int tileWidth, int tileHeight, int baseHeight, int tilesPerRow) + public IsometricRenderSystem(ModContext modContext, int tileWidth, int tileHeight, int baseHeight, int tilesPerRow) { OutputDescription screenFormat = SimpleFramebuffer.Output; @@ -75,7 +76,12 @@ public IsometricRenderSystem(int tileWidth, int tileHeight, int baseHeight, int IsoBuffer = _manager.GetFramebuffer("fb_iso"); AttachChild(_manager); - Builder = new IsometricBuilder(_manager.GetFramebuffer("fb_iso"), sceneManager, tileWidth, tileHeight, baseHeight, tilesPerRow); + Builder = new IsometricBuilder(modContext, + _manager.GetFramebuffer("fb_iso"), + sceneManager, + tileWidth, tileHeight, + baseHeight, + tilesPerRow); AddHelpers(); sceneManager diff --git a/src/Game.Veldrid/Assets/MeshLoader.cs b/src/Game.Veldrid/Assets/MeshLoader.cs index 11599647a..9d92f3957 100644 --- a/src/Game.Veldrid/Assets/MeshLoader.cs +++ b/src/Game.Veldrid/Assets/MeshLoader.cs @@ -12,7 +12,6 @@ using UAlbion.Config; using UAlbion.Core.Veldrid; using UAlbion.Core.Visual; -using UAlbion.Formats; namespace UAlbion.Game.Veldrid.Assets; @@ -21,9 +20,8 @@ public class MeshLoader : IAssetLoader readonly PngDecoder _decoder = new(); readonly Configuration _configuration = new(); - public Mesh Serdes(Mesh existing, AssetInfo info, ISerializer s, SerdesContext context) + public Mesh Serdes(Mesh existing, ISerializer s, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (s == null) throw new ArgumentNullException(nameof(s)); if (context == null) throw new ArgumentNullException(nameof(context)); @@ -41,9 +39,9 @@ public Mesh Serdes(Mesh existing, AssetInfo info, ISerializer s, SerdesContext c var mesh = obj.GetFirstMesh(); - var materialPath = Path.Combine(info.File.Filename, obj.MaterialLibName); + var materialPath = Path.Combine(context.Filename, obj.MaterialLibName); if (!context.Disk.FileExists(materialPath)) - throw new FileNotFoundException($"Could not find material file \"{materialPath}\" for object {info.AssetId} \"{info.File.Filename}\""); + throw new FileNotFoundException($"Could not find material file \"{materialPath}\" for object {context.AssetId} \"{context.Filename}\""); using var materialStream = context.Disk.OpenRead(materialPath); MtlParser mtlParser = new(); @@ -59,7 +57,7 @@ void AddTexture(string filename) if (string.IsNullOrEmpty(filename) || textures.ContainsKey(filename)) return; - var texture = LoadTexture(info, filename, context.Disk); + var texture = LoadTexture(context, filename, context.Disk); if (texture != null) textures[filename] = texture; } @@ -73,13 +71,13 @@ void AddTexture(string filename) AddTexture(material.DisplacementMap); AddTexture(material.StencilDecalTexture); - var key = new MeshId(info.AssetId); + var key = new MeshId(context.AssetId); return new Mesh(key, mesh, material, textures); } - ITexture LoadTexture(AssetInfo info, string filename, IFileSystem disk) + ITexture LoadTexture(AssetLoadContext context, string filename, IFileSystem disk) { - var path = Path.Combine(info.File.Filename, filename); + var path = Path.Combine(context.Filename, filename); if (!disk.FileExists(path)) return null; @@ -89,12 +87,12 @@ ITexture LoadTexture(AssetInfo info, string filename, IFileSystem disk) throw new InvalidOperationException("Could not retrieve single span from Image"); var span = MemoryMarshal.Cast(rgbaSpan); - var texture = new SimpleTexture(info.AssetId, info.AssetId.ToString(), image.Width, image.Height, span); + var texture = new SimpleTexture(context.AssetId, context.AssetId.ToString(), image.Width, image.Height, span); texture.AddRegion(0, 0, image.Width, image.Height); return texture; } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((Mesh)existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((Mesh)existing, s, context); } diff --git a/src/Game.Veldrid/Assets/NpcTilesetLoader.cs b/src/Game.Veldrid/Assets/NpcTilesetLoader.cs index b3d0741e6..4bed8dcf9 100644 --- a/src/Game.Veldrid/Assets/NpcTilesetLoader.cs +++ b/src/Game.Veldrid/Assets/NpcTilesetLoader.cs @@ -5,6 +5,7 @@ using UAlbion.Api.Eventing; using UAlbion.Api.Visual; using UAlbion.Config; +using UAlbion.Config.Properties; using UAlbion.Formats; using UAlbion.Formats.Exporters.Tiled; using UAlbion.Formats.Parsers; @@ -13,12 +14,13 @@ namespace UAlbion.Game.Veldrid.Assets; public class NpcTilesetLoader : Component, IAssetLoader { + public static readonly BoolAssetProperty IsSmallProperty = new("IsSmall"); // TODO, combine with UseSmallGraphics + public static readonly PathPatternProperty GraphicsPattern = new("GraphicsPattern"); readonly Png8Loader _png8Loader = new(); public NpcTilesetLoader() => AttachChild(_png8Loader); - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) + public object Serdes(object existing, ISerializer s, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (s == null) throw new ArgumentNullException(nameof(s)); if (context == null) throw new ArgumentNullException(nameof(context)); @@ -26,13 +28,12 @@ public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesConte return new object(); if (existing == null) throw new ArgumentNullException(nameof(existing)); - var graphicsPattern = info.GetPattern(AssetProperty.GraphicsPattern, ""); - bool small = info.Get(AssetProperty.IsSmall, false); + var graphicsPattern = context.GetProperty(GraphicsPattern); + bool small = context.GetProperty(IsSmallProperty); - var tsxDir = Path.GetDirectoryName(info.File.Filename); + var tsxDir = Path.GetDirectoryName(context.Filename); var tiles = new List(); - var assets = Resolve(); - var modApplier = Resolve(); + var sourceAssets = Resolve(); var pathResolver = Resolve(); var assetIds = AssetMapping.Global.EnumerateAssetsOfType(small @@ -41,18 +42,18 @@ public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesConte foreach (var id in assetIds) { - var sprite = assets.LoadTexture(id); // Get sprite from source mod - var spriteInfo = modApplier.GetAssetInfo(id, null); // But get AssetInfo from target mod - + var sprite = sourceAssets.LoadTexture(id); // Get sprite from source mod + var sourceNode = sourceAssets.GetAssetInfo(id); // Ugh, hacky. - int palId = spriteInfo.Get(AssetProperty.PaletteId, 0); - if (palId == 0) - spriteInfo.Set(AssetProperty.PaletteId, - assets.GetAssetInfo(id).Get(AssetProperty.PaletteId, 0)); + var npcNode = new AssetNode(id); + npcNode.SetProperty(AssetProps.Palette, sourceNode.PaletteId); + + var subContext = new AssetLoadContext(id, npcNode, context.ModContext); + var assetPath = subContext.BuildAssetPath(9); // 9 = First frame facing west for both large and small - var path = graphicsPattern.Format(new AssetPath(spriteInfo, 9)); // 9 = First frame facing west for both large and small + var path = graphicsPattern.Format(assetPath); path = pathResolver.ResolvePath(path); - WriteNpcSprite(path, sprite, spriteInfo, context); + WriteNpcSprite(path, sprite, subContext); var pathRelativeToTsx = ConfigUtil.GetRelativePath(path, tsxDir, true); @@ -71,13 +72,13 @@ public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesConte return existing; } - void WriteNpcSprite(string path, ITexture sprite, AssetInfo info, SerdesContext context) + void WriteNpcSprite(string path, ITexture sprite, AssetLoadContext context) { var dir = Path.GetDirectoryName(path); if (!context.Disk.DirectoryExists(dir)) context.Disk.CreateDirectory(dir); - using var s = FormatUtil.SerializeWithSerdes(s => _png8Loader.Serdes(sprite, info, s, context)); + using var s = FormatUtil.SerializeWithSerdes(s => _png8Loader.Serdes(sprite, s, context)); int i = 0; foreach (var (chunk, _) in PackedChunks.Unpack(s)) { diff --git a/src/Game.Veldrid/Assets/PalettePng32Loader.cs b/src/Game.Veldrid/Assets/PalettePng32Loader.cs index cedde1a10..378375427 100644 --- a/src/Game.Veldrid/Assets/PalettePng32Loader.cs +++ b/src/Game.Veldrid/Assets/PalettePng32Loader.cs @@ -14,7 +14,7 @@ namespace UAlbion.Game.Veldrid.Assets; public class PalettePng32Loader : Component, IAssetLoader { - public AlbionPalette Serdes(AlbionPalette existing, AssetInfo info, ISerializer s, SerdesContext context) + public AlbionPalette Serdes(AlbionPalette existing, ISerializer s, AssetLoadContext context) { if (existing == null) throw new ArgumentNullException(nameof(existing)); if (s == null) throw new ArgumentNullException(nameof(s)); @@ -43,6 +43,6 @@ public AlbionPalette Serdes(AlbionPalette existing, AssetInfo info, ISerializer return existing; } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((AlbionPalette)existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((AlbionPalette)existing, s, context); } \ No newline at end of file diff --git a/src/Game.Veldrid/Assets/Png32Loader.cs b/src/Game.Veldrid/Assets/Png32Loader.cs index 49730d257..d975f70ca 100644 --- a/src/Game.Veldrid/Assets/Png32Loader.cs +++ b/src/Game.Veldrid/Assets/Png32Loader.cs @@ -58,9 +58,8 @@ static IReadOnlyTexture Read(AssetId id, IList> images) return new SimpleTexture(id, id.ToString(), totalWidth, totalHeight, pixels, frames); } - public IReadOnlyTexture Serdes(IReadOnlyTexture existing, AssetInfo info, ISerializer s, SerdesContext context) + public IReadOnlyTexture Serdes(IReadOnlyTexture existing, ISerializer s, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (s == null) throw new ArgumentNullException(nameof(s)); if (context == null) throw new ArgumentNullException(nameof(context)); @@ -86,11 +85,11 @@ public IReadOnlyTexture Serdes(IReadOnlyTexture existing, AssetInfo images.Add(decoder.Decode(configuration, stream)); } - return Read(info.AssetId, images); + return Read(context.AssetId, images); } finally { foreach (var image in images) image.Dispose(); } } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((IReadOnlyTexture)existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((IReadOnlyTexture)existing, s, context); } \ No newline at end of file diff --git a/src/Game.Veldrid/Assets/Png8Loader.cs b/src/Game.Veldrid/Assets/Png8Loader.cs index 711762061..f553f765c 100644 --- a/src/Game.Veldrid/Assets/Png8Loader.cs +++ b/src/Game.Veldrid/Assets/Png8Loader.cs @@ -20,6 +20,53 @@ namespace UAlbion.Game.Veldrid.Assets; public class Png8Loader : Component, IAssetLoader> { + public IReadOnlyTexture Serdes(IReadOnlyTexture existing, ISerializer s, AssetLoadContext context) + { + if (s == null) throw new ArgumentNullException(nameof(s)); + if (context == null) throw new ArgumentNullException(nameof(context)); + + var assets = Resolve(); + var paletteId = context.PaletteId; + + if (paletteId.IsNone) + throw new InvalidOperationException($"No palette id specified for {context.AssetId} ({context.AssetId.Id})"); + + var palette = assets.LoadPalette(paletteId); + if (palette == null) + throw new InvalidOperationException($"Could not load palette {paletteId} for asset {context.AssetId} in file {context.Filename}"); + + var unambiguousPalette = palette.GetUnambiguousPalette(); + + if (s.IsWriting()) + { + if (existing == null) + throw new ArgumentNullException(nameof(existing)); + + var encoder = new PngEncoder(); + PackedChunks.Pack(s, existing.Regions.Count, frameNum => Write(encoder, unambiguousPalette, existing, frameNum)); + return existing; + } + + // Read + var decoder = new PngDecoder(); + var configuration = new Configuration(); + var images = new List>(); + try + { + foreach (var (bytes, _) in PackedChunks.Unpack(s)) + { + using var stream = new MemoryStream(bytes); + images.Add(decoder.Decode(configuration, stream)); + } + + return Read(context.AssetId, unambiguousPalette, images, paletteId); + } + finally { foreach (var image in images) image.Dispose(); } + } + + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((IReadOnlyTexture)existing, s, context); + static byte[] Write(IImageEncoder encoder, uint[] palette, IReadOnlyTexture existing, int frameNum) { var frame = existing.Regions[frameNum]; @@ -34,7 +81,7 @@ static byte[] Write(IImageEncoder encoder, uint[] palette, IReadOnlyTexture Read(AssetId id, uint[] palette, IList> images) + static IReadOnlyTexture Read(AssetId id, uint[] palette, IList> images, PaletteId paletteId) { int totalWidth = images.Max(x => x.Width); int totalHeight = images.Sum(x => x.Height); @@ -42,6 +89,7 @@ static IReadOnlyTexture Read(AssetId id, uint[] palette, IList(); int currentY = 0; var quantizeCache = new Dictionary(); + for (int i = 0; i < images.Count; i++) { Image image = images[i]; @@ -60,49 +108,4 @@ static IReadOnlyTexture Read(AssetId id, uint[] palette, IList(id, id.ToString(), totalWidth, totalHeight, pixels, frames); } - - public IReadOnlyTexture Serdes(IReadOnlyTexture existing, AssetInfo info, ISerializer s, SerdesContext context) - { - if (info == null) throw new ArgumentNullException(nameof(info)); - if (s == null) throw new ArgumentNullException(nameof(s)); - if (context == null) throw new ArgumentNullException(nameof(context)); - - var assets = Resolve(); - var paletteNum = info.Get(AssetProperty.PaletteId, 0); - var paletteId = new PaletteId(paletteNum); - var palette = assets.LoadPalette(paletteId); - if (palette == null) - throw new InvalidOperationException($"Could not load palette {paletteId} ({paletteNum}) for asset {info.AssetId} in file {info.File.Filename}"); - - var unambiguousPalette = palette.GetUnambiguousPalette(); - - if (s.IsWriting()) - { - if (existing == null) - throw new ArgumentNullException(nameof(existing)); - - var encoder = new PngEncoder(); - PackedChunks.Pack(s, existing.Regions.Count, frameNum => Write(encoder, unambiguousPalette, existing, frameNum)); - return existing; - } - - // Read - var decoder = new PngDecoder(); - var configuration = new Configuration(); - var images = new List>(); - try - { - foreach (var (bytes, _) in PackedChunks.Unpack(s)) - { - using var stream = new MemoryStream(bytes); - images.Add(decoder.Decode(configuration, stream)); - } - - return Read(info.AssetId, unambiguousPalette, images); - } - finally { foreach (var image in images) image.Dispose(); } - } - - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((IReadOnlyTexture)existing, info, s, context); } diff --git a/src/Game.Veldrid/Assets/PngSheetLoader.cs b/src/Game.Veldrid/Assets/PngSheetLoader.cs index 4790c265c..4f828e314 100644 --- a/src/Game.Veldrid/Assets/PngSheetLoader.cs +++ b/src/Game.Veldrid/Assets/PngSheetLoader.cs @@ -11,6 +11,7 @@ using UAlbion.Api.Eventing; using UAlbion.Api.Visual; using UAlbion.Config; +using UAlbion.Config.Properties; using UAlbion.Core.Veldrid.Textures; using UAlbion.Formats; using UAlbion.Formats.Assets; @@ -26,7 +27,8 @@ static byte[] Write(IImageEncoder encoder, uint[] palette, IReadOnlyTexture encoder.Encode(image, stream)); } - static IReadOnlyTexture Read(AssetId id, uint[] palette, Image image, int subItemWidth, int subItemHeight) + static IReadOnlyTexture Read(AssetId id, uint[] palette, AssetId paletteId, Image image, + int subItemWidth, int subItemHeight) { var pixels = new byte[image.Width * image.Height]; var frames = new List(); @@ -55,17 +57,19 @@ static bool IsFrameEmpty(Region frame, byte[] pixels, int stride) return true; } - public IReadOnlyTexture Serdes(IReadOnlyTexture existing, AssetInfo info, ISerializer s, SerdesContext context) + public IReadOnlyTexture Serdes(IReadOnlyTexture existing, ISerializer s, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (s == null) throw new ArgumentNullException(nameof(s)); if (context == null) throw new ArgumentNullException(nameof(context)); var assets = Resolve(); - var paletteId = info.Get(AssetProperty.PaletteId, 0); - var palette = info.AssetId.Type == AssetType.FontGfx + var paletteId = context.GetProperty(AssetProps.Palette); + if (paletteId.IsNone) + paletteId = (PaletteId)Base.Palette.Common; + + var palette = context.AssetId.Type == AssetType.FontGfx ? FontDefinition.ExportPalette - : assets.LoadPalette(new PaletteId(paletteId)).GetUnambiguousPalette(); + : assets.LoadPalette(paletteId).GetUnambiguousPalette(); if (s.IsWriting()) { @@ -78,15 +82,15 @@ public IReadOnlyTexture Serdes(IReadOnlyTexture existing, AssetInfo } else // Read { - if (info.Width == 0) + if (context.Width == 0) { - throw new InvalidOperationException($"The asset {info.AssetId} ({info.File.Filename}) is set to use " + + throw new InvalidOperationException($"The asset {context.AssetId} ({context.Filename}) is set to use " + "PngSheetLoader, but does not have its Width property set"); } - if (info.Height == 0) + if (context.Height == 0) { - throw new InvalidOperationException($"The asset {info.AssetId} ({info.File.Filename}) is set to use " + + throw new InvalidOperationException($"The asset {context.AssetId} ({context.Filename}) is set to use " + "PngSheetLoader, but does not have its Height property set"); } @@ -95,10 +99,10 @@ public IReadOnlyTexture Serdes(IReadOnlyTexture existing, AssetInfo var bytes = s.Bytes(null, null, (int) s.BytesRemaining); using var stream = new MemoryStream(bytes); using var image = decoder.Decode(configuration, stream); - return Read(info.AssetId, palette, image, info.Width, info.Height); + return Read(context.AssetId, palette, paletteId, image, context.Width, context.Height); } } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((IReadOnlyTexture)existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((IReadOnlyTexture)existing, s, context); } diff --git a/src/Game.Veldrid/Assets/PngTileLoader32Bit.cs b/src/Game.Veldrid/Assets/PngTileLoader32Bit.cs index 0e43108c9..ebed63318 100644 --- a/src/Game.Veldrid/Assets/PngTileLoader32Bit.cs +++ b/src/Game.Veldrid/Assets/PngTileLoader32Bit.cs @@ -11,9 +11,9 @@ using UAlbion.Api.Eventing; using UAlbion.Api.Visual; using UAlbion.Config; +using UAlbion.Config.Properties; using UAlbion.Core; using UAlbion.Core.Veldrid; -using UAlbion.Formats; using UAlbion.Formats.Assets.Maps; using Veldrid; @@ -22,27 +22,28 @@ namespace UAlbion.Game.Veldrid.Assets; public class PngTileLoader32Bit : Component, IAssetLoader { const PixelFormat TextureFormat = PixelFormat.R8_G8_B8_A8_UNorm; + public static readonly StringAssetProperty DayPath = new("DayPath"); + public static readonly StringAssetProperty NightPath = new("NightPath"); record FrameInfo(string Path, int SubId, int PalFrame); readonly PngDecoder _decoder = new(); readonly Configuration _configuration = new(); - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((ITileGraphics)existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((ITileGraphics)existing, s, context); - public ITileGraphics Serdes(ITileGraphics existing, AssetInfo info, ISerializer s, SerdesContext context) + public ITileGraphics Serdes(ITileGraphics existing, ISerializer s, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (context == null) throw new ArgumentNullException(nameof(context)); if (s.IsWriting()) throw new NotSupportedException("Saving png tile graphics is not currently supported"); - return Load(info, context); + return Load(context); } - ITileGraphics Load(AssetInfo info, SerdesContext context) + ITileGraphics Load(AssetLoadContext context) { var engine = (Engine)Resolve(); - var (dayInfo, nightInfo) = FindFiles(info, context.Disk); + var (dayInfo, nightInfo) = FindFiles(context); var totalPngs = 1 + // Region 0 = Blank instance @@ -80,7 +81,7 @@ ReadOnlyImageBuffer AccessRegion(LazyTexture texture, Region region, return new ReadOnlyImageBuffer(png.Width, png.Height, png.Width, MemoryMarshal.Cast(rgbaSpan)); } - var texture = new LazyTexture(AccessRegion, info.AssetId, info.ToString(), layout.Width, layout.Height, layout.Layers); + var texture = new LazyTexture(AccessRegion, context.AssetId, context.ToString(), layout.Width, layout.Height, layout.Layers); texture.AddRegion(null, 0, 0, tileWidth, tileHeight); // Region 0 is a blank one for unmapped sub-ids int regionNum = 1; @@ -108,12 +109,12 @@ public int Compare(FrameInfo x, FrameInfo y) } } - static List GetInfo(string dir, IFileSystem disk, AssetPathPattern pattern) + static List GetInfo(string dir, AssetType assetType, IFileSystem disk, AssetPathPattern pattern) { var frames = new List(); - foreach (var path in disk.EnumerateDirectory(dir, "*.png")) + foreach (var path in disk.EnumerateFiles(dir, "*.png")) { - if (!pattern.TryParse(Path.GetFileName(path), out var assetPath)) + if (!pattern.TryParse(Path.GetFileName(path), assetType, out var assetPath)) continue; frames.Add(new FrameInfo(path, assetPath.SubAsset, assetPath.PaletteFrame ?? 0)); @@ -159,20 +160,21 @@ static void AddAtIndex(List list, T element, T defaultElement, int index) } - static (List dayInfo, List nightInfo) FindFiles(AssetInfo info, IFileSystem disk) + static (List dayInfo, List nightInfo) FindFiles(AssetLoadContext context) { - var pattern = info.GetPattern(AssetProperty.Pattern, "{ignorenum}_{frame:0000}_{palframe:0000}.png"); - var dayPath = info.Get(AssetProperty.DayPath, null); - var nightPath = info.Get(AssetProperty.NightPath, null); + var pattern = context.GetProperty(AssetProps.Pattern, AssetPathPattern.Build("{ignorenum}_{frame:0000}_{palframe:0000}.png")); + var dayPath = context.GetProperty(DayPath); + var nightPath = context.GetProperty(NightPath); if (!string.IsNullOrEmpty(dayPath)) - dayPath = Path.Combine(info.File.Filename, dayPath); + dayPath = Path.Combine(context.Filename, dayPath); if (!string.IsNullOrEmpty(nightPath)) - nightPath = Path.Combine(info.File.Filename, nightPath); + nightPath = Path.Combine(context.Filename, nightPath); - var dayInfo = GetInfo(dayPath, disk, pattern); - var nightInfo = nightPath != null ? GetInfo(nightPath, disk, pattern) : null; + var assetType = context.AssetId.Type; + var dayInfo = GetInfo(dayPath, assetType, context.Disk, pattern); + var nightInfo = nightPath != null ? GetInfo(nightPath, assetType, context.Disk, pattern) : null; return (dayInfo, nightInfo); } diff --git a/src/Game.Veldrid/Assets/PngTileLoader8Bit.cs b/src/Game.Veldrid/Assets/PngTileLoader8Bit.cs index 1e524ac9f..8d9bee312 100644 --- a/src/Game.Veldrid/Assets/PngTileLoader8Bit.cs +++ b/src/Game.Veldrid/Assets/PngTileLoader8Bit.cs @@ -3,7 +3,6 @@ using UAlbion.Api.Eventing; using UAlbion.Api.Visual; using UAlbion.Config; -using UAlbion.Formats; using UAlbion.Formats.Assets.Maps; using UAlbion.Formats.Parsers; @@ -14,9 +13,9 @@ public class PngTileLoader8Bit : Component, IAssetLoader readonly Png8Loader _png8Loader = new(); public PngTileLoader8Bit() => AttachChild(_png8Loader); - public ITileGraphics Serdes(ITileGraphics existing, AssetInfo info, ISerializer s, SerdesContext context) + public ITileGraphics Serdes(ITileGraphics existing, ISerializer s, AssetLoadContext context) { - if (info == null) throw new ArgumentNullException(nameof(info)); + if (context == null) throw new ArgumentNullException(nameof(context)); if (s.IsWriting()) { @@ -24,19 +23,19 @@ public ITileGraphics Serdes(ITileGraphics existing, AssetInfo info, ISerializer if (existing.Texture is not IReadOnlyTexture texture) throw new FormatException( - $"Tried to save tileset {info.AssetId} as pngs using PngTileLoader8Bit, but it is not backed by 8-bit textures"); + $"Tried to save tileset {context.AssetId} as pngs using PngTileLoader8Bit, but it is not backed by 8-bit textures"); - _png8Loader.Serdes(texture, info, s, context); + _png8Loader.Serdes(texture, s, context); return existing; } else { - var texture = _png8Loader.Serdes(null, info, s, context); - texture = AtlasPostProcessor.Process(texture, info); + var texture = _png8Loader.Serdes(null, s, context); + texture = AtlasPostProcessor.Process(texture, context); return new SimpleTileGraphics(texture); } } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes((ITileGraphics)existing, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes((ITileGraphics)existing, s, context); } \ No newline at end of file diff --git a/src/Game.Veldrid/Assets/ShaderLoader.cs b/src/Game.Veldrid/Assets/ShaderLoader.cs deleted file mode 100644 index 418b9fe24..000000000 --- a/src/Game.Veldrid/Assets/ShaderLoader.cs +++ /dev/null @@ -1 +0,0 @@ -namespace UAlbion.Game.Veldrid.Assets; \ No newline at end of file diff --git a/src/Game.Veldrid/Audio/AudioManager.cs b/src/Game.Veldrid/Audio/AudioManager.cs index 9a41db69f..5c71cb3a5 100644 --- a/src/Game.Veldrid/Audio/AudioManager.cs +++ b/src/Game.Veldrid/Audio/AudioManager.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using UAlbion.Api.Eventing; using UAlbion.Config; +using UAlbion.Config.Properties; using UAlbion.Core.Events; using UAlbion.Core.Veldrid.Audio; using UAlbion.Core.Visual; @@ -21,6 +22,8 @@ namespace UAlbion.Game.Veldrid.Audio; public sealed class AudioManager : ServiceComponent, IAudioManager, IDisposable { const int DefaultSampleRate = 11025; + public static readonly AssetIdAssetProperty WaveLibProperty = new("WaveLib", WaveLibraryId.None, x => x); + readonly bool _standalone; readonly IDictionary _sampleCache = new Dictionary(); readonly IDictionary<(SongId, int), AudioBuffer> _waveLibCache = new Dictionary<(SongId, int), AudioBuffer>(); @@ -104,7 +107,7 @@ AudioBuffer GetBuffer(SongId songId, int instrument) return buffer; var assets = Resolve(); var songInfo = assets.GetAssetInfo(songId); - var waveLibId = songInfo.Get(AssetProperty.WaveLib, WaveLibraryId.None); + var waveLibId = songInfo.GetProperty(WaveLibProperty); if (waveLibId.IsNone) { Info($"Song {songId} has no associated wave library"); diff --git a/src/Game.Veldrid/Diag/CodeWindow.cs b/src/Game.Veldrid/Diag/CodeWindow.cs index 0f60e2d6f..02b29993d 100644 --- a/src/Game.Veldrid/Diag/CodeWindow.cs +++ b/src/Game.Veldrid/Diag/CodeWindow.cs @@ -67,7 +67,7 @@ void DrawContext(EventContext context) if (set.Decompiled == null) { var assets = Resolve(); - var eventFormatter = new EventFormatter(assets.LoadString, context.EventSet.TextId); + var eventFormatter = new EventFormatter(assets.LoadStringSafe, context.EventSet.StringSetId); set.Decompiled = eventFormatter.Decompile(set.Events, set.Chains, set.ExtraEntryPoints); var code = set.Decompiled.Script; _editor.AllText = code; diff --git a/src/Game.Veldrid/Input/CursorManager.cs b/src/Game.Veldrid/Input/CursorManager.cs index 4a2ca7b89..e40f0c489 100644 --- a/src/Game.Veldrid/Input/CursorManager.cs +++ b/src/Game.Veldrid/Input/CursorManager.cs @@ -1,6 +1,7 @@ using System.Numerics; using UAlbion.Api.Eventing; using UAlbion.Api.Visual; +using UAlbion.Config.Properties; using UAlbion.Core; using UAlbion.Core.Events; using UAlbion.Core.Veldrid.Events; @@ -20,7 +21,7 @@ namespace UAlbion.Game.Veldrid.Input; public class CursorManager : ServiceComponent, ICursorManager { static readonly Vector2 ItemSpriteOffset = new(3, 3); - + static readonly StringAssetProperty Hotspot = new("Hotspot"); public Vector2 Position { get; private set; } Vector2 _hotspot; BatchLease _cursorSprite; @@ -91,13 +92,14 @@ void SetCursor(SpriteId cursorId) var config = assets.GetAssetInfo(cursorId); _cursorId = cursorId; - var hotspot = CursorHotspot.Parse(config?.Get("Hotspot", null)); + var hotspot = CursorHotspot.Parse(config?.GetProperty(Hotspot, null)); _hotspot = hotspot == null ? Vector2.Zero : window.GuiScale * new Vector2(hotspot.X, hotspot.Y); _dirty = true; } + void Render() { var showHotspot = (Var(UserVars.Debug.DebugFlags) & DebugFlags.ShowCursorHotspot) != 0; diff --git a/src/Game/Assets/AssetCache.cs b/src/Game/Assets/AssetCache.cs index 5b66d8a47..578665811 100644 --- a/src/Game/Assets/AssetCache.cs +++ b/src/Game/Assets/AssetCache.cs @@ -17,9 +17,11 @@ class Entry { public WeakReference Weak; public object Strong; + public AssetNode Node; public DateTime LastAccessed; } + readonly Dictionary> _langCache = new(); readonly Dictionary _cache = new(); public AssetCache() @@ -28,67 +30,148 @@ public AssetCache() { lock (_syncRoot) { + static void Cycle(Dictionary cache, DateTime threshold) + { + + foreach (var key in cache.Keys.ToList()) + { + var entry = cache[key]; + if (entry.Strong != null) + { + if (entry.LastAccessed > threshold) + continue; + + entry.Strong = null; + } + + if (entry.Strong == null && entry.Weak.Target == null) + cache.Remove(key); + } + } + var utcNow = DateTime.UtcNow; var threshold = utcNow.AddSeconds(30); - foreach (var key in _cache.Keys.ToList()) + Cycle(_cache, threshold); + + List emptyLanguages = null; + foreach (var kvp in _langCache) { - var entry = _cache[key]; - if (entry.Strong != null) + Cycle(kvp.Value, threshold); + if (kvp.Value.Count == 0) { - if (entry.LastAccessed > threshold) - continue; - - entry.Strong = null; + emptyLanguages ??= new List(); + emptyLanguages.Add(kvp.Key); } - - if (entry.Strong == null && entry.Weak.Target == null) - _cache.Remove(key); } + + if (emptyLanguages != null) + foreach (var lang in emptyLanguages) + _langCache.Remove(lang); + } + }); + + On(_ => + { + lock (_syncRoot) + { + _cache.Clear(); + _langCache.Clear(); + } + }); + + On(e => + { + lock (_syncRoot) + { + _cache.Remove(e.Id); + foreach (var kvp in _langCache) + kvp.Value.Remove(e.Id); } }); - On(_ => { lock (_syncRoot) { _cache.Clear(); } }); - On(e => { lock (_syncRoot) { _cache.Remove(e.Id); } }); On(_ => { var sb = new StringBuilder(); sb.AppendLine("Asset Statistics:"); + lock (_syncRoot) { - var countByType = _cache.Keys - .GroupBy(x => x.Type) - .Select(x => (x.Key, x.Count())) - .OrderBy(x => x.Key.ToString()); + static void Dump(StringBuilder sb2, Dictionary cache, string lang) + { + var countByType = cache.Keys + .GroupBy(x => x.Type) + .Select(x => (x.Key, x.Count())) + .OrderBy(x => x.Key.ToString()); - foreach(var (type, count) in countByType) - sb.AppendLine(Invariant($" {type}: {count} items")); + if (lang == null) + { + foreach (var (type, count) in countByType) + sb2.AppendLine(Invariant($" {type}: {count} items")); + } + else + { + foreach (var (type, count) in countByType) + sb2.AppendLine(Invariant($" {type} ({lang}): {count} items")); + } + } + + Dump(sb, _cache, ""); + foreach (var kvp in _langCache) + Dump(sb, kvp.Value, kvp.Key); } Info(sb.ToString()); }); } - public object Get(AssetId key) + Dictionary GetCache(string language) + { + var cache = _cache; + if (language != null) + _langCache.TryGetValue(language, out cache); + + return cache; + } + + Dictionary GetOrAddCache(string language) + { + var cache = _cache; + if (language != null) + { + if (!_langCache.TryGetValue(language, out cache)) + { + cache = new Dictionary(); + _langCache[language] = cache; + } + } + + return cache; + } + + public (object, AssetNode) Get(AssetId key, string language) { lock (_syncRoot) { - if (!_cache.TryGetValue(key, out var entry)) - return null; + var cache = GetCache(language); + if (cache == null || !_cache.TryGetValue(key, out var entry)) + return (null, null); entry.LastAccessed = DateTime.UtcNow; - return entry.Strong ?? entry.Weak.Target; + return (entry.Strong ?? entry.Weak.Target, entry.Node); } } - public void Add(object asset, AssetId key) + public void Add(AssetId id, string language, object asset, AssetNode node) { lock (_syncRoot) { - _cache[key] = new Entry + var cache = GetOrAddCache(language); + cache[id] = new Entry { LastAccessed = DateTime.UtcNow, Strong = asset, Weak = new WeakReference(asset), + Node = node }; } } diff --git a/src/Game/Assets/AssetConverter.cs b/src/Game/Assets/AssetConverter.cs index 30410e8e7..e525f3b6e 100644 --- a/src/Game/Assets/AssetConverter.cs +++ b/src/Game/Assets/AssetConverter.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text.RegularExpressions; using UAlbion.Api; using UAlbion.Api.Eventing; @@ -8,7 +7,6 @@ using UAlbion.Config; using UAlbion.Core; using UAlbion.Formats; -using UAlbion.Formats.Assets; using UAlbion.Formats.Ids; using UAlbion.Game.Magic; @@ -40,6 +38,7 @@ public sealed class AssetConverter : IDisposable .Attach(assetLoaderRegistry) .Attach(new ContainerRegistry()) .Attach(new PostProcessorRegistry()) + .Attach(new VarRegistry()) .Attach(new AssetLocator()) .Attach(new SpellManager()) .Attach(applier) @@ -68,40 +67,63 @@ public AssetConverter(string appName, AssetMapping mapping, IFileSystem disk, IJ _fromExchange.Attach(new AssetManager(_from)); // From also needs an asset manager for the inventory post-processor etc } - public void Convert(string[] ids, ISet assetTypes, Regex filePattern, Func converter = null) + public void Convert( + ISet ids, + ISet assetTypes, + Regex filePattern, + Func converter = null, + string[] validLanguages = null) { - var parsedIds = ids?.Select(AssetId.Parse).ToHashSet(); - var cache = new Dictionary<(AssetId, string), object>(); + var cache = new Dictionary<(AssetId, string), AssetLoadResult>(); - (object, AssetInfo) LoaderFunc(AssetId id, string language) + AssetLoadResult LoaderFunc(AssetId assetId, string language) { - StringId? stringId = TextId.ValidTypes.Contains(id.Type) - ? (TextId)id - : null; + AssetLoadResult result; - if (stringId != null) + if (cache.TryGetValue((assetId, language), out var cached)) { - if (stringId.Value.Id == id) - stringId = null; - else - id = stringId.Value.Id; + result = cached; + } + else + { + result = _from.LoadAssetAndNode(assetId, language); + cache[(assetId, language)] = result; } - var info = _from.GetAssetInfo(id, language); - var asset = cache.TryGetValue((id, language), out var cached) - ? cached - : cache[(id, language)] = _from.LoadAsset(id, language); + if (converter != null) + result = converter(result); - if (stringId.HasValue && asset is IStringSet collection) - asset = collection.GetString(stringId.Value, language); + return result; + } - if (converter != null) - asset = converter(id, asset); + var options = new AssetConversionOptions( + LoaderFunc, + () => cache.Clear(), + ids, + assetTypes, + validLanguages, + filePattern); - return (asset, info); + _to.SaveAssets(options); + } + + public string[] DiscoverLanguages(TextId? languageSearchId = null) + { + languageSearchId ??= Base.SystemText.MainMenu_MainMenu; + var fromAssets = _fromExchange.Resolve(); + + // Return null if no languages are found, this will cause all languages defined to be searched for + // on language-specific assets. In practice this scenario will happen in minimal round-trip tests + // which don't export system text. + List validLanguages = null; + foreach (var language in _from.Languages.Keys) + { + if (!fromAssets.IsStringDefined(languageSearchId.Value, language)) continue; + validLanguages ??= new List(); + validLanguages.Add(language); } - _to.SaveAssets(LoaderFunc, () => cache.Clear(), parsedIds, assetTypes, filePattern); + return validLanguages?.ToArray(); } public void Dispose() diff --git a/src/Game/Assets/AssetLoaderRegistry.cs b/src/Game/Assets/AssetLoaderRegistry.cs index 66033d6b8..5c3eac345 100644 --- a/src/Game/Assets/AssetLoaderRegistry.cs +++ b/src/Game/Assets/AssetLoaderRegistry.cs @@ -2,40 +2,35 @@ using System.Collections.Generic; using System.Linq; using UAlbion.Api.Eventing; -using UAlbion.Formats; +using UAlbion.Config; namespace UAlbion.Game.Assets; public sealed class AssetLoaderRegistry : ServiceComponent, IAssetLoaderRegistry, IDisposable { readonly object _syncRoot = new(); - readonly IDictionary _loaders = new Dictionary(); + readonly IDictionary _loaders = new Dictionary(); - public IAssetLoader GetLoader(string loaderName) + public IAssetLoader GetLoader(Type loaderType) { lock (_syncRoot) - return _loaders.TryGetValue(loaderName, out var loader) ? loader : Instantiate(loaderName); + return _loaders.TryGetValue(loaderType, out var loader) ? loader : Instantiate(loaderType); } - IAssetLoader Instantiate(string loaderName) + IAssetLoader Instantiate(Type loaderType) { - if(string.IsNullOrEmpty(loaderName)) - throw new ArgumentNullException(nameof(loaderName)); + if (loaderType == null) throw new ArgumentNullException(nameof(loaderType)); - var type = Type.GetType(loaderName); - if(type == null) - throw new InvalidOperationException($"Could not find loader type \"{loaderName}\""); - - var constructor = type.GetConstructor(Array.Empty()); + var constructor = loaderType.GetConstructor(Array.Empty()); if(constructor == null) - throw new InvalidOperationException($"Could not find parameterless constructor for loader type \"{type}\""); + throw new InvalidOperationException($"Could not find parameterless constructor for loader type \"{loaderType}\""); var loader = (IAssetLoader)constructor.Invoke(Array.Empty()); if (loader is IComponent component) AttachChild(component); - _loaders[loaderName] = loader; + _loaders[loaderType] = loader; return loader; } diff --git a/src/Game/Assets/AssetLocator.cs b/src/Game/Assets/AssetLocator.cs index c53515470..6d71a4e5b 100644 --- a/src/Game/Assets/AssetLocator.cs +++ b/src/Game/Assets/AssetLocator.cs @@ -22,51 +22,54 @@ protected override void Subscribed() base.Subscribed(); } - public object LoadAsset(AssetInfo info, SerdesContext context, TextWriter annotationWriter, List filesSearched) + public object LoadAsset(AssetLoadContext context, TextWriter annotationWriter, List filesSearched) { - if (info == null) throw new ArgumentNullException(nameof(info)); if (context == null) throw new ArgumentNullException(nameof(context)); var pathResolver = Resolve(); - using ISerializer s = Search(pathResolver, info, context, annotationWriter, filesSearched); + if (context.Node.Filename == null || context.Node.Filename.StartsWith('!')) // If this is a meta-asset (e.g. is loaded from another asset) + { + var metaLoader = _assetLoaderRegistry.GetLoader(context.Node.Loader); + if (metaLoader == null) + throw new InvalidOperationException($"Could not instantiate loader \"{context.Node.Loader}\" required by asset {context.AssetId}"); + + return metaLoader.Serdes(null, null, context); + } + + using ISerializer s = Search(pathResolver, context, annotationWriter, filesSearched); if (s == null) return null; if (s.BytesRemaining == 0 && s is not EmptySerializer) // Happens all the time when dumping, just return rather than throw to preserve perf. - return new AssetNotFoundException($"Asset for {info.AssetId} found but size was 0 bytes.", info.AssetId); + return new AssetNotFoundException($"Asset for {context.AssetId} found but size was 0 bytes.", context.AssetId); - var loader = _assetLoaderRegistry.GetLoader(info.File.Loader); + var loader = _assetLoaderRegistry.GetLoader(context.Node.Loader); if (loader == null) - throw new InvalidOperationException($"Could not instantiate loader \"{info.File.Loader}\" required by asset {info.AssetId}"); + throw new InvalidOperationException($"Could not instantiate loader \"{context.Node.Loader}\" required by asset {context.AssetId}"); - return loader.Serdes(null, info, s, context); + return loader.Serdes(null, s, context); } - public List<(int,int)> GetSubItemRangesForFile(AssetFileInfo info, SerdesContext context) + ISerializer Search(IPathResolver pathResolver, AssetLoadContext context, TextWriter annotationWriter, List filesSearched) { - if (info == null) throw new ArgumentNullException(nameof(info)); - if (context == null) throw new ArgumentNullException(nameof(context)); + var node = context.Node; + var disk = context.ModContext.Disk; + var path = pathResolver.ResolvePath(node.Filename); - var pathResolver = Resolve(); - var resolved = pathResolver.ResolvePath(info.Filename); - var container = _containerRegistry.GetContainer(resolved, info.Container, context.Disk); - return container?.GetSubItemRanges(resolved, info, context) ?? new List<(int, int)> { (0, 1) }; - } - - ISerializer Search(IPathResolver pathResolver, AssetInfo info, SerdesContext context, TextWriter annotationWriter, List filesSearched) - { - var path = pathResolver.ResolvePath(info.File.Filename); - if (info.File.Sha256Hash != null) + if (disk.FileExists(path) || disk.DirectoryExists(path)) { - var hash = GetHash(path, context.Disk); - filesSearched?.Add($"{context.Disk.ToAbsolutePath(path)} (actual hash {hash})"); - if (!info.File.Sha256Hash.Equals(hash, StringComparison.OrdinalIgnoreCase)) - return null; + if (node.Sha256Hash != null) + { + var hash = GetHash(path, disk); + filesSearched?.Add($"{disk.ToAbsolutePath(path)} (actual hash {hash})"); + if (!node.Sha256Hash.Equals(hash, StringComparison.OrdinalIgnoreCase)) + return null; + } + else filesSearched?.Add(disk.ToAbsolutePath(path)); } - else filesSearched?.Add(context.Disk.ToAbsolutePath(path)); - var container = _containerRegistry.GetContainer(path, info.File.Container, context.Disk); - var s = container?.Read(path, info, context); + var container = _containerRegistry.GetContainer(path, node.Container, disk); + var s = container?.Read(path, context); if (annotationWriter != null) s = new AnnotationProxySerializer(s, annotationWriter, FormatUtil.BytesFrom850String); return s; diff --git a/src/Game/Assets/AssetManager.cs b/src/Game/Assets/AssetManager.cs index 15c97f948..70f132593 100644 --- a/src/Game/Assets/AssetManager.cs +++ b/src/Game/Assets/AssetManager.cs @@ -32,7 +32,7 @@ protected override void Subscribed() } protected override void Unsubscribed() => Exchange.Unregister(this); - public AssetInfo GetAssetInfo(AssetId id, string language = null) => _modApplier.GetAssetInfo(id, language); + public AssetNode GetAssetInfo(AssetId id, string language = null) => _modApplier.GetAssetInfo(id, language); public IMapData LoadMap(MapId id) => (IMapData)_modApplier.LoadAsset(id); // No caching for map data public ItemData LoadItem(ItemId id) => (ItemData)_modApplier.LoadAssetCached(id); @@ -66,6 +66,20 @@ public AlbionPalette LoadPalette(PaletteId id) public TilesetData LoadTileData(TilesetId id) => (TilesetData)_modApplier.LoadAssetCached(id); public LabyrinthData LoadLabyrinthData(LabyrinthId id) => (LabyrinthData)_modApplier.LoadAssetCached(id); + public IStringSet LoadStringSet(StringSetId id) => LoadStringSet(id, null); + public IStringSet LoadStringSet(StringSetId id, string language) + { + var currentLanguage = Var(UserVars.Gameplay.Language); + bool cached = !(language != null && language != currentLanguage); + language ??= currentLanguage; + + var asset = cached + ? _modApplier.LoadAssetCached(id) + : _modApplier.LoadAsset(id, language); + + return (IStringSet)asset; + } + string LoadStringCore(StringId id, string language, bool cached) { var currentLanguage = Var(UserVars.Gameplay.Language); @@ -79,18 +93,17 @@ string LoadStringCore(StringId id, string language, bool cached) return asset switch { - IStringSet collection => collection.GetString(id, language), + IStringSet collection => collection.GetString(id), string s => s, _ => null }; } - public bool IsStringDefined(TextId id, string language) => LoadStringCore(id, language, false) != null; + public bool IsStringDefined(TextId id, string language) => LoadStringCore(new StringId(id), language, false) != null; public bool IsStringDefined(StringId id, string language) => LoadStringCore(id, language, false) != null; - public string LoadString(TextId id) => LoadString((StringId)id, null); - public string LoadString(StringId id) => LoadString(id, null); - public string LoadString(TextId id, string language) => LoadString((StringId)id, language); - public string LoadString(StringId id, string language) - => LoadStringCore(id, language, true) ?? $"!MISSING STRING {id.Id}:{id.SubId}!"; + public string LoadStringRaw(TextId id, string language) => LoadStringCore(new StringId(id), language, true); + public string LoadStringRaw(StringId id, string language) => LoadStringCore(id, language, true); + public string LoadStringSafe(TextId id, string language) => LoadStringCore(new StringId(id), language, true) ?? $"!MISSING STRING {id.Id}!"; + public string LoadStringSafe(StringId id, string language) => LoadStringCore(id, language, true) ?? $"!MISSING STRING {id.Id}:{id.SubId}!"; public ISample LoadSample(SampleId id) => (AlbionSample)_modApplier.LoadAssetCached(id); public WaveLib LoadWaveLib(WaveLibraryId waveLibraryId) => (WaveLib)_modApplier.LoadAssetCached(waveLibraryId); diff --git a/src/Game/Assets/ContainerRegistry.cs b/src/Game/Assets/ContainerRegistry.cs index 53c83aebb..c9b0974ae 100644 --- a/src/Game/Assets/ContainerRegistry.cs +++ b/src/Game/Assets/ContainerRegistry.cs @@ -3,6 +3,7 @@ using System.IO; using UAlbion.Api; using UAlbion.Api.Eventing; +using UAlbion.Config; using UAlbion.Formats.Containers; namespace UAlbion.Game.Assets; @@ -12,10 +13,10 @@ public class ContainerRegistry : ServiceComponent, IContaine readonly object _syncRoot = new(); readonly IDictionary _containers = new Dictionary(); - public IAssetContainer GetContainer(string path, string container, IFileSystem disk) + public IAssetContainer GetContainer(string path, Type container, IFileSystem disk) { if (disk == null) throw new ArgumentNullException(nameof(disk)); - if (!string.IsNullOrEmpty(container)) + if (container != null) return GetContainer(container); switch (Path.GetExtension(path).ToUpperInvariant()) @@ -29,26 +30,15 @@ public IAssetContainer GetContainer(string path, string container, IFileSystem d } } - IAssetContainer GetContainer(string containerName) + IAssetContainer GetContainer(Type type) { - if (string.IsNullOrEmpty(containerName)) - throw new ArgumentNullException(nameof(containerName)); - - var type = Type.GetType(containerName); if (type == null) - throw new InvalidOperationException($"Could not find container type \"{containerName}\""); + throw new InvalidOperationException($"Could not find container type \"{type}\""); lock (_syncRoot) return _containers.TryGetValue(type, out var container) ? container : Instantiate(type); } - IAssetContainer GetContainer(Type containerType) - { - if (containerType == null) throw new ArgumentNullException(nameof(containerType)); - lock (_syncRoot) - return _containers.TryGetValue(containerType, out var container) ? container : Instantiate(containerType); - } - IAssetContainer Instantiate(Type type) { var constructor = type.GetConstructor(Array.Empty()); diff --git a/src/Game/Assets/IAssetLoaderRegistry.cs b/src/Game/Assets/IAssetLoaderRegistry.cs index 949fdfb88..bbb6d7891 100644 --- a/src/Game/Assets/IAssetLoaderRegistry.cs +++ b/src/Game/Assets/IAssetLoaderRegistry.cs @@ -1,8 +1,9 @@ -using UAlbion.Formats; +using System; +using UAlbion.Config; namespace UAlbion.Game.Assets; public interface IAssetLoaderRegistry { - IAssetLoader GetLoader(string loaderName); + IAssetLoader GetLoader(Type loaderType); } \ No newline at end of file diff --git a/src/Game/Assets/IAssetLocator.cs b/src/Game/Assets/IAssetLocator.cs index 1e2680fc5..f74cb485f 100644 --- a/src/Game/Assets/IAssetLocator.cs +++ b/src/Game/Assets/IAssetLocator.cs @@ -2,12 +2,10 @@ using System.IO; using UAlbion.Api.Eventing; using UAlbion.Config; -using UAlbion.Formats; namespace UAlbion.Game.Assets; public interface IAssetLocator : IComponent { - object LoadAsset(AssetInfo info, SerdesContext context, TextWriter annotationWriter, List filesSearched); - List<(int,int)> GetSubItemRangesForFile(AssetFileInfo info, SerdesContext context); + object LoadAsset(AssetLoadContext context, TextWriter annotationWriter, List filesSearched); } \ No newline at end of file diff --git a/src/Game/Assets/IContainerRegistry.cs b/src/Game/Assets/IContainerRegistry.cs index 61371017f..cb80de63b 100644 --- a/src/Game/Assets/IContainerRegistry.cs +++ b/src/Game/Assets/IContainerRegistry.cs @@ -1,9 +1,10 @@ -using UAlbion.Api; -using UAlbion.Formats.Containers; +using System; +using UAlbion.Api; +using UAlbion.Config; namespace UAlbion.Game.Assets; public interface IContainerRegistry { - IAssetContainer GetContainer(string path, string container, IFileSystem disk); + IAssetContainer GetContainer(string path, Type container, IFileSystem disk); } \ No newline at end of file diff --git a/src/Game/Assets/ModApplier.cs b/src/Game/Assets/ModApplier.cs index ecb15f00b..e7f55e82b 100644 --- a/src/Game/Assets/ModApplier.cs +++ b/src/Game/Assets/ModApplier.cs @@ -1,37 +1,34 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.IO; using System.Linq; -using System.Text; -using System.Text.RegularExpressions; using UAlbion.Api; using UAlbion.Api.Eventing; -using UAlbion.Api.Settings; using UAlbion.Config; +using UAlbion.Config.Properties; using UAlbion.Core; using UAlbion.Formats; using UAlbion.Formats.Assets; using UAlbion.Formats.Assets.Save; -using UAlbion.Formats.Containers; using UAlbion.Game.Events; using UAlbion.Game.Settings; + +#if DEBUG +using System.Text; using static System.FormattableString; +#endif namespace UAlbion.Game.Assets; public class ModApplier : Component, IModApplier { readonly AssetCache _assetCache = new(); - readonly Dictionary _mods = new(); - readonly List _modsInReverseDependencyOrder = new(); - readonly Dictionary _languages = new(); - + readonly ModLoader _mods = new(); IAssetLocator _assetLocator; public ModApplier() { - Languages = new ReadOnlyDictionary(_languages); + AttachChild(_mods); AttachChild(_assetCache); On(_ => { @@ -42,7 +39,7 @@ public ModApplier() }); } - public IReadOnlyDictionary Languages { get; } + public IReadOnlyDictionary Languages => _mods.Languages; protected override void Subscribed() { @@ -50,193 +47,72 @@ protected override void Subscribed() Exchange.Register(this); } - public void LoadMods(AssetMapping mapping, IPathResolver pathResolver, IReadOnlyList mods) - { - if (mapping == null) throw new ArgumentNullException(nameof(mapping)); - if (pathResolver == null) throw new ArgumentNullException(nameof(pathResolver)); - if (mods == null) throw new ArgumentNullException(nameof(mods)); - - pathResolver.RegisterPath("ALBION", pathResolver.ResolvePathAbsolute(Var(UserVars.Path.Albion))); - pathResolver.RegisterPath("SAVES", pathResolver.ResolvePathAbsolute(Var(UserVars.Path.Saves))); - - _mods.Clear(); - _modsInReverseDependencyOrder.Clear(); - TryResolve()?.Clear(); - mapping.Clear(); - - foreach (var mod in mods.Reverse()) - LoadMod(pathResolver.ResolvePathAbsolute("$(MODS)"), mod.Trim(), mapping); - - _modsInReverseDependencyOrder.Reverse(); - - Raise(ModsLoadedEvent.Instance); - } - public IEnumerable ShaderPaths => - _modsInReverseDependencyOrder + _mods.ModsInReverseDependencyOrder .Where(x => !string.IsNullOrEmpty(x.ModConfig.ShaderPath)) - .Select(mod => mod.SerdesContext.Disk.ToAbsolutePath(mod.ShaderPath)); - - void LoadMod(string dataDir, string modName, AssetMapping mapping) - { - if (string.IsNullOrEmpty(modName)) - return; + .Select(mod => mod.ModContext.Disk.ToAbsolutePath(mod.ShaderPath)); - if (_mods.ContainsKey(modName)) - return; - - if (modName.Any(c => c is '\\' or '/' || c == Path.DirectorySeparatorChar)) - { - Error($"Mod {modName} is not a simple directory name"); - return; - } - - var disk = Resolve(); - var pathResolver = Resolve(); - var jsonUtil = Resolve(); - - string path = Path.Combine(dataDir, modName); - if (!disk.DirectoryExists(path)) - { - Error($"Mod directory {modName} does not exist in {dataDir}"); - return; - } - - var modDisk = disk.Duplicate(path); - var modConfigPath = Path.Combine(path, ModConfig.ModConfigFilename); - if (!modDisk.FileExists(modConfigPath)) - { - Error($"Mod {modName} does not contain a {ModConfig.ModConfigFilename} file"); - return; - } - - var modConfig = ModConfig.Load(modConfigPath, modDisk, jsonUtil); - if (modConfig.SymLinks != null) - { - foreach (var kvp in modConfig.SymLinks) - modDisk = new RedirectionFileSystemDecorator(modDisk, kvp.Key, pathResolver.ResolvePath(kvp.Value)); - } - - var assetConfigPath = Path.Combine(path, modConfig.AssetConfig); - if (!modDisk.FileExists(assetConfigPath)) - { - Error($"Mod {modName} does not contain an {modConfig.AssetConfig} file"); - return; - } - var modMapping = new AssetMapping(); + public void LoadMods(AssetMapping mapping, IPathResolver pathResolver, IReadOnlyList mods) + => _mods.LoadMods(mapping, pathResolver, mods); - // Load dependencies - foreach (var dependency in modConfig.Dependencies) + public AssetNode GetAssetInfo(AssetId key, string language = null) + { + foreach (var mod in _mods.ModsInReverseDependencyOrder) { - LoadMod(dataDir, dependency, mapping); - if (!_mods.TryGetValue(dependency, out var dependencyInfo)) + var assetNodes = mod.AssetConfig.GetAssetInfo(key); + foreach(var node in assetNodes) { - Error($"Dependency {dependency} of mod {modName} could not be loaded, skipping load of {modName}"); - return; - } - - modMapping.MergeFrom(dependencyInfo.SerdesContext.Mapping); - } - - var parentConfig = modConfig.InheritAssetConfigFrom != null && _mods.TryGetValue(modConfig.InheritAssetConfigFrom, out var parent) ? parent.AssetConfig : null; - var assetConfig = AssetConfig.Load(assetConfigPath, parentConfig, modMapping, modDisk, jsonUtil); - assetConfig.Validate(assetConfigPath); - var modInfo = new ModInfo(modName, assetConfig, modConfig, modMapping, jsonUtil, modDisk); - - foreach (var kvp in assetConfig.Languages) - _languages[kvp.Key] = kvp.Value; - - MergeTypesToMapping(modMapping, assetConfig, assetConfigPath); - mapping.MergeFrom(modMapping); - - assetConfig.PopulateAssetIds( - jsonUtil, - x => _assetLocator.GetSubItemRangesForFile(x, modInfo.SerdesContext), - x => modInfo.SerdesContext.Disk.ReadAllBytes(pathResolver.ResolvePath(x))); - - if (assetConfig.VarTypes != null) - LoadVarTypes(modName, assetConfig.VarTypes); - - _mods.Add(modName, modInfo); - _modsInReverseDependencyOrder.Add(modInfo); - } + if (language == null) + return node; - static void MergeTypesToMapping(AssetMapping mapping, AssetConfig config, string assetConfigPath) - { - foreach (var assetType in config.IdTypes.Values) - { - var enumType = Type.GetType(assetType.EnumType); - if (enumType == null) - throw new InvalidOperationException( - $"Could not load enum type \"{assetType.EnumType}\" defined in \"{assetConfigPath}\""); + var assetLanguage = node.GetProperty(AssetProps.Language); + if (assetLanguage == null) + return node; - mapping.RegisterAssetType(assetType.EnumType, assetType.AssetType); + if (string.Equals(assetLanguage, language, StringComparison.OrdinalIgnoreCase)) + return node; + } } - config.RegisterStringRedirects(mapping); + return null; } - void LoadVarTypes(string modName, IEnumerable varTypes) + public object LoadAsset(AssetId id, string language = null) { - var registry = TryResolve(); - if (registry == null) - { - Warn("No VarRegistry found, skipping var registration"); - return; - } - - foreach (var typeName in varTypes) + try { - var type = Type.GetType(typeName); - if (type == null) - throw new InvalidOperationException($"Could not load type \"{typeName}\" as Var container from mod {modName}"); - - registry.Register(type); + var result = LoadAssetInternal(id, language); + return result.Asset is Exception ? null : result.Asset; } - } - - public AssetInfo GetAssetInfo(AssetId key, string language = null) - { - foreach (var mod in _modsInReverseDependencyOrder) + catch (Exception e) { - var assetInfos = mod.AssetConfig.GetAssetInfo(key); - foreach(var info in assetInfos) - { - if (language == null) - return info; - - var assetLanguage = info.Get(AssetProperty.Language, null); - if (assetLanguage == null) - return info; + if (CoreUtil.IsCriticalException(e)) + throw; - if (string.Equals(assetLanguage, language, StringComparison.OrdinalIgnoreCase)) - return info; - } + Error($"Could not load asset {id}: {e}"); + return null; } - - return null; } - public object LoadAsset(AssetId id) => LoadAsset(id, null); - public object LoadAsset(AssetId id, string language) + public AssetLoadResult LoadAssetAndNode(AssetId assetId, string language = null) { try { - var asset = LoadAssetInternal(id, language); - return asset is Exception ? null : asset; + var result = LoadAssetInternal(assetId, language); + return result.Asset is Exception ? null : result; } catch (Exception e) { if (CoreUtil.IsCriticalException(e)) throw; - Error($"Could not load asset {id}: {e}"); + Error($"Could not load asset {assetId}: {e}"); return null; } } - public string LoadAssetAnnotated(AssetId id, string language) + public string LoadAssetAnnotated(AssetId id, string language = null) { using var ms = new MemoryStream(); using var annotationWriter = new StreamWriter(ms); @@ -248,76 +124,82 @@ public string LoadAssetAnnotated(AssetId id, string language) return reader.ReadToEnd(); } - public object LoadAssetCached(AssetId assetId) + public object LoadAssetCached(AssetId id, string language = null) { - object asset = _assetCache.Get(assetId); - if (asset is Exception) // If it failed to load once then stop trying (at least until an asset:reload / cycle) - return null; - - if (asset != null) - return asset; + var result = GetCached(id, language); + if (result != null) + return result.Asset; try { - asset = LoadAssetInternal(assetId); - _assetCache.Add(asset ?? new AssetNotFoundException($"Could not load asset for {assetId}"), assetId); - return asset is Exception ? null : asset; + result = LoadAssetInternal(id, language); + _assetCache.Add(id, language, result?.Asset ?? new AssetNotFoundException($"Could not load asset for {id}"), result?.Node); + return result?.Asset is Exception ? null : result?.Asset; } catch (Exception e) { if (CoreUtil.IsCriticalException(e)) throw; - Error($"Could not load asset {assetId}: {e}"); - _assetCache.Add(e, assetId); + Error($"Could not load asset {id}: {e}"); + _assetCache.Add(id, language, e, null); return null; } } - object LoadAssetInternal(AssetId id, string language = null, TextWriter annotationWriter = null) + AssetLoadResult GetCached(AssetId assetId, string language) + { + var (asset, node) = _assetCache.Get(assetId, language); + return asset is null or Exception + ? null // If it failed to load once then stop trying (at least until an asset:reload / cycle) + : new AssetLoadResult(assetId, asset, node); + } + + AssetLoadResult LoadAssetInternal(AssetId id, string language, TextWriter annotationWriter = null) { if (id.IsNone) return null; if (id.Type == AssetType.MetaFont) - { - var assets = Resolve(); - var metaId = (MetaFontId)id; - var font = assets.LoadFontDefinition(metaId.FontId); - return font.Build(metaId.FontId, metaId.InkId, assets); - } + return LoadMetaFont(id); object asset = null; + AssetNode loadedNode = null; Stack patches = null; // Create the stack lazily, as most assets won't have any patches. + language ??= Var(UserVars.Gameplay.Language); - List filesSearched = #if DEBUG - new List(); - + var filesSearched = new List(); bool isOptional = false; var loaderWarnings = new StringBuilder(); -#else - null; -#endif - foreach (var mod in _modsInReverseDependencyOrder) + foreach (var mod in _mods.ModsInReverseDependencyOrder) { -#if DEBUG filesSearched.Clear(); + bool anyFiles = false; + var assetLocations = mod.AssetConfig.GetAssetInfo(id).ToArray(); +#else + foreach (var mod in _mods.ModsInReverseDependencyOrder) + { + var assetLocations = mod.AssetConfig.GetAssetInfo(id); #endif - var assetLocations = mod.AssetConfig.GetAssetInfo(id); - foreach (var info in assetLocations) + foreach (AssetNode node in assetLocations) { - var assetLang = info.Get(AssetProperty.Language, null); - if (assetLang != null) - { - language ??= Var(UserVars.Gameplay.Language); - if (!string.Equals(assetLang, language, StringComparison.OrdinalIgnoreCase)) - continue; - } +#if DEBUG + anyFiles = true; +#endif + var assetLang = node.GetProperty(AssetProps.Language); + if (assetLang != null && !string.Equals(assetLang, language, StringComparison.OrdinalIgnoreCase)) + continue; - var modAsset = _assetLocator.LoadAsset(info, mod.SerdesContext, annotationWriter, filesSearched); + var context = new AssetLoadContext(id, node, mod.ModContext, language); + +#if DEBUG + var modAsset = _assetLocator.LoadAsset(context, annotationWriter, filesSearched); +#else + var modAsset = _assetLocator.LoadAsset(context, annotationWriter, null); +#endif if (modAsset is IPatch patch) { @@ -330,32 +212,34 @@ object LoadAssetInternal(AssetId id, string language = null, TextWriter annotati } else if (modAsset != null) { - if (!string.IsNullOrEmpty(info.File.Post)) + var postProcessorType = node.PostProcessor; + if (postProcessorType != null) { var registry = Resolve(); - var postProcessor = registry.GetPostProcessor(info.File.Post); + var postProcessor = registry.GetPostProcessor(postProcessorType); if (postProcessor != null) - modAsset = postProcessor.Process(modAsset, info); + modAsset = postProcessor.Process(modAsset, context); } asset = modAsset; + loadedNode = node; goto assetFound; } #if DEBUG - isOptional |= info.Get(AssetProperty.Optional, false); + isOptional |= node.GetProperty(AssetProps.Optional); #endif } #if DEBUG - if (!isOptional && asset == null && assetLocations.Length > 0 && filesSearched is { Count: > 0 }) + if (!isOptional && asset == null && anyFiles && filesSearched is { Count: > 0 } && AssetMapping.Global.IsMapped(id)) { loaderWarnings.AppendLine(Invariant($"Tried to load asset {id} from mod {mod.Name}")); loaderWarnings.AppendLine(" Files searched:"); - foreach (var info in assetLocations) + foreach (var node in assetLocations) { - var hash = string.IsNullOrEmpty(info.File.Sha256Hash) ? "" : $" (expected hash {info.File.Sha256Hash})"; - loaderWarnings.AppendLine(Invariant($" {info.File.Filename}{hash}")); + var hash = string.IsNullOrEmpty(node.Sha256Hash) ? "" : $" (expected hash {node.Sha256Hash})"; + loaderWarnings.AppendLine(Invariant($" {node.Filename}{hash}")); } loaderWarnings.AppendLine(" Files found:"); @@ -377,7 +261,16 @@ object LoadAssetInternal(AssetId id, string language = null, TextWriter annotati Warn(loaderWarnings.ToString()); #endif - return asset; + return new AssetLoadResult(id, asset, loadedNode); + } + + AssetLoadResult LoadMetaFont(AssetId id) + { + var assets = Resolve(); + var metaId = (MetaFontId)id; + var font = assets.LoadFontDefinition(metaId.FontId); + var metaFont = font.Build(metaId.FontId, metaId.InkId, assets); + return new AssetLoadResult(id, metaFont, null); } public SavedGame LoadSavedGame(string path) @@ -396,119 +289,125 @@ public SavedGame LoadSavedGame(string path) return SavedGame.Serdes(null, AssetMapping.Global, s, spellManager); } - public void SaveAssets( - IModApplier.AssetLoader loaderFunc, - Action flushCacheFunc, - ISet ids, - ISet assetTypes, - Regex filePattern) + public void SaveAssets(AssetConversionOptions options) { - if (loaderFunc == null) throw new ArgumentNullException(nameof(loaderFunc)); - if (flushCacheFunc == null) throw new ArgumentNullException(nameof(flushCacheFunc)); + if (options == null) throw new ArgumentNullException(nameof(options)); + if (options.LoaderFunc == null) throw new ArgumentException(nameof(options.LoaderFunc)); + if (options.FlushCacheFunc == null) throw new ArgumentException(nameof(options.FlushCacheFunc)); var pathResolver = Resolve(); - var loaderRegistry = Resolve(); var containerRegistry = Resolve(); var writeDisk = Resolve(); - var jsonUtil = Resolve(); - var target = _modsInReverseDependencyOrder.First(); + var target = _mods.ModsInReverseDependencyOrder[0]; + var filesWritten = new HashSet(); - // Add any missing ids - Info("Populating destination asset info..."); - target.AssetConfig.PopulateAssetIds( - jsonUtil, - BuildSubItemCountMethod(assetTypes, filePattern), - x => writeDisk.ReadAllBytes(pathResolver.ResolvePath(x))); - - foreach (var file in target.AssetConfig.Files.Values) + foreach (var rangeInfo in target.AssetConfig.Ranges.AllRanges) { - if (filePattern != null && !filePattern.IsMatch(file.Filename)) - continue; - - if (file.Get(AssetProperty.IsReadOnly, false)) + if (options.AssetTypes != null && !options.AssetTypes.Contains(rangeInfo.Range.From.Type)) continue; - bool notify = true; - flushCacheFunc(); - var path = pathResolver.ResolvePath(file.Filename); - var loader = loaderRegistry.GetLoader(file.Loader); - var writeContainer = containerRegistry.GetContainer(path, file.Container, writeDisk); - var assets = new List<(AssetInfo, byte[])>(); - foreach (var assetInfo in file.Map.Values) + var assets = new Dictionary>(); + foreach (var assetId in rangeInfo.Range) { - if (ids != null && !ids.Contains(assetInfo.AssetId)) continue; - if (assetTypes != null && !assetTypes.Contains(assetInfo.AssetId.Type)) continue; + if (!AssetMapping.Global.IsMapped(assetId)) continue; + if (options.Ids != null && !options.Ids.Contains(assetId)) continue; + options.FlushCacheFunc(); - var language = assetInfo.Get(AssetProperty.Language, null); - var (asset, sourceInfo) = loaderFunc(assetInfo.AssetId, language); - if (asset == null) + var nodes = target.AssetConfig.GetAssetInfo(assetId); + foreach (var node in nodes) { - // Automaps should only load for 3D maps, no need for 'not found' errors, also unmapped ids might be getting requested - // due to populating the full range of an XLD, as the ids aren't actually in use it's fine to ignore their absence. - var id = assetInfo.AssetId; - if (id.Type != AssetType.Automap && AssetMapping.Global.IsMapped(id)) - Error($"Could not load {assetInfo.AssetId}"); - continue; + var saveContext = new AssetLoadContext(assetId, node, target.ModContext); + ConvertAsset(options, saveContext, filesWritten, assets); } + } - if (notify) - { - Info($"Saving {file.Filename}..."); - notify = false; - } + foreach (var kvp in assets.OrderBy(x => x.Key)) + { + var first = kvp.Value[0].Context; + var path = pathResolver.ResolvePath(first.Filename); + var writeContainer = containerRegistry.GetContainer(path, first.Node.Container, writeDisk); + writeContainer.Write(path, kvp.Value, target.ModContext); + } + } + + Info("Finished saving assets"); + } + + void ConvertAsset( + AssetConversionOptions options, + AssetLoadContext saveContext, + HashSet filesWritten, + Dictionary> assets) + { + var filename = saveContext.Filename; + if (options.FilePattern != null && !options.FilePattern.IsMatch(filename)) return; + if (saveContext.GetProperty(AssetProps.IsReadOnly)) return; - var paletteId = sourceInfo.Get(AssetProperty.PaletteId, 0); - if (paletteId != 0) - assetInfo.Set(AssetProperty.PaletteId, paletteId); + var language = saveContext.GetProperty(AssetProps.Language); + if (language != null && options.Languages != null && !options.Languages.Contains(language)) + return; - using var ms = new MemoryStream(); - using var bw = new BinaryWriter(ms); - using var s = new AlbionWriter(bw); - loader.Serdes(asset, assetInfo, s, target.SerdesContext); + AssetLoadResult result; + if (saveContext.GetProperty(AssetProps.UseDummyRead)) + { + result = new AssetLoadResult(saveContext.AssetId, new object(), saveContext.Node); + } + else + { + result = options.LoaderFunc(saveContext.AssetId, language); + if (result?.Asset == null) + { + if (AssetMapping.Global.IsMapped(saveContext.AssetId) && !saveContext.GetProperty(AssetProps.Optional)) + Error($"Could not load {saveContext.AssetId} {language}"); - ms.Position = 0; - assets.Add((assetInfo, ms.ToArray())); + return; } + } - if (assets.Count > 0) - writeContainer.Write(path, assets, target.SerdesContext); + // Hacky copying of palette property to make png export/import work + var sourcePalette = result.Node?.PaletteId ?? AssetId.None; + if (!sourcePalette.IsNone) + { + saveContext = saveContext with { Node = new AssetNode(saveContext.Node) }; // Clone the node to prevent affecting any other assets that share the node + saveContext.SetProperty(AssetProps.Palette, sourcePalette); } - Info("Finished saving assets"); + if (filesWritten.Add(filename)) + Info($"Saving {filename}..."); + + SaveAsset(saveContext, result.Asset, assets); } - AssetConfig.GetSubItemCountMethod BuildSubItemCountMethod(ISet assetTypes, Regex filePattern) + void SaveAsset(AssetLoadContext targetInfo, object asset, Dictionary> assets) { - var containerRegistry = Resolve(); - var writeDisk = Resolve(); - var target = _modsInReverseDependencyOrder.First(); + var loaderRegistry = Resolve(); + var loader = loaderRegistry.GetLoader(targetInfo.Node.Loader); + + using var ms = new MemoryStream(); + using var bw = new BinaryWriter(ms); + using var s = new AlbionWriter(bw); - return file => + try { - // Don't need to resolve the filename as we're not actually using the container - we just want to find the type. - var container = containerRegistry.GetContainer(file.Filename, file.Container, writeDisk); - var firstAsset = file.Map[file.Map.Keys.Min()]; - if (assetTypes != null && !assetTypes.Contains(firstAsset.AssetId.Type)) - return new List<(int, int)> { (firstAsset.Index, 1) }; - - if (filePattern != null && !filePattern.IsMatch(file.Filename)) - return new List<(int, int)> { (firstAsset.Index, 1) }; - - var assets = target.SerdesContext.Mapping.EnumerateAssetsOfType(firstAsset.AssetId.Type).ToList(); - var idsInRange = - assets - .Where(x => x.Id >= firstAsset.AssetId.Id) - .OrderBy(x => x.Id) - .Select(x => x.Id - firstAsset.AssetId.Id + firstAsset.Index); - - if (container is XldContainer) - idsInRange = idsInRange.Where(x => x < 100); - - int? maxSubId = file.Max; - if (maxSubId.HasValue) - idsInRange = idsInRange.Where(x => x <= maxSubId.Value); - - return FormatUtil.SortedIntsToRanges(idsInRange); - }; + loader.Serdes(asset, s, targetInfo); + + ms.Position = 0; + + assets ??= new Dictionary>(); + if (!assets.TryGetValue(targetInfo.Filename, out var list)) + { + list = new List<(AssetLoadContext, byte[])>(); + assets[targetInfo.Filename] = list; + } + + list.Add((targetInfo, ms.ToArray())); + } + catch (Exception e) + { + if (CoreUtil.IsCriticalException(e)) + throw; + + Error($"Could not save asset {targetInfo.AssetId}: {e}"); + } } } \ No newline at end of file diff --git a/src/Game/Assets/ModInfo.cs b/src/Game/Assets/ModInfo.cs index 8dceb60a7..66d89832c 100644 --- a/src/Game/Assets/ModInfo.cs +++ b/src/Game/Assets/ModInfo.cs @@ -1,7 +1,6 @@ using System; using UAlbion.Api; using UAlbion.Config; -using UAlbion.Formats; namespace UAlbion.Game.Assets; @@ -9,6 +8,7 @@ public class ModInfo { public ModInfo( string name, + TypeConfig typeConfig, AssetConfig assetConfig, ModConfig modConfig, AssetMapping mapping, @@ -20,6 +20,7 @@ public ModInfo( Name = name ?? throw new ArgumentNullException(nameof(name)); AssetConfig = assetConfig ?? throw new ArgumentNullException(nameof(assetConfig)); + TypeConfig = typeConfig ?? throw new ArgumentNullException(nameof(typeConfig)); ModConfig = modConfig ?? throw new ArgumentNullException(nameof(modConfig)); if (!string.IsNullOrEmpty(ModConfig.AssetConfig)) @@ -32,13 +33,14 @@ public ModInfo( } } - SerdesContext = new SerdesContext(name, json, mapping, disk); + ModContext = new ModContext(name, json, disk, mapping); } public string Name { get; } public string ShaderPath => ModConfig.ShaderPath; public AssetConfig AssetConfig { get; } + public TypeConfig TypeConfig { get; } public ModConfig ModConfig { get; } - public SerdesContext SerdesContext { get; } + public ModContext ModContext { get; } public override string ToString() => $"Mod:{Name}"; } \ No newline at end of file diff --git a/src/Game/Assets/ModLoader.cs b/src/Game/Assets/ModLoader.cs new file mode 100644 index 000000000..4fdf4e5ab --- /dev/null +++ b/src/Game/Assets/ModLoader.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using UAlbion.Api; +using UAlbion.Api.Eventing; +using UAlbion.Api.Settings; +using UAlbion.Config; +using UAlbion.Game.Events; +using UAlbion.Game.Settings; + +namespace UAlbion.Game.Assets; + +class ModLoader : Component // Shouldn't be referenced outside ModApplier +{ + readonly List _modsInReverseDependencyOrder = new(); + readonly Dictionary _mods = new(); + readonly Dictionary _languages = new(); + + public IReadOnlyList ModsInReverseDependencyOrder => _modsInReverseDependencyOrder; + public IReadOnlyDictionary Languages { get; } + + public ModLoader() + { + Languages = new ReadOnlyDictionary(_languages); + } + + public void LoadMods(AssetMapping mapping, IPathResolver pathResolver, IReadOnlyList mods) + { + if (mapping == null) throw new ArgumentNullException(nameof(mapping)); + if (pathResolver == null) throw new ArgumentNullException(nameof(pathResolver)); + if (mods == null) throw new ArgumentNullException(nameof(mods)); + + pathResolver.RegisterPath("ALBION", pathResolver.ResolvePathAbsolute(Var(UserVars.Path.Albion))); + pathResolver.RegisterPath("SAVES", pathResolver.ResolvePathAbsolute(Var(UserVars.Path.Saves))); + + _mods.Clear(); + _modsInReverseDependencyOrder.Clear(); + TryResolve()?.Clear(); + mapping.Clear(); + + foreach (var mod in mods.Reverse()) + LoadMod(pathResolver.ResolvePathAbsolute("$(MODS)"), mod.Trim(), mapping); + + _modsInReverseDependencyOrder.Reverse(); + + Raise(ModsLoadedEvent.Instance); + } + + void LoadMod(string dataDir, string modName, AssetMapping mapping) + { + if (string.IsNullOrEmpty(modName)) + return; + + if (_mods.ContainsKey(modName)) + return; + + if (modName.Any(c => c is '\\' or '/' || c == Path.DirectorySeparatorChar)) + { + Error($"Mod {modName} is not a simple directory name"); + return; + } + + var disk = Resolve(); + var pathResolver = Resolve(); + var jsonUtil = Resolve(); + + string path = Path.Combine(dataDir, modName); + if (!disk.DirectoryExists(path)) + { + Error($"Mod directory {modName} does not exist in {dataDir}"); + return; + } + + var modDisk = disk.Duplicate(path); + var modConfigPath = Path.Combine(path, ModConfig.ModConfigFilename); + if (!modDisk.FileExists(modConfigPath)) + { + Error($"Mod {modName} does not contain a {ModConfig.ModConfigFilename} file"); + return; + } + + var modConfig = ModConfig.Load(modConfigPath, modDisk, jsonUtil); + if (modConfig.SymLinks != null) + foreach (var kvp in modConfig.SymLinks) + modDisk = new RedirectionFileSystemDecorator(modDisk, kvp.Key, pathResolver.ResolvePath(kvp.Value)); + + var modMapping = new AssetMapping(); + + // Load dependencies + foreach (var dependency in modConfig.Dependencies) + { + LoadMod(dataDir, dependency, mapping); + if (!_mods.TryGetValue(dependency, out var dependencyInfo)) + { + Error($"Dependency {dependency} of mod {modName} could not be loaded, skipping load of {modName}"); + return; + } + + modMapping.MergeFrom(dependencyInfo.ModContext.Mapping); + } + + var parentTypeConfig = modConfig.InheritTypeConfigFrom != null && _mods.TryGetValue(modConfig.InheritTypeConfigFrom, out var parentMod) ? parentMod.TypeConfig : null; + var parentAssetConfig = modConfig.InheritAssetConfigFrom != null && _mods.TryGetValue(modConfig.InheritAssetConfigFrom, out parentMod) ? parentMod.AssetConfig : null; + + TypeConfig typeConfig = parentTypeConfig; + if (modConfig.TypeConfig != null) + { + var typeConfigPath = Path.Combine(path, modConfig.TypeConfig); + if (!modDisk.FileExists(typeConfigPath)) + { + Error($"Mod {modName} specifies {modConfig.TypeConfig} as a type config file, but it could not be found."); + return; + } + var tcl = new TypeConfigLoader(jsonUtil); + typeConfig = tcl.Load(typeConfigPath, modName, parentTypeConfig, modMapping, modDisk); + + if (typeConfig.VarTypes != null) + LoadVarTypes(modName, typeConfig.VarTypes); + + MergeTypesToMapping(modMapping, typeConfig, typeConfigPath); + } + typeConfig ??= new TypeConfig(modName, modMapping); + + AssetConfig assetConfig = parentAssetConfig; + if (modConfig.AssetConfig != null) + { + var assetConfigPath = Path.Combine(path, modConfig.AssetConfig); + if (!modDisk.FileExists(assetConfigPath)) + { + Error($"Mod {modName} specifies {modConfig.AssetConfig} as an asset config file, but it could not be found."); + return; + } + + var acl = new AssetConfigLoader(modDisk, jsonUtil, pathResolver, typeConfig); + assetConfig = acl.Load(assetConfigPath, modName, parentAssetConfig); + } + + assetConfig ??= new AssetConfig(modName, new RangeLookup()); + + var modInfo = new ModInfo(modName, typeConfig, assetConfig, modConfig, modMapping, jsonUtil, modDisk); + + if (typeConfig.Languages != null) + foreach (var kvp in typeConfig.Languages) + _languages[kvp.Key] = kvp.Value; + + mapping.MergeFrom(modMapping); + + _mods.Add(modName, modInfo); + _modsInReverseDependencyOrder.Add(modInfo); + } + + void LoadVarTypes(string modName, IEnumerable varTypes) + { + var varRegistry = Resolve(); + foreach (var typeName in varTypes) + { + var type = Type.GetType(typeName); + if (type == null) + throw new InvalidOperationException($"Could not load type \"{typeName}\" as Var container from mod {modName}"); + + varRegistry.Register(type); + } + } + + static void MergeTypesToMapping(AssetMapping mapping, TypeConfig config, string typeConfigPath) + { + foreach (var assetType in config.IdTypes.Values) + { + var enumType = Type.GetType(assetType.EnumType); + if (enumType == null) + throw new InvalidOperationException($"Could not load enum type \"{assetType.EnumType}\" defined in \"{typeConfigPath}\""); + + mapping.RegisterAssetType(assetType.EnumType, assetType.AssetType); + } + } +} \ No newline at end of file diff --git a/src/Game/Assets/PostProcessorRegistry.cs b/src/Game/Assets/PostProcessorRegistry.cs index 9c5e6baed..2741707f0 100644 --- a/src/Game/Assets/PostProcessorRegistry.cs +++ b/src/Game/Assets/PostProcessorRegistry.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using UAlbion.Api.Eventing; +using UAlbion.Config; using UAlbion.Formats; namespace UAlbion.Game.Assets; @@ -8,23 +9,19 @@ namespace UAlbion.Game.Assets; public class PostProcessorRegistry : ServiceComponent, IAssetPostProcessorRegistry { readonly object _syncRoot = new(); - readonly IDictionary _loaders = new Dictionary(); + readonly IDictionary _loaders = new Dictionary(); - public IAssetPostProcessor GetPostProcessor(string postProcessorName) + public IAssetPostProcessor GetPostProcessor(Type postProcessorType) { + if (postProcessorType == null) + throw new ArgumentNullException(nameof(postProcessorType)); + lock (_syncRoot) - return _loaders.TryGetValue(postProcessorName, out var postProcessor) ? postProcessor : Instantiate(postProcessorName); + return _loaders.TryGetValue(postProcessorType, out var postProcessor) ? postProcessor : Instantiate(postProcessorType); } - IAssetPostProcessor Instantiate(string postProcessorName) + IAssetPostProcessor Instantiate(Type type) { - if(string.IsNullOrEmpty(postProcessorName)) - throw new ArgumentNullException(nameof(postProcessorName)); - - var type = Type.GetType(postProcessorName); - if(type == null) - throw new InvalidOperationException($"Could not find post-processor type \"{postProcessorName}\""); - var constructor = type.GetConstructor(Array.Empty()); if(constructor == null) throw new InvalidOperationException($"Could not find parameterless constructor for post-processor type \"{type}\""); @@ -34,7 +31,7 @@ IAssetPostProcessor Instantiate(string postProcessorName) if (postProcessor is IComponent component) AttachChild(component); - _loaders[postProcessorName] = postProcessor; + _loaders[type] = postProcessor; return postProcessor; } } \ No newline at end of file diff --git a/src/Game/Assets/RawAssetManager.cs b/src/Game/Assets/RawAssetManager.cs index c39073f5e..7a860ea22 100644 --- a/src/Game/Assets/RawAssetManager.cs +++ b/src/Game/Assets/RawAssetManager.cs @@ -34,7 +34,7 @@ protected override void Subscribed() public void Save(AssetId key, object asset) { } public IEnumerable EnumerateAssets(AssetType type) => AssetMapping.Global.EnumerateAssetsOfType(type); - public AssetInfo GetAssetInfo(AssetId id, string language = null) => _modApplier.GetAssetInfo(id, language); + public AssetNode GetAssetInfo(AssetId id, string language = null) => _modApplier.GetAssetInfo(id, language); public IMapData LoadMap(MapId id) => (IMapData)_modApplier.LoadAsset(id); // No caching for map data public ItemData LoadItem(ItemId id) => (ItemData)_modApplier.LoadAsset(id); @@ -61,24 +61,32 @@ public AlbionPalette LoadPalette(PaletteId id) public TilesetData LoadTileData(TilesetId id) => (TilesetData)_modApplier.LoadAsset(id); public LabyrinthData LoadLabyrinthData(LabyrinthId id) => (LabyrinthData)_modApplier.LoadAsset(id); + public IStringSet LoadStringSet(StringSetId id) => LoadStringSet(id, null); + public IStringSet LoadStringSet(StringSetId id, string language) + { + var currentLanguage = Var(UserVars.Gameplay.Language); + language ??= currentLanguage; + + var asset = _modApplier.LoadAsset(id, language); + return (IStringSet)asset; + } string LoadStringCore(StringId id, string language) { language ??= Var(UserVars.Gameplay.Language); var asset = _modApplier.LoadAsset(id.Id, language); return asset switch { - IStringSet collection => collection.GetString(id, language), + IStringSet collection => collection.GetString(id), string s => s, _ => null }; } - public bool IsStringDefined(TextId id, string language) => LoadStringCore(id, language) != null; + public bool IsStringDefined(TextId id, string language) => LoadStringCore(new StringId(id), language) != null; public bool IsStringDefined(StringId id, string language) => LoadStringCore(id, language) != null; - public string LoadString(TextId id) => LoadString((StringId)id, null); - public string LoadString(StringId id) => LoadString(id, null); - public string LoadString(TextId id, string language) => LoadString((StringId)id, language); - public string LoadString(StringId id, string language) => LoadStringCore(id, language) // Raw manager - not cached - ?? $"!MISSING STRING {id.Id}:{id.SubId}!"; + public string LoadStringRaw(TextId id, string language = null) => LoadStringCore(new StringId(id), language); + public string LoadStringRaw(StringId id, string language = null) => LoadStringCore(id, language); + public string LoadStringSafe(TextId id, string language) => LoadStringCore(new StringId(id), language) ?? $"!MISSING STRING {id.Id}!"; + public string LoadStringSafe(StringId id, string language) => LoadStringCore(id, language) ?? $"!MISSING STRING {id.Id}:{id.SubId}!"; public ISample LoadSample(SampleId id) => (AlbionSample)_modApplier.LoadAsset(id); public WaveLib LoadWaveLib(WaveLibraryId waveLibraryId) => (WaveLib)_modApplier.LoadAsset(waveLibraryId); diff --git a/src/Game/Assets/SavedGameLoader.cs b/src/Game/Assets/SavedGameLoader.cs index a643f296e..7a079ec18 100644 --- a/src/Game/Assets/SavedGameLoader.cs +++ b/src/Game/Assets/SavedGameLoader.cs @@ -9,12 +9,12 @@ namespace UAlbion.Game.Assets; public class SavedGameLoader : Component, IAssetLoader { - public SavedGame Serdes(SavedGame existing, AssetInfo info, ISerializer s, SerdesContext context) + public SavedGame Serdes(SavedGame existing, ISerializer s, AssetLoadContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); return SavedGame.Serdes(existing, context.Mapping, s, Resolve()); } - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) - => Serdes(existing as SavedGame, info, s, context); + public object Serdes(object existing, ISerializer s, AssetLoadContext context) + => Serdes(existing as SavedGame, s, context); } diff --git a/src/Game/Assets/SoundBankLoader.cs b/src/Game/Assets/SoundBankLoader.cs index 78c454595..aa400fc17 100644 --- a/src/Game/Assets/SoundBankLoader.cs +++ b/src/Game/Assets/SoundBankLoader.cs @@ -2,12 +2,11 @@ using SerdesNet; using UAlbion.Api.Eventing; using UAlbion.Config; -using UAlbion.Formats; namespace UAlbion.Game.Assets; public class SoundBankLoader : Component, IAssetLoader { - public object Serdes(object existing, AssetInfo info, ISerializer s, SerdesContext context) + public object Serdes(object existing, ISerializer s, AssetLoadContext context) => GlobalTimbreLibrary.Serdes((GlobalTimbreLibrary)existing, s); } \ No newline at end of file diff --git a/src/Game/Entities/Map2D/SelectionHandler2D.cs b/src/Game/Entities/Map2D/SelectionHandler2D.cs index f1fd0f54b..baa329cca 100644 --- a/src/Game/Entities/Map2D/SelectionHandler2D.cs +++ b/src/Game/Entities/Map2D/SelectionHandler2D.cs @@ -44,7 +44,7 @@ public SelectionHandler2D(LogicalMap2D map, MapRenderable2D renderable) protected override void Subscribed() { var assets = Resolve(); - var eventFormatter = new EventFormatter(assets.LoadString, _map.Id.ToMapText()); + var eventFormatter = new EventFormatter(assets.LoadStringSafe, _map.Id.ToMapText()); _formatChain = x => { var builder = new UnformattedScriptBuilder(false); diff --git a/src/Game/Entities/Map3D/IsometricLayout.cs b/src/Game/Entities/Map3D/IsometricLayout.cs index 43b7c1a1a..d1d12fb2d 100644 --- a/src/Game/Entities/Map3D/IsometricLayout.cs +++ b/src/Game/Entities/Map3D/IsometricLayout.cs @@ -4,6 +4,7 @@ using UAlbion.Api.Eventing; using UAlbion.Api.Visual; using UAlbion.Config; +using UAlbion.Config.Properties; using UAlbion.Core.Visual; using UAlbion.Formats; using UAlbion.Formats.Assets.Labyrinth; @@ -15,12 +16,16 @@ namespace UAlbion.Game.Entities.Map3D; public class IsometricLayout : Component { readonly Dictionary _relativeSpritePositions = new(); + readonly ModContext _modContext; IExtrudedTilemap _tilemap; byte[] _contents; byte[] _floors; byte[] _ceilings; int _wallCount; + public IsometricLayout(ModContext modContext) + => _modContext = modContext ?? throw new ArgumentNullException(nameof(modContext)); + public int TileCount => _tilemap?.TileCount ?? 0; public List[] FloorFrames { get; private set; } public List[] CeilingFrames { get; private set; } @@ -51,17 +56,18 @@ public void Load(LabyrinthId labyrinthId, IsometricMode mode, TilemapRequest req { var assets = Resolve(); var labyrinthData = assets.LoadLabyrinthData(labyrinthId); - var info = assets.GetAssetInfo(labyrinthId); - if (labyrinthData == null || info == null) + var node = assets.GetAssetInfo(labyrinthId); + if (labyrinthData == null || node == null) return; - Load(labyrinthData, info, mode, request, paletteId, assets); + var context = new AssetLoadContext(labyrinthId, node, _modContext); + Load(labyrinthData, context, mode, request, paletteId, assets); } - public void Load(LabyrinthData labyrinthData, AssetInfo info, IsometricMode mode, TilemapRequest request, int? paletteNumber, IAssetManager assets) + public void Load(LabyrinthData labyrinthData, AssetLoadContext context, IsometricMode mode, TilemapRequest request, int? paletteNumber, IAssetManager assets) { if (labyrinthData == null) throw new ArgumentNullException(nameof(labyrinthData)); - if (info == null) throw new ArgumentNullException(nameof(info)); + if (context == null) throw new ArgumentNullException(nameof(context)); if (request == null) throw new ArgumentNullException(nameof(request)); if (assets == null) throw new ArgumentNullException(nameof(assets)); @@ -73,7 +79,7 @@ public void Load(LabyrinthData labyrinthData, AssetInfo info, IsometricMode mode bool walls = mode is IsometricMode.Walls or IsometricMode.All; bool contents = mode is IsometricMode.Contents or IsometricMode.All; - paletteNumber ??= info.Get(AssetProperty.PaletteId, 0); + paletteNumber ??= context.GetProperty(AssetProps.Palette).Id; var paletteId = new PaletteId(paletteNumber.Value); var palette = assets.LoadPalette(paletteId); if (palette == null) diff --git a/src/Game/Entities/MapObject.cs b/src/Game/Entities/MapObject.cs index 9b12f44f0..abaf213b4 100644 --- a/src/Game/Entities/MapObject.cs +++ b/src/Game/Entities/MapObject.cs @@ -144,7 +144,9 @@ public static MapObject Build(int tileX, int tileY, LabyrinthData labyrinth, Sub if (subObject == null) return null; if (subObject.ObjectInfoNumber >= labyrinth.Objects.Count) { - ApiUtil.Assert($"Tried to build object {subObject.ObjectInfoNumber} in {labyrinth.Id}, but there are only {labyrinth.Objects.Count} objects"); + ApiUtil.Assert(labyrinth.Objects.Count == 0 + ? $"Tried to place object with index {subObject.ObjectInfoNumber} in {labyrinth.Id}, but the labyrinth has no objects defined" + : $"Tried to place object with index {subObject.ObjectInfoNumber} in {labyrinth.Id}, but the maximum defined index is {labyrinth.Objects.Count - 1}"); return null; } diff --git a/src/Game/Events/NumericPromptEvent.cs b/src/Game/Events/NumericPromptEvent.cs index a8225aaeb..65e111822 100644 --- a/src/Game/Events/NumericPromptEvent.cs +++ b/src/Game/Events/NumericPromptEvent.cs @@ -1,11 +1,15 @@ using UAlbion.Api.Eventing; using UAlbion.Formats.Assets; +using UAlbion.Formats.Ids; namespace UAlbion.Game.Events; [Event("prompt:numeric")] public class NumericPromptEvent : Event, IAsyncEvent { + public NumericPromptEvent(TextId textId, int min, int max) : this(new StringId(textId), min, max) { } + + [EventConstructor] public NumericPromptEvent(StringId text, int min, int max) { Text = text; diff --git a/src/Game/Events/PartyMemberPromptEvent.cs b/src/Game/Events/PartyMemberPromptEvent.cs index 8d79388ae..f68fd6c2f 100644 --- a/src/Game/Events/PartyMemberPromptEvent.cs +++ b/src/Game/Events/PartyMemberPromptEvent.cs @@ -9,6 +9,7 @@ public class PartyMemberPromptEvent : GameEvent, IAsyncEvent public StringId Prompt { get; } public PartyMemberId[] Members { get; } + public PartyMemberPromptEvent(TextId prompt, PartyMemberId[] members = null) : this(new StringId(prompt), members) { } public PartyMemberPromptEvent(StringId prompt, PartyMemberId[] members = null) { Prompt = prompt; diff --git a/src/Game/Events/YesNoPromptEvent.cs b/src/Game/Events/YesNoPromptEvent.cs index e0611f754..e3123ebac 100644 --- a/src/Game/Events/YesNoPromptEvent.cs +++ b/src/Game/Events/YesNoPromptEvent.cs @@ -1,11 +1,15 @@ using UAlbion.Api.Eventing; using UAlbion.Formats.Assets; +using UAlbion.Formats.Ids; namespace UAlbion.Game.Events; [Event("prompt:yes_no")] public class YesNoPromptEvent : Event, IAsyncEvent, IVerboseEvent { + public YesNoPromptEvent(TextId textId) => StringId = new StringId(textId); + + [EventConstructor] public YesNoPromptEvent(StringId stringId) => StringId = stringId; [EventPart("id")] public StringId StringId { get; } } \ No newline at end of file diff --git a/src/Game/Gui/Controls/BoldHeader.cs b/src/Game/Gui/Controls/BoldHeader.cs index 11837ed00..a5b347c45 100644 --- a/src/Game/Gui/Controls/BoldHeader.cs +++ b/src/Game/Gui/Controls/BoldHeader.cs @@ -1,4 +1,5 @@ using UAlbion.Formats.Assets; +using UAlbion.Formats.Ids; using UAlbion.Game.Gui.Text; using UAlbion.Game.Text; @@ -8,6 +9,7 @@ class BoldHeader : UiElement { readonly StringId _id; + public BoldHeader(TextId id) : this(new StringId(id)) { } public BoldHeader(StringId id) => _id = id; public BoldHeader(IText source) => AttachChild(new UiText(source)); diff --git a/src/Game/Gui/Controls/Button.cs b/src/Game/Gui/Controls/Button.cs index fa6f8ee2c..846897f31 100644 --- a/src/Game/Gui/Controls/Button.cs +++ b/src/Game/Gui/Controls/Button.cs @@ -135,7 +135,7 @@ void OnEngineUpdate(EngineUpdateEvent e) } public Button(IText textSource) : this(new UiText(textSource)) { } - public Button(TextId textId) : this((StringId)textId) { } + public Button(TextId textId) : this(new StringId(textId)) { } public Button(StringId textId) : this((IUiElement)new UiTextBuilder(textId).Center().NoWrap()) { } public Button(string literalText) : this((IUiElement)new SimpleText(literalText).Center().NoWrap()) { } public Button OnClick(Action callback) { Click += callback; return this; } diff --git a/src/Game/Gui/Controls/DialogFrame.cs b/src/Game/Gui/Controls/DialogFrame.cs index 96f00e97b..0b1dbaa9a 100644 --- a/src/Game/Gui/Controls/DialogFrame.cs +++ b/src/Game/Gui/Controls/DialogFrame.cs @@ -54,7 +54,8 @@ void Rebuild(int width, int height, DrawLayer order) } var assets = Resolve(); - var multi = new CompositedTexture(AssetId.None, $"DialogFrame {width}x{height}", assets.LoadPalette(Id(Base.Palette.Inventory))); + var palette = assets.LoadPalette(Id(Base.Palette.Inventory)); + var multi = new CompositedTexture(AssetId.None, $"DialogFrame {width}x{height}", palette); void DrawLine(int y) { diff --git a/src/Game/Gui/Controls/Header.cs b/src/Game/Gui/Controls/Header.cs index f81b2536b..f85cea0e8 100644 --- a/src/Game/Gui/Controls/Header.cs +++ b/src/Game/Gui/Controls/Header.cs @@ -12,14 +12,7 @@ public class Header : UiElement // Header with midlines on either side readonly StringId _id; readonly int _padding; - public Header(TextId id, int padding = 0) : this(id, padding, Base.Ink.White) { } - public Header(TextId id, int padding, InkId color) - { - _id = id; - _padding = padding; - _inkId = color; - } - + public Header(TextId id, int padding = 0) : this(new StringId(id), padding, Base.Ink.White) { } public Header(StringId id, int padding = 0) : this(id, padding, Base.Ink.White) { } public Header(StringId id, int padding, InkId color) { diff --git a/src/Game/Gui/Controls/Label.cs b/src/Game/Gui/Controls/Label.cs index 0046d86d1..26085805a 100644 --- a/src/Game/Gui/Controls/Label.cs +++ b/src/Game/Gui/Controls/Label.cs @@ -1,9 +1,11 @@ using UAlbion.Formats.Assets; +using UAlbion.Formats.Ids; using UAlbion.Game.Gui.Text; namespace UAlbion.Game.Gui.Controls; class Label : UiElement { + public Label(TextId textId) : this(new StringId(textId)) { } public Label(StringId stringId) => AttachChild(new UiTextBuilder(stringId).Center()); } \ No newline at end of file diff --git a/src/Game/Gui/Dialogs/Conversation.cs b/src/Game/Gui/Dialogs/Conversation.cs index ca0685556..9e0e0835d 100644 --- a/src/Game/Gui/Dialogs/Conversation.cs +++ b/src/Game/Gui/Dialogs/Conversation.cs @@ -12,7 +12,6 @@ using UAlbion.Formats.MapEvents; using UAlbion.Game.Events; using UAlbion.Game.Events.Transitions; -using UAlbion.Game.Settings; using UAlbion.Game.State; using UAlbion.Game.Text; @@ -138,13 +137,12 @@ void OnClicked() var etId = _npc.EventSetId.ToEventText(); var strings = (IStringSet)Resolve().LoadAssetCached(etId); - var lang = Var(UserVars.Gameplay.Language); ushort subId = 0; for (ushort i = 0; i < strings.Count; i++) { - var s = strings.GetString(new StringId(etId, i), lang); - if (Tokeniser.Tokenise(s).Any(x => x.Item1 == Token.Block && x.Item2 is 0)) + var s = strings.GetString(new StringId(etId, i)); + if (Tokeniser.Tokenise(s).Any(x => x.Token == Token.Block && x.Argument is 0)) { subId = i; break; diff --git a/src/Game/Gui/Inventory/InventoryMidPane.cs b/src/Game/Gui/Inventory/InventoryMidPane.cs index acc15d6c4..e3f376995 100644 --- a/src/Game/Gui/Inventory/InventoryMidPane.cs +++ b/src/Game/Gui/Inventory/InventoryMidPane.cs @@ -19,7 +19,7 @@ public class InventoryMidPane : UiElement protected override void Subscribed() { var assets = Resolve(); - var positions = assets.LoadPartyMember(_activeCharacter).InventorySlots; + var positions = assets.LoadPartyMember(_activeCharacter)?.InventorySlots; if (positions == null) throw new AssetNotFoundException($"Could not load inventory slot positions for party member {_activeCharacter}"); diff --git a/src/Game/Gui/Inventory/InventorySummaryPage.cs b/src/Game/Gui/Inventory/InventorySummaryPage.cs index e7df02869..c072cab36 100644 --- a/src/Game/Gui/Inventory/InventorySummaryPage.cs +++ b/src/Game/Gui/Inventory/InventorySummaryPage.cs @@ -71,7 +71,7 @@ IEnumerable BuildSummary() // {INVE}{NAME} ({SEXC}), %u years old, {RACE}, {CLAS}, level %d. var formatBlocks = tf .Format( - assets.LoadString(Base.SystemText.Inv1_NYearsOldRaceClassLevelN), + assets.LoadStringSafe(Base.SystemText.Inv1_NYearsOldRaceClassLevelN), member.Apparent.Age, member.Apparent.Level).GetBlocks(); foreach (var block in formatBlocks) diff --git a/src/Game/Gui/Inventory/LogicalInventorySlot.cs b/src/Game/Gui/Inventory/LogicalInventorySlot.cs index c3a15c50b..89ce2046a 100644 --- a/src/Game/Gui/Inventory/LogicalInventorySlot.cs +++ b/src/Game/Gui/Inventory/LogicalInventorySlot.cs @@ -114,7 +114,7 @@ void Hover() if (slotInfo?.Item.Type == AssetType.Item) { var item = assets.LoadItem(slotInfo.Item); - itemName = assets.LoadString(item.Name); + itemName = assets.LoadStringSafe(item.Name); } var hand = inventoryManager.ItemInHand; @@ -122,7 +122,7 @@ void Hover() if (hand.Item.Type == AssetType.Item) { var itemInHand = assets.LoadItem(hand.Item); - itemInHandName = assets.LoadString(itemInHand.Name); + itemInHandName = assets.LoadStringSafe(itemInHand.Name); } var action = inventoryManager.GetInventoryAction(_id); diff --git a/src/Game/Gui/Menus/MainMenu.cs b/src/Game/Gui/Menus/MainMenu.cs index 2fe2a42ca..c120ab267 100644 --- a/src/Game/Gui/Menus/MainMenu.cs +++ b/src/Game/Gui/Menus/MainMenu.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using UAlbion.Core.Events; using UAlbion.Formats.Assets; -using UAlbion.Formats.Ids; using UAlbion.Game.Events; using UAlbion.Game.Gui.Controls; using UAlbion.Game.State; @@ -12,7 +11,7 @@ public class MainMenu : Dialog { public MainMenu() : base(DialogPositioning.Center) { - On(e => Raise(new PopSceneEvent())); + On(_ => Raise(new PopSceneEvent())); } protected override void Subscribed() @@ -23,7 +22,7 @@ protected override void Subscribed() var elements = new List { new Spacing(0, 2), - new HorizontalStacker(new Spacing(5, 0), new BoldHeader((TextId)Base.SystemText.MainMenu_MainMenu), new Spacing(5, 0)), + new HorizontalStacker(new Spacing(5, 0), new BoldHeader(Base.SystemText.MainMenu_MainMenu), new Spacing(5, 0)), new Divider(CommonColor.Yellow3), new Spacing(0, 2), }; @@ -68,7 +67,7 @@ void Continue() void NewGame() { - var e = new YesNoPromptEvent((TextId)Base.SystemText.MainMenu_DoYouReallyWantToStartANewGame); + var e = new YesNoPromptEvent(Base.SystemText.MainMenu_DoYouReallyWantToStartANewGame); var exchange = Exchange; RaiseAsync(e, response => { @@ -81,7 +80,7 @@ void NewGame() void LoadGame() { - var menu = new PickSaveSlotMenu(false, (TextId)Base.SystemText.MainMenu_WhichSavedGameDoYouWantToLoad, 1); + var menu = new PickSaveSlotMenu(false, Base.SystemText.MainMenu_WhichSavedGameDoYouWantToLoad, 1); var exchange = Exchange; menu.Closed += (args, id) => { @@ -95,7 +94,7 @@ void LoadGame() void SaveGame() { - var menu = new PickSaveSlotMenu(true, (TextId)Base.SystemText.MainMenu_SaveOnWhichPosition, 1); + var menu = new PickSaveSlotMenu(true, Base.SystemText.MainMenu_SaveOnWhichPosition, 1); var exchange = Exchange; menu.Closed += (args, _) => { diff --git a/src/Game/Gui/Menus/OptionsMenu.cs b/src/Game/Gui/Menus/OptionsMenu.cs index 74750b217..4fc1932ac 100644 --- a/src/Game/Gui/Menus/OptionsMenu.cs +++ b/src/Game/Gui/Menus/OptionsMenu.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using UAlbion.Api.Settings; using UAlbion.Formats; -using UAlbion.Formats.Ids; using UAlbion.Game.Events; using UAlbion.Game.Gui.Controls; using UAlbion.Game.Settings; @@ -40,16 +39,16 @@ protected override void Subscribed() var elements = new List { new Spacing(156,2), - new Label(TextId.From(Base.UAlbionString.LanguageLabel)), + new Label(Base.UAlbionString.LanguageLabel), new HorizontalStacker(languageButtons), new Spacing(0,2), - new Label((TextId)Base.SystemText.Options_MusicVolume), + new Label(Base.SystemText.Options_MusicVolume), new Slider(() => _musicVolume, x => _musicVolume = x, 0, 127), new Spacing(0,2), - new Label((TextId)Base.SystemText.Options_FXVolume), + new Label(Base.SystemText.Options_FXVolume), new Slider(() => _fxVolume, x => _fxVolume = x, 0, 127), new Spacing(0,2), - new Label((TextId)Base.SystemText.Options_CombatTextDelay), + new Label(Base.SystemText.Options_CombatTextDelay), new Slider(() => _combatDelay, x => _combatDelay = x, 1, 50), new Spacing(0,2), new Button(Base.SystemText.MsgBox_OK).OnClick(SaveAndClose), diff --git a/src/Game/Gui/Menus/PickSaveSlotMenu.cs b/src/Game/Gui/Menus/PickSaveSlotMenu.cs index 463d80c54..6ab99111b 100644 --- a/src/Game/Gui/Menus/PickSaveSlotMenu.cs +++ b/src/Game/Gui/Menus/PickSaveSlotMenu.cs @@ -6,6 +6,7 @@ using UAlbion.Config; using UAlbion.Formats.Assets; using UAlbion.Formats.Assets.Save; +using UAlbion.Formats.Ids; using UAlbion.Game.Events; using UAlbion.Game.Gui.Controls; using UAlbion.Game.Gui.Dialogs; @@ -20,6 +21,7 @@ public class PickSaveSlotMenu : ModalDialog readonly StringId _stringId; const ushort MaxSaveNumber = 10; // TODO: Add scroll bar and bump up to 99 + public PickSaveSlotMenu(bool showEmptySlots, TextId textId, int depth) : this(showEmptySlots, new StringId(textId), depth) { } public PickSaveSlotMenu(bool showEmptySlots, StringId stringId, int depth) : base(DialogPositioning.Center, depth) { On(e => diff --git a/src/Game/Gui/Text/ConversationManager.cs b/src/Game/Gui/Text/ConversationManager.cs index 966380706..e721d6db2 100644 --- a/src/Game/Gui/Text/ConversationManager.cs +++ b/src/Game/Gui/Text/ConversationManager.cs @@ -26,9 +26,9 @@ public ConversationManager() OnAsync(OnPartyMemberTextEvent); } - TextId ContextTextSource => + StringSetId ContextTextSource => Context is EventContext { EventSet: { } } context - ? context.EventSet.TextId + ? context.EventSet.StringSetId : Resolve().Current.MapId.ToMapText(); bool OnNpcTextEvent(NpcTextEvent e, Action continuation) diff --git a/src/Game/Gui/Text/UiTextBuilder.cs b/src/Game/Gui/Text/UiTextBuilder.cs index 95f0112f5..86de82b34 100644 --- a/src/Game/Gui/Text/UiTextBuilder.cs +++ b/src/Game/Gui/Text/UiTextBuilder.cs @@ -11,7 +11,7 @@ public class UiTextBuilder : UiElement, IText, ITextBuilder readonly StringId _stringId; string _language; - public UiTextBuilder(TextId textId) => _stringId = textId; + public UiTextBuilder(TextId textId) => _stringId = new StringId(textId); public UiTextBuilder(StringId stringId) => _stringId = stringId; public int Version => _implicitTokens.Count + 1; public IEnumerable GetBlocks() diff --git a/src/Game/Querier.cs b/src/Game/Querier.cs index 00b802b42..6059b6094 100644 --- a/src/Game/Querier.cs +++ b/src/Game/Querier.cs @@ -71,7 +71,7 @@ public Querier() if (context.Source == null) return false; - return RaiseAsync(new YesNoPromptEvent(new StringId(context.EventSet.TextId, q.Argument)), continuation) > 0; + return RaiseAsync(new YesNoPromptEvent(new StringId(context.EventSet.StringSetId, q.Argument)), continuation) > 0; }); OnAsync((q, continuation) => @@ -81,7 +81,7 @@ public Querier() return false; return RaiseAsync( - new NumericPromptEvent((TextId)Base.SystemText.MsgBox_EnterNumber, 0, 9999), + new NumericPromptEvent(Base.SystemText.MsgBox_EnterNumber, 0, 9999), x => continuation(x == q.Argument)) > 0; }); diff --git a/src/Game/State/Player/InventoryManager.cs b/src/Game/State/Player/InventoryManager.cs index c74d74f95..192f304e9 100644 --- a/src/Game/State/Player/InventoryManager.cs +++ b/src/Game/State/Player/InventoryManager.cs @@ -277,7 +277,7 @@ void GetQuantity(bool discard, IInventory inventory, ItemSlotId slotId, Action Base.SystemText.InvMsg_ReallyThrowTheseItemsAway, }; - RaiseAsync(new YesNoPromptEvent((TextId)prompt), response => + RaiseAsync(new YesNoPromptEvent(prompt), response => { if (!response) { @@ -650,7 +650,7 @@ void OnDrinkItem(DrinkItemEvent e) if (item.TypeId != ItemType.Drink) return; - RaiseAsync(new PartyMemberPromptEvent((TextId)Base.SystemText.InvMsg_WhoShouldDrinkThis), result => + RaiseAsync(new PartyMemberPromptEvent(Base.SystemText.InvMsg_WhoShouldDrinkThis), result => { if (result == PartyMemberId.None) return; diff --git a/src/Game/Text/TextFormatter.cs b/src/Game/Text/TextFormatter.cs index 4013aaa87..1a49bc121 100644 --- a/src/Game/Text/TextFormatter.cs +++ b/src/Game/Text/TextFormatter.cs @@ -116,31 +116,31 @@ public class TextFormatter : ServiceComponent, ITextFormatter switch (character.Race) { case PlayerRace.Terran: - yield return (Token.Text, assets.LoadString(Base.SystemText.Race_Terran)); + yield return (Token.Text, assets.LoadStringSafe(Base.SystemText.Race_Terran)); break; case PlayerRace.Iskai: - yield return (Token.Text, assets.LoadString(Base.SystemText.Race_Iskai)); + yield return (Token.Text, assets.LoadStringSafe(Base.SystemText.Race_Iskai)); break; case PlayerRace.Celt: - yield return (Token.Text, assets.LoadString(Base.SystemText.Race_Celt)); + yield return (Token.Text, assets.LoadStringSafe(Base.SystemText.Race_Celt)); break; case PlayerRace.KengetKamulos: - yield return (Token.Text, assets.LoadString(Base.SystemText.Race_KengetKamulos)); + yield return (Token.Text, assets.LoadStringSafe(Base.SystemText.Race_KengetKamulos)); break; case PlayerRace.DjiCantos: - yield return (Token.Text, assets.LoadString(Base.SystemText.Race_DjiCantos)); + yield return (Token.Text, assets.LoadStringSafe(Base.SystemText.Race_DjiCantos)); break; case PlayerRace.Mahino: - yield return (Token.Text, assets.LoadString(Base.SystemText.Race_Mahino)); + yield return (Token.Text, assets.LoadStringSafe(Base.SystemText.Race_Mahino)); break; case PlayerRace.Decadent: - yield return (Token.Text, assets.LoadString(Base.SystemText.Race_Decadent)); + yield return (Token.Text, assets.LoadStringSafe(Base.SystemText.Race_Decadent)); break; case PlayerRace.Umajo: - yield return (Token.Text, assets.LoadString(Base.SystemText.Race_Umajo)); + yield return (Token.Text, assets.LoadStringSafe(Base.SystemText.Race_Umajo)); break; case PlayerRace.Monster: - yield return (Token.Text, assets.LoadString(Base.SystemText.Race_Monster)); + yield return (Token.Text, assets.LoadStringSafe(Base.SystemText.Race_Monster)); break; default: throw new InvalidEnumArgumentException(nameof(character.Race), (int)character.Race, typeof(PlayerRace)); @@ -188,7 +188,7 @@ active is not ItemData item _ => throw new NotImplementedException() }; - yield return (Token.Text, assets.LoadString(word)); + yield return (Token.Text, assets.LoadStringSafe(word)); } static IEnumerable<(Token, object)> SubstituteClass(IAssetManager assets, object active) @@ -202,31 +202,31 @@ active is not ItemData item switch (character.PlayerClass) { case PlayerClass.Pilot: - yield return (Token.Text, assets.LoadString(Base.SystemText.Class_Pilot)); + yield return (Token.Text, assets.LoadStringSafe(Base.SystemText.Class_Pilot)); break; case PlayerClass.Scientist: - yield return (Token.Text, assets.LoadString(Base.SystemText.Class_Scientist)); + yield return (Token.Text, assets.LoadStringSafe(Base.SystemText.Class_Scientist)); break; case PlayerClass.IskaiWarrior: - yield return (Token.Text, assets.LoadString(Base.SystemText.Class_Warrior)); + yield return (Token.Text, assets.LoadStringSafe(Base.SystemText.Class_Warrior)); break; case PlayerClass.DjiKasMage: - yield return (Token.Text, assets.LoadString(Base.SystemText.Class_DjiKasMage)); + yield return (Token.Text, assets.LoadStringSafe(Base.SystemText.Class_DjiKasMage)); break; case PlayerClass.Druid: - yield return (Token.Text, assets.LoadString(Base.SystemText.Class_Druid)); + yield return (Token.Text, assets.LoadStringSafe(Base.SystemText.Class_Druid)); break; case PlayerClass.EnlightenedOne: - yield return (Token.Text, assets.LoadString(Base.SystemText.Class_EnlightenedOne)); + yield return (Token.Text, assets.LoadStringSafe(Base.SystemText.Class_EnlightenedOne)); break; case PlayerClass.Technician: - yield return (Token.Text, assets.LoadString(Base.SystemText.Class_Technician)); + yield return (Token.Text, assets.LoadStringSafe(Base.SystemText.Class_Technician)); break; case PlayerClass.OquloKamulos: - yield return (Token.Text, assets.LoadString(Base.SystemText.Class_OquloKamulos)); + yield return (Token.Text, assets.LoadStringSafe(Base.SystemText.Class_OquloKamulos)); break; case PlayerClass.Warrior: - yield return (Token.Text, assets.LoadString(Base.SystemText.Class_Warrior2)); + yield return (Token.Text, assets.LoadStringSafe(Base.SystemText.Class_Warrior2)); break; case PlayerClass.Monster: yield return (Token.Text, "Monster"); @@ -337,13 +337,13 @@ public IText Format(string templateText, params object[] arguments) => Format(templateText, null, arguments); public IText Format(TextId textId, IList<(Token, object)> implicitTokens, params object[] arguments) - => Format((StringId)textId, implicitTokens, arguments); + => Format(new StringId(textId), implicitTokens, arguments); public IText Format(StringId stringId, IList<(Token, object)> implicitTokens, params object[] arguments) => new DynamicText(() => { var assets = Resolve(); - string templateText = assets.LoadString(stringId); + string templateText = assets.LoadStringSafe(stringId); return InnerFormat(templateText, arguments, implicitTokens, assets); }); diff --git a/src/Game/Text/Tokeniser.cs b/src/Game/Text/Tokeniser.cs index 2b2449aa4..9dbfbab0a 100644 --- a/src/Game/Text/Tokeniser.cs +++ b/src/Game/Text/Tokeniser.cs @@ -14,7 +14,7 @@ enum TokeniserState InPercentage, } - public static IEnumerable<(Token, object)> Tokenise(string template) + public static IEnumerable<(Token Token, object Argument)> Tokenise(string template) { if (template == null) yield break; var sb = new StringBuilder(); diff --git a/src/Game/Text/WordLookup.cs b/src/Game/Text/WordLookup.cs index bb9df35c3..4a354a36c 100644 --- a/src/Game/Text/WordLookup.cs +++ b/src/Game/Text/WordLookup.cs @@ -43,7 +43,7 @@ void Rebuild() foreach (var id in AssetId.EnumerateAll(AssetType.Word)) { - var text = assets.LoadString(id).Trim().ToUpperInvariant(); + var text = assets.LoadStringSafe(id).Trim().ToUpperInvariant(); if (!_lookup.TryGetValue(text, out var ids)) { ids = new List(); diff --git a/src/Tests/UAlbion.Base.Tests/AssetConversionTests.cs b/src/Tests/UAlbion.Base.Tests/AssetConversionTests.cs index f01f440e2..9a0a92d6e 100644 --- a/src/Tests/UAlbion.Base.Tests/AssetConversionTests.cs +++ b/src/Tests/UAlbion.Base.Tests/AssetConversionTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using UAlbion.Api; @@ -53,8 +54,6 @@ void Test( Func canonicalize = null) where T : class { prerequisites ??= Array.Empty(); - var allIds = prerequisites.Append(id); - var resultsDir = Path.Combine(_baseDir, "re", "ConversionTests"); var baseAsset = (T)_baseApplier.LoadAsset(id); @@ -63,13 +62,17 @@ void Test( var mapping = AssetMapping.Global; var stubDisk = new StubFileSystem(); - var context = new SerdesContext("Test", JsonUtil, mapping, stubDisk); + var modContext = new ModContext("Test", JsonUtil, stubDisk, mapping); + var node = _baseApplier.GetAssetInfo(id, null); + var context = new AssetLoadContext(id, node, modContext); var (baseBytes, baseNotes) = Asset.Save(baseAsset, serdes, context); var baseJson = Asset.SaveJson(baseAsset, JsonUtil); - var idStrings = allIds.Select(x => $"{x.Type}.{x.Id}").ToArray(); - var assetTypes = allIds.Select(x => x.Type).Distinct().ToHashSet(); + var idSet = new[] { id }.ToHashSet(); + var prereqSet = prerequisites.ToHashSet(); + var assetTypes = prerequisites.Select(x => x.Type).Distinct().ToHashSet(); + assetTypes.Add(id.Type); using (var unpacker = new AssetConverter( "ualbion-tests", @@ -79,7 +82,9 @@ void Test( new[] { BaseAssetMod }, UnpackedAssetMod)) { - unpacker.Convert(idStrings, assetTypes, null); + if (prerequisites.Length > 0) + unpacker.Convert(prereqSet, assetTypes, null); + unpacker.Convert(idSet, assetTypes, null); } var unpackedAsset = (T)BuildApplier(UnpackedAssetMod, AssetMapping.Global).LoadAsset(id); @@ -107,7 +112,10 @@ void Test( new[] { UnpackedAssetMod }, RepackedAssetMod)) { - repacker.Convert(idStrings, assetTypes, null); + if (prerequisites.Length > 0) + repacker.Convert(prereqSet, assetTypes, null); + + repacker.Convert(idSet, assetTypes, null); } var repackedAsset = (T)BuildApplier(RepackedAssetMod, AssetMapping.Global).LoadAsset(id); @@ -130,7 +138,7 @@ void Test( [Fact] public void ItemTest() { - var info = new AssetInfo { AssetId = AssetId.From(Item.Knife) }; + var id = AssetId.From(Item.Knife); var spell = new SpellData(Spell.ThornSnare, SpellClass.DjiKas, 0) { Cost = 1, @@ -145,116 +153,99 @@ public void ItemTest() .Attach(spellManager) .Attach(itemDataLoader); - Test(info.AssetId, null, (x, s, c) => itemDataLoader.Serdes(x, info, s, c)); + Test(id, null, (x, s, c) => itemDataLoader.Serdes(x, s, c)); } [Fact] public void ItemNameTest() { - var info = new AssetInfo { AssetId = AssetId.From(Special.ItemNames) }; - Test(info.AssetId, - AssetMapping.Global.EnumerateAssetsOfType(AssetType.ItemName).ToArray(), - (x, s, c) => Loaders.ItemNameLoader.Serdes(x, info, s, c)); + var id = AssetId.From(Special.ItemNamesMultiLang); + Test>(id, + AssetId.EnumerateAll(AssetType.ItemName).ToArray(), + (x, s, c) => Loaders.ItemNameLoader.Serdes(x, s, c)); } [Fact] public void AutomapTest() { - var info = new AssetInfo { AssetId = AssetId.From(Automap.Jirinaar) }; - Test(info.AssetId, null, (x, s, c) => Loaders.AutomapLoader.Serdes(x, info, s, c)); + var id = AssetId.From(Automap.Jirinaar); + Test(id, null, (x, s, c) => Loaders.AutomapLoader.Serdes(x, s, c)); } [Fact] public void BlockListTest() { - var info = new AssetInfo { AssetId = AssetId.From(BlockList.Toronto) }; - Test(info.AssetId, null, (x, s, c) => Loaders.BlockListLoader.Serdes(x, info, s, c)); + var id = AssetId.From(BlockList.Toronto); + Test(id, null, (x, s, c) => Loaders.BlockListLoader.Serdes(x, s, c)); } [Fact] public void ChestTest() { - var info = new AssetInfo { AssetId = AssetId.From(Chest.HClanCellar_ID_IKn_ILC_StC_LSh_3g) }; + var id = AssetId.From(Chest.HClanCellar_ID_IKn_ILC_StC_LSh_3g); Test( - info.AssetId, + id, AssetId.EnumerateAll(AssetType.Item).ToArray(), - (x, s, c) => Loaders.ChestLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.ChestLoader.Serdes(x, s, c)); } [Fact] public void CommonPaletteTest() { - var info = new AssetInfo { AssetId = AssetId.From(Palette.Common) }; - info.Set(AssetProperty.IsCommon, true); - Test(info.AssetId, null, (x, s, c) => Loaders.PaletteLoader.Serdes(x, info, s, c)); + var id = AssetId.From(Palette.Common); + Test(id, null, (x, s, c) => Loaders.PaletteLoader.Serdes(x, s, c)); } [Fact] public void EventSetTest() { - var info = new AssetInfo { AssetId = AssetId.From(EventSet.Frill) }; + var id = AssetId.From(EventSet.Frill); Test( - info.AssetId, + id, null, - (x, s, c) => Loaders.EventSetLoader.Serdes(x, info, s, c), + (x, s, c) => Loaders.EventSetLoader.Serdes(x, s, c), LayoutTestUtil.CanonicalizeEventSet); } [Fact] public void EventTextTest() { - var info = new AssetInfo { AssetId = AssetId.From(EventText.Frill) }; - Test(info.AssetId, null, (x, s, c) => Loaders.AlbionStringTableLoader.Serdes(x, info, s, c)); + var id = AssetId.From(EventText.Frill); + Test(id, null, (x, s, c) => Loaders.AlbionStringTableLoader.Serdes(x, s, c)); } [Fact] public void LabyrinthTest() { - var info = new AssetInfo { AssetId = AssetId.From(Labyrinth.Jirinaar) }; - Test(info.AssetId, null, (x, s, c) => Loaders.LabyrinthDataLoader.Serdes(x, info, s, c)); + var id = AssetId.From(Labyrinth.Jirinaar); + Test(id, null, (x, s, c) => Loaders.LabyrinthDataLoader.Serdes(x, s, c)); } [Fact] public void Map2DTest_200() // An outdoor level { - var info = new AssetInfo { AssetId = AssetId.From(Map.Nakiridaani) }; - - var small = AssetMapping.Global.EnumerateAssetsOfType(AssetType.NpcSmallGfx); - var large = AssetMapping.Global.EnumerateAssetsOfType(AssetType.NpcLargeGfx); + var id = AssetId.From(Map.Nakiridaani); var palettes = AssetMapping.Global.EnumerateAssetsOfType(AssetType.Palette); - var prereqs = - new[] { AssetId.From(Special.DummyObject) } - .Concat(small) - .Concat(large) - .Concat(palettes) - .ToArray(); + var prereqs = new[] { AssetId.From(Special.TiledNpcsSmall) }.Concat(palettes).ToArray(); Test( - info.AssetId, + id, prereqs, - (x, s, c) => MapData2D.Serdes(info, x, c.Mapping, s), + (x, s, c) => MapData2D.Serdes(id, x, c.Mapping, s), LayoutTestUtil.CanonicalizeMap); } [Fact] public void Map2DTest_300() // Starting level { - var info = new AssetInfo { AssetId = AssetId.From(Map.TorontoBegin) }; - - var small = AssetMapping.Global.EnumerateAssetsOfType(AssetType.NpcSmallGfx); - var large = AssetMapping.Global.EnumerateAssetsOfType(AssetType.NpcLargeGfx); + var id = AssetId.From(Map.TorontoBegin); var palettes = AssetMapping.Global.EnumerateAssetsOfType(AssetType.Palette); - var prereqs = - new[] { AssetId.From(Special.DummyObject) } - .Concat(small) - .Concat(large) - .Concat(palettes) - .ToArray(); + var prereqs = new[] { AssetId.From(Special.TiledNpcsLarge) }.Concat(palettes).ToArray(); Test( - info.AssetId, + id, prereqs, - (x, s, c) => MapData2D.Serdes(info, x, c.Mapping, s), + (x, s, c) => MapData2D.Serdes(id, x, c.Mapping, s), LayoutTestUtil.CanonicalizeMap); } @@ -262,11 +253,11 @@ public void Map2DTest_300() // Starting level public void Map3DTest_110() // A town level with automap markers { var prereqs = new[] { AssetId.From(Labyrinth.Jirinaar) }; - var info = new AssetInfo { AssetId = AssetId.From(Map.Jirinaar) }; + var id = AssetId.From(Map.Jirinaar); Test( - info.AssetId, + id, prereqs, - (x, s, c) => MapData3D.Serdes(info, x, c.Mapping, s), + (x, s, c) => MapData3D.Serdes(id, x, c.Mapping, s), LayoutTestUtil.CanonicalizeMap); } @@ -274,46 +265,46 @@ public void Map3DTest_110() // A town level with automap markers public void Map3DTest_122() // A dungeon level { var prereqs = new[] { AssetId.From(Labyrinth.Argim) }; - var info = new AssetInfo { AssetId = AssetId.From(Map.OldFormerBuilding) }; + var id = AssetId.From(Map.OldFormerBuilding); Test( - info.AssetId, + id, prereqs, - (x, s, c) => MapData3D.Serdes(info, x, c.Mapping, s), + (x, s, c) => MapData3D.Serdes(id, x, c.Mapping, s), LayoutTestUtil.CanonicalizeMap); } [Fact] public void MapTextTest() { - var info = new AssetInfo { AssetId = AssetId.From(MapText.TorontoBegin) }; - Test(info.AssetId, null, (x, s, c) => Loaders.AlbionStringTableLoader.Serdes(x, info, s, c)); + var id = AssetId.From(MapText.TorontoBegin); + Test(id, null, (x, s, c) => Loaders.AlbionStringTableLoader.Serdes(x, s, c)); } [Fact] public void MerchantTest() { - var info = new AssetInfo { AssetId = AssetId.From(Merchant.AltheaSpells) }; + var id = AssetId.From(Merchant.AltheaSpells); Test( - info.AssetId, + id, AssetId.EnumerateAll(AssetType.Item).ToArray(), - (x, s, c) => Loaders.MerchantLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.MerchantLoader.Serdes(x, s, c)); } [Fact] public void BrokenMerchantTest() { - var info = new AssetInfo { AssetId = AssetId.From(Merchant.Unknown1) }; + var id = AssetId.From(Merchant.Unknown1); Test( - info.AssetId, + id, AssetId.EnumerateAll(AssetType.Item).ToArray(), - (x, s, c) => Loaders.MerchantLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.MerchantLoader.Serdes(x, s, c)); } [Fact] public void MonsterGroupTest() { - var info = new AssetInfo { AssetId = AssetId.From(MonsterGroup.TwoSkrinn1OneKrondir1) }; - Test(info.AssetId, null, (x, s, c) => Loaders.MonsterGroupLoader.Serdes(x, info, s, c)); + var id = AssetId.From(MonsterGroup.TwoSkrinn1OneKrondir1); + Test(id, null, (x, s, c) => Loaders.MonsterGroupLoader.Serdes(x, s, c)); } static CharacterSheetLoader BuildCharacterLoader() @@ -338,45 +329,45 @@ static CharacterSheetLoader BuildCharacterLoader() [Fact] public void MonsterTest() { - var info = new AssetInfo { AssetId = AssetId.From(MonsterSheet.Krondir1) }; + var id = AssetId.From(MonsterSheet.Krondir1); var loader = BuildCharacterLoader(); Test( - info.AssetId, + id, AssetId.EnumerateAll(AssetType.Item).ToArray(), - (x, s, c) => loader.Serdes(x, info, s, c)); + (x, s, c) => loader.Serdes(x, s, c)); } [Fact] public void NpcTest() { - var info = new AssetInfo { AssetId = AssetId.From(NpcSheet.Christine) }; + var id = AssetId.From(NpcSheet.Christine); var loader = BuildCharacterLoader(); - Test(info.AssetId, null, (x, s, c) => loader.Serdes(x, info, s, c)); + Test(id, null, (x, s, c) => loader.Serdes(x, s, c)); } [Fact] public void PaletteTest() { - var info = new AssetInfo { AssetId = AssetId.From(Palette.Toronto2D) }; - Test(info.AssetId, new[] { AssetId.From(Palette.Common) }, (x, s, c) => Loaders.PaletteLoader.Serdes(x, info, s, c)); + var id = AssetId.From(Palette.Toronto2D); + Test(id, new[] { AssetId.From(Palette.Common) }, (x, s, c) => Loaders.PaletteLoader.Serdes(x, s, c)); } [Fact] public void PartySheetTest() { - var info = new AssetInfo { AssetId = AssetId.From(PartySheet.Tom) }; + var id = AssetId.From(PartySheet.Tom); var loader = BuildCharacterLoader(); Test( - info.AssetId, + id, AssetId.EnumerateAll(AssetType.Item).ToArray(), - (x, s, c) => loader.Serdes(x, info, s, c)); + (x, s, c) => loader.Serdes(x, s, c)); } [Fact] public void SampleTest() { - var info = new AssetInfo { AssetId = AssetId.From(Sample.IllTemperedLlama) }; - Test(info.AssetId, null, (x, s, c) => Loaders.SampleLoader.Serdes(x, info, s, c)); + var id = AssetId.From(Sample.IllTemperedLlama); + Test(id, null, (x, s, c) => Loaders.SampleLoader.Serdes(x, s, c)); } /* They're text anyway so not too bothered - at the moment they don't round trip due to using friendly asset id names @@ -384,279 +375,259 @@ public void SampleTest() [Fact] public void ScriptTest() { - var info = new AssetInfo { AssetId = AssetId.From(Script.TomMeetsChristine) }; - Test>(info.AssetId, null, (x, s, c) => Loaders.ScriptLoader.Serdes(x, info, s, c)); + var id = AssetId.From(Script.TomMeetsChristine); + Test>(id, null, (x, s, c) => Loaders.ScriptLoader.Serdes(x, s, c)); } //*/ [Fact] public void SongTest() { - var info = new AssetInfo { AssetId = AssetId.From(Song.Toronto) }; - Test(info.AssetId, null, (x, s, c) => Loaders.SongLoader.Serdes(x, info, s, c)); + var id = AssetId.From(Song.Toronto); + Test(id, null, (x, s, c) => Loaders.SongLoader.Serdes(x, s, c)); } [Fact] public void SpellTest() { - var info = new AssetInfo { AssetId = AssetId.From(Spell.FrostAvalanche) }; - Test(info.AssetId, null, (x, s, c) => Loaders.SpellLoader.Serdes(x, info, s, c)); + var id = AssetId.From(Spell.FrostAvalanche); + Test(id, null, (x, s, c) => Loaders.SpellLoader.Serdes(x, s, c)); } [Fact] public void TilesetTest() { - var info = new AssetInfo { AssetId = AssetId.From(Tileset.Outdoors) }; - Test(info.AssetId, null, (x, s, c) => Loaders.TilesetLoader.Serdes(x, info, s, c)); + var id = AssetId.From(Tileset.Outdoors); + Test(id, null, (x, s, c) => Loaders.TilesetLoader.Serdes(x, s, c)); } [Fact] public void WaveLibTest() { - var info = new AssetInfo { AssetId = AssetId.From(WaveLibrary.Unknown5) }; - Test(info.AssetId, null, (x, s, c) => Loaders.WaveLibLoader.Serdes(x, info, s, c)); + var id = AssetId.From(WaveLibrary.Unknown5); + Test(id, null, (x, s, c) => Loaders.WaveLibLoader.Serdes(x, s, c)); + } + + [Fact] + public void Words1Test() + { + var id = AssetId.From(Special.Words1); + Test( + id, + AssetMapping.Global.EnumerateAssetsOfType(AssetType.Word).ToArray(), + (x, s, c) => Loaders.WordListLoader.Serdes(x, s, c)); } [Fact] - public void WordTest() + public void Words2Test() { - var info = new AssetInfo { AssetId = AssetId.From(Special.Words1) }; + var id = AssetId.From(Special.Words2); Test( - info.AssetId, + id, AssetMapping.Global.EnumerateAssetsOfType(AssetType.Word).ToArray(), - (x, s, c) => Loaders.WordListLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.WordListLoader.Serdes(x, s, c)); } + + [Fact] + public void Words3Test() + { + var id = AssetId.From(Special.Words3); + Test( + id, + AssetMapping.Global.EnumerateAssetsOfType(AssetType.Word).ToArray(), + (x, s, c) => Loaders.WordListLoader.Serdes(x, s, c)); + } + //* [Fact] public void AutomapGfxTest() { - var info = new AssetInfo { AssetId = AssetId.From(AutomapTiles.Set1) }; - info.Set(AssetProperty.SubSprites, "(8,8,576) (16,16)"); - Test>(info.AssetId, + var id = AssetId.From(AutomapTiles.Set1); + Test>(id, new[] { AssetId.From(Palette.Common), AssetId.From(Palette.Unknown11) }, - (x, s, c) => Loaders.AmorphousSpriteLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.AmorphousSpriteLoader.Serdes(x, s, c)); } [Fact] public void CombatBgTest() { - var info = new AssetInfo - { - AssetId = AssetId.From(CombatBackground.Toronto), - Width = 360 - }; - Test>(info.AssetId, + var id = AssetId.From(CombatBackground.Toronto); + Test>(id, new[] { AssetId.From(Palette.TorontoCombat), AssetId.From(Palette.Common) }, - (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, s, c)); } [Fact] public void DungeonObjectTest() { - var info = new AssetInfo - { - AssetId = AssetId.From(DungeonObject.Krondir), - Width = 145, - Height = 165 - }; - Test>(info.AssetId, + var id = AssetId.From(DungeonObject.Krondir); + Test>(id, new[] { AssetId.From(Palette.JirinaarDay), AssetId.From(Palette.Common) }, - (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, s, c)); } [Fact] public void FontGfxTest() { - var info = new AssetInfo { AssetId = AssetId.From(FontGfx.Regular), Width = 8, Height = 8 }; - Test>(info.AssetId, + var id = AssetId.From(FontGfx.Regular); + Test>(id, new[] { AssetId.From(Palette.Common), AssetId.From(FontGfx.Bold) }, - (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, s, c)); } [Fact] public void ItemSpriteTest() { - var info = new AssetInfo - { - AssetId = AssetId.From(ItemGfx.ItemSprites), - Width = 16, - Height = 16 - }; - Test>(info.AssetId, + var id = AssetId.From(ItemGfx.ItemSprites); + Test>(id, new[] { AssetId.From(Palette.Common) }, - (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, s, c)); } [Fact] public void SlabTest() { - var info = new AssetInfo { AssetId = AssetId.From(UiBackground.Slab), Width = 360 }; - Test>(info.AssetId, + var id = AssetId.From(UiBackground.Slab); + Test>(id, new[] { AssetId.From(Palette.Common) }, - (x, s, c) => Loaders.SlabLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.SlabLoader.Serdes(x, s, c)); } [Fact] public void TileGfxTest() { - var info = new AssetInfo - { - AssetId = AssetId.From(TilesetGfx.Toronto), - Width = 16, - Height = 16 - }; + var id = AssetId.From(TilesetGfx.Toronto); var palettes = AssetMapping.Global.EnumerateAssetsOfType(AssetType.Palette); var prereqs = palettes.Append(AssetId.From(Palette.Toronto2D)).ToArray(); - Test(info.AssetId, prereqs, (x, s, c) => Loaders.TilesetGraphicsLoader.Serdes(x, info, s, c)); + Test(id, prereqs, (x, s, c) => Loaders.TilesetGraphicsLoader.Serdes(x, s, c)); } [Fact] public void CombatGfxTest() { - var info = new AssetInfo { AssetId = AssetId.From(CombatGfx.SplashYellow) }; - Test>(info.AssetId, + var id = AssetId.From(CombatGfx.SplashYellow); + Test>(id, new[] { AssetId.From(Palette.PlainsCombat), AssetId.From(Palette.Common) }, - (x, s, c) => Loaders.MultiHeaderSpriteLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.MultiHeaderSpriteLoader.Serdes(x, s, c)); } [Fact] public void DungeonBgTest() { - var info = new AssetInfo { AssetId = AssetId.From(DungeonBackground.EarlyGameL) }; - Test>(info.AssetId, + var id = AssetId.From(DungeonBackground.EarlyGameL); + Test>(id, new[] { AssetId.From(Palette.JirinaarDay), AssetId.From(Palette.Common) }, - (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, s, c)); } [Fact] public void FloorTest() { - var info = new AssetInfo - { - AssetId = AssetId.From(Floor.Water), - Width = 64, - Height = 64 - }; - Test>(info.AssetId, + var id = AssetId.From(Floor.Water); + + Test>(id, new[] { AssetId.From(Palette.JirinaarDay), AssetId.From(Palette.Common) }, - (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, s, c)); } [Fact] public void FullBodyPictureTest() { - var info = new AssetInfo { AssetId = AssetId.From(PartyInventoryGfx.Tom) }; - Test>(info.AssetId, + var id = AssetId.From(PartyInventoryGfx.Tom); + Test>(id, new[] { AssetId.From(Palette.Inventory), AssetId.From(Palette.Common) }, - (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, s, c)); } [Fact] public void LargeNpcTest() { - var info = new AssetInfo { AssetId = AssetId.From(NpcLargeGfx.Christine) }; - Test>(info.AssetId, + var id = AssetId.From(NpcLargeGfx.Christine); + Test>(id, new[] { AssetId.From(Palette.Toronto2D), AssetId.From(Palette.Common) }, - (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, s, c)); } [Fact] public void LargePartyMemberTest() { - var info = new AssetInfo { AssetId = AssetId.From(PartyLargeGfx.Tom) }; - Test>(info.AssetId, + var id = AssetId.From(PartyLargeGfx.Tom); + Test>(id, new[] { AssetId.From(Palette.IskaiIndoorDark), AssetId.From(Palette.Common) }, - (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, s, c)); } [Fact] public void MonsterGfxTest() { - var info = new AssetInfo { AssetId = AssetId.From(MonsterGfx.Krondir) }; - Test>(info.AssetId, + var id = AssetId.From(MonsterGfx.Krondir); + Test>(id, new[] { AssetId.From(Palette.DungeonCombat), AssetId.From(Palette.Common) }, - (x, s, c) => Loaders.MultiHeaderSpriteLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.MultiHeaderSpriteLoader.Serdes(x, s, c)); } [Fact] public void OverlayTest() { - var info = new AssetInfo - { - AssetId = AssetId.From(WallOverlay.JiriWindow), - Width = 44, - File = new AssetFileInfo() - }; - info.File.Set(AssetProperty.Transposed, true); - Test>(info.AssetId, + var id = AssetId.From(WallOverlay.JiriWindow); + + Test>(id, new[] { AssetId.From(Palette.JirinaarDay), AssetId.From(Palette.Common) }, - (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, s, c)); } /* No code to write these atm, if anyone wants to mod them or add new ones they can still use ImageMagick or something to convert to ILBM [Fact] public void PictureTest() { - var info = new AssetInfo { AssetId = AssetId.From(Picture.OpenChestWithGold) }; - Test(info.AssetId, null, (x, s, c) => Loaders.InterlacedBitmapLoader.Serdes(x, info, s, c)); + var id = AssetId.From(Picture.OpenChestWithGold); + Test(id, null, (x, s, c) => Loaders.InterlacedBitmapLoader.Serdes(x, s, c)); } //*/ [Fact] public void PortraitTest() { - var info = new AssetInfo - { - AssetId = AssetId.From(Portrait.Tom), - Width = 34 - }; - Test>(info.AssetId, + var id = AssetId.From(Portrait.Tom); + Test>(id, new[] { AssetId.From(Palette.Common) }, - (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, s, c)); } [Fact] public void SmallNpcTest() { - var info = new AssetInfo { AssetId = AssetId.From(NpcSmallGfx.Krondir) }; - Test>(info.AssetId, + var id = AssetId.From(NpcSmallGfx.Krondir); + Test>(id, new[] { AssetId.From(Palette.FirstIslandDay), AssetId.From(Palette.Common) }, - (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, s, c)); } [Fact] public void SmallPartyMemberTest() { - var info = new AssetInfo { AssetId = AssetId.From(PartySmallGfx.Tom) }; - Test>(info.AssetId, + var id = AssetId.From(PartySmallGfx.Tom); + Test>(id, new[] { AssetId.From(Palette.FirstIslandDay), AssetId.From(Palette.Common) }, - (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, s, c)); } [Fact] public void TacticalGfxTest() { - var info = new AssetInfo - { - AssetId = AssetId.From(TacticalGfx.Tom), - Width = 32 - }; - Test>(info.AssetId, + var id = AssetId.From(TacticalGfx.Tom); + Test>(id, new[] { AssetId.From(Palette.Common) }, - (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, s, c)); } [Fact] public void WallTest() { - var info = new AssetInfo - { - AssetId = AssetId.From(Wall.TorontoPanelling), - Width = 80 - }; - Test>(info.AssetId, + var id = AssetId.From(Wall.TorontoPanelling); + Test>(id, new[] { AssetId.From(Palette.Toronto3D), AssetId.From(Palette.Common) }, - (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, s, c)); } // */ } \ No newline at end of file diff --git a/src/Tests/UAlbion.Base.Tests/AssetLoadTests.cs b/src/Tests/UAlbion.Base.Tests/AssetLoadTests.cs index 566676df4..901ecfb93 100644 --- a/src/Tests/UAlbion.Base.Tests/AssetLoadTests.cs +++ b/src/Tests/UAlbion.Base.Tests/AssetLoadTests.cs @@ -59,7 +59,7 @@ static T Test(Func func) public void ItemTest() { var item = Test(assets => assets.LoadItem(Item.LughsShield)); - var name = Test(assets => assets.LoadString(item.Name)); + var name = Test(assets => assets.LoadStringSafe(item.Name)); Assert.Equal("Lugh's shield", name); Assert.Equal(18, item.Charges); Assert.Equal(PlayerClasses.Humans, item.Class); @@ -201,10 +201,10 @@ public void EventTextTest() Assert.Equal("\"Dsarii-ma, foreign visitors. My name is Frill, and I serve the council as a " + "scholar in history. What horrible events you had to go through! I am sure it is not " + "easy for strangers to be subjected to our laws!\"", - Test(assets => assets.LoadString(new StringId(EventText.Frill, 1)))); + Test(assets => assets.LoadStringSafe(new StringId(EventText.Frill, 1)))); Assert.Equal("\"I am trying to help them in any way I can, Sebai-Giz Frill!\"", - Test(assets => assets.LoadString(new StringId(EventText.Frill, 2)))); + Test(assets => assets.LoadStringSafe(new StringId(EventText.Frill, 2)))); } [Fact] @@ -470,8 +470,8 @@ public void Map3DTest() [Fact] public void MapTextTest() { - Assert.Equal("A fuse box.", Test(assets => assets.LoadString(new StringId(MapText.TorontoBegin, 1)))); - Assert.Equal("An armchair.", Test(assets => assets.LoadString(new StringId(MapText.TorontoBegin, 2)))); + Assert.Equal("A fuse box.", Test(assets => assets.LoadStringSafe(new StringId(MapText.TorontoBegin, 1)))); + Assert.Equal("An armchair.", Test(assets => assets.LoadStringSafe(new StringId(MapText.TorontoBegin, 2)))); } [Fact] @@ -551,7 +551,7 @@ public void MonsterTest() [Fact] public void NewStringTest() { - Assert.Equal("Take all", Test(assets => assets.LoadString(UAlbionString.TakeAll))); + Assert.Equal("Take all", Test(assets => assets.LoadStringSafe(UAlbionString.TakeAll))); } [Fact] @@ -729,13 +729,13 @@ public void SpellTest() Assert.Equal(SpellTargets.AllMonsters, s.Targets); Assert.Equal(SpellClass.DjiKas, s.Class); Assert.Equal(7, s.OffsetInClass); - Assert.Equal((TextId)SystemText.Spell0_7_FrostAvalanche, s.Name); + Assert.Equal((AssetId)(TextId)SystemText.Spell0_7_FrostAvalanche, s.Name); } [Fact] public void SystemTextTest() { - Assert.Equal("Main menu", Test(assets => assets.LoadString(SystemText.MainMenu_MainMenu))); + Assert.Equal("Main menu", Test(assets => assets.LoadStringSafe(SystemText.MainMenu_MainMenu))); } [Fact] @@ -798,7 +798,7 @@ public void WaveLibTest() [Fact] public void WordTest() { - Assert.Equal("key", Test(assets => assets.LoadString(Word.Key))); + Assert.Equal("key", Test(assets => assets.LoadStringSafe(Word.Key))); } [Fact] public void CombatGfxTest() { Test(assets => assets.LoadTexture(CombatGfx.SplashYellow)); } diff --git a/src/Tests/UAlbion.Base.Tests/CanonicalEventTests.cs b/src/Tests/UAlbion.Base.Tests/CanonicalEventTests.cs index f3f4940df..52dc63cf3 100644 --- a/src/Tests/UAlbion.Base.Tests/CanonicalEventTests.cs +++ b/src/Tests/UAlbion.Base.Tests/CanonicalEventTests.cs @@ -26,10 +26,13 @@ public CanonicalEventTests() AssetMapping.Global.MergeFrom(Mapping); } - void TestEventSet(EventSetId eventSetId) + static void TestEventSet(EventSetId eventSetId) { var assets = Exchange.Resolve(); var eventSet = assets.LoadEventSet(eventSetId); + if (eventSet == null) + throw new AssetNotFoundException($"Could not load event set {eventSetId}"); + Test(eventSetId, eventSet); } diff --git a/src/Tests/UAlbion.Base.Tests/EventRoundTripTests.cs b/src/Tests/UAlbion.Base.Tests/EventRoundTripTests.cs index 59afa7c8f..7200ad14d 100644 --- a/src/Tests/UAlbion.Base.Tests/EventRoundTripTests.cs +++ b/src/Tests/UAlbion.Base.Tests/EventRoundTripTests.cs @@ -25,18 +25,17 @@ public EventRoundTripTests() var disk = new MockFileSystem(true); var jsonUtil = new FormatJsonUtil(); var baseDir = ConfigUtil.FindBasePath(disk); - var baseAssetConfigPath = Path.Combine(baseDir, "mods", "Base", "base_assets.json"); - var assetConfigPath = Path.Combine(baseDir, "mods", "Albion", "alb_assets.json"); + var typeConfigPath = Path.Combine(baseDir, "mods", "Base", "types.json"); - var baseAssetConfig = AssetConfig.Load(baseAssetConfigPath, null, mapping, disk, jsonUtil); - var assetConfig = AssetConfig.Load(assetConfigPath, baseAssetConfig, mapping, disk, jsonUtil); + var tcl = new TypeConfigLoader(jsonUtil); + var typeConfig = tcl.Load(typeConfigPath, "Base", null, mapping, disk); - foreach (var assetType in assetConfig.IdTypes.Values) + foreach (var assetType in typeConfig.IdTypes.Values) { var enumType = Type.GetType(assetType.EnumType); if (enumType == null) throw new InvalidOperationException( - $"Could not load enum type \"{assetType.EnumType}\" defined in \"{assetConfigPath}\""); + $"Could not load enum type \"{assetType.EnumType}\" defined in \"{typeConfigPath}\""); mapping.RegisterAssetType(assetType.EnumType, assetType.AssetType); } diff --git a/src/Tests/UAlbion.Base.Tests/Loaders.cs b/src/Tests/UAlbion.Base.Tests/Loaders.cs index 903509f6a..86e790764 100644 --- a/src/Tests/UAlbion.Base.Tests/Loaders.cs +++ b/src/Tests/UAlbion.Base.Tests/Loaders.cs @@ -16,6 +16,7 @@ public static class Loaders // public static readonly InterlacedBitmapLoader InterlacedBitmapLoader = new InterlacedBitmapLoader(); public static readonly ItemNameLoader ItemNameLoader = new(); public static readonly LabyrinthDataLoader LabyrinthDataLoader = new(); + public static readonly MapLoader MapLoader = new(); public static readonly MerchantLoader MerchantLoader = new(); public static readonly MonsterGroupLoader MonsterGroupLoader = new(); public static readonly PaletteLoader PaletteLoader = new(); diff --git a/src/Tests/UAlbion.Base.Tests/RoundtripTests.cs b/src/Tests/UAlbion.Base.Tests/RoundtripTests.cs index 3d34cf48b..aaeb4fbd3 100644 --- a/src/Tests/UAlbion.Base.Tests/RoundtripTests.cs +++ b/src/Tests/UAlbion.Base.Tests/RoundtripTests.cs @@ -1,10 +1,12 @@ using System; +using System.Collections.Generic; using System.IO; using SerdesNet; using UAlbion.Api; using UAlbion.Api.Eventing; using UAlbion.Api.Visual; using UAlbion.Config; +using UAlbion.Config.Properties; using UAlbion.Formats; using UAlbion.Formats.Assets; using UAlbion.Formats.Assets.Labyrinth; @@ -50,12 +52,13 @@ public RoundtripTests() var baseDir = ConfigUtil.FindBasePath(disk); var pathResolver = new PathResolver(baseDir, "ualbion-tests"); pathResolver.RegisterPath("ALBION", Path.Combine(baseDir, "Albion")); - var baseAssetConfigPath = Path.Combine(baseDir, "mods", "Base", "base_assets.json"); + var typeConfigPath = Path.Combine(baseDir, "mods", "Base", "types.json"); var assetConfigPath = Path.Combine(baseDir, "mods", "Albion", "alb_assets.json"); - var baseAssetConfig = AssetConfig.Load(baseAssetConfigPath, null, mapping, disk, jsonUtil); - var assetConfig = AssetConfig.Load(assetConfigPath, baseAssetConfig, mapping, disk, jsonUtil); - foreach (var assetType in assetConfig.IdTypes.Values) + var tcl = new TypeConfigLoader(jsonUtil); + var typeConfig = tcl.Load(typeConfigPath, "RoundtripTests", null, mapping, disk); + + foreach (var assetType in typeConfig.IdTypes.Values) { var enumType = Type.GetType(assetType.EnumType); if (enumType == null) @@ -68,7 +71,15 @@ public RoundtripTests() return (disk, baseDir, pathResolver, mapping); } - static T RoundTrip(string testName, byte[] bytes, Asset.SerdesFunc serdes) where T : class + static T RoundTrip(string testName, byte[] bytes, Asset.SerdesFunc serdes, AssetId id, AssetNode node, string language = null) where T : class + { + node ??= new AssetNode(id); + var modContext = new ModContext("Test", JsonUtil, Disk, AssetMapping.Global); + var context = new AssetLoadContext(id, node, modContext, language); + return RoundTrip(testName, bytes, serdes, context); + } + + static T RoundTrip(string testName, byte[] bytes, Asset.SerdesFunc serdes, AssetLoadContext context) where T : class { TResult Wrap(Func func, string extension) { @@ -85,8 +96,6 @@ TResult Wrap(Func func, string extension) } } - var context = new SerdesContext("Test", JsonUtil, AssetMapping.Global, Disk); - var (asset, preTxt) = Wrap(() => Asset.Load(bytes, serdes, context), ".pre.ex.txt"); var (postBytes, postTxt) = Wrap(() => Asset.Save(asset, serdes, context), ".post.ex.txt"); var (_, reloadTxt) = Wrap(() => Asset.Load(postBytes, serdes, context), ".reload.ex.txt"); @@ -112,50 +121,74 @@ TResult Wrap(Func func, string extension) return asset; } - static void RoundTripXld(string testName, string file, int subId, Asset.SerdesFunc serdes) where T : class + static void RoundTripXld( + string testName, + string file, + AssetId firstId, + AssetId id, + Asset.SerdesFunc serdes, + Action propertySetter = null) where T : class + { + var node = new AssetNode(firstId); + if (propertySetter != null) + propertySetter(node); + + var modContext = new ModContext("Test", JsonUtil, Disk, AssetMapping.Global); + var context = new AssetLoadContext(id, node, modContext); + var bytes = Asset.BytesFromXld(PathResolver, file, context); + RoundTrip(testName, bytes, serdes, context); + } + + static void RoundTripRaw(string testName, string file, Asset.SerdesFunc serdes, string language) where T : class { - var info = new AssetInfo { Index = subId }; - var context = new SerdesContext("Test", JsonUtil, AssetMapping.Global, Disk); - var bytes = Asset.BytesFromXld(PathResolver, file, info, context); - RoundTrip(testName, bytes, serdes); + var bytes = File.ReadAllBytes(PathResolver.ResolvePath(file)); + RoundTrip(testName, bytes, serdes, AssetId.None, null, language); } - static void RoundTripRaw(string testName, string file, Asset.SerdesFunc serdes) where T : class + static void RoundTripRaw(string testName, string file, Asset.SerdesFunc serdes, AssetId id, AssetNode node) where T : class { var bytes = File.ReadAllBytes(PathResolver.ResolvePath(file)); - RoundTrip(testName, bytes, serdes); + RoundTrip(testName, bytes, serdes, id, node); } - static void RoundTripItem(string testName, string file, int subId, Asset.SerdesFunc serdes) where T : class + static void RoundTripItem(string testName, string file, ItemId id, Asset.SerdesFunc serdes) where T : class { - var info = new AssetInfo { Index = subId }; var loader = new ItemListContainer(); - var context = new SerdesContext("Test", JsonUtil, AssetMapping.Global, Disk); - using var s = loader.Read(PathResolver.ResolvePath(file), info, context); + var node = new AssetNode((ItemId)Item.Knife); + var modContext = new ModContext("Test", JsonUtil, Disk, AssetMapping.Global); + var context = new AssetLoadContext(id, node, modContext); + + using var s = loader.Read(PathResolver.ResolvePath(file), context); var bytes = s.Bytes(null, null, (int)s.BytesRemaining); - RoundTrip(testName, bytes, serdes); + RoundTrip(testName, bytes, serdes, context); } - static void RoundTripSpell(string testName, string file, int subId, Asset.SerdesFunc serdes) where T : class + static void RoundTripSpell(string testName, string file, SpellId id, Asset.SerdesFunc serdes) where T : class { - var info = new AssetInfo { Index = subId }; var loader = new SpellListContainer(); - var context = new SerdesContext("Test", JsonUtil, AssetMapping.Global, Disk); - using var s = loader.Read(PathResolver.ResolvePath(file), info, context); + var node = new AssetNode((SpellId)Spell.ThornSnare); + var modContext = new ModContext("Test", JsonUtil, Disk, AssetMapping.Global); + var context = new AssetLoadContext(id, node, modContext); + + using var s = loader.Read(PathResolver.ResolvePath(file), context); var bytes = s.Bytes(null, null, (int)s.BytesRemaining); - RoundTrip(testName, bytes, serdes); + RoundTrip(testName, bytes, serdes, context); } [Fact] public void ItemTest() { - var info = new AssetInfo { AssetId = AssetId.From(Item.Knife) }; - var spell = new SpellData(Spell.ThornSnare, SpellClass.DjiKas, 0) + var id = (ItemId)Item.DanusLight; + var spell = new SpellData(Spell.Lifebringer, SpellClass.DjiKantos, 2) { - Cost = 1, - Environments = SpellEnvironments.Combat, - LevelRequirement = 2, - Targets = SpellTargets.OneMonster, + Cost = 60, + Environments = SpellEnvironments.Indoors + | SpellEnvironments.Outdoors + | SpellEnvironments.Dungeon + | SpellEnvironments.Inventory, + + LevelRequirement = 13, + Targets = SpellTargets.DeadParty, }; var spellManager = new MockSpellManager().Add(spell); @@ -164,113 +197,141 @@ public void ItemTest() .Attach(spellManager) .Attach(itemDataLoader); - RoundTripItem(nameof(ItemTest), "$(ALBION)/CD/XLDLIBS/ITEMLIST.DAT", 10, - (x, s, c) => itemDataLoader.Serdes(x, info, s, c)); + RoundTripItem(nameof(ItemTest), + "$(ALBION)/CD/XLDLIBS/ITEMLIST.DAT", id, (x, s, c) => itemDataLoader.Serdes(x, s, c)); } [Fact] public void ItemNameTest() { - var info = new AssetInfo { AssetId = AssetId.From(Special.ItemNames) }; - RoundTripRaw(nameof(ItemNameTest), "$(ALBION)/CD/XLDLIBS/ITEMNAME.DAT", - (x, s, c) => Loaders.ItemNameLoader.Serdes(x, info, s, c)); + RoundTripRaw>(nameof(ItemNameTest), + "$(ALBION)/CD/XLDLIBS/ITEMNAME.DAT", + (x, s, c) => Loaders.ItemNameLoader.Serdes(x, s, c), + Language.English); } [Fact] public void AutomapTest() { - var info = new AssetInfo { AssetId = AssetId.From(Automap.Jirinaar) }; - RoundTripXld(nameof(AutomapTest), "$(ALBION)/CD/XLDLIBS/INITIAL/AUTOMAP1.XLD", 10, - (x, s, c) => Loaders.AutomapLoader.Serdes(x, info, s, c)); + RoundTripXld( + nameof(AutomapTest), + "$(ALBION)/CD/XLDLIBS/INITIAL/AUTOMAP1.XLD", + (AutomapId)(Automap)100, + (AutomapId)Automap.Jirinaar, + (x, s, c) => Loaders.AutomapLoader.Serdes(x, s, c)); } [Fact] public void BlockListTest() { - var info = new AssetInfo { AssetId = AssetId.From(BlockList.Toronto) }; - RoundTripXld(nameof(BlockListTest), "$(ALBION)/CD/XLDLIBS/BLKLIST0.XLD", 7, - (x, s, c) => Loaders.BlockListLoader.Serdes(x, info, s, c)); + RoundTripXld(nameof(BlockListTest), + "$(ALBION)/CD/XLDLIBS/BLKLIST0.XLD", + (BlockListId)(BlockList)1, + (BlockListId)BlockList.Toronto, + (x, s, c) => Loaders.BlockListLoader.Serdes(x, s, c)); } [Fact] public void ChestTest() { - var info = new AssetInfo { AssetId = AssetId.From(Chest.HClanCellar_ID_IKn_ILC_StC_LSh_3g) }; - RoundTripXld(nameof(ChestTest), "$(ALBION)/CD/XLDLIBS/INITIAL/CHESTDT1.XLD", 21, - (x, s, c) => Loaders.ChestLoader.Serdes(x, info, s, c)); + RoundTripXld(nameof(ChestTest), + "$(ALBION)/CD/XLDLIBS/INITIAL/CHESTDT1.XLD", + (ChestId)(Chest)100, + (ChestId)Chest.HClanCellar_ID_IKn_ILC_StC_LSh_3g, + (x, s, c) => Loaders.ChestLoader.Serdes(x, s, c)); } [Fact] public void CommonPaletteTest() { - var info = new AssetInfo { AssetId = AssetId.From(Palette.Common) }; - info.Set(AssetProperty.IsCommon, true); - RoundTripRaw(nameof(CommonPaletteTest), "$(ALBION)/CD/XLDLIBS/PALETTE.000", - (x, s, c) => Loaders.PaletteLoader.Serdes(x, info, s, c)); + var node = new AssetNode((PaletteId)Palette.Common); + node.SetProperty(PaletteLoader.IsCommon, true); + + RoundTripRaw(nameof(CommonPaletteTest), + "$(ALBION)/CD/XLDLIBS/PALETTE.000", + (x, s, c) => Loaders.PaletteLoader.Serdes(x, s, c), + (PaletteId)Palette.Common, + node); } [Fact] public void EventSetTest() { - var info = new AssetInfo { AssetId = AssetId.From(EventSet.Frill) }; - RoundTripXld(nameof(EventSetTest), "$(ALBION)/CD/XLDLIBS/EVNTSET1.XLD", 11, - (x, s, c) => Loaders.EventSetLoader.Serdes(x, info, s, c)); + RoundTripXld(nameof(EventSetTest), + "$(ALBION)/CD/XLDLIBS/EVNTSET1.XLD", + (EventSetId)(EventSet)100, + (EventSetId)EventSet.Frill, + (x, s, c) => Loaders.EventSetLoader.Serdes(x, s, c)); } [Fact] public void EventTextTest() { - var info = new AssetInfo { AssetId = AssetId.From(EventText.Frill) }; - RoundTripXld(nameof(EventTextTest), "$(ALBION)/CD/XLDLIBS/ENGLISH/EVNTTXT1.XLD", 11, - (x, s, c) => Loaders.AlbionStringTableLoader.Serdes(x, info, s, c)); + RoundTripXld(nameof(EventTextTest), + "$(ALBION)/CD/XLDLIBS/ENGLISH/EVNTTXT1.XLD", + (StringSetId)(EventText)100, + (StringSetId)EventText.Frill, + (x, s, c) => Loaders.AlbionStringTableLoader.Serdes(x, s, c)); } [Fact] public void LabyrinthTest() { - var info = new AssetInfo { AssetId = AssetId.From(Labyrinth.Jirinaar) }; - RoundTripXld(nameof(LabyrinthTest), "$(ALBION)/CD/XLDLIBS/LABDATA1.XLD", 9, - (x, s, c) => Loaders.LabyrinthDataLoader.Serdes(x, info, s, c)); + RoundTripXld(nameof(LabyrinthTest), + "$(ALBION)/CD/XLDLIBS/LABDATA1.XLD", + (LabyrinthId)(Labyrinth)100, + (LabyrinthId)Labyrinth.Jirinaar, + (x, s, c) => Loaders.LabyrinthDataLoader.Serdes(x, s, c)); } [Fact] public void Map2DTest() { - var info = new AssetInfo { AssetId = AssetId.From(Map.TorontoBegin) }; - RoundTripXld(nameof(Map2DTest), "$(ALBION)/CD/XLDLIBS/MAPDATA3.XLD", 0, - (x, s, c) => MapData2D.Serdes(info, x, c.Mapping, s)); + RoundTripXld(nameof(Map2DTest), + "$(ALBION)/CD/XLDLIBS/MAPDATA3.XLD", + (MapId)(Map)300, + (MapId)Map.TorontoBegin, + (x, s, c) => (MapData2D)Loaders.MapLoader.Serdes(x, s, c)); } [Fact] public void Map3DTest() { - var info = new AssetInfo { AssetId = AssetId.From(Map.OldFormerBuilding) }; - RoundTripXld(nameof(Map3DTest), "$(ALBION)/CD/XLDLIBS/MAPDATA1.XLD", 22, - (x, s, c) => MapData3D.Serdes(info, x, c.Mapping, s)); + RoundTripXld(nameof(Map3DTest), + "$(ALBION)/CD/XLDLIBS/MAPDATA1.XLD", + (MapId)(Map)100, + (MapId)Map.OldFormerBuilding, + (x, s, c) => (MapData3D)Loaders.MapLoader.Serdes(x, s, c)); } [Fact] public void MapTextTest() { - var info = new AssetInfo { AssetId = AssetId.From(MapText.TorontoBegin) }; - RoundTripXld(nameof(MapTextTest), "$(ALBION)/CD/XLDLIBS/ENGLISH/MAPTEXT3.XLD", 0, - (x, s, c) => Loaders.AlbionStringTableLoader.Serdes(x, info, s, c)); + RoundTripXld(nameof(MapTextTest), + "$(ALBION)/CD/XLDLIBS/ENGLISH/MAPTEXT3.XLD", + (StringSetId)(MapText)300, + (StringSetId)MapText.TorontoBegin, + (x, s, c) => Loaders.AlbionStringTableLoader.Serdes(x, s, c)); } [Fact] public void MerchantTest() { - var info = new AssetInfo { AssetId = AssetId.From(Merchant.AltheaSpells) }; - RoundTripXld(nameof(MerchantTest), "$(ALBION)/CD/XLDLIBS/INITIAL/MERCHDT1.XLD", 9, - (x, s, c) => Loaders.MerchantLoader.Serdes(x, info, s, c)); + RoundTripXld(nameof(MerchantTest), + "$(ALBION)/CD/XLDLIBS/INITIAL/MERCHDT1.XLD", + (MerchantId)(Merchant)100, + (MerchantId)Merchant.AltheaSpells, + (x, s, c) => Loaders.MerchantLoader.Serdes(x, s, c)); } [Fact] public void MonsterGroupTest() { - var info = new AssetInfo { AssetId = AssetId.From(MonsterGroup.TwoSkrinn1OneKrondir1) }; - RoundTripXld(nameof(MonsterGroupTest), "$(ALBION)/CD/XLDLIBS/MONGRP0.XLD", 9, - (x, s, c) => Loaders.MonsterGroupLoader.Serdes(x, info, s, c)); + RoundTripXld(nameof(MonsterGroupTest), + "$(ALBION)/CD/XLDLIBS/MONGRP0.XLD", + (MonsterGroupId)(MonsterGroup)1, + (MonsterGroupId)MonsterGroup.TwoSkrinn1OneKrondir1, + (x, s, c) => Loaders.MonsterGroupLoader.Serdes(x, s, c)); } static SpellData BuildMockSpell(SpellId id, SpellClass school, byte number) => new(id, school, number) @@ -303,44 +364,54 @@ static CharacterSheetLoader BuildCharacterLoader() [Fact] public void MonsterTest() { - var info = new AssetInfo { AssetId = AssetId.From(MonsterSheet.Krondir1) }; var loader = BuildCharacterLoader(); - RoundTripXld(nameof(MonsterTest), "$(ALBION)/CD/XLDLIBS/MONCHAR0.XLD", 9, - (x, s, c) => loader.Serdes(x, info, s, c)); + RoundTripXld(nameof(MonsterTest), + "$(ALBION)/CD/XLDLIBS/MONCHAR0.XLD", + (SheetId)(MonsterSheet)1, + (SheetId)MonsterSheet.Krondir1, + (x, s, c) => loader.Serdes(x, s, c)); } [Fact] public void NpcTest() { - var info = new AssetInfo { AssetId = AssetId.From(NpcSheet.Christine) }; var loader = BuildCharacterLoader(); - RoundTripXld(nameof(NpcTest), "$(ALBION)/CD/XLDLIBS/INITIAL/NPCCHAR1.XLD", 83, - (x, s, c) => loader.Serdes(x, info, s, c)); + RoundTripXld(nameof(NpcTest), + "$(ALBION)/CD/XLDLIBS/INITIAL/NPCCHAR1.XLD", + (SheetId)(NpcSheet)100, + (SheetId)NpcSheet.Christine, + (x, s, c) => loader.Serdes(x, s, c)); } [Fact] public void PaletteTest() { - var info = new AssetInfo { AssetId = AssetId.From(Palette.Toronto2D) }; - RoundTripXld(nameof(PaletteTest), "$(ALBION)/CD/XLDLIBS/PALETTE0.XLD", 25, - (x, s, c) => Loaders.PaletteLoader.Serdes(x, info, s, c)); + RoundTripXld(nameof(PaletteTest), + "$(ALBION)/CD/XLDLIBS/PALETTE0.XLD", + (PaletteId)(Palette)1, + (PaletteId)Palette.Toronto2D, + (x, s, c) => Loaders.PaletteLoader.Serdes(x, s, c)); } [Fact] public void PartyMemberTest() { - var info = new AssetInfo { AssetId = AssetId.From(PartySheet.Tom) }; var loader = BuildCharacterLoader(); - RoundTripXld(nameof(PartyMemberTest), "$(ALBION)/CD/XLDLIBS/INITIAL/PRTCHAR0.XLD", 0, - (x, s, c) => loader.Serdes(x, info, s, c)); + RoundTripXld(nameof(PartyMemberTest), + "$(ALBION)/CD/XLDLIBS/INITIAL/PRTCHAR0.XLD", + (SheetId)(PartySheet)1, + (SheetId)PartySheet.Tom, + (x, s, c) => loader.Serdes(x, s, c)); } [Fact] public void SampleTest() { - var info = new AssetInfo { AssetId = AssetId.From(Sample.IllTemperedLlama) }; - RoundTripXld(nameof(SampleTest), "$(ALBION)/CD/XLDLIBS/SAMPLES0.XLD", 47, - (x, s, c) => Loaders.SampleLoader.Serdes(x, info, s, c)); + RoundTripXld(nameof(SampleTest), + "$(ALBION)/CD/XLDLIBS/SAMPLES0.XLD", + (SampleId)(Sample)1, + (SampleId)Sample.IllTemperedLlama, + (x, s, c) => Loaders.SampleLoader.Serdes(x, s, c)); } /* They're text anyway so not too bothered - at the moment they don't round trip due to using friendly asset id names @@ -349,59 +420,70 @@ public void SampleTest() public void ScriptTest() { var info = new AssetInfo { AssetId = AssetId.From(Script.TomMeetsChristine) }; - RoundTripXld>(nameof(ScriptTest), "$(ALBION)/CD/XLDLIBS/SCRIPT0.XLD", 1, - (x, s, c) => Loaders.ScriptLoader.Serdes(x, info, s, c)); + RoundTripXld>(nameof(ScriptTest), + "$(ALBION)/CD/XLDLIBS/SCRIPT0.XLD", 1, + (x, s, c) => Loaders.ScriptLoader.Serdes(x, s, c)); } //*/ [Fact] public void SongTest() { - var info = new AssetInfo { AssetId = AssetId.From(Song.Toronto) }; - RoundTripXld(nameof(SongTest), "$(ALBION)/CD/XLDLIBS/SONGS0.XLD", 3, - (x, s, c) => Loaders.SongLoader.Serdes(x, info, s, c)); + RoundTripXld(nameof(SongTest), + "$(ALBION)/CD/XLDLIBS/SONGS0.XLD", + (SongId)(Song)1, + (SongId)Song.Toronto, + (x, s, c) => Loaders.SongLoader.Serdes(x, s, c)); } [Fact] public void SpellTest() { - var info = new AssetInfo { AssetId = AssetId.From(Spell.FrostAvalanche) }; - RoundTripSpell(nameof(SpellTest), "$(ALBION)/CD/XLDLIBS/SPELLDAT.DAT", 7, - (x, s, c) => Loaders.SpellLoader.Serdes(x, info, s, c)); + RoundTripSpell(nameof(SpellTest), + "$(ALBION)/CD/XLDLIBS/SPELLDAT.DAT", + Spell.FrostAvalanche, + (x, s, c) => Loaders.SpellLoader.Serdes(x, s, c)); } [Fact] public void TilesetTest() { - var info = new AssetInfo { AssetId = AssetId.From(Tileset.Toronto) }; - RoundTripXld(nameof(TilesetTest), "$(ALBION)/CD/XLDLIBS/ICONDAT0.XLD", 7, - (x, s, c) => Loaders.TilesetLoader.Serdes(x, info, s, c)); + RoundTripXld(nameof(TilesetTest), + "$(ALBION)/CD/XLDLIBS/ICONDAT0.XLD", + (TilesetId)(Tileset)1, + (TilesetId)Tileset.Toronto, + (x, s, c) => Loaders.TilesetLoader.Serdes(x, s, c)); } [Fact] public void TiledTilesetTest() { - var info = new AssetInfo { AssetId = AssetId.From(Tileset.Toronto), Index = 7 }; - var gfxInfo = new AssetInfo {AssetId = AssetId.From(TilesetGfx.Toronto), Index = 7}; - gfxInfo.Set(AssetProperty.PaletteId, 26); + var tilesetId = (TilesetId)Tileset.Toronto; + + var gfxId = (SpriteId)TilesetGfx.Toronto; + var gfxNode = new AssetNode((SpriteId)(TilesetGfx)1); + gfxNode.SetProperty(AssetProps.Palette, (PaletteId)Palette.Toronto2D); var modApplier = new MockModApplier(); - modApplier.AddInfo(gfxInfo.AssetId, gfxInfo); + modApplier.AddInfo(gfxId, gfxNode); + var exchange = new EventExchange() .Attach(modApplier) .Attach(new AssetManager()); - var context = new SerdesContext("Test", JsonUtil, AssetMapping.Global, Disk); - var bytes = Asset.BytesFromXld(PathResolver, "$(ALBION)/CD/XLDLIBS/ICONDAT0.XLD", info, context); + var modContext = new ModContext("Test", JsonUtil, Disk, AssetMapping.Global); + var context = new AssetLoadContext(tilesetId, new AssetNode((TilesetId)(Tileset)1), modContext); - TilesetData Serdes(TilesetData x, ISerializer s, SerdesContext context) => Loaders.TilesetLoader.Serdes(x, info, s, context); + var bytes = Asset.BytesFromXld(PathResolver, "$(ALBION)/CD/XLDLIBS/ICONDAT0.XLD", context); + + TilesetData Serdes(TilesetData x, ISerializer s, AssetLoadContext c) => Loaders.TilesetLoader.Serdes(x, s, c); var (asset, preTxt) = Asset.Load(bytes, Serdes, context); var loader = new TiledTilesetLoader(); exchange.Attach(loader); - var (tiledBytes, tiledTxt) = Asset.Save(asset, (x, s, c) => loader.Serdes(x, info, s, c), context); + var (tiledBytes, tiledTxt) = Asset.Save(asset, (x, s, c) => loader.Serdes(x, s, c), context); var (fromTiled, _) = Asset.Load(tiledBytes, - (x, s, c) => loader.Serdes(x, info, s, c), context); + (x, s, c) => loader.Serdes(x, s, c), context); var (roundTripped, roundTripTxt) = Asset.Save(fromTiled, Serdes, context); Asset.Compare(ResultDir, @@ -414,18 +496,19 @@ public void TiledTilesetTest() [Fact] public void TiledStampTest() { - var info = new AssetInfo { AssetId = AssetId.From(BlockList.Toronto), Index = 7 }; - var context = new SerdesContext("Test", JsonUtil, AssetMapping.Global, Disk); - var bytes = Asset.BytesFromXld(PathResolver, "$(ALBION)/CD/XLDLIBS/BLKLIST0.XLD", info, context); + var id = (BlockListId)BlockList.Toronto; + var modContext = new ModContext("Test", JsonUtil, Disk, AssetMapping.Global); + var context = new AssetLoadContext(id, new AssetNode((BlockListId)(BlockList)1), modContext); + var bytes = Asset.BytesFromXld(PathResolver, "$(ALBION)/CD/XLDLIBS/BLKLIST0.XLD", context); - Formats.Assets.BlockList Serdes(Formats.Assets.BlockList x, ISerializer s, SerdesContext c2) => Loaders.BlockListLoader.Serdes(x, info, s, c2); + Formats.Assets.BlockList Serdes(Formats.Assets.BlockList x, ISerializer s, AssetLoadContext c2) => Loaders.BlockListLoader.Serdes(x, s, c2); var (asset, preTxt) = Asset.Load(bytes, Serdes, context); var loader = new StampLoader(); - var (tiledBytes, tiledTxt) = Asset.Save(asset, (x, s, c) => loader.Serdes(x, info, s, c), context); + var (tiledBytes, tiledTxt) = Asset.Save(asset, (x, s, c) => loader.Serdes(x, s, c), context); var (fromTiled, _) = Asset.Load(tiledBytes, - (x, s, c) => loader.Serdes(x, info, s, c), + (x, s, c) => loader.Serdes(x, s, c), context); var (roundTripped, roundTripTxt) = Asset.Save(fromTiled, Serdes, context); @@ -444,26 +527,32 @@ public void TiledMap2dTest() [Fact] public void WaveLibTest() { - var info = new AssetInfo { AssetId = AssetId.From(WaveLibrary.Unknown5) }; - RoundTripXld(nameof(WaveLibTest), "$(ALBION)/CD/XLDLIBS/WAVELIB0.XLD", 4, - (x, s, c) => Loaders.WaveLibLoader.Serdes(x, info, s, c)); + RoundTripXld(nameof(WaveLibTest), + "$(ALBION)/CD/XLDLIBS/WAVELIB0.XLD", + (WaveLibraryId)(WaveLibrary)1, + (WaveLibraryId)WaveLibrary.Unknown5, + (x, s, c) => Loaders.WaveLibLoader.Serdes(x, s, c)); } [Fact] public void WordTest() { - var info = new AssetInfo { AssetId = AssetId.From(Special.Words1) }; - RoundTripXld(nameof(WordTest), "$(ALBION)/CD/XLDLIBS/ENGLISH/WORDLIS0.XLD", 0, - (x, s, c) => Loaders.WordListLoader.Serdes(x, info, s, c)); + RoundTripXld(nameof(WordTest), + "$(ALBION)/CD/XLDLIBS/ENGLISH/WORDLIS0.XLD", + (SpecialId)Special.Words1, + (SpecialId)Special.Words1, + (x, s, c) => Loaders.WordListLoader.Serdes(x, s, c)); } //* [Fact] public void AutomapGfxTest() { - var info = new AssetInfo { AssetId = AssetId.From(AutomapTiles.Set1) }; - info.Set(AssetProperty.SubSprites, "(8,8,576) (16,16)"); - RoundTripXld>(nameof(AutomapGfxTest), "$(ALBION)/CD/XLDLIBS/AUTOGFX0.XLD", 0, - (x, s, c) => Loaders.AmorphousSpriteLoader.Serdes(x, info, s, c)); + RoundTripXld>(nameof(AutomapGfxTest), + "$(ALBION)/CD/XLDLIBS/AUTOGFX0.XLD", + (SpriteId)(AutomapTiles)1, + (SpriteId)AutomapTiles.Set1, + (x, s, c) => Loaders.AmorphousSpriteLoader.Serdes(x, s, c), + node => node.SetProperty(AmorphousSpriteLoader.SubSpritesProperty, "(8,8,576) (16,16)")); } [Fact] @@ -481,12 +570,16 @@ public void AmorphousTest() 7, 8, 9, 10 }; - var info = new AssetInfo(); - info.Set(AssetProperty.SubSprites, "(1,1,3) (2,2)"); + + var id = (SpriteId)AutomapTiles.Set1; + var node = new AssetNode((SpriteId)(AutomapTiles)1); + node.SetProperty(AmorphousSpriteLoader.SubSpritesProperty, "(1,1,3) (2,2)"); + var sprite = RoundTrip>( nameof(AmorphousTest), bytes, - (x, s, c) => Loaders.AmorphousSpriteLoader.Serdes(x, info, s, c)); + (x, s, c) => Loaders.AmorphousSpriteLoader.Serdes(x, s, c), + id, node); Assert.Equal(2, sprite.Width); Assert.Equal(7, sprite.Height); @@ -504,158 +597,189 @@ public void AmorphousTest() [Fact] public void CombatBgTest() { - var info = new AssetInfo - { - AssetId = AssetId.From(CombatBackground.Toronto), - Width = 360 - }; - RoundTripXld>(nameof(CombatBgTest), "$(ALBION)/CD/XLDLIBS/COMBACK0.XLD", 0, - (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, info, s, c)); + RoundTripXld>(nameof(CombatBgTest), + "$(ALBION)/CD/XLDLIBS/COMBACK0.XLD", + (SpriteId)(CombatBackground)1, + (SpriteId)CombatBackground.Toronto, + (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, s, c), + node => node.SetProperty(AssetProps.Width, 360)); } [Fact] public void DungeonObjectTest() { - var info = new AssetInfo - { - AssetId = AssetId.From(DungeonObject.Krondir), - Width = 145, - Height = 165 - }; - RoundTripXld>(nameof(DungeonObjectTest), "$(ALBION)/CD/XLDLIBS/3DOBJEC2.XLD", 81, - (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, info, s, c)); + RoundTripXld>(nameof(DungeonObjectTest), + "$(ALBION)/CD/XLDLIBS/3DOBJEC2.XLD", + (SpriteId)(DungeonObject)200, + (SpriteId)DungeonObject.Krondir, + (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, s, c), + node => + { + node.SetProperty(AssetProps.Width, 145); + node.SetProperty(AssetProps.Height, 165); + }); } [Fact] public void FontGfxTest() { - var info = new AssetInfo { AssetId = AssetId.From(FontGfx.Regular), Width = 8, Height = 8 }; - RoundTripXld>(nameof(FontGfxTest), "$(ALBION)/CD/XLDLIBS/FONTS0.XLD", 0, - (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, info, s, c)); + RoundTripXld>(nameof(FontGfxTest), + "$(ALBION)/CD/XLDLIBS/FONTS0.XLD", + (SpriteId)(FontGfx)1, + (SpriteId)FontGfx.Regular, + (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, s, c), + node => + { + node.SetProperty(AssetProps.Width, 8); + node.SetProperty(AssetProps.Height, 8); + }); } [Fact] public void ItemSpriteTest() { - var info = new AssetInfo - { - AssetId = AssetId.From(ItemGfx.ItemSprites), - Width = 16, - Height = 16 - }; - RoundTripRaw>(nameof(ItemSpriteTest), "$(ALBION)/CD/XLDLIBS/ITEMGFX", - (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, info, s, c)); + var id = (SpriteId)ItemGfx.ItemSprites; + var node = new AssetNode((SpriteId)(ItemGfx)1); + node.SetProperty(AssetProps.Width, 16); + node.SetProperty(AssetProps.Height, 16); + + RoundTripRaw>(nameof(ItemSpriteTest), + "$(ALBION)/CD/XLDLIBS/ITEMGFX", + (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, s, c), + id, node); } [Fact] public void SlabTest() { - var info = new AssetInfo { AssetId = AssetId.From(UiBackground.Slab), Width = 360 }; - RoundTripRaw>(nameof(SlabTest), "$(ALBION)/CD/XLDLIBS/SLAB", - (x, s, c) => Loaders.SlabLoader.Serdes(x, info, s, c)); + var id = (SpriteId)UiBackground.Slab; + var node = new AssetNode((SpriteId)UiBackground.Slab); + node.SetProperty(AssetProps.Width, 360); + RoundTripRaw>(nameof(SlabTest), + "$(ALBION)/CD/XLDLIBS/SLAB", + (x, s, c) => Loaders.SlabLoader.Serdes(x, s, c), + id, node); } [Fact] public void TileGfxTest() { - var info = new AssetInfo - { - AssetId = AssetId.From(TilesetGfx.Toronto), - Width = 16, - Height = 16 - }; - RoundTripXld>(nameof(TileGfxTest), "$(ALBION)/CD/XLDLIBS/ICONGFX0.XLD", 7, - (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, info, s, c)); + RoundTripXld>(nameof(TileGfxTest), + "$(ALBION)/CD/XLDLIBS/ICONGFX0.XLD", + (TilesetGfxId)(TilesetGfx)1, + (TilesetGfxId)TilesetGfx.Toronto, + (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, s, c), + node => + { + node.SetProperty(AssetProps.Width, 16); + node.SetProperty(AssetProps.Height, 16); + }); } [Fact] public void CombatGfxTest() { - var info = new AssetInfo { AssetId = AssetId.From(CombatGfx.SplashYellow) }; - RoundTripXld>(nameof(CombatGfxTest), "$(ALBION)/CD/XLDLIBS/COMGFX0.XLD", 26, - (x, s, c) => Loaders.MultiHeaderSpriteLoader.Serdes(x, info, s, c)); + RoundTripXld>(nameof(CombatGfxTest), + "$(ALBION)/CD/XLDLIBS/COMGFX0.XLD", + (SpriteId)(CombatGfx)1, + (SpriteId)CombatGfx.SplashYellow, + (x, s, c) => Loaders.MultiHeaderSpriteLoader.Serdes(x, s, c)); } [Fact] public void DungeonBgTest() { - var info = new AssetInfo { AssetId = AssetId.From(DungeonBackground.EarlyGameL) }; - RoundTripXld>(nameof(DungeonBgTest), "$(ALBION)/CD/XLDLIBS/3DBCKGR0.XLD", 0, - (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, info, s, c)); + RoundTripXld>(nameof(DungeonBgTest), + "$(ALBION)/CD/XLDLIBS/3DBCKGR0.XLD", + (SpriteId)(DungeonBackground)1, + (SpriteId)DungeonBackground.EarlyGameL, + (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, s, c)); } [Fact] public void FloorTest() { - var info = new AssetInfo - { - AssetId = AssetId.From(Floor.Water), - Width = 64, - Height = 64 - }; - RoundTripXld>(nameof(FloorTest), "$(ALBION)/CD/XLDLIBS/3DFLOOR0.XLD", 2, - (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, info, s, c)); + RoundTripXld>(nameof(FloorTest), + "$(ALBION)/CD/XLDLIBS/3DFLOOR0.XLD", + (SpriteId)(Floor)1, + (SpriteId)Floor.Water, + (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, s, c), + node => + { + node.SetProperty(AssetProps.Width, 64); + node.SetProperty(AssetProps.Height, 64); + }); } [Fact] public void FullBodyPictureTest() { - var info = new AssetInfo { AssetId = AssetId.From(PartyInventoryGfx.Tom) }; - RoundTripXld>(nameof(FullBodyPictureTest), "$(ALBION)/CD/XLDLIBS/FBODPIX0.XLD", 0, - (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, info, s, c)); + RoundTripXld>(nameof(FullBodyPictureTest), + "$(ALBION)/CD/XLDLIBS/FBODPIX0.XLD", + (SpriteId)(PartyInventoryGfx)1, + (SpriteId)PartyInventoryGfx.Tom, + (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, s, c)); } [Fact] public void LargeNpcTest() { - var info = new AssetInfo { AssetId = AssetId.From(NpcLargeGfx.Christine) }; - RoundTripXld>(nameof(LargeNpcTest), "$(ALBION)/CD/XLDLIBS/NPCGR0.XLD", 20, - (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, info, s, c)); + RoundTripXld>(nameof(LargeNpcTest), + "$(ALBION)/CD/XLDLIBS/NPCGR0.XLD", + (SpriteId)(NpcLargeGfx)1, + (SpriteId)NpcLargeGfx.Christine, + (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, s, c)); } [Fact] public void LargePartyMemberTest() { - var info = new AssetInfo { AssetId = AssetId.From(PartyLargeGfx.Tom) }; - RoundTripXld>(nameof(LargePartyMemberTest), "$(ALBION)/CD/XLDLIBS/PARTGR0.XLD", 0, - (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, info, s, c)); + RoundTripXld>(nameof(LargePartyMemberTest), + "$(ALBION)/CD/XLDLIBS/PARTGR0.XLD", + (SpriteId)(PartyLargeGfx)1, + (SpriteId)PartyLargeGfx.Tom, + (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, s, c)); } [Fact] public void MonsterGfxTest() { - var info = new AssetInfo { AssetId = AssetId.From(MonsterGfx.Krondir) }; - RoundTripXld>(nameof(MonsterGfxTest), "$(ALBION)/CD/XLDLIBS/MONGFX0.XLD", 9, - (x, s, c) => Loaders.MultiHeaderSpriteLoader.Serdes(x, info, s, c)); + RoundTripXld>(nameof(MonsterGfxTest), + "$(ALBION)/CD/XLDLIBS/MONGFX0.XLD", + (SpriteId)(MonsterGfx)1, + (SpriteId)MonsterGfx.Krondir, + (x, s, c) => Loaders.MultiHeaderSpriteLoader.Serdes(x, s, c)); } [Fact] public void OverlayTest() { - var info = new AssetInfo - { - AssetId = AssetId.From(WallOverlay.JiriFrameL), - Width = 112, - File = new AssetFileInfo() - }; - info.File.Set(AssetProperty.Transposed, true); - RoundTripXld>(nameof(OverlayTest), "$(ALBION)/CD/XLDLIBS/3DOVERL0.XLD", 17, - (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, info, s, c)); + RoundTripXld>(nameof(OverlayTest), + "$(ALBION)/CD/XLDLIBS/3DOVERL0.XLD", + (SpriteId)(WallOverlay)1, + (SpriteId)WallOverlay.JiriFrameL, + (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, s, c), + node => + { + node.SetProperty(AssetProps.Width, 112); + node.SetProperty(FixedSizeSpriteLoader.TransposedProperty, true); + }); } [Fact] public void OverlayMultiFrameTest() { - var info = new AssetInfo - { - AssetId = AssetId.From(WallOverlay.Unknown201), - Width = 6, - Height = 20, - File = new AssetFileInfo() - }; - info.File.Set(AssetProperty.Transposed, true); - RoundTripXld>(nameof(OverlayTest), "$(ALBION)/CD/XLDLIBS/3DOVERL2.XLD", 1, - (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, info, s, c)); + RoundTripXld>(nameof(OverlayTest), + "$(ALBION)/CD/XLDLIBS/3DOVERL2.XLD", + (SpriteId)(WallOverlay)200, + (SpriteId)WallOverlay.Unknown201, + (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, s, c), + node => + { + node.SetProperty(AssetProps.Width, 6); + node.SetProperty(AssetProps.Height, 20); + node.SetProperty(FixedSizeSpriteLoader.TransposedProperty, true); + }); } // 201 @@ -664,61 +788,64 @@ public void OverlayMultiFrameTest() [Fact] public void PictureTest() { - var info = new AssetInfo { AssetId = AssetId.From(Picture.OpenChestWithGold) }; - RoundTripXld(nameof(PictureTest), "$(ALBION)/CD/XLDLIBS/PICTURE0.XLD", 11, - (x, s, c) => Loaders.InterlacedBitmapLoader.Serdes(x, info, s, c)); + RoundTripXld(nameof(PictureTest), + "$(ALBION)/CD/XLDLIBS/PICTURE0.XLD", + (PictureId)(Picture)1, + (PictureId)Picture.OpenChestWithGold, + (x, s, c) => Loaders.InterlacedBitmapLoader.Serdes(x, s, c)); } //*/ [Fact] public void PortraitTest() { - var info = new AssetInfo - { - AssetId = AssetId.From(Portrait.Tom), - Width = 34 - }; - RoundTripXld>(nameof(PortraitTest), "$(ALBION)/CD/XLDLIBS/SMLPORT0.XLD", 0, - (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, info, s, c)); + RoundTripXld>(nameof(PortraitTest), + "$(ALBION)/CD/XLDLIBS/SMLPORT0.XLD", + (PortraitId)(Portrait)1, + (PortraitId)Portrait.Tom, + (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, s, c), + node => node.SetProperty(AssetProps.Width, 34)); } [Fact] public void SmallNpcTest() { - var info = new AssetInfo { AssetId = AssetId.From(NpcSmallGfx.Krondir) }; - RoundTripXld>(nameof(SmallNpcTest), "$(ALBION)/CD/XLDLIBS/NPCKL0.XLD", 22, - (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, info, s, c)); + RoundTripXld>(nameof(SmallNpcTest), + "$(ALBION)/CD/XLDLIBS/NPCKL0.XLD", + (SpriteId)(NpcSmallGfx)1, + (SpriteId)NpcSmallGfx.Krondir, + (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, s, c)); } [Fact] public void SmallPartyMemberTest() { - var info = new AssetInfo { AssetId = AssetId.From(PartySmallGfx.Tom) }; - RoundTripXld>(nameof(SmallPartyMemberTest), "$(ALBION)/CD/XLDLIBS/PARTKL0.XLD", 0, - (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, info, s, c)); + RoundTripXld>(nameof(SmallPartyMemberTest), + "$(ALBION)/CD/XLDLIBS/PARTKL0.XLD", + (SpriteId)(PartySmallGfx)1, + (SpriteId)PartySmallGfx.Tom, + (x, s, c) => Loaders.SingleHeaderSpriteLoader.Serdes(x, s, c)); } [Fact] public void TacticalGfxTest() { - var info = new AssetInfo - { - AssetId = AssetId.From(TacticalGfx.Tom), - Width = 32 - }; - RoundTripXld>(nameof(TacticalGfxTest), "$(ALBION)/CD/XLDLIBS/TACTICO0.XLD", 0, - (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, info, s, c)); + RoundTripXld>(nameof(TacticalGfxTest), + "$(ALBION)/CD/XLDLIBS/TACTICO0.XLD", + (SpriteId)(TacticalGfx)1, + (SpriteId)TacticalGfx.Tom, + (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, s, c), + node => node.SetProperty(AssetProps.Width, 32)); } [Fact] public void WallTest() { - var info = new AssetInfo - { - AssetId = AssetId.From(Wall.TorontoPanelling), - Width = 80 - }; - RoundTripXld>(nameof(WallTest), "$(ALBION)/CD/XLDLIBS/3DWALLS0.XLD", 11, - (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, info, s, c)); + RoundTripXld>(nameof(WallTest), + "$(ALBION)/CD/XLDLIBS/3DWALLS0.XLD", + (SpriteId)(Wall)1, + (SpriteId)Wall.TorontoPanelling, + (x, s, c) => Loaders.FixedSizeSpriteLoader.Serdes(x, s, c), + node => node.SetProperty(AssetProps.Width, 80)); } // */ -} \ No newline at end of file +} diff --git a/src/Tests/UAlbion.Config.Tests/AssetConfigTests.cs b/src/Tests/UAlbion.Config.Tests/AssetConfigTests.cs index 28eca60ff..d0513d788 100644 --- a/src/Tests/UAlbion.Config.Tests/AssetConfigTests.cs +++ b/src/Tests/UAlbion.Config.Tests/AssetConfigTests.cs @@ -1,7 +1,14 @@ -using System.ComponentModel; +using System; +using System.ComponentModel; using System.Linq; using System.Text; using UAlbion.Api; +using UAlbion.Config.Properties; +using UAlbion.Formats.Assets; +using UAlbion.Formats.Containers; +using UAlbion.Formats.Parsers; +using UAlbion.Game.Assets; +using UAlbion.TestCommon; using Xunit; // ReSharper disable ParameterOnlyUsedForPreconditionCheck.Local @@ -9,173 +16,251 @@ namespace UAlbion.Config.Tests; public class AssetConfigTests : Component { - const string TestConfig1 = @"{ + static readonly IJsonUtil JsonUtil = new JsonUtil(); + + // TODO: Add MapFile and Map tests + + const string TypeConfig1 = @" +{ ""IdTypes"": { - ""3dobj"": { ""AssetType"": ""Object3D"", ""EnumType"": ""UAlbion.Base.DungeonObject, UAlbion.Base"" }, - ""autotile"": { ""AssetType"": ""AutomapGfx"", ""EnumType"": ""UAlbion.Base.AutomapTiles, UAlbion.Base"" }, - ""block"": { - ""AssetType"": ""BlockList"", - ""EnumType"": ""UAlbion.Base.BlockList, UAlbion.Base"", - ""CopiedFrom"": ""UAlbion.Base.Tileset, UAlbion.Base"" - }, - ""combg"": { ""AssetType"": ""CombatBackground"", ""EnumType"": ""UAlbion.Base.CombatBackground, UAlbion.Base"" }, - ""comgfx"": { ""AssetType"": ""CombatGfx"", ""EnumType"": ""UAlbion.Base.CombatGfx, UAlbion.Base"" }, - ""coresprite"": { ""AssetType"": ""CoreGfx"", ""EnumType"": ""UAlbion.Base.CoreSprite, UAlbion.Base"" }, - ""floor"": { ""AssetType"": ""Floor"", ""EnumType"": ""UAlbion.Base.Floor, UAlbion.Base"" }, - ""font"": { ""AssetType"": ""FontDefinition"", ""EnumType"": ""UAlbion.Base.Font, UAlbion.Base"" }, - ""fontgfx"": { ""AssetType"": ""FontGfx"", ""EnumType"": ""UAlbion.Base.FontGfx, UAlbion.Base"" }, - ""item"": { ""AssetType"": ""Item"", ""EnumType"": ""UAlbion.Base.Item, UAlbion.Base"" }, - ""overlay"": { ""AssetType"": ""WallOverlay"", ""EnumType"": ""UAlbion.Base.WallOverlay, UAlbion.Base"" }, - ""pal"": { ""AssetType"": ""Palette"", ""EnumType"": ""UAlbion.Base.Palette, UAlbion.Base"" }, - ""special"": { ""AssetType"": ""Special"", ""EnumType"": ""UAlbion.Base.Special, UAlbion.Base"" }, - ""spell"": { ""AssetType"": ""Spell"", ""EnumType"": ""UAlbion.Base.Spell, UAlbion.Base"" }, - ""tiledata"": { ""AssetType"": ""Tileset"", ""EnumType"": ""UAlbion.Base.Tileset, UAlbion.Base"" }, - ""tilegfx"": { - ""AssetType"": ""TilesetGfx"", - ""EnumType"": ""UAlbion.Base.TilesetGfx, UAlbion.Base"", - ""CopiedFrom"": ""UAlbion.Base.Tileset, UAlbion.Base"" - }, - ""word"": { ""AssetType"": ""Word"", ""EnumType"": ""UAlbion.Base.Word, UAlbion.Base"" } + ""3dobj"": { ""AssetType"": ""Object3D"", ""EnumType"": ""UAlbion.Base.DungeonObject, UAlbion.Base"" }, + ""autotile"": { ""AssetType"": ""AutomapGfx"", ""EnumType"": ""UAlbion.Base.AutomapTiles, UAlbion.Base"" }, + ""block"": { ""AssetType"": ""BlockList"", ""EnumType"": ""UAlbion.Base.BlockList, UAlbion.Base"" }, + ""combg"": { ""AssetType"": ""CombatBackground"", ""EnumType"": ""UAlbion.Base.CombatBackground, UAlbion.Base"" }, + ""comgfx"": { ""AssetType"": ""CombatGfx"", ""EnumType"": ""UAlbion.Base.CombatGfx, UAlbion.Base"" }, + ""coregfx"": { ""AssetType"": ""CoreGfx"", ""EnumType"": ""UAlbion.Base.CoreGfx, UAlbion.Base"" }, + ""floor"": { ""AssetType"": ""Floor"", ""EnumType"": ""UAlbion.Base.Floor, UAlbion.Base"" }, + ""font"": { ""AssetType"": ""FontDefinition"", ""EnumType"": ""UAlbion.Base.Font, UAlbion.Base"" }, + ""fontgfx"": { ""AssetType"": ""FontGfx"", ""EnumType"": ""UAlbion.Base.FontGfx, UAlbion.Base"" }, + ""item"": { ""AssetType"": ""Item"", ""EnumType"": ""UAlbion.Base.Item, UAlbion.Base"" }, + ""itemname"": { ""AssetType"": ""ItemName"", ""EnumType"": ""UAlbion.Base.ItemName, UAlbion.Base"" }, + ""overlay"": { ""AssetType"": ""WallOverlay"", ""EnumType"": ""UAlbion.Base.WallOverlay, UAlbion.Base"" }, + ""pal"": { ""AssetType"": ""Palette"", ""EnumType"": ""UAlbion.Base.Palette, UAlbion.Base"" }, + ""special"": { ""AssetType"": ""Special"", ""EnumType"": ""UAlbion.Base.Special, UAlbion.Base"" }, + ""spell"": { ""AssetType"": ""Spell"", ""EnumType"": ""UAlbion.Base.Spell, UAlbion.Base"" }, + ""stext"": { ""AssetType"": ""Text"", ""EnumType"": ""UAlbion.Base.SystemText, UAlbion.Base"" }, + ""tiledata"": { ""AssetType"": ""Tileset"", ""EnumType"": ""UAlbion.Base.Tileset, UAlbion.Base"" }, + ""tilegfx"": { ""AssetType"": ""TilesetGfx"", ""EnumType"": ""UAlbion.Base.TilesetGfx, UAlbion.Base"" }, + ""utext"": { ""AssetType"": ""Text"", ""EnumType"": ""UAlbion.Base.UAlbionString, UAlbion.Base"" }, + ""word"": { ""AssetType"": ""Word"", ""EnumType"": ""UAlbion.Base.Word, UAlbion.Base"" } }, ""Loaders"": { - ""amorphous"": ""UAlbion.Formats.Parsers.AmorphousSpriteLoader, UAlbion.Formats"", - ""block"": ""UAlbion.Formats.Parsers.BlockListLoader, UAlbion.Formats"", - ""fixedsize"": ""UAlbion.Formats.Parsers.FixedSizeSpriteLoader, UAlbion.Formats"", - ""font"": ""UAlbion.Formats.Parsers.FontSpriteLoader, UAlbion.Formats"", - ""header"": ""UAlbion.Formats.Parsers.HeaderBasedSpriteLoader, UAlbion.Formats"", - ""multiheader"": ""UAlbion.Formats.Parsers.MultiHeaderSpriteLoader, UAlbion.Formats"", - ""itemdata"": ""UAlbion.Formats.Parsers.ItemDataLoader, UAlbion.Formats"", - ""json"": ""UAlbion.Formats.Parsers.JsonStringLoader, UAlbion.Formats"", - ""pal"": ""UAlbion.Formats.Parsers.PaletteLoader, UAlbion.Formats"", - ""soundbank"": ""UAlbion.Game.Assets.SoundBankLoader, UAlbion.Game"", - ""spell"": ""UAlbion.Formats.Parsers.SpellLoader, UAlbion.Formats"", - ""systemtext"": ""UAlbion.Formats.Parsers.SystemTextLoader, UAlbion.Formats"", - ""tileset"": ""UAlbion.Formats.Parsers.TilesetLoader, UAlbion.Formats"", - ""wordlist"": ""UAlbion.Formats.Parsers.WordListLoader, UAlbion.Formats"" + ""amorphous"": ""UAlbion.Formats.Parsers.AmorphousSpriteLoader, UAlbion.Formats"", + ""block"": ""UAlbion.Formats.Parsers.BlockListLoader, UAlbion.Formats"", + ""fixedsize"": ""UAlbion.Formats.Parsers.FixedSizeSpriteLoader, UAlbion.Formats"", + ""font"": ""UAlbion.Formats.Parsers.JsonLoader`1[[UAlbion.Formats.Assets.FontDefinition, UAlbion.Formats]], UAlbion.Formats"", + ""header"": ""UAlbion.Formats.Parsers.SingleHeaderSpriteLoader, UAlbion.Formats"", + ""itemdata"": ""UAlbion.Formats.Parsers.ItemDataLoader, UAlbion.Formats"", + ""itemname"": ""UAlbion.Formats.Parsers.ItemNameLoader, UAlbion.Formats"", + ""itemnameMeta"": ""UAlbion.Formats.Parsers.ItemNameMetaLoader, UAlbion.Formats"", + ""json"": ""UAlbion.Formats.Parsers.JsonStringLoader, UAlbion.Formats"", + ""multiheader"": ""UAlbion.Formats.Parsers.MultiHeaderSpriteLoader, UAlbion.Formats"", + ""pal"": ""UAlbion.Formats.Parsers.PaletteLoader, UAlbion.Formats"", + ""soundbank"": ""UAlbion.Game.Assets.SoundBankLoader, UAlbion.Game"", + ""spell"": ""UAlbion.Formats.Parsers.SpellLoader, UAlbion.Formats"", + ""stext"": ""UAlbion.Formats.Parsers.SystemTextLoader, UAlbion.Formats"", + ""stringset"": ""UAlbion.Formats.Parsers.StringSetStringLoader, UAlbion.Formats"", + ""tilegfx"": ""UAlbion.Formats.Parsers.TilesetGraphicsLoader, UAlbion.Formats"", + ""tileset"": ""UAlbion.Formats.Parsers.TilesetLoader, UAlbion.Formats"", + ""wordlist"": ""UAlbion.Formats.Parsers.WordListLoader, UAlbion.Formats"" }, ""Containers"": { - ""raw"": ""UAlbion.Formats.Containers.RawContainer, UAlbion.Formats"", - ""items"": ""UAlbion.Formats.Containers.ItemListContainer, UAlbion.Formats"", - ""spells"": ""UAlbion.Formats.Containers.SpellListContainer, UAlbion.Formats"", - ""binaryoffsets"": ""UAlbion.Formats.Containers.BinaryOffsetContainer, UAlbion.Formats"" + ""binaryoffsets"": ""UAlbion.Formats.Containers.BinaryOffsetContainer, UAlbion.Formats"", + ""items"": ""UAlbion.Formats.Containers.ItemListContainer, UAlbion.Formats"", + ""raw"": ""UAlbion.Formats.Containers.RawContainer, UAlbion.Formats"", + ""spells"": ""UAlbion.Formats.Containers.SpellListContainer, UAlbion.Formats"" }, - ""Files"": { - ""$(ALBION)/DRIVERS/ALBISND.OPL"": { ""Loader"": ""soundbank"", ""Map"": { ""0"": { ""Id"": ""special.SoundBank"" } } }, - ""$(XLD)/ITEMLIST.DAT"": { ""Loader"": ""itemdata"", ""Container"": ""items"", ""Map"": { ""0"": { ""Id"": ""item.1"" } } }, - ""$(XLD)/BLKLIST0.XLD"": { ""Loader"": ""block"", ""Map"": { ""0"": {""Id"": ""block.1"" } } }, - ""$(XLD)/3DFLOOR2.XLD"": { ""Loader"": ""fixedsize"", ""Width"": 64, ""Height"": 64, ""Map"": { ""0"": { ""Id"": ""floor.200"" } } }, - ""$(XLD)/COMBACK0.XLD"": { ""Loader"": ""fixedsize"", ""Width"": 360, ""Map"": { ""0"": { ""Id"": ""combg.1"" } } }, - ""$(XLD)/ICONGFX0.XLD"": { ""Loader"": ""fixedsize"", ""Width"": 16, ""Height"": 16, ""Map"": { ""0"": { ""Id"": ""tilegfx.1"" } } }, - ""$(XLD)/COMGFX0.XLD"": { ""Loader"": ""multiheader"", ""Map"": { ""0"": { ""Id"": ""comgfx.1""} } }, - - ""$(MOD)/$(LANG)/strings.json"": { - ""Loader"": ""json"", - ""Container"": ""raw"", - ""Map"": { ""0"": { ""Id"": ""special.UAlbionStrings"" } } - }, - ""$(XLD)/$(LANG)/SYSTEXTS"": { - ""Loader"": ""stext"", - ""Container"": ""raw"", - ""Map"": { ""0"": { ""Id"": ""special.SystemStrings"" } } - }, - ""$(XLD)/$(LANG)/WORDLIS0.XLD"": { - ""Loader"": ""wordlist"", - ""Map"": { - ""0"": { ""Id"": ""special.Words1"" }, - ""1"": { ""Id"": ""special.Words2"" }, - ""2"": { ""Id"": ""special.Words3"" } - } - }, + ""PostProcessors"": { + ""atlas"": ""UAlbion.Formats.Parsers.AtlasPostProcessor, UAlbion.Formats"" + }, - ""$(XLD)/AUTOGFX0.XLD"": { - ""Loader"": ""amorphous"", - ""Map"": { - ""0"": { ""Id"": ""autotile.1"", ""SubSprites"": ""(8,8,576) (16,16)"" }, - ""1"": { ""SubSprites"": ""(8,8,576) (16,16)"" } - } - }, + ""GlobalPropertyTypes"": [ ""UAlbion.Config.Properties.AssetProps, UAlbion.Config"" ] +} +"; - ""$(XLD)/3DOBJEC0.XLD"": { - ""Loader"": ""fixedsize"", - ""Map"": { - ""0"": { ""Id"": ""3dobj.1"", ""Width"": 32 }, - ""1"": { ""Width"": 16 }, - ""26"": { ""Width"": 50, ""Height"": 128 } - } + const string AssetConfig1 = @" +{ + ""special.SoundBank"": { ""Files"": { ""Albion/DRIVERS/ALBISND.OPL"": { ""Container"": ""raw"", ""Loader"": ""soundbank"" } } }, + ""itemname.1-462"": { + ""Files"": { // Dummy files used for ensuring the language is available on the AssetLoadContext + ""!GERMAN"": { ""Language"": ""GERMAN"" }, + ""!ENGLISH"": { ""Language"": ""ENGLISH"" }, + ""!FRENCH"": { ""Language"": ""FRENCH"" } }, + ""Loader"": ""stringset"", + ""FirstId"": ""itemname.1"", + ""Target"": ""special.ItemNamesSingleLang"", + ""IsReadOnly"": true + }, - ""$(XLD)/3DOVERL0.XLD"": { - ""Transposed"": true, - ""Loader"": ""fixedsize"", - ""Map"": { - ""0"": { ""Id"": ""overlay.1"", ""Width"": 51 }, - ""1"": { ""Width"": 44 }, - ""22"": { ""Width"": 62, ""Height"": 42 } - } + ""special.ItemNamesSingleLang"": { + ""Files"": { + ""!GERMAN"": { ""Language"": ""GERMAN"" }, + ""!ENGLISH"": { ""Language"": ""ENGLISH"" }, + ""!FRENCH"": { ""Language"": ""FRENCH"" } }, + ""Loader"": ""itemnameMeta"", + ""Target"": ""special.ItemNamesMultiLang"", + ""IsReadOnly"": true + }, - ""$(XLD)/FONTS0.XLD"": { - ""Loader"": ""font"", - ""Width"": 8, - ""Height"": 8, - ""Max"": 2, - ""Map"": { - ""0"": { ""Id"": ""font.1"" } + ""special.ItemNamesMultiLang"": { + ""Files"": { + ""Albion/CD/XLDLIBS/ITEMNAME.DAT"": { + ""Loader"": ""itemname"", + ""Container"": ""raw"" } - }, + } + }, + ""item.1-462"": { ""Files"": { ""Albion/CD/XLDLIBS/ITEMLIST.DAT"": { ""Loader"": ""itemdata"", ""Container"": ""items"" } } }, + + ""block.1-11"": { ""Files"": { ""Albion/CD/XLDLIBS/BLKLIST0.XLD"": { ""Loader"": ""block"" } } }, + ""floor.200-299"": { ""Files"": { ""Albion/CD/XLDLIBS/3DFLOOR2.XLD"": { ""Loader"": ""fixedsize"", ""Width"": 64, ""Height"": 64 } } }, + ""combg.1-19"": { ""Files"": { ""Albion/CD/XLDLIBS/COMBACK0.XLD"": { ""Loader"": ""fixedsize"", ""Width"": 360 } } }, + ""tilegfx.1-11"": { + ""Files"": { + ""Albion/CD/XLDLIBS/ICONGFX0.XLD"": { + ""Loader"": ""tilegfx"", + ""Width"": 16, + ""Height"": 16, + ""Map"": { + ""tilegfx.1"": { ""Palette"": ""pal.1"" }, + ""tilegfx.2"": { ""Palette"": ""pal.2"" }, + ""tilegfx.3"": { ""Palette"": ""pal.6"" }, + ""tilegfx.4"": { ""Palette"": ""pal.4"" }, + ""tilegfx.5"": { ""Palette"": ""pal.5"" }, + ""tilegfx.6"": { ""Palette"": ""pal.16"" }, + ""tilegfx.7"": { ""Palette"": ""pal.9"" }, + ""tilegfx.8"": { ""Palette"": ""pal.26"" }, + ""tilegfx.9"": { ""Palette"": ""pal.28"" }, + ""tilegfx.10"": { ""Palette"": ""pal.45"" }, + ""tilegfx.11"": { ""Palette"": ""pal.56"" } + } + } + } + }, + ""comgfx.1-85"": { ""Files"": { ""Albion/CD/XLDLIBS/COMGFX0.XLD"": { ""Loader"": ""multiheader"", ""Palette"": ""pal.23"" } } }, + + ""utext.0-*"": { ""Loader"": ""stringset"", ""FirstId"": ""utext.0"", ""Target"": ""special.UAlbionStrings"" }, + ""stext.0-777"": { ""Loader"": ""stringset"", ""FirstId"": ""stext.0"", ""Target"": ""special.SystemStrings"" }, + + ""autotile.1-2"": { + ""Files"": { + ""Albion/CD/XLDLIBS/AUTOGFX0.XLD"": { + ""Post"": ""atlas"", + ""Loader"": ""amorphous"", + ""Map"": { + ""autotile.1"": { ""SubSprites"": ""(8,8,576) (16,16)"", ""Palette"": ""pal.11"" }, + ""autotile.2"": { ""SubSprites"": ""(8,8,576) (16,16)"", ""Palette"": ""pal.30"" } + } + } + } + }, - ""$(XLD)/ICONDAT0.XLD"": { - ""Loader"": ""tileset"", - ""Map"": { - ""0"": { ""Id"": ""tiledata.1"", ""UseSmallGraphics"": true }, - ""1"": { ""UseSmallGraphics"": true }, - ""3"": { ""UseSmallGraphics"": true } + ""3dobj.1-99"": { ""Files"": { ""Albion/CD/XLDLIBS/3DOBJEC0.XLD"": { ""Loader"": ""fixedsize"" } } }, + ""overlay.1-99"": { ""Files"": { ""Albion/CD/XLDLIBS/3DOVERL0.XLD"": { ""Loader"": ""fixedsize"", ""Transposed"": true } } }, + ""fontgfx.1-2"": { // English/French fonts, regular & bold + ""Files"": { + ""Albion/CD/XLDLIBS/FONTS0.XLD#33906F62"": { // EN+FR + ""Loader"": ""fixedsize"", + ""Optional"": true, + ""Width"": 8, + ""Height"": 8 } - }, + } + }, - ""$(XLD)/PALETTE0.XLD"": { - ""Loader"": ""pal"", - ""Map"": { - ""0"": { ""Id"": ""pal.1"", ""AnimatedRanges"": ""0x99-0x9f, 0xb0-0xbf"" }, - ""1"": { ""AnimatedRanges"": ""0x99-0x9f, 0xb0-0xb4, 0xb5-0xbf"" } + ""tiledata.1-11"": { + ""Files"": { + ""Albion/CD/XLDLIBS/ICONDAT0.XLD"": { + ""Loader"": ""tileset"", + ""Map"": { + ""tiledata.1"": { ""UseSmallGraphics"": true }, + ""tiledata.2"": { ""UseSmallGraphics"": true }, + ""tiledata.4"": { ""UseSmallGraphics"": true } + } } - }, + } + }, - ""$(XLD)/SPELLDAT.XLD"": { - ""Loader"": ""spell"", - ""Container"": ""spells"", - ""Map"": { // Ids = 1 + OffsetInSchool + School * 256 - ""0"": { ""Id"": ""spell.1"" }, - ""30"": { ""Id"": ""spell.257"" }, - ""60"": { ""Id"": ""spell.513"" } + ""pal.0"": { + ""Files"": { + ""Albion/CD/XLDLIBS/PALETTE.000"": { + ""IsCommon"": true, + ""Container"": ""raw"", + ""Loader"": ""pal"" } - }, + } + }, - ""$(ALBION)/MAIN.EXE#476227b0391cf3452166b7a1d52b012ccf6c86bc9e46886dafbed343e9140710"": { // EN+DE - ""Loader"": ""fixedsize"", - ""Container"": ""binaryoffsets"", - ""Map"": { - ""0"": { ""Id"": ""coresprite.0"", ""Offset"": 1031768, ""Width"": 14, ""Height"": 14, ""Hotspot"": ""-6 0"" }, - ""1"": { ""Offset"": 1031964, ""Width"": 16, ""Height"": 16, ""Hotspot"": ""0 4"" }, - ""27"": { ""Offset"": 1039632, ""Width"": 32, ""Height"": 64 } + ""pal.1-56"": { + ""Files"": { + ""Albion/CD/XLDLIBS/PALETTE0.XLD"": { + ""Loader"": ""pal"", + ""Map"": { + ""pal.1"": { ""NightPalette"": ""pal.47"", ""AnimatedRanges"": ""0x99-0x9f, 0xb0-0xb4, 0xb5-0xbf"" }, + ""pal.2"": { ""NightPalette"": ""pal.47"", ""AnimatedRanges"": ""0x99-0x9f, 0xb0-0xb4, 0xb5-0xbf"" }, + ""pal.3"": { ""NightPalette"": ""pal.55"", ""AnimatedRanges"": ""0x40-0x43, 0x44-0x4f"" }, + ""pal.4"": { ""NightPalette"": ""pal.48"" }, + ""pal.6"": { ""AnimatedRanges"": ""0xb0-0xb4, 0xb5-0xbf"" }, + ""pal.14"": { ""NightPalette"": ""pal.49"", ""AnimatedRanges"": ""0xb0-0xb3, 0xb4-0xbf"" }, + ""pal.15"": { ""AnimatedRanges"": ""0x58-0x5f"" }, + ""pal.25"": { ""NightPalette"": ""pal.49"", ""AnimatedRanges"": ""0xb0-0xb3, 0xb4-0xbf"" }, + ""pal.26"": { ""AnimatedRanges"": ""0xb4-0xb7, 0xb8-0xbb, 0xbc-0xbf"" }, + ""pal.31"": { ""AnimatedRanges"": ""0x10-0x4f"" }, + ""pal.47"": { ""AnimatedRanges"": ""0x99-0x9f, 0xb0-0xb4, 0xb5-0xbf"" }, + ""pal.49"": { ""AnimatedRanges"": ""0xb0-0xb3, 0xb4-0xbf"" }, + ""pal.51"": { ""NightPalette"": ""pal.49"", ""AnimatedRanges"": ""0xb0-0xb3, 0xb4-0xbf"" }, + ""pal.55"": { ""AnimatedRanges"": ""0x40-0x43, 0x44-0x4f"" } + } } } + }, + + ""spell.1-210"": { ""Files"": { ""Albion/CD/XLDLIBS/SPELLDAT.DAT"": { ""Loader"": ""spell"", ""Container"": ""spells"" } } }, + ""coregfx.0-88"": { + ""Files"": { + ""Albion/MAIN.EXE#476227B0"": {}, // EN GOG, built Aug 22 1996 + ""Albion/MAIN.EXE#9FC7ABCF"": {}, // EN, built Jul 25 1996 + ""Albion/MAIN.EXE#487DA334"": {}, // FR + ""Albion/MAIN.EXE#EC6D6389"": {} // DE GOG ISO, built Dec 14 1995 + }, + ""Container"": ""binaryoffsets"", + ""Loader"": ""fixedsize"", + ""IsReadOnly"": true } } "; - static byte[] TestConfig1Bytes { get; } = Encoding.UTF8.GetBytes(TestConfig1); - static readonly IJsonUtil JsonUtil = new JsonUtil(); - public AssetConfigTests() => AssetMapping.GlobalIsThreadLocal = true; + TypeConfig TypeConfig { get; } + AssetConfig AssetConfig { get; } + + public AssetConfigTests() + { + AssetMapping.GlobalIsThreadLocal = true; + + var tcl = new TypeConfigLoader(JsonUtil); + var typeConfigBytes = Encoding.UTF8.GetBytes(TypeConfig1); + TypeConfig = tcl.Parse(typeConfigBytes, "Test", null, AssetMapping.Global); + + foreach (var kvp in TypeConfig.IdTypes) + AssetMapping.Global.RegisterAssetType(Type.GetType(kvp.Value.EnumType), kvp.Value.AssetType); + + var disk = new MockFileSystem(false); + var baseDir = @"C:\ualbion"; + var pathResolver = new PathResolver(baseDir, nameof(AssetConfigTests)); + var acl = new AssetConfigLoader(disk, JsonUtil, pathResolver, TypeConfig); + var assetConfigBytes = Encoding.UTF8.GetBytes(AssetConfig1); + AssetConfig = acl.Parse(assetConfigBytes, nameof(AssetConfigTests), null); + } [Fact] public void VerifyIdTypes() { - var c = AssetConfig.Parse(TestConfig1Bytes, null, AssetMapping.Global, JsonUtil); - Assert.Collection(c.IdTypes.Values.OrderBy(x => x.Alias), + Assert.Collection(TypeConfig.IdTypes.Values.OrderBy(x => x.Alias), t => { Assert.Equal("3dobj", t.Alias); @@ -208,9 +293,9 @@ public void VerifyIdTypes() }, t => { - Assert.Equal("coresprite", t.Alias); + Assert.Equal("coregfx", t.Alias); Assert.Equal(AssetType.CoreGfx, t.AssetType); - Assert.Equal("UAlbion.Base.CoreSprite, UAlbion.Base", t.EnumType); + Assert.Equal("UAlbion.Base.CoreGfx, UAlbion.Base", t.EnumType); }, t => { @@ -237,6 +322,12 @@ public void VerifyIdTypes() Assert.Equal("UAlbion.Base.Item, UAlbion.Base", t.EnumType); }, t => + { + Assert.Equal("itemname", t.Alias); + Assert.Equal(AssetType.ItemName, t.AssetType); + Assert.Equal("UAlbion.Base.ItemName, UAlbion.Base", t.EnumType); + }, + t => { Assert.Equal("overlay", t.Alias); Assert.Equal(AssetType.WallOverlay, t.AssetType); @@ -261,6 +352,12 @@ public void VerifyIdTypes() Assert.Equal("UAlbion.Base.Spell, UAlbion.Base", t.EnumType); }, t => + { + Assert.Equal("stext", t.Alias); + Assert.Equal(AssetType.Text, t.AssetType); + Assert.Equal("UAlbion.Base.SystemText, UAlbion.Base", t.EnumType); + }, + t => { Assert.Equal("tiledata", t.Alias); Assert.Equal(AssetType.Tileset, t.AssetType); @@ -273,6 +370,12 @@ public void VerifyIdTypes() Assert.Equal("UAlbion.Base.TilesetGfx, UAlbion.Base", t.EnumType); }, t => + { + Assert.Equal("utext", t.Alias); + Assert.Equal(AssetType.Text, t.AssetType); + Assert.Equal("UAlbion.Base.UAlbionString, UAlbion.Base", t.EnumType); + }, + t => { Assert.Equal("word", t.Alias); Assert.Equal(AssetType.Word, t.AssetType); @@ -283,111 +386,113 @@ public void VerifyIdTypes() [Fact] public void VerifyLoaders() { - var c = AssetConfig.Parse(TestConfig1Bytes, null, AssetMapping.Global, JsonUtil); - Assert.Collection(c.Loaders.OrderBy(x => x.Key), + Assert.Collection(TypeConfig.Loaders.OrderBy(x => x.Key), l => { Assert.Equal("amorphous", l.Key); - Assert.Equal("UAlbion.Formats.Parsers.AmorphousSpriteLoader, UAlbion.Formats", l.Value); + Assert.Equal(typeof(AmorphousSpriteLoader), l.Value); }, l => { Assert.Equal("block", l.Key); - Assert.Equal("UAlbion.Formats.Parsers.BlockListLoader, UAlbion.Formats", l.Value); + Assert.Equal(typeof(BlockListLoader), l.Value); }, l => { Assert.Equal("fixedsize", l.Key); - Assert.Equal("UAlbion.Formats.Parsers.FixedSizeSpriteLoader, UAlbion.Formats", l.Value); + Assert.Equal(typeof(FixedSizeSpriteLoader), l.Value); }, l => { Assert.Equal("font", l.Key); - Assert.Equal("UAlbion.Formats.Parsers.FontSpriteLoader, UAlbion.Formats", l.Value); + Assert.Equal(typeof(JsonLoader), l.Value); }, l => { Assert.Equal("header", l.Key); - Assert.Equal("UAlbion.Formats.Parsers.HeaderBasedSpriteLoader, UAlbion.Formats", l.Value); + Assert.Equal(typeof(SingleHeaderSpriteLoader), l.Value); }, l => { Assert.Equal("itemdata", l.Key); - Assert.Equal("UAlbion.Formats.Parsers.ItemDataLoader, UAlbion.Formats", l.Value); + Assert.Equal(typeof(ItemDataLoader), l.Value); + }, + l => + { + Assert.Equal("itemname", l.Key); + Assert.Equal(typeof(ItemNameLoader), l.Value); + }, + l => + { + Assert.Equal("itemnameMeta", l.Key); + Assert.Equal(typeof(ItemNameMetaLoader), l.Value); }, l => { Assert.Equal("json", l.Key); - Assert.Equal("UAlbion.Formats.Parsers.JsonStringLoader, UAlbion.Formats", l.Value); + Assert.Equal(typeof(JsonStringLoader), l.Value); }, l => { Assert.Equal("multiheader", l.Key); - Assert.Equal("UAlbion.Formats.Parsers.MultiHeaderSpriteLoader, UAlbion.Formats", l.Value); + Assert.Equal(typeof(MultiHeaderSpriteLoader), l.Value); }, l => { Assert.Equal("pal", l.Key); - Assert.Equal("UAlbion.Formats.Parsers.PaletteLoader, UAlbion.Formats", l.Value); + Assert.Equal(typeof(PaletteLoader), l.Value); }, l => { Assert.Equal("soundbank", l.Key); - Assert.Equal("UAlbion.Game.Assets.SoundBankLoader, UAlbion.Game", l.Value); + Assert.Equal(typeof(SoundBankLoader), l.Value); }, l => { Assert.Equal("spell", l.Key); - Assert.Equal("UAlbion.Formats.Parsers.SpellLoader, UAlbion.Formats", l.Value); + Assert.Equal(typeof(SpellLoader), l.Value); + }, + l => + { + Assert.Equal("stext", l.Key); + Assert.Equal(typeof(SystemTextLoader), l.Value); }, l => { - Assert.Equal("systemtext", l.Key); - Assert.Equal("UAlbion.Formats.Parsers.SystemTextLoader, UAlbion.Formats", l.Value); + Assert.Equal("stringset", l.Key); + Assert.Equal(typeof(StringSetStringLoader), l.Value); + }, + l => + { + Assert.Equal("tilegfx", l.Key); + Assert.Equal(typeof(TilesetGraphicsLoader), l.Value); }, l => { Assert.Equal("tileset", l.Key); - Assert.Equal("UAlbion.Formats.Parsers.TilesetLoader, UAlbion.Formats", l.Value); + Assert.Equal(typeof(TilesetLoader), l.Value); }, l => { Assert.Equal("wordlist", l.Key); - Assert.Equal("UAlbion.Formats.Parsers.WordListLoader, UAlbion.Formats", l.Value); + Assert.Equal(typeof(WordListLoader), l.Value); }); } - +#if false [Fact] - public void VerifyFiles() + public void VerifyRanges() { - var c = AssetConfig.Parse(TestConfig1Bytes, null, AssetMapping.Global, JsonUtil); - Assert.Collection(c.Files.Keys.OrderBy(x => x), - x => Assert.Equal("$(ALBION)/DRIVERS/ALBISND.OPL", x), - x => Assert.Equal("$(ALBION)/MAIN.EXE#476227b0391cf3452166b7a1d52b012ccf6c86bc9e46886dafbed343e9140710", x), - x => Assert.Equal("$(MOD)/$(LANG)/strings.json", x), - x => Assert.Equal("$(XLD)/$(LANG)/SYSTEXTS", x), - x => Assert.Equal("$(XLD)/$(LANG)/WORDLIS0.XLD", x), - x => Assert.Equal("$(XLD)/3DFLOOR2.XLD", x), - x => Assert.Equal("$(XLD)/3DOBJEC0.XLD", x), - x => Assert.Equal("$(XLD)/3DOVERL0.XLD", x), - x => Assert.Equal("$(XLD)/AUTOGFX0.XLD", x), - x => Assert.Equal("$(XLD)/BLKLIST0.XLD", x), - x => Assert.Equal("$(XLD)/COMBACK0.XLD", x), - x => Assert.Equal("$(XLD)/COMGFX0.XLD", x), - x => Assert.Equal("$(XLD)/FONTS0.XLD", x), - x => Assert.Equal("$(XLD)/ICONDAT0.XLD", x), - x => Assert.Equal("$(XLD)/ICONGFX0.XLD", x), - x => Assert.Equal("$(XLD)/ITEMLIST.DAT", x), - x => Assert.Equal("$(XLD)/PALETTE0.XLD", x), - x => Assert.Equal("$(XLD)/SPELLDAT.XLD", x) + var ordered = AssetConfig.Ranges.AllRanges.OrderBy(x => x.Range.From); + Assert.Collection(ordered.Select(x => x.Range), + x => Assert.Equal(new AssetRange(AssetId.None, AssetId.None), x), + x => Assert.Equal(new AssetRange(AssetId.None, AssetId.None), x) ); } [Fact] public void VerifySpecial() { - var c = AssetConfig.Parse(TestConfig1Bytes, null, AssetMapping.Global, JsonUtil); - var f = c.Files["$(ALBION)/DRIVERS/ALBISND.OPL"]; + var f = TypeConfig.Files["$(ALBION)/DRIVERS/ALBISND.OPL"]; Assert.Equal("UAlbion.Game.Assets.SoundBankLoader, UAlbion.Game", f.Loader); Assert.Null(f.Container); Assert.Collection(f.Map.OrderBy(x => x.Key), @@ -401,8 +506,7 @@ public void VerifySpecial() [Fact] public void VerifyItemList() { - var c = AssetConfig.Parse(TestConfig1Bytes, null, AssetMapping.Global, JsonUtil); - var f = c.Files["$(XLD)/ITEMLIST.DAT"]; + var f = TypeConfig.Files["$(XLD)/ITEMLIST.DAT"]; Assert.Equal("UAlbion.Formats.Parsers.ItemDataLoader, UAlbion.Formats", f.Loader); Assert.Equal("UAlbion.Formats.Containers.ItemListContainer, UAlbion.Formats", f.Container); Assert.Collection(f.Map.OrderBy(x => x.Key), @@ -416,8 +520,7 @@ public void VerifyItemList() [Fact] public void VerifyBlockList() { - var c = AssetConfig.Parse(TestConfig1Bytes, null, AssetMapping.Global, JsonUtil); - var f = c.Files["$(XLD)/BLKLIST0.XLD"]; + var f = TypeConfig.Files["$(XLD)/BLKLIST0.XLD"]; Assert.Equal("UAlbion.Formats.Parsers.BlockListLoader, UAlbion.Formats", f.Loader); Assert.Null(f.Container); Assert.Collection(f.Map.OrderBy(x => x.Key), @@ -430,8 +533,7 @@ public void VerifyBlockList() [Fact] public void VerifyFloors() { - var c = AssetConfig.Parse(TestConfig1Bytes, null, AssetMapping.Global, JsonUtil); - var f = c.Files["$(XLD)/3DFLOOR2.XLD"]; + var f = TypeConfig.Files["$(XLD)/3DFLOOR2.XLD"]; Assert.Equal("UAlbion.Formats.Parsers.FixedSizeSpriteLoader, UAlbion.Formats", f.Loader); Assert.Equal(64, f.Width); Assert.Equal(64, f.Height); @@ -445,8 +547,7 @@ public void VerifyFloors() [Fact] public void VerifyCombatBackgrounds() { - var c = AssetConfig.Parse(TestConfig1Bytes, null, AssetMapping.Global, JsonUtil); - var f = c.Files["$(XLD)/COMBACK0.XLD"]; + var f = TypeConfig.Files["$(XLD)/COMBACK0.XLD"]; Assert.Equal("UAlbion.Formats.Parsers.FixedSizeSpriteLoader, UAlbion.Formats", f.Loader); Assert.Equal(360, f.Width); Assert.Collection(f.Map.OrderBy(x => x.Key), m => @@ -459,8 +560,7 @@ public void VerifyCombatBackgrounds() [Fact] public void VerifyTileGraphics() { - var c = AssetConfig.Parse(TestConfig1Bytes, null, AssetMapping.Global, JsonUtil); - var f = c.Files["$(XLD)/ICONGFX0.XLD"]; + var f = TypeConfig.Files["$(XLD)/ICONGFX0.XLD"]; Assert.Equal("UAlbion.Formats.Parsers.FixedSizeSpriteLoader, UAlbion.Formats", f.Loader); Assert.Equal(16, f.Width); Assert.Equal(16, f.Height); @@ -474,8 +574,7 @@ public void VerifyTileGraphics() [Fact] public void VerifyCombatGraphics() { - var c = AssetConfig.Parse(TestConfig1Bytes, null, AssetMapping.Global, JsonUtil); - var f = c.Files["$(XLD)/COMGFX0.XLD"]; + var f = TypeConfig.Files["$(XLD)/COMGFX0.XLD"]; Assert.Equal("UAlbion.Formats.Parsers.MultiHeaderSpriteLoader, UAlbion.Formats", f.Loader); Assert.Collection(f.Map.OrderBy(x => x.Key), m => { @@ -487,28 +586,26 @@ public void VerifyCombatGraphics() [Fact] public void VerifyAutomapGraphics() { - var c = AssetConfig.Parse(TestConfig1Bytes, null, AssetMapping.Global, JsonUtil); - var f = c.Files["$(XLD)/AUTOGFX0.XLD"]; + var f = TypeConfig.Files["$(XLD)/AUTOGFX0.XLD"]; Assert.Equal("UAlbion.Formats.Parsers.AmorphousSpriteLoader, UAlbion.Formats", f.Loader); Assert.Collection(f.Map.OrderBy(x => x.Key), m => { Assert.Equal(0, m.Key); Assert.Equal("autotile.1", m.Value.Id); - Assert.Equal("(8,8,576) (16,16)", m.Value.Get(AssetProperty.SubSprites, null)); + Assert.Equal("(8,8,576) (16,16)", m.Value.GetProperty(AssetProps.SubSprites, null)); }, m => { Assert.Equal(1, m.Key); - Assert.Equal("(8,8,576) (16,16)", m.Value.Get(AssetProperty.SubSprites, null)); + Assert.Equal("(8,8,576) (16,16)", m.Value.GetProperty(AssetProps.SubSprites, null)); }); } [Fact] public void Verify3dObjects() { - var c = AssetConfig.Parse(TestConfig1Bytes, null, AssetMapping.Global, JsonUtil); - var f = c.Files["$(XLD)/3DOBJEC0.XLD"]; + var f = TypeConfig.Files["$(XLD)/3DOBJEC0.XLD"]; Assert.Equal("UAlbion.Formats.Parsers.FixedSizeSpriteLoader, UAlbion.Formats", f.Loader); Assert.Collection(f.Map.OrderBy(x => x.Key), m => @@ -533,9 +630,8 @@ public void Verify3dObjects() [Fact] public void VerifyOverlays() { - var c = AssetConfig.Parse(TestConfig1Bytes, null, AssetMapping.Global, JsonUtil); - var f = c.Files["$(XLD)/3DOVERL0.XLD"]; - Assert.True(f.Get(AssetProperty.Transposed, false)); + var f = TypeConfig.Files["$(XLD)/3DOVERL0.XLD"]; + Assert.True(f.GetProperty(AssetProps.Transposed, false)); Assert.Equal("UAlbion.Formats.Parsers.FixedSizeSpriteLoader, UAlbion.Formats", f.Loader); Assert.Collection(f.Map.OrderBy(x => x.Key), m => @@ -560,8 +656,7 @@ public void VerifyOverlays() [Fact] public void VerifyFonts() { - var c = AssetConfig.Parse(TestConfig1Bytes, null, AssetMapping.Global, JsonUtil); - var f = c.Files["$(XLD)/FONTS0.XLD"]; + var f = TypeConfig.Files["$(XLD)/FONTS0.XLD"]; Assert.Equal("UAlbion.Formats.Parsers.FontSpriteLoader, UAlbion.Formats", f.Loader); Assert.Equal(8, f.Width); Assert.Equal(8, f.Height); @@ -576,53 +671,50 @@ public void VerifyFonts() [Fact] public void VerifyTilesets() { - var c = AssetConfig.Parse(TestConfig1Bytes, null, AssetMapping.Global, JsonUtil); - var f = c.Files["$(XLD)/ICONDAT0.XLD"]; + var f = TypeConfig.Files["$(XLD)/ICONDAT0.XLD"]; Assert.Equal("UAlbion.Formats.Parsers.TilesetLoader, UAlbion.Formats", f.Loader); Assert.Collection(f.Map.OrderBy(x => x.Key), m => { Assert.Equal(0, m.Key); Assert.Equal("tiledata.1", m.Value.Id); - Assert.True(m.Value.Get(AssetProperty.UseSmallGraphics, false)); + Assert.True(m.Value.GetProperty(AssetProps.UseSmallGraphics, false)); }, m => { Assert.Equal(1, m.Key); - Assert.True(m.Value.Get(AssetProperty.UseSmallGraphics, false)); + Assert.True(m.Value.GetProperty(AssetProps.UseSmallGraphics, false)); }, m => { Assert.Equal(3, m.Key); - Assert.True(m.Value.Get(AssetProperty.UseSmallGraphics, false)); + Assert.True(m.Value.GetProperty(AssetProps.UseSmallGraphics, false)); }); } [Fact] public void VerifyPalettes() { - var c = AssetConfig.Parse(TestConfig1Bytes, null, AssetMapping.Global, JsonUtil); - var f = c.Files["$(XLD)/PALETTE0.XLD"]; + var f = TypeConfig.Files["$(XLD)/PALETTE0.XLD"]; Assert.Equal("UAlbion.Formats.Parsers.PaletteLoader, UAlbion.Formats", f.Loader); Assert.Collection(f.Map.OrderBy(x => x.Key), m => { Assert.Equal(0, m.Key); Assert.Equal("pal.1", m.Value.Id); - Assert.Equal("0x99-0x9f, 0xb0-0xbf", m.Value.Get(AssetProperty.AnimatedRanges, "")); + Assert.Equal("0x99-0x9f, 0xb0-0xbf", m.Value.GetProperty(AssetProps.AnimatedRanges, "")); }, m => { Assert.Equal(1, m.Key); - Assert.Equal("0x99-0x9f, 0xb0-0xb4, 0xb5-0xbf", m.Value.Get(AssetProperty.AnimatedRanges, "")); + Assert.Equal("0x99-0x9f, 0xb0-0xb4, 0xb5-0xbf", m.Value.GetProperty(AssetProps.AnimatedRanges, "")); }); } [Fact] public void VerifySpells() { - var c = AssetConfig.Parse(TestConfig1Bytes, null, AssetMapping.Global, JsonUtil); - var f = c.Files["$(XLD)/SPELLDAT.XLD"]; + var f = TypeConfig.Files["$(XLD)/SPELLDAT.XLD"]; Assert.Equal("UAlbion.Formats.Parsers.SpellLoader, UAlbion.Formats", f.Loader); Assert.Equal("UAlbion.Formats.Containers.SpellListContainer, UAlbion.Formats", f.Container); Assert.Collection(f.Map.OrderBy(x => x.Key), @@ -635,8 +727,6 @@ public void VerifySpells() [Fact] public void VerifyWords() { - var c = AssetConfig.Parse(TestConfig1Bytes); - var f = c.Files["$(LANG)/WORDLIS0.XLD"]; Assert.Equal("UAlbion.Formats.Parsers.WordListLoader, UAlbion.Formats", f.Loader); Assert.Collection(f.Map.OrderBy(x => x.Key), m => @@ -650,8 +740,7 @@ public void VerifyWords() [Fact] public void VerifyMain() { - var c = AssetConfig.Parse(TestConfig1Bytes, null, AssetMapping.Global, JsonUtil); - var f = c.Files["$(ALBION)/MAIN.EXE#476227b0391cf3452166b7a1d52b012ccf6c86bc9e46886dafbed343e9140710"]; + var f = TypeConfig.Files["$(ALBION)/MAIN.EXE#476227b0391cf3452166b7a1d52b012ccf6c86bc9e46886dafbed343e9140710"]; Assert.Equal("UAlbion.Formats.Parsers.FixedSizeSpriteLoader, UAlbion.Formats", f.Loader); Assert.Equal("UAlbion.Formats.Containers.BinaryOffsetContainer, UAlbion.Formats", f.Container); Assert.Collection(f.Map.OrderBy(x => x.Key), @@ -659,26 +748,27 @@ public void VerifyMain() { Assert.Equal(0, m.Key); Assert.Equal("coresprite.0", m.Value.Id); - Assert.Equal(1031768, m.Value.Get(AssetProperty.Offset, 0)); + Assert.Equal(1031768, m.Value.GetProperty(BinaryOffsetContainer.Offset, 0)); Assert.Equal(14, m.Value.Width); Assert.Equal(14, m.Value.Height); - Assert.Equal("-6 0", m.Value.Get("Hotspot", null)); + Assert.Equal("-6 0", m.Value.GetProperty(BinaryOffsetContainer.Hotspot)); }, m => { Assert.Equal(1, m.Key); - Assert.Equal(1031964, m.Value.Get(AssetProperty.Offset, 0)); + Assert.Equal(1031964, m.Value.GetProperty(AssetProperty.Offset, 0)); Assert.Equal(16, m.Value.Width); Assert.Equal(16, m.Value.Height); - Assert.Equal("0 4", m.Value.Get("Hotspot", null)); + Assert.Equal("0 4", m.Value.GetProperty(BinaryOffsetContainer.Hotspot)); }, m => { Assert.Equal(27, m.Key); - Assert.Equal(1039632, m.Value.Get(AssetProperty.Offset, 0)); + Assert.Equal(1039632, m.Value.GetProperty(AssetProperty.Offset, 0)); Assert.Equal(32, m.Value.Width); Assert.Equal(64, m.Value.Height); - Assert.Null(m.Value.Get("Hotspot", null)); + Assert.Null(m.Value.GetProperty("Hotspot", null)); }); } +#endif } \ No newline at end of file diff --git a/src/Tests/UAlbion.Config.Tests/UAlbion.Config.Tests.csproj b/src/Tests/UAlbion.Config.Tests/UAlbion.Config.Tests.csproj index ae139ec08..560de0980 100644 --- a/src/Tests/UAlbion.Config.Tests/UAlbion.Config.Tests.csproj +++ b/src/Tests/UAlbion.Config.Tests/UAlbion.Config.Tests.csproj @@ -15,6 +15,9 @@ + + + diff --git a/src/Tests/UAlbion.Formats.Tests/SpriteLoaderTests.cs b/src/Tests/UAlbion.Formats.Tests/SpriteLoaderTests.cs index d2835ea88..ca2186c61 100644 --- a/src/Tests/UAlbion.Formats.Tests/SpriteLoaderTests.cs +++ b/src/Tests/UAlbion.Formats.Tests/SpriteLoaderTests.cs @@ -4,6 +4,7 @@ using UAlbion.Api; using UAlbion.Api.Visual; using UAlbion.Config; +using UAlbion.Formats.Ids; using UAlbion.Formats.Parsers; using UAlbion.TestCommon; using Xunit; @@ -18,33 +19,35 @@ public class SpriteLoaderTests static readonly MultiHeaderSpriteLoader MultiHeaderLoader = new(); static readonly AmorphousSpriteLoader AmorphousLoader = new(); - delegate IReadOnlyTexture SerdesFunc(IReadOnlyTexture x, ISerializer s, SerdesContext context); - static IReadOnlyTexture Load(byte[] bytes, SerdesFunc serdes) + delegate IReadOnlyTexture SerdesFunc(IReadOnlyTexture x, ISerializer s, AssetLoadContext context); + static IReadOnlyTexture Load(byte[] bytes, AssetLoadContext context, SerdesFunc serdes) { using var ms = new MemoryStream(bytes); using var br = new BinaryReader(ms); using var s = new AlbionReader(br); - var context = new SerdesContext("Test", JsonUtil, AssetMapping.Global, Disk); return serdes(null, s, context); } - static byte[] Save(IReadOnlyTexture sprite, SerdesFunc serdes) + static byte[] Save(IReadOnlyTexture sprite, AssetLoadContext context, SerdesFunc serdes) { using var ms = new MemoryStream(); using var bw = new BinaryWriter(ms); using var s = new AlbionWriter(bw); - var context = new SerdesContext("Test", JsonUtil, AssetMapping.Global, Disk); serdes(sprite, s, context); ms.Position = 0; return ms.ToArray(); } - - static void RoundTrip(byte[] bytes, SerdesFunc serdes, Action> assert) + static void RoundTrip(byte[] bytes, SerdesFunc serdes, Action> assert) => RoundTrip(bytes, serdes, assert, AssetId.None); + static void RoundTrip(byte[] bytes, SerdesFunc serdes, Action> assert, AssetId assetId, AssetNode node = null) { - var sprite = Load(bytes, serdes); + var mod = new ModContext("Test", JsonUtil, Disk, AssetMapping.Global); + var context = new AssetLoadContext(assetId, node ?? new AssetNode(assetId), mod); + + var sprite = Load(bytes, context, serdes); assert(sprite); - var roundTripped = Save(sprite, serdes); + + var roundTripped = Save(sprite, context, serdes); var a = FormatUtil.BytesToHexString(bytes); var b = FormatUtil.BytesToHexString(roundTripped); Assert.Equal(a, b); @@ -63,7 +66,7 @@ public void SingleHeader() }; RoundTrip(oneFrame, - (x, s, c) => HeaderLoader.Serdes(x, new AssetInfo(), s, c), + (x, s, c) => HeaderLoader.Serdes(x, s, c), sprite => { Assert.Equal(4, sprite.Width); @@ -93,7 +96,7 @@ public void TwoUniformHeaders() }; RoundTrip(twoFrames, - (x, s, c) => HeaderLoader.Serdes(x, new AssetInfo(), s, c), + (x, s, c) => HeaderLoader.Serdes(x, s, c), sprite => { Assert.Equal(4, sprite.Width); @@ -141,9 +144,8 @@ public void NonUniformHeaders() 01, 02, 03, 04, 05 }; - var info = new AssetInfo(); RoundTrip(nonUniform, - (x, s, c) => MultiHeaderLoader.Serdes(x, info, s, c), + (x, s, c) => MultiHeaderLoader.Serdes(x, s, c), sprite => { Assert.Equal(5, sprite.Width); @@ -177,10 +179,11 @@ public void AmorphousSimple() 0x13, 0x14, }; - var info = new AssetInfo(); - info.Set(AssetProperty.SubSprites, "(3,2,2) (2,1)"); + var id = SpriteId.None; + var node = new AssetNode(id); + node.SetProperty(AmorphousSpriteLoader.SubSpritesProperty, "(3,2,2) (2,1)"); RoundTrip(oneFrame, - (x, s, c) => AmorphousLoader.Serdes(x, info, s, c), + (x, s, c) => AmorphousLoader.Serdes(x, s, c), sprite => { Assert.Equal(3, sprite.Width); @@ -211,6 +214,6 @@ public void AmorphousSimple() Assert.Equal(1, sprite.Regions[4].Height); Assert.Equal(0, sprite.Regions[4].X); Assert.Equal(6, sprite.Regions[4].Y); - }); + }, id, node); } } \ No newline at end of file diff --git a/src/Tests/UAlbion.Game.Tests/DialogManagerTests.cs b/src/Tests/UAlbion.Game.Tests/DialogManagerTests.cs index 10486b897..6b914af9d 100644 --- a/src/Tests/UAlbion.Game.Tests/DialogManagerTests.cs +++ b/src/Tests/UAlbion.Game.Tests/DialogManagerTests.cs @@ -98,7 +98,7 @@ void AddDummySprite(SpriteId id) var metaFont = font.Build(Base.Font.Regular, Base.Ink.White, assetManager); mma.Add(metaFont.Id, metaFont); - var e = new YesNoPromptEvent((TextId)Base.SystemText.MainMenu_DoYouReallyWantToQuit); + var e = new YesNoPromptEvent(Base.SystemText.MainMenu_DoYouReallyWantToQuit); bool? result = null; ex.RaiseAsync(e, null, x => result = x); @@ -117,7 +117,7 @@ void AddDummySprite(SpriteId id) Assert.Equal(0, layout.Children.Count); // Dialog should be closed, so no top-level dialogs // Open another yes/no dialog - e = new YesNoPromptEvent((TextId)Base.SystemText.MainMenu_DoYouReallyWantToQuit); + e = new YesNoPromptEvent(Base.SystemText.MainMenu_DoYouReallyWantToQuit); ex.RaiseAsync(e, this, x => result = x); layout = lm.GetLayout(); Assert.Equal(1, layout.Children.Count); // Should only be one top-level dialog diff --git a/src/Tests/UAlbion.Game.Tests/MockAssetLocator.cs b/src/Tests/UAlbion.Game.Tests/MockAssetLocator.cs index e99d32303..f4f88f5fe 100644 --- a/src/Tests/UAlbion.Game.Tests/MockAssetLocator.cs +++ b/src/Tests/UAlbion.Game.Tests/MockAssetLocator.cs @@ -13,6 +13,5 @@ public class MockAssetLocator : ServiceComponent, IAssetLocator public IAssetLocator AddAssetLocator(IAssetLocator locator, bool useAsDefault) => this; public IAssetLocator AddAssetPostProcessor(IAssetPostProcessor postProcessor) => this; public MockAssetLocator Add(AssetId key, object asset) { _assets[key] = asset; return this; } - public object LoadAsset(AssetInfo info, SerdesContext context, TextWriter annotationWriter, List filesSearched) => _assets[info.AssetId]; - public List<(int, int)> GetSubItemRangesForFile(AssetFileInfo info, SerdesContext context) => new() { (0, 100) }; + public object LoadAsset(AssetLoadContext context, TextWriter annotationWriter, List filesSearched) => _assets[context.AssetId]; } \ No newline at end of file diff --git a/src/Tests/UAlbion.Game.Tests/MockModApplier.cs b/src/Tests/UAlbion.Game.Tests/MockModApplier.cs index 6c5c20bc0..7b8c4fa37 100644 --- a/src/Tests/UAlbion.Game.Tests/MockModApplier.cs +++ b/src/Tests/UAlbion.Game.Tests/MockModApplier.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Text.RegularExpressions; using UAlbion.Api.Eventing; using UAlbion.Config; using UAlbion.Formats; @@ -12,14 +11,19 @@ namespace UAlbion.Game.Tests; public class MockModApplier : ServiceComponent, IModApplier { readonly Dictionary _assets = new(); - readonly Dictionary _infos = new(); + readonly Dictionary _infos = new(); public void LoadMods(AssetMapping mapping, IPathResolver pathResolver, IReadOnlyList mods) { } - public AssetInfo GetAssetInfo(AssetId id, string language) => _infos[id]; - public object LoadAsset(AssetId id) => _assets[id]; + public AssetNode GetAssetInfo(AssetId id, string language) => _infos[id]; public object LoadAsset(AssetId id, string language) => _assets[id]; - public object LoadAssetCached(AssetId id) => _assets[id]; + public object LoadAssetCached(AssetId id, string language) => _assets[id]; public string LoadAssetAnnotated(AssetId id, string language) => throw new NotImplementedException(); + public AssetLoadResult LoadAssetAndNode(AssetId assetId, string language = null) + { + var asset = LoadAsset(assetId, language); + var node = GetAssetInfo(assetId, language); + return new AssetLoadResult(assetId, asset, node); + } public SavedGame LoadSavedGame(string path) => throw new NotImplementedException(); public IReadOnlyDictionary Languages { get; } @@ -27,10 +31,7 @@ public void LoadMods(AssetMapping mapping, IPathResolver pathResolver, IReadOnly new Dictionary()); public IEnumerable ShaderPaths => Array.Empty(); - public void SaveAssets(IModApplier.AssetLoader loaderFunc, Action flushCacheFunc, ISet ids, ISet assetTypes, Regex filePattern) - { - throw new NotImplementedException(); - } + public void SaveAssets(AssetConversionOptions options) => throw new NotImplementedException(); public MockModApplier Add(AssetId id, object asset) { @@ -38,7 +39,7 @@ public MockModApplier Add(AssetId id, object asset) return this; } - public MockModApplier AddInfo(AssetId id, AssetInfo asset) + public MockModApplier AddInfo(AssetId id, AssetNode asset) { _infos[id] = asset; return this; diff --git a/src/Tests/UAlbion.Scripting.Tests/TestUtil.cs b/src/Tests/UAlbion.Scripting.Tests/TestUtil.cs index 569003c8e..b4dd6b38a 100644 --- a/src/Tests/UAlbion.Scripting.Tests/TestUtil.cs +++ b/src/Tests/UAlbion.Scripting.Tests/TestUtil.cs @@ -23,7 +23,7 @@ static string Probe(string start) { var curDir = new DirectoryInfo(start ?? throw new InvalidOperationException()); - while (curDir != null && !File.Exists(Path.Combine(curDir.FullName, "mods", "Base", "base_assets.json"))) + while (curDir != null && !File.Exists(Path.Combine(curDir.FullName, "mods", "Base", "types.json"))) curDir = curDir.Parent; return curDir?.FullName; diff --git a/src/Tests/UAlbion.TestCommon/Asset.cs b/src/Tests/UAlbion.TestCommon/Asset.cs index e7bb9f0f9..2f668a046 100644 --- a/src/Tests/UAlbion.TestCommon/Asset.cs +++ b/src/Tests/UAlbion.TestCommon/Asset.cs @@ -14,7 +14,7 @@ namespace UAlbion.TestCommon; public static class Asset { static readonly XldContainer XldLoader = new(); - public delegate T SerdesFunc(T x, ISerializer s, SerdesContext context) where T : class; + public delegate T SerdesFunc(T x, ISerializer s, AssetLoadContext context) where T : class; public static void Compare( string resultDir, string testName, @@ -57,7 +57,7 @@ static string ReadToEnd(Stream stream) return reader.ReadToEnd(); } - public static (T, string) Load(byte[] bytes, SerdesFunc serdes, SerdesContext context) where T : class + public static (T, string) Load(byte[] bytes, SerdesFunc serdes, AssetLoadContext context) where T : class { using var stream = new MemoryStream(bytes); using var br = new BinaryReader(stream); @@ -91,7 +91,7 @@ public static (T, string) Load(byte[] bytes, SerdesFunc serdes, SerdesCont return (result, annotation); } - public static (byte[], string) Save(T asset, SerdesFunc serdes, SerdesContext context) where T : class + public static (byte[], string) Save(T asset, SerdesFunc serdes, AssetLoadContext context) where T : class { using var ms = new MemoryStream(); using var bw = new BinaryWriter(ms); @@ -184,9 +184,9 @@ public static T LoadJson(string json, IJsonUtil jsonUtil) return jsonUtil.Deserialize(Encoding.UTF8.GetBytes(json)); } - public static byte[] BytesFromXld(IPathResolver conf, string path, AssetInfo info, SerdesContext context) + public static byte[] BytesFromXld(IPathResolver conf, string path, AssetLoadContext context) { - using var s = XldLoader.Read(conf.ResolvePath(path), info, context); + using var s = XldLoader.Read(conf.ResolvePath(path), context); return s.Bytes(null, null, (int)s.BytesRemaining); } } \ No newline at end of file diff --git a/src/Tests/UAlbion.TestCommon/MockFileSystem.cs b/src/Tests/UAlbion.TestCommon/MockFileSystem.cs index ffbd740b1..03cdcde12 100644 --- a/src/Tests/UAlbion.TestCommon/MockFileSystem.cs +++ b/src/Tests/UAlbion.TestCommon/MockFileSystem.cs @@ -181,7 +181,7 @@ public bool DirectoryExists(string path) } } - public IEnumerable EnumerateDirectory(string path, string filter = null) + public IEnumerable EnumerateFiles(string path, string filter = null) { lock (_syncRoot) { @@ -216,6 +216,37 @@ public IEnumerable EnumerateDirectory(string path, string filter = null) } } + public IEnumerable EnumerateDirectories(string path) + { + lock (_syncRoot) + { + path = ToAbsolutePath(path); + if (GetDir(path) is not DirNode dir) + yield break; + + if (Directory.Exists(path)) + { + var actualDirectories = Directory.EnumerateDirectories(path); + + foreach (var filePath in actualDirectories) + { + if (!_maskingFunc(filePath)) // If it's hidden, ignore it + continue; + + var filename = Path.GetFileName(filePath); + if (dir.ContainsKey(filename)) // If a mocked copy doesn't exist yet, create one + continue; + + yield return filePath; + } + } + + foreach (var kvp in dir) + if (kvp.Value is DirNode dirNode) + yield return dirNode.Path; + } + } + public Stream OpenRead(string path) { lock (_syncRoot) diff --git a/src/Tests/UAlbion.TestCommon/MockFileSystemChild.cs b/src/Tests/UAlbion.TestCommon/MockFileSystemChild.cs index eba2a032b..1e65687db 100644 --- a/src/Tests/UAlbion.TestCommon/MockFileSystemChild.cs +++ b/src/Tests/UAlbion.TestCommon/MockFileSystemChild.cs @@ -21,7 +21,8 @@ public MockFileSystemChild(MockFileSystem parent, string currentDirectory) public IFileSystem Duplicate(string currentDirectory) => _parent.Duplicate(currentDirectory); public bool FileExists(string path) => _parent.FileExists(ToAbsolutePath(path)); public bool DirectoryExists(string path) => _parent.DirectoryExists(ToAbsolutePath(path)); - public IEnumerable EnumerateDirectory(string path, string filter = null) => _parent.EnumerateDirectory(ToAbsolutePath(path), filter); + public IEnumerable EnumerateFiles(string path, string filter = null) => _parent.EnumerateFiles(ToAbsolutePath(path), filter); + public IEnumerable EnumerateDirectories(string path) => _parent.EnumerateDirectories(ToAbsolutePath(path)); public void CreateDirectory(string path) => _parent.CreateDirectory(ToAbsolutePath(path)); public Stream OpenRead(string path) => _parent.OpenRead(ToAbsolutePath(path)); public Stream OpenWriteTruncate(string path) => _parent.OpenWriteTruncate(ToAbsolutePath(path)); diff --git a/src/Tests/UAlbion.TestCommon/StubFileSystem.cs b/src/Tests/UAlbion.TestCommon/StubFileSystem.cs index f4948374b..b49b906f5 100644 --- a/src/Tests/UAlbion.TestCommon/StubFileSystem.cs +++ b/src/Tests/UAlbion.TestCommon/StubFileSystem.cs @@ -12,7 +12,8 @@ public class StubFileSystem : IFileSystem public IFileSystem Duplicate(string currentDirectory) => throw new NotSupportedException(); public bool FileExists(string path) => throw new NotSupportedException(); public bool DirectoryExists(string path) => throw new NotSupportedException(); - public IEnumerable EnumerateDirectory(string path, string filter = null) => throw new NotSupportedException(); + public IEnumerable EnumerateFiles(string path, string filter = null) => throw new NotSupportedException(); + public IEnumerable EnumerateDirectories(string path) => throw new NotSupportedException(); public void CreateDirectory(string path) => throw new NotSupportedException(); public Stream OpenRead(string path) => throw new NotSupportedException(); public Stream OpenWriteTruncate(string path) => throw new NotSupportedException(); diff --git a/src/Tools/BuildTestingMaps/Program.cs b/src/Tools/BuildTestingMaps/Program.cs index e85da9f7b..1862f21f9 100644 --- a/src/Tools/BuildTestingMaps/Program.cs +++ b/src/Tools/BuildTestingMaps/Program.cs @@ -2,6 +2,7 @@ using UAlbion.Api; using UAlbion.Base; using UAlbion.Config; +using UAlbion.Config.Properties; using UAlbion.Formats; using static BuildTestingMaps.Constants; @@ -52,10 +53,14 @@ void Merge(Dictionary newAssets) Merge(AutoJumpMap.Build((Map)300, (Map)100, 7, 7, tileset1)); - (object? asset, AssetInfo? info) LoaderFunc(AssetId id, string lang) - => assets.TryGetValue(id, out var asset) - ? (asset, new AssetInfo(new Dictionary { { AssetProperty.PaletteId, Palette1Id.Id } })) - : (null, null); + AssetLoadResult LoaderFunc(AssetId assetId, string language) + { + var node = new AssetNode(assetId); + node.SetProperty(AssetProps.Palette, Palette1Id); + return assets.TryGetValue(assetId, out var asset) + ? new AssetLoadResult(assetId, asset, null) + : null!; + } // Create 3D lab graphics // Create 3D lab data @@ -84,8 +89,9 @@ change_icon npcnum 0 AbsTemp NpcSprite id mem monitoring, movement details */ - testExchange.Resolve().SaveAssets(LoaderFunc, () => { }, assets.Keys.ToHashSet(), null, null); - repackedExchange.Resolve().SaveAssets(LoaderFunc, () => { }, assets.Keys.ToHashSet(), null, null); + var options = new AssetConversionOptions(LoaderFunc, () => { }, assets.Keys.ToHashSet(), null, null, null); + testExchange.Resolve().SaveAssets(options); + repackedExchange.Resolve().SaveAssets(options); Console.WriteLine("Done"); } } \ No newline at end of file diff --git a/src/Tools/DumpEnums/Program.cs b/src/Tools/DumpEnums/Program.cs index fe0729fc4..7339e5b52 100644 --- a/src/Tools/DumpEnums/Program.cs +++ b/src/Tools/DumpEnums/Program.cs @@ -15,7 +15,7 @@ public static void Main() var enumsDir = Path.Combine(baseDir, @"src/Base"); var outputDir = @"D:\Depot\AlbionRE\scripts"; - foreach (var file in disk.EnumerateDirectory(enumsDir, "*.cs")) + foreach (var file in disk.EnumerateFiles(enumsDir, "*.cs")) { var lines = disk.ReadAllLines(file); var enums = Collect(ParseLines(lines)); diff --git a/src/Tools/GenerateEnums/Assets.cs b/src/Tools/GenerateEnums/Assets.cs index 2c0703637..56c3b2da8 100644 --- a/src/Tools/GenerateEnums/Assets.cs +++ b/src/Tools/GenerateEnums/Assets.cs @@ -10,7 +10,7 @@ namespace UAlbion.CodeGenerator; public class Assets { public string BaseDir { get; } - public AssetConfig AssetConfig { get; } + public IReadOnlyDictionary IdTypes { get; } public AssetIdConfig AssetIdConfig { get; } public Dictionary ParentsByAssetId { get; } public Dictionary AssetIdsByEnum { get; } @@ -26,13 +26,15 @@ public Assets(string appName, IFileSystem disk, IJsonUtil jsonUtil, string asset var modDisk = disk.Duplicate(config.ResolvePath($"$(MODS)/{modName}")); var modConfig = ModConfig.Load(ModConfig.ModConfigFilename, modDisk, jsonUtil); - AssetConfig = AssetConfig.Load(modConfig.AssetConfig, null, AssetMapping.Global, modDisk, jsonUtil); + + var typeConfigLoader = new TypeConfigLoader(jsonUtil); + IdTypes = typeConfigLoader.LoadIdTypesOnly(config.ResolvePath(modConfig.TypeConfig), modDisk); AssetIdConfig = AssetIdConfig.Load(assetIdConfigPath, modDisk, jsonUtil); AssetIdsByType = FindAssetIdsByType(AssetIdConfig); ParentsByAssetId = FindAssetIdParents(AssetIdConfig, AssetIdsByType); - AssetIdsByEnum = FindAssetIdsForEnums(AssetConfig.IdTypes, AssetIdsByType); - EnumsByAssetId = FindEnumsByAssetId(AssetConfig.IdTypes, AssetIdsByType); + AssetIdsByEnum = FindAssetIdsForEnums(IdTypes, AssetIdsByType); + EnumsByAssetId = FindEnumsByAssetId(IdTypes, AssetIdsByType); // HandleIsomorphism(AssetConfig.IdTypes); // TODO: Build family based on IsomorphicToAttribute. diff --git a/src/Tools/SingleAssetConverter/Program.cs b/src/Tools/SingleAssetConverter/Program.cs index 82d7963ad..950db971c 100644 --- a/src/Tools/SingleAssetConverter/Program.cs +++ b/src/Tools/SingleAssetConverter/Program.cs @@ -9,70 +9,9 @@ namespace UAlbion.SingleAssetConverter; static class Program { + #if false // TODO static readonly AssetLoaderRegistry Registry = new(); - static readonly Dictionary Loaders = new() - { - { "amorphous", "UAlbion.Formats.Parsers.AmorphousSpriteLoader, UAlbion.Formats" }, - { "automap", "UAlbion.Formats.Parsers.AutomapLoader, UAlbion.Formats" }, - { "block", "UAlbion.Formats.Parsers.BlockListLoader, UAlbion.Formats" }, - { "chest", "UAlbion.Formats.Parsers.ChestLoader, UAlbion.Formats" }, - { "dummy", "UAlbion.Formats.Parsers.DummyLoader, UAlbion.Formats" }, - { "eset", "UAlbion.Formats.Parsers.EventSetLoader, UAlbion.Formats" }, - { "eventSetScript", "UAlbion.Game.Assets.EventSetScriptLoader, UAlbion.Game" }, - { "fixedsize", "UAlbion.Formats.Parsers.FixedSizeSpriteLoader, UAlbion.Formats" }, - { "flic", "UAlbion.Formats.Parsers.FlicLoader, UAlbion.Formats" }, - { "afont", "UAlbion.Game.Assets.FontSpriteLoader`1[[UAlbion.Formats.Parsers.FixedSizeSpriteLoader, UAlbion.Formats]], UAlbion.Game" }, - { "vfont", "UAlbion.Game.Assets.FontSpriteLoader`1[[UAlbion.Game.Veldrid.Assets.PngSheetLoader, UAlbion.Game.Veldrid]], UAlbion.Game" }, - { "header", "UAlbion.Formats.Parsers.SingleHeaderSpriteLoader, UAlbion.Formats" }, - { "interlaced", "UAlbion.Formats.Parsers.InterlacedBitmapLoader, UAlbion.Formats" }, - { "itemNameCollector", "UAlbion.Game.Assets.ItemNameCollector, UAlbion.Game" }, - { "itemdata", "UAlbion.Game.Assets.ItemDataLoader, UAlbion.Game" }, - { "itemname", "UAlbion.Formats.Parsers.ItemNameLoader, UAlbion.Formats" }, - { "json", "UAlbion.Formats.Parsers.JsonStringLoader, UAlbion.Formats" }, - { "jsonEventSet", "UAlbion.Formats.Parsers.JsonLoader`1[[UAlbion.Formats.Assets.EventSet, UAlbion.Formats]], UAlbion.Formats" }, - { "jsonInv", "UAlbion.Formats.Parsers.JsonLoader`1[[UAlbion.Formats.Assets.Inventory, UAlbion.Formats]], UAlbion.Formats" }, - { "jsonItems", "UAlbion.Formats.Parsers.JsonLoader`1[[UAlbion.Formats.Assets.ItemData, UAlbion.Formats]], UAlbion.Formats" }, - { "jsonLab", "UAlbion.Formats.Parsers.JsonLoader`1[[UAlbion.Formats.Assets.Labyrinth.LabyrinthData, UAlbion.Formats]], UAlbion.Formats" }, - { "jsonMonsterGroup", "UAlbion.Formats.Parsers.JsonLoader`1[[UAlbion.Formats.Assets.MonsterGroup, UAlbion.Formats]], UAlbion.Formats" }, - { "jsonPal", "UAlbion.Formats.Parsers.JsonLoader`1[[UAlbion.Formats.Assets.AlbionPalette, UAlbion.Formats]], UAlbion.Formats" }, - { "jsonSheet", "UAlbion.Formats.Parsers.JsonLoader`1[[UAlbion.Formats.Assets.CharacterSheet, UAlbion.Formats]], UAlbion.Formats" }, - { "jsonSpell", "UAlbion.Formats.Parsers.JsonLoader`1[[UAlbion.Formats.Assets.SpellData, UAlbion.Formats]], UAlbion.Formats" }, - { "jsonText", "UAlbion.Formats.Parsers.JsonLoader`1[[UAlbion.Formats.Assets.ListStringCollection, UAlbion.Formats]], UAlbion.Formats" }, - { "jsonTileset", "UAlbion.Formats.Parsers.JsonLoader`1[[UAlbion.Formats.Assets.Maps.TilesetData, UAlbion.Formats]], UAlbion.Formats" }, - { "lab", "UAlbion.Formats.Parsers.LabyrinthDataLoader, UAlbion.Formats" }, - { "map", "UAlbion.Formats.Parsers.MapLoader, UAlbion.Formats" }, - { "merchant", "UAlbion.Formats.Parsers.MerchantLoader, UAlbion.Formats" }, - { "mongrp", "UAlbion.Formats.Parsers.MonsterGroupLoader, UAlbion.Formats" }, - { "multiheader", "UAlbion.Formats.Parsers.MultiHeaderSpriteLoader, UAlbion.Formats" }, - { "pal", "UAlbion.Formats.Parsers.PaletteLoader, UAlbion.Formats" }, - { "png", "UAlbion.Game.Veldrid.Assets.PngLoader, UAlbion.Game.Veldrid" }, - { "pngsheet", "UAlbion.Game.Veldrid.Assets.PngSheetLoader, UAlbion.Game.Veldrid" }, - { "raw", "UAlbion.Formats.Parsers.RawLoader, UAlbion.Formats" }, - { "sample", "UAlbion.Formats.Parsers.SampleLoader, UAlbion.Formats" }, - { "script", "UAlbion.Formats.Parsers.ScriptLoader, UAlbion.Formats" }, - { "sheet", "UAlbion.Game.Assets.CharacterSheetLoader, UAlbion.Game" }, - { "slab", "UAlbion.Formats.Parsers.SlabLoader, UAlbion.Formats" }, - { "song", "UAlbion.Formats.Parsers.SongLoader, UAlbion.Formats" }, - { "soundbank", "UAlbion.Game.Assets.SoundBankLoader, UAlbion.Game" }, - { "spell", "UAlbion.Formats.Parsers.SpellLoader, UAlbion.Formats" }, - { "stext", "UAlbion.Formats.Parsers.SystemTextLoader, UAlbion.Formats" }, - { "stringtable", "UAlbion.Formats.Parsers.AlbionStringTableLoader, UAlbion.Formats" }, - { "tiledLabyrinth", "UAlbion.Game.Veldrid.Assets.IsometricLabyrinthLoader, UAlbion.Game.Veldrid" }, - { "tiledMap", "UAlbion.Game.Assets.TiledMapLoader, UAlbion.Game" }, - { "tiledNpcTileset", "UAlbion.Game.Veldrid.Assets.NpcTilesetLoader, UAlbion.Game.Veldrid" }, - { "tiledStamp", "UAlbion.Formats.Exporters.Tiled.StampLoader, UAlbion.Formats" }, - { "tiledTileGfx", "UAlbion.Formats.Exporters.Tiled.TileGraphicsLoader, UAlbion.Formats" }, - { "tiledTileset", "UAlbion.Game.Assets.TiledTilesetLoader, UAlbion.Game" }, - { "tileset", "UAlbion.Formats.Parsers.TilesetLoader, UAlbion.Formats" }, - { "utf8", "UAlbion.Formats.Parsers.Utf8Loader, UAlbion.Formats" }, - { "wav", "UAlbion.Formats.Parsers.WavLoader, UAlbion.Formats" }, - { "wavlib", "UAlbion.Formats.Parsers.WaveLibLoader, UAlbion.Formats" }, - { "wavlibwav", "UAlbion.Formats.Parsers.WaveLibWavLoader, UAlbion.Formats" }, - { "wordCollector", "UAlbion.Game.Assets.WordCollector, UAlbion.Game" }, - { "wordlist", "UAlbion.Formats.Parsers.WordListLoader, UAlbion.Formats" }, - }; - - record Options(IAssetLoader Loader, IAssetLoader Saver, string InputPath, string OutputPath, int? SubItem, AssetInfo Info); + record Options(IAssetLoader Loader, IAssetLoader Saver, string InputPath, string OutputPath, int? SubItem, AssetNode Info); static Options? ParseCommandLine(string[] args) { if (args.Length < 4) { Console.WriteLine("Not enough arguments"); return null; } @@ -97,7 +36,7 @@ record Options(IAssetLoader Loader, IAssetLoader Saver, string InputPath, string if (saver == null) { Console.WriteLine($"Could not find loader \"{saverAlias}\""); return null; } if (!File.Exists(inputPath)) { Console.WriteLine($"Could not find file {inputPath}"); return null; } - var info = new AssetInfo(); + var node = new AssetNode(); for (int i = 4; i < args.Length; i++) { var arg = args[i]; @@ -108,16 +47,18 @@ record Options(IAssetLoader Loader, IAssetLoader Saver, string InputPath, string var key = arg[..index]; var value = arg[(index + 1)..]; if (int.TryParse(value, out var asInt)) - info.Set(key, asInt); + node.SetProperty(key, asInt); else - info.Set(key, value); + node.SetProperty(key, value); } return new Options(loader, saver, inputPath, outputPath, subItem, info); } + #endif public static void Main(string[] args) { + #if false var options = ParseCommandLine(args); if (options == null) { @@ -137,21 +78,22 @@ public static void Main(string[] args) var disk = new FileSystem(Directory.GetCurrentDirectory()); var jsonUtil = new FormatJsonUtil(); - var context = new SerdesContext("AdHoc", jsonUtil, AssetMapping.Global, disk); + var modContext = new ModContext("AdHoc", jsonUtil, disk, AssetMapping.Global); var container = options.SubItem.HasValue ? (IAssetContainer)new XldContainer() : new RawContainer(); if (options.SubItem.HasValue) options.Info.Index = options.SubItem.Value; - using var inputSerializer = container.Read(options.InputPath, options.Info, context); + var context = new AssetLoadContext(options.AssetId, options.Info, modContext); + using var inputSerializer = container.Read(options.InputPath, context); if (inputSerializer == null) { Console.WriteLine($"Could not extract sub-asset {options.SubItem} from {options.InputPath}"); return; } - var asset = options.Loader.Serdes(null, options.Info, inputSerializer, context); - File.WriteAllBytes(options.OutputPath, FormatUtil.SerializeToBytes(s => options.Saver.Serdes(asset, options.Info, s, context))); + var asset = options.Loader.Serdes(null, inputSerializer, context); + File.WriteAllBytes(options.OutputPath, FormatUtil.SerializeToBytes(s => options.Saver.Serdes(asset, s, context))); } static IAssetLoader? Resolve(string alias) => !Loaders.TryGetValue(alias, out var loaderName) ? null : Registry.GetLoader(loaderName); @@ -167,5 +109,6 @@ static void ShowUsage() var maxLen = Loaders.Max(x => x.Key.Length); foreach (var loader in Loaders.OrderBy(x => x.Key)) Console.WriteLine($" {loader.Key.PadRight(maxLen)} : {loader.Value}"); + #endif } } \ No newline at end of file diff --git a/src/UAlbion/AssetSystem.cs b/src/UAlbion/AssetSystem.cs index c9ec9ee81..a60607e25 100644 --- a/src/UAlbion/AssetSystem.cs +++ b/src/UAlbion/AssetSystem.cs @@ -60,8 +60,8 @@ public static EventExchange Setup( .Add(new PostProcessorRegistry()) .Add(new VarRegistry()) .Add(new AssetLocator()); - PerfTracker.StartupEvent("Registered asset services"); + PerfTracker.StartupEvent("Registered asset services"); IModApplier modApplier = new ModApplier(); exchange.Attach(modApplier); modApplier.LoadMods(mapping, pathResolver, mods ?? UserVars.Gameplay.ActiveMods.Read(settings)); diff --git a/src/UAlbion/CommandLineOptions.cs b/src/UAlbion/CommandLineOptions.cs index ad1f691a5..4e10495b4 100644 --- a/src/UAlbion/CommandLineOptions.cs +++ b/src/UAlbion/CommandLineOptions.cs @@ -25,6 +25,7 @@ class CommandLineOptions public List Mods { get; } public DumpFormats DumpFormats { get; } = DumpFormats.Json; public ISet DumpAssetTypes { get; } + public string[] DumpLanguages { get; } public CommandLineOptions(string[] args) { @@ -108,6 +109,18 @@ public CommandLineOptions(string[] args) DumpIds = args[i].Split(' ', StringSplitOptions.RemoveEmptyEntries); } + if (arg is "--LANGUAGES" or "--LANG" or "-L") + { + i++; + if (i == args.Length) + { + Console.WriteLine("\"-l\" requires an argument specifying the languages to process"); + Mode = ExecutionMode.Exit; + return; + } + DumpLanguages = args[i].ToUpperInvariant().Split(' ', StringSplitOptions.RemoveEmptyEntries); + } + if (arg is "--FILES" or "-F") { i++; diff --git a/src/UAlbion/DumpGraphics.cs b/src/UAlbion/DumpGraphics.cs index 4b81715a0..fa3e0ff19 100644 --- a/src/UAlbion/DumpGraphics.cs +++ b/src/UAlbion/DumpGraphics.cs @@ -7,6 +7,7 @@ using UAlbion.Api.Eventing; using UAlbion.Api.Visual; using UAlbion.Config; +using UAlbion.Config.Properties; using UAlbion.Core.Veldrid.Textures; using UAlbion.Formats; using UAlbion.Formats.Assets; @@ -91,7 +92,7 @@ public IList ExportImage( AlbionPalette palette; if (config != null) { - var rawPaletteId = config.Get(AssetProperty.PaletteId, 0); + var rawPaletteId = config.GetProperty(AssetProps.Palette); var paletteId = new PaletteId(rawPaletteId); palette = assets.LoadPalette(paletteId); } diff --git a/src/UAlbion/DumpText.cs b/src/UAlbion/DumpText.cs index 346480b43..9c893638e 100644 --- a/src/UAlbion/DumpText.cs +++ b/src/UAlbion/DumpText.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; using UAlbion.Api.Eventing; @@ -250,7 +249,7 @@ static void MapData(IAssetManager assets, string baseDir, AssetId[] dumpIds) if (npc.Chain != 0xffff) { sw.WriteLine($" EventChain: {npc.Chain}"); - var formatter = new EventFormatter(assets.LoadString, id.ToMapText()); + var formatter = new EventFormatter(assets.LoadStringSafe, id.ToMapText()); var builder = new UnformattedScriptBuilder(false); formatter.FormatChain(builder, npc.Node, 2); sw.WriteLine(builder.Build()); @@ -333,7 +332,7 @@ static void DumpCharacterSheet(SheetId id, CharacterSheet c, StreamWriter sw, IA { if (e.Event is TextEvent textEvent) { - var textSource = tf.Format(textEvent.ToId(eventSet.TextId)); + var textSource = tf.Format(textEvent.ToId(eventSet.StringSetId)); var text = string.Join(", ", textSource.GetBlocks().Select(x => x.Text)); sw.WriteLine($" {e} = {text}"); } @@ -457,7 +456,7 @@ static void PrintEvent(IScriptBuilder builder, EventFormatter formatter, IEventN static void DumpMapEvents(StreamWriter sw, IAssetManager assets, MapId mapId, IMapData map) { - var formatter = new EventFormatter(assets.LoadString, mapId.ToMapText()); + var formatter = new EventFormatter(assets.LoadStringSafe, mapId.ToMapText()); sw.WriteLine(); sw.WriteLine($"Map {mapId.Id} {mapId}:"); var builder = new UnformattedScriptBuilder(false); @@ -511,7 +510,7 @@ static void EventSets(IAssetManager assets, string baseDir, AssetId[] dumpIds) if (set == null) continue; - var formatter = new EventFormatter(assets.LoadString, ((EventSetId)eventSetId).ToEventText()); + var formatter = new EventFormatter(assets.LoadStringSafe, ((EventSetId)eventSetId).ToEventText()); var builder = new UnformattedScriptBuilder(false); foreach (var e in set.Events) { @@ -528,7 +527,7 @@ static void Spells(IAssetManager assets, string baseDir, AssetId[] dumpIds) foreach (var spellId in Ids(dumpIds)) { var spell = assets.LoadSpell(spellId); - var name = assets.LoadString(spell.Name); + var name = assets.LoadStringSafe(spell.Name); //int classNumber = (int)spellId / SpellData.MaxSpellsPerClass; //int offsetInClass = (int)spellId % SpellData.MaxSpellsPerClass; sw.Write($"Spell{spellId.Id:D3} {spell.Class}_{spell.OffsetInClass} "); diff --git a/src/UAlbion/FrameStats.cs b/src/UAlbion/FrameStats.cs index 39a84c8d2..413c47410 100644 --- a/src/UAlbion/FrameStats.cs +++ b/src/UAlbion/FrameStats.cs @@ -4,6 +4,7 @@ using UAlbion.Api; using UAlbion.Api.Visual; using UAlbion.Config; +using UAlbion.Config.Properties; using UAlbion.Formats; using UAlbion.Formats.Ids; @@ -21,8 +22,7 @@ static void StatsForEnum(IAssetManager assets, Dictionary dict) if (texture == null) continue; - var palleteId = new PaletteId(info.Get(AssetProperty.PaletteId, 0)); - var palette = assets.LoadPalette(palleteId); + var palette = assets.LoadPalette(info.PaletteId); var frames = texture.Regions.Count; var uniqueColours = new HashSet(); diff --git a/src/UAlbion/Program.cs b/src/UAlbion/Program.cs index 8275690f7..7685c3ac3 100644 --- a/src/UAlbion/Program.cs +++ b/src/UAlbion/Program.cs @@ -60,10 +60,15 @@ static void Main(string[] args) commandLine.ConvertFrom, commandLine.ConvertTo); + var languages = commandLine.DumpLanguages ?? converter.DiscoverLanguages(); + + var parsedIds = commandLine.DumpIds?.Select(AssetId.Parse).ToHashSet(); converter.Convert( - commandLine.DumpIds, + parsedIds, commandLine.DumpAssetTypes, - commandLine.ConvertFilePattern); + commandLine.ConvertFilePattern, + null, + languages); return; }