commit 8ee38dfc1d94096ffa2e7b1a8f9f46c21acd7fa8 Author: Anna Clemens Date: Sat Mar 5 18:26:14 2022 -0500 chore: initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..447496e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,398 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "159bb86af3a200e19a068f4224eae4c8bb2d0fa054c7e5d1cacd5cef95e684cd" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "crossbeam-channel" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" +dependencies = [ + "cfg-if", + "lazy_static", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "ffxii-tza-auto-notes" +version = "0.1.0" +dependencies = [ + "anyhow", + "itertools", + "lazy_static", + "maplit", + "process_list", + "serde", + "serde_yaml", + "sysinfo", + "vmemory", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "indexmap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "nix" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "ntapi" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97ba11b4249e387303cc3a41ac79d7bdf8382ad72b64621a77bac197b75a73e2" +dependencies = [ + "winapi", +] + +[[package]] +name = "ntapi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" +dependencies = [ + "winapi", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "process_list" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1132b222742f1d1bbe2078836970d9eac85cb354076b58534edc29aa14668ff0" +dependencies = [ + "winapi", +] + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_yaml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "sysinfo" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07fa4c84a5305909b0eedfcc8d1f2fafdbede645bb700a45ecaafe681a0ac5d6" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi 0.3.7", + "once_cell", + "rayon", + "winapi", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "vmemory" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe1e3c3facc6d67d5972f73b14189930ca96cd3ec4f7df151863f4377480e8c0" +dependencies = [ + "mach", + "nix", + "ntapi 0.2.0", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a0c0fe2 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "ffxii-tza-auto-notes" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1" +vmemory = "0.1" +sysinfo = "0.23" +process_list = "0.2" +itertools = "0.10" +serde = { version = "1", features = ["derive"] } +serde_yaml = "0.8" +maplit = "1" +lazy_static = "1" diff --git a/notes.yaml b/notes.yaml new file mode 100644 index 0000000..8ae4466 --- /dev/null +++ b/notes.yaml @@ -0,0 +1,841 @@ +steps: + # 0: title screen/into cutscenes + - stage: 0 + areas: + # 274: aerial gardens + - area: 274 + steps: |- + - **Skip**: 3 + # 13: control of reks + - stage: 13 + areas: + # 274: aerial gardens + - area: 274 + steps: |- + - Talk to Basch and the soldier + - Open the gate + # 16: gone through gate + - stage: 16 + areas: + # 275: inner ward + - area: 275 + steps: |- + - Menu + 1. Active + 2. Battle speed: fast + 3. Cursor position: last selection + - **Skip**: 1 + - Kill Remora with Thunder spam + - **Skip**: 1 + - Go upstairs + # 19: after door after remora + - stage: 19 + areas: + # 279: lower apartments + - area: 279 + steps: |- + - **Skip**: 1 + - Go forward and up the stairs to save crystal + # 21: after save tutorial cutscene + - stage: 21 + areas: + # 279: lower apartments + # 280: upper apartments + # 282: the highhall + - area: 280 + steps: |- + - Go upstairs + - **Skip**: 1 + - Kill the guards + - Go into throne room + - **Skip**: 3 + # 22: reks decides to kill swordsmen + # 23: swordsmen dead + # 25: control of vaan + - stage: 25 + areas: + # 1145: overflow cloaca + - area: 1145 + steps: |- + - Kill rats + - **Skip**: 3 + # 35: map tutorial opens + # 40: control of vaan after rat slaughter + - stage: 40 + areas: + # 291: east end + - area: 291 + steps: |- + - South to item shop + - **Skip**: 1 + # 45: after item shop cutscene + - stage: 45 + areas: + # 291: east end + # 304: the sandsea + - area: 291 + steps: |- + - North to Sandsea + - area: 304 + steps: |- + - **Skip**: 1 + - Choose not to hear the details + - Choose not to repeat the message + # 65: control of vaan after picking up rogue tomato + - stage: 65 + areas: + # 304: the sandsea + # 291: east end + # 292: southern plaza + - area: 304 + steps: |- + - Exit + - area: 291 + steps: |- + - Go South + - Exit across from item shop + - area: 292 + steps: |- + - Go to East Gate + - **Skip**: 1 + # 68: after east gate cutscene + - stage: 68 + areas: + # 305: eastgate + # 227: the stepping + - area: 305 + steps: |- + - Enter the Estersand + - area: 227 + steps: |- + - **Skip**: 1 + - Straight ahead to Rogue Tomato + - Attack, follow, attack + - **Skip**: 1 + # 78: killed rogue tomato + - stage: 78 + areas: + # 227: the stepping + # 305: eastgate + - area: 227 + steps: |- + - Return to Rabanastre + - area: 305 + steps: |- + - Talk to Kytes + - **Skip**: 3 + # 85: after first cutscene + # 100: after second cutscene + # 105: control of vaan after talking to kytes + - stage: 105 + areas: + # 289: north end + # 701: north sprawl + # 702: south sprawl + # 703: dalan's house + - area: 289 + steps: |- + - Go straight into Lowtown + - area: 701 + steps: |- + - Exit South + - area: 702 + steps: |- + - South then west to Dalan + - **Skip**: 1 + # 120: after cutscene, talking to dalan + - stage: 120 + areas: + # 703: dalan's house + # 702: south sprawl + # 306: southgate + # 236: throne road (dry) + # 243: nomad village (dry) + - area: 703 + steps: |- + - Exit + - area: 702 + steps: |- + - Exit right to Southgate + - area: 306 + steps: |- + - Exit left to Giza + - area: 236 + steps: |- + - **Skip**: 1 + - Straight ahead to camp + - area: 243 + steps: |- + - Talk to lady by sunstone + - **Skip**: 1 + # 125: cutscene after talking to sunstone lady + # 130: control after penelo joins party + - stage: 130 + areas: + # 243: nomad village (dry) + - area: 243 + steps: |- + - Exit West + # 140: after exiting camp and listening to penelo + - stage: 140 + areas: + # 239: toam hills (dry) + # 247: starfall field (dry) + - area: 239 + steps: |- + - Exit South + - area: 247 + steps: |- + - Quit to title + - stage: 0 + areas: + # 12: main menu + - area: 12 + steps: |- + - Trial mode + - stage: 140 + areas: + # 1155: stage 1 + # 1166: stage 2 + - area: 1155 + steps: |- + - Grab Diamond Armlet, kill rats + - area: 1166 + steps: |- + - Quit to title + - stage: 0 + areas: + # 12: main menu + - area: 12 + steps: |- + - Load autosave + - stage: 140 + areas: + # 247: starfall field (dry) + # 248: crystal glade (dry) + - area: 247 + steps: |- + - Exit Southeast to Jinn + - area: 248 + steps: |- + - Talk to Jinn + # 145: after talking to jinn + - stage: 145 + areas: + # 248: crystal glade (dry) + # 247: starfall field (dry) + # 249: gizas south bank (dry) + # 228: yardang labyrinth + # 630: west barbican + - area: 248 + steps: |- + - Exit West + - area: 247 + steps: |- + - Use West sunstone + - Exit East back to Jinn + - area: 248 + steps: |- + - Exit East + - area: 249 + steps: |- + - Exit Northeast towards Nalbina + - area: 228 + steps: |- + - Go northeast, then north, exit to Nalbina + - area: 630 + steps: |- + - Shop: + 1. Sell Diamond Armlet + 2. Buy 11x Phoenix Down + - Return to Giza + - area: 249 + steps: |- + - Use sunstone + - Exit North + - area: 0 + steps: |- + - Use Northwest sunstone + # 160: after getting sunstone + - stage: 160 + areas: + # 243: nomad village (dry) + # 236: throne road (dry) + # 306: southgate + # 292: southern plaza + # 307: westgate + # 345: galtea downs + # 306: southgate + # 702: south sprawl + - area: 243 + steps: |- + - Shop: + 1. Buy Dark + - Exit North + - area: 236 + steps: |- + - Exit North to Rabanastre + - area: 306 + steps: |- + - Exit North + - area: 292 + steps: |- + - Exit West to Westgate + - area: 307 + steps: |- + - Exit to Westersand + - area: 345 + steps: |- + - **Skip**: 1 + - Northeast, stop before zoning + - Get Vaan to critical HP + ![vaan dustia hp chart](./img/vaan_dustia_hp_chart.png) + - Kill Penelo, set battle speed to slow + - Exit, spawn Dustia, use Phoenix Down to kill + - Kill 12 times total, until level 13 Vaan + - Return to Rabanastre + - area: 307 + steps: |- + - Exit + - area: 292 + steps: |- + - Exit South to Southgate + - area: 306 + steps: |- + - West to Lowtown + - area: 702 + steps: |- + - West to Dalan's place + - **Skip**: 1 + # 170: penelo leaves party, control in dalan's house + - stage: 170 + areas: + # 703: dalan's house + - area: 703 + steps: |- + - Talk to Dalan + - **Skip**: 1 + # 175: obtain crescent stone, control in dalan's house + - stage: 175 + areas: + # 703: dalan's house + # 312: east waterway control (cutscene) + - area: 703 + steps: |- + - Exit + - area: 312 + steps: |- + - **Skip**: 1 + # 180: after leaving dalan's house with crescent stone + - stage: 180 + areas: + # 702: south sprawl + # 701: north sprawl + # 311: central spur stairs + # 315: northern sluiceway + # 313: north spur sluiceway + # 567: cellar stores + - area: 702 + steps: |- + - Exit North + - area: 701 + steps: |- + - North and West to Garamsythe + - Enter unlocked door + - area: 311 + steps: |- + - **Skip**: 1 + - Exit West + - area: 315 + steps: |- + - North, East, exit North + - area: 313 + steps: |- + - West until North stairs + # 190: after going up stairs, fireworks fmv + - stage: 190 + # 576: invitation to heresy (cutscene) + areas: + - area: 576 + steps: |- + - **Skip**: 2 + # 200: after fireworks fmv, control in palace + - stage: 200 + areas: + # 567: cellar stores + # 568: cellars + # 569: lower halls + - area: 567 + steps: |- + - Exit + - area: 568 + steps: |- + - Go straight + - Get East-most (left) chest for Elixir + - North for cutscene + - Approach guard for cutscene + - Talk to servant, shout + - Exit upstairs + - area: 569 + steps: |- + - Straight, South, East, shout at intersection + - West, North, shout at intersection + - East, North, West, use + - West, shout at intersection + - East, North, West to exit at wall + # 210: after entering secret chamber + - stage: 210 + areas: + # 570: secret passage + - area: 570 + steps: |- + - Use switch on back left wall + - Open door + - **Skip**: 1 + # 215: gone through hidden door + # 220: you obtain the goddess's magicite + - stage: 220 + areas: + # 572: the garden stairs + - area: 0 + steps: |- + - **Skip**: 1 + - Exit upstairs + - **Skip**: 2 + # 225: after fmv + # 235: control in garamsythe + - stage: 235 + areas: + # 314: east spur stairs + - area: 314 + steps: |- + - Go down the stairs + - Gambit tutorial + # 244: after gambit tutorial + - stage: 244 + areas: + # 314: east spur stairs + # 316: east waterway control + # 318: no. 11 channel + # 319: east sluice control + - area: 314 + steps: |- + - Menu: + 1. Licence boards + - All: Red battlemage + 2. Licences + - All: Dark + 3. Gambits + - Vaan, Fran: turn on gambits, highest hp: Dark + - Balthier: HP < 50%: Potion, highest hp: Dark + 4. Config + - Battle speed: fast + - Exit ahead + - area: 316 + steps: |- + - West (no stairs), South to exit + - area: 318 + steps: |- + - Follow path South, take branch, exit South + - area: 319 + steps: |- + - Follow path South to downstairs + - **Skip**: 1 + - **Boss**: gambit to win + - **Skip**: 1 + # 257: before ashe cutscene + - stage: 257 + areas: + # 319: east sluice control + - area: 319 + steps: |- + - Approach Ashe + - **Skip**: 1 + # 260: amalia joins + - stage: 260 + areas: + # 319: east sluice control + # 326: southern sluiceway + - area: 319 + steps: |- + - Exit ahead + - area: 326 + steps: |- + - Walk ahead + - **Skip**: 1 + - **Boss**: gambit to win + - **Skip**: 1 + # 265: after flan boss + - stage: 265 + areas: + # 326: southern sluiceway + # 320: west sluice control + # 321: no. 10 channel + # 322: central waterway control + # 329: overflow cloaca + - area: 326 + steps: |- + - Exit Northwest + - area: 320 + steps: |- + - South, West, North, exit + - area: 321 + steps: |- + - North, East, North, exit + - area: 322 + steps: |- + - Through gate + - area: 329 + steps: |- + - **Skip**: 1 + - **Boss**: flee to the left in the water, gambit to win + - **Skip**: 4 + # 285: after post-firemane cutscene + # 290: next cutscene + # 301: next cutscene + # 303: control in nalbina dungeon + - stage: 303 + areas: + # 48: stockade + # 49: arena + # 710: the confiscatory + - area: 48 + steps: |- + - East, then North and exit near Save Crystal + - area: 49 + steps: |- + - **Skip**: 1 + - **Boss**: gambits to win + - **Skip**: 1 + - area: 710 + steps: |- + - Walk forward + - **Skip**: 1 + # 310: control after getting equipment back + - stage: 310 + areas: + # 710: the confiscatory + # 709: the black watch + - area: 710 + steps: |- + - Exit East + - area: 709 + steps: |- + - **Skip**: 1 + - Go all the way North, then West + - **Skip**: 1 + - Exit ahead + - **Skip**: 3 + # 312: cutscene + # 315: after judges open door + # 316: next cutscene + # 317: control in barheim + - stage: 317 + areas: + # 53: the lightworks + - area: 53 + steps: |- + - Down the stairs, East, grab (left) chest for gil + - Touch device + - Continue down stairs, talk to shopkeep + # 319: get fuse + - stage: 319 + areas: + # 53: the lightworks + - area: 53 + steps: |- + - Have Vaan potion, pilot someone else to device + - Install fuse + # 321: used fuse + - stage: 321 + areas: + # 53: the lightworks + # 58: op sector 29 + - area: 53 + steps: |- + - Switch back to Vaan, use device + - Exit + - area: 58 + steps: |- + - **Skip**: 1 + # 323: after mimic cutscene + - stage: 323 + areas: + # 58: op sector 29 + # 54: great eastern passage + # 56: special op sector 3 + # 57: op sector 37 + # 66: north-south junction + - area: 58 + steps: |- + - Follow path South + - Kill mimics stealing charge + - Exit East + - area: 54 + steps: |- + - Kill mimic stealing charge + - Follow path south, killing big mimics + - Left path + - Right path + - Exit South + - area: 56 + steps: |- + - West, kill big mimic + - East, North + - Exit West + - area: 57 + steps: |- + - Use switchboard + - South, kill mimic, West, exit way you came + - area: 56 + steps: |- + - East, exit South (kill mimic) + - area: 66 + steps: |- + - **Skip**: 3 + # 333: after basch cutscenes + - stage: 333 + areas: + # 66: north-south junction + # 61: great central passage + # 62: the zeviah subterrane + # 70: terminus no. 4 adjunct + # 69: terminus no. 4 + - area: 66 + steps: |- + - Exit South + - area: 61 + steps: |- + - Exit South + - area: 62 + steps: |- + - South, loop North (kill mimics as necessary) + - Exit East + - area: 70 + steps: |- + - Exit South + - area: 69 + steps: |- + - **Skip**: 1 + - **Boss**: gambit to win + - **Skip**: 1 + # 340: mimic queen cutscene + # 341: post-mimic queen (to think dalmascan air could taste so sweet) + - stage: 341 + areas: + # 231: passage entrance + - area: 231 + steps: |- + - Unskippable cutscene + # 343: after dalmascan air tastes so sweet + - stage: 343 + areas: + # 231: passage entrance + # 229: sand-swept naze + # 228: yardang labyrinth + # 630: west barbican + # 306: southgate + - area: 231 + steps: |- + - Exit towards camera + - area: 229 + steps: |- + - West-by-northwest, follow path, exit South + - area: 228 + steps: |- + - South, loop East, exit North to Nalbina + - area: 630 + steps: |- + - Teleport to Rabanastre + - area: 306 + steps: |- + - **Skip**: 1 + # 350: control after balthier and fran leave + - stage: 350 + areas: + # 306: southgate + # 702: south sprawl + # 703: dalan's house + - area: 306 + steps: |- + - East to enter Lowtown + - area: 702 + steps: |- + - Enter Dalan's house + - area: 703 + steps: |- + - Talk to Dalan + - **Skip**: 1 + # 375: after getting sword from dalan + - stage: 375 + areas: + # 703: dalan's house + # 702: south sprawl + # 701: north sprawl + - area: 703 + steps: |- + - Exit + - area: 702 + steps: |- + - Exit North + - area: 701 + steps: |- + - Cross over West, go West to hideout + - **Skip**: 2 + # 386: after first cutscene + # 390: control after basch joins + - stage: 390 + areas: + # 701: north sprawl + # 291: east end + - area: 701 + steps: |- + - Back across to the East + - East at intersection + - Exit North + - area: 291 + steps: |- + - North + - **Skip**: 1 + # 400: after basch cutscene outside sandsea + - stage: 400 + areas: + # 291: east end + # 304: the sandsea + - area: 291 + steps: |- + - Enter the Sandsea + - area: 304 + steps: |- + - Go upstairs + - **Skip**: 1 + # 420: after balthier cutscene in sandsea + - stage: 420 + areas: + # 304: the sandsea + # 291: east end + # 307: westgate + # 788: aerodrome (rabanastre) + - area: 304 + steps: |- + - Exit + - area: 291 + steps: |- + - Moogling to Westgate + - area: 307 + steps: |- + - Enter Aerodrome + - area: 788 + steps: |- + - Straight back and talk to Balthier + - **Skip**: 3 + # 460: strahl cutscene + # 520: clouds cutscene + # 546: control in bhujerba + - stage: 546 + areas: + # 791: aerodrome (bhujerba) + # 803: travica way + - area: 791 + steps: |- + - Exit ahead + - area: 803 + steps: |- + - **Skip**: 1 + # 550: larsa joins + - stage: 550 + areas: + # 803: travica way + # 805: miners' end + # 806: lhusu square + - area: 803 + steps: |- + - East, South, exit East + - area: 805 + steps: |- + - East, exit North + - area: 806 + steps: |- + - Go downstairs + - **Skip**: 1 + # 580: control in lhusu + - stage: 580 + areas: + # 357: shaft entry + - area: 357 + steps: |- + - Go downstairs + - **Skip**: 1 + # 610: control after cutscene + - stage: 610 + areas: + # 357: shaft entry + # 358: oltam span + # 359: transitway 1 + # 365: shunia twinspan + # 933: site 2 + - area: 357 + steps: |- + - North, West, second South, exit West + - area: 358 + steps: |- + - Exit ahead + - area: 359 + steps: |- + - Exit North + - area: 365 + steps: |- + - Exit ahead + - area: 933 + steps: |- + - East, follow South path to caves + - **Skip**: 1 + # 645: fleeing + - stage: 645 + areas: + # 933: site 2 + # 934: shunia twinspan + # 935: transitway 1 + # 936: oltam span + # 357: shaft entry + - area: 933 + steps: |- + - South, exit West + - area: 934 + steps: |- + - Exit ahead + - area: 935 + steps: |- + - South, East, exit East + - area: 936 + steps: |- + - Exit ahead + - area: 357 + steps: |- + - **Skip**: 1 + # 660: escaped + - stage: 660 + areas: + # 357: shaft entry + # 806: lhusu square + - area: 357 + steps: |- + - North, East, South to exit + - area: 806 + steps: |- + - **Skip**: 1 + # 680: after lhusu 1 + - stage: 680 + areas: + # 806: lhusu square + - area: 806 + steps: |- + - Upstairs + - **Skip**: 1 + # 710: I'm Captain Basch! + - stage: 710 + areas: + # 806: lhusu square + - area: 806 + steps: |- + - bruh idk lmao diff --git a/src/game_state.rs b/src/game_state.rs new file mode 100644 index 0000000..03f7da2 --- /dev/null +++ b/src/game_state.rs @@ -0,0 +1,98 @@ +use anyhow::{Context, Result}; +use sysinfo::{PidExt, ProcessExt, ProcessRefreshKind, RefreshKind, System, SystemExt}; +use vmemory::ProcessMemory; + +pub struct GameState { + pub story: u16, + pub location: u16, + + pid: u32, + base: usize, + base_size: usize, + mem: Vec, + proc_mem: ProcessMemory, + + story_pattern: Vec, + location_pattern: Vec, +} + +impl GameState { + pub fn new() -> Result { + let mut sys = System::new_with_specifics(RefreshKind::new().with_processes(ProcessRefreshKind::new())); + let proc = sys.processes() + .iter() + .find(|(_, proc)| proc.name() == "FFXII_TZA.exe"); + let (pid, _) = match proc { + Some(p) => p, + None => anyhow::bail!("could not find TZA process") + }; + + let mem = ProcessMemory::attach_process(pid.as_u32()).unwrap(); + let mut base = 0; + let mut base_size = 0; + process_list::for_each_module(pid.as_u32(), |(address, size), name| { + let stem = name.file_stem().unwrap_or_default(); + if stem == "FFXII_TZA" { + base = address; + base_size = size; + } + }).context("could not loop TZA modules")?; + if base == 0 || base_size == 0 { + anyhow::bail!("could not find TZA base address"); + } + + let tza_mem = mem.read_memory(base, base_size, false); + let story_ptr_pattern = crate::util::parse_pattern("48 8B 05 ?? ?? ?? ?? 48 85 C0 74 03 66 89 08 C3").unwrap(); + let location_pattern = crate::util::parse_pattern("8B 0D ?? ?? ?? ?? FF D0 4C 89 3D ?? ?? ?? ?? 48 83 C4 30 41 5F C3").unwrap(); + + Ok(Self { + story: 0, + location: 0, + + pid: pid.as_u32(), + base, + base_size, + mem: tza_mem, + proc_mem: mem, + + story_pattern: story_ptr_pattern, + location_pattern, + }) + } + + pub fn refresh(&mut self) -> Result<()> { + let story_ptr_ptr = match crate::util::find_pattern(&self.mem, &self.story_pattern) { + Some(ptr) => ptr, + None => anyhow::bail!("could not find story pointer"), + }; + let location_ptr = match crate::util::find_pattern(&self.mem, &self.location_pattern) { + Some(ptr) => ptr, + None => anyhow::bail!("could not find location pointer"), + }; + let story_ptr_offset = match crate::util::get_static_address(&self.mem, story_ptr_ptr, self.base) { + Some(addr) => addr, + None => anyhow::bail!("could not find story pointer offset"), + }; + let location_ptr_offset = match crate::util::get_static_address(&self.mem, location_ptr, self.base) { + Some(addr) => addr, + None => anyhow::bail!("could not find location pointer offset"), + }; + + let story_ptr_array: [u8; 8] = self.mem[story_ptr_offset..story_ptr_offset + 8].try_into().unwrap(); + let story_ptr = u64::from_le_bytes(story_ptr_array) as usize - self.base; + + let story_vec = self.proc_mem.read_memory(self.base + story_ptr, 2, false); + let story_array: [u8; 2] = story_vec.try_into().map_err(|_| anyhow::anyhow!("not enough story bytes"))?; + self.story = u16::from_le_bytes(story_array); + + let location_vec = self.proc_mem.read_memory(self.base + location_ptr_offset, 2, false); + let location_array: [u8; 2] = location_vec.try_into().map_err(|_| anyhow::anyhow!("not enough location bytes"))?; + self.location = u16::from_le_bytes(location_array); + + Ok(()) + } + + pub fn location_name(&self) -> Option<&'static str> { + crate::util::LOCATIONS.get(&self.location).map(std::ops::Deref::deref) + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..1c73bb5 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,76 @@ +use anyhow::Result; +use std::fs::File; +use std::thread; +use crate::notes::Notes; +use crate::game_state::GameState; +use crate::notes_state::NotesState; + +mod notes; +mod game_state; +mod notes_state; +mod util; + +fn main() -> Result<()> { + let notes: Notes = serde_yaml::from_reader(File::open("notes.yaml")?)?; + let mut game_state = GameState::new()?; + let mut notes_state = NotesState::new(¬es, &mut game_state)?; + + println!("Ready"); + + // let mut first = true; + // let mut last_story = 0; + // let mut last_location = 0; + // let mut last_printed_location = 0; + // let mut step_idx = 23; + // let mut area_idx = 0; + // let mut current_step = ¬es.steps[0]; + // let mut force_instructions = false; + + loop { + notes_state.tick(&mut game_state)?; + thread::sleep(std::time::Duration::from_millis(16)); + + // last_story = state.story; + // last_location = state.location; + // state.refresh()?; + // + // if last_story != state.story { + // let next_step = match notes.steps.get(step_idx) { + // Some(step) => step, + // None => continue, + // }; + // + // if next_step.stage == state.story { + // current_step = next_step; + // step_idx += 1; + // area_idx = 0; + // force_instructions = current_step.areas[0].area == 0 || current_step.areas[0].area == last_location; + // } + // } + // + // if force_instructions || last_location != state.location { + // let next_area = match current_step.areas.get(area_idx) { + // Some(area) => area, + // None => continue, + // }; + // + // if next_area.area == state.location || next_area.area == 0 { + // if first { + // first = false; + // step_idx += 1; + // } + // + // area_idx += 1; + // force_instructions = false; + // if last_printed_location != next_area.area { + // println!("{}", state.location_name().unwrap_or("Unknown Location")); + // last_printed_location = state.location; + // } + // + // println!("{}", next_area.steps); + // } + // } + } + + Ok(()) +} diff --git a/src/notes.rs b/src/notes.rs new file mode 100644 index 0000000..0d8b02e --- /dev/null +++ b/src/notes.rs @@ -0,0 +1,18 @@ +use serde::Deserialize; + +#[derive(Debug, Deserialize)] +pub struct Notes { + pub steps: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct Step { + pub stage: u16, + pub areas: Vec, +} + +#[derive(Debug, Deserialize, Clone)] +pub struct Area { + pub area: u16, + pub steps: String, +} diff --git a/src/notes_state.rs b/src/notes_state.rs new file mode 100644 index 0000000..71d14a7 --- /dev/null +++ b/src/notes_state.rs @@ -0,0 +1,111 @@ +use anyhow::Result; +use crate::{GameState, Notes}; +use crate::notes::{Area, Step}; + +pub struct NotesState<'a> { + notes: &'a Notes, + last_printed_location: u16, + last_stage: u16, + last_location: u16, + + step_idx: usize, + area_idx: usize, + + current_step: &'a Step, + first_step: bool, + force_print: bool, +} + +impl<'a> NotesState<'a> { + pub fn new(notes: &'a Notes, game: &mut GameState) -> Result { + game.refresh()?; + // find first step with a matching story stage + let steps = notes.steps + .iter() + .take_while(|step| step.stage <= game.story) + .count(); + let (step, step_idx, force) = if steps <= 1 { + // nothing previous OR first step + (¬es.steps[0], 0, false) + } else { + // in progress + (¬es.steps[steps - 1], steps - 1, true) + }; + // find first area that matches + let area_idx = step.areas + .iter() + .position(|area| area.area == 0 || area.area == game.location) + .unwrap_or(0); + + Ok(Self { + notes, + last_printed_location: 0, + last_stage: 0, + last_location: 0, + step_idx, + area_idx, + current_step: step, + first_step: true, + force_print: force, + }) + } + + pub fn tick(&mut self, game: &mut GameState) -> Result<()> { + self.last_stage = game.story; + self.last_location = game.location; + game.refresh()?; + + let stage_changed = self.last_stage != game.story; + let location_changed = self.last_location != game.location; + + let step_advanced = stage_changed && self.change_step(game); + let area = match self.area() { + Some(area) => area.clone(), + None => return Ok(()), + }; + + if self.force_print || ((step_advanced || location_changed) && (game.location == area.area || area.area == 0)) { + if self.last_printed_location != game.location { + self.last_printed_location = game.location; + println!("{}", game.location_name().unwrap_or("Unknown Location")) + } + + println!("{}", area.steps); + self.area_idx += 1; + + if self.first_step { + self.first_step = false; + self.step_idx += 1; + if !self.force_print { + self.area_idx = 0; + } + } + + if self.force_print { + self.force_print = false; + } + } + + Ok(()) + } + + fn area(&self) -> Option<&Area> { + self.current_step.areas.get(self.area_idx) + } + + fn change_step(&mut self, game: &mut GameState) -> bool { + let next = match self.notes.steps.get(self.step_idx) { + Some(step) => step, + None => return false, + }; + + if next.stage == game.story { + self.current_step = next; + self.step_idx += 1; + self.area_idx = 0; + return true; + } + + false + } +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..b58a655 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,761 @@ +use std::collections::HashMap; +use itertools::Itertools; + +pub fn parse_pattern(s: &str) -> Option> { + let no_whitespace = s.replace(char::is_whitespace, ""); + if no_whitespace.len() % 2 == 1 { + return None; + } + + let mut pattern = Vec::with_capacity(no_whitespace.len() / 2); + for mut chunk in &no_whitespace.chars().chunks(2) { + let joined = chunk.join(""); + if joined == "??" { + pattern.push(0); + continue; + } + + let byte = u8::from_str_radix(&joined, 16).ok()?; + pattern.push(byte); + } + + Some(pattern) +} + +pub fn find_pattern(mem: &[u8], pattern: &[u8]) -> Option { + if pattern.len() > mem.len() { + return None; + } + + let last = pattern.len() - 1; + let mut offset = 0; + let max_offset = mem.len() - pattern.len(); + + while offset < max_offset { + let mut position = last; + while pattern[position] == mem[position + offset] || pattern[position] == 0 { + if position == 0 { + return Some(offset); + } + + position -= 1; + } + + offset += 1; + } + + None +} + +pub fn get_static_address(mem: &[u8], mut addr: usize, base: usize) -> Option { + loop { + addr += 1; + if addr + 4 > mem.len() { + return None; + } + + let array: [u8; 4] = mem[addr..addr + 4].try_into().unwrap(); + let num = i32::from_le_bytes(array) as isize + addr as isize + 4 - base as isize; + if num > mem.len() as isize || num < 0 { + continue; + } + + let offset = addr as isize + i32::from_le_bytes(array) as isize; + return Some(offset as usize + 4); + } +} + +lazy_static::lazy_static! { + pub static ref LOCATIONS: HashMap = maplit::hashmap! { + 12 => "Main Menu", + 13 => "End Credits", + 48 => "Stockade", + 49 => "Arena", + 51 => "Oubliette", + 53 => "The Lightworks", + 54 => "Great Eastern Passage", + 55 => "Op Sector 36", + 56 => "Special Op Sector 3", + 57 => "Op Sector 37", + 58 => "Op Sector 29", + 61 => "Great Central Passage", + 62 => "The Zeviah Subterrane", + 66 => "North-South Junction", + 69 => "Terminus No. 4", + 70 => "Terminus No. 4 Adjunct", + 71 => "Terminus No. 7", + 124 => "Observation Parlour", + 125 => "Sky Saloon", + 126 => "Air Deck", + 129 => "South Bank Village", + 130 => "North Bank Village", + 132 => "The Yoma", + 133 => "Broken Sands", + 137 => "Uazcuff Hills", + 138 => "Sundered Earth", + 139 => "The Highlands", + 140 => "Fields of Eternity", + 141 => "The Shaded Path", + 142 => "The Chosen Path", + 144 => "The Skytrail", + 145 => "Realm of the Elder Dream", + 146 => "The Lost Way", + 147 => "Garden of Life's Circle", + 148 => "Oliphzak Rise", + 149 => "The Nameless Spring", + 151 => "The Omen-Spur", + 152 => "Trunkwall Road", + 153 => "Diverging Way", + 154 => "Sun-dappled Path", + 155 => "Garden of Decay", + 156 => "Path of Hours", + 157 => "Quietened Trace", + 158 => "Grand Bower", + 162 => "Corridor of Ages", + 163 => "Piebald Path", + 167 => "Greencrag", + 168 => "The Muted Scarp", + 169 => "Vale of Lingering Sorrow", + 170 => "Hope's Reach", + 171 => "Echoes of the Past", + 175 => "The Slumbermead", + 176 => "Succor Midst Sorrow", + 177 => "The Fog Mutters", + 178 => "Overlooking Eternity", + 179 => "Lifeless Strand", + 180 => "Field of the Fallen Lord", + 184 => "Falls of Time", + 185 => "Mirror of the Soul", + 186 => "The Acolyte's Burden", + 187 => "Doubt Abandoned", + 188 => "Skybent Chamber", + 192 => "Destiny's March", + 193 => "Temptation Eluded", + 194 => "Chamber of the Chosen", + 198 => "Hall of Shadowlight", + 200 => "Hall of Lambent Darkness", + 202 => "Hall of the Wroth God", + 209 => "Southern Skirts", + 210 => "Summit Path", + 215 => "Rays of Ashen Light", + 216 => "Empyrean Way", + 217 => "Skyreach Ridge", + 218 => "Trail of Sky-flung Stone", + 219 => "Northern Skirts", + 220 => "Halny Crossing", + 223 => "Empyrean Seat", + 227 => "The Stepping", + 228 => "Yardang Labyrinth", + 229 => "Sand-swept Naze", + 230 => "Banks of the Nebra", + 231 => "Passage Entrance", + 232 => "Murmuring Defile", + 233 => "Outpost", + 236 => "Throne Road", + 237 => "Warrior's Wash", + 238 => "Gizas North Bank", + 239 => "Toam Hills", + 243 => "Nomad Village", + 247 => "Starfall Field", + 248 => "Crystal Glade", + 249 => "Gizas South Bank", + 253 => "Throne Road", + 254 => "Warrior's Wash", + 255 => "Gizas North Bank", + 256 => "Toam Hills", + 260 => "Nomad Village", + 264 => "Starfall Field", + 265 => "Crystal Glade", + 266 => "Gizas South Bank", + 270 => "Tracks of the Beast", + 274 => "Aerial Gardens", + 275 => "Inner Ward", + 279 => "Lower Apartments", + 280 => "Upper Apartments", + 282 => "The Highhall", + 286 => "Energy Transitarium", + 289 => "North End", + 290 => "Muthru Bazaar", + 291 => "East End", + 292 => "Southern Plaza", + 295 => "Amal's Weaponry", + 296 => "Panamis's Protectives", + 297 => "Migelo's Sundries", + 298 => "Yugri's Magicks", + 299 => "Batahn's Technicks", + 300 => "Yamoora's Gambits", + 301 => "Samalzalam Manor", + 302 => "The Clan Hall", + 304 => "The Sandsea", + 305 => "Eastgate", + 306 => "Southgate", + 307 => "Westgate", + 311 => "Central Spur Stairs", + 312 => "East Waterway Control", + 313 => "North Spur Sluiceway", + 314 => "East Spur Stairs", + 315 => "Northern Sluiceway", + 316 => "East Waterway Control", + 318 => "No. 11 Channel", + 319 => "East Sluice Control", + 320 => "West Sluice Control", + 321 => "No. 10 Channel", + 322 => "Central Waterway Control", + 326 => "Southern Sluiceway", + 329 => "Overflow Cloaca", + 332 => "A Vikaari Bhrum", + 333 => "Trahk Pis Praa", + 334 => "Sthaana Pisces", + 335 => "Dha Vikaari Jula", + 336 => "Trahk Jilaam Praa'dii", + 337 => "Sthaana Aries", + 341 => "Crystal Core", + 342 => "No. 1 Cloaca", + 345 => "Galtea Downs", + 346 => "Corridor of Sand", + 347 => "Shimmering Horizons", + 348 => "The Midfault", + 349 => "Windtrace Dunes", + 350 => "The Western Divide", + 354 => "Wyrm's Nest", + 357 => "Shaft Entry", + 358 => "Oltam Span", + 359 => "Transitway 1", + 360 => "Transitway 2", + 365 => "Shunia Twinspan", + 366 => "Site 2", + 367 => "Site 3", + 369 => "Tasche Span", + 372 => "Site 9", + 374 => "Site 11", + 376 => "Site 7", + 379 => "Platform 1 - East Tanks", + 380 => "Platform 1 - Refinery", + 381 => "East Junction", + 382 => "Primary Tank Complex", + 383 => "Central Junction", + 384 => "Platform 1 - South Tanks", + 385 => "Platform 2 - Refinery", + 386 => "Yensa Border Tunnel", + 387 => "South Tank Approach", + 390 => "The Urutan-Yensa Sea", + 391 => "Withering Shores", + 392 => "Augur Hill", + 393 => "Yellow Sands", + 394 => "The Sandscale Bank", + 398 => "Demesne of the Sandqueen", + 399 => "Trail of Fading Warmth", + 400 => "Simoon Bluff", + 403 => "Valley of the Dead", + 406 => "Hall of the Destroyer", + 407 => "Hall of the Sentinel", + 410 => "Royal Passage", + 411 => "Southfall Passage", + 412 => "Northfall Passage", + 415 => "Cloister of Flame", + 418 => "Chamber of First Light", + 421 => "Hall of Effulgent Light", + 422 => "Cloister of Distant Song", + 426 => "Cloister of the Highborn", + 430 => "Hall of the Ivory Covenant", + 431 => "Hall of Slumbering Might", + 438 => "The Crucible", + 441 => "Cloister of Solace", + 444 => "Cloister of Reason", + 447 => "Paths of Chained Light", + 448 => "The Needlebrake", + 449 => "Whisperleaf Way", + 450 => "The Parting Glade", + 453 => "The Rustling Chapel", + 456 => "Dell of the Dreamer", + 459 => "The Branchway", + 460 => "The Greenswathe", + 463 => "Fading Vale", + 464 => "Head of the Silverflow", + 465 => "Freezing Gorge", + 468 => "Frozen Brook", + 469 => "Icebound Flow", + 470 => "Karydine Glacier", + 473 => "Path of the Firstfall", + 474 => "Spine of the Icewyrm", + 475 => "Silverflow's End", + 478 => "The Reseta Strand", + 479 => "Pora-Pora Sands", + 480 => "The Mauleia Strand", + 481 => "Cape Uahuk", + 482 => "Cape Tialan", + 483 => "Kaukula Pass", + 484 => "The Hakawea Shore", + 488 => "Hunters' Camp", + 491 => "Caima Hills", + 492 => "The Vaddu Strand", + 493 => "Limatra Hills", + 494 => "Rava's Pass", + 497 => "Old Elanise Road", + 498 => "Crossfield", + 499 => "The Terraced Bank", + 500 => "Journey's Rest", + 503 => "North Liavell Hills", + 504 => "South Liavell Hills", + 505 => "Feddik River", + 506 => "The Northsward", + 509 => "Footfalls of the Past", + 511 => "Echoes from Time's Garden", + 517 => "City of Other Days", + 518 => "Path of Hidden Blessing", + 522 => "They Who Thirst Not", + 525 => "Field of Fallen Wings", + 526 => "The Switchback", + 527 => "Haulo Green", + 530 => "Dagan Flats", + 531 => "Field of Light Winds", + 532 => "The Greensnake", + 533 => "Sunlit Path", + 536 => "The Shred", + 539 => "Walk of Flitting Rifts", + 540 => "Walk of Stolen Truths", + 541 => "Walk of Dancing Shadow", + 542 => "Antiquity's End", + 545 => "Redolent Glade", + 548 => "White Magick's Embrace", + 549 => "Ice Field of Clearsight", + 550 => "The Edge of Reason", + 552 => "Port Launch", + 555 => "Port Section", + 556 => "Large Freight Stores", + 557 => "Starboard Section", + 558 => "Sub-control Room", + 561 => "Airship Berth Access", + 564 => "Central Brig Access", + 567 => "Cellar Stores", + 568 => "Cellars", + 569 => "Lower Halls", + 570 => "Secret Passage", + 571 => "Treasure Room No. 8", + 572 => "The Garden Stairs", + 576 => "Invitation to Heresy", + 577 => "Sandfalls", + 578 => "Hourglass Basin", + 581 => "The Undershore", + 582 => "Halls of Ardent Darkness", + 585 => "The Balamka Fault", + 586 => "Drybeam Cavern", + 587 => "Darkened Wharf", + 588 => "Canopy of Clay", + 590 => "Athroza Quicksands", + 593 => "Walk of Sky", + 594 => "Walk of Mind", + 597 => "Ward of Measure", + 598 => "Cold Distance", + 599 => "Walk of Prescience", + 600 => "Walk of Reason", + 603 => "Ward of Steel", + 606 => "Ward of Velitation", + 607 => "Walk of Torn Illusion", + 608 => "Walk of Revelation", + 609 => "Ward of the Sword-King", + 612 => "Hall of Worth", + 615 => "Vault of the Champion", + 618 => "Throne of Veiled Gods", + 621 => "A Prama Vikaari", + 622 => "Kabonii Jilaam Pratii'vaa", + 623 => "Kabonii Jilaam Avaa", + 624 => "Dha Vikaari Bhrum", + 625 => "Sthaana Scorpio", + 626 => "A Vikaari Dhebon", + 630 => "West Barbican", + 631 => "Jajim Bazaar", + 632 => "West Ward", + 635 => "Grand Arcade", + 636 => "Highgarden Terrace", + 640 => "Molberry", + 641 => "Trant", + 642 => "Charlotte's Magickery", + 643 => "Bulward's Technicks", + 666 => "Womb of the Sun-cryst", + 668 => "Womb of the Sun-cryst", + 670 => "Heaven's Challenge", + 682 => "Hell's Challenge", + 686 => "Gate of Earth", + 687 => "Gate of Water", + 690 => "The Trimahla Water-Steps", + 691 => "The Aadha Water-Steps", + 694 => "The Haalmikah Water-Steps", + 695 => "Gate of Fire", + 698 => "Gate of Wind", + 701 => "North Sprawl", + 702 => "South Sprawl", + 703 => "Dalan's House", + 704 => "Residence", + 709 => "The Black Watch", + 710 => "The Confiscatory", + 712 => "North Entrance", + 713 => "Pithead Junction A", + 714 => "Phase 1 Shaft", + 715 => "Phase 1 Dig", + 716 => "Crossover A", + 717 => "Pithead Junction B", + 718 => "Staging Shaft", + 719 => "Crossover B", + 722 => "Ore Separation", + 725 => "Phase 2 Dig", + 726 => "Crossover C", + 727 => "Pithead Junction C", + 728 => "Phase 2 Shaft", + 730 => "Special Charter Shaft", + 733 => "Special Charter Dig", + 736 => "Hall of the Light", + 737 => "Hall of the Light", + 738 => "Temple Grounds", + 741 => "Temple Approach", + 742 => "Sand-strewn Pass", + 745 => "Nilbasse", + 746 => "Rienna", + 747 => "Vint's Armaments", + 748 => "Granch's Requisites", + 749 => "Lebleu's Gambits", + 751 => "Banks of the Sogoht", + 752 => "Lull of the Land", + 753 => "The Elderknoll", + 756 => "Tsenoble", + 762 => "Alley of Muted Sighs", + 763 => "Alley of Low Whispers", + 766 => "Fane of the Path", + 767 => "The Spiritwood", + 768 => "Road of Verdant Praise", + 774 => "Periphery", + 775 => "Catwalk", + 776 => "Antechamber", + 777 => "Antechamber", + 779 => "Central Lift", + 782 => "Central Shaft", + 785 => "Cannon Superstructure", + 788 => "Aerodrome (Rabanastre)", + 791 => "Aerodrome (Bhujerba)", + 794 => "Aerodrome (Archades)", + 797 => "Aerodrome (Balfonheim Port)", + 800 => "Aerodrome (Nalbina Town)", + 803 => "Travica Way", + 804 => "Cloudborne Row", + 805 => "Miners' End", + 806 => "Lhusu Square", + 809 => "Khus Skygrounds", + 810 => "Kaff Terrace", + 813 => "Targe's Arms", + 814 => "Rithil's Protectives", + 816 => "Mait's Magicks", + 817 => "Clio's Technicks", + 818 => "Bashketi's Gambits", + 819 => "The Staras Residence", + 820 => "The Cloudborne", + 823 => "Sea Breeze Lane", + 824 => "Gallerina Marketplace", + 825 => "Quayside Court", + 826 => "Saccio Lane", + 827 => "Chivany Breakwater", + 828 => "Canal Lane", + 833 => "Beruny's Armaments", + 834 => "Odo's Technicks", + 835 => "Port Villa", + 836 => "The Whitecap", + 837 => "Port Villa", + 838 => "No. 11 Channel", + 839 => "No. 11 Channel", + 840 => "East Sluice Control", + 841 => "Southern Sluiceway", + 842 => "West Sluice Control", + 843 => "No. 10 Channel", + 844 => "No. 10 Channel", + 845 => "No. 3 Cloaca Spur", + 846 => "No. 3 Cloaca Spur", + 847 => "No. 1 Cloaca", + 848 => "No. 4 Cloaca Spur", + 849 => "No. 4 Cloaca Spur", + 850 => "Central Waterway Control", + 854 => "C.D.B.", + 856 => "Dalan's Marker", + 871 => "Bridge", + 872 => "Battle Launch", + 876 => "Brig No. 1", + 888 => "Crystal Peak", + 891 => "East-West Bypass", + 892 => "The Zeviah Span", + 893 => "West Annex", + 894 => "Terminus No. 7 Adjunct", + 895 => "Special Op Sector 5", + 898 => "Colosseum", + 901 => "Lasche Span", + 902 => "Site 5", + 903 => "Site 6 South", + 904 => "Site 6 North", + 905 => "Staging Area", + 908 => "Living Chasm", + 916 => "Siti Bhrusuna", + 926 => "A Vikaari Kabonii", + 927 => "Sthaana Cancer", + 928 => "Bhrum Pis Avaa", + 929 => "Bhrum Pis Pratii", + 930 => "Dha Vikaari Trahk", + 933 => "Site 2", + 934 => "Shunia Twinspan", + 935 => "Transitway 1", + 936 => "Oltam Span", + 939 => "Dha Vikaari Kabonii", + 940 => "A Vikaari Kanbhru Ra", + 941 => "Dhebon Jilaam Praa'dii", + 942 => "Dhebon Jilaam Pratii'dii", + 943 => "Sthaana Sagittarius", + 944 => "A Vikaari Sirhru Pratii", + 945 => "A Vikaari Sirhru Praa", + 946 => "Dhebon Jilaam Avaapratii", + 947 => "A Vikaari Sirhru Si", + 949 => "Dhebon Jilaam Avaa", + 952 => "Dha Vikaari Dhebon Praa", + 953 => "Dha Vikaari Dhebon Pratii", + 954 => "Sirhru Phullam Praa", + 955 => "Sthaana Leo", + 956 => "Sirhru Phullam Praa'vaa", + 957 => "Sirhru Phullam Pratii'vaa", + 958 => "Sthaana Gemini", + 959 => "Sirhru Phullam Udiipratii", + 960 => "Sirhru Jilaam Praa'dii", + 961 => "Sirhru Jilaam Pratii'dii", + 962 => "Sirhru Jilaam Praa", + 963 => "Sirhru Jilaam Pratii", + 964 => "Sirhru Jilaam Praa'vaa", + 965 => "Sirhru Jilaam Pratii'vaa", + 966 => "Sirhru Pis Praa", + 967 => "Sirhru Pis Pratii", + 968 => "Sirhru Pis Avaa", + 969 => "Sirhru Jilaam Avaapratii", + 970 => "Sirhru Jilaam Avaapraa", + 971 => "A Vikaari Uldobi", + 972 => "A Vikaari Uldobi Si", + 973 => "Dha Vikaari Dhebon Si", + 979 => "Dha Vikaari Sirhru", + 980 => "Sthaana Virgo", + 981 => "Uldobi Jilaam Praa'dii", + 982 => "Uldobi Jilaam Pratii", + 983 => "Uldobi Jilaam Praa", + 984 => "Uldobi Phullam Pratii'dii", + 985 => "Sthaana Capricorn", + 986 => "Dha Vikaari Sirhru Si", + 987 => "Uldobi Phullam Udiipraa", + 988 => "Uldobi Phullam Pratii'vaa", + 989 => "Uldobi Phullam Praa'vaa", + 990 => "Uldobi Phullam Pratii", + 991 => "Sthaana Taurus", + 992 => "Sthaana Libra", + 993 => "Uldobi Jilaam Praa'vaa", + 994 => "Uldobi Jilaam Pratii'vaa", + 995 => "Uldobi Jilaam Avaa", + 996 => "A Vikaari Kanbhru", + 1002 => "Dha Vikaari Uldobi", + 1003 => "Kanbhru Pis", + 1004 => "Dha Vikaari Dhebon Ra", + 1005 => "Sthaana Aquarius", + 1008 => "66th Floor", + 1009 => "Rm 6613 West", + 1010 => "Rm 6613 East", + 1011 => "Rm 6612 West", + 1013 => "Rm 6611 West", + 1014 => "Rm 6611 East", + 1015 => "Rm 6602 West", + 1016 => "Rm 6601 West", + 1017 => "Rm 6602 East", + 1018 => "Rm 6601 East", + 1019 => "67th Floor", + 1020 => "Rm 6711 West", + 1021 => "Rm 6711 East", + 1023 => "Rm 6703 West", + 1024 => "Rm 6704 East", + 1025 => "Rm 6703 East", + 1026 => "Rm 6702 West", + 1027 => "Rm 6701 West", + 1028 => "Rm 6702 East", + 1030 => "68th Floor", + 1031 => "69th Floor", + 1036 => "66th Floor", + 1037 => "Rm 6613 West", + 1038 => "Rm 6613 East", + 1039 => "Rm 6612 West", + 1041 => "Rm 6611 West", + 1042 => "Rm 6611 East", + 1043 => "Rm 6602 West", + 1044 => "Rm 6601 West", + 1045 => "Rm 6602 East", + 1046 => "Rm 6601 East", + 1047 => "70th Floor", + 1048 => "Rm 7002 West", + 1049 => "Rm 7001 West", + 1050 => "Rm 7002 East", + 1054 => "67th Floor", + 1055 => "Rm 6711 West", + 1056 => "Rm 6711 East", + 1058 => "Rm 6703 West", + 1059 => "Rm 6704 East", + 1060 => "Rm 6703 East", + 1061 => "Rm 6702 West", + 1062 => "Rm 6701 West", + 1063 => "Rm 6702 East", + 1067 => "68th Floor", + 1068 => "Rm 6814 West", + 1069 => "Rm 6814 East", + 1070 => "Rm 6813 West", + 1071 => "Rm 6813 East", + 1072 => "Rm 6812 West", + 1073 => "Rm 6812 East", + 1074 => "Rm 6811 West", + 1075 => "Rm 6811 East", + 1076 => "Rm 6804 West", + 1077 => "Rm 6803 West", + 1078 => "Rm 6804 East", + 1079 => "Rm 6803 East", + 1080 => "Rm 6802 West", + 1081 => "Rm 6801 West", + 1082 => "Rm 6802 East", + 1083 => "Rm 6801 East", + 1088 => "Rm 6912 East", + 1089 => "Rm 6911 West", + 1091 => "Rm 6904 West", + 1094 => "Rm 6903 East", + 1095 => "Rm 6902 West", + 1096 => "Rm 6901 West", + 1098 => "Rm 6901 East", + 1101 => "The Wellspring", + 1102 => "Horizon's Break", + 1103 => "The Reach", + 1104 => "Reach of the Damned", + 1105 => "Reach of the Occult", + 1111 => "Wellspring Labyrinth", + 1113 => "Dunes of Profaning Wind", + 1114 => "Blackrock Vault", + 1115 => "Wellspring Ravel - 1st Flight", + 1116 => "Wellspring Ravel - 2nd Flight", + 1118 => "Wellspring Ravel - 3rd Flight", + 1119 => "Wellspring Ravel - 4th Flight", + 1121 => "Marsh of Profaning Wind", + 1122 => "Horizon's Cusp", + 1123 => "Penumbra - Interior", + 1124 => "Penumbra - North", + 1125 => "Penumbra - South", + 1126 => "Umbra - Interior", + 1127 => "Umbra - North", + 1128 => "Umbra - South", + 1129 => "Abyssal - Interior", + 1130 => "Abyssal - North", + 1131 => "Abyssal - South", + 1132 => "Cleft of Profaning Wind", + 1133 => "The Bounds of Truth", + 1134 => "Station of Banishment", + 1136 => "Station of Suffering", + 1138 => "Station of Ascension", + 1140 => "Spire Ravel - 1st Flight", + 1141 => "Spire Ravel - 2nd Flight", + 1143 => "Empyrean Ravel", + 1145 => "Overflow Cloaca", + 1150 => "Air Deck", + 1151 => "Babbling Vale", + 1153 => "Withering Shores", + 1155 => "Stage 1", + 1156 => "Stage 2", + 1157 => "Stage 3", + 1158 => "Stage 4", + 1159 => "Stage 5", + 1163 => "Stage 6", + 1164 => "Stage 7", + 1165 => "Stage 8", + 1166 => "Stage 9", + 1167 => "Stage 10", + 1171 => "Stage 11", + 1172 => "Stage 12", + 1173 => "Stage 13", + 1174 => "Stage 14", + 1175 => "Stage 15", + 1179 => "Stage 16", + 1180 => "Stage 17", + 1181 => "Stage 18", + 1182 => "Stage 19", + 1183 => "Stage 20", + 1187 => "Stage 21", + 1188 => "Stage 22", + 1189 => "Stage 23", + 1190 => "Stage 24", + 1191 => "Stage 25", + 1195 => "Stage 26", + 1196 => "Stage 27", + 1197 => "Stage 28", + 1198 => "Stage 29", + 1199 => "Stage 30", + 1203 => "Stage 31", + 1204 => "Stage 32", + 1205 => "Stage 33", + 1206 => "Stage 34", + 1207 => "Stage 35", + 1211 => "Stage 36", + 1212 => "Stage 37", + 1213 => "Stage 38", + 1214 => "Stage 39", + 1215 => "Stage 40", + 1219 => "Stage 41", + 1220 => "Stage 42", + 1221 => "Stage 43", + 1222 => "Stage 44", + 1223 => "Stage 45", + 1227 => "Stage 46", + 1228 => "Stage 47", + 1229 => "Stage 48", + 1230 => "Stage 49", + 1231 => "Stage 50", + 1235 => "Stage 51", + 1236 => "Stage 52", + 1237 => "Stage 53", + 1238 => "Stage 54", + 1239 => "Stage 55", + 1243 => "Stage 56", + 1244 => "Stage 57", + 1245 => "Stage 58", + 1246 => "Stage 59", + 1247 => "Stage 60", + 1251 => "Stage 61", + 1252 => "Stage 62", + 1253 => "Stage 63", + 1254 => "Stage 64", + 1255 => "Stage 65", + 1259 => "Stage 66", + 1260 => "Stage 67", + 1261 => "Stage 68", + 1262 => "Stage 69", + 1263 => "Stage 70", + 1267 => "Stage 71", + 1268 => "Stage 72", + 1269 => "Stage 73", + 1270 => "Stage 74", + 1271 => "Stage 75", + 1275 => "Stage 76", + 1276 => "Stage 77", + 1277 => "Stage 78", + 1278 => "Stage 79", + 1279 => "Stage 80", + 1283 => "Stage 81", + 1284 => "Stage 82", + 1285 => "Stage 83", + 1286 => "Stage 84", + 1287 => "Stage 85", + 1291 => "Stage 86", + 1292 => "Stage 87", + 1293 => "Stage 88", + 1294 => "Stage 89", + 1295 => "Stage 90", + 1299 => "Stage 91", + 1300 => "Stage 92", + 1301 => "Stage 93", + 1302 => "Stage 94", + 1303 => "Stage 95", + 1307 => "Stage 96", + 1308 => "Stage 97", + 1309 => "Stage 98", + 1310 => "Stage 99", + 1311 => "Stage 100", + }; +}