diff --git a/CMakeLists.txt b/CMakeLists.txt index 78cd2af..5b9087b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -394,10 +394,12 @@ configure_file( # ========== Source-filer ========== file(GLOB SRC_FILES src/*.c) +list(APPEND SRC_FILES third_party/tomlc99/toml.c) # ========== Bygg kjørbar ========== add_executable(circle ${SRC_FILES}) target_link_libraries(circle ${EXTRA_LIBS}) +target_include_directories(circle PRIVATE ${CMAKE_SOURCE_DIR}/third_party/tomlc99) add_subdirectory(src/util) diff --git a/README.md b/README.md index 7186eb3..62c04d5 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,10 @@ Changes in v1.1.0-alpha: * Migration away from OLC with new command "mcreate" and for builders to modify NPC's * Fixed issue with msave not saving items in containers on NPC's -Features that would have been implemented in the next few releases: +Changes in v1.2.0-alpha: + * Replaced ASCII files in favor of TOML for ease of reading + +Features that will be implemented in the next few releases: * Height and weight normalized to species * Stables allow for purchasing of mounts @@ -110,7 +113,6 @@ Features that would have been implemented in the next few releases: ...and down the road: -* Replace ASCII files in favor of SQL database on the backend * Replace DG Scripts with a Python abstraction layer for modern scripting support * Replace Oasis OLC with more modern interface for easing builder duties * Discord server integration for ticketing and community diff --git a/THIRD-PARTY-LICENSES.md b/THIRD-PARTY-LICENSES.md new file mode 100644 index 0000000..a100632 --- /dev/null +++ b/THIRD-PARTY-LICENSES.md @@ -0,0 +1,26 @@ +# Third-Party Licenses + +## tomlc99 (TOML C library) + +Copyright (c) CK Tan +https://github.com/cktan/tomlc99 + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/doc/TOML_SCHEMA.md b/doc/TOML_SCHEMA.md new file mode 100644 index 0000000..e0d45e1 --- /dev/null +++ b/doc/TOML_SCHEMA.md @@ -0,0 +1,445 @@ +# TOML Schema Proposal (Draft) + +This is a draft schema for the planned TOML cutover of gameplay data only. +It mirrors all fields currently written by the ASCII formats. Values should +be written in full (including defaults) to preserve fidelity and avoid +behavior changes. + +All filenames change to `.toml`. Index files become TOML too. + +## Conventions + +- Strings mirror existing text fields; preserve current escaping rules for + embedded newlines where applicable. +- Flags and bitvectors are stored as integer arrays unless otherwise noted. +- VNUM and RNUM are stored as integers. +- Timestamps are Unix epoch seconds. + +## Accounts + +Path: `lib/acctfiles//.toml` + +```toml +name = "AccountName" +password = "hashed" +email = "user@example.com" # optional +current_pc = "CharName" # optional +pcs = ["CharOne", "CharTwo"] +``` + +## Player Index + +Path: `lib/plrfiles/index.toml` (and `index.mini.toml` if mini mode persists) + +```toml +[[player]] +id = 12345 +name = "charname" +level = 10 +flags = 0 +last = 1700000000 +``` + +## Player File + +Path: `lib/plrfiles//.toml` + +```toml +name = "CharName" +short_desc = "short desc" # optional +password = "hashed" +account = "AccountName" # optional +description = "long desc" # optional +background = "long background" # optional +poofin = "text" # optional +poofout = "text" # optional +sex = 0 +class = 0 +species = 0 +level = 1 +id = 12345 +birth = 1700000000 +age = 18 +age_year = 205 +played = 1234 +logon = 1700000000 +last_motd = 0 +last_news = 0 +reroll_used = 0 +reroll_expires = 0 +reroll_old_abils = [0, 0, 0, 0, 0, 0] # str int wis dex con cha +host = "example.net" # optional +height = 180 +weight = 180 +act_flags = [0, 0, 0, 0] +aff_flags = [0, 0, 0, 0] +pref_flags = [0, 0, 0, 0] +saving_throws = [0, 0, 0, 0, 0] # thr1..thr5 +wimp = 0 +freeze = 0 +invis = 0 +load_room = 0 +bad_passwords = 0 +conditions = { hunger = 0, thirst = 0, drunk = 0 } +hmv = { hit = 0, max_hit = 0, mana = 0, max_mana = 0, stamina = 0, max_stamina = 0 } +abilities = { str = 0, int = 0, wis = 0, dex = 0, con = 0, cha = 0 } +ac = 0 +coins = 0 +bank_coins = 0 +exp = 0 +olc_zone = 0 +page_length = 0 +screen_width = 0 +quest_points = 0 +quest_counter = 0 +current_quest = 0 +completed_quests = [101, 102] +triggers = [2001, 2002] +skill_gain_next = [0, 0, 0] # length MAX_SKILLS; index 1..MAX_SKILLS + +[[skill]] +id = 1 +level = 50 + +[[affect]] +spell = 0 +duration = 0 +modifier = 0 +location = 0 +bitvector = [0, 0, 0, 0] + +[[alias]] +alias = "look" +replacement = "l" +type = 0 + +[[var]] +name = "quest_state" +context = 0 +value = "value" +``` + +## Player Objects (Crash/Forced/Logout/etc.) + +Path: `lib/plrobjs//.toml` + +```toml +[header] +save_code = 0 +timed = 0 +net_cost = 0 +coins = 0 +account = 0 +item_count = 0 + +[[object]] +vnum = 1234 +locate = 0 +nest = 0 +values = [0, 0, 0, 0, 0, 0] # NUM_OBJ_VAL_POSITIONS +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +name = "obj name" # optional +short = "short desc" # optional +description = "long desc" # optional +main_description = "extra desc" # optional +type = 0 +weight = 0 +cost = 0 +``` + +## Player Vars (standalone file) + +Path: `lib/plrvars//.toml` + +```toml +[[var]] +name = "varname" +context = 0 +value = "value" +``` + +## Houses (objects only) + +Path: `lib/house/.toml` + +```toml +[[object]] +vnum = 1234 +locate = 0 +nest = 0 +values = [0, 0, 0, 0, 0, 0] +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +name = "obj name" +short = "short desc" +description = "long desc" +main_description = "extra desc" +type = 0 +weight = 0 +cost = 0 +``` + +## Room Save + +Path: `lib/world/rsv/.toml` + +```toml +[[room]] +vnum = 1000 +saved_at = 1700000000 + +[[room.object]] +vnum = 1234 +timer = 0 +weight = 0 +cost = 0 +cost_per_day = 0 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0] +contents = [] # recursive list of objects with same shape + +[[room.mob]] +vnum = 2000 + +[[room.mob.equipment]] +wear_pos = 5 +vnum = 1234 +contents = [] # recursive list of objects by vnum only + +[[room.mob.inventory]] +vnum = 1234 +contents = [] +``` + +## World Index Files + +Paths: +- `lib/world/wld/index.toml` +- `lib/world/mob/index.toml` +- `lib/world/obj/index.toml` +- `lib/world/zon/index.toml` +- `lib/world/shp/index.toml` +- `lib/world/trg/index.toml` +- `lib/world/qst/index.toml` + +```toml +files = ["0.toml", "1.toml"] +``` + +## Rooms + +Path: `lib/world/wld/.toml` + +```toml +[[room]] +vnum = 1000 +name = "Room name" +description = "Room description" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "exit desc" +keyword = "door" +exit_info = 0 +key = 0 +to_room = 1001 + +[[room.extra_desc]] +keyword = "sign" +description = "text" + +[[room.forage]] +obj_vnum = 1234 +dc = 10 + +room.triggers = [3000, 3001] +``` + +## Mobiles + +Path: `lib/world/mob/.toml` + +```toml +[[mob]] +vnum = 2000 +name = "mob name" +keywords = "keywords" +short = "short" +long = "long" +description = "desc" +background = "background" # optional +flags = [0, 0, 0, 0] +aff_flags = [0, 0, 0, 0] +alignment = 0 +mob_type = "simple" # "simple" or "enhanced" + +[mob.simple] +level = 1 +hit_dice = 1 +mana_dice = 1 +stamina_dice = 1 +pos = 0 +default_pos = 0 +sex = 0 + +[mob.enhanced] +pos = 0 +default_pos = 0 +sex = 0 +abilities = { str = 11, dex = 11, con = 11, int = 11, wis = 11, cha = 11 } +class = 0 +species = 0 +age = 0 +saving_throws = { str = 0, dex = 0, con = 0, int = 0, wis = 0, cha = 0 } +skills = [{ id = 1, level = 50 }] +attack_type = 0 + +[[mob.loadout]] +wear_pos = 0 +vnum = 1234 +quantity = 1 + +[[mob.skin_yield]] +obj_vnum = 1234 +dc = 10 + +mob.triggers = [3000, 3001] +``` + +## Objects + +Path: `lib/world/obj/.toml` + +```toml +[[object]] +vnum = 3000 +name = "obj name" +short = "short" +description = "desc" +main_description = "long" +type = 0 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0] +weight = 0 +cost = 0 +level = 0 +timer = 0 + +[[object.extra_desc]] +keyword = "inscription" +description = "text" + +[[object.affect]] +location = 0 +modifier = 0 + +object.triggers = [3000, 3001] +``` + +## Zones + +Path: `lib/world/zon/.toml` + +```toml +[[zone]] +vnum = 1 +builders = "None." +name = "Zone name" +bot = 100 +top = 199 +lifespan = 30 +reset_mode = 2 +flags = [0, 0, 0, 0] +min_level = -1 +max_level = -1 + +[[zone.command]] +command = "M" +if_flag = 0 +arg1 = 0 +arg2 = 0 +arg3 = 0 +sarg1 = "" # for V command +sarg2 = "" # for V command +line = 0 +``` + +## Shops + +Path: `lib/world/shp/.toml` + +```toml +[[shop]] +vnum = 4000 +products = [3000, 3001] +buy_profit = 1.0 +sell_profit = 1.0 +trade_with = 0 +broke_temper = 0 +bitvector = 0 +keeper = 2000 +rooms = [1000] +open1 = 0 +close1 = 28 +open2 = 0 +close2 = 0 +bank = 0 +sort = 0 + +[[shop.buy_type]] +type = 0 +keyword = "" # optional + +[shop.messages] +no_such_item1 = "" +no_such_item2 = "" +do_not_buy = "" +missing_cash1 = "" +missing_cash2 = "" +message_buy = "" +message_sell = "" +``` + +## Triggers + +Path: `lib/world/trg/.toml` + +```toml +[[trigger]] +vnum = 5000 +name = "trigger name" +attach_type = 0 +flags = 0 +narg = 0 +arglist = "" +commands = ["say hello", "wait 2", "say bye"] +``` + +## Quests + +Path: `lib/world/qst/.toml` + +```toml +[[quest]] +vnum = 6000 +name = "Quest name" +description = "desc" +info = "info" +done = "done" +quit = "quit" +type = 0 +quest_master = 2000 +flags = 0 +target = 0 +prev_quest = 0 +next_quest = 0 +prereq = 0 +values = [0, 0, 0, 0, 0, 0, 0] +rewards = { coins = 0, exp = 0, obj_vnum = 0 } +``` diff --git a/lib/plrfiles/index.toml b/lib/plrfiles/index.toml new file mode 100644 index 0000000..6d5f377 --- /dev/null +++ b/lib/plrfiles/index.toml @@ -0,0 +1,7 @@ +[[player]] +id = 1 +name = "kinther" +level = 5 +flags = 0 +last = 1769189131 + diff --git a/lib/world/mob/0.toml b/lib/world/mob/0.toml new file mode 100644 index 0000000..e3275aa --- /dev/null +++ b/lib/world/mob/0.toml @@ -0,0 +1 @@ +mob = [] diff --git a/lib/world/mob/1.toml b/lib/world/mob/1.toml new file mode 100644 index 0000000..9caa838 --- /dev/null +++ b/lib/world/mob/1.toml @@ -0,0 +1,446 @@ +[[mob]] +vnum = 100 +name = "John" +keywords = "soldier tall burly human guard" +short = "the tall, burly human soldier" +long = "A tall, burly human soldier stands here at the gate.\n" +description = " This man stands slightly taller than the average human, but not by much. He\nhas a thick, haggard looking brown beard that is flecked with dust and sand. \nHis eyes have a bluish-tint to them with a small amount of green. One might\ncall his stature bulky, as he has quite a bit of muscle.\n" +flags = [6218, 0, 0, 0] +aff_flags = [0, 0, 0, 0] +alignment = 0 +mob_type = "enhanced" + +[mob.simple] +level = 1 +hit_dice = 3 +mana_dice = 20 +stamina_dice = 40 +pos = 8 +default_pos = 8 +sex = 1 + +[mob.enhanced] +class = 3 +species = 0 + +[mob.enhanced.abilities] +str = 16 +dex = 11 +con = 11 +int = 11 +wis = 11 +cha = 11 + +[mob.enhanced.saving_throws] +str = 0 +dex = 0 +con = 0 +int = 0 +wis = 0 +cha = 0 + +[[mob.enhanced.skills]] +id = 132 +level = 5 +[[mob.enhanced.skills]] +id = 134 +level = 5 +[[mob.enhanced.skills]] +id = 137 +level = 5 +[[mob.enhanced.skills]] +id = 141 +level = 5 +[[mob.enhanced.skills]] +id = 142 +level = 5 +[[mob.enhanced.skills]] +id = 143 +level = 5 +[[mob.enhanced.skills]] +id = 144 +level = 5 +[[mob.enhanced.skills]] +id = 145 +level = 5 +[[mob.enhanced.skills]] +id = 146 +level = 5 +[[mob.enhanced.skills]] +id = 147 +level = 5 + +[[mob.loadout]] +wear_pos = 3 +vnum = 118 +quantity = 1 +[[mob.loadout]] +wear_pos = 5 +vnum = 131 +quantity = 1 +[[mob.loadout]] +wear_pos = 6 +vnum = 110 +quantity = 1 +[[mob.loadout]] +wear_pos = 7 +vnum = 108 +quantity = 1 +[[mob.loadout]] +wear_pos = 8 +vnum = 115 +quantity = 1 +[[mob.loadout]] +wear_pos = 9 +vnum = 124 +quantity = 1 +[[mob.loadout]] +wear_pos = 10 +vnum = 107 +quantity = 1 +[[mob.loadout]] +wear_pos = 11 +vnum = 111 +quantity = 1 +[[mob.loadout]] +wear_pos = 15 +vnum = 117 +quantity = 1 +[[mob.loadout]] +wear_pos = 16 +vnum = 117 +quantity = 1 +[[mob.loadout]] +wear_pos = 17 +vnum = 127 +quantity = 1 + +[[mob]] +vnum = 101 +name = "Sally" +keywords = "slim lanky human soldier guard" +short = "the slim, lanky human soldier" +long = "A slim, lanky human soldier stands here eyeing passerbys.\n" +description = " This woman looks rather thin, with her darkly tanned skin hugging her frame\ntightly. What muscle she does have is accentuated, though there is not much. \nHer arms and legs are lanky, seeming to be longer than normal. A ponytail of\ndark hair has been pulled back behind her head, intensifying the sharpness of\nher nose.\n" +flags = [6218, 0, 0, 0] +aff_flags = [0, 0, 0, 0] +alignment = 0 +mob_type = "enhanced" + +[mob.simple] +level = 1 +hit_dice = 3 +mana_dice = 20 +stamina_dice = 40 +pos = 8 +default_pos = 8 +sex = 2 + +[mob.enhanced] +class = 3 +species = 0 + +[mob.enhanced.abilities] +str = 11 +dex = 11 +con = 11 +int = 11 +wis = 11 +cha = 11 + +[mob.enhanced.saving_throws] +str = 0 +dex = 0 +con = 0 +int = 0 +wis = 0 +cha = 0 + +[[mob.enhanced.skills]] +id = 132 +level = 5 +[[mob.enhanced.skills]] +id = 134 +level = 5 +[[mob.enhanced.skills]] +id = 137 +level = 5 +[[mob.enhanced.skills]] +id = 140 +level = 5 +[[mob.enhanced.skills]] +id = 141 +level = 5 +[[mob.enhanced.skills]] +id = 142 +level = 5 +[[mob.enhanced.skills]] +id = 143 +level = 5 +[[mob.enhanced.skills]] +id = 144 +level = 5 +[[mob.enhanced.skills]] +id = 145 +level = 5 +[[mob.enhanced.skills]] +id = 146 +level = 5 +[[mob.enhanced.skills]] +id = 152 +level = 5 +[[mob.enhanced.skills]] +id = 156 +level = 5 +[[mob.enhanced.skills]] +id = 163 +level = 5 + +[[mob.loadout]] +wear_pos = 3 +vnum = 118 +quantity = 1 +[[mob.loadout]] +wear_pos = 5 +vnum = 131 +quantity = 1 +[[mob.loadout]] +wear_pos = 6 +vnum = 110 +quantity = 1 +[[mob.loadout]] +wear_pos = 7 +vnum = 108 +quantity = 1 +[[mob.loadout]] +wear_pos = 8 +vnum = 115 +quantity = 1 +[[mob.loadout]] +wear_pos = 9 +vnum = 124 +quantity = 1 +[[mob.loadout]] +wear_pos = 10 +vnum = 107 +quantity = 1 +[[mob.loadout]] +wear_pos = 11 +vnum = 111 +quantity = 1 +[[mob.loadout]] +wear_pos = 15 +vnum = 117 +quantity = 1 +[[mob.loadout]] +wear_pos = 16 +vnum = 117 +quantity = 1 +[[mob.loadout]] +wear_pos = 17 +vnum = 127 +quantity = 1 + +[[mob]] +vnum = 102 +name = "Baldy" +keywords = "barkeep stocky bald" +short = "a stocky, bald barkeep" +long = "A stocky, bald barkeep stands behind the bar here.\n" +description = " This man is short and stocky, with a cleanly shaven head. Pattern baldness\nhas taken hold, yet he has a thick and wiry beard that covers most of his face.\nPale blue eyes look bloodshot, and bluish-purple bags hang under them. His\nhands are marred with minor cuts and scars from years of working in service to\nothers.\n" +flags = [10, 0, 0, 0] +aff_flags = [0, 0, 0, 0] +alignment = 0 +mob_type = "enhanced" + +[mob.simple] +level = 1 +hit_dice = 3 +mana_dice = 12 +stamina_dice = 60 +pos = 8 +default_pos = 8 +sex = 1 + +[mob.enhanced] + +[mob.enhanced.abilities] +str = 11 +dex = 11 +con = 11 +int = 11 +wis = 11 +cha = 11 + +[mob.enhanced.saving_throws] +str = 0 +dex = 0 +con = 0 +int = 0 +wis = 0 +cha = 0 + +[[mob.loadout]] +wear_pos = 14 +vnum = 113 +quantity = 1 +[[mob.loadout]] +wear_pos = 9 +vnum = 112 +quantity = 1 + +[[mob]] +vnum = 103 +name = "Lanky" +keywords = "woman lanky scarred" +short = "the lanky, scarred woman" +long = "The lanky, scarred woman is leaning against a wall here.\n" +description = " This human woman is of average height and has a very lanky frame. Her skin\nhugs her body tightly and there is little fat to be seen, possibly due to\ndehydration. Tanned dark from the harsh rays of the sun, her upper torso is\ncovered in scars from a lifetime of labor. Unkempt brown hair covers her head,\ndescending to just above her shoulders. Her eyes are a light blue color and\nappear slightly bloodshot.\n" +flags = [10, 0, 0, 0] +aff_flags = [0, 0, 0, 0] +alignment = 0 +mob_type = "enhanced" + +[mob.simple] +level = 1 +hit_dice = 3 +mana_dice = 8 +stamina_dice = 60 +pos = 8 +default_pos = 8 +sex = 2 + +[mob.enhanced] + +[mob.enhanced.abilities] +str = 11 +dex = 11 +con = 11 +int = 11 +wis = 11 +cha = 11 + +[mob.enhanced.saving_throws] +str = 0 +dex = 0 +con = 0 +int = 0 +wis = 0 +cha = 0 + +[[mob.enhanced.skills]] +id = 131 +level = 5 +[[mob.enhanced.skills]] +id = 133 +level = 5 +[[mob.enhanced.skills]] +id = 135 +level = 5 +[[mob.enhanced.skills]] +id = 138 +level = 5 +[[mob.enhanced.skills]] +id = 139 +level = 5 +[[mob.enhanced.skills]] +id = 140 +level = 5 +[[mob.enhanced.skills]] +id = 142 +level = 5 +[[mob.enhanced.skills]] +id = 143 +level = 5 +[[mob.enhanced.skills]] +id = 144 +level = 5 +[[mob.enhanced.skills]] +id = 147 +level = 5 + +[[mob]] +vnum = 104 +name = "Rat" +keywords = "rat small furry" +short = "a small, furry rat" +long = "Keeping low to the ground, a small, furry rat wanders around here.\n" +description = " This small rat is covered in thick fur. The fur itself appears matted and\nhas grime coating it. Two beady black eyes look around, constantly shifting. \nIts tail is four, perhaps five inches long and grey in color. Both hindlings\nappear thick and ready to propel the animal if it feels threatened.\n" +background = "\n\n\n\n\n\n\n\n\n\n\n\nIt's a rat.\n" +flags = [8, 0, 0, 0] +aff_flags = [0, 0, 0, 0] +alignment = 0 +mob_type = "enhanced" + +[mob.simple] +level = 1 +hit_dice = 0 +mana_dice = 0 +stamina_dice = 10 +pos = 8 +default_pos = 8 +sex = 2 + +[mob.enhanced] +species = 25 +age = 42 +attack_type = 4 + +[mob.enhanced.abilities] +str = 3 +dex = 7 +con = 8 +int = 3 +wis = 8 +cha = 3 + +[mob.enhanced.saving_throws] +str = 0 +dex = 0 +con = 0 +int = 0 +wis = 0 +cha = 0 + +[[mob]] +vnum = 105 +name = "Kank" +keywords = "kank sandy brown" +short = "a sandy brown kank" +long = "A sandy brown kank is here, clacking its pincers.\n" +description = "It looks unfinished.\n" +background = "\n\n\n\nNo background has been recorded.\n" +flags = [1048584, 0, 0, 0] +aff_flags = [0, 0, 0, 0] +alignment = 0 +mob_type = "enhanced" + +[mob.simple] +level = 1 +hit_dice = 0 +mana_dice = 0 +stamina_dice = 10 +pos = 8 +default_pos = 8 +sex = 0 + +[mob.enhanced] +species = 21 +attack_type = 4 + +[mob.enhanced.abilities] +str = 18 +dex = 5 +con = 18 +int = 3 +wis = 7 +cha = 3 + +[mob.enhanced.saving_throws] +str = 0 +dex = 0 +con = 0 +int = 0 +wis = 0 +cha = 0 + diff --git a/lib/world/mob/index.mini.toml b/lib/world/mob/index.mini.toml new file mode 100644 index 0000000..042ad07 --- /dev/null +++ b/lib/world/mob/index.mini.toml @@ -0,0 +1,2 @@ +files = [ +] diff --git a/lib/world/mob/index.toml b/lib/world/mob/index.toml new file mode 100644 index 0000000..e9d4550 --- /dev/null +++ b/lib/world/mob/index.toml @@ -0,0 +1,4 @@ +files = [ + "0.toml", + "1.toml" +] diff --git a/lib/world/obj/0.toml b/lib/world/obj/0.toml new file mode 100644 index 0000000..2f37fab --- /dev/null +++ b/lib/world/obj/0.toml @@ -0,0 +1,16 @@ +[[object]] +vnum = 1 +name = "board immortal" +short = "the immortal board" +description = "The immortal bulletin board is here." +main_description = "Made specifically for immortals to post on.\n" +type = 13 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +weight = 0 +cost = 0 +level = 0 +timer = 0 + diff --git a/lib/world/obj/1.toml b/lib/world/obj/1.toml new file mode 100644 index 0000000..3df67d8 --- /dev/null +++ b/lib/world/obj/1.toml @@ -0,0 +1,1104 @@ +[[object]] +vnum = 100 +name = "padded armor" +short = "some padded armor" +description = "Some padded armor has been discarded here." +main_description = "" +type = 9 +extra_flags = [0, 0, 0, 0] +wear_flags = [17, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 1, 0, 0, 0, 0, 0, 0] +weight = 10 +cost = 4 +level = 0 +timer = 0 + +[[object]] +vnum = 101 +name = "erdlu leather armor" +short = "some erdlu leather armor" +description = "An unfinished object is lying here." +main_description = "" +type = 9 +extra_flags = [0, 0, 0, 0] +wear_flags = [17, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 1, 0, 0, 0, 0, 0, 0] +weight = 5 +cost = 5 +level = 0 +timer = 0 + +[[object]] +vnum = 102 +name = "studded leather jacket" +short = "a studded leather jacket" +description = "A jacket made from studded leather lies here." +main_description = "" +type = 9 +extra_flags = [0, 0, 0, 0] +wear_flags = [17, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [2, 1, 0, 0, 0, 0, 0, 0] +weight = 13 +cost = 10 +level = 0 +timer = 0 + +[[object]] +vnum = 103 +name = "bone chitin armor" +short = "some bone and chitin armor" +description = "A piece of armor made from bone and chitin lies here." +main_description = "" +type = 9 +extra_flags = [0, 0, 0, 0] +wear_flags = [17, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 1, 0, 0, 0, 0, 0, 0] +weight = 20 +cost = 12 +level = 0 +timer = 0 + +[[object]] +vnum = 104 +name = "pair padded sleeves" +short = "a pair of padded sleeves" +description = "A pair of padded cloth sleeves lie here abandoned." +main_description = "" +type = 9 +extra_flags = [0, 0, 0, 0] +wear_flags = [513, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 1, 0, 0, 0, 0, 0, 0] +weight = 5 +cost = 10 +level = 0 +timer = 0 + +[[object]] +vnum = 105 +name = "pair padded leggings" +short = "a pair of padded leggings" +description = "A pair of padded leggings lie here in the dust." +main_description = "" +type = 9 +extra_flags = [0, 0, 0, 0] +wear_flags = [65, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 1, 0, 0, 0, 0, 0, 0] +weight = 5 +cost = 10 +level = 0 +timer = 0 + +[[object]] +vnum = 106 +name = "pair cloth gloves" +short = "a pair of cloth gloves" +description = "A pair of yellowed cloth gloves lie here." +main_description = "" +type = 11 +extra_flags = [0, 0, 0, 0] +wear_flags = [257, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +weight = 2 +cost = 10 +level = 0 +timer = 0 + +[[object]] +vnum = 107 +name = "pair leather gloves" +short = "a pair of leather gloves" +description = "A pair of thick leather gloves have been left here." +main_description = "" +type = 9 +extra_flags = [0, 0, 0, 0] +wear_flags = [257, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 1, 0, 0, 0, 0, 0, 0] +weight = 6 +cost = 10 +level = 0 +timer = 0 + +[[object]] +vnum = 108 +name = "bone helmet" +short = "a bone helmet" +description = "A helmet made of bone has been discarded here." +main_description = "" +type = 9 +extra_flags = [0, 0, 0, 0] +wear_flags = [33, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 1, 0, 0, 0, 0, 0, 0] +weight = 12 +cost = 18 +level = 0 +timer = 0 + +[[object]] +vnum = 109 +name = "padded cloth helmet" +short = "a padded cloth helmet" +description = "A padded cloth helmet has been left here." +main_description = "" +type = 9 +extra_flags = [0, 0, 0, 0] +wear_flags = [33, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 1, 0, 0, 0, 0, 0, 0] +weight = 4 +cost = 11 +level = 0 +timer = 0 + +[[object]] +vnum = 110 +name = "braxat hide jacket" +short = "a braxat hide jacket" +description = "A thick jacket made of braxat hide has been abandoned here." +main_description = "" +type = 9 +extra_flags = [0, 0, 0, 0] +wear_flags = [17, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [3, 3, 0, 1, 0, 0, 0, 0] +weight = 30 +cost = 250 +level = 0 +timer = 0 + +[[object]] +vnum = 111 +name = "pair thick leather sleeves" +short = "a pair of thick leather sleeves" +description = "Cured and stitched tight, a pair of thick leather sleeves are here." +main_description = "" +type = 9 +extra_flags = [0, 0, 0, 0] +wear_flags = [513, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 1, 0, 0, 0, 0, 0, 0] +weight = 14 +cost = 30 +level = 0 +timer = 0 + +[[object]] +vnum = 112 +name = "pair sandals" +short = "a pair of sandals" +description = "Some cheap looking sandals have been left in the dust here." +main_description = "" +type = 11 +extra_flags = [0, 0, 0, 0] +wear_flags = [129, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +weight = 1 +cost = 8 +level = 0 +timer = 0 + +[[object]] +vnum = 113 +name = "loincloth" +short = "a loincloth" +description = "A scrap of cloth with a string attached has been left here." +main_description = "" +type = 11 +extra_flags = [0, 0, 0, 0] +wear_flags = [4097, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +weight = 1 +cost = 1 +level = 0 +timer = 0 + +[[object]] +vnum = 114 +name = "erdlu scale shield" +short = "an erdlu scale shield" +description = "A shield made from erdlu scales lies here, collecting dust." +main_description = "" +type = 9 +extra_flags = [0, 0, 0, 0] +wear_flags = [1025, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +weight = 5 +cost = 50 +level = 0 +timer = 0 + +[[object]] +vnum = 115 +name = "pair thick leather leggings" +short = "a pair of thick leather leggings" +description = "A pair of leggings made from thick, dark leather are here." +main_description = "" +type = 9 +extra_flags = [0, 0, 0, 0] +wear_flags = [65, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 1, 0, 0, 0, 0, 0, 0] +weight = 12 +cost = 50 +level = 0 +timer = 0 + +[[object]] +vnum = 116 +name = "hide wrist wrap wrist-wrap" +short = "a hide wrist-wrap" +description = "A simple hide wrist-wrap gathers dust and sand here." +main_description = "" +type = 9 +extra_flags = [0, 0, 0, 0] +wear_flags = [8193, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 1, 0, 0, 0, 0, 0, 0] +weight = 2 +cost = 25 +level = 0 +timer = 0 + +[[object]] +vnum = 117 +name = "studded hide wrist wrap wrist-wrap" +short = "a studded hide wrist-wrap" +description = "A hide wrist-wrap with studded bone bits has been left here." +main_description = "" +type = 9 +extra_flags = [0, 0, 0, 0] +wear_flags = [8193, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 1, 0, 0, 0, 0, 0, 0] +weight = 8 +cost = 40 +level = 0 +timer = 0 + +[[object]] +vnum = 118 +name = "padded neckguard" +short = "a padded neckguard" +description = "A padded neckguard made of cloth is here." +main_description = "" +type = 9 +extra_flags = [0, 0, 0, 0] +wear_flags = [5, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 1, 0, 0, 0, 0, 0, 0] +weight = 5 +cost = 25 +level = 0 +timer = 0 + +[[object]] +vnum = 119 +name = "pair padded shoes" +short = "a pair of padded shoes" +description = "An assuming pair of padded cloth shoes are here." +main_description = "" +type = 9 +extra_flags = [0, 0, 0, 0] +wear_flags = [129, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 1, 0, 0, 0, 0, 0, 0] +weight = 8 +cost = 25 +level = 0 +timer = 0 + +[[object]] +vnum = 120 +name = "black belt" +short = "a black belt" +description = "A belt made of black cloth lies here." +main_description = "" +type = 15 +extra_flags = [0, 0, 0, 0] +wear_flags = [4097, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [10, 0, 0, 0, 0, 0, 0, 0] +weight = 1 +cost = 25 +level = 0 +timer = 0 + +[[object]] +vnum = 121 +name = "small bag" +short = "a small bag" +description = "A small bag made of cloth lies here." +main_description = "" +type = 15 +extra_flags = [0, 0, 0, 0] +wear_flags = [1, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [15, 0, 0, 0, 0, 0, 0, 0] +weight = 1 +cost = 25 +level = 0 +timer = 0 + +[[object]] +vnum = 122 +name = "bag large" +short = "a large bag" +description = "A large bag made of cloth is lying here." +main_description = "" +type = 15 +extra_flags = [0, 0, 0, 0] +wear_flags = [1, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [50, 0, 0, 0, 0, 0, 0, 0] +weight = 1 +cost = 50 +level = 0 +timer = 0 + +[[object]] +vnum = 123 +name = "pair boots" +short = "a pair of boots" +description = "A pair of simple leather boots are here." +main_description = "" +type = 9 +extra_flags = [0, 0, 0, 0] +wear_flags = [129, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 1, 0, 0, 0, 0, 0, 0] +weight = 1 +cost = 25 +level = 0 +timer = 0 + +[[object]] +vnum = 124 +name = "pair thick leather boots" +short = "a pair of thick leather boots" +description = "Boots made of thick leather have been left here." +main_description = "" +type = 9 +extra_flags = [0, 0, 0, 0] +wear_flags = [129, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [2, 2, 0, 0, 0, 0, 0, 0] +weight = 5 +cost = 50 +level = 0 +timer = 0 + +[[object]] +vnum = 125 +name = "bone shortsword sword" +short = "a bone shortsword" +description = "Made of bone, a shortsword has been left here." +main_description = "" +type = 5 +extra_flags = [0, 0, 0, 0] +wear_flags = [49153, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 6, 11, 0, 0, 0, 0, 0] +weight = 5 +cost = 50 +level = 0 +timer = 0 + +[[object]] +vnum = 126 +name = "bone dagger" +short = "a bone dagger" +description = "A dagger made of bone lies here abandoned." +main_description = "" +type = 5 +extra_flags = [0, 0, 0, 0] +wear_flags = [49153, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 4, 11, 0, 0, 0, 0, 0] +weight = 2 +cost = 25 +level = 0 +timer = 0 + +[[object]] +vnum = 127 +name = "bone longsword sword" +short = "a bone longsword" +description = "Long and slender, a sword lies here." +main_description = "" +type = 5 +extra_flags = [0, 0, 0, 0] +wear_flags = [16385, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 8, 3, 0, 0, 0, 0, 0] +weight = 8 +cost = 100 +level = 0 +timer = 0 + +[[object]] +vnum = 128 +name = "bone club" +short = "a bone club" +description = "Made of bone, a club is lying here." +main_description = "" +type = 5 +extra_flags = [0, 0, 0, 0] +wear_flags = [49153, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 4, 5, 0, 0, 0, 0, 0] +weight = 5 +cost = 50 +level = 0 +timer = 0 + +[[object]] +vnum = 129 +name = "bone javelin" +short = "a bone javelin" +description = "A long, bone javelin has been left here." +main_description = "" +type = 5 +extra_flags = [0, 0, 0, 0] +wear_flags = [16385, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 6, 11, 0, 0, 0, 0, 0] +weight = 5 +cost = 50 +level = 0 +timer = 0 + +[[object]] +vnum = 130 +name = "bone spear" +short = "a bone spear" +description = "A long piece of bones with a sharpened edge has been left here." +main_description = "" +type = 5 +extra_flags = [0, 0, 0, 0] +wear_flags = [16385, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 6, 11, 0, 0, 0, 0, 0] +weight = 3 +cost = 50 +level = 0 +timer = 0 + +[[object]] +vnum = 131 +name = "backpack pack hide" +short = "a hide backpack" +description = "A backpack made of thick hide is lying here." +main_description = "" +type = 15 +extra_flags = [0, 0, 0, 0] +wear_flags = [9, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [75, 0, 0, 0, 0, 0, 0, 0] +weight = 5 +cost = 50 +level = 0 +timer = 0 + +[[object]] +vnum = 132 +name = "counter long rough hewn stone" +short = "a long, rough-hewn stone counter" +description = "A long, rough-hewn stone counter is here along the eastern wall." +main_description = " This stone countertop is a crude L-shape, allowing the staff of the\nestablishment to enter from behind it as need be. Carved of stone, it is\nrough-hewn except on the very top, which has been polished down as much as\npossible. Chips and cracks have formed across its surface, which is beginning\nto show its age. Most of the counter is relatively clean, but certain spots are\nsticky from spilled drink and food.\n" +type = 6 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [8, 0, 0, 0, 0, 0, 0, 0] +weight = 200 +cost = 0 +level = 0 +timer = 0 + +[[object]] +vnum = 133 +name = "table long carved stone" +short = "a long, carved stone table" +description = "A long, carved stone table is near the southern wall." +main_description = "Made of one piece of solid stone, this table is easily six feet in length. At\neach of its corners a thick leg supports the top. Each leg at the base has been\ncarved to look like an erdlu's foot, with small talons extending out diagonally.\nOver time it has been worn and chipped, leaving it stained but still functional.\n" +type = 6 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [6, 0, 1, 0, 0, 0, 0, 0] +weight = 100 +cost = 0 +level = 0 +timer = 0 + +[[object]] +vnum = 134 +name = "ledge small stone" +short = "a small stone ledge" +description = "A small stone ledge rests against the western wall here." +main_description = "This makeshift seat is really nothing more than a large slab of stone. It is\nunfinished, yet shows some attempts to carve it into a more suitable place to\nsit upon. One end is still slightly jagged, though has been dulled so as not to\neasily hurt someone.\n" +type = 6 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [2, 0, 0, 0, 0, 0, 0, 0] +weight = 100 +cost = 0 +level = 0 +timer = 0 + +[[object]] +vnum = 135 +name = "mug ale" +short = "a mug of ale" +description = "A mug of ale has been left here." +main_description = "Made of clay, this mug appears to have been fired in a kiln to retain its shape.\nThe craftsmanship is questionable, but it does it job well enough.\n" +type = 17 +extra_flags = [0, 0, 0, 0] +wear_flags = [32769, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [3, 3, 3, 0, 0, 0, 0, 0] +weight = 8 +cost = 12 +level = 0 +timer = 0 + +[[object]] +vnum = 136 +name = "shot whiskey" +short = "a shot of whiskey" +description = "A shot of whiskey has been set here." +main_description = "This shotglass is made of fired clay and is small enough to hold a single pour\nof spirits.\n" +type = 17 +extra_flags = [0, 0, 0, 0] +wear_flags = [32769, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 1, 5, 0, 0, 0, 0, 0] +weight = 1 +cost = 15 +level = 0 +timer = 0 + +[[object]] +vnum = 137 +name = "shot firebreather" +short = "a shot of firebreather" +description = "A shot of firebreather has been set here." +main_description = " Made of discolored stone, this shotglass appears to have been chipped by\nhand. It could probably hold a small amount of spirits.\n" +type = 17 +extra_flags = [0, 0, 0, 0] +wear_flags = [32769, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +weight = 1 +cost = 15 +level = 0 +timer = 0 + +[[object]] +vnum = 138 +name = "cup water" +short = "a cup of water" +description = "A small cup filled with water is here." +main_description = " This small cup is carved from wood and looks like it could hold a fair amount\nof liquid.\n" +type = 17 +extra_flags = [0, 0, 0, 0] +wear_flags = [1, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [4, 4, 0, 0, 0, 0, 0, 0] +weight = 9 +cost = 8 +level = 0 +timer = 0 + +[[object]] +vnum = 139 +name = "skewer erdlu meat" +short = "an erdlu meat skewer" +description = "A skewer with some browned meat is here." +main_description = " A thin piece of wood has been carved into a long skewer, with one end tied\ninto a small hook. Pieces of dark erdlu meat have been pressed into the skewer\nand cooked over an open fire.\n" +type = 19 +extra_flags = [0, 0, 0, 0] +wear_flags = [32769, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [2, 2, 1, 0, 0, 0, 0, 0] +weight = 1 +cost = 12 +level = 0 +timer = 0 + +[[object]] +vnum = 140 +name = "steak carru thick" +short = "a thick carru steak" +description = "A thick, cooked steak is here." +main_description = " This steak is a very large piece of meat that has been cooked over an open\nfire. Grill marks can be seen on both sides which form small X's.\n" +type = 19 +extra_flags = [0, 0, 0, 0] +wear_flags = [1, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [8, 8, 1, 0, 0, 0, 0, 0] +weight = 2 +cost = 20 +level = 0 +timer = 0 + +[[object]] +vnum = 141 +name = "bacon aprig plate" +short = "a plate of aprig bacon" +description = "A plate with several slices of cooked bacon is here." +main_description = " This plate is small, but piled high with pieces of bacon. They are cooked to\nthe point that they are still chewy but not quite crunchy.\n" +type = 19 +extra_flags = [0, 0, 0, 0] +wear_flags = [1, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [4, 4, 1, 0, 0, 0, 0, 0] +weight = 1 +cost = 18 +level = 0 +timer = 0 + +[[object]] +vnum = 142 +name = "bread hunk" +short = "a hunk of bread" +description = "A piece of bread has been torn from a loaf and left here." +main_description = " Torn from a larger piece of bread, this hunk is a still a decent size. The\ncrust is brown and the inside is fluffy.\n" +type = 19 +extra_flags = [0, 0, 0, 0] +wear_flags = [1, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [2, 2, 1, 0, 0, 0, 0, 0] +weight = 1 +cost = 14 +level = 0 +timer = 0 + +[[object]] +vnum = 143 +name = "cot small" +short = "a small cot" +description = "A small cot has been set up here." +main_description = " This cot is made primarily from pieces of wood that are lashed together with\nrope. Pieces of hide have been tied down, providing a soft yet supportive place\nfor someone to rest upon.\n" +type = 6 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 0, 0, 0, 0, 0, 0, 0] +weight = 10 +cost = 100 +level = 0 +timer = 0 + +[[object]] +vnum = 144 +name = "barrel water" +short = "a barrel of water" +description = "A barrel of water has been placed here." +main_description = " This barrel is made of thick agafari wood and bound by pieces of rope to keep\nit from falling apart. Pitch has been pressed between each each stave.\n" +type = 17 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [100, 100, 0, 0, 0, 0, 0, 0] +weight = 20 +cost = 0 +level = 0 +timer = 0 + +[[object]] +vnum = 145 +name = "table" +short = "a squat wooden table" +description = "A squat wooden table is here, stained and dirty." +main_description = " This table was once expertly carved and stained, but years of use have begun\nto show. Drinks have been spilled on it repeatedly, staining its top in\ndifferent patterns.\n" +type = 6 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [4, 0, 2, 0, 0, 0, 0, 0] +weight = 40 +cost = 0 +level = 0 +timer = 0 + +[[object]] +vnum = 146 +name = "table long chipped granite" +short = "a long, chipped granite table" +description = "A long granite table that is chipped and worn is here." +main_description = " Carved from several pieces of granite, this table seems to have been made in\nsuch a way that the pieces can slot into each other to make the final form. \nLong lines can be seen at various points which have accumulated dirt and debris\nover time. The pieces don't fit properly in some areas, which has led to them\nbecoming chipped and worn.\n" +type = 6 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [6, 0, 2, 0, 0, 0, 0, 0] +weight = 80 +cost = 0 +level = 0 +timer = 0 + +[[object]] +vnum = 147 +name = "pen animal wooden" +short = "a wooden animal pen" +description = "A wooden animal pen is here, holding several mounts." +main_description = " Within the wooden walls of this animal pen, several beasts can be seen\nroaming about. There isn't a whole lot of room, but enough so that they can\npush past each other to get to the water troughs and feeding bins.\n" +type = 13 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +weight = 100 +cost = 0 +level = 0 +timer = 0 + +[[object]] +vnum = 148 +name = "torch bone" +short = "a bone torch" +description = "A torch made out of a piece of bone has been discarded here." +main_description = " Made out of a piece of broken bone, this torch has some oil-wrapped cloth\nwrapped around one end.\n" +type = 1 +extra_flags = [0, 0, 0, 0] +wear_flags = [32769, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 8, 0, 0, 0, 0, 0] +weight = 1 +cost = 20 +level = 0 +timer = 0 + +[[object]] +vnum = 149 +name = "torch wooden" +short = "a wooden torch" +description = "A wooden torch has been left here." +main_description = " A piece of carved wood has been fashioned into a crude torch, with one end\nwrapped in oil-soaked cloth.\n" +type = 1 +extra_flags = [0, 0, 0, 0] +wear_flags = [32769, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 8, 0, 0, 0, 0, 0] +weight = 1 +cost = 20 +level = 0 +timer = 0 + +[[object]] +vnum = 150 +name = "board rumor" +short = "the Caleran rumor board" +description = "A board with rumors has been placed here." +main_description = "OOC: This is the rumor board for the city of Caleran. Treat it as if you are overhearing\na rumor at the bar.\n" +type = 13 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +weight = 0 +cost = 0 +level = 0 +timer = 0 + +[[object]] +vnum = 151 +name = "lantern stone" +short = "a stone lantern" +description = "A lantern carved out of grey stone is here." +main_description = " Made out of a single piece of grey stone, this lantern has been carved to\nallow for a candle to be placed inside. A small handle on the top allows the\nuser to carry it.\n" +type = 1 +extra_flags = [0, 0, 0, 0] +wear_flags = [32769, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 14, 0, 0, 0, 0, 0] +weight = 2 +cost = 40 +level = 0 +timer = 0 + +[[object]] +vnum = 152 +name = "basket wicker" +short = "a wicker basket" +description = "A wicker basket has been left here." +main_description = " Made of some sort of dried plant reed, this basket has been tightly braided\nwith few visible holes. A handle has been made that that allows for ease of\ncarry.\n" +type = 15 +extra_flags = [0, 0, 0, 0] +wear_flags = [32769, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [10, 0, 0, 0, 0, 0, 0, 0] +weight = 1 +cost = 40 +level = 0 +timer = 0 + +[[object]] +vnum = 153 +name = "backpack pack jozhal hide" +short = "a jozhal-hide backpack" +description = "A backpack made out of jozhal hide is lying here." +main_description = " This backpack has been made out of several pieces of jozhal hide. Sewn\ntogether to form the main compartment, it has a pair of shoulder straps that\nwould fit most humanoids.\n" +type = 15 +extra_flags = [0, 0, 0, 0] +wear_flags = [32777, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [40, 0, 0, 0, 0, 0, 0, 0] +weight = 4 +cost = 80 +level = 0 +timer = 0 + +[[object]] +vnum = 154 +name = "rope short piece" +short = "a short piece of rope" +description = "A short piece of rope has been left here." +main_description = " This piece of rope is no more than six feet in length and has been made out\nof some sort of plant fiber.\n" +type = 13 +extra_flags = [0, 0, 0, 0] +wear_flags = [32769, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +weight = 1 +cost = 50 +level = 0 +timer = 0 + +[[object]] +vnum = 155 +name = "rope coil" +short = "a coil of rope" +description = "A long piece of rope has been coiled and left here." +main_description = " This piece of rope must be at least thirty feet in length. Made out of some\nsort of plant fiber, the ends have been waxed slightly to prevent it from\nunraveling.\n" +type = 13 +extra_flags = [0, 0, 0, 0] +wear_flags = [32769, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +weight = 5 +cost = 200 +level = 0 +timer = 0 + +[[object]] +vnum = 156 +name = "ring bone" +short = "a bone ring" +description = "A simple hoop of bone is lying here." +main_description = " This ring is carved from a single piece of yellowed bone. It is unevenly\nshaped and likely would slip off the finger.\n" +type = 11 +extra_flags = [0, 0, 0, 0] +wear_flags = [3, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +weight = 1 +cost = 10 +level = 0 +timer = 0 + +[[object]] +vnum = 157 +name = "ring bone blackened" +short = "a blackened bone ring" +description = "A blackened piece of bone in the shape of a ring is here." +main_description = " This piece of bone has been carved into the shape of a ring, then blackened\nin a fire.\n" +type = 11 +extra_flags = [0, 0, 0, 0] +wear_flags = [3, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +weight = 1 +cost = 15 +level = 0 +timer = 0 + +[[object]] +vnum = 158 +name = "necklace bone" +short = "a bone necklace" +description = "A necklace made of pieces of bone is lying here." +main_description = " Various pieces of bone have had a hole drilled through them to allow for a\nstring to pass through, forming a simple necklace.\n" +type = 11 +extra_flags = [0, 0, 0, 0] +wear_flags = [5, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +weight = 1 +cost = 25 +level = 0 +timer = 0 + +[[object]] +vnum = 159 +name = "necklace blackened bone" +short = "a blackened bone necklace" +description = "A blackened bone necklace has been left here." +main_description = " Small and thick piece of bone have had a small hole drilled into them to\ncreate this crude necklace. Each piece seems to have been blackened in a fire,\nthough some are lighter than others.\n" +type = 11 +extra_flags = [0, 0, 0, 0] +wear_flags = [5, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +weight = 1 +cost = 40 +level = 0 +timer = 0 + +[[object]] +vnum = 160 +name = "waterskin skin" +short = "a waterskin" +description = "A simple waterskin is lying here." +main_description = " Made of some sort of animal hide, the pieces have been sewn tightly together\nto form a receptacle that can hold water.\n" +type = 17 +extra_flags = [0, 0, 0, 0] +wear_flags = [32769, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [4, 4, 0, 0, 0, 0, 0, 0] +weight = 9 +cost = 40 +level = 0 +timer = 0 + +[[object]] +vnum = 161 +name = "waterpouch pouch leather" +short = "a leather waterpouch" +description = "A leather waterpouch has been left here." +main_description = " Formed out of a few pieces of cheap leather that have been sewn tightly\ntogether, this waterpouch has a small opening on one side which can be capped to\nprevent water spilling out.\n" +type = 17 +extra_flags = [0, 0, 0, 0] +wear_flags = [32769, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [4, 4, 0, 0, 0, 0, 0, 0] +weight = 9 +cost = 40 +level = 0 +timer = 0 + +[[object]] +vnum = 162 +name = "cloak dark hooded" +short = "a dark, hooded cloak" +description = "A piece of dark fabric lays here in a heap." +main_description = " This piece of fabric is made of linen and has been dyed a dark, blackish-blue\ncolor. It has a few small pockets sewn inside and a clasp near the neck to\nclose it. A large hood has been attached, allowing the wearer to partially\ncover their face.\n" +type = 11 +extra_flags = [0, 0, 0, 0] +wear_flags = [2049, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [1, 1, 0, 10, 0, 0, 0, 0] +weight = 1 +cost = 10 +level = 0 +timer = 0 + +[[object]] +vnum = 163 +name = "pants worn fraying" +short = "a pair of worn, fraying pants" +description = "Some worn, fraying fabric is lying here in a heap." +main_description = " What was once likely a nice pair of pants has been worn down over several\nyears. The bottom has frayed and is in tatters, while the knees are completely\nsplit open.\n" +type = 11 +extra_flags = [0, 0, 0, 0] +wear_flags = [65, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +weight = 1 +cost = 5 +level = 0 +timer = 0 + +[[object]] +vnum = 164 +name = "shirt worn sleeveless" +short = "a worn, sleeveless shirt" +description = "A plain shirt without sleeves lies here." +main_description = " This shirt at one point had sleeves, but they have been cut from the body. \nA plain undyed color, it looks similar in color to sand.\n" +type = 11 +extra_flags = [0, 0, 0, 0] +wear_flags = [17, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +weight = 1 +cost = 5 +level = 0 +timer = 0 + +[[object]] +vnum = 165 +name = "shirt sandcloth" +short = "a sandcloth shirt" +description = "A shirt made of sandcloth lies here." +main_description = " This shirt is made from undyed sandcloth. Woven to keep out the harsh gritty\nsand that is found nearly everywhere, the fabric is known for being lightweight\nand resistant to the elements.\n" +type = 11 +extra_flags = [0, 0, 0, 0] +wear_flags = [17, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +weight = 1 +cost = 20 +level = 0 +timer = 0 + +[[object]] +vnum = 166 +name = "trousers sandcloth pair" +short = "a pair of sandcloth trousers" +description = "A pile of undyed sandcloth lies here in a heap." +main_description = " These pants have been sewn by a modest tailor and made out of undyed\nsandcloth. Known for its ruggedness and being lightweight, they look capable of\nkeeping the wearer safe from the elements.\n" +type = 11 +extra_flags = [0, 0, 0, 0] +wear_flags = [65, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +weight = 2 +cost = 30 +level = 0 +timer = 0 + +[[object]] +vnum = 167 +name = "boots carru hide pair" +short = "a pair of carru-hide boots" +description = "A pair of boots made out of carru hide are here." +main_description = " Made from tanned carru hide, these boots look sturdy and well worn. The\nstitching is dark and repeats an X pattern, reinforcing them to ensure they\nremain reliable for a long time. The sole appears to be made of two layers of\nhide, reinforcing the foot from the ground below.\n" +type = 11 +extra_flags = [0, 0, 0, 0] +wear_flags = [129, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +weight = 1 +cost = 40 +level = 0 +timer = 0 + +[[object]] +vnum = 168 +name = "unfinished object" +short = "unfinished object made by Kinther" +description = "This is an unfinished object created by Kinther on Wed Dec 31 14:19:06 2025" +main_description = "" +type = 0 +extra_flags = [0, 0, 0, 0] +wear_flags = [1, 0, 0, 0] +affect_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +weight = 0 +cost = 0 +level = 0 +timer = 0 + diff --git a/lib/world/obj/index.mini.toml b/lib/world/obj/index.mini.toml new file mode 100644 index 0000000..042ad07 --- /dev/null +++ b/lib/world/obj/index.mini.toml @@ -0,0 +1,2 @@ +files = [ +] diff --git a/lib/world/obj/index.toml b/lib/world/obj/index.toml new file mode 100644 index 0000000..e9d4550 --- /dev/null +++ b/lib/world/obj/index.toml @@ -0,0 +1,4 @@ +files = [ + "0.toml", + "1.toml" +] diff --git a/lib/world/qst/0.toml b/lib/world/qst/0.toml new file mode 100644 index 0000000..f8a3274 --- /dev/null +++ b/lib/world/qst/0.toml @@ -0,0 +1 @@ +quest = [] diff --git a/lib/world/qst/1.toml b/lib/world/qst/1.toml new file mode 100644 index 0000000..f8a3274 --- /dev/null +++ b/lib/world/qst/1.toml @@ -0,0 +1 @@ +quest = [] diff --git a/lib/world/qst/index.mini.toml b/lib/world/qst/index.mini.toml new file mode 100644 index 0000000..042ad07 --- /dev/null +++ b/lib/world/qst/index.mini.toml @@ -0,0 +1,2 @@ +files = [ +] diff --git a/lib/world/qst/index.toml b/lib/world/qst/index.toml new file mode 100644 index 0000000..e9d4550 --- /dev/null +++ b/lib/world/qst/index.toml @@ -0,0 +1,4 @@ +files = [ + "0.toml", + "1.toml" +] diff --git a/lib/world/rsv/0.toml b/lib/world/rsv/0.toml new file mode 100644 index 0000000..d788f42 --- /dev/null +++ b/lib/world/rsv/0.toml @@ -0,0 +1,15 @@ +[[room]] +vnum = 1 +saved_at = 1759515981 + +[[room.object]] +vnum = 1 +timer = 0 +weight = 0 +cost = 0 +cost_per_day = 0 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +contents = [] + diff --git a/lib/world/rsv/1.toml b/lib/world/rsv/1.toml new file mode 100644 index 0000000..6b08619 --- /dev/null +++ b/lib/world/rsv/1.toml @@ -0,0 +1,287 @@ +[[room]] +vnum = 100 +saved_at = 1759514843 + +[[room.mob]] +vnum = 100 + +[[room.mob.equipment]] +wear_pos = 3 +vnum = 118 +contents = [] + +[[room.mob.equipment]] +wear_pos = 5 +vnum = 131 +contents = [] + +[[room.mob.equipment]] +wear_pos = 6 +vnum = 110 +contents = [] + +[[room.mob.equipment]] +wear_pos = 7 +vnum = 108 +contents = [] + +[[room.mob.equipment]] +wear_pos = 8 +vnum = 115 +contents = [] + +[[room.mob.equipment]] +wear_pos = 9 +vnum = 124 +contents = [] + +[[room.mob.equipment]] +wear_pos = 10 +vnum = 107 +contents = [] + +[[room.mob.equipment]] +wear_pos = 11 +vnum = 111 +contents = [] + +[[room.mob.equipment]] +wear_pos = 15 +vnum = 117 +contents = [] + +[[room.mob.equipment]] +wear_pos = 16 +vnum = 117 +contents = [] + +[[room.mob.equipment]] +wear_pos = 17 +vnum = 127 +contents = [] + +[[room.mob]] +vnum = 101 + +[[room.mob.equipment]] +wear_pos = 3 +vnum = 118 +contents = [] + +[[room.mob.equipment]] +wear_pos = 5 +vnum = 131 +contents = [] + +[[room.mob.equipment]] +wear_pos = 6 +vnum = 110 +contents = [] + +[[room.mob.equipment]] +wear_pos = 7 +vnum = 108 +contents = [] + +[[room.mob.equipment]] +wear_pos = 8 +vnum = 115 +contents = [] + +[[room.mob.equipment]] +wear_pos = 9 +vnum = 124 +contents = [] + +[[room.mob.equipment]] +wear_pos = 10 +vnum = 107 +contents = [] + +[[room.mob.equipment]] +wear_pos = 11 +vnum = 111 +contents = [] + +[[room.mob.equipment]] +wear_pos = 15 +vnum = 117 +contents = [] + +[[room.mob.equipment]] +wear_pos = 16 +vnum = 117 +contents = [] + +[[room.mob.equipment]] +wear_pos = 17 +vnum = 127 +contents = [] + +[[room]] +vnum = 200 +saved_at = 1759595106 + +[[room.object]] +vnum = 143 +timer = 0 +weight = 10 +cost = 100 +cost_per_day = 0 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +values = [1, 0, 0, 0, 0, 0, 0, 0] +contents = [] + +[[room]] +vnum = 136 +saved_at = 1759603517 + +[[room.object]] +vnum = 147 +timer = 0 +weight = 100 +cost = 0 +cost_per_day = 0 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +contents = [] + +[[room]] +vnum = 134 +saved_at = 1760390381 + +[[room.object]] +vnum = 134 +timer = 0 +weight = 100 +cost = 0 +cost_per_day = 0 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +values = [2, 0, 0, 0, 0, 0, 0, 0] +contents = [] + +[[room.object]] +vnum = 133 +timer = 0 +weight = 100 +cost = 0 +cost_per_day = 0 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +values = [6, 0, 1, 0, 0, 0, 0, 0] +contents = [] + +[[room.object]] +vnum = 132 +timer = 0 +weight = 200 +cost = 0 +cost_per_day = 0 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +values = [8, 0, 0, 0, 0, 0, 0, 0] +contents = [] + +[[room.object]] +vnum = 150 +timer = 0 +weight = 0 +cost = 0 +cost_per_day = 0 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +values = [0, 0, 0, 0, 0, 0, 0, 0] +contents = [] + +[[room.mob]] +vnum = 102 + +[[room.mob.equipment]] +wear_pos = 9 +vnum = 112 +contents = [] + +[[room.mob.equipment]] +wear_pos = 14 +vnum = 113 +contents = [] + +[[room.mob.inventory]] +vnum = 138 +contents = [] + +[[room.mob.inventory]] +vnum = 137 +contents = [] + +[[room.mob.inventory]] +vnum = 136 +contents = [] + +[[room.mob.inventory]] +vnum = 135 +contents = [] + +[[room.mob.inventory]] +vnum = 139 +contents = [] + +[[room.mob.inventory]] +vnum = 140 +contents = [] + +[[room.mob.inventory]] +vnum = 141 +contents = [] + +[[room.mob.inventory]] +vnum = 142 +contents = [] + +[[room.mob]] +vnum = 103 + +[[room]] +vnum = 131 +saved_at = 1767296856 + +[[room.object]] +vnum = 144 +timer = 0 +weight = 20 +cost = 0 +cost_per_day = 0 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +values = [100, 100, 0, 0, 0, 0, 0, 0] +contents = [] + +[[room.object]] +vnum = 145 +timer = 0 +weight = 40 +cost = 0 +cost_per_day = 0 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +values = [4, 0, 2, 0, 0, 0, 0, 0] +contents = [] + +[[room.object]] +vnum = 146 +timer = 0 +weight = 80 +cost = 0 +cost_per_day = 0 +extra_flags = [0, 0, 0, 0] +wear_flags = [0, 0, 0, 0] +values = [6, 0, 2, 0, 0, 0, 0, 0] +contents = [] + +[[room]] +vnum = 101 +saved_at = 1767296996 + diff --git a/lib/world/shp/0.toml b/lib/world/shp/0.toml new file mode 100644 index 0000000..001791d --- /dev/null +++ b/lib/world/shp/0.toml @@ -0,0 +1 @@ +shop = [] diff --git a/lib/world/shp/1.toml b/lib/world/shp/1.toml new file mode 100644 index 0000000..ee98b21 --- /dev/null +++ b/lib/world/shp/1.toml @@ -0,0 +1,54 @@ +[[shop]] +vnum = 100 +products = [135, 136, 137, 138, 139, 140, 141, 142] +buy_profit = 1.00 +sell_profit = 1.00 +broke_temper = 0 +bitvector = 4 +keeper = 102 +trade_with = 0 +rooms = [134] +open1 = 0 +close1 = 28 +open2 = 0 +close2 = 0 +bank = 0 +sort = 0 + +[shop.messages] +no_such_item1 = "%s We're fresh out of that." +no_such_item2 = "%s Trying to sell something you don't have?" +do_not_buy = "%s Not interested. Try someone else." +missing_cash1 = "%s I can't afford that!" +missing_cash2 = "%s Come back when you have some coin." +message_buy = "%s That'll be %d coins." +message_sell = "%s I'll give you %d coins for that." + + +[[shop]] +vnum = 101 +products = [138] +buy_profit = 1.00 +sell_profit = 1.00 +broke_temper = 0 +bitvector = 0 +keeper = 103 +trade_with = 0 +rooms = [] +open1 = 0 +close1 = 28 +open2 = 0 +close2 = 0 +bank = 0 +sort = 0 + +[shop.messages] +no_such_item1 = "%s Sorry, I don't stock that item." +no_such_item2 = "%s You don't seem to have that." +do_not_buy = "%s I don't trade in such items." +missing_cash1 = "%s I can't afford that!" +missing_cash2 = "%s You are too poor!" +message_buy = "%s That'll be %d coins, thanks." +message_sell = "%s I'll give you %d coins for that." + + diff --git a/lib/world/shp/index.mini.toml b/lib/world/shp/index.mini.toml new file mode 100644 index 0000000..042ad07 --- /dev/null +++ b/lib/world/shp/index.mini.toml @@ -0,0 +1,2 @@ +files = [ +] diff --git a/lib/world/shp/index.toml b/lib/world/shp/index.toml new file mode 100644 index 0000000..e9d4550 --- /dev/null +++ b/lib/world/shp/index.toml @@ -0,0 +1,4 @@ +files = [ + "0.toml", + "1.toml" +] diff --git a/lib/world/trg/0.toml b/lib/world/trg/0.toml new file mode 100644 index 0000000..c125925 --- /dev/null +++ b/lib/world/trg/0.toml @@ -0,0 +1,11 @@ +[[trigger]] +vnum = 1 +name = "Test" +attach_type = 0 +flags = 65536 +narg = 1 +arglist = "" +commands = [ + "* No Script", +] + diff --git a/lib/world/trg/1.toml b/lib/world/trg/1.toml new file mode 100644 index 0000000..60a49b1 --- /dev/null +++ b/lib/world/trg/1.toml @@ -0,0 +1 @@ +trigger = [] diff --git a/lib/world/trg/2.toml b/lib/world/trg/2.toml new file mode 100644 index 0000000..60a49b1 --- /dev/null +++ b/lib/world/trg/2.toml @@ -0,0 +1 @@ +trigger = [] diff --git a/lib/world/trg/index.mini.toml b/lib/world/trg/index.mini.toml new file mode 100644 index 0000000..042ad07 --- /dev/null +++ b/lib/world/trg/index.mini.toml @@ -0,0 +1,2 @@ +files = [ +] diff --git a/lib/world/trg/index.toml b/lib/world/trg/index.toml new file mode 100644 index 0000000..d18678b --- /dev/null +++ b/lib/world/trg/index.toml @@ -0,0 +1,5 @@ +files = [ + "0.toml", + "1.toml", + "2.toml" +] diff --git a/lib/world/wld/0.toml b/lib/world/wld/0.toml new file mode 100644 index 0000000..b8b6c00 --- /dev/null +++ b/lib/world/wld/0.toml @@ -0,0 +1,7 @@ +[[room]] +vnum = 1 +name = "Limbo" +description = "Floating in the void, you lose all sense of time and space.\n" +flags = [0, 0, 0, 0] +sector = 0 + diff --git a/lib/world/wld/1.toml b/lib/world/wld/1.toml new file mode 100644 index 0000000..b4a4a19 --- /dev/null +++ b/lib/world/wld/1.toml @@ -0,0 +1,2370 @@ +[[room]] +vnum = 100 +name = "Caravan Gate" +description = " Two large doors, each at least ten feet in height, make up the massive gate\nset into the walls that surround the city. Made of agafari wood, mekillot hide,\nrusted old studs and iron hinges, they provide access into the city for\ntravelers and traders alike. A massive stone block sits off to the side of the\nroad, which seems to have been carved to the exact size and shape of the gate\nitself. Caravans and wagons can be seen passing through at all hours of the day\nwhen the gates are open, with some pausing just inside the gate to unload while\nothers continue on their way to the inner city.\n To the north and south, Wall Road continues onward. To the west, the main\nthoroughfare of Caravan Way passes by several buildings.\n" +flags = [0, 0, 0, 0] +sector = 1 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 146 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 135 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 101 + +[[room]] +vnum = 101 +name = "Caravan Way" +description = " This broad avenue is wide enough for several mounted riders to easily\nnavigate, or even a pair of wagons to pass by without incident. Buildings line\nthe road offering services to travelers and visitors of the city. Street\nvendors line the sides, with an occasional shout to draw attention to their\nwares. The route through the city itself is made up of brick, though several\nappear cracked and broken when given a thorough inspection.\n Caravan Way continues to the west and east. A placard with a stylized\neyeball, ear, and lips hangs as a standard above a building to the north,\nwhile the smell of a stable wafts from the south.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 131 +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 100 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 136 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 102 + +[[room]] +vnum = 102 +name = "Caravan Way" +description = " This broad avenue is wide enough for several mounted riders to easily\nnavigate, or even a pair of wagons to pass by without incident. Buildings line\nthe road offering services to travelers and visitors of the city. Street\nvendors line the sides, with an occasional shout to draw attention to their\nwares. The route through the city itself is made up of brick, though several\nappear cracked and broken when given a thorough inspection.\n Caravan Way continues to the west and east. To the south is a two-story\nbuilding with a sign hanging above the entrance of crossed swords.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 101 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 132 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 103 + +[[room]] +vnum = 103 +name = "Caravan Way" +description = " This broad avenue is wide enough for several mounted riders to easily\nnavigate, or even a pair of wagons to pass by without incident. Buildings line\nthe road offering services to travelers and visitors of the city. Street\nvendors line the sides, with an occasional shout to draw attention to their\nwares. The route through the city itself is made up of brick, though several\nappear cracked and broken when given a thorough inspection.\n Caravan Way continues to the north and east. To the south, a side street\npasses between some buildings.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 104 +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 102 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 145 + +[[room]] +vnum = 104 +name = "Caravan Way" +description = " This broad avenue is wide enough for several mounted riders to easily\nnavigate, or even a pair of wagons to pass by without incident. Buildings line\nthe road offering services to travelers and visitors of the city. Street\nvendors line the sides, with an occasional shout to draw attention to their\nwares. The route through the city itself is made up of brick, though several\nappear cracked and broken when given a thorough inspection.\n Caravan Way continues to the west and south. Buildings surround you on\nall sides.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 103 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 105 + +[[room]] +vnum = 105 +name = "Caravan Way" +description = " This broad avenue is wide enough for several mounted riders to easily\nnavigate, or even a pair of wagons to pass by without incident. Buildings line\nthe road offering services to travelers and visitors of the city. Street\nvendors line the sides, with an occasional shout to draw attention to their\nwares. The route through the city itself is made up of brick, though several\nappear cracked and broken when given a thorough inspection.\n Caravan Way continues to the west and east. To the north, a side street\npasses between some buildings.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 155 +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 104 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 106 + +[[room]] +vnum = 106 +name = "Caravan Way" +description = " This broad avenue is wide enough for several mounted riders to easily\nnavigate, or even a pair of wagons to pass by without incident. Buildings line\nthe road offering services to travelers and visitors of the city. Street\nvendors line the sides, with an occasional shout to draw attention to their\nwares. The route through the city itself is made up of brick, though several\nappear cracked and broken when given a thorough inspection.\n Caravan Way continues to the west and south. Buildings surround you on\nall sides.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 105 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 107 + +[[room]] +vnum = 107 +name = "Caravan Way" +description = " This broad avenue is wide enough for several mounted riders to easily\nnavigate, or even a pair of wagons to pass by without incident. Buildings line\nthe road offering services to travelers and visitors of the city. Street\nvendors line the sides, with an occasional shout to draw attention to their\nwares. The route through the city itself is made up of brick, though several\nappear cracked and broken when given a thorough inspection.\n Caravan Way continues to the south and east. To the west appears to be a\nbuilding that deals with garments.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 106 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 108 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 133 + +[[room]] +vnum = 108 +name = "Caravan Way" +description = " This broad avenue is wide enough for several mounted riders to easily\nnavigate, or even a pair of wagons to pass by without incident. Buildings line\nthe road offering services to travelers and visitors of the city. Street\nvendors line the sides, with an occasional shout to draw attention to their\nwares. The route through the city itself is made up of brick, though several\nappear cracked and broken when given a thorough inspection.\n Caravan Way continues to the north and west. To the south, a two story\nbuilding rises above the others. Hanging above the entrance is a sign with\na golden inix upon it and a stylized bottle.\n" +flags = [0, 0, 0, 0] +sector = 1 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 107 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 134 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 109 + +[[room]] +vnum = 109 +name = "Caravan Way" +description = " This broad avenue is wide enough for several mounted riders to easily\nnavigate, or even a pair of wagons to pass by without incident. Buildings line\nthe road offering services to travelers and visitors of the city. Street\nvendors line the sides, with an occasional shout to draw attention to their\nwares. The route through the city itself is made up of brick, though several\nappear cracked and broken when given a thorough inspection.\n Caravan Way continues to the west and east. To the north appears to be a\nbuilding that deals with garments.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 133 +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 108 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 110 + +[[room]] +vnum = 110 +name = "Caravan Way" +description = " This broad avenue is wide enough for several mounted riders to easily\nnavigate, or even a pair of wagons to pass by without incident. Buildings line\nthe road offering services to travelers and visitors of the city. Street\nvendors line the sides, with an occasional shout to draw attention to their\nwares. The route through the city itself is made up of brick, though several\nappear cracked and broken when given a thorough inspection.\n Caravan Way continues to the east, while to the west the noise of Iron\nSquare can be heard. Buildings surround you on all sides.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 109 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 111 + +[[room]] +vnum = 111 +name = "Iron Square" +description = " This section of the city is filled with large buildings and seems well\nmaintained. A large cobblestone road known as Iron Square circles around the\nbuildings at the center, providing access to the merchant houses that occupy it.\nThe area is quieter than other parts of the city, giving the impression this is\nwhere business is done between traders and the nobility. There is a noticeable\nincrease in soldiers here, which might explain the lack of street vendors.\n Iron Square continues to the north and south, while Caravan Way winds its\nway back into the Tradesmen's District to the east.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 122 +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 110 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 112 + +[[room]] +vnum = 112 +name = "Iron Square" +description = " This section of the city is filled with large buildings and seems well\nmaintained. A large cobblestone road known as Iron Square circles around the\nbuildings at the center, providing access to the merchant houses that occupy it.\nThe area is quieter than other parts of the city, giving the impression this is\nwhere business is done between traders and the nobility. There is a noticeable\nincrease in soldiers here, which might explain the lack of street vendors.\n Iron Square continues to the north and west. To the east is a larger\nbuilding that seems to be both emporium and warehouse, bearing a sign above\nits entrance with a silver quill on a field of red.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 111 +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 128 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 113 + +[[room]] +vnum = 113 +name = "Iron Square" +description = " This section of the city is filled with large buildings and seems well\nmaintained. A large cobblestone road known as Iron Square circles around the\nbuildings at the center, providing access to the merchant houses that occupy it.\nThe area is quieter than other parts of the city, giving the impression this is\nwhere business is done between traders and the nobility. There is a noticeable\nincrease in soldiers here, which might explain the lack of street vendors.\n Iron Square continues to the west and east. To the north is a massive\nemporium with a sign bearing a black diamond on a red-brown field. To the\nsouth is a smaller emporium with a sign bearing a gold circle on a black\nfield.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 123 +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 112 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 160 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 114 + +[[room]] +vnum = 114 +name = "Iron Square" +description = " This section of the city is filled with large buildings and seems well\nmaintained. A large cobblestone road known as Iron Square circles around the\nbuildings at the center, providing access to the merchant houses that occupy it.\nThe area is quieter than other parts of the city, giving the impression this is\nwhere business is done between traders and the nobility. There is a noticeable\nincrease in soldiers here, which might explain the lack of street vendors.\n Iron Square continues to the west and east. To the north is a rather wide\nbuilding with a sign bearing a silver jozhal on a blue field.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 124 +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 113 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 115 + +[[room]] +vnum = 115 +name = "Iron Square" +description = " This section of the city is filled with large buildings and seems well\nmaintained. A large cobblestone road known as Iron Square circles around the\nbuildings at the center, providing access to the merchant houses that occupy it.\nThe area is quieter than other parts of the city, giving the impression this is\nwhere business is done between traders and the nobility. There is a noticeable\nincrease in soldiers here, which might explain the lack of street vendors.\n Iron Square continues to the north and east. To the west is a dusty path\nthe leads toward a ziggurat that rises up above the city.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 116 +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 114 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 161 + +[[room]] +vnum = 116 +name = "Iron Square" +description = " This section of the city is filled with large buildings and seems well\nmaintained. A large cobblestone road known as Iron Square circles around the\nbuildings at the center, providing access to the merchant houses that occupy it.\nThe area is quieter than other parts of the city, giving the impression this is\nwhere business is done between traders and the nobility. There is a noticeable\nincrease in soldiers here, which might explain the lack of street vendors.\n Iron Square continues to the north and south. To the west is a smaller\nbuilding with a sign bearing a green inix on a field of yellow.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 117 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 115 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 129 + +[[room]] +vnum = 117 +name = "Iron Square" +description = " This section of the city is filled with large buildings and seems well\nmaintained. A large cobblestone road known as Iron Square circles around the\nbuildings at the center, providing access to the merchant houses that occupy it.\nThe area is quieter than other parts of the city, giving the impression this is\nwhere business is done between traders and the nobility. There is a noticeable\nincrease in soldiers here, which might explain the lack of street vendors.\n Iron Square continues to the north and south. To the west is a building\nwith a sign bearing a silver spear on an azure field. To the east is a three\nstory building with a sign bearing three white dragonflies on a divided field.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 118 +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 125 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 116 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 130 + +[[room]] +vnum = 118 +name = "Iron Square" +description = " This section of the city is filled with large buildings and seems well\nmaintained. A large cobblestone road known as Iron Square circles around the\nbuildings at the center, providing access to the merchant houses that occupy it.\nThe area is quieter than other parts of the city, giving the impression this is\nwhere business is done between traders and the nobility. There is a noticeable\nincrease in soldiers here, which might explain the lack of street vendors.\n Iron Square continues to the south and east. To the north lies a path that\nappears to be an old, dry riverbed.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 182 +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 119 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 117 + +[[room]] +vnum = 119 +name = "Iron Square" +description = " This section of the city is filled with large buildings and seems well\nmaintained. A large cobblestone road known as Iron Square circles around the\nbuildings at the center, providing access to the merchant houses that occupy it.\nThe area is quieter than other parts of the city, giving the impression this is\nwhere business is done between traders and the nobility. There is a noticeable\nincrease in soldiers here, which might explain the lack of street vendors.\n Iron Square continues to the west and east. Buildings surround you on all\nsides.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 120 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 118 + +[[room]] +vnum = 120 +name = "Iron Square" +description = " This section of the city is filled with large buildings and seems well\nmaintained. A large cobblestone road known as Iron Square circles around the\nbuildings at the center, providing access to the merchant houses that occupy it.\nThe area is quieter than other parts of the city, giving the impression this is\nwhere business is done between traders and the nobility. There is a noticeable\nincrease in soldiers here, which might explain the lack of street vendors.\n Iron Square continues to the west and east. To the north is what seems to\nbe a small fortress, with a sign above its entrance bearing two black\nscimitars over a white field. To the south is a building with a sign above\nbearing a pair of glaring, bestial eyes set on a black banner.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 127 +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 121 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 126 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 119 + +[[room]] +vnum = 121 +name = "Iron Square" +description = " This section of the city is filled with large buildings and seems well\nmaintained. A large cobblestone road known as Iron Square circles around the\nbuildings at the center, providing access to the merchant houses that occupy it.\nThe area is quieter than other parts of the city, giving the impression this is\nwhere business is done between traders and the nobility. There is a noticeable\nincrease in soldiers here, which might explain the lack of street vendors.\n Iron Square continues to the west and south. Buildings surround you on all\nsides.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 198 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 122 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 120 + +[[room]] +vnum = 122 +name = "Iron Square" +description = " This section of the city is filled with large buildings and seems well\nmaintained. A large cobblestone road known as Iron Square circles around the\nbuildings at the center, providing access to the merchant houses that occupy it.\nThe area is quieter than other parts of the city, giving the impression this is\nwhere business is done between traders and the nobility. There is a noticeable\nincrease in soldiers here, which might explain the lack of street vendors.\n Iron Square continues to the north and south. Buildings surround you on all\nsides.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 121 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 111 + +[[room]] +vnum = 123 +name = "House Vordon" +description = " A small path from the entrance into the main area of this emporium has been\ncreated with crates. The path is swept clean and leads to multicolored rugs\nwhich line the remainder of the floor. Upon the walls are racks of steel tools\nand weapons which glint in the light of nearby lamps. A central counter has\nintricate pieces of glasswork, carved stone, and cut gems. Bolts of undyed\ncotton have been placed in a corner of the room on a large wooden platform. \nNear the northern end of his expansive room is a set of double doors that lead\nto another part of the building.\n Outside to the south is Iron Square.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 113 + +[[room]] +vnum = 124 +name = "House Wavir" +description = " A long, expansive countertop separates the goods in this building from the\nrest. Embedded in one side of the counter is a passage system of two panels\nthat lock together, allowing incoming or outgoing shipments. Behind the\ncountertop are orderly stacks of crates with various grains and gems. Smaller\ndisplays of precious metals can be seen, with bits of copper, tin, and even\nsilver in its raw form. A small pyramid has been created with pieces of\nhardwood near the countertop which draws the eye.\n Outside to the south is Iron Square.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 114 + +[[room]] +vnum = 125 +name = "House Shom" +description = " Barrels line a small section of this building, filled with grains such as\nrice or wheat. Another series of barrels with a blue slash painted across them\nhave been filled with clear water. The floor near the northern wall are dusty\nwith obsidian powder, having visible footprints on the floor nearby. Large\ncrates have been stacked, each with a different size of obsidian chunk. Near\nthe northeast corner are a few smaller crates with wooden branches and stacked\nlogs. A counter made of stone has been placed near the entrance.\n Outside to the west is Iron Square.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 117 + +[[room]] +vnum = 126 +name = "House Tsalaxa" +description = " Within this building is a large open area in the middle. Flanking the walls\nare crates, boxes, and chests full of what appears to be hemp. Ranging from\ncompletely raw to premade bolts, they are placed in such a way that it appears\nas if the further down the row of boxes one looks, the more processed the\nmaterial becomes. A series of barrels are placed around a support beam, with\ndifferent grains and cereals placed within. A set of sconces light the room,\nwhich would otherwise appear very dark for a lack of windows.\n Outside to the north is Iron Square.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 120 + +[[room]] +vnum = 127 +name = "House Stel" +description = " The first thing one notices about the inside of this building is the massive\namount of weapons on display. They line the walls, countertops, and some have\nbeen run through a pair of straw-filled dummies propped up by wooden poles. \nNot as visible and clearly an afterthought, there are armor pieces made of iron.\nIn a small corner of the building, ceramic bowls, vases, cups, and plates have\nbeen stacked upon a long wooden table. A pair of wooden beams support the roof\nof this building, which have carved pieces of bone decorating them.\n Outside to the south is Iron Square.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 120 + +[[room]] +vnum = 128 +name = "House M'ke" +description = " Clearly underutilized and lacking in merchandise, this building for the most\npart is empty. The entryway has not been swept in some time and has small piles\nof dust and sand for the first few feet. A wooden counter has been setup more\nthan halfway through the building. Scarce crates and barrels can be seen behind\nthe counter, with dried food, chunks of irregular obsidian, and raw copper\nvisible within. A few small weapons have been placed on the countertop, as if\nto offer the appearance of inventory one could buy.\n Outside to the west is Iron Square.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 112 + +[[room]] +vnum = 129 +name = "House Ianto" +description = " From the outside the building looks rather small, but on the inside it is\neven smaller. The roof seems shorter than one would expect, which feels\nuncomfortably close. Lacking a countertop for dealing with customers, a wooden\ntable has been setup in its place near the entrance. Scattered throughout the\nroom are various chests filled with pieces of iron, while small tables have\nstacks of bolt of fabric. A single circular table has pieces of silk sitting in\na corner.\n Outside to the east is Iron Square.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 116 + +[[room]] +vnum = 130 +name = "House Troika" +description = " Display tables are placed throughout this building, creating a small maze\nmaking it difficult to get in, but also a chore to get out. Upon each table are\npiles of different colored silk fabric. Most are in raw bolt form, however\nthere are some leftover pieces that are of varying sizes. A small counter sits\nagainst the north wall. Behind it are various pieces of glassware, ranging from\ncreative art to more functional goblets.\n Outside to the east is Iron Square.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 117 + +[[room]] +vnum = 131 +name = "Draqoman Station" +description = " This large building has a variety of tables and seats available in it, and\nseems to be a meeting point for those new to the city. The walls are lined with\nsconces to provide light throughout the evening, and barrels of water can be\nseen in the northern side of the room. Conversations can be heard in hushed\ntones, while others may be more boisterous as if between old friends. A crude\nmap of the city can be seen upon one wall, giving the impression this would be a\ngood place to learn more about where certain landmarks are.\n To the south lies Caravan Way, while to the east Wall Road can be seen.\n" +flags = [131080, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 146 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 101 + +[[room]] +vnum = 132 +name = "Grik's Weapons" +description = " The walls of this building have been made out of uneven blocks of sandstone.\nPiled haphazardly on top of each other, they somehow remain stable despite\nlooking precarious and ready to fall anytime. Upon the walls are hooks that\nhave weapons placed on them, as well as trophies from gladiator tournaments. A\ncounter sits in the southwest corner of the room, and a stairway behind it leads\nupward to a trapdoor in the ceiling.\n Outside to the north lies Caravan Way.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 102 + +[[room]] +vnum = 133 +name = "The Shining Sands" +description = " Within this spacious building are several clotheslines and hanging garments.\nSome appear to be waiting to be cleaned while others are awaiting pickup by\ntheir owner. Long work tables have been placed throughout the area which look\nto be used for cleaning clothing. Barrels of white sand have been set near each\nof these tables, with a stone scoop protruding from them. A simple countertop\nhas been setup near the southeastern corner.\n Outside to the east and south is Caravan Way.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 107 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 109 + +[[room]] +vnum = 134 +name = "The Golden Inix Inn" +description = " This large, open room is filled with tables of various shapes and sizes. \nThe smell of food being cooked drifts through the area and seems to be coming\nfrom a curtained doorway. In a corner is an L-shaped counter made of rough-hewn\nstone which has a selection of drinks behind it. Near the eastern wall, a\nstairway heads upward toward an open dormitory.\n" +flags = [8, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 108 +[[room.exit]] +dir = 4 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 200 + +[[room]] +vnum = 135 +name = "Wall Road" +description = " Following along the walls of the city is a small road only large enough for a\npair of mounted riders to traverse side by side. The walls are thick and\nprimarily made of adobe brick, rising at least ten feet. The occasional figure\ncan be seen walking the walls, keeping an eye on the horizon. The buildings\nnear the gate are small and unassuming. Most have no windows and a simple\nwooden door.\n Wall Road continues to the south, while Caravan Gate can be seen to the \nnorth. To the west the smell of a stables wafts out onto the road.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 100 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 137 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 136 + +[[room]] +vnum = 136 +name = "Messenger's Mount" +description = " This two story building eminates a smell from the multitude of beasts within\nthat is all that is needed to knowing that this is a stable. Various pens have\nbeen set up here, holding kanks, erdlus, crodlus, and occasionally other more\nuncommon livestock. Traders and caravans come and go, leaving and retrieving\nmounts. Animal handlers can be seen moving about with food and water, as well\nas shoveling dung to be taken away.\n A small staircase is upon the southern wall and heads upward to the\nsecond floor. To the north lies Caravan Way, and to the east Wall Road can\nbe seen.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 101 +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 135 + +[[room]] +vnum = 137 +name = "Wall Road" +description = " Following along the walls of the city is a small road only large enough for a\npair of mounted riders to traverse side by side. The walls are thick and\nprimarily made of adobe brick, rising at least ten feet. The occasional figure\ncan be seen walking the walls, keeping an eye on the horizon. The buildings\nnear the gate are small and unassuming. Most have no windows and a simple\nwooden door.\n Wall Road continues to the north and south. Buildings rise up to the west,\nwhile the city walls are to the east.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 135 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 138 + +[[room]] +vnum = 138 +name = "Wall Road" +description = " Following along the walls of the city is a small road only large enough for a\npair of mounted riders to traverse side by side. The walls are thick and\nprimarily made of adobe brick, rising at least ten feet. The occasional figure\ncan be seen walking the walls, keeping an eye on the horizon. The buildings\nnear the gate are small and unassuming. Most have no windows and a simple\nwooden door.\n Wall Road continues to the north and south. Buildings rise up to the west,\nwhile the city walls are to the east.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 137 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 139 + +[[room]] +vnum = 139 +name = "Wall Road" +description = " Following along the walls of the city is a small road only large enough for a\npair of mounted riders to traverse side by side. The walls are thick and\nprimarily made of adobe brick, rising at least ten feet. The occasional figure\ncan be seen walking the walls, keeping an eye on the horizon. The buildings\nnear the gate are small and unassuming. Most have no windows and a simple\nwooden door.\n Wall Road continues to the north and south. To the west, an old dusty\nstreambed runs between two buildings.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 138 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 140 + +[[room]] +vnum = 140 +name = "Near a Dusty Streambed" +description = " This pathway appears to have once been a streambed where water flowed freely.\nOver the years the water must have dried up or has been diverted elsewhere. \nDespite this, the path seems to be used as a walkway between buildings now. \nThe ground is exposed dirt which is kicked up in the air as passersby make their\nway through. Small rocks and gravel can be seen every few feet in an attempt to\nkeep the semblance of an actual road.\n The pathway continues to the west, while to the east Wall Road can be\nseen.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 139 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 141 + +[[room]] +vnum = 141 +name = "Near a Dusty Streambed" +description = " This pathway appears to have once been a streambed where water flowed freely.\nOver the years the water must have dried up or has been diverted elsewhere. \nDespite this, the path seems to be used as a walkway between buildings now. \nThe ground is exposed dirt which is kicked up in the air as passersby make their\nway through. Small rocks and gravel can be seen every few feet in an attempt to\nkeep the semblance of an actual road.\n The pathway continues to the west and east. Buildings flank either side,\nrising upward.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 140 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 142 + +[[room]] +vnum = 142 +name = "Near a Dusty Streambed" +description = " This pathway appears to have once been a streambed where water flowed freely.\nOver the years the water must have dried up or has been diverted elsewhere. \nDespite this, the path seems to be used as a walkway between buildings now. \nThe ground is exposed dirt which is kicked up in the air as passersby make their\nway through. Small rocks and gravel can be seen every few feet in an attempt to\nkeep the semblance of an actual road.\n The pathway continues to the east, while a small side street opens up to\nthe north.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 143 +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 141 + +[[room]] +vnum = 143 +name = "Side Street" +description = " This small street is hardly big enough for a single mounted rider to pass\nthrough. The ground is made up of a mix of broken brick and rough stones,\ngiving the impression that it is not considered as main route through the city.\nSmall buildings can be seen on either side without any windows, and most have a\nsingle small wooden door.\n The street continues to the north, while to the south a dry streambed can\nbe seen.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 144 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 142 + +[[room]] +vnum = 144 +name = "Side Street" +description = " This small street is hardly big enough for a single mounted rider to pass\nthrough. The ground is made up of a mix of broken brick and rough stones,\ngiving the impression that it is not considered as main route through the city.\nSmall buildings can be seen on either side without any windows, and most have a\nsingle small wooden door.\n The street continues to the north and south. Buildings flank either side,\nrising upward.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 145 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 143 +[[room.exit]] +dir = 3 +description = "" +keyword = "door" +exit_info = 1 +key = 0 +to_room = 159 + +[[room]] +vnum = 145 +name = "Side Street" +description = " This small street is hardly big enough for a single mounted rider to pass\nthrough. The ground is made up of a mix of broken brick and rough stones,\ngiving the impression that it is not considered as main route through the city.\nSmall buildings can be seen on either side without any windows, and most have a\nsingle small wooden door.\n The street continues to the south, while to the north Caravan Way can\nbe seen.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 103 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 144 + +[[room]] +vnum = 146 +name = "Wall Road" +description = " Following along the walls of the city is a small road only large enough for a\npair of mounted riders to traverse side by side. The walls are thick and\nprimarily made of adobe brick, rising at least ten feet. The occasional figure\ncan be seen walking the walls, keeping an eye on the horizon. The buildings\nnear the gate are small and unassuming. Most have no windows and a simple\nwooden door.\n Wall Road continues to the north, while the Caravan Gate can be seen to\nthe south. A placard with a stylized eyeball, ears, and lips hangs as a\nstandard above a building to the west.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 147 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 100 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 131 + +[[room]] +vnum = 147 +name = "Wall Road" +description = " Following along the walls of the city is a small road only large enough for a\npair of mounted riders to traverse side by side. The walls are thick and\nprimarily made of adobe brick, rising at least ten feet. The occasional figure\ncan be seen walking the walls, keeping an eye on the horizon. The buildings\nnear the gate are small and unassuming. Most have no windows and a simple\nwooden door.\n Wall Road continues to the north and south. Buildings rise up to the west,\nwhile the city walls are to the east.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 148 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 146 + +[[room]] +vnum = 148 +name = "Wall Road" +description = " Following along the walls of the city is a small road only large enough for a\npair of mounted riders to traverse side by side. The walls are thick and\nprimarily made of adobe brick, rising at least ten feet. The occasional figure\ncan be seen walking the walls, keeping an eye on the horizon. The buildings\nnear the gate are small and unassuming. Most have no windows and a simple\nwooden door.\n Wall Road continues to the north and south. Buildings rise up to the west,\nwhile the city walls are to the east.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 149 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 147 + +[[room]] +vnum = 149 +name = "Wall Road" +description = " Following along the walls of the city is a small road only large enough for a\npair of mounted riders to traverse side by side. The walls are thick and\nprimarily made of adobe brick, rising at least ten feet. The occasional figure\ncan be seen walking the walls, keeping an eye on the horizon. The buildings\nnear the gate are small and unassuming. Most have no windows and a simple\nwooden door.\n Wall Road continues to the north and south. To the west, a side street opens\nup between two buildings.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 148 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 150 + +[[room]] +vnum = 150 +name = "Side Street" +description = " This small street is hardly big enough for a single mounted rider to pass\nthrough. The ground is made up of a mix of broken brick and rough stones,\ngiving the impression that it is not considered as main route through the city.\nSmall buildings can be seen on either side without any windows, and most have a\nsingle small wooden door.\n The street continues to the west, while Wall Road can be seen to the east.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 149 +[[room.exit]] +dir = 2 +description = "" +keyword = "door" +exit_info = 1 +key = 0 +to_room = 157 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 151 + +[[room]] +vnum = 151 +name = "Side Street" +description = " This small street is hardly big enough for a single mounted rider to pass\nthrough. The ground is made up of a mix of broken brick and rough stones,\ngiving the impression that it is not considered as main route through the city.\nSmall buildings can be seen on either side without any windows, and most have a\nsingle small wooden door.\n The street continues to the west and east. Buildings flank either side,\nrising upward.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 150 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 152 + +[[room]] +vnum = 152 +name = "Side Street" +description = " This small street is hardly big enough for a single mounted rider to pass\nthrough. The ground is made up of a mix of broken brick and rough stones,\ngiving the impression that it is not considered as main route through the city.\nSmall buildings can be seen on either side without any windows, and most have a\nsingle small wooden door.\n The street continues to the west and east. Buildings flank either side,\nrising upward.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 151 +[[room.exit]] +dir = 2 +description = "" +keyword = "door" +exit_info = 9 +key = 0 +to_room = 156 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 153 + +[[room]] +vnum = 153 +name = "Side Street" +description = " This small street is hardly big enough for a single mounted rider to pass\nthrough. The ground is made up of a mix of broken brick and rough stones,\ngiving the impression that it is not considered as main route through the city.\nSmall buildings can be seen on either side without any windows, and most have a\nsingle small wooden door.\n The street continues to the south and east. Buildings flank either side,\nrising upward.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 152 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 154 + +[[room]] +vnum = 154 +name = "Side Street" +description = " This small street is hardly big enough for a single mounted rider to pass\nthrough. The ground is made up of a mix of broken brick and rough stones,\ngiving the impression that it is not considered as main route through the city.\nSmall buildings can be seen on either side without any windows, and most have a\nsingle small wooden door.\n The street continues to the north and south. Buildings flank either side,\nrising upward.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 153 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 155 +[[room.exit]] +dir = 3 +description = "" +keyword = "door" +exit_info = 9 +key = 0 +to_room = 158 + +[[room]] +vnum = 155 +name = "Side Street" +description = " This small street is hardly big enough for a single mounted rider to pass\nthrough. The ground is made up of a mix of broken brick and rough stones,\ngiving the impression that it is not considered as main route through the city.\nSmall buildings can be seen on either side without any windows, and most have a\nsingle small wooden door.\n The street continues to the north, while to the south Caravan Way can\nbe seen.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 154 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 105 + +[[room]] +vnum = 156 +name = "A Small Domicile" +description = " Somewhat upscale compared to other buildings around it, this domicile feels\nmore like a home. It has a hearth in the south side of the room to allow the\noccupants to cook their own food. The walls are made of adobe brick set in\nalternating patterns, with the grout having been smoothed over by the\nbricklayers. Pieces of flat wood have been placed along the floor, giving the\ndwelling a reprieve from the dusty cobblestone outside.\n A simple wooden door has been placed in the northern wall, leading\nback outside.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "door" +exit_info = 9 +key = 0 +to_room = 152 + +[[room]] +vnum = 157 +name = "A Large Domicile" +description = " Somewhat upscale compared to other buildings around it, this domicile feels\nmore like a home. It has a hearth in the south side of the room to allow the\noccupants to cook their own food. The walls are made of adobe brick set in\nalternating patterns, with the grout having been smoothed over by the\nbricklayers. Pieces of flat wood have been placed along the floor, giving the\ndwelling a reprieve from the dusty cobblestone outside.\n A simple wooden door has been placed in the northern wall, leading\nback outside.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "door" +exit_info = 1 +key = 0 +to_room = 150 + +[[room]] +vnum = 158 +name = "A Small Hovel" +description = " Small and dilapidated, this hovel is only big enough for a few pieces of\nfurniture. The walls are adobe brick that are showing their age with cracks\nthroughout. In one corner the ceiling appears to be falling in on itself,\nletting light through during the day.\n A simple wooden door has been placed in the eastern wall, leading back\noutside.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "door" +exit_info = 9 +key = 0 +to_room = 154 + +[[room]] +vnum = 159 +name = "A Small Hovel" +description = " Small and dilapidated, this hovel is only big enough for a few pieces of\nfurniture. The walls are adobe brick that are showing their age with cracks\nthroughout. In one corner the ceiling appears to be falling in on itself,\nletting light through during the day.\n A simple wooden door has been placed in the eastern wall, leading back\noutside.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "door" +exit_info = 1 +key = 0 +to_room = 144 + +[[room]] +vnum = 160 +name = "House Inika" +description = " Inside this building are several barrels filled with all sorts of nuts,\nspices, feathers, and other small items. The floor is swept clean, keeping dust\nand debris outside. There is a single counter in the middle of the room that\nforms a square shape, with a wooden pass-through panel on one side to allow the\nmerchants to step behind it. Within the counter are a few other boxes and\ncrates of goods for trade.\n Outside to the north is Iron Square.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 113 + +[[room]] +vnum = 161 +name = "A Dusty Path" +description = " This pathway is more of a road that has fallen into disrepair. Through years\nof use the cobblestone beneath has been cracked and broken. Sections of the\nroad appear to have deep divets, making it dangerous to not watch where you are\nstepping. Large buildings made of adobe brick rise up on either side, though\nfew have entrances that can be entered.\n To the west, an immense ziggurat can be seen that rises up above the city.\nThe sound of Iron Square can be heard to the east.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 115 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 162 + +[[room]] +vnum = 162 +name = "Before the Ziggurat" +description = " This pathway circles around an immense ziggurat that towers over the city. \nThe pyramid has multiple levels, each made of glazed brick of different color.\nThe base is made of violet, the second tier indigo, third colored azure, the\nfourth a dark green, fifth yellow, sixth of a fiery orange, and finally at the\ntop a deep scarlet. From this point an enormous staircase can be seen which\nleads up to the pinnacle.\n The path circling the ziggurat continues to the north and south. To the\neast a dusty pathway can be seen that leads into the rest of the city.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 163 +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 161 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 177 + +[[room]] +vnum = 163 +name = "Before the Ziggurat" +description = " This pathway circles around an immense ziggurat that towers over the city. \nThe pyramid has multiple levels, each made of glazed brick of different color.\nThe base is made of violet, the second tier indigo, third colored azure, the\nfourth a dark green, fifth yellow, sixth of a fiery orange, and finally at the\ntop a deep scarlet. From this point an enormous staircase can be seen which\nleads up to the pinnacle.\n The path circling the ziggurat continues to the north and south. Buildings\nrise up to the east.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 164 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 162 + +[[room]] +vnum = 164 +name = "Before the Ziggurat" +description = " This pathway circles around an immense ziggurat that towers over the city. \nThe pyramid has multiple levels, each made of glazed brick of different color.\nThe base is made of violet, the second tier indigo, third colored azure, the\nfourth a dark green, fifth yellow, sixth of a fiery orange, and finally at the\ntop a deep scarlet. From this point an enormous staircase can be seen which\nleads up to the pinnacle.\n The path circling the ziggurat continues to the north and south. Buildings\nrise up to the east.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 165 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 163 + +[[room]] +vnum = 165 +name = "Before the Ziggurat" +description = " This pathway circles around an immense ziggurat that towers over the city. \nThe pyramid has multiple levels, each made of glazed brick of different color.\nThe base is made of violet, the second tier indigo, third colored azure, the\nfourth a dark green, fifth yellow, sixth of a fiery orange, and finally at the\ntop a deep scarlet. From this point an enormous staircase can be seen which\nleads up to the pinnacle.\n The path circling the ziggurat continues to the south and west. To the\nnorth a dusty pathway can be seen that leads into the rest of the city.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 178 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 164 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 166 + +[[room]] +vnum = 166 +name = "Before the Ziggurat" +description = " This pathway circles around an immense ziggurat that towers over the city. \nThe pyramid has multiple levels, each made of glazed brick of different color.\nThe base is made of violet, the second tier indigo, third colored azure, the\nfourth a dark green, fifth yellow, sixth of a fiery orange, and finally at the\ntop a deep scarlet. The ziggurat seems to dwarf everything else in sight except\nfor an imposing golden tower off to the west.\n The path circling the ziggurat continues to the west and east. Buildings\nrise up to the north.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 165 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 167 + +[[room]] +vnum = 167 +name = "Before the Ziggurat" +description = " This pathway circles around an immense ziggurat that towers over the city. \nThe pyramid has multiple levels, each made of glazed brick of different color.\nThe base is made of violet, the second tier indigo, third colored azure, the\nfourth a dark green, fifth yellow, sixth of a fiery orange, and finally at the\ntop a deep scarlet. The ziggurat seems to dwarf everything else in sight except\nfor an imposing golden tower off to the west.\n The path circling the ziggurat continues to the west and east. Buildings\nrise up to the north.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 166 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 168 + +[[room]] +vnum = 168 +name = "Before the Ziggurat" +description = " This pathway circles around an immense ziggurat that towers over the city. \nThe pyramid has multiple levels, each made of glazed brick of different color.\nThe base is made of violet, the second tier indigo, third colored azure, the\nfourth a dark green, fifth yellow, sixth of a fiery orange, and finally at the\ntop a deep scarlet. The ziggurat seems to dwarf everything else in sight except\nfor an imposing golden tower off to the west.\n The path circling the ziggurat continues to the west and east. Buildings\nrise up to the north.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 167 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 169 + +[[room]] +vnum = 169 +name = "Before the Ziggurat" +description = " This pathway circles around an immense ziggurat that towers over the city. \nThe pyramid has multiple levels, each made of glazed brick of different color.\nThe base is made of violet, the second tier indigo, third colored azure, the\nfourth a dark green, fifth yellow, sixth of a fiery orange, and finally at the\ntop a deep scarlet. The ziggurat seems to dwarf everything else in sight except\nfor an imposing golden tower off to the west.\n The path circling the ziggurat continues to the south and east. Buildings\nrise up to the north and west.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 168 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 170 + +[[room]] +vnum = 170 +name = "Before the Ziggurat" +description = " This pathway circles around an immense ziggurat that towers over the city. \nThe pyramid has multiple levels, each made of glazed brick of different color.\nThe base is made of violet, the second tier indigo, third colored azure, the\nfourth a dark green, fifth yellow, sixth of a fiery orange, and finally at the\ntop a deep scarlet. The ziggurat seems to dwarf everything else in sight except\nfor an imposing golden tower off to the west.\n The path circling the ziggurat continues to the north and south. Buildings\nrise up to the west.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 169 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 171 + +[[room]] +vnum = 171 +name = "Before the Ziggurat" +description = " This pathway circles around an immense ziggurat that towers over the city. \nThe pyramid has multiple levels, each made of glazed brick of different color.\nThe base is made of violet, the second tier indigo, third colored azure, the\nfourth a dark green, fifth yellow, sixth of a fiery orange, and finally at the\ntop a deep scarlet. The ziggurat seems to dwarf everything else in sight except\nfor an imposing golden tower off to the west.\n The path circling the ziggurat continues to the north and south. Buildings\nrise up to the west.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 170 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 172 + +[[room]] +vnum = 172 +name = "Before the Ziggurat" +description = " This pathway circles around an immense ziggurat that towers over the city. \nThe pyramid has multiple levels, each made of glazed brick of different color.\nThe base is made of violet, the second tier indigo, third colored azure, the\nfourth a dark green, fifth yellow, sixth of a fiery orange, and finally at the\ntop a deep scarlet. The ziggurat seems to dwarf everything else in sight except\nfor an imposing golden tower off to the west.\n The path circling the ziggurat continues to the north and south. Buildings\nrise up to the west.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 171 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 173 + +[[room]] +vnum = 173 +name = "Before the Ziggurat" +description = " This pathway circles around an immense ziggurat that towers over the city. \nThe pyramid has multiple levels, each made of glazed brick of different color.\nThe base is made of violet, the second tier indigo, third colored azure, the\nfourth a dark green, fifth yellow, sixth of a fiery orange, and finally at the\ntop a deep scarlet. The ziggurat seems to dwarf everything else in sight except\nfor an imposing golden tower off to the west.\n The path circling the ziggurat continues to the north and east. Buildings\nrise up to the south and west.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 172 +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 174 + +[[room]] +vnum = 174 +name = "Before the Ziggurat" +description = " This pathway circles around an immense ziggurat that towers over the city. \nThe pyramid has multiple levels, each made of glazed brick of different color.\nThe base is made of violet, the second tier indigo, third colored azure, the\nfourth a dark green, fifth yellow, sixth of a fiery orange, and finally at the\ntop a deep scarlet. The ziggurat seems to dwarf everything else in sight except\nfor an imposing golden tower off to the west.\n The path circling the ziggurat continues to the west and east. Buildings\nrise up to the south.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 175 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 173 + +[[room]] +vnum = 175 +name = "Before the Ziggurat" +description = " This pathway circles around an immense ziggurat that towers over the city. \nThe pyramid has multiple levels, each made of glazed brick of different color.\nThe base is made of violet, the second tier indigo, third colored azure, the\nfourth a dark green, fifth yellow, sixth of a fiery orange, and finally at the\ntop a deep scarlet. The ziggurat seems to dwarf everything else in sight except\nfor an imposing golden tower off to the west.\n The path circling the ziggurat continues to the west and east. Buildings\nrise up to the south.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 176 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 174 + +[[room]] +vnum = 176 +name = "Before the Ziggurat" +description = " This pathway circles around an immense ziggurat that towers over the city. \nThe pyramid has multiple levels, each made of glazed brick of different color.\nThe base is made of violet, the second tier indigo, third colored azure, the\nfourth a dark green, fifth yellow, sixth of a fiery orange, and finally at the\ntop a deep scarlet. The ziggurat seems to dwarf everything else in sight except\nfor an imposing golden tower off to the west.\n The path circling the ziggurat continues to the west and east. Buildings\nrise up to the south.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 177 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 175 + +[[room]] +vnum = 177 +name = "Before the Ziggurat" +description = " This pathway circles around an immense ziggurat that towers over the city. \nThe pyramid has multiple levels, each made of glazed brick of different color.\nThe base is made of violet, the second tier indigo, third colored azure, the\nfourth a dark green, fifth yellow, sixth of a fiery orange, and finally at the\ntop a deep scarlet. From this point an enormous staircase can be seen which\nleads up to the pinnacle.\n The path circling the ziggurat continues to the north and south. Buildings\nrise up to the east.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 162 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 176 + +[[room]] +vnum = 178 +name = "A Dusty Path" +description = " This pathway is more of a road that has fallen into disrepair. Through years\nof use the cobblestone beneath has been cracked and broken. Sections of the\nroad appear to have deep divets, making it dangerous to not watch where you are\nstepping. Large buildings made of adobe brick rise up on either side, though\nfew have entrances that can be entered.\n To the south, an immense ziggurat can be seen that rises up above the city.\nThe dusty pathway continues to the north.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 179 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 165 + +[[room]] +vnum = 179 +name = "A Dusty Path" +description = " This pathway is more of a road that has fallen into disrepair. Through years\nof use the cobblestone beneath has been cracked and broken. Sections of the\nroad appear to have deep divets, making it dangerous to not watch where you are\nstepping. Large buildings made of adobe brick rise up on either side, though\nfew have entrances that can be entered.\n The dusty pathway continues to the west and south. A small side street can\nbe seen to the east.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 180 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 178 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 184 + +[[room]] +vnum = 180 +name = "A Side Street" +description = " This small street is hardly big enough for a single mounted rider to pass\nthrough. The ground is made up of a mix of broken brick and rough stones,\ngiving the impression that it is not considered as main route through the city.\nSmall buildings can be seen on either side without any windows, and most have a\nsingle small wooden door.\n The street gives way to a dusty old riverbed to the east, and to the west\na wider path can be seen.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 181 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 179 + +[[room]] +vnum = 181 +name = "A Dry Riverbed" +description = " This wide, dust choked pathway looks as if it may have been a riverbed at\nsome point in the distant past. The water seems to have carved its way through\nthe land, leaving a natural route through the city and between buildings. \nSmall plants attempt to grow near the buildings nearby, clinging to the shade.\nSeveral extremely smooth stones can be seen, having been worn down from years of\nwater flowing over them.\n The dusty riverbed continues to the south, while to the north a market can\nbe seen. A small side street intersects with this pathway to the west.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 183 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 182 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 180 + +[[room]] +vnum = 182 +name = "A Dry Riverbed" +description = " This wide, dust choked pathway looks as if it may have been a riverbed at\nsome point in the distant past. The water seems to have carved its way through\nthe land, leaving a natural route through the city and between buildings. \nSmall plants attempt to grow near the buildings nearby, clinging to the shade.\nSeveral extremely smooth stones can be seen, having been worn down from years of\nwater flowing over them.\n The dusty riverbed continues to the north, while to the south the sounds\nof Iron Square can be heard.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 181 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 118 + +[[room]] +vnum = 183 +name = "Elven Market" +description = " Numerous stalls form small collectives in this open air market. Sets of\nfour, six, or eight seems to have been setup near each other, creating walkways\nthroughout. Most are made of simple wooden poles and sandcloth, with either a\ncounter and seat behind it for the proprietor, or simply an open stall to walk\ninto. At all hours of the day conversations can be heard and bartering taking\nplace.\n To the south is an old dry riverbed, which acts as a pathway through the\ncity. To the east is the Elven Bridge, which stretches over to another part\nof the city.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 194 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 181 + +[[room]] +vnum = 184 +name = "A Dusty Path" +description = " This pathway is more of a road that has fallen into disrepair. Through years\nof use the cobblestone beneath has been cracked and broken. Sections of the\nroad appear to have deep divets, making it dangerous to not watch where you are\nstepping. Large buildings made of adobe brick rise up on either side, though\nfew have entrances that can be entered.\n The dusty pathway continues to the east. To the west, a small square of\nshops can be seen.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 179 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 185 + +[[room]] +vnum = 185 +name = "Shadow Square" +description = " This plaza within the city has many roads that join it, making it a clear\nconvergence point for people. The cobblestone seems well taken care of, with\nnewer and lighter bricks replacing those that have cracked. Benches have been\nplaced in shaded areas, offering a reprieve from the harsh sun. A few small\nstands have been placed here, offering food to those that pass by.\n Shadow Square continues to the north and west. To the east, a dusty\npathway leads on to the rest of the city.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 186 +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 184 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 188 + +[[room]] +vnum = 186 +name = "Shadow Square" +description = " This plaza within the city has many roads that join it, making it a clear\nconvergence point for people. The cobblestone seems well taken care of, with\nnewer and lighter bricks replacing those that have cracked. Benches have been\nplaced in shaded areas, offering a reprieve from the harsh sun. A few small\nstands have been placed here, offering food to those that pass by.\n Shadow Square continues to the south and west. To the north is a building\nwith a curtain covering the entrance and oil lamps illuminating the inside.\nTo the east is a two story building that appears to be some sort of shop.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 192 +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 193 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 185 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 187 + +[[room]] +vnum = 187 +name = "Shadow Square" +description = " This plaza within the city has many roads that join it, making it a clear\nconvergence point for people. The cobblestone seems well taken care of, with\nnewer and lighter bricks replacing those that have cracked. Benches have been\nplaced in shaded areas, offering a reprieve from the harsh sun. A few small\nstands have been placed here, offering food to those that pass by.\n Shadow square continues to the south and east. To the north is a well lit\nbuilding with sweet scents eminating from within. To the west is a small\nbuilding with an obvious bar immediately inside.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 191 +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 186 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 188 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 190 + +[[room]] +vnum = 188 +name = "Shadow Square" +description = " This plaza within the city has many roads that join it, making it a clear\nconvergence point for people. The cobblestone seems well taken care of, with\nnewer and lighter bricks replacing those that have cracked. Benches have been\nplaced in shaded areas, offering a reprieve from the harsh sun. A few small\nstands have been placed here, offering food to those that pass by.\n Shadow Square continues to the north and east. To the west, a dimly lit\nestablishment has a propped open door.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 187 +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 185 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 189 + +[[room]] +vnum = 189 +name = "The Rat's Nest" +description = " This building is not well lit, which takes your eyes a moment to adjust to.\nBehind the bar is a small oil lamp that provides light for those nearby, but\nbesides that the shadows prevail here. A wooden beam supports a ceiling that\nappears to be sagging or falling in on itself, with cracks extending toward the\nwalls. The smell of something that has gone bad lingers in the air, though it\nis difficult to identify its source.\n Outside to the east is Shadow Square.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 188 + +[[room]] +vnum = 190 +name = "The Weeping Wyvern" +description = " Hanging on the walls are the heads of different animals, bloodied weapons,\nshields, and harnesses worn by the city's gladiators. A stuffed jhakar head\ndescends from the ceiling in the middle of the room, its jaws open but eyes\ndistant. There isn't much seating in this building except for what is near the\nbar.\n Outside to the east is Shadow Square.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 187 + +[[room]] +vnum = 191 +name = "The Drunken Giant" +description = " The smell of cheap beer and roasting meats assaults your senses as you enter\nthis room. Parts of the floor are sticky, possibly from vomit or perhaps just\nspilled drinks. Numerous tables have been placed throughout the floor, with\nsome of them missing chairs. A long wooden bar runs the length of the northern\nwall with fixed stools mounted to the ground before it. A hearth takes up a\nportion of the western wall where food is being cooked.\n Outside to the south is Shadow Square.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 187 + +[[room]] +vnum = 192 +name = "The Midnight Sands" +description = " This dimly lit establishment has oil lamps placed strategically throughout,\ngranting a small amount of illumination to those that sit nearby. The scent of\nincense drifts about the room from various burners. A counter with no seating\nhas been set near the northwestern corner. Behind the counter are several\nbottles and delicate glasses.\n Outside to the south is Shadow Square.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 186 + +[[room]] +vnum = 193 +name = "The Red Kank" +description = " This building has a long counter with rows of bottles behind it. The entire\neast wall has had wooden pieces installed in an X pattern, which allows for the\nbottles to be placed within up to the ceiling. The floor is made of smoothed\npieces of granite and is nearly free of sand despite it flowing in from the\noutside square when a breeze passes through.\n Outside to the west is Shadow Square. A staircase climbs the northern\nside of the building and leads through a trapdoor to a room above.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 186 +[[room.exit]] +dir = 4 +description = "A trapdoor in the ceiling can be passed through by climbing a set of stairs.\n" +keyword = "trapdoor" +exit_info = 1 +key = 0 +to_room = 201 + +[[room]] +vnum = 194 +name = "Elven Bridge" +description = " This bridge looks absolutely ancient. Made of solid stone quarried long ago,\nthe pieces fit together smoothly and leave little gap between them. While the\nmortar looks like it is aging in places, it still seems sturdy. Below the\nbridge is a dusty, serpentine streambed that once had water free flowing in it.\nYears ago the water must have dried up was diverted elsewhere, leaving a place\nunder the bridge for people to gather. \n The bridge leads west to east across the old streambed. To the east is a\nsmall side street that leads between buildings, while to the west a market\ncan be seen.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 195 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 183 + +[[room]] +vnum = 195 +name = "Side Street" +description = " This small street is hardly big enough for a single mounted rider to pass\nthrough. The ground is made up of a mix of broken brick and rough stones,\ngiving the impression that it is not considered as main route through the city.\nSmall buildings can be seen on either side without any windows, and most have a\nsingle small wooden door.\n The street continues to the west and east. Buildings flank either side,\nrising upward.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 196 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 194 + +[[room]] +vnum = 196 +name = "Side Street" +description = " This small street is hardly big enough for a single mounted rider to pass\nthrough. The ground is made up of a mix of broken brick and rough stones,\ngiving the impression that it is not considered as main route through the city.\nSmall buildings can be seen on either side without any windows, and most have a\nsingle small wooden door.\n The street continues to the west and south. To the east is a building that\nhas the scent of cooking meat wafting from it.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 1 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 199 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 197 +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 195 + +[[room]] +vnum = 197 +name = "Side Street" +description = " This small street is hardly big enough for a single mounted rider to pass\nthrough. The ground is made up of a mix of broken brick and rough stones,\ngiving the impression that it is not considered as main route through the city.\nSmall buildings can be seen on either side without any windows, and most have a\nsingle small wooden door.\n The street continues to the north and south. Buildings flank either side,\nrising upward.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 196 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 198 + +[[room]] +vnum = 198 +name = "Side Street" +description = " This small street is hardly big enough for a single mounted rider to pass\nthrough. The ground is made up of a mix of broken brick and rough stones,\ngiving the impression that it is not considered as main route through the city.\nSmall buildings can be seen on either side without any windows, and most have a\nsingle small wooden door.\n The street continues to the north, while to the south Iron Square can\nbe seen.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 0 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 197 +[[room.exit]] +dir = 2 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 121 + +[[room]] +vnum = 199 +name = "The Sandstone Inn" +description = " This modest building has two entrances, one on the west end and one on the\neast. The occasional breeze passes through, cooling the building slightly. On\nthe south end is a hearth that is used for cooking food, and nearby is a small\ncounter with drinks behind it. The northern end of the room has a couple small\ntables. The overall feel of the place is a cozy place to rest after a long day.\n Outside to the west is a small side street, while to the east is a\ncourtyard that smells vaguely of a stable.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 3 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 196 + +[[room]] +vnum = 200 +name = "A Cramped Dormitory" +description = " While this room is rather large, the amount of cots crammed into it makes it\nfeel quite cramped. Between the cots are small pathways that one can walk,\nthough backpacks and other items spill out from below their respective cots to\noccasionally block the way. The walls have small hooks on them and some appear\nto have cloaks attached. An opening in the wall lets light in from the north\nand allows for fresh air to circulate. A pair of sconces are on either side of\nthe room to provide light during the evening hours. On the south side of the\nroom, a staircase descends to the room below.\n" +flags = [131080, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 5 +description = "" +keyword = "" +exit_info = 0 +key = 0 +to_room = 134 + +[[room]] +vnum = 201 +name = "The Red Kank Terrace" +description = " This upper floor is partially covered, with a terrace that looks out on\nShadow Square below. Artistically carved pieces of stone have been placed at\nthe edge of the terrace, providing a small amount of protection from falling as\nwell as preventing those below from seeing what transpires here. Canopies have\nbeen installed above the exposed terrace, shading it from the sun.\n A trapdoor on the northern end of the terrace leads downward to the shop\nbelow.\n" +flags = [0, 0, 0, 0] +sector = 0 + +[[room.exit]] +dir = 5 +description = "A trapdoor in the floor can be passed through by climbing a set of stairs.\n" +keyword = "trapdoor" +exit_info = 1 +key = 0 +to_room = 193 + diff --git a/lib/world/wld/index.mini.toml b/lib/world/wld/index.mini.toml new file mode 100644 index 0000000..042ad07 --- /dev/null +++ b/lib/world/wld/index.mini.toml @@ -0,0 +1,2 @@ +files = [ +] diff --git a/lib/world/wld/index.toml b/lib/world/wld/index.toml new file mode 100644 index 0000000..e9d4550 --- /dev/null +++ b/lib/world/wld/index.toml @@ -0,0 +1,4 @@ +files = [ + "0.toml", + "1.toml" +] diff --git a/lib/world/zon/0.toml b/lib/world/zon/0.toml new file mode 100644 index 0000000..c9c2d1f --- /dev/null +++ b/lib/world/zon/0.toml @@ -0,0 +1,12 @@ +[[zone]] +vnum = 0 +builders = "Immortal Zone" +name = "Kinther" +bot = 1 +top = 99 +lifespan = 30 +reset_mode = 2 +flags = [0, 0, 0, 0] +min_level = -1 +max_level = -1 + diff --git a/lib/world/zon/1.toml b/lib/world/zon/1.toml new file mode 100644 index 0000000..a038812 --- /dev/null +++ b/lib/world/zon/1.toml @@ -0,0 +1,12 @@ +[[zone]] +vnum = 1 +builders = "City of Caleran" +name = "Kinther" +bot = 100 +top = 999 +lifespan = 30 +reset_mode = 2 +flags = [0, 0, 0, 0] +min_level = -1 +max_level = -1 + diff --git a/lib/world/zon/index.mini.toml b/lib/world/zon/index.mini.toml new file mode 100644 index 0000000..042ad07 --- /dev/null +++ b/lib/world/zon/index.mini.toml @@ -0,0 +1,2 @@ +files = [ +] diff --git a/lib/world/zon/index.toml b/lib/world/zon/index.toml new file mode 100644 index 0000000..e9d4550 --- /dev/null +++ b/lib/world/zon/index.toml @@ -0,0 +1,4 @@ +files = [ + "0.toml", + "1.toml" +] diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 51832b4..2869edc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,6 +16,8 @@ file(GLOB CIRCLE_SOURCES ) add_executable(circle ${CIRCLE_SOURCES}) +target_sources(circle PRIVATE ${CMAKE_SOURCE_DIR}/third_party/tomlc99/toml.c) +target_include_directories(circle PRIVATE ${CMAKE_SOURCE_DIR}/third_party/tomlc99) if(MSVC) target_link_libraries(circle wsock32.lib) @@ -23,4 +25,4 @@ if(MSVC) set_target_properties(circle PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/.." ) -endif() \ No newline at end of file +endif() diff --git a/src/Makefile b/src/Makefile index 5fc08a5..bbf440b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -17,11 +17,11 @@ PROFILE = BINDIR = ../bin -CFLAGS = -g -O2 $(MYFLAGS) $(PROFILE) +CFLAGS = -g -O2 $(MYFLAGS) $(PROFILE) -I../third_party/tomlc99 LIBS = -lcrypt -SRCFILES := $(shell ls *.c | sort) +SRCFILES := $(shell ls *.c | sort) ../third_party/tomlc99/toml.c OBJFILES := $(patsubst %.c,%.o,$(SRCFILES)) default: all @@ -47,13 +47,14 @@ $%.o: %.c clean: rm -f *.o depend + rm -f ../third_party/tomlc99/toml.o rm -f ./tests/*.o depend # Dependencies for the object files (automagically generated with # gcc -MM) depend: - $(CC) -MM *.c > depend + $(CC) $(CFLAGS) -MM *.c > depend -include depend diff --git a/src/Makefile.in b/src/Makefile.in index fceea2a..e0517be 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -16,11 +16,11 @@ PROFILE = BINDIR = ../bin -CFLAGS = @CFLAGS@ $(MYFLAGS) $(PROFILE) +CFLAGS = @CFLAGS@ $(MYFLAGS) $(PROFILE) -I../third_party/tomlc99 LIBS = @LIBS@ @CRYPTLIB@ @NETLIB@ -SRCFILES := $(shell ls *.c | sort) +SRCFILES := $(shell ls *.c | sort) ../third_party/tomlc99/toml.c OBJFILES := $(patsubst %.c,%.o,$(SRCFILES)) default: all @@ -46,6 +46,7 @@ $%.o: %.c clean: rm -f *.o depend + rm -f ../third_party/tomlc99/toml.o rm -f ./tests/*.o depend # Dependencies for the object files (automagically generated with diff --git a/src/accounts.c b/src/accounts.c index da4efa4..9009ccb 100644 --- a/src/accounts.c +++ b/src/accounts.c @@ -12,6 +12,8 @@ #include "utils.h" #include "db.h" #include "accounts.h" +#include "toml.h" +#include "toml_utils.h" static void set_account_name(struct account_data *account, const char *name) { @@ -28,6 +30,31 @@ static void set_account_name(struct account_data *account, const char *name) account->name = strdup(tmp); } +static char *toml_dup_string(toml_table_t *tab, const char *key) +{ + toml_datum_t d = toml_string_in(tab, key); + + if (!d.ok) + return NULL; + + return d.u.s; +} + +static void toml_set_account_pc_name(struct account_data *account, const char *value) +{ + char tmp[MAX_INPUT_LENGTH]; + + if (!value || !*value) + return; + + strlcpy(tmp, value, sizeof(tmp)); + CAP(tmp); + + if (account->pc_name) + free(account->pc_name); + account->pc_name = strdup(tmp); +} + int account_has_pc(const struct account_data *account, const char *pc_name) { int i; @@ -77,9 +104,11 @@ struct account_data *account_load(const char *name) struct account_data *account; FILE *fl; char filename[PATH_MAX]; - char line[MAX_INPUT_LENGTH + 1]; - char tag[6]; - int i; + char errbuf[256]; + toml_table_t *tab = NULL; + toml_array_t *arr = NULL; + char *value = NULL; + int i, count; if (!name || !*name) return NULL; @@ -91,29 +120,55 @@ struct account_data *account_load(const char *name) if (!fl) return NULL; + tab = toml_parse_file(fl, errbuf, sizeof(errbuf)); + fclose(fl); + if (!tab) { + log("SYSERR: Couldn't parse account file %s: %s.", filename, errbuf); + return NULL; + } + account = account_create(NULL); - while (get_line(fl, line)) { - tag_argument(line, tag); + value = toml_dup_string(tab, "name"); + if (value) { + set_account_name(account, value); + free(value); + } - if (!strcmp(tag, "Name")) - set_account_name(account, line); - else if (!strcmp(tag, "Pass")) - strlcpy(account->passwd, line, sizeof(account->passwd)); - else if (!strcmp(tag, "Mail")) { - if (account->email) - free(account->email); - account->email = strdup(line); - } else if (!strcmp(tag, "Char")) - account_add_pc(account, line); - else if (!strcmp(tag, "Curr")) { - if (account->pc_name) - free(account->pc_name); - account->pc_name = strdup(line); + value = toml_dup_string(tab, "password"); + if (value) { + strlcpy(account->passwd, value, sizeof(account->passwd)); + free(value); + } + + value = toml_dup_string(tab, "email"); + if (value) { + if (account->email) + free(account->email); + account->email = value; + } + + value = toml_dup_string(tab, "current_pc"); + if (value) { + toml_set_account_pc_name(account, value); + free(value); + } + + arr = toml_array_in(tab, "pcs"); + if (arr) { + count = toml_array_nelem(arr); + for (i = 0; i < count; i++) { + toml_datum_t d = toml_string_at(arr, i); + if (!d.ok) { + log("SYSERR: Invalid account character entry %d in %s.", i, filename); + continue; + } + account_add_pc(account, d.u.s); + free(d.u.s); } } - fclose(fl); + toml_free(tab); if (!account->name) set_account_name(account, name); @@ -151,14 +206,33 @@ int account_save(const struct account_data *account) return 0; } - fprintf(fl, "Name: %s\n", account->name); - fprintf(fl, "Pass: %s\n", account->passwd); + fprintf(fl, "name = "); + toml_write_string(fl, account->name); + fprintf(fl, "\n"); + fprintf(fl, "password = "); + toml_write_string(fl, account->passwd); + fprintf(fl, "\n"); if (account->email && *account->email) - fprintf(fl, "Mail: %s\n", account->email); + { + fprintf(fl, "email = "); + toml_write_string(fl, account->email); + fprintf(fl, "\n"); + } if (account->pc_name && *account->pc_name) - fprintf(fl, "Curr: %s\n", account->pc_name); - for (i = 0; i < account->pc_count; i++) - fprintf(fl, "Char: %s\n", account->pc_list[i]); + { + fprintf(fl, "current_pc = "); + toml_write_string(fl, account->pc_name); + fprintf(fl, "\n"); + } + if (account->pc_count > 0) { + fprintf(fl, "pcs = [\n"); + for (i = 0; i < account->pc_count; i++) { + fprintf(fl, " "); + toml_write_string(fl, account->pc_list[i]); + fprintf(fl, "%s\n", (i + 1 < account->pc_count) ? "," : ""); + } + fprintf(fl, "]\n"); + } fclose(fl); return 1; diff --git a/src/db.c b/src/db.c index 9471b81..ffe7a93 100644 --- a/src/db.c +++ b/src/db.c @@ -40,6 +40,7 @@ #include "mud_event.h" #include "msgedit.h" #include "screen.h" +#include "toml.h" #include /* declarations of most of the 'global' variables */ @@ -139,6 +140,11 @@ static void parse_simple_mob(FILE *mob_f, int i, int nr); static void interpret_espec(const char *keyword, const char *value, int i, int nr); static void parse_espec(char *buf, int i, int nr); static void parse_enhanced_mob(FILE *mob_f, int i, int nr); +static void parse_room_toml(toml_table_t *room_tab); +static void parse_mobile_toml(toml_table_t *mob_tab); +static void parse_object_toml(toml_table_t *obj_tab); +static void parse_trigger_toml(toml_table_t *trig_tab); +static void parse_quest_toml(toml_table_t *quest_tab); static void get_one_line(FILE *fl, char *buf); static void check_start_rooms(void); static void renum_zone_table(void); @@ -944,12 +950,234 @@ int count_hash_records(FILE *fl) return (count); } +static const char *toml_mode_key(int mode) +{ + switch (mode) { + case DB_BOOT_WLD: + return "room"; + case DB_BOOT_MOB: + return "mob"; + case DB_BOOT_OBJ: + return "object"; + case DB_BOOT_ZON: + return "zone"; + case DB_BOOT_SHP: + return "shop"; + case DB_BOOT_TRG: + return "trigger"; + case DB_BOOT_QST: + return "quest"; + default: + return NULL; + } +} + +static char **toml_load_index_files(const char *index_path, int *count) +{ + FILE *fp; + toml_table_t *tab; + toml_array_t *files; + char errbuf[200]; + char **list = NULL; + int i, n; + + if (!count) + return NULL; + + *count = 0; + + if (!(fp = fopen(index_path, "r"))) { + log("SYSERR: opening index file '%s': %s", index_path, strerror(errno)); + exit(1); + } + + tab = toml_parse_file(fp, errbuf, sizeof(errbuf)); + fclose(fp); + if (!tab) { + log("SYSERR: parsing index file '%s': %s", index_path, errbuf); + exit(1); + } + + files = toml_array_in(tab, "files"); + if (!files) { + toml_free(tab); + log("SYSERR: index file '%s' missing 'files' array.", index_path); + exit(1); + } + + n = toml_array_nelem(files); + if (n > 0) + CREATE(list, char *, n); + + for (i = 0; i < n; i++) { + toml_datum_t v = toml_string_at(files, i); + if (!v.ok || !v.u.s) { + toml_free(tab); + log("SYSERR: index file '%s' has invalid entry at %d.", index_path, i); + exit(1); + } + list[i] = strdup(v.u.s); + free(v.u.s); + } + + toml_free(tab); + *count = n; + return list; +} + +static int toml_count_records(const char *path, int mode) +{ + FILE *fp; + toml_table_t *tab; + toml_array_t *arr; + char errbuf[200]; + const char *key; + int count = 0; + + key = toml_mode_key(mode); + if (!key) + return 0; + + fp = fopen(path, "r"); + if (!fp) { + log("SYSERR: File '%s' listed in '%s': %s", path, key, strerror(errno)); + return 0; + } + + tab = toml_parse_file(fp, errbuf, sizeof(errbuf)); + fclose(fp); + if (!tab) { + log("SYSERR: parsing file '%s': %s", path, errbuf); + return 0; + } + + arr = toml_array_in(tab, key); + if (arr) + count = toml_array_nelem(arr); + + toml_free(tab); + return count; +} + +static int toml_get_int_default(toml_table_t *tab, const char *key, int def) +{ + toml_datum_t v = toml_int_in(tab, key); + if (!v.ok) + return def; + return (int)v.u.i; +} + +static char *toml_get_string_dup(toml_table_t *tab, const char *key) +{ + toml_datum_t v = toml_string_in(tab, key); + char *out; + + if (!v.ok || !v.u.s) + return NULL; + + out = strdup(v.u.s); + free(v.u.s); + return out; +} + +static int toml_get_int_array(toml_table_t *tab, const char *key, int *out, int out_len) +{ + toml_array_t *arr; + int i, n; + + if (!out || out_len <= 0) + return 0; + + arr = toml_array_in(tab, key); + if (!arr) + return 0; + + n = toml_array_nelem(arr); + for (i = 0; i < out_len && i < n; i++) { + toml_datum_t v = toml_int_at(arr, i); + if (v.ok) + out[i] = (int)v.u.i; + } + return n; +} + +static void add_proto_trigger(void *proto, int type, int vnum) +{ + struct trig_proto_list *trg_proto, *new_trg; + int rnum; + + rnum = real_trigger(vnum); + if (rnum == NOTHING) { + switch (type) { + case MOB_TRIGGER: + mudlog(BRF, LVL_BUILDER, TRUE, + "SYSERR: Trigger vnum #%d asked for but non-existant! (mob: %s - %d)", + vnum, GET_NAME((char_data *)proto), GET_MOB_VNUM((char_data *)proto)); + break; + case WLD_TRIGGER: + mudlog(BRF, LVL_BUILDER, TRUE, + "SYSERR: Trigger vnum #%d asked for but non-existant! (room:%d)", + vnum, GET_ROOM_VNUM(((room_data *)proto)->number)); + break; + case OBJ_TRIGGER: + mudlog(BRF, LVL_BUILDER, TRUE, + "SYSERR: Trigger vnum #%d asked for but non-existant! (obj:%d)", + vnum, GET_OBJ_VNUM((obj_data *)proto)); + break; + default: + mudlog(BRF, LVL_BUILDER, TRUE, + "SYSERR: Trigger vnum #%d asked for but non-existant! (?)", vnum); + break; + } + return; + } + + CREATE(new_trg, struct trig_proto_list, 1); + new_trg->vnum = vnum; + new_trg->next = NULL; + + switch (type) { + case MOB_TRIGGER: + trg_proto = ((char_data *)proto)->proto_script; + if (!trg_proto) + ((char_data *)proto)->proto_script = trg_proto = new_trg; + else { + while (trg_proto->next) + trg_proto = trg_proto->next; + trg_proto->next = new_trg; + } + break; + case WLD_TRIGGER: + trg_proto = ((room_data *)proto)->proto_script; + if (!trg_proto) + ((room_data *)proto)->proto_script = trg_proto = new_trg; + else { + while (trg_proto->next) + trg_proto = trg_proto->next; + trg_proto->next = new_trg; + } + break; + case OBJ_TRIGGER: + trg_proto = ((obj_data *)proto)->proto_script; + if (!trg_proto) + ((obj_data *)proto)->proto_script = trg_proto = new_trg; + else { + while (trg_proto->next) + trg_proto = trg_proto->next; + trg_proto->next = new_trg; + } + break; + } +} + void index_boot(int mode) { const char *index_filename, *prefix = NULL; /* NULL or egcs 1.1 complains */ - FILE *db_index, *db_file; + FILE *db_index = NULL, *db_file; int line_number, rec_count = 0, size[2]; char buf2[PATH_MAX], buf1[PATH_MAX - 100]; // - 100 to make room for prefix + char **index_files = NULL; + int index_count = 0; switch (mode) { case DB_BOOT_WLD: @@ -981,48 +1209,55 @@ void index_boot(int mode) exit(1); } - if (mini_mud) - index_filename = MINDEX_FILE; - else - index_filename = INDEX_FILE; + if (mode == DB_BOOT_HLP) { + index_filename = mini_mud ? "index.mini" : "index"; - snprintf(buf2, sizeof(buf2), "%s%s", prefix, index_filename); - if (!(db_index = fopen(buf2, "r"))) { - log("SYSERR: opening index file '%s': %s", buf2, strerror(errno)); - exit(1); - } - - for (line_number = 0;; ++line_number) { - /* first, count the number of records in the file so we can malloc */ - if (fscanf(db_index, "%s\n", buf1) != 1) { - if (feof(db_index)) - log("SYSERR: boot error -- unexpected end of file encountered in index file ./%s%s. " - "Ensure that the last line of the file starts with the character '$'.", - prefix, index_filename); - else if (ferror(db_index)) - log("SYSERR: boot error -- unexpected end of file encountered in index file ./%s%s: %s", - prefix, index_filename, strerror(errno)); - else - log("SYSERR: boot error -- error parsing index file %s%s on line %d", - prefix, index_filename, line_number); + snprintf(buf2, sizeof(buf2), "%s%s", prefix, index_filename); + if (!(db_index = fopen(buf2, "r"))) { + log("SYSERR: opening index file '%s': %s", buf2, strerror(errno)); exit(1); } - if (*buf1 == '$') - break; + for (line_number = 0;; ++line_number) { + /* first, count the number of records in the file so we can malloc */ + if (fscanf(db_index, "%s\n", buf1) != 1) { + if (feof(db_index)) + log("SYSERR: boot error -- unexpected end of file encountered in index file ./%s%s. " + "Ensure that the last line of the file starts with the character '$'.", + prefix, index_filename); + else if (ferror(db_index)) + log("SYSERR: boot error -- unexpected end of file encountered in index file ./%s%s: %s", + prefix, index_filename, strerror(errno)); + else + log("SYSERR: boot error -- error parsing index file %s%s on line %d", + prefix, index_filename, line_number); + exit(1); + } - snprintf(buf2, sizeof(buf2), "%s%s", prefix, buf1); - if (!(db_file = fopen(buf2, "r"))) { - log("SYSERR: File '%s' listed in '%s/%s': %s", buf2, prefix, - index_filename, strerror(errno)); - } else { - if (mode == DB_BOOT_ZON) - rec_count++; - else if (mode == DB_BOOT_HLP) + if (*buf1 == '$') + break; + + snprintf(buf2, sizeof(buf2), "%s%s", prefix, buf1); + if (!(db_file = fopen(buf2, "r"))) { + log("SYSERR: File '%s' listed in '%s/%s': %s", buf2, prefix, + index_filename, strerror(errno)); + } else { rec_count += count_alias_records(db_file); - else - rec_count += count_hash_records(db_file); - fclose(db_file); + fclose(db_file); + } + } + } else { + if (mini_mud) + index_filename = MINDEX_FILE; + else + index_filename = INDEX_FILE; + + snprintf(buf2, sizeof(buf2), "%s%s", prefix, index_filename); + index_files = toml_load_index_files(buf2, &index_count); + + for (line_number = 0; line_number < index_count; line_number++) { + snprintf(buf2, sizeof(buf2), "%s%s", prefix, index_files[line_number]); + rec_count += toml_count_records(buf2, mode); } } @@ -1076,52 +1311,58 @@ void index_boot(int mode) break; } - rewind(db_index); + if (mode == DB_BOOT_HLP) { + rewind(db_index); - for (line_number = 1;; ++line_number) { - if (fscanf(db_index, "%s\n", buf1) != 1) { - if (feof(db_index)) - log("SYSERR: boot error -- unexpected end of file encountered in index file ./%s%s", - prefix, index_filename); - else if (ferror(db_index)) - log("SYSERR: boot error -- unexpected end of file encountered in index file ./%s%s: %s", - prefix, index_filename, strerror(errno)); - else - log("SYSERR: boot error -- error parsing index file ./%s%s on line %d", - prefix, index_filename, line_number); - exit(1); - } + for (line_number = 1;; ++line_number) { + if (fscanf(db_index, "%s\n", buf1) != 1) { + if (feof(db_index)) + log("SYSERR: boot error -- unexpected end of file encountered in index file ./%s%s", + prefix, index_filename); + else if (ferror(db_index)) + log("SYSERR: boot error -- unexpected end of file encountered in index file ./%s%s: %s", + prefix, index_filename, strerror(errno)); + else + log("SYSERR: boot error -- error parsing index file ./%s%s on line %d", + prefix, index_filename, line_number); + exit(1); + } - if (*buf1 == '$') - break; + if (*buf1 == '$') + break; - snprintf(buf2, sizeof(buf2), "%s%s", prefix, buf1); - if (!(db_file = fopen(buf2, "r"))) { - log("SYSERR: %s: %s", buf2, strerror(errno)); - exit(1); - } - switch (mode) { - case DB_BOOT_WLD: - case DB_BOOT_OBJ: - case DB_BOOT_MOB: - case DB_BOOT_TRG: - case DB_BOOT_QST: - discrete_load(db_file, mode, buf2); - break; - case DB_BOOT_ZON: - load_zones(db_file, buf2); - break; - case DB_BOOT_HLP: + snprintf(buf2, sizeof(buf2), "%s%s", prefix, buf1); + if (!(db_file = fopen(buf2, "r"))) { + log("SYSERR: %s: %s", buf2, strerror(errno)); + exit(1); + } load_help(db_file, buf2); - break; - case DB_BOOT_SHP: - boot_the_shops(db_file, buf2, rec_count); - break; + fclose(db_file); } - - fclose(db_file); + fclose(db_index); + } else { + for (line_number = 0; line_number < index_count; line_number++) { + snprintf(buf2, sizeof(buf2), "%s%s", prefix, index_files[line_number]); + switch (mode) { + case DB_BOOT_WLD: + case DB_BOOT_OBJ: + case DB_BOOT_MOB: + case DB_BOOT_TRG: + case DB_BOOT_QST: + discrete_load(NULL, mode, buf2); + break; + case DB_BOOT_ZON: + load_zones(NULL, buf2); + break; + case DB_BOOT_SHP: + boot_the_shops(NULL, buf2, rec_count); + break; + } + } + for (line_number = 0; line_number < index_count; line_number++) + free(index_files[line_number]); + free(index_files); } - fclose(db_index); /* Sort the help index. */ if (mode == DB_BOOT_HLP) { @@ -1131,63 +1372,63 @@ void index_boot(int mode) void discrete_load(FILE *fl, int mode, char *filename) { - int nr = -1, last; - char line[READ_SIZE]; + FILE *fp; + toml_table_t *tab; + toml_array_t *arr; + char errbuf[200]; + const char *key; + int i, count; - const char *modes[] = {"world", "mob", "obj", "ZON", "SHP", "HLP", "trg", "qst"}; - /* modes positions correspond to DB_BOOT_xxx in db.h */ + (void)fl; - for (;;) { - /* We have to do special processing with the obj files because they have no - * end-of-record marker. */ - if (mode != DB_BOOT_OBJ || nr < 0) - if (!get_line(fl, line)) { - if (nr == -1) { - log("SYSERR: %s file %s is empty!", modes[mode], filename); - } else { - log("SYSERR: Format error in %s after %s #%d\n" - "...expecting a new %s, but file ended!\n" - "(maybe the file is not terminated with '$'?)", filename, - modes[mode], nr, modes[mode]); - } - exit(1); - } - if (*line == '$') - return; + key = toml_mode_key(mode); + if (!key) + return; - if (*line == '#') { - last = nr; - if (sscanf(line, "#%d", &nr) != 1) { - log("SYSERR: Format error after %s #%d", modes[mode], last); - exit(1); - } - if (nr >= 99999) - return; - else - switch (mode) { - case DB_BOOT_WLD: - parse_room(fl, nr); - break; - case DB_BOOT_MOB: - parse_mobile(fl, nr); - break; - case DB_BOOT_TRG: - parse_trigger(fl, nr); - break; - case DB_BOOT_OBJ: - strlcpy(line, parse_object(fl, nr), sizeof(line)); - break; - case DB_BOOT_QST: - parse_quest(fl, nr); - break; - } - } else { - log("SYSERR: Format error in %s file %s near %s #%d", modes[mode], - filename, modes[mode], nr); - log("SYSERR: ... offending line: '%s'", line); - exit(1); + if (!(fp = fopen(filename, "r"))) { + log("SYSERR: %s: %s", filename, strerror(errno)); + exit(1); + } + + tab = toml_parse_file(fp, errbuf, sizeof(errbuf)); + fclose(fp); + if (!tab) { + log("SYSERR: parsing file '%s': %s", filename, errbuf); + exit(1); + } + + arr = toml_array_in(tab, key); + if (!arr) { + toml_free(tab); + log("SYSERR: TOML file '%s' missing '%s' array.", filename, key); + exit(1); + } + + count = toml_array_nelem(arr); + for (i = 0; i < count; i++) { + toml_table_t *item = toml_table_at(arr, i); + if (!item) + continue; + switch (mode) { + case DB_BOOT_WLD: + parse_room_toml(item); + break; + case DB_BOOT_MOB: + parse_mobile_toml(item); + break; + case DB_BOOT_TRG: + parse_trigger_toml(item); + break; + case DB_BOOT_OBJ: + parse_object_toml(item); + break; + case DB_BOOT_QST: + parse_quest_toml(item); + break; } } + + toml_free(tab); } static char fread_letter(FILE *fp) @@ -1263,6 +1504,729 @@ void ensure_newline_terminated(struct extra_descr_data* new_descr) { } } +static void parse_room_toml(toml_table_t *room_tab) +{ + static int room_nr = 0, zone = 0; + int virtual_nr, i; + int flags[AF_ARRAY_MAX]; + int sector; + toml_array_t *arr; + char buf2[MAX_STRING_LENGTH], buf[128]; + + if (!room_tab) + return; + + virtual_nr = toml_get_int_default(room_tab, "vnum", NOWHERE); + if (virtual_nr == NOWHERE) { + log("SYSERR: TOML room missing vnum."); + exit(1); + } + + /* This really had better fit or there are other problems. */ + snprintf(buf2, sizeof(buf2), "room #%d", virtual_nr); + + if (virtual_nr < zone_table[zone].bot) { + log("SYSERR: Room #%d is below zone %d (bot=%d, top=%d).", virtual_nr, zone_table[zone].number, zone_table[zone].bot, zone_table[zone].top); + exit(1); + } + while (virtual_nr > zone_table[zone].top) + if (++zone > top_of_zone_table) { + log("SYSERR: Room %d is outside of any zone.", virtual_nr); + exit(1); + } + world[room_nr].zone = zone; + world[room_nr].number = virtual_nr; + + world[room_nr].name = toml_get_string_dup(room_tab, "name"); + world[room_nr].description = toml_get_string_dup(room_tab, "description"); + if (!world[room_nr].name || !world[room_nr].description) { + log("SYSERR: Room #%d missing name/description.", virtual_nr); + exit(1); + } + + for (i = 0; i < AF_ARRAY_MAX; i++) + flags[i] = 0; + toml_get_int_array(room_tab, "flags", flags, AF_ARRAY_MAX); + for (i = 0; i < AF_ARRAY_MAX; i++) + world[room_nr].room_flags[i] = flags[i]; + + snprintf(buf, sizeof(buf), "room #%d", virtual_nr); + for (i = 0; i < AF_ARRAY_MAX; i++) + check_bitvector_names(world[room_nr].room_flags[i], room_bits_count, buf, "room"); + + sector = toml_get_int_default(room_tab, "sector", SECT_INSIDE); + if (sector > NUM_ROOM_SECTORS) + sector = SECT_INSIDE; + world[room_nr].sector_type = sector; + + world[room_nr].func = NULL; + world[room_nr].contents = NULL; + world[room_nr].people = NULL; + world[room_nr].light = 0; /* Zero light sources */ + + for (i = 0; i < NUM_OF_DIRS; i++) /* NUM_OF_DIRS here, not DIR_COUNT */ + world[room_nr].dir_option[i] = NULL; + + world[room_nr].ex_description = NULL; + world[room_nr].forage = NULL; + world[room_nr].proto_script = NULL; + + arr = toml_array_in(room_tab, "exit"); + if (arr) { + int n = toml_array_nelem(arr); + for (i = 0; i < n; i++) { + toml_table_t *exit_tab = toml_table_at(arr, i); + int dir, exit_info, key, to_room; + char *general_description, *keyword; + + if (!exit_tab) + continue; + + dir = toml_get_int_default(exit_tab, "dir", -1); + if (dir < 0 || dir >= NUM_OF_DIRS) + continue; + + if (!CONFIG_DIAGONAL_DIRS && IS_DIAGONAL(dir)) { + snprintf(buf2, sizeof(buf2), "room #%d, direction D%d", virtual_nr, dir); + log("Warning: Diagonal direction disabled: %s", buf2); + continue; + } + + CREATE(world[room_nr].dir_option[dir], struct room_direction_data, 1); + general_description = toml_get_string_dup(exit_tab, "description"); + keyword = toml_get_string_dup(exit_tab, "keyword"); + world[room_nr].dir_option[dir]->general_description = general_description; + world[room_nr].dir_option[dir]->keyword = keyword; + + exit_info = toml_get_int_default(exit_tab, "exit_info", 0); + key = toml_get_int_default(exit_tab, "key", NOTHING); + to_room = toml_get_int_default(exit_tab, "to_room", NOWHERE); + + world[room_nr].dir_option[dir]->exit_info = exit_info; + world[room_nr].dir_option[dir]->key = ((key == -1 || key == 65535) ? NOTHING : key); + world[room_nr].dir_option[dir]->to_room = ((to_room == -1 || to_room == 0) ? NOWHERE : to_room); + } + } + + arr = toml_array_in(room_tab, "extra_desc"); + if (arr) { + int n = toml_array_nelem(arr); + for (i = 0; i < n; i++) { + toml_table_t *ed_tab = toml_table_at(arr, i); + struct extra_descr_data *new_descr; + char *keyword, *description; + + if (!ed_tab) + continue; + keyword = toml_get_string_dup(ed_tab, "keyword"); + description = toml_get_string_dup(ed_tab, "description"); + if (!keyword || !description) { + if (keyword) free(keyword); + if (description) free(description); + continue; + } + CREATE(new_descr, struct extra_descr_data, 1); + new_descr->keyword = keyword; + new_descr->description = description; + ensure_newline_terminated(new_descr); + + new_descr->next = world[room_nr].ex_description; + world[room_nr].ex_description = new_descr; + } + } + + arr = toml_array_in(room_tab, "forage"); + if (arr) { + int n = toml_array_nelem(arr); + for (i = 0; i < n; i++) { + toml_table_t *f_tab = toml_table_at(arr, i); + struct forage_entry *e, *tail; + int ovnum, dc; + + if (!f_tab) + continue; + ovnum = toml_get_int_default(f_tab, "obj_vnum", 0); + dc = toml_get_int_default(f_tab, "dc", 0); + if (ovnum <= 0) + continue; + + CREATE(e, struct forage_entry, 1); + e->obj_vnum = ovnum; + e->dc = dc; + e->next = NULL; + if (!world[room_nr].forage) { + world[room_nr].forage = e; + } else { + tail = world[room_nr].forage; + while (tail->next) + tail = tail->next; + tail->next = e; + } + } + } + + arr = toml_array_in(room_tab, "triggers"); + if (arr) { + int n = toml_array_nelem(arr); + for (i = 0; i < n; i++) { + toml_datum_t v = toml_int_at(arr, i); + if (v.ok) + add_proto_trigger(&world[room_nr], WLD_TRIGGER, (int)v.u.i); + } + } + + top_of_world = room_nr++; +} + +static void parse_mobile_toml(toml_table_t *mob_tab) +{ + static int i = 0; + int j; + int vnum; + int level, hit_dice, mana_dice, stamina_dice; + int pos, default_pos, sex; + int val; + char *mob_type; + toml_table_t *simple_tab, *enh_tab, *sub_tab; + toml_array_t *arr; + char buf2[128]; + + if (!mob_tab) + return; + + vnum = toml_get_int_default(mob_tab, "vnum", -1); + if (vnum < 0) { + log("SYSERR: TOML mob missing vnum."); + exit(1); + } + + mob_index[i].vnum = vnum; + mob_index[i].number = 0; + mob_index[i].func = NULL; + mob_index[i].skin_yields = NULL; + + clear_char(mob_proto + i); + + /* Mobiles should NEVER use anything in the 'player_specials' structure. */ + mob_proto[i].player_specials = &dummy_mob; + sprintf(buf2, "mob vnum %d", vnum); /* sprintf: OK (for 'buf2 >= 19') */ + + mob_proto[i].player.name = toml_get_string_dup(mob_tab, "name"); + mob_proto[i].player.keywords = toml_get_string_dup(mob_tab, "keywords"); + mob_proto[i].player.short_descr = toml_get_string_dup(mob_tab, "short"); + mob_proto[i].player.long_descr = toml_get_string_dup(mob_tab, "long"); + mob_proto[i].player.description = toml_get_string_dup(mob_tab, "description"); + mob_proto[i].player.background = toml_get_string_dup(mob_tab, "background"); + + if (!mob_proto[i].player.name || !mob_proto[i].player.keywords || + !mob_proto[i].player.short_descr || !mob_proto[i].player.long_descr || + !mob_proto[i].player.description) { + log("SYSERR: Mob #%d missing required strings.", vnum); + exit(1); + } + + if (mob_proto[i].player.short_descr && *mob_proto[i].player.short_descr) + if (!str_cmp(fname(mob_proto[i].player.short_descr), "a") || + !str_cmp(fname(mob_proto[i].player.short_descr), "an") || + !str_cmp(fname(mob_proto[i].player.short_descr), "the")) + *mob_proto[i].player.short_descr = LOWER(*mob_proto[i].player.short_descr); + + for (j = 0; j < AF_ARRAY_MAX; j++) { + MOB_FLAGS(mob_proto + i)[j] = 0; + AFF_FLAGS(mob_proto + i)[j] = 0; + } + + toml_get_int_array(mob_tab, "flags", (int *)MOB_FLAGS(mob_proto + i), AF_ARRAY_MAX); + toml_get_int_array(mob_tab, "aff_flags", (int *)AFF_FLAGS(mob_proto + i), AF_ARRAY_MAX); + + for (j = 0; j < AF_ARRAY_MAX; j++) + check_bitvector_names(MOB_FLAGS(mob_proto + i)[j], action_bits_count, buf2, "mobile"); + for (j = 0; j < AF_ARRAY_MAX; j++) + check_bitvector_names(AFF_FLAGS(mob_proto + i)[j], affected_bits_count, buf2, "mobile affect"); + + GET_ALIGNMENT(mob_proto + i) = toml_get_int_default(mob_tab, "alignment", 0); + + SET_BIT_AR(MOB_FLAGS(mob_proto + i), MOB_ISNPC); + if (MOB_FLAGGED(mob_proto + i, MOB_NOTDEADYET)) { + log("SYSERR: Mob #%d has reserved bit MOB_NOTDEADYET set.", vnum); + REMOVE_BIT_AR(MOB_FLAGS(mob_proto + i), MOB_NOTDEADYET); + } + + mob_proto[i].real_abils.str = 11; + mob_proto[i].real_abils.intel = 11; + mob_proto[i].real_abils.wis = 11; + mob_proto[i].real_abils.dex = 11; + mob_proto[i].real_abils.con = 11; + mob_proto[i].real_abils.cha = 11; + + simple_tab = toml_table_in(mob_tab, "simple"); + if (!simple_tab) { + log("SYSERR: Mob #%d missing [mob.simple] table.", vnum); + exit(1); + } + + level = toml_get_int_default(simple_tab, "level", 1); + if (level != 1) + log("INFO: Forcing mob #%d level from %d to 1 per level lock.", vnum, level); + GET_LEVEL(mob_proto + i) = 1; + + hit_dice = toml_get_int_default(simple_tab, "hit_dice", 0); + mana_dice = toml_get_int_default(simple_tab, "mana_dice", 0); + stamina_dice = toml_get_int_default(simple_tab, "stamina_dice", 0); + + GET_MAX_HIT(mob_proto + i) = 0; + GET_HIT(mob_proto + i) = hit_dice; + GET_MANA(mob_proto + i) = mana_dice; + GET_STAMINA(mob_proto + i) = stamina_dice; + + GET_MAX_MANA(mob_proto + i) = 10; + GET_MAX_STAMINA(mob_proto + i) = 50; + + pos = toml_get_int_default(simple_tab, "pos", POS_STANDING); + default_pos = toml_get_int_default(simple_tab, "default_pos", POS_STANDING); + sex = toml_get_int_default(simple_tab, "sex", SEX_NEUTRAL); + + GET_POS(mob_proto + i) = pos; + GET_DEFAULT_POS(mob_proto + i) = default_pos; + GET_SEX(mob_proto + i) = sex; + + GET_CLASS(mob_proto + i) = CLASS_UNDEFINED; + GET_SPECIES(mob_proto + i) = SPECIES_UNDEFINED; + GET_WEIGHT(mob_proto + i) = 200; + GET_HEIGHT(mob_proto + i) = 198; + + for (j = 0; j < NUM_OF_SAVING_THROWS; j++) + GET_SAVE(mob_proto + i, j) = 0; + + mob_type = toml_get_string_dup(mob_tab, "mob_type"); + if (!mob_type) + mob_type = strdup("simple"); + + if (!str_cmp(mob_type, "enhanced")) { + enh_tab = toml_table_in(mob_tab, "enhanced"); + if (!enh_tab) { + log("SYSERR: Mob #%d missing [mob.enhanced] table.", vnum); + exit(1); + } + + sub_tab = toml_table_in(enh_tab, "abilities"); + if (sub_tab) { + val = toml_get_int_default(sub_tab, "str", mob_proto[i].real_abils.str); + mob_proto[i].real_abils.str = MAX(3, MIN(25, val)); + val = toml_get_int_default(sub_tab, "dex", mob_proto[i].real_abils.dex); + mob_proto[i].real_abils.dex = MAX(3, MIN(25, val)); + val = toml_get_int_default(sub_tab, "con", mob_proto[i].real_abils.con); + mob_proto[i].real_abils.con = MAX(3, MIN(25, val)); + val = toml_get_int_default(sub_tab, "int", mob_proto[i].real_abils.intel); + mob_proto[i].real_abils.intel = MAX(3, MIN(25, val)); + val = toml_get_int_default(sub_tab, "wis", mob_proto[i].real_abils.wis); + mob_proto[i].real_abils.wis = MAX(3, MIN(25, val)); + val = toml_get_int_default(sub_tab, "cha", mob_proto[i].real_abils.cha); + mob_proto[i].real_abils.cha = MAX(3, MIN(25, val)); + } + + val = toml_get_int_default(enh_tab, "class", GET_CLASS(mob_proto + i)); + if (val >= CLASS_UNDEFINED && val < NUM_CLASSES) + GET_CLASS(mob_proto + i) = val; + val = toml_get_int_default(enh_tab, "species", GET_SPECIES(mob_proto + i)); + if (val >= SPECIES_UNDEFINED && val < NUM_SPECIES) + GET_SPECIES(mob_proto + i) = val; + val = toml_get_int_default(enh_tab, "age", 0); + if (val > 0) + mob_proto[i].player.roleplay_age = MAX(MIN_CHAR_AGE, MIN(MAX_CHAR_AGE, val)); + + sub_tab = toml_table_in(enh_tab, "saving_throws"); + if (sub_tab) { + mob_proto[i].char_specials.saved.saving_throws[ABIL_STR] = + MAX(0, MIN(100, toml_get_int_default(sub_tab, "str", 0))); + mob_proto[i].char_specials.saved.saving_throws[ABIL_DEX] = + MAX(0, MIN(100, toml_get_int_default(sub_tab, "dex", 0))); + mob_proto[i].char_specials.saved.saving_throws[ABIL_CON] = + MAX(0, MIN(100, toml_get_int_default(sub_tab, "con", 0))); + mob_proto[i].char_specials.saved.saving_throws[ABIL_INT] = + MAX(0, MIN(100, toml_get_int_default(sub_tab, "int", 0))); + mob_proto[i].char_specials.saved.saving_throws[ABIL_WIS] = + MAX(0, MIN(100, toml_get_int_default(sub_tab, "wis", 0))); + mob_proto[i].char_specials.saved.saving_throws[ABIL_CHA] = + MAX(0, MIN(100, toml_get_int_default(sub_tab, "cha", 0))); + } + + arr = toml_array_in(enh_tab, "skills"); + if (!arr) + arr = toml_array_in(mob_tab, "skills"); + if (arr) { + int n = toml_array_nelem(arr); + for (j = 0; j < n; j++) { + toml_table_t *sk_tab = toml_table_at(arr, j); + int snum, sval; + if (!sk_tab) + continue; + snum = toml_get_int_default(sk_tab, "id", 0); + sval = toml_get_int_default(sk_tab, "level", 0); + if (snum >= 0 && snum < MAX_SKILLS) + SET_SKILL(&mob_proto[i], snum, (byte)MIN(MAX(0, sval), 100)); + } + } + + val = toml_get_int_default(enh_tab, "attack_type", -1); + if (val >= TYPE_HIT && val < (TYPE_HIT + NUM_ATTACK_TYPES)) + val -= TYPE_HIT; + if (val >= 0 && val < NUM_ATTACK_TYPES) + mob_proto[i].mob_specials.attack_type = val; + } + + free(mob_type); + + if (mob_proto[i].player.time.birth == 0) + mob_proto[i].player.time.birth = time(0); + if (mob_proto[i].player.roleplay_age == 0) + mob_proto[i].player.roleplay_age = MIN_CHAR_AGE; + if (mob_proto[i].player.roleplay_age_year == 0) + mob_proto[i].player.roleplay_age_year = time_info.year; + + arr = toml_array_in(mob_tab, "loadout"); + if (arr) { + int n = toml_array_nelem(arr); + for (j = 0; j < n; j++) { + toml_table_t *lo_tab = toml_table_at(arr, j); + int v, wpos, qty; + if (!lo_tab) + continue; + v = toml_get_int_default(lo_tab, "vnum", NOTHING); + wpos = toml_get_int_default(lo_tab, "wear_pos", 0); + qty = toml_get_int_default(lo_tab, "quantity", 1); + if (v > 0) + loadout_append_entry(&mob_proto[i].proto_loadout, v, wpos, qty); + } + } + + arr = toml_array_in(mob_tab, "skin_yield"); + if (arr) { + int n = toml_array_nelem(arr); + for (j = 0; j < n; j++) { + toml_table_t *y_tab = toml_table_at(arr, j); + struct skin_yield_entry *e; + int ovnum, dc; + + if (!y_tab) + continue; + ovnum = toml_get_int_default(y_tab, "obj_vnum", 0); + dc = toml_get_int_default(y_tab, "dc", 0); + if (ovnum <= 0) + continue; + + CREATE(e, struct skin_yield_entry, 1); + e->mob_vnum = mob_index[i].vnum; + e->obj_vnum = ovnum; + e->dc = dc; + e->next = mob_index[i].skin_yields; + mob_index[i].skin_yields = e; + } + } + + arr = toml_array_in(mob_tab, "triggers"); + if (arr) { + int n = toml_array_nelem(arr); + for (j = 0; j < n; j++) { + toml_datum_t v = toml_int_at(arr, j); + if (v.ok) + add_proto_trigger(&mob_proto[i], MOB_TRIGGER, (int)v.u.i); + } + } + + mob_proto[i].aff_abils = mob_proto[i].real_abils; + + for (j = 0; j < NUM_WEARS; j++) + mob_proto[i].equipment[j] = NULL; + + mob_proto[i].nr = i; + mob_proto[i].desc = NULL; + + top_of_mobt = i++; +} + +static void parse_object_toml(toml_table_t *obj_tab) +{ + static int i = 0; + int j; + int vnum; + toml_array_t *arr; + char *tmpptr; + char buf2[128]; + + if (!obj_tab) + return; + + vnum = toml_get_int_default(obj_tab, "vnum", -1); + if (vnum < 0) { + log("SYSERR: TOML object missing vnum."); + exit(1); + } + + obj_index[i].vnum = vnum; + obj_index[i].number = 0; + obj_index[i].func = NULL; + + clear_object(obj_proto + i); + obj_proto[i].item_number = i; + + sprintf(buf2, "object #%d", vnum); /* sprintf: OK (for 'buf2 >= 19') */ + + /* string data */ + if ((obj_proto[i].name = toml_get_string_dup(obj_tab, "name")) == NULL) { + log("SYSERR: Null obj name or format error at or near %s", buf2); + exit(1); + } + tmpptr = obj_proto[i].short_description = toml_get_string_dup(obj_tab, "short"); + if (tmpptr && *tmpptr) + if (!str_cmp(fname(tmpptr), "a") || !str_cmp(fname(tmpptr), "an") || + !str_cmp(fname(tmpptr), "the")) + *tmpptr = LOWER(*tmpptr); + + tmpptr = obj_proto[i].description = toml_get_string_dup(obj_tab, "description"); + if (tmpptr && *tmpptr) + CAP(tmpptr); + obj_proto[i].main_description = toml_get_string_dup(obj_tab, "main_description"); + + for (j = 0; j < AF_ARRAY_MAX; j++) { + GET_OBJ_EXTRA(obj_proto + i)[j] = 0; + GET_OBJ_WEAR(obj_proto + i)[j] = 0; + GET_OBJ_AFFECT(obj_proto + i)[j] = 0; + } + toml_get_int_array(obj_tab, "extra_flags", (int *)GET_OBJ_EXTRA(obj_proto + i), AF_ARRAY_MAX); + toml_get_int_array(obj_tab, "wear_flags", (int *)GET_OBJ_WEAR(obj_proto + i), AF_ARRAY_MAX); + toml_get_int_array(obj_tab, "affect_flags", (int *)GET_OBJ_AFFECT(obj_proto + i), AF_ARRAY_MAX); + + GET_OBJ_TYPE(obj_proto + i) = toml_get_int_default(obj_tab, "type", 0); + + { + int values[NUM_OBJ_VAL_POSITIONS]; + for (j = 0; j < NUM_OBJ_VAL_POSITIONS; j++) + values[j] = 0; + toml_get_int_array(obj_tab, "values", values, NUM_OBJ_VAL_POSITIONS); + for (j = 0; j < NUM_OBJ_VAL_POSITIONS; j++) + GET_OBJ_VAL(obj_proto + i, j) = values[j]; + } + + GET_OBJ_WEIGHT(obj_proto + i) = toml_get_int_default(obj_tab, "weight", 0); + GET_OBJ_COST(obj_proto + i) = toml_get_int_default(obj_tab, "cost", 0); + GET_OBJ_COST_PER_DAY(obj_proto + i) = 0; + GET_OBJ_LEVEL(obj_proto + i) = toml_get_int_default(obj_tab, "level", 0); + GET_OBJ_TIMER(obj_proto + i) = toml_get_int_default(obj_tab, "timer", 0); + + obj_proto[i].sitting_here = NULL; + + /* check to make sure that weight of containers exceeds curr. quantity */ + if (GET_OBJ_TYPE(obj_proto + i) == ITEM_DRINKCON || + GET_OBJ_TYPE(obj_proto + i) == ITEM_FOUNTAIN) { + if (GET_OBJ_WEIGHT(obj_proto + i) < GET_OBJ_VAL(obj_proto + i, 1) && CAN_WEAR(obj_proto + i, ITEM_WEAR_TAKE)) + GET_OBJ_WEIGHT(obj_proto + i) = GET_OBJ_VAL(obj_proto + i, 1) + 5; + } + + /* extra descriptions and affect fields */ + for (j = 0; j < MAX_OBJ_AFFECT; j++) { + obj_proto[i].affected[j].location = APPLY_NONE; + obj_proto[i].affected[j].modifier = 0; + } + + arr = toml_array_in(obj_tab, "extra_desc"); + if (arr) { + int n = toml_array_nelem(arr); + for (j = 0; j < n; j++) { + toml_table_t *ed_tab = toml_table_at(arr, j); + struct extra_descr_data *new_descr; + char *keyword, *description; + if (!ed_tab) + continue; + keyword = toml_get_string_dup(ed_tab, "keyword"); + description = toml_get_string_dup(ed_tab, "description"); + if (!keyword || !description) { + if (keyword) free(keyword); + if (description) free(description); + continue; + } + CREATE(new_descr, struct extra_descr_data, 1); + new_descr->keyword = keyword; + new_descr->description = description; + new_descr->next = obj_proto[i].ex_description; + obj_proto[i].ex_description = new_descr; + } + } + + arr = toml_array_in(obj_tab, "affect"); + if (arr) { + int n = toml_array_nelem(arr); + for (j = 0; j < n && j < MAX_OBJ_AFFECT; j++) { + toml_table_t *af_tab = toml_table_at(arr, j); + if (!af_tab) + continue; + obj_proto[i].affected[j].location = toml_get_int_default(af_tab, "location", APPLY_NONE); + obj_proto[i].affected[j].modifier = toml_get_int_default(af_tab, "modifier", 0); + } + } + + arr = toml_array_in(obj_tab, "triggers"); + if (arr) { + int n = toml_array_nelem(arr); + for (j = 0; j < n; j++) { + toml_datum_t v = toml_int_at(arr, j); + if (v.ok) + add_proto_trigger(&obj_proto[i], OBJ_TRIGGER, (int)v.u.i); + } + } + + top_of_objt = i; + check_object(obj_proto + i); + i++; +} + +static void parse_trigger_toml(toml_table_t *trig_tab) +{ + int vnum; + toml_array_t *arr; + int i, n; + struct cmdlist_element *cle = NULL; + struct index_data *t_index; + struct trig_data *trig; + + if (!trig_tab) + return; + + vnum = toml_get_int_default(trig_tab, "vnum", -1); + if (vnum < 0) { + log("SYSERR: TOML trigger missing vnum."); + exit(1); + } + + CREATE(trig, trig_data, 1); + memset(trig, 0, sizeof(*trig)); + CREATE(t_index, index_data, 1); + + t_index->vnum = vnum; + t_index->number = 0; + t_index->func = NULL; + t_index->proto = trig; + + trig->nr = top_of_trigt; + trig->name = toml_get_string_dup(trig_tab, "name"); + if (!trig->name) { + trig->name = strdup("unnamed trigger"); + log("Trigger with no name! (%d)", trig->nr); + } + + trig->attach_type = (byte)toml_get_int_default(trig_tab, "attach_type", 0); + trig->trigger_type = (long)toml_get_int_default(trig_tab, "flags", 0); + trig->narg = toml_get_int_default(trig_tab, "narg", 0); + trig->arglist = toml_get_string_dup(trig_tab, "arglist"); + + arr = toml_array_in(trig_tab, "commands"); + if (arr) { + n = toml_array_nelem(arr); + for (i = 0; i < n; i++) { + toml_datum_t v = toml_string_at(arr, i); + if (!v.ok || !v.u.s) + continue; + if (!trig->cmdlist) { + CREATE(trig->cmdlist, struct cmdlist_element, 1); + trig->cmdlist->cmd = strdup(v.u.s); + trig->cmdlist->next = NULL; + cle = trig->cmdlist; + } else { + CREATE(cle->next, struct cmdlist_element, 1); + cle = cle->next; + cle->cmd = strdup(v.u.s); + cle->next = NULL; + } + free(v.u.s); + } + } + + trig_index[top_of_trigt++] = t_index; +} + +static void parse_quest_toml(toml_table_t *quest_tab) +{ + static int i = 0; + int j; + int vnum; + toml_table_t *rewards; + toml_array_t *values; + + if (!quest_tab) + return; + + vnum = toml_get_int_default(quest_tab, "vnum", -1); + if (vnum < 0) { + log("SYSERR: TOML quest missing vnum."); + exit(1); + } + + aquest_table[i].vnum = vnum; + aquest_table[i].qm = NOBODY; + aquest_table[i].name = NULL; + aquest_table[i].desc = NULL; + aquest_table[i].info = NULL; + aquest_table[i].done = NULL; + aquest_table[i].quit = NULL; + aquest_table[i].flags = 0; + aquest_table[i].type = -1; + aquest_table[i].target = -1; + aquest_table[i].prereq = NOTHING; + for (j = 0; j < 7; j++) + aquest_table[i].value[j] = 0; + aquest_table[i].prev_quest = NOTHING; + aquest_table[i].next_quest = NOTHING; + aquest_table[i].func = NULL; + + aquest_table[i].coins_reward = 0; + aquest_table[i].exp_reward = 0; + aquest_table[i].obj_reward = NOTHING; + + aquest_table[i].name = toml_get_string_dup(quest_tab, "name"); + aquest_table[i].desc = toml_get_string_dup(quest_tab, "description"); + aquest_table[i].info = toml_get_string_dup(quest_tab, "info"); + aquest_table[i].done = toml_get_string_dup(quest_tab, "done"); + aquest_table[i].quit = toml_get_string_dup(quest_tab, "quit"); + + if (!aquest_table[i].name || !aquest_table[i].desc || !aquest_table[i].info || + !aquest_table[i].done || !aquest_table[i].quit) { + log("SYSERR: Quest #%d missing required strings.", vnum); + exit(1); + } + + aquest_table[i].type = toml_get_int_default(quest_tab, "type", -1); + { + int qm = toml_get_int_default(quest_tab, "quest_master", NOBODY); + aquest_table[i].qm = (real_mobile(qm) == NOBODY) ? NOBODY : qm; + } + aquest_table[i].flags = toml_get_int_default(quest_tab, "flags", 0); + aquest_table[i].target = toml_get_int_default(quest_tab, "target", NOTHING); + aquest_table[i].prev_quest = toml_get_int_default(quest_tab, "prev_quest", NOTHING); + aquest_table[i].next_quest = toml_get_int_default(quest_tab, "next_quest", NOTHING); + aquest_table[i].prereq = toml_get_int_default(quest_tab, "prereq", NOTHING); + + values = toml_array_in(quest_tab, "values"); + if (values) { + int n = toml_array_nelem(values); + for (j = 0; j < 7 && j < n; j++) { + toml_datum_t v = toml_int_at(values, j); + if (v.ok) + aquest_table[i].value[j] = (int)v.u.i; + } + } + + rewards = toml_table_in(quest_tab, "rewards"); + if (rewards) { + aquest_table[i].coins_reward = toml_get_int_default(rewards, "coins", 0); + aquest_table[i].exp_reward = toml_get_int_default(rewards, "exp", 0); + aquest_table[i].obj_reward = toml_get_int_default(rewards, "obj_vnum", NOTHING); + if (aquest_table[i].obj_reward == -1) + aquest_table[i].obj_reward = NOTHING; + } + + total_quests = ++i; +} + /* load the rooms */ void parse_room(FILE *fl, int virtual_nr) { @@ -2296,154 +3260,107 @@ char *parse_object(FILE *obj_f, int nr) static void load_zones(FILE *fl, char *zonename) { static zone_rnum zone = 0; - int i, cmd_no, num_of_cmds = 0, line_num = 0, tmp, error; - char *ptr, buf[READ_SIZE], zname[READ_SIZE], buf2[MAX_STRING_LENGTH]; - int zone_fix = FALSE; - char t1[80], t2[80]; - char zbuf1[MAX_STRING_LENGTH], zbuf2[MAX_STRING_LENGTH]; - char zbuf3[MAX_STRING_LENGTH], zbuf4[MAX_STRING_LENGTH]; + FILE *fp; + toml_table_t *tab; + toml_array_t *zones; + char errbuf[200]; + int i, zcount; - strlcpy(zname, zonename, sizeof(zname)); + (void)fl; - /* Skip first 3 lines lest we mistake the zone name for a command. */ - for (tmp = 0; tmp < 3; tmp++) - get_line(fl, buf); - - /* More accurate count. Previous was always 4 or 5 too high. -gg Note that if - * a new zone command is added to reset_zone(), this string will need to be - * updated to suit. - ae. */ - while (get_line(fl, buf)) - if ((strchr("MOPGERDTV", buf[0]) && buf[1] == ' ') || (buf[0] == 'S' && buf[1] == '\0')) - num_of_cmds++; - - rewind(fl); - - if (num_of_cmds == 0) { - log("SYSERR: %s is empty!", zname); - exit(1); - } else - CREATE(Z.cmd, struct reset_com, num_of_cmds); - - line_num += get_line(fl, buf); - - if (sscanf(buf, "#%d", &Z.number) != 1) { - log("SYSERR: Format error in %s, line %d", zname, line_num); - exit(1); - } - snprintf(buf2, sizeof(buf2), "beginning of zone #%d", Z.number); - - line_num += get_line(fl, buf); - if ((ptr = strchr(buf, '~')) != NULL) /* take off the '~' if it's there */ - *ptr = '\0'; - Z.builders = strdup(buf); - - line_num += get_line(fl, buf); - if ((ptr = strchr(buf, '~')) != NULL) /* take off the '~' if it's there */ - *ptr = '\0'; - Z.name = strdup(buf); - parse_at(Z.name); - - /* Clear all the zone flags */ - for (i=0; i Z.top) { - log("SYSERR: Zone %d bottom (%d) > top (%d).", Z.number, Z.bot, Z.top); + fp = fopen(zonename, "r"); + if (!fp) { + log("SYSERR: %s: %s", zonename, strerror(errno)); exit(1); } - cmd_no = 0; + tab = toml_parse_file(fp, errbuf, sizeof(errbuf)); + fclose(fp); + if (!tab) { + log("SYSERR: parsing file '%s': %s", zonename, errbuf); + exit(1); + } - for (;;) { - /* skip reading one line if we fixed above (line is correct already) */ - if (zone_fix != TRUE) { - if ((tmp = get_line(fl, buf)) == 0) { - log("SYSERR: Format error in %s - premature end of file", zname); - exit(1); - } - } else - zone_fix = FALSE; + zones = toml_array_in(tab, "zone"); + if (!zones) { + toml_free(tab); + log("SYSERR: TOML file '%s' missing 'zone' array.", zonename); + exit(1); + } - line_num += tmp; - ptr = buf; - skip_spaces(&ptr); + zcount = toml_array_nelem(zones); + for (i = 0; i < zcount; i++) { + toml_table_t *z_tab = toml_table_at(zones, i); + toml_array_t *cmds; + int j, cmd_count; - if ((ZCMD.command = *ptr) == '*') + if (!z_tab) continue; - ptr++; + Z.number = toml_get_int_default(z_tab, "vnum", 0); + Z.builders = toml_get_string_dup(z_tab, "builders"); + Z.name = toml_get_string_dup(z_tab, "name"); + if (!Z.builders) + Z.builders = strdup("None."); + if (!Z.name) + Z.name = strdup("Unnamed zone"); + parse_at(Z.name); - if (ZCMD.command == 'S' || ZCMD.command == '$') { - ZCMD.command = 'S'; - break; - } - error = 0; - if (strchr("MOGEPDTV", ZCMD.command) == NULL) { /* a 3-arg command */ - if (sscanf(ptr, " %d %d %d ", &tmp, &ZCMD.arg1, &ZCMD.arg2) != 3) - error = 1; - } else if (ZCMD.command=='V') { /* a string-arg command */ - if (sscanf(ptr, " %d %d %d %d %79s %79[^\f\n\r\t\v]", &tmp, &ZCMD.arg1, &ZCMD.arg2, - &ZCMD.arg3, t1, t2) != 6) - error = 1; - else { - ZCMD.sarg1 = strdup(t1); - ZCMD.sarg2 = strdup(t2); - } - } else { - if (sscanf(ptr, " %d %d %d %d ", &tmp, &ZCMD.arg1, &ZCMD.arg2, - &ZCMD.arg3) != 4) - error = 1; - } + for (j = 0; j < ZN_ARRAY_MAX; j++) + Z.zone_flags[j] = 0; + toml_get_int_array(z_tab, "flags", (int *)Z.zone_flags, ZN_ARRAY_MAX); - ZCMD.if_flag = tmp; + Z.bot = toml_get_int_default(z_tab, "bot", 0); + Z.top = toml_get_int_default(z_tab, "top", 0); + Z.lifespan = toml_get_int_default(z_tab, "lifespan", 0); + Z.reset_mode = toml_get_int_default(z_tab, "reset_mode", 0); + Z.min_level = toml_get_int_default(z_tab, "min_level", -1); + Z.max_level = toml_get_int_default(z_tab, "max_level", -1); - if (error) { - log("SYSERR: Format error in %s, line %d: '%s'", zname, line_num, buf); + if (Z.bot > Z.top) { + log("SYSERR: Zone %d bottom (%d) > top (%d).", Z.number, Z.bot, Z.top); exit(1); } - ZCMD.line = line_num; - cmd_no++; + + cmds = toml_array_in(z_tab, "command"); + cmd_count = cmds ? toml_array_nelem(cmds) : 0; + CREATE(Z.cmd, struct reset_com, cmd_count + 1); + + for (j = 0; j < cmd_count; j++) { + toml_table_t *c_tab = toml_table_at(cmds, j); + char *cmd = NULL; + if (!c_tab) + continue; + + cmd = toml_get_string_dup(c_tab, "command"); + Z.cmd[j].command = (cmd && *cmd) ? *cmd : 'S'; + if (cmd) + free(cmd); + + Z.cmd[j].if_flag = toml_get_int_default(c_tab, "if_flag", 0); + Z.cmd[j].arg1 = toml_get_int_default(c_tab, "arg1", 0); + Z.cmd[j].arg2 = toml_get_int_default(c_tab, "arg2", 0); + Z.cmd[j].arg3 = toml_get_int_default(c_tab, "arg3", 0); + Z.cmd[j].line = toml_get_int_default(c_tab, "line", 0); + + Z.cmd[j].sarg1 = toml_get_string_dup(c_tab, "sarg1"); + Z.cmd[j].sarg2 = toml_get_string_dup(c_tab, "sarg2"); + } + + Z.cmd[cmd_count].command = 'S'; + Z.cmd[cmd_count].if_flag = 0; + Z.cmd[cmd_count].arg1 = 0; + Z.cmd[cmd_count].arg2 = 0; + Z.cmd[cmd_count].arg3 = 0; + Z.cmd[cmd_count].sarg1 = NULL; + Z.cmd[cmd_count].sarg2 = NULL; + Z.cmd[cmd_count].line = 0; + + top_of_zone_table = zone; + zone++; } - if (num_of_cmds != cmd_no + 1) { - log("SYSERR: Zone command count mismatch for %s. Estimated: %d, Actual: %d", zname, num_of_cmds, cmd_no + 1); - exit(1); - } - - top_of_zone_table = zone++; + toml_free(tab); } #undef Z diff --git a/src/db.h b/src/db.h index 29edc95..d2d3ec4 100644 --- a/src/db.h +++ b/src/db.h @@ -54,11 +54,11 @@ #error "Unknown path components." #endif -#define SUF_OBJS "objs" +#define SUF_OBJS "objs.toml" #define SUF_TEXT "text" -#define SUF_MEM "mem" -#define SUF_PLR "plr" -#define SUF_ACCT "acc" +#define SUF_MEM "mem.toml" +#define SUF_PLR "plr.toml" +#define SUF_ACCT "acc.toml" #if defined(CIRCLE_AMIGA) #define EXE_FILE "/bin/circle" /* maybe use argv[0] but it's not reliable */ @@ -77,8 +77,8 @@ #endif /* names of various files and directories */ -#define INDEX_FILE "index" /* index of world files */ -#define MINDEX_FILE "index.mini" /* ... and for mini-mud-mode */ +#define INDEX_FILE "index.toml" /* index of world files */ +#define MINDEX_FILE "index.mini.toml" /* ... and for mini-mud-mode */ #define WLD_PREFIX LIB_WORLD"wld"SLASH /* room definitions */ #define MOB_PREFIX LIB_WORLD"mob"SLASH /* monster prototypes */ #define OBJ_PREFIX LIB_WORLD"obj"SLASH /* object prototypes */ diff --git a/src/dg_olc.c b/src/dg_olc.c index 2bf8aae..4934e6a 100644 --- a/src/dg_olc.c +++ b/src/dg_olc.c @@ -113,6 +113,7 @@ ACMD(do_oasis_trigedit) void script_save_to_disk(FILE *fp, void *item, int type) { struct trig_proto_list *t; + int first = 1; if (type==MOB_TRIGGER) t = ((struct char_data *)item)->proto_script; @@ -125,11 +126,18 @@ void script_save_to_disk(FILE *fp, void *item, int type) return; } - while (t) - { - fprintf(fp,"T %d\n", t->vnum); + if (!t) + return; + + fprintf(fp, "triggers = ["); + while (t) { + if (!first) + fputs(", ", fp); + fprintf(fp, "%d", t->vnum); + first = 0; t = t->next; } + fputs("]\n", fp); } static void trigedit_setup_new(struct descriptor_data *d) @@ -852,9 +860,9 @@ void trigedit_save(struct descriptor_data *d) fclose(trig_file); #ifdef CIRCLE_MAC - snprintf(buf, sizeof(buf), "%s:%d.trg", TRG_PREFIX, zone); + snprintf(buf, sizeof(buf), "%s:%d.toml", TRG_PREFIX, zone); #else - snprintf(buf, sizeof(buf), "%s/%d.trg", TRG_PREFIX, zone); + snprintf(buf, sizeof(buf), "%s/%d.toml", TRG_PREFIX, zone); #endif remove(buf); diff --git a/src/dg_scripts.c b/src/dg_scripts.c index 1e98efc..5dad339 100644 --- a/src/dg_scripts.c +++ b/src/dg_scripts.c @@ -29,6 +29,8 @@ #include "genzon.h" /* for real_zone_by_thing */ #include "act.h" #include "modify.h" +#include "toml.h" +#include "toml_utils.h" #define PULSES_PER_MUD_HOUR (SECS_PER_MUD_HOUR*PASSES_PER_SEC) @@ -2826,11 +2828,11 @@ static struct cmdlist_element *find_done(struct cmdlist_element *cl) void read_saved_vars(struct char_data *ch) { FILE *file; - long context; char fn[127]; - char input_line[1024], *temp, *p; - char varname[32]; - char context_str[16]; + char errbuf[256]; + toml_table_t *tab = NULL; + toml_array_t *arr = NULL; + int i, count; /* If getting to the menu from inside the game, the vars aren't removed. So * let's not allocate them again. */ @@ -2851,22 +2853,44 @@ void read_saved_vars(struct char_data *ch) log("%s had no variable file", GET_NAME(ch)); return; } - /* walk through each line in the file parsing variables */ - do { - if (get_line(file, input_line)>0) { - p = temp = strdup(input_line); - temp = any_one_arg(temp, varname); - temp = any_one_arg(temp, context_str); - skip_spaces(&temp); /* temp now points to the rest of the line */ - context = atol(context_str); - add_var(&(SCRIPT(ch)->global_vars), varname, temp, context); - free(p); /* plug memory hole */ - } - } while( !feof(file) ); - - /* close the file and return */ + tab = toml_parse_file(file, errbuf, sizeof(errbuf)); fclose(file); + if (!tab) { + log("SYSERR: Could not parse variable file %s: %s", fn, errbuf); + return; + } + + arr = toml_array_in(tab, "var"); + if (!arr) { + toml_free(tab); + return; + } + + count = toml_array_nelem(arr); + for (i = 0; i < count; i++) { + toml_table_t *var_tab = toml_table_at(arr, i); + toml_datum_t name; + toml_datum_t value; + toml_datum_t context; + + if (!var_tab) + continue; + + name = toml_string_in(var_tab, "name"); + value = toml_string_in(var_tab, "value"); + context = toml_int_in(var_tab, "context"); + + if (!name.ok || !value.ok) + continue; + + add_var(&(SCRIPT(ch)->global_vars), name.u.s, value.u.s, + context.ok ? (long)context.u.i : 0); + free(name.u.s); + free(value.u.s); + } + + toml_free(tab); } /* save a characters variables out to disk */ @@ -2901,7 +2925,12 @@ void save_char_vars(struct char_data *ch) * future. */ while (vars) { if (*vars->name != '-') /* don't save if it begins with - */ - fprintf(file, "%s %ld %s\n", vars->name, vars->context, vars->value); + { + fprintf(file, "[[var]]\n"); + toml_write_kv_string(file, "name", vars->name); + fprintf(file, "context = %ld\n", vars->context); + toml_write_kv_string(file, "value", vars->value); + } vars = vars->next; } diff --git a/src/genmob.c b/src/genmob.c index adb1b12..d596bde 100644 --- a/src/genmob.c +++ b/src/genmob.c @@ -17,6 +17,7 @@ #include "genzon.h" #include "dg_olc.h" #include "spells.h" +#include "toml_utils.h" /* local functions */ static void extract_mobile_all(mob_vnum vnum); @@ -312,10 +313,9 @@ int save_mobiles(zone_rnum rznum) if (write_mobile_record(i, &mob_proto[rmob], mobfd) < 0) log("SYSERR: GenOLC: Error writing mobile #%d.", i); } - fputs("$\n", mobfd); written = ftell(mobfd); fclose(mobfd); - snprintf(usedfname, sizeof(usedfname), "%s%d.mob", MOB_PREFIX, vznum); + snprintf(usedfname, sizeof(usedfname), "%s%d.toml", MOB_PREFIX, vznum); remove(usedfname); rename(mobfname, usedfname); @@ -407,7 +407,6 @@ int write_mobile_record(mob_vnum mvnum, struct char_data *mob, FILE *fd) char ldesc[MAX_STRING_LENGTH]; char ddesc[MAX_STRING_LENGTH]; char bdesc[MAX_STRING_LENGTH]; - char buf[MAX_STRING_LENGTH]; int has_bdesc = 0; ldesc[MAX_STRING_LENGTH - 1] = '\0'; @@ -429,94 +428,71 @@ int write_mobile_record(mob_vnum mvnum, struct char_data *mob, FILE *fd) } else bdesc[0] = '\0'; - int n; - if (has_bdesc) { - n = snprintf(buf, MAX_STRING_LENGTH, - "#%d\n" - "%s%c\n" - "%s%c\n" - "%s%c\n" - "%s%c\n" - "%s%c\n" - "B\n" - "%s%c\n", - mvnum, - GET_NAME(mob), STRING_TERMINATOR, - GET_KEYWORDS(mob), STRING_TERMINATOR, - GET_SDESC(mob), STRING_TERMINATOR, - ldesc, STRING_TERMINATOR, - ddesc, STRING_TERMINATOR, - bdesc, STRING_TERMINATOR); - } else { - n = snprintf(buf, MAX_STRING_LENGTH, - "#%d\n" - "%s%c\n" - "%s%c\n" - "%s%c\n" - "%s%c\n" - "%s%c\n", - mvnum, - GET_NAME(mob), STRING_TERMINATOR, - GET_KEYWORDS(mob), STRING_TERMINATOR, - GET_SDESC(mob), STRING_TERMINATOR, - ldesc, STRING_TERMINATOR, - ddesc, STRING_TERMINATOR); - } + fprintf(fd, "[[mob]]\n"); + fprintf(fd, "vnum = %d\n", mvnum); + toml_write_kv_string(fd, "name", GET_NAME(mob)); + toml_write_kv_string(fd, "keywords", GET_KEYWORDS(mob)); + toml_write_kv_string(fd, "short", GET_SDESC(mob)); + toml_write_kv_string(fd, "long", ldesc); + toml_write_kv_string(fd, "description", ddesc); + if (has_bdesc) + toml_write_kv_string_opt(fd, "background", bdesc); - if (n >= MAX_STRING_LENGTH) { - mudlog(BRF, LVL_BUILDER, TRUE, - "SYSERR: Could not save mobile #%d due to size (%d > maximum of %d)", - mvnum, n, MAX_STRING_LENGTH); - return TRUE; - } - - fprintf(fd, "%s", convert_from_tabs(buf)); - - /* --- FLAGS/AFFECT/ALIGN line --- */ - fprintf(fd, - "%d %d %d %d %d %d %d %d %d E\n", + fprintf(fd, "flags = [%d, %d, %d, %d]\n", MOB_FLAGS(mob)[0], MOB_FLAGS(mob)[1], - MOB_FLAGS(mob)[2], MOB_FLAGS(mob)[3], + MOB_FLAGS(mob)[2], MOB_FLAGS(mob)[3]); + fprintf(fd, "aff_flags = [%d, %d, %d, %d]\n", AFF_FLAGS(mob)[0], AFF_FLAGS(mob)[1], - AFF_FLAGS(mob)[2], AFF_FLAGS(mob)[3], - GET_ALIGNMENT(mob)); + AFF_FLAGS(mob)[2], AFF_FLAGS(mob)[3]); + fprintf(fd, "alignment = %d\n", GET_ALIGNMENT(mob)); + toml_write_kv_string(fd, "mob_type", "enhanced"); - /* --- Level, hitdice, mana, move --- */ - fprintf(fd, "%d %dd%d+%d\n", - GET_LEVEL(mob), - GET_HIT(mob), - GET_MANA(mob), - GET_STAMINA(mob)); + fprintf(fd, "\n[mob.simple]\n"); + fprintf(fd, "level = %d\n", GET_LEVEL(mob)); + fprintf(fd, "hit_dice = %d\n", GET_HIT(mob)); + fprintf(fd, "mana_dice = %d\n", GET_MANA(mob)); + fprintf(fd, "stamina_dice = %d\n", GET_STAMINA(mob)); + fprintf(fd, "pos = %d\n", GET_POS(mob)); + fprintf(fd, "default_pos = %d\n", GET_DEFAULT_POS(mob)); + fprintf(fd, "sex = %d\n", GET_SEX(mob)); - /* --- Position / default position / sex --- */ - fprintf(fd, "%d %d %d\n", - GET_POS(mob), - GET_DEFAULT_POS(mob), - GET_SEX(mob)); + fprintf(fd, "\n[mob.enhanced]\n"); + fprintf(fd, "class = %d\n", GET_CLASS(mob)); + fprintf(fd, "species = %d\n", GET_SPECIES(mob)); + fprintf(fd, "age = %d\n", GET_ROLEPLAY_AGE(mob)); + fprintf(fd, "attack_type = %d\n", mob->mob_specials.attack_type); - /* --- Enhanced (E-spec + Skills) --- */ - if (write_mobile_espec(mvnum, mob, fd) < 0) - log("SYSERR: GenOLC: Error writing E-specs for mobile #%d.", mvnum); + fprintf(fd, "\n[mob.enhanced.abilities]\n"); + fprintf(fd, "str = %d\n", GET_STR(mob)); + fprintf(fd, "dex = %d\n", GET_DEX(mob)); + fprintf(fd, "con = %d\n", GET_CON(mob)); + fprintf(fd, "int = %d\n", GET_INT(mob)); + fprintf(fd, "wis = %d\n", GET_WIS(mob)); + fprintf(fd, "cha = %d\n", GET_CHA(mob)); + + fprintf(fd, "\n[mob.enhanced.saving_throws]\n"); + fprintf(fd, "str = %d\n", GET_SAVE(mob, ABIL_STR)); + fprintf(fd, "dex = %d\n", GET_SAVE(mob, ABIL_DEX)); + fprintf(fd, "con = %d\n", GET_SAVE(mob, ABIL_CON)); + fprintf(fd, "int = %d\n", GET_SAVE(mob, ABIL_INT)); + fprintf(fd, "wis = %d\n", GET_SAVE(mob, ABIL_WIS)); + fprintf(fd, "cha = %d\n", GET_SAVE(mob, ABIL_CHA)); /* Write NPC skills (if any set) */ for (int s = 0; s < MAX_SKILLS; s++) { - if (mob->mob_specials.skills[s] > 0) - fprintf(fd, "Skill %d %d\n", s, mob->mob_specials.skills[s]); + if (mob->mob_specials.skills[s] > 0) { + fprintf(fd, "\n[[mob.enhanced.skills]]\n"); + fprintf(fd, "id = %d\n", s); + fprintf(fd, "level = %d\n", mob->mob_specials.skills[s]); + } } - /* Write attack type (if set) */ - if (mob->mob_specials.attack_type > 0) - fprintf(fd, "AtkT %d\n", mob->mob_specials.attack_type); - - /* Single proper terminator */ - fprintf(fd, "E\n"); - - /* --- Loadout lines --- */ + /* --- Loadout entries --- */ for (struct mob_loadout *e = mob->proto_loadout; e; e = e->next) { - fprintf(fd, "L %d %d %d\n", - (int)e->wear_pos, - (int)e->vnum, - MAX(1, e->quantity)); + fprintf(fd, "\n[[mob.loadout]]\n"); + fprintf(fd, "wear_pos = %d\n", (int)e->wear_pos); + fprintf(fd, "vnum = %d\n", (int)e->vnum); + fprintf(fd, "quantity = %d\n", MAX(1, e->quantity)); } /* --- DG Scripts --- */ @@ -528,10 +504,11 @@ int write_mobile_record(mob_vnum mvnum, struct char_data *mob, FILE *fd) struct skin_yield_entry *sy; if (rmob != NOBODY && mob_index[rmob].skin_yields) { - fprintf(fd, "Y\n"); - for (sy = mob_index[rmob].skin_yields; sy; sy = sy->next) - fprintf(fd, "%d %d\n", sy->obj_vnum, sy->dc); - fprintf(fd, "0 0\n"); + for (sy = mob_index[rmob].skin_yields; sy; sy = sy->next) { + fprintf(fd, "\n[[mob.skin_yield]]\n"); + fprintf(fd, "obj_vnum = %d\n", sy->obj_vnum); + fprintf(fd, "dc = %d\n", sy->dc); + } } } @@ -540,6 +517,7 @@ int write_mobile_record(mob_vnum mvnum, struct char_data *mob, FILE *fd) log("SYSERR: GenOLC: Error writing MobProgs for mobile #%d.", mvnum); #endif + fputc('\n', fd); return TRUE; } diff --git a/src/genobj.c b/src/genobj.c index 85b9003..87c6d90 100644 --- a/src/genobj.c +++ b/src/genobj.c @@ -19,6 +19,7 @@ #include "handler.h" #include "interpreter.h" #include "boards.h" /* for board_info */ +#include "toml_utils.h" /* local functions */ @@ -178,10 +179,7 @@ obj_rnum index_object(struct obj_data *obj, obj_vnum ovnum, obj_rnum ornum) int save_objects(zone_rnum zone_num) { - char filename[128], buf[MAX_STRING_LENGTH], buf2[MAX_STRING_LENGTH]; - char ebuf1[MAX_STRING_LENGTH], ebuf2[MAX_STRING_LENGTH], ebuf3[MAX_STRING_LENGTH], ebuf4[MAX_STRING_LENGTH]; - char wbuf1[MAX_STRING_LENGTH], wbuf2[MAX_STRING_LENGTH], wbuf3[MAX_STRING_LENGTH], wbuf4[MAX_STRING_LENGTH]; - char pbuf1[MAX_STRING_LENGTH], pbuf2[MAX_STRING_LENGTH], pbuf3[MAX_STRING_LENGTH], pbuf4[MAX_STRING_LENGTH]; + char filename[128], buf[MAX_STRING_LENGTH]; int counter, counter2, realcounter; FILE *fp; struct obj_data *obj; @@ -210,54 +208,33 @@ int save_objects(zone_rnum zone_num) } else *buf = '\0'; - int n = snprintf(buf2, MAX_STRING_LENGTH, - "#%d\n" - "%s~\n" - "%s~\n" - "%s~\n" - "%s~\n", - - GET_OBJ_VNUM(obj), - (obj->name && *obj->name) ? obj->name : "undefined", - (obj->short_description && *obj->short_description) ? obj->short_description : "undefined", - (obj->description && *obj->description) ? obj->description : "undefined", - buf); - - if(n >= MAX_STRING_LENGTH) { - mudlog(BRF,LVL_BUILDER,TRUE, - "SYSERR: Could not save object #%d due to size (%d > maximum of %d).", - GET_OBJ_VNUM(obj), n, MAX_STRING_LENGTH); - continue; + fprintf(fp, "[[object]]\n"); + fprintf(fp, "vnum = %d\n", GET_OBJ_VNUM(obj)); + toml_write_kv_string(fp, "name", (obj->name && *obj->name) ? obj->name : "undefined"); + toml_write_kv_string(fp, "short", (obj->short_description && *obj->short_description) ? obj->short_description : "undefined"); + toml_write_kv_string(fp, "description", (obj->description && *obj->description) ? obj->description : "undefined"); + toml_write_kv_string(fp, "main_description", buf); + fprintf(fp, "type = %d\n", GET_OBJ_TYPE(obj)); + fprintf(fp, "extra_flags = [%d, %d, %d, %d]\n", + GET_OBJ_EXTRA(obj)[0], GET_OBJ_EXTRA(obj)[1], + GET_OBJ_EXTRA(obj)[2], GET_OBJ_EXTRA(obj)[3]); + fprintf(fp, "wear_flags = [%d, %d, %d, %d]\n", + GET_OBJ_WEAR(obj)[0], GET_OBJ_WEAR(obj)[1], + GET_OBJ_WEAR(obj)[2], GET_OBJ_WEAR(obj)[3]); + fprintf(fp, "affect_flags = [%d, %d, %d, %d]\n", + GET_OBJ_AFFECT(obj)[0], GET_OBJ_AFFECT(obj)[1], + GET_OBJ_AFFECT(obj)[2], GET_OBJ_AFFECT(obj)[3]); + fprintf(fp, "values = ["); + for (counter2 = 0; counter2 < NUM_OBJ_VAL_POSITIONS; counter2++) { + if (counter2) + fputs(", ", fp); + fprintf(fp, "%d", GET_OBJ_VAL(obj, counter2)); } - - fprintf(fp, "%s", convert_from_tabs(buf2)); - - sprintascii(ebuf1, GET_OBJ_EXTRA(obj)[0]); - sprintascii(ebuf2, GET_OBJ_EXTRA(obj)[1]); - sprintascii(ebuf3, GET_OBJ_EXTRA(obj)[2]); - sprintascii(ebuf4, GET_OBJ_EXTRA(obj)[3]); - sprintascii(wbuf1, GET_OBJ_WEAR(obj)[0]); - sprintascii(wbuf2, GET_OBJ_WEAR(obj)[1]); - sprintascii(wbuf3, GET_OBJ_WEAR(obj)[2]); - sprintascii(wbuf4, GET_OBJ_WEAR(obj)[3]); - sprintascii(pbuf1, GET_OBJ_AFFECT(obj)[0]); - sprintascii(pbuf2, GET_OBJ_AFFECT(obj)[1]); - sprintascii(pbuf3, GET_OBJ_AFFECT(obj)[2]); - sprintascii(pbuf4, GET_OBJ_AFFECT(obj)[3]); - - fprintf(fp, "%d %s %s %s %s %s %s %s %s %s %s %s %s\n" - "%d %d %d %d\n" - "%d %d %d %d %d\n", - - GET_OBJ_TYPE(obj), - ebuf1, ebuf2, ebuf3, ebuf4, - wbuf1, wbuf2, wbuf3, wbuf4, - pbuf1, pbuf2, pbuf3, pbuf4, - GET_OBJ_VAL(obj, 0), GET_OBJ_VAL(obj, 1), - GET_OBJ_VAL(obj, 2), GET_OBJ_VAL(obj, 3), - GET_OBJ_WEIGHT(obj), GET_OBJ_COST(obj), - GET_OBJ_COST_PER_DAY(obj), GET_OBJ_LEVEL(obj), GET_OBJ_TIMER(obj) - ); + fputs("]\n", fp); + fprintf(fp, "weight = %d\n", GET_OBJ_WEIGHT(obj)); + fprintf(fp, "cost = %d\n", GET_OBJ_COST(obj)); + fprintf(fp, "level = %d\n", GET_OBJ_LEVEL(obj)); + fprintf(fp, "timer = %d\n", GET_OBJ_TIMER(obj)); /* Do we have script(s) attached? */ script_save_to_disk(fp, obj, OBJ_TRIGGER); @@ -272,24 +249,25 @@ int save_objects(zone_rnum zone_num) } strncpy(buf, ex_desc->description, sizeof(buf) - 1); strip_cr(buf); - fprintf(fp, "E\n" - "%s~\n" - "%s~\n", ex_desc->keyword, buf); + fprintf(fp, "\n[[object.extra_desc]]\n"); + toml_write_kv_string(fp, "keyword", ex_desc->keyword); + toml_write_kv_string(fp, "description", buf); } } /* Do we have affects? */ - for (counter2 = 0; counter2 < MAX_OBJ_AFFECT; counter2++) - if (obj->affected[counter2].modifier) - fprintf(fp, "A\n" - "%d %d\n", obj->affected[counter2].location, - obj->affected[counter2].modifier); + for (counter2 = 0; counter2 < MAX_OBJ_AFFECT; counter2++) { + if (obj->affected[counter2].modifier) { + fprintf(fp, "\n[[object.affect]]\n"); + fprintf(fp, "location = %d\n", obj->affected[counter2].location); + fprintf(fp, "modifier = %d\n", obj->affected[counter2].modifier); + } + } + fputc('\n', fp); } } - /* Write the final line, close the file. */ - fprintf(fp, "$~\n"); fclose(fp); - snprintf(buf, sizeof(buf), "%s/%d.obj", OBJ_PREFIX, zone_table[zone_num].number); + snprintf(buf, sizeof(buf), "%s/%d.toml", OBJ_PREFIX, zone_table[zone_num].number); remove(buf); rename(filename, buf); @@ -615,4 +593,4 @@ void clamp_armor_values(struct obj_data *obj) { GET_OBJ_VAL(obj, VAL_ARMOR_MAGIC_BONUS) = v; /* flags are a bitvector; leave as-is (OLC will manage legal bits) */ -} \ No newline at end of file +} diff --git a/src/genolc.c b/src/genolc.c index ec4f65d..add026a 100644 --- a/src/genolc.c +++ b/src/genolc.c @@ -313,7 +313,7 @@ ACMD(do_export_zone) #else /* all other configurations */ zone_rnum zrnum; zone_vnum zvnum; - char sysbuf[MAX_INPUT_LENGTH]; + char sysbuf[MAX_STRING_LENGTH]; char zone_name[READ_SIZE], fixed_file_name[READ_SIZE]; int success, errorcode = 0; @@ -384,7 +384,7 @@ ACMD(do_export_zone) /* Tar the new copy. */ - snprintf(sysbuf, sizeof(sysbuf), "tar -cf %s%s.tar %sqq.info %sqq.wld %sqq.zon %sqq.mob %sqq.obj %sqq.trg %sqq.shp", path, fixed_file_name, path, path, path, path, path, path, path); + snprintf(sysbuf, sizeof(sysbuf), "tar -cf %s%s.tar %sqq.info %sqq.wld.toml %sqq.zon.toml %sqq.mob.toml %sqq.obj.toml %sqq.trg.toml %sqq.shp.toml %sqq.qst.toml", path, fixed_file_name, path, path, path, path, path, path, path, path); errorcode = system(sysbuf); if (errorcode) { send_to_char(ch, "Failed to tar files.\r\n"); @@ -425,8 +425,8 @@ static int export_info_file(zone_rnum zrnum) fprintf(info_file, "Implementation:\n"); fprintf(info_file, "1. All the files have been QQ'ed. This means all occurences of the zone number\n"); fprintf(info_file, " have been changed to QQ. In other words, if you decide to have this zone as\n"); - fprintf(info_file, " zone 123, replace all occurences of QQ with 123 and rename the qq.zon file\n"); - fprintf(info_file, " to 123.zon (etc.). And of course add 123.zon to the respective index file.\n"); + fprintf(info_file, " zone 123, replace all occurences of QQ with 123 and rename the qq.wld.toml file\n"); + fprintf(info_file, " to 123.toml (etc.). And of course add 123.toml to the respective index file.\n"); if (zone_exits) { fprintf(info_file, "2. Exits out of this zone have been ZZ'd. So all doors leading out have ZZ??\n"); fprintf(info_file, " instead of the room vnum (?? are numbers 00 - 99).\n"); @@ -481,7 +481,7 @@ static int export_save_shops(zone_rnum zrnum) FILE *shop_file; struct shop_data *shop; - if (!(shop_file = fopen("world/export/qq.shp", "w"))) { + if (!(shop_file = fopen("world/export/qq.shp.toml", "w"))) { mudlog(BRF, LVL_GOD, TRUE, "SYSERR: export_save_shops : Cannot open shop file!"); return FALSE; } else if (fprintf(shop_file, "CircleMUD v3.0 Shop File~\n") < 0) { @@ -571,7 +571,7 @@ static int export_save_mobiles(zone_rnum rznum) mob_vnum i; mob_rnum rmob; - if (!(mob_file = fopen("world/export/qq.mob", "w"))) { + if (!(mob_file = fopen("world/export/qq.mob.toml", "w"))) { mudlog(BRF, LVL_GOD, TRUE, "SYSERR: export_save_mobiles : Cannot open file!"); return FALSE; } @@ -648,7 +648,7 @@ static int export_save_zone(zone_rnum zrnum) int subcmd; FILE *zone_file; - if (!(zone_file = fopen("world/export/qq.zon", "w"))) { + if (!(zone_file = fopen("world/export/qq.zon.toml", "w"))) { mudlog(BRF, LVL_GOD, TRUE, "SYSERR: export_save_zone : Cannot open file!"); return FALSE; } @@ -783,7 +783,7 @@ static int export_save_objects(zone_rnum zrnum) struct obj_data *obj; struct extra_descr_data *ex_desc; - if (!(obj_file = fopen("world/export/qq.obj", "w"))) { + if (!(obj_file = fopen("world/export/qq.obj.toml", "w"))) { mudlog(BRF, LVL_GOD, TRUE, "SYSERR: export_save_objects : Cannot open file!"); return FALSE; } @@ -889,7 +889,7 @@ static int export_save_rooms(zone_rnum zrnum) char buf[MAX_STRING_LENGTH]; char buf1[MAX_STRING_LENGTH]; - if (!(room_file = fopen("world/export/qq.wld", "w"))) { + if (!(room_file = fopen("world/export/qq.wld.toml", "w"))) { mudlog(BRF, LVL_GOD, TRUE, "SYSERR: export_save_rooms : Cannot open file!"); return FALSE; } @@ -1028,7 +1028,7 @@ static int export_save_triggers(zone_rnum zrnum) FILE *trig_file; char bitBuf[MAX_INPUT_LENGTH]; - if (!(trig_file = fopen("world/export/qq.trg", "w"))) { + if (!(trig_file = fopen("world/export/qq.trg.toml", "w"))) { mudlog(BRF, LVL_GOD, TRUE, "SYSERR: export_save_triggers : Cannot open file!"); return FALSE; } diff --git a/src/genqst.c b/src/genqst.c index 730371a..954e4db 100644 --- a/src/genqst.c +++ b/src/genqst.c @@ -16,6 +16,7 @@ #include "quest.h" #include "genolc.h" #include "genzon.h" /* for create_world_index */ +#include "toml_utils.h" /*-------------------------------------------------------------------*/ @@ -176,10 +177,9 @@ int delete_quest(qst_rnum rnum) int save_quests(zone_rnum zone_num) { FILE *sf; - char filename[128], oldname[128], quest_flags[MAX_STRING_LENGTH]; + char filename[128], oldname[128]; char quest_desc[MAX_STRING_LENGTH], quest_info[MAX_STRING_LENGTH]; char quest_done[MAX_STRING_LENGTH], quest_quit[MAX_STRING_LENGTH]; - char buf[MAX_STRING_LENGTH]; int i, num_quests = 0; #if CIRCLE_UNSIGNED_INDEX @@ -219,53 +219,37 @@ int save_quests(zone_rnum zone_num) strip_cr(quest_done); strip_cr(quest_quit); /* Save the quest details to the file. */ - sprintascii(quest_flags, QST_FLAGS(rnum)); - int n = snprintf(buf, MAX_STRING_LENGTH, - "#%d\n" - "%s%c\n" - "%s%c\n" - "%s%c\n" - "%s%c\n" - "%s%c\n" - "%d %d %s %d %d %d %d\n" - "%d %d %d %d %d %d %d\n" - "%d %d %d\n" - "S\n", - QST_NUM(rnum), - QST_NAME(rnum) ? QST_NAME(rnum) : "Untitled", STRING_TERMINATOR, - quest_desc, STRING_TERMINATOR, - quest_info, STRING_TERMINATOR, - quest_done, STRING_TERMINATOR, - quest_quit, STRING_TERMINATOR, - QST_TYPE(rnum), - QST_MASTER(rnum) == NOBODY ? -1 : QST_MASTER(rnum), - quest_flags, - QST_TARGET(rnum) == NOTHING ? -1 : QST_TARGET(rnum), - QST_PREV(rnum) == NOTHING ? -1 : QST_PREV(rnum), - QST_NEXT(rnum) == NOTHING ? -1 : QST_NEXT(rnum), - QST_PREREQ(rnum) == NOTHING ? -1 : QST_PREREQ(rnum), - QST_POINTS(rnum), QST_PENALTY(rnum), QST_MINLEVEL(rnum), - QST_MAXLEVEL(rnum), QST_TIME(rnum), - QST_RETURNMOB(rnum) == NOBODY ? -1 : QST_RETURNMOB(rnum), - QST_QUANTITY(rnum), QST_COINS(rnum), QST_EXP(rnum), QST_OBJ(rnum) - ); - - if(n < MAX_STRING_LENGTH) { - fprintf(sf, "%s", convert_from_tabs(buf)); - num_quests++; - } else { - mudlog(BRF,LVL_BUILDER,TRUE, - "SYSERR: Could not save quest #%d due to size (%d > maximum of %d).", - QST_NUM(rnum), n, MAX_STRING_LENGTH); - } + fprintf(sf, "[[quest]]\n"); + fprintf(sf, "vnum = %d\n", QST_NUM(rnum)); + toml_write_kv_string(sf, "name", QST_NAME(rnum) ? QST_NAME(rnum) : "Untitled"); + toml_write_kv_string(sf, "description", quest_desc); + toml_write_kv_string(sf, "info", quest_info); + toml_write_kv_string(sf, "done", quest_done); + toml_write_kv_string(sf, "quit", quest_quit); + fprintf(sf, "type = %d\n", QST_TYPE(rnum)); + fprintf(sf, "quest_master = %d\n", QST_MASTER(rnum) == NOBODY ? -1 : QST_MASTER(rnum)); + fprintf(sf, "flags = %d\n", (int)QST_FLAGS(rnum)); + fprintf(sf, "target = %d\n", QST_TARGET(rnum) == NOTHING ? -1 : QST_TARGET(rnum)); + fprintf(sf, "prev_quest = %d\n", QST_PREV(rnum) == NOTHING ? -1 : QST_PREV(rnum)); + fprintf(sf, "next_quest = %d\n", QST_NEXT(rnum) == NOTHING ? -1 : QST_NEXT(rnum)); + fprintf(sf, "prereq = %d\n", QST_PREREQ(rnum) == NOTHING ? -1 : QST_PREREQ(rnum)); + fprintf(sf, "values = [%d, %d, %d, %d, %d, %d, %d]\n", + QST_POINTS(rnum), QST_PENALTY(rnum), QST_MINLEVEL(rnum), + QST_MAXLEVEL(rnum), QST_TIME(rnum), + QST_RETURNMOB(rnum) == NOBODY ? -1 : QST_RETURNMOB(rnum), + QST_QUANTITY(rnum)); + fprintf(sf, "[quest.rewards]\n"); + fprintf(sf, "coins = %d\n", QST_COINS(rnum)); + fprintf(sf, "exp = %d\n", QST_EXP(rnum)); + fprintf(sf, "obj_vnum = %d\n", QST_OBJ(rnum) == NOTHING ? -1 : QST_OBJ(rnum)); + fputc('\n', sf); + num_quests++; } } - /* Write the final line and close it. */ - fprintf(sf, "$~\n"); fclose(sf); /* Old file we're replacing. */ - snprintf(oldname, sizeof(oldname), "%s/%d.qst", + snprintf(oldname, sizeof(oldname), "%s/%d.toml", QST_PREFIX, zone_table[zone_num].number); remove(oldname); rename(filename, oldname); diff --git a/src/genshp.c b/src/genshp.c index ea29098..d565ec7 100644 --- a/src/genshp.c +++ b/src/genshp.c @@ -14,6 +14,7 @@ #include "genolc.h" #include "genshp.h" #include "genzon.h" +#include "toml_utils.h" /* NOTE (gg): Didn't modify sedit much. Don't consider it as 'recent' as the * other editors with regard to updates or style. */ @@ -345,7 +346,7 @@ int save_shops(zone_rnum zone_num) { int i, j, rshop, num_shops = 0; FILE *shop_file; - char fname[128], oldname[128], buf[MAX_STRING_LENGTH]; + char fname[128], oldname[128]; struct shop_data *shop; #if CIRCLE_UNSIGNED_INDEX @@ -361,77 +362,68 @@ int save_shops(zone_rnum zone_num) if (!(shop_file = fopen(fname, "w"))) { mudlog(BRF, LVL_GOD, TRUE, "SYSERR: OLC: Cannot open shop file!"); return FALSE; - } else if (fprintf(shop_file, "CircleMUD v3.0 Shop File~\n") < 0) { - mudlog(BRF, LVL_GOD, TRUE, "SYSERR: OLC: Cannot write to shop file!"); - fclose(shop_file); - return FALSE; } /* Search database for shops in this zone. */ for (i = genolc_zone_bottom(zone_num); i <= zone_table[zone_num].top; i++) { if ((rshop = real_shop(i)) != NOWHERE) { - fprintf(shop_file, "#%d~\n", i); shop = shop_index + rshop; - /* Save the products. */ - for (j = 0; S_PRODUCT(shop, j) != NOTHING; j++) - fprintf(shop_file, "%d\n", obj_index[S_PRODUCT(shop, j)].vnum); - fprintf(shop_file, "-1\n"); + fprintf(shop_file, "[[shop]]\n"); + fprintf(shop_file, "vnum = %d\n", i); - /* Save the rates. */ - fprintf(shop_file, "%1.2f\n" - "%1.2f\n", - S_BUYPROFIT(shop), - S_SELLPROFIT(shop)); + fprintf(shop_file, "products = ["); + for (j = 0; S_PRODUCT(shop, j) != NOTHING; j++) { + if (j) + fputs(", ", shop_file); + fprintf(shop_file, "%d", obj_index[S_PRODUCT(shop, j)].vnum); + } + fputs("]\n", shop_file); - /* Save the buy types and namelists. */ - for (j = 0;S_BUYTYPE(shop, j) != NOTHING; j++) - fprintf(shop_file, "%d%s\n", - S_BUYTYPE(shop, j), - S_BUYWORD(shop, j) ? S_BUYWORD(shop, j) : ""); - fprintf(shop_file, "-1\n"); + fprintf(shop_file, "buy_profit = %1.2f\n", S_BUYPROFIT(shop)); + fprintf(shop_file, "sell_profit = %1.2f\n", S_SELLPROFIT(shop)); - /* Save messages. Added some defaults as sanity checks. */ - sprintf(buf, - "%s~\n" - "%s~\n" - "%s~\n" - "%s~\n" - "%s~\n" - "%s~\n" - "%s~\n" - "%d\n" - "%ld\n" - "%d\n" - "%d\n", - S_NOITEM1(shop) ? S_NOITEM1(shop) : "%s Ke?!", - S_NOITEM2(shop) ? S_NOITEM2(shop) : "%s Ke?!", - S_NOBUY(shop) ? S_NOBUY(shop) : "%s Ke?!", - S_NOCASH1(shop) ? S_NOCASH1(shop) : "%s Ke?!", - S_NOCASH2(shop) ? S_NOCASH2(shop) : "%s Ke?!", - S_BUY(shop) ? S_BUY(shop) : "%s Ke?! %d?", - S_SELL(shop) ? S_SELL(shop) : "%s Ke?! %d?", - S_BROKE_TEMPER(shop), - S_BITVECTOR(shop), - S_KEEPER(shop) == NOBODY ? -1 : mob_index[S_KEEPER(shop)].vnum, - S_NOTRADE(shop) - ); - - fputs(convert_from_tabs(buf), shop_file); + for (j = 0; S_BUYTYPE(shop, j) != NOTHING; j++) { + fprintf(shop_file, "\n[[shop.buy_type]]\n"); + fprintf(shop_file, "type = %d\n", S_BUYTYPE(shop, j)); + toml_write_kv_string_opt(shop_file, "keyword", S_BUYWORD(shop, j)); + } - /* Save the rooms. */ - for (j = 0;S_ROOM(shop, j) != NOWHERE; j++) - fprintf(shop_file, "%d\n", S_ROOM(shop, j)); - fprintf(shop_file, "-1\n"); + fprintf(shop_file, "\n[shop.messages]\n"); + toml_write_kv_string(shop_file, "no_such_item1", S_NOITEM1(shop) ? S_NOITEM1(shop) : ""); + toml_write_kv_string(shop_file, "no_such_item2", S_NOITEM2(shop) ? S_NOITEM2(shop) : ""); + toml_write_kv_string(shop_file, "do_not_buy", S_NOBUY(shop) ? S_NOBUY(shop) : ""); + toml_write_kv_string(shop_file, "missing_cash1", S_NOCASH1(shop) ? S_NOCASH1(shop) : ""); + toml_write_kv_string(shop_file, "missing_cash2", S_NOCASH2(shop) ? S_NOCASH2(shop) : ""); + toml_write_kv_string(shop_file, "message_buy", S_BUY(shop) ? S_BUY(shop) : ""); + toml_write_kv_string(shop_file, "message_sell", S_SELL(shop) ? S_SELL(shop) : ""); - /* Save open/closing times. */ - fprintf(shop_file, "%d\n%d\n%d\n%d\n", S_OPEN1(shop), S_CLOSE1(shop), - S_OPEN2(shop), S_CLOSE2(shop)); + fprintf(shop_file, "broke_temper = %d\n", S_BROKE_TEMPER(shop)); + fprintf(shop_file, "bitvector = %ld\n", S_BITVECTOR(shop)); + fprintf(shop_file, "keeper = %d\n", + S_KEEPER(shop) == NOBODY ? -1 : mob_index[S_KEEPER(shop)].vnum); + fprintf(shop_file, "trade_with = %d\n", S_NOTRADE(shop)); + + fprintf(shop_file, "rooms = ["); + for (j = 0; S_ROOM(shop, j) != NOWHERE; j++) { + if (j) + fputs(", ", shop_file); + fprintf(shop_file, "%d", S_ROOM(shop, j)); + } + fputs("]\n", shop_file); + + fprintf(shop_file, "open1 = %d\n", S_OPEN1(shop)); + fprintf(shop_file, "close1 = %d\n", S_CLOSE1(shop)); + fprintf(shop_file, "open2 = %d\n", S_OPEN2(shop)); + fprintf(shop_file, "close2 = %d\n", S_CLOSE2(shop)); + fprintf(shop_file, "bank = %d\n", S_BANK(shop)); + fprintf(shop_file, "sort = %d\n", S_SORT(shop)); + + fputc('\n', shop_file); num_shops++; } } - fprintf(shop_file, "$~\n"); fclose(shop_file); - snprintf(oldname, sizeof(oldname), "%s/%d.shp", SHP_PREFIX, zone_table[zone_num].number); + snprintf(oldname, sizeof(oldname), "%s/%d.toml", SHP_PREFIX, zone_table[zone_num].number); remove(oldname); rename(fname, oldname); diff --git a/src/genwld.c b/src/genwld.c index 6788cbd..5e6aad0 100644 --- a/src/genwld.c +++ b/src/genwld.c @@ -18,6 +18,7 @@ #include "shop.h" #include "dg_olc.h" #include "mud_event.h" +#include "toml_utils.h" /* This function will copy the strings so be sure you free your own copies of @@ -269,7 +270,6 @@ int save_rooms(zone_rnum rzone) char filename[128]; char buf[MAX_STRING_LENGTH]; char buf1[MAX_STRING_LENGTH]; - char buf2[MAX_STRING_LENGTH]; #if CIRCLE_UNSIGNED_INDEX if (rzone == NOWHERE || rzone > top_of_zone_table) { @@ -301,96 +301,70 @@ int save_rooms(zone_rnum rzone) strncpy(buf, room->description ? room->description : "Empty room.", sizeof(buf)-1 ); strip_cr(buf); - /* Save the numeric and string section of the file. */ - int n = snprintf(buf2, MAX_STRING_LENGTH, "#%d\n" - "%s%c\n" - "%s%c\n" - "%d %d %d %d %d %d\n", - room->number, - room->name ? room->name : "Untitled", STRING_TERMINATOR, - buf, STRING_TERMINATOR, - zone_table[room->zone].number, room->room_flags[0], room->room_flags[1], room->room_flags[2], - room->room_flags[3], room->sector_type - ); - - if(n >= MAX_STRING_LENGTH) { - mudlog(BRF,LVL_BUILDER,TRUE, - "SYSERR: Could not save room #%d due to size (%d > maximum of %d).", - room->number, n, MAX_STRING_LENGTH); - continue; - } + fprintf(sf, "[[room]]\n"); + fprintf(sf, "vnum = %d\n", room->number); + toml_write_kv_string(sf, "name", room->name ? convert_from_tabs(room->name) : "Untitled"); + toml_write_kv_string(sf, "description", convert_from_tabs(buf)); + fprintf(sf, "flags = [%d, %d, %d, %d]\n", + room->room_flags[0], room->room_flags[1], + room->room_flags[2], room->room_flags[3]); + fprintf(sf, "sector = %d\n", room->sector_type); - fprintf(sf, "%s", convert_from_tabs(buf2)); - /* Now you write out the exits for the room. */ for (j = 0; j < DIR_COUNT; j++) { - if (R_EXIT(room, j)) { - int dflag; - if (R_EXIT(room, j)->general_description) { - strncpy(buf, R_EXIT(room, j)->general_description, sizeof(buf)-1); - strip_cr(buf); - } else - *buf = '\0'; + if (R_EXIT(room, j)) { + if (R_EXIT(room, j)->general_description) { + strncpy(buf, R_EXIT(room, j)->general_description, sizeof(buf)-1); + strip_cr(buf); + } else + *buf = '\0'; - /* Figure out door flag. */ - if (IS_SET(R_EXIT(room, j)->exit_info, EX_ISDOOR)) { - if (IS_SET(R_EXIT(room, j)->exit_info, EX_PICKPROOF)) - dflag = 2; - else - dflag = 1; - - if (IS_SET(R_EXIT(room, j)->exit_info, EX_HIDDEN)) - dflag += 2; - - } else - dflag = 0; + if (R_EXIT(room, j)->keyword) + strncpy(buf1, R_EXIT(room, j)->keyword, sizeof(buf1)-1 ); + else + *buf1 = '\0'; - if (R_EXIT(room, j)->keyword) - strncpy(buf1, R_EXIT(room, j)->keyword, sizeof(buf1)-1 ); - else - *buf1 = '\0'; - - /* Now write the exit to the file. */ - fprintf(sf, "D%d\n" - "%s~\n" - "%s~\n" - "%d %d %d\n", j, buf, buf1, dflag, - R_EXIT(room, j)->key != NOTHING ? R_EXIT(room, j)->key : -1, - R_EXIT(room, j)->to_room != NOWHERE ? world[R_EXIT(room, j)->to_room].number : -1); - - } + fprintf(sf, "\n[[room.exit]]\n"); + fprintf(sf, "dir = %d\n", j); + toml_write_kv_string(sf, "description", convert_from_tabs(buf)); + toml_write_kv_string(sf, "keyword", buf1); + fprintf(sf, "exit_info = %d\n", R_EXIT(room, j)->exit_info); + fprintf(sf, "key = %d\n", + R_EXIT(room, j)->key != NOTHING ? R_EXIT(room, j)->key : -1); + fprintf(sf, "to_room = %d\n", + R_EXIT(room, j)->to_room != NOWHERE ? world[R_EXIT(room, j)->to_room].number : -1); + } } if (room->ex_description) { struct extra_descr_data *xdesc; - for (xdesc = room->ex_description; xdesc; xdesc = xdesc->next) { - strncpy(buf, xdesc->description, sizeof(buf) - 1); - buf[sizeof(buf) - 1] = '\0'; - strip_cr(buf); - fprintf(sf, "E\n" - "%s~\n" - "%s~\n", xdesc->keyword, buf); - } + for (xdesc = room->ex_description; xdesc; xdesc = xdesc->next) { + strncpy(buf, xdesc->description, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + strip_cr(buf); + fprintf(sf, "\n[[room.extra_desc]]\n"); + toml_write_kv_string(sf, "keyword", xdesc->keyword); + toml_write_kv_string(sf, "description", convert_from_tabs(buf)); + } } if (room->forage) { struct forage_entry *entry; - fprintf(sf, "F\n"); - for (entry = room->forage; entry; entry = entry->next) - fprintf(sf, "%d %d\n", entry->obj_vnum, entry->dc); - fprintf(sf, "0 0\n"); + for (entry = room->forage; entry; entry = entry->next) { + fprintf(sf, "\n[[room.forage]]\n"); + fprintf(sf, "obj_vnum = %d\n", entry->obj_vnum); + fprintf(sf, "dc = %d\n", entry->dc); + } } - fprintf(sf, "S\n"); script_save_to_disk(sf, room, WLD_TRIGGER); + fputc('\n', sf); } } - /* Write the final line and close it. */ - fprintf(sf, "$~\n"); fclose(sf); /* Old file we're replacing. */ - snprintf(buf, sizeof(buf), "%s/%d.wld", WLD_PREFIX, zone_table[rzone].number); + snprintf(buf, sizeof(buf), "%s/%d.toml", WLD_PREFIX, zone_table[rzone].number); remove(buf); rename(filename, buf); diff --git a/src/genzon.c b/src/genzon.c index bf672ce..9099a6d 100644 --- a/src/genzon.c +++ b/src/genzon.c @@ -11,6 +11,8 @@ #include "utils.h" #include "db.h" #include "genolc.h" +#include "toml.h" +#include "toml_utils.h" #include "genzon.h" #include "dg_scripts.h" @@ -89,7 +91,7 @@ zone_rnum create_new_zone(zone_vnum vzone_num, room_vnum bottom, room_vnum top, } /* Create the zone file. */ - snprintf(buf, sizeof(buf), "%s/%d.zon", ZON_PREFIX, vzone_num); + snprintf(buf, sizeof(buf), "%s/%d.toml", ZON_PREFIX, vzone_num); if (!(fp = fopen(buf, "w"))) { mudlog(BRF, LVL_IMPL, TRUE, "SYSERR: OLC: Can't write new zone file."); *error = "Could not write zone file.\r\n"; @@ -99,7 +101,7 @@ zone_rnum create_new_zone(zone_vnum vzone_num, room_vnum bottom, room_vnum top, fclose(fp); /* Create the room file. */ - snprintf(buf, sizeof(buf), "%s/%d.wld", WLD_PREFIX, vzone_num); + snprintf(buf, sizeof(buf), "%s/%d.toml", WLD_PREFIX, vzone_num); if (!(fp = fopen(buf, "w"))) { mudlog(BRF, LVL_IMPL, TRUE, "SYSERR: OLC: Can't write new world file."); *error = "Could not write world file.\r\n"; @@ -109,7 +111,7 @@ zone_rnum create_new_zone(zone_vnum vzone_num, room_vnum bottom, room_vnum top, fclose(fp); /* Create the mobile file. */ - snprintf(buf, sizeof(buf), "%s/%d.mob", MOB_PREFIX, vzone_num); + snprintf(buf, sizeof(buf), "%s/%d.toml", MOB_PREFIX, vzone_num); if (!(fp = fopen(buf, "w"))) { mudlog(BRF, LVL_IMPL, TRUE, "SYSERR: OLC: Can't write new mob file."); *error = "Could not write mobile file.\r\n"; @@ -119,7 +121,7 @@ zone_rnum create_new_zone(zone_vnum vzone_num, room_vnum bottom, room_vnum top, fclose(fp); /* Create the object file. */ - snprintf(buf, sizeof(buf), "%s/%d.obj", OBJ_PREFIX, vzone_num); + snprintf(buf, sizeof(buf), "%s/%d.toml", OBJ_PREFIX, vzone_num); if (!(fp = fopen(buf, "w"))) { mudlog(BRF, LVL_IMPL, TRUE, "SYSERR: OLC: Can't write new obj file."); *error = "Could not write object file.\r\n"; @@ -129,7 +131,7 @@ zone_rnum create_new_zone(zone_vnum vzone_num, room_vnum bottom, room_vnum top, fclose(fp); /* Create the shop file. */ - snprintf(buf, sizeof(buf), "%s/%d.shp", SHP_PREFIX, vzone_num); + snprintf(buf, sizeof(buf), "%s/%d.toml", SHP_PREFIX, vzone_num); if (!(fp = fopen(buf, "w"))) { mudlog(BRF, LVL_IMPL, TRUE, "SYSERR: OLC: Can't write new shop file."); *error = "Could not write shop file.\r\n"; @@ -139,7 +141,7 @@ zone_rnum create_new_zone(zone_vnum vzone_num, room_vnum bottom, room_vnum top, fclose(fp); /* Create the quests file */ - snprintf(buf, sizeof(buf), "%s/%d.qst", QST_PREFIX, vzone_num); + snprintf(buf, sizeof(buf), "%s/%d.toml", QST_PREFIX, vzone_num); if (!(fp = fopen(buf, "w"))) { mudlog(BRF, LVL_IMPL, TRUE, "SYSERR: OLC: Can't write new quest file"); *error = "Could not write quest file.\r\n"; @@ -149,7 +151,7 @@ zone_rnum create_new_zone(zone_vnum vzone_num, room_vnum bottom, room_vnum top, fclose(fp); /* Create the trigger file. */ - snprintf(buf, sizeof(buf), "%s/%d.trg", TRG_PREFIX, vzone_num); + snprintf(buf, sizeof(buf), "%s/%d.toml", TRG_PREFIX, vzone_num); if (!(fp = fopen(buf, "w"))) { mudlog(BRF, LVL_IMPL, TRUE, "SYSERR: OLC: Can't write new trigger file"); *error = "Could not write trigger file.\r\n"; @@ -219,9 +221,12 @@ void create_world_index(int znum, const char *type) { FILE *newfile, *oldfile; char new_name[32], old_name[32], *prefix; - int num, found = FALSE; - char buf[MAX_STRING_LENGTH]; - char buf1[MAX_STRING_LENGTH]; + char **files = NULL; + char errbuf[256]; + char file_name[32]; + toml_table_t *tab = NULL; + toml_array_t *arr = NULL; + int i, num, count, insert_at; switch (*type) { case 'z': @@ -250,44 +255,98 @@ void create_world_index(int znum, const char *type) return; } - snprintf(old_name, sizeof(old_name), "%s/index", prefix); - snprintf(new_name, sizeof(new_name), "%s/newindex", prefix); + snprintf(old_name, sizeof(old_name), "%s/index.toml", prefix); + snprintf(new_name, sizeof(new_name), "%s/newindex.toml", prefix); + snprintf(file_name, sizeof(file_name), "%d.toml", znum); if (!(oldfile = fopen(old_name, "r"))) { mudlog(BRF, LVL_IMPL, TRUE, "SYSERR: OLC: Failed to open %s.", old_name); return; - } else if (!(newfile = fopen(new_name, "w"))) { - mudlog(BRF, LVL_IMPL, TRUE, "SYSERR: OLC: Failed to open %s.", new_name); - fclose(oldfile); + } + + tab = toml_parse_file(oldfile, errbuf, sizeof(errbuf)); + fclose(oldfile); + if (!tab) { + mudlog(BRF, LVL_IMPL, TRUE, "SYSERR: OLC: Failed to parse %s: %s.", old_name, errbuf); return; } - /* Index contents must be in order: search through the old file for the right - * place, insert the new file, then copy the rest over. */ - snprintf(buf1, sizeof(buf1), "%d.%s", znum, type); - while (get_line(oldfile, buf)) { - if (*buf == '$') { - /* The following used to add a blank line, thanks to Brian Taylor for the fix. */ - fprintf(newfile, "%s", (!found ? strncat(buf1, "\n$\n", sizeof(buf1) - strlen(buf1) - 1) : "$\n")); - break; - } else if (!found) { - sscanf(buf, "%d", &num); - if (num > znum) { - found = TRUE; - fprintf(newfile, "%s\n", buf1); - } else if (num == znum) { - /* index file already had an entry for this zone. */ - fclose(oldfile); - fclose(newfile); - remove(new_name); - return; - } - } - fprintf(newfile, "%s\n", buf); + arr = toml_array_in(tab, "files"); + if (!arr) { + mudlog(BRF, LVL_IMPL, TRUE, "SYSERR: OLC: %s missing 'files' array.", old_name); + toml_free(tab); + return; } + count = toml_array_nelem(arr); + CREATE(files, char *, count + 1); + for (i = 0; i < count; i++) { + toml_datum_t d = toml_string_at(arr, i); + if (!d.ok) { + mudlog(BRF, LVL_IMPL, TRUE, "SYSERR: OLC: %s has invalid 'files' entry at %d.", old_name, i); + toml_free(tab); + while (i-- > 0) + free(files[i]); + free(files); + return; + } + files[i] = d.u.s; + } + + for (i = 0; i < count; i++) { + if (!strcmp(files[i], file_name)) { + toml_free(tab); + for (i = 0; i < count; i++) + free(files[i]); + free(files); + return; + } + if (sscanf(files[i], "%d", &num) == 1 && num == znum) { + toml_free(tab); + for (i = 0; i < count; i++) + free(files[i]); + free(files); + return; + } + } + + insert_at = count; + for (i = 0; i < count; i++) { + if (sscanf(files[i], "%d", &num) == 1 && num > znum) { + insert_at = i; + break; + } + } + + RECREATE(files, char *, count + 2); + if (insert_at < count) + memmove(&files[insert_at + 1], &files[insert_at], sizeof(char *) * (count - insert_at)); + files[insert_at] = strdup(file_name); + count++; + + if (!(newfile = fopen(new_name, "w"))) { + mudlog(BRF, LVL_IMPL, TRUE, "SYSERR: OLC: Failed to open %s.", new_name); + toml_free(tab); + for (i = 0; i < count; i++) + free(files[i]); + free(files); + return; + } + + fprintf(newfile, "files = [\n"); + for (i = 0; i < count; i++) { + fprintf(newfile, " "); + toml_write_string(newfile, files[i]); + fprintf(newfile, "%s\n", (i + 1 < count) ? "," : ""); + } + fprintf(newfile, "]\n"); + fclose(newfile); - fclose(oldfile); + toml_free(tab); + for (i = 0; i < count; i++) + free(files[i]); + free(files); + /* Out with the old, in with the new. */ remove(old_name); rename(new_name, old_name); @@ -326,12 +385,9 @@ void remove_room_zone_commands(zone_rnum zone, room_rnum room_num) * field is also there. */ int save_zone(zone_rnum zone_num) { - int subcmd, arg1 = -1, arg2 = -1, arg3 = -1, flag_tot=0, i; + int subcmd, arg1 = -1, arg2 = -1, arg3 = -1; char fname[128], oldname[128]; - const char *comment = NULL; FILE *zfile; - char zbuf1[MAX_STRING_LENGTH], zbuf2[MAX_STRING_LENGTH]; - char zbuf3[MAX_STRING_LENGTH], zbuf4[MAX_STRING_LENGTH]; #if CIRCLE_UNSIGNED_INDEX if (zone_num == NOWHERE || zone_num > top_of_zone_table) { @@ -348,67 +404,25 @@ int save_zone(zone_rnum zone_num) return FALSE; } - for (i=0; ivnum; /* trigger vnum */ arg3 = world[ZCMD(zone_num, subcmd).arg3].number; /* room num */ - comment = GET_TRIG_NAME(trig_index[real_trigger(arg2)]->proto); break; case 'V': arg1 = ZCMD(zone_num, subcmd).arg1; /* trigger type */ @@ -472,17 +478,20 @@ int save_zone(zone_rnum zone_num) mudlog(BRF, LVL_BUILDER, TRUE, "SYSERR: OLC: z_save_to_disk(): Unknown cmd '%c' - NOT saving", ZCMD(zone_num, subcmd).command); continue; } - if (ZCMD(zone_num, subcmd).command != 'V') - fprintf(zfile, "%c %d %d %d %d \t(%s)\n", - ZCMD(zone_num, subcmd).command, ZCMD(zone_num, subcmd).if_flag, arg1, arg2, arg3, comment); - else - fprintf(zfile, "%c %d %d %d %d %s %s\n", - ZCMD(zone_num, subcmd).command, ZCMD(zone_num, subcmd).if_flag, arg1, arg2, arg3, - ZCMD(zone_num, subcmd).sarg1, ZCMD(zone_num, subcmd).sarg2); + fprintf(zfile, "\n[[zone.command]]\n"); + fprintf(zfile, "command = \"%c\"\n", ZCMD(zone_num, subcmd).command); + fprintf(zfile, "if_flag = %d\n", ZCMD(zone_num, subcmd).if_flag); + fprintf(zfile, "arg1 = %d\n", arg1); + fprintf(zfile, "arg2 = %d\n", arg2); + fprintf(zfile, "arg3 = %d\n", arg3); + if (ZCMD(zone_num, subcmd).command == 'V') { + toml_write_kv_string(zfile, "sarg1", ZCMD(zone_num, subcmd).sarg1 ? ZCMD(zone_num, subcmd).sarg1 : ""); + toml_write_kv_string(zfile, "sarg2", ZCMD(zone_num, subcmd).sarg2 ? ZCMD(zone_num, subcmd).sarg2 : ""); + } + fprintf(zfile, "line = %d\n", ZCMD(zone_num, subcmd).line); } - fputs("S\n$\n", zfile); fclose(zfile); - snprintf(oldname, sizeof(oldname), "%s/%d.zon", ZON_PREFIX, zone_table[zone_num].number); + snprintf(oldname, sizeof(oldname), "%s/%d.toml", ZON_PREFIX, zone_table[zone_num].number); remove(oldname); rename(fname, oldname); @@ -586,4 +595,3 @@ void delete_zone_command(struct zone_data *zone, int pos) /* Ok, let's zap it. */ remove_cmd_from_list(&zone->cmd, pos); } - diff --git a/src/house.c b/src/house.c index 75f4413..f183c94 100644 --- a/src/house.c +++ b/src/house.c @@ -48,7 +48,7 @@ static int House_get_filename(room_vnum vnum, char *filename, size_t maxlen) if (vnum == NOWHERE) return (0); - snprintf(filename, maxlen, LIB_HOUSE"%d.house", vnum); + snprintf(filename, maxlen, LIB_HOUSE"%d.house.toml", vnum); return (1); } @@ -710,8 +710,6 @@ static int ascii_convert_house(struct char_data *ch, obj_vnum vnum) } } - fprintf(out, "$~\n"); - fclose(in); fclose(out); diff --git a/src/objsave.c b/src/objsave.c index 5bc10a6..8fc8934 100644 --- a/src/objsave.c +++ b/src/objsave.c @@ -22,6 +22,8 @@ #include "config.h" #include "modify.h" #include "genolc.h" /* for strip_cr and sprintascii */ +#include "toml.h" +#include "toml_utils.h" /* these factors should be unique integers */ #define CRYO_FACTOR 4 @@ -37,106 +39,84 @@ static int Crash_load_objs(struct char_data *ch); static int handle_obj(struct obj_data *obj, struct char_data *ch, int locate, struct obj_data **cont_rows); static void Crash_write_header(struct char_data *ch, FILE *fp, int savecode); +static int toml_get_int_default(toml_table_t *tab, const char *key, int def) +{ + toml_datum_t d = toml_int_in(tab, key); + + if (!d.ok) + return def; + + return (int)d.u.i; +} + +static char *toml_get_string_dup(toml_table_t *tab, const char *key) +{ + toml_datum_t d = toml_string_in(tab, key); + + if (!d.ok) + return NULL; + + return d.u.s; +} + +static void toml_read_int_array(toml_array_t *arr, int *out, int out_count, int def) +{ + int i; + + for (i = 0; i < out_count; i++) + out[i] = def; + + if (!arr) + return; + + for (i = 0; i < out_count; i++) { + toml_datum_t d = toml_int_at(arr, i); + if (d.ok) + out[i] = (int)d.u.i; + } +} + /* Writes one object record to FILE. Old name: Obj_to_store(). * Updated to save all NUM_OBJ_VAL_POSITIONS values instead of only 4. */ int objsave_save_obj_record(struct obj_data *obj, FILE *fp, int locate) { int i; char buf1[MAX_STRING_LENGTH + 1]; - struct obj_data *temp = NULL; + int out_locate = (locate > 0) ? locate : 0; + int nest = (locate < 0) ? -locate : 0; - /* Build a prototype baseline to diff against so we only emit changed fields */ - if (GET_OBJ_VNUM(obj) != NOTHING) - temp = read_object(GET_OBJ_VNUM(obj), VIRTUAL); - else { - temp = create_obj(); - temp->item_number = NOWHERE; + fprintf(fp, "\n[[object]]\n"); + fprintf(fp, "vnum = %d\n", GET_OBJ_VNUM(obj)); + fprintf(fp, "locate = %d\n", out_locate); + fprintf(fp, "nest = %d\n", nest); + + fprintf(fp, "values = ["); + for (i = 0; i < NUM_OBJ_VAL_POSITIONS; i++) { + if (i > 0) + fputs(", ", fp); + fprintf(fp, "%d", GET_OBJ_VAL(obj, i)); } + fprintf(fp, "]\n"); - if (obj->main_description) { - strcpy(buf1, obj->main_description); + fprintf(fp, "extra_flags = [%d, %d, %d, %d]\n", + GET_OBJ_EXTRA(obj)[0], GET_OBJ_EXTRA(obj)[1], + GET_OBJ_EXTRA(obj)[2], GET_OBJ_EXTRA(obj)[3]); + fprintf(fp, "wear_flags = [%d, %d, %d, %d]\n", + GET_OBJ_WEAR(obj)[0], GET_OBJ_WEAR(obj)[1], + GET_OBJ_WEAR(obj)[2], GET_OBJ_WEAR(obj)[3]); + + toml_write_kv_string_opt(fp, "name", obj->name); + toml_write_kv_string_opt(fp, "short", obj->short_description); + toml_write_kv_string_opt(fp, "description", obj->description); + if (obj->main_description && *obj->main_description) { + strlcpy(buf1, obj->main_description, sizeof(buf1)); strip_cr(buf1); - } else - *buf1 = 0; - - /* Header and placement */ - fprintf(fp, "#%d\n", GET_OBJ_VNUM(obj)); - - /* Top-level worn slots are positive (1..NUM_WEARS); inventory is 0. - * Children use negative numbers from Crash_save recursion (…,-1,-2,…) — we map that to Nest. */ - if (locate > 0) - fprintf(fp, "Loc : %d\n", locate); - - if (locate < 0) { - int nest = -locate; /* e.g. -1 => Nest:1, -2 => Nest:2, etc. */ - fprintf(fp, "Nest: %d\n", nest); - } else { - fprintf(fp, "Nest: %d\n", 0); /* top-level object (inventory or worn) */ + toml_write_kv_string(fp, "main_description", buf1); } - /* Save all object values (diffed against proto) */ - { - bool diff = FALSE; - for (i = 0; i < NUM_OBJ_VAL_POSITIONS; i++) { - if (GET_OBJ_VAL(obj, i) != GET_OBJ_VAL(temp, i)) { - diff = TRUE; - break; - } - } - if (diff) { - fprintf(fp, "Vals:"); - for (i = 0; i < NUM_OBJ_VAL_POSITIONS; i++) - fprintf(fp, " %d", GET_OBJ_VAL(obj, i)); - fprintf(fp, "\n"); - } - } - - /* Extra flags (array words) */ - if (GET_OBJ_EXTRA(obj) != GET_OBJ_EXTRA(temp)) - fprintf(fp, "Flag: %d %d %d %d\n", - GET_OBJ_EXTRA(obj)[0], GET_OBJ_EXTRA(obj)[1], - GET_OBJ_EXTRA(obj)[2], GET_OBJ_EXTRA(obj)[3]); - - /* Names/descriptions */ - if (obj->name && (!temp->name || strcmp(obj->name, temp->name))) - fprintf(fp, "Name: %s\n", obj->name); - if (obj->short_description && (!temp->short_description || - strcmp(obj->short_description, temp->short_description))) - fprintf(fp, "Shrt: %s\n", obj->short_description); - if (obj->description && (!temp->description || - strcmp(obj->description, temp->description))) - fprintf(fp, "Desc: %s\n", obj->description); - if (obj->main_description && (!temp->main_description || - strcmp(obj->main_description, temp->main_description))) - fprintf(fp, "ADes:\n%s~\n", buf1); - - /* Core fields */ - if (GET_OBJ_TYPE(obj) != GET_OBJ_TYPE(temp)) - fprintf(fp, "Type: %d\n", GET_OBJ_TYPE(obj)); - if (GET_OBJ_WEIGHT(obj) != GET_OBJ_WEIGHT(temp)) - fprintf(fp, "Wght: %d\n", GET_OBJ_WEIGHT(obj)); - if (GET_OBJ_COST(obj) != GET_OBJ_COST(temp)) - fprintf(fp, "Cost: %d\n", GET_OBJ_COST(obj)); - - /* Permanent affects (array words) */ - if (GET_OBJ_AFFECT(obj)[0] != GET_OBJ_AFFECT(temp)[0] || - GET_OBJ_AFFECT(obj)[1] != GET_OBJ_AFFECT(temp)[1] || - GET_OBJ_AFFECT(obj)[2] != GET_OBJ_AFFECT(temp)[2] || - GET_OBJ_AFFECT(obj)[3] != GET_OBJ_AFFECT(temp)[3]) - fprintf(fp, "Perm: %d %d %d %d\n", - GET_OBJ_AFFECT(obj)[0], GET_OBJ_AFFECT(obj)[1], - GET_OBJ_AFFECT(obj)[2], GET_OBJ_AFFECT(obj)[3]); - - /* Wear flags (array words) */ - if (GET_OBJ_WEAR(obj)[0] != GET_OBJ_WEAR(temp)[0] || - GET_OBJ_WEAR(obj)[1] != GET_OBJ_WEAR(temp)[1] || - GET_OBJ_WEAR(obj)[2] != GET_OBJ_WEAR(temp)[2] || - GET_OBJ_WEAR(obj)[3] != GET_OBJ_WEAR(temp)[3]) - fprintf(fp, "Wear: %d %d %d %d\n", - GET_OBJ_WEAR(obj)[0], GET_OBJ_WEAR(obj)[1], - GET_OBJ_WEAR(obj)[2], GET_OBJ_WEAR(obj)[3]); - - /* (If you also persist applies, extra descs, scripts, etc., keep that code here unchanged) */ + fprintf(fp, "type = %d\n", GET_OBJ_TYPE(obj)); + fprintf(fp, "weight = %d\n", GET_OBJ_WEIGHT(obj)); + fprintf(fp, "cost = %d\n", GET_OBJ_COST(obj)); return 1; } @@ -266,10 +246,11 @@ int Crash_delete_file(char *name) int Crash_delete_crashfile(struct char_data *ch) { char filename[MAX_INPUT_LENGTH]; - int numread; FILE *fl; + char errbuf[256]; + toml_table_t *tab = NULL; + toml_table_t *header = NULL; int savecode; - char line[READ_SIZE]; if (!get_filename(filename, sizeof(filename), CRASH_FILE, GET_NAME(ch))) return FALSE; @@ -279,12 +260,19 @@ int Crash_delete_crashfile(struct char_data *ch) log("SYSERR: checking for crash file %s (3): %s", filename, strerror(errno)); return FALSE; } - numread = get_line(fl,line); + tab = toml_parse_file(fl, errbuf, sizeof(errbuf)); fclose(fl); - if (numread == FALSE) + if (!tab) return FALSE; - sscanf(line,"%d ",&savecode); + + header = toml_table_in(tab, "header"); + if (!header) { + toml_free(tab); + return FALSE; + } + savecode = toml_get_int_default(header, "save_code", SAVE_UNDEF); + toml_free(tab); if (savecode == SAVE_CRASH) Crash_delete_file(GET_NAME(ch)); @@ -295,10 +283,11 @@ int Crash_delete_crashfile(struct char_data *ch) int Crash_clean_file(char *name) { char filename[MAX_INPUT_LENGTH], filetype[20]; - int numread; FILE *fl; - int savecode, timed, netcost, coins, account, nitems; - char line[READ_SIZE]; + char errbuf[256]; + toml_table_t *tab = NULL; + toml_table_t *header = NULL; + int savecode, timed; if (!get_filename(filename, sizeof(filename), CRASH_FILE, name)) return FALSE; @@ -310,13 +299,20 @@ int Crash_clean_file(char *name) return FALSE; } - numread = get_line(fl,line); + tab = toml_parse_file(fl, errbuf, sizeof(errbuf)); fclose(fl); - if (numread == FALSE) + if (!tab) return FALSE; - sscanf(line, "%d %d %d %d %d %d",&savecode,&timed,&netcost, - &coins,&account,&nitems); + header = toml_table_in(tab, "header"); + if (!header) { + toml_free(tab); + return FALSE; + } + + savecode = toml_get_int_default(header, "save_code", SAVE_UNDEF); + timed = toml_get_int_default(header, "timed", 0); + toml_free(tab); if ((savecode == SAVE_CRASH) || (savecode == SAVE_LOGOUT) || @@ -403,7 +399,13 @@ static void Crash_write_header(struct char_data *ch, FILE *fp, int savecode) int account = ch ? GET_BANK_COINS(ch) : 0; int nitems = 0; - fprintf(fp, "%d %d %d %d %d %d\n", savecode, timed, netcost, coins, account, nitems); + fprintf(fp, "[header]\n"); + fprintf(fp, "save_code = %d\n", savecode); + fprintf(fp, "timed = %d\n", timed); + fprintf(fp, "net_cost = %d\n", netcost); + fprintf(fp, "coins = %d\n", coins); + fprintf(fp, "account = %d\n", account); + fprintf(fp, "item_count = %d\n", nitems); } void Crash_crashsave(struct char_data *ch) @@ -438,7 +440,6 @@ void Crash_crashsave(struct char_data *ch) } Crash_restore_weight(ch->carrying); - fprintf(fp, "$~\n"); fclose(fp); REMOVE_BIT_AR(PLR_FLAGS(ch), PLR_CRASH); } @@ -476,7 +477,6 @@ void Crash_idlesave(struct char_data *ch) } Crash_restore_weight(ch->carrying); - fprintf(fp, "$~\n"); fclose(fp); REMOVE_BIT_AR(PLR_FLAGS(ch), PLR_CRASH); } @@ -514,7 +514,6 @@ void Crash_rentsave(struct char_data *ch, int cost) } Crash_restore_weight(ch->carrying); - fprintf(fp, "$~\n"); fclose(fp); REMOVE_BIT_AR(PLR_FLAGS(ch), PLR_CRASH); } @@ -553,100 +552,32 @@ void Crash_save_all(void) } } -/* Load all objects from file into memory. Updated to load NUM_OBJ_VAL_POSITIONS values. */ -obj_save_data *objsave_parse_objects(FILE *fl) +/* Load all objects from TOML into memory. */ +static obj_save_data *objsave_parse_objects_from_toml(toml_table_t *tab, const char *filename) { - char line[MAX_STRING_LENGTH]; - obj_save_data *head = NULL, *tail = NULL; + toml_array_t *arr = NULL; + int count, i; - /* State for the object we’re currently assembling */ - struct obj_data *temp = NULL; - int pending_locate = 0; /* 0 = inventory, 1..NUM_WEARS = worn slot */ - int pending_nest = 0; /* 0 = top-level; >0 = inside container at level-1 */ + arr = toml_array_in(tab, "object"); + if (!arr) + return NULL; - /* --- helpers (GCC nested functions OK in tbaMUD build) ---------------- */ + count = toml_array_nelem(arr); + for (i = 0; i < count; i++) { + toml_table_t *obj_tab = toml_table_at(arr, i); + struct obj_data *temp = NULL; + int locate, nest; + int values[NUM_OBJ_VAL_POSITIONS]; + int flags[4]; + char *str = NULL; - /* append current object to the result list with proper locate */ - void commit_current(void) { - if (!temp) return; + if (!obj_tab) + continue; - if (GET_OBJ_TYPE(temp) == ITEM_MONEY) - update_money_obj(temp); - - /* sanitize top-level locate range only; children will be negative later */ - int loc = pending_locate; - if (pending_nest <= 0) { - if (loc < 0 || loc > NUM_WEARS) { - mudlog(NRM, LVL_IMMORT, TRUE, - "SAVE-LOAD: bad locate %d for vnum %d; defaulting to inventory.", - loc, GET_OBJ_VNUM(temp)); - loc = 0; - } - } - - /* convert Nest>0 into negative locate for handle_obj()/cont_row */ - int effective_loc = (pending_nest > 0) ? -pending_nest : loc; - - obj_save_data *node = NULL; - CREATE(node, obj_save_data, 1); - node->obj = temp; - node->locate = effective_loc; - node->next = NULL; - - if (!head) head = node, tail = node; - else tail->next = node, tail = node; - - temp = NULL; - pending_locate = 0; - pending_nest = 0; - } - - /* split a line into normalized tag (no colon) and payload pointer */ - void split_tag_line(const char *src, char tag_out[6], const char **payload_out) { - const char *s = src; - - while (*s && isspace((unsigned char)*s)) s++; /* skip leading ws */ - - const char *te = s; - while (*te && !isspace((unsigned char)*te) && *te != ':') te++; - - size_t tlen = (size_t)(te - s); - if (tlen > 5) tlen = 5; - memcpy(tag_out, s, tlen); - tag_out[tlen] = '\0'; - - const char *p = te; - while (*p && isspace((unsigned char)*p)) p++; - if (*p == ':') { - p++; - while (*p && isspace((unsigned char)*p)) p++; - } - *payload_out = p; - } - - /* ---------------------------------------------------------------------- */ - - while (get_line(fl, line)) { - if (!*line) continue; - - /* New object header: "#" (commit any previous one first) */ - if (*line == '#') { - if (temp) commit_current(); - - long vnum = -1L; - vnum = strtol(line + 1, NULL, 10); - - if (vnum <= 0 && vnum != -1L) { - mudlog(NRM, LVL_IMMORT, TRUE, "SAVE-LOAD: bad vnum header: '%s'", line); - temp = NULL; - pending_locate = 0; - pending_nest = 0; - continue; - } - - /* Instantiate from prototype if available, else create a blank */ - if (vnum == -1L) { + { + int vnum = toml_get_int_default(obj_tab, "vnum", -1); + if (vnum == -1) { temp = create_obj(); temp->item_number = NOTHING; if (!temp->name) temp->name = strdup("object"); @@ -658,133 +589,122 @@ obj_save_data *objsave_parse_objects(FILE *fl) temp = read_object(rnum, REAL); } else { temp = create_obj(); - /* Do NOT assign GET_OBJ_VNUM(temp); item_number derives vnum. */ if (!temp->name) temp->name = strdup("object"); if (!temp->short_description) temp->short_description = strdup("an object"); if (!temp->description) temp->description = strdup("An object lies here."); } } - - pending_locate = 0; - pending_nest = 0; - continue; } - /* Normal data line: TAG [ : ] payload */ - char tag[6]; - const char *payload = NULL; - split_tag_line(line, tag, &payload); + locate = toml_get_int_default(obj_tab, "locate", 0); + if (locate < 0 || locate > NUM_WEARS) + locate = 0; - if (!*tag) continue; - if (!temp) { - mudlog(NRM, LVL_IMMORT, TRUE, "SAVE-LOAD: data before header ignored: '%s'", line); - continue; - } - - if (!strcmp(tag, "Loc")) { - pending_locate = (int)strtol(payload, NULL, 10); - } - else if (!strcmp(tag, "Nest")) { - pending_nest = (int)strtol(payload, NULL, 10); - if (pending_nest < 0) pending_nest = 0; - if (pending_nest > MAX_BAG_ROWS) { + nest = toml_get_int_default(obj_tab, "nest", 0); + if (nest < 0) + nest = 0; + if (nest > MAX_BAG_ROWS) { + if (filename) + mudlog(NRM, LVL_IMMORT, TRUE, + "SAVE-LOAD: nest level %d too deep in %s; clamping to %d.", + nest, filename, MAX_BAG_ROWS); + else mudlog(NRM, LVL_IMMORT, TRUE, "SAVE-LOAD: nest level %d too deep; clamping to %d.", - pending_nest, MAX_BAG_ROWS); - pending_nest = MAX_BAG_ROWS; - } + nest, MAX_BAG_ROWS); + nest = MAX_BAG_ROWS; } - else if (!strcmp(tag, "Vals")) { - const char *p = payload; - for (int i = 0; i < NUM_OBJ_VAL_POSITIONS; i++) { - if (!*p) { GET_OBJ_VAL(temp, i) = 0; continue; } - GET_OBJ_VAL(temp, i) = (int)strtol(p, (char **)&p, 10); - } - } - else if (!strcmp(tag, "Wght")) { - GET_OBJ_WEIGHT(temp) = (int)strtol(payload, NULL, 10); - } - else if (!strcmp(tag, "Cost")) { - GET_OBJ_COST(temp) = (int)strtol(payload, NULL, 10); - } - else if (!strcmp(tag, "Rent")) { - /* Legacy tag ignored (cost-per-day no longer used). */ - } - else if (!strcmp(tag, "Type")) { - GET_OBJ_TYPE(temp) = (int)strtol(payload, NULL, 10); - } - else if (!strcmp(tag, "Wear")) { - unsigned long words[4] = {0,0,0,0}; - const char *p = payload; - for (int i = 0; i < 4 && *p; i++) words[i] = strtoul(p, (char **)&p, 10); -#if defined(TW_ARRAY_MAX) && defined(GET_OBJ_WEAR_AR) - for (int i = 0; i < 4; i++) { - if (i < TW_ARRAY_MAX) GET_OBJ_WEAR_AR(temp, i) = (bitvector_t)words[i]; - else if (words[i]) - mudlog(NRM, LVL_IMMORT, TRUE, - "SAVE-LOAD: Wear word %d (%lu) truncated (TW_ARRAY_MAX=%d).", - i, words[i], TW_ARRAY_MAX); - } -#elif defined(GET_OBJ_WEAR_AR) - for (int i = 0; i < 4; i++) GET_OBJ_WEAR_AR(temp, i) = (bitvector_t)words[i]; -#endif - } - else if (!strcmp(tag, "Flag")) { - unsigned long words[4] = {0,0,0,0}; - const char *p = payload; - for (int i = 0; i < 4 && *p; i++) words[i] = strtoul(p, (char **)&p, 10); + toml_read_int_array(toml_array_in(obj_tab, "values"), values, NUM_OBJ_VAL_POSITIONS, 0); + for (int j = 0; j < NUM_OBJ_VAL_POSITIONS; j++) + GET_OBJ_VAL(temp, j) = values[j]; -#if defined(EF_ARRAY_MAX) && defined(GET_OBJ_EXTRA_AR) - for (int i = 0; i < 4; i++) { - if (i < EF_ARRAY_MAX) GET_OBJ_EXTRA_AR(temp, i) = (bitvector_t)words[i]; - else if (words[i]) - mudlog(NRM, LVL_IMMORT, TRUE, - "SAVE-LOAD: Extra word %d (%lu) truncated (EF_ARRAY_MAX=%d).", - i, words[i], EF_ARRAY_MAX); - } -#elif defined(GET_OBJ_EXTRA_AR) - for (int i = 0; i < 4; i++) GET_OBJ_EXTRA_AR(temp, i) = (bitvector_t)words[i]; -#endif + toml_read_int_array(toml_array_in(obj_tab, "extra_flags"), flags, 4, 0); + for (int j = 0; j < 4; j++) + GET_OBJ_EXTRA(temp)[j] = flags[j]; + + toml_read_int_array(toml_array_in(obj_tab, "wear_flags"), flags, 4, 0); + for (int j = 0; j < 4; j++) + GET_OBJ_WEAR(temp)[j] = flags[j]; + + str = toml_get_string_dup(obj_tab, "name"); + if (str) { + if (temp->name) + free(temp->name); + temp->name = str; } - else if (!strcmp(tag, "Name")) { - if (temp->name) free(temp->name); - temp->name = *payload ? strdup(payload) : strdup("object"); + + str = toml_get_string_dup(obj_tab, "short"); + if (str) { + if (temp->short_description) + free(temp->short_description); + temp->short_description = str; } - else if (!strcmp(tag, "Shrt")) { - if (temp->short_description) free(temp->short_description); - temp->short_description = *payload ? strdup(payload) : strdup("an object"); + + str = toml_get_string_dup(obj_tab, "description"); + if (str) { + if (temp->description) + free(temp->description); + temp->description = str; } - else if (!strcmp(tag, "Desc")) { - if (temp->description) free(temp->description); - temp->description = *payload ? strdup(payload) : strdup("An object lies here."); + + str = toml_get_string_dup(obj_tab, "main_description"); + if (str) { + if (temp->main_description) + free(temp->main_description); + temp->main_description = str; } - else if (!strcmp(tag, "ADes")) { - if (temp->main_description) free(temp->main_description); - temp->main_description = *payload ? strdup(payload) : NULL; - } - else if (!strcmp(tag, "End")) { - commit_current(); - } - else { - mudlog(NRM, LVL_IMMORT, TRUE, "SAVE-LOAD: unknown tag '%s'", tag); + + GET_OBJ_TYPE(temp) = toml_get_int_default(obj_tab, "type", GET_OBJ_TYPE(temp)); + GET_OBJ_WEIGHT(temp) = toml_get_int_default(obj_tab, "weight", GET_OBJ_WEIGHT(temp)); + GET_OBJ_COST(temp) = toml_get_int_default(obj_tab, "cost", GET_OBJ_COST(temp)); + + if (GET_OBJ_TYPE(temp) == ITEM_MONEY) + update_money_obj(temp); + + { + int effective_loc = (nest > 0) ? -nest : locate; + obj_save_data *node = NULL; + CREATE(node, obj_save_data, 1); + node->obj = temp; + node->locate = effective_loc; + node->next = NULL; + + if (!head) + head = node, tail = node; + else + tail->next = node, tail = node; } } - if (temp) commit_current(); - return head; } +obj_save_data *objsave_parse_objects(FILE *fl) +{ + char errbuf[256]; + toml_table_t *tab = toml_parse_file(fl, errbuf, sizeof(errbuf)); + obj_save_data *list = NULL; + + if (!tab) + return NULL; + + list = objsave_parse_objects_from_toml(tab, NULL); + toml_free(tab); + return list; +} + static int Crash_load_objs(struct char_data *ch) { FILE *fl; char filename[PATH_MAX]; - char line[READ_SIZE]; char buf[MAX_STRING_LENGTH]; int i, orig_save_code, num_objs=0; struct obj_data *cont_row[MAX_BAG_ROWS]; int savecode = SAVE_UNDEF; - int timed=0,netcost=0,coins,account,nitems; + int timed=0,netcost=0,coins=0,account=0,nitems=0; + char errbuf[256]; + toml_table_t *tab = NULL; + toml_table_t *header = NULL; obj_save_data *loaded, *current; if (!get_filename(filename, sizeof(filename), CRASH_FILE, GET_NAME(ch))) @@ -810,10 +730,28 @@ static int Crash_load_objs(struct char_data *ch) { return 1; } - if (!get_line(fl, line)) - mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, "Failed to read player's save code: %s.", GET_NAME(ch)); - else - sscanf(line,"%d %d %d %d %d %d",&savecode, &timed, &netcost,&coins,&account,&nitems); + tab = toml_parse_file(fl, errbuf, sizeof(errbuf)); + fclose(fl); + if (!tab) { + mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, + "Failed to parse player's object file %s: %s.", filename, errbuf); + return 1; + } + + header = toml_table_in(tab, "header"); + if (!header) { + mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, + "Player object file %s missing header.", filename); + toml_free(tab); + return 1; + } + + savecode = toml_get_int_default(header, "save_code", SAVE_UNDEF); + timed = toml_get_int_default(header, "timed", 0); + netcost = toml_get_int_default(header, "net_cost", 0); + coins = toml_get_int_default(header, "coins", 0); + account = toml_get_int_default(header, "account", 0); + nitems = toml_get_int_default(header, "item_count", 0); if (savecode == SAVE_LOGOUT || savecode == SAVE_TIMEDOUT) { mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(ch)), TRUE, @@ -844,7 +782,8 @@ static int Crash_load_objs(struct char_data *ch) { break; } - loaded = objsave_parse_objects(fl); + loaded = objsave_parse_objects_from_toml(tab, filename); + toml_free(tab); for (current = loaded; current != NULL; current=current->next) num_objs += handle_obj(current->obj, ch, current->locate, cont_row); @@ -874,8 +813,6 @@ static int Crash_load_objs(struct char_data *ch) { mudlog(NRM, MAX(LVL_GOD, GET_INVIS_LEV(ch)), TRUE, "%s (level %d) has %d object%s.", GET_NAME(ch), GET_LEVEL(ch), num_objs, num_objs != 1 ? "s" : ""); - fclose(fl); - if ((orig_save_code == SAVE_LOGOUT) || (orig_save_code == SAVE_CRYO)) return 0; else diff --git a/src/players.c b/src/players.c index 9fffa7c..edb6f66 100644 --- a/src/players.c +++ b/src/players.c @@ -22,6 +22,8 @@ #include "config.h" /* for pclean_criteria[] */ #include "dg_scripts.h" /* To enable saving of player variables to disk */ #include "quest.h" +#include "toml.h" +#include "toml_utils.h" #define LOAD_HIT 0 #define LOAD_MANA 1 @@ -35,21 +37,32 @@ #define PT_LLAST(i) (player_table[(i)].last) /* local functions */ +#if 0 static void load_affects(FILE *fl, struct char_data *ch); static void load_skills(FILE *fl, struct char_data *ch); static void load_quests(FILE *fl, struct char_data *ch); static void load_HMVS(struct char_data *ch, const char *line, int mode); static void write_aliases_ascii(FILE *file, struct char_data *ch); static void read_aliases_ascii(FILE *file, struct char_data *ch, int count); +#endif +static int toml_get_int_default(toml_table_t *tab, const char *key, int def); +static long toml_get_long_default(toml_table_t *tab, const char *key, long def); +static char *toml_get_string_dup(toml_table_t *tab, const char *key); +static void toml_read_int_array(toml_array_t *arr, int *out, int out_count, int def); +static void toml_read_long_array(toml_array_t *arr, long *out, int out_count, long def); +static void toml_write_int_array(FILE *fp, const char *key, const int *values, int count); +static void toml_write_long_array(FILE *fp, const char *key, const long *values, int count); -/* New version to build player index for ASCII Player Files. Generate index +/* New version to build player index for TOML Player Files. Generate index * table for the player file. */ void build_player_index(void) { int rec_count = 0, i; FILE *plr_index; - char index_name[40], line[256], bits[64]; - char arg2[80]; + char index_name[40]; + char errbuf[256]; + toml_table_t *tab = NULL; + toml_array_t *arr = NULL; sprintf(index_name, "%s%s", LIB_PLRFILES, INDEX_FILE); if (!(plr_index = fopen(index_name, "r"))) { @@ -58,12 +71,25 @@ void build_player_index(void) return; } - while (get_line(plr_index, line)) - if (*line != '~') - rec_count++; - rewind(plr_index); + tab = toml_parse_file(plr_index, errbuf, sizeof(errbuf)); + fclose(plr_index); + if (!tab) { + top_of_p_table = -1; + log("SYSERR: Could not parse player index file %s: %s", index_name, errbuf); + return; + } + arr = toml_array_in(tab, "player"); + if (!arr) { + toml_free(tab); + player_table = NULL; + top_of_p_table = -1; + return; + } + + rec_count = toml_array_nelem(arr); if (rec_count == 0) { + toml_free(tab); player_table = NULL; top_of_p_table = -1; return; @@ -71,16 +97,27 @@ void build_player_index(void) CREATE(player_table, struct player_index_element, rec_count); for (i = 0; i < rec_count; i++) { - get_line(plr_index, line); - sscanf(line, "%ld %s %d %s %ld", &player_table[i].id, arg2, - &player_table[i].level, bits, (long *)&player_table[i].last); - CREATE(player_table[i].name, char, strlen(arg2) + 1); - strcpy(player_table[i].name, arg2); - player_table[i].flags = asciiflag_conv(bits); + toml_table_t *entry = toml_table_at(arr, i); + char *name = NULL; + int j; + + if (!entry) + continue; + player_table[i].id = toml_get_long_default(entry, "id", 0); + player_table[i].level = toml_get_int_default(entry, "level", 0); + player_table[i].flags = toml_get_int_default(entry, "flags", 0); + player_table[i].last = toml_get_long_default(entry, "last", 0); + + name = toml_get_string_dup(entry, "name"); + if (!name) + name = strdup(""); + CREATE(player_table[i].name, char, strlen(name) + 1); + for (j = 0; (player_table[i].name[j] = LOWER(name[j])); j++) + /* Nothing */; + free(name); top_idnum = MAX(top_idnum, player_table[i].id); } - - fclose(plr_index); + toml_free(tab); top_of_p_file = top_of_p_table = i - 1; } @@ -148,11 +185,11 @@ static void remove_player_from_index(int pos) } } -/* This function necessary to save a seperate ASCII player index */ +/* This function necessary to save a seperate TOML player index */ void save_player_index(void) { int i; - char index_name[50], bits[64]; + char index_name[50]; FILE *index_file; sprintf(index_name, "%s%s", LIB_PLRFILES, INDEX_FILE); @@ -163,12 +200,13 @@ void save_player_index(void) for (i = 0; i <= top_of_p_table; i++) if (*player_table[i].name) { - sprintascii(bits, player_table[i].flags); - fprintf(index_file, "%ld %s %d %s %ld\n", player_table[i].id, - player_table[i].name, player_table[i].level, *bits ? bits : "0", - (long)player_table[i].last); + fprintf(index_file, "[[player]]\n"); + fprintf(index_file, "id = %ld\n", player_table[i].id); + toml_write_kv_string(index_file, "name", player_table[i].name); + fprintf(index_file, "level = %d\n", player_table[i].level); + fprintf(index_file, "flags = %d\n", player_table[i].flags); + fprintf(index_file, "last = %ld\n\n", (long)player_table[i].last); } - fprintf(index_file, "~\n"); fclose(index_file); } @@ -236,17 +274,111 @@ static void update_roleplay_age(struct char_data *ch) } } +static int toml_get_int_default(toml_table_t *tab, const char *key, int def) +{ + toml_datum_t d = toml_int_in(tab, key); + + if (!d.ok) + return def; + + return (int)d.u.i; +} + +static long toml_get_long_default(toml_table_t *tab, const char *key, long def) +{ + toml_datum_t d = toml_int_in(tab, key); + + if (!d.ok) + return def; + + return (long)d.u.i; +} + +static char *toml_get_string_dup(toml_table_t *tab, const char *key) +{ + toml_datum_t d = toml_string_in(tab, key); + + if (!d.ok) + return NULL; + + return d.u.s; +} + +static void toml_read_int_array(toml_array_t *arr, int *out, int out_count, int def) +{ + int i; + + for (i = 0; i < out_count; i++) + out[i] = def; + + if (!arr) + return; + + for (i = 0; i < out_count; i++) { + toml_datum_t d = toml_int_at(arr, i); + if (d.ok) + out[i] = (int)d.u.i; + } +} + +static void toml_read_long_array(toml_array_t *arr, long *out, int out_count, long def) +{ + int i; + + for (i = 0; i < out_count; i++) + out[i] = def; + + if (!arr) + return; + + for (i = 0; i < out_count; i++) { + toml_datum_t d = toml_int_at(arr, i); + if (d.ok) + out[i] = (long)d.u.i; + } +} + +static void toml_write_int_array(FILE *fp, const char *key, const int *values, int count) +{ + int i; + + fprintf(fp, "%s = [", key); + for (i = 0; i < count; i++) { + if (i > 0) + fputs(", ", fp); + fprintf(fp, "%d", values[i]); + } + fprintf(fp, "]\n"); +} + +static void toml_write_long_array(FILE *fp, const char *key, const long *values, int count) +{ + int i; + + fprintf(fp, "%s = [", key); + for (i = 0; i < count; i++) { + if (i > 0) + fputs(", ", fp); + fprintf(fp, "%ld", values[i]); + } + fprintf(fp, "]\n"); +} + /* Stuff related to the save/load player system. */ -/* New load_char reads ASCII Player Files. Load a char, TRUE if loaded, FALSE +/* New load_char reads TOML Player Files. Load a char, TRUE if loaded, FALSE * if not. */ int load_char(const char *name, struct char_data *ch) { int id, i; FILE *fl; char filename[40]; - char buf[128], buf2[128], line[MAX_INPUT_LENGTH + 1], tag[6]; - char f1[128], f2[128], f3[128], f4[128]; - bool loaded_stamina = FALSE; + char errbuf[256]; + toml_table_t *tab = NULL; + toml_table_t *sub = NULL; + toml_array_t *arr = NULL; + char *str = NULL; + long long_values[MAX_SKILLS]; + int int_values[6]; trig_data *t = NULL; trig_rnum t_rnum = NOTHING; @@ -329,223 +461,308 @@ int load_char(const char *name, struct char_data *ch) for (i = 0; i < PR_ARRAY_MAX; i++) PRF_FLAGS(ch)[i] = PFDEF_PREFFLAGS; - while (get_line(fl, line)) { - tag_argument(line, tag); + tab = toml_parse_file(fl, errbuf, sizeof(errbuf)); + fclose(fl); + if (!tab) { + mudlog(NRM, LVL_GOD, TRUE, "SYSERR: Couldn't parse player file %s: %s", filename, errbuf); + return (-1); + } - switch (*tag) { - case 'A': - if (!strcmp(tag, "Ac ")) GET_AC(ch) = atoi(line); - else if (!strcmp(tag, "AgYr")) GET_ROLEPLAY_AGE_YEAR(ch) = atoi(line); - else if (!strcmp(tag, "Age ")) GET_ROLEPLAY_AGE(ch) = LIMIT(atoi(line), MIN_CHAR_AGE, MAX_CHAR_AGE); - else if (!strcmp(tag, "Acct")) { - if (GET_ACCOUNT(ch)) - free(GET_ACCOUNT(ch)); - GET_ACCOUNT(ch) = strdup(line); - } - else if (!strcmp(tag, "Act ")) { - if (sscanf(line, "%s %s %s %s", f1, f2, f3, f4) == 4) { - PLR_FLAGS(ch)[0] = asciiflag_conv(f1); - PLR_FLAGS(ch)[1] = asciiflag_conv(f2); - PLR_FLAGS(ch)[2] = asciiflag_conv(f3); - PLR_FLAGS(ch)[3] = asciiflag_conv(f4); - } else - PLR_FLAGS(ch)[0] = asciiflag_conv(line); - } else if (!strcmp(tag, "Aff ")) { - if (sscanf(line, "%s %s %s %s", f1, f2, f3, f4) == 4) { - AFF_FLAGS(ch)[0] = asciiflag_conv(f1); - AFF_FLAGS(ch)[1] = asciiflag_conv(f2); - AFF_FLAGS(ch)[2] = asciiflag_conv(f3); - AFF_FLAGS(ch)[3] = asciiflag_conv(f4); - } else - AFF_FLAGS(ch)[0] = asciiflag_conv(line); - } - if (!strcmp(tag, "Affs")) load_affects(fl, ch); - else if (!strcmp(tag, "Alis")) read_aliases_ascii(fl, ch, atoi(line)); - break; + str = toml_get_string_dup(tab, "name"); + if (str) { + if (GET_PC_NAME(ch)) + free(GET_PC_NAME(ch)); + GET_PC_NAME(ch) = str; + } else if (!GET_PC_NAME(ch)) + GET_PC_NAME(ch) = strdup(name); - case 'B': - if (!strcmp(tag, "Back")) ch->player.background = fread_string(fl, buf2); - else if (!strcmp(tag, "Badp")) GET_BAD_PWS(ch) = atoi(line); - else if (!strcmp(tag, "BankCoins")) GET_BANK_COINS(ch) = atoi(line); - else if (!strcmp(tag, "Brth")) ch->player.time.birth = atol(line); - break; + str = toml_get_string_dup(tab, "short_desc"); + if (str) { + if (GET_SHORT_DESC(ch)) + free(GET_SHORT_DESC(ch)); + GET_SHORT_DESC(ch) = str; + } - case 'C': - if (!strcmp(tag, "Cha ")) ch->real_abils.cha = atoi(line); - else if (!strcmp(tag, "Clas")) GET_CLASS(ch) = atoi(line); - else if (!strcmp(tag, "Coin")) GET_COINS(ch) = atoi(line); - else if (!strcmp(tag, "Con ")) ch->real_abils.con = atoi(line); - break; + str = toml_get_string_dup(tab, "password"); + if (str) { + strlcpy(GET_PASSWD(ch), str, MAX_PWD_LENGTH + 1); + free(str); + } - case 'D': - if (!strcmp(tag, "Desc")) ch->player.description = fread_string(fl, buf2); - else if (!strcmp(tag, "Dex ")) ch->real_abils.dex = atoi(line); - else if (!strcmp(tag, "Drnk")) GET_COND(ch, DRUNK) = atoi(line); - break; + str = toml_get_string_dup(tab, "account"); + if (str) { + if (GET_ACCOUNT(ch)) + free(GET_ACCOUNT(ch)); + GET_ACCOUNT(ch) = str; + } - case 'E': - if (!strcmp(tag, "Exp ")) GET_EXP(ch) = atoi(line); - break; + str = toml_get_string_dup(tab, "description"); + if (str) { + if (ch->player.description) + free(ch->player.description); + ch->player.description = str; + } - case 'F': - if (!strcmp(tag, "Frez")) GET_FREEZE_LEV(ch) = atoi(line); - break; + str = toml_get_string_dup(tab, "background"); + if (str) { + if (ch->player.background) + free(ch->player.background); + ch->player.background = str; + } - case 'H': - if (!strcmp(tag, "Hit ")) load_HMVS(ch, line, LOAD_HIT); - else if (!strcmp(tag, "Hite")) GET_HEIGHT(ch) = atoi(line); - else if (!strcmp(tag, "Host")) { - if (GET_HOST(ch)) - free(GET_HOST(ch)); - GET_HOST(ch) = strdup(line); - } - else if (!strcmp(tag, "Hung")) GET_COND(ch, HUNGER) = atoi(line); - break; + str = toml_get_string_dup(tab, "poofin"); + if (str) { + if (POOFIN(ch)) + free(POOFIN(ch)); + POOFIN(ch) = str; + } - case 'I': - if (!strcmp(tag, "Id ")) GET_IDNUM(ch) = atol(line); - else if (!strcmp(tag, "Int ")) ch->real_abils.intel = atoi(line); - else if (!strcmp(tag, "Invs")) GET_INVIS_LEV(ch) = atoi(line); - break; + str = toml_get_string_dup(tab, "poofout"); + if (str) { + if (POOFOUT(ch)) + free(POOFOUT(ch)); + POOFOUT(ch) = str; + } - case 'L': - if (!strcmp(tag, "Last")) ch->player.time.logon = atol(line); - else if (!strcmp(tag, "Levl")) GET_LEVEL(ch) = atoi(line); - else if (!strcmp(tag, "Lmot")) GET_LAST_MOTD(ch) = atoi(line); - else if (!strcmp(tag, "Lnew")) GET_LAST_NEWS(ch) = atoi(line); - break; + GET_SEX(ch) = toml_get_int_default(tab, "sex", GET_SEX(ch)); + GET_CLASS(ch) = toml_get_int_default(tab, "class", GET_CLASS(ch)); + GET_SPECIES(ch) = toml_get_int_default(tab, "species", GET_SPECIES(ch)); + GET_LEVEL(ch) = toml_get_int_default(tab, "level", GET_LEVEL(ch)); + GET_IDNUM(ch) = toml_get_long_default(tab, "id", GET_IDNUM(ch)); + ch->player.time.birth = toml_get_long_default(tab, "birth", ch->player.time.birth); + GET_ROLEPLAY_AGE(ch) = toml_get_int_default(tab, "age", GET_ROLEPLAY_AGE(ch)); + GET_ROLEPLAY_AGE_YEAR(ch) = toml_get_int_default(tab, "age_year", GET_ROLEPLAY_AGE_YEAR(ch)); + ch->player.time.played = toml_get_int_default(tab, "played", ch->player.time.played); + ch->player.time.logon = toml_get_long_default(tab, "logon", ch->player.time.logon); + GET_LAST_MOTD(ch) = toml_get_int_default(tab, "last_motd", GET_LAST_MOTD(ch)); + GET_LAST_NEWS(ch) = toml_get_int_default(tab, "last_news", GET_LAST_NEWS(ch)); + GET_REROLL_USED(ch) = toml_get_int_default(tab, "reroll_used", GET_REROLL_USED(ch)); + GET_REROLL_EXPIRES(ch) = (time_t)toml_get_long_default(tab, "reroll_expires", (long)GET_REROLL_EXPIRES(ch)); - case 'M': - if (!strcmp(tag, "Mana")) load_HMVS(ch, line, LOAD_MANA); - else if (!strcmp(tag, "Move") && !loaded_stamina) load_HMVS(ch, line, LOAD_STAMINA); - break; + arr = toml_array_in(tab, "reroll_old_abils"); + if (arr) { + toml_read_int_array(arr, int_values, 6, 0); + GET_REROLL_OLD_ABILS(ch).str = int_values[0]; + GET_REROLL_OLD_ABILS(ch).intel = int_values[1]; + GET_REROLL_OLD_ABILS(ch).wis = int_values[2]; + GET_REROLL_OLD_ABILS(ch).dex = int_values[3]; + GET_REROLL_OLD_ABILS(ch).con = int_values[4]; + GET_REROLL_OLD_ABILS(ch).cha = int_values[5]; + } - case 'N': - if (!strcmp(tag, "Name")) GET_PC_NAME(ch) = strdup(line); - break; + str = toml_get_string_dup(tab, "host"); + if (str) { + if (GET_HOST(ch)) + free(GET_HOST(ch)); + GET_HOST(ch) = str; + } - case 'O': - if (!strcmp(tag, "Olc ")) GET_OLC_ZONE(ch) = atoi(line); - break; + GET_HEIGHT(ch) = toml_get_int_default(tab, "height", GET_HEIGHT(ch)); + GET_WEIGHT(ch) = toml_get_int_default(tab, "weight", GET_WEIGHT(ch)); - case 'P': - if (!strcmp(tag, "Page")) GET_PAGE_LENGTH(ch) = atoi(line); - else if (!strcmp(tag, "Pass")) strcpy(GET_PASSWD(ch), line); - else if (!strcmp(tag, "Plyd")) ch->player.time.played = atoi(line); - else if (!strcmp(tag, "PfIn")) POOFIN(ch) = strdup(line); - else if (!strcmp(tag, "PfOt")) POOFOUT(ch) = strdup(line); - else if (!strcmp(tag, "Pref")) { - if (sscanf(line, "%s %s %s %s", f1, f2, f3, f4) == 4) { - PRF_FLAGS(ch)[0] = asciiflag_conv(f1); - PRF_FLAGS(ch)[1] = asciiflag_conv(f2); - PRF_FLAGS(ch)[2] = asciiflag_conv(f3); - PRF_FLAGS(ch)[3] = asciiflag_conv(f4); - } else - PRF_FLAGS(ch)[0] = asciiflag_conv(f1); - } - break; + arr = toml_array_in(tab, "act_flags"); + toml_read_int_array(arr, int_values, PM_ARRAY_MAX, PFDEF_PLRFLAGS); + for (i = 0; i < PM_ARRAY_MAX; i++) + PLR_FLAGS(ch)[i] = int_values[i]; - case 'Q': - if (!strcmp(tag, "Qstp")) GET_QUESTPOINTS(ch) = atoi(line); - else if (!strcmp(tag, "Qpnt")) GET_QUESTPOINTS(ch) = atoi(line); /* Backward compatibility */ - else if (!strcmp(tag, "Qcur")) GET_QUEST(ch) = atoi(line); - else if (!strcmp(tag, "Qcnt")) GET_QUEST_COUNTER(ch) = atoi(line); - else if (!strcmp(tag, "Qest")) load_quests(fl, ch); - break; + arr = toml_array_in(tab, "aff_flags"); + toml_read_int_array(arr, int_values, AF_ARRAY_MAX, PFDEF_AFFFLAGS); + for (i = 0; i < AF_ARRAY_MAX; i++) + AFF_FLAGS(ch)[i] = int_values[i]; - case 'R': - if (!strcmp(tag, "Room")) GET_LOADROOM(ch) = atoi(line); - else if (!strcmp(tag, "RrUs")) GET_REROLL_USED(ch) = atoi(line); - else if (!strcmp(tag, "RrTm")) GET_REROLL_EXPIRES(ch) = (time_t)atol(line); - else if (!strcmp(tag, "RrAb")) { - int rstr, rint, rwis, rdex, rcon, rcha; + arr = toml_array_in(tab, "pref_flags"); + toml_read_int_array(arr, int_values, PR_ARRAY_MAX, PFDEF_PREFFLAGS); + for (i = 0; i < PR_ARRAY_MAX; i++) + PRF_FLAGS(ch)[i] = int_values[i]; - if (sscanf(line, "%d %d %d %d %d %d", &rstr, &rint, &rwis, &rdex, &rcon, &rcha) == 6) { - GET_REROLL_OLD_ABILS(ch).str = rstr; - GET_REROLL_OLD_ABILS(ch).intel = rint; - GET_REROLL_OLD_ABILS(ch).wis = rwis; - GET_REROLL_OLD_ABILS(ch).dex = rdex; - GET_REROLL_OLD_ABILS(ch).con = rcon; - GET_REROLL_OLD_ABILS(ch).cha = rcha; - } - } - break; + arr = toml_array_in(tab, "saving_throws"); + toml_read_int_array(arr, int_values, NUM_OF_SAVING_THROWS, PFDEF_SAVETHROW); + for (i = 0; i < NUM_OF_SAVING_THROWS; i++) + GET_SAVE(ch, i) = int_values[i]; - case 'S': - if (!strcmp(tag, "Spec")) { - int val = atoi(line); - if (val < SPECIES_UNDEFINED || val >= NUM_SPECIES) - val = SPECIES_UNDEFINED; - GET_SPECIES(ch) = val; + GET_WIMP_LEV(ch) = toml_get_int_default(tab, "wimp", GET_WIMP_LEV(ch)); + GET_FREEZE_LEV(ch) = toml_get_int_default(tab, "freeze", GET_FREEZE_LEV(ch)); + GET_INVIS_LEV(ch) = toml_get_int_default(tab, "invis", GET_INVIS_LEV(ch)); + GET_LOADROOM(ch) = toml_get_int_default(tab, "load_room", GET_LOADROOM(ch)); + GET_BAD_PWS(ch) = toml_get_int_default(tab, "bad_passwords", GET_BAD_PWS(ch)); + + sub = toml_table_in(tab, "conditions"); + if (sub) { + GET_COND(ch, HUNGER) = toml_get_int_default(sub, "hunger", GET_COND(ch, HUNGER)); + GET_COND(ch, THIRST) = toml_get_int_default(sub, "thirst", GET_COND(ch, THIRST)); + GET_COND(ch, DRUNK) = toml_get_int_default(sub, "drunk", GET_COND(ch, DRUNK)); + } + + sub = toml_table_in(tab, "hmv"); + if (sub) { + GET_HIT(ch) = toml_get_int_default(sub, "hit", GET_HIT(ch)); + GET_MAX_HIT(ch) = toml_get_int_default(sub, "max_hit", GET_MAX_HIT(ch)); + GET_MANA(ch) = toml_get_int_default(sub, "mana", GET_MANA(ch)); + GET_MAX_MANA(ch) = toml_get_int_default(sub, "max_mana", GET_MAX_MANA(ch)); + GET_STAMINA(ch) = toml_get_int_default(sub, "stamina", GET_STAMINA(ch)); + GET_MAX_STAMINA(ch) = toml_get_int_default(sub, "max_stamina", GET_MAX_STAMINA(ch)); + } + + sub = toml_table_in(tab, "abilities"); + if (sub) { + ch->real_abils.str = toml_get_int_default(sub, "str", ch->real_abils.str); + ch->real_abils.intel = toml_get_int_default(sub, "int", ch->real_abils.intel); + ch->real_abils.wis = toml_get_int_default(sub, "wis", ch->real_abils.wis); + ch->real_abils.dex = toml_get_int_default(sub, "dex", ch->real_abils.dex); + ch->real_abils.con = toml_get_int_default(sub, "con", ch->real_abils.con); + ch->real_abils.cha = toml_get_int_default(sub, "cha", ch->real_abils.cha); + } + + GET_AC(ch) = toml_get_int_default(tab, "ac", GET_AC(ch)); + GET_COINS(ch) = toml_get_int_default(tab, "coins", GET_COINS(ch)); + GET_BANK_COINS(ch) = toml_get_int_default(tab, "bank_coins", GET_BANK_COINS(ch)); + GET_EXP(ch) = toml_get_int_default(tab, "exp", GET_EXP(ch)); + GET_OLC_ZONE(ch) = toml_get_int_default(tab, "olc_zone", GET_OLC_ZONE(ch)); + GET_PAGE_LENGTH(ch) = toml_get_int_default(tab, "page_length", GET_PAGE_LENGTH(ch)); + GET_SCREEN_WIDTH(ch) = toml_get_int_default(tab, "screen_width", GET_SCREEN_WIDTH(ch)); + GET_QUESTPOINTS(ch) = toml_get_int_default(tab, "quest_points", GET_QUESTPOINTS(ch)); + GET_QUEST_COUNTER(ch) = toml_get_int_default(tab, "quest_counter", GET_QUEST_COUNTER(ch)); + GET_QUEST(ch) = toml_get_int_default(tab, "current_quest", GET_QUEST(ch)); + + arr = toml_array_in(tab, "completed_quests"); + if (arr) { + int count = toml_array_nelem(arr); + for (i = 0; i < count; i++) { + toml_datum_t d = toml_int_at(arr, i); + if (d.ok) + add_completed_quest(ch, (int)d.u.i); } - else if (!strcmp(tag, "Stam")) { - load_HMVS(ch, line, LOAD_STAMINA); - loaded_stamina = TRUE; - } - else if (!strcmp(tag, "Sex ")) GET_SEX(ch) = atoi(line); - else if (!strcmp(tag, "Sdsc")) { - /* Clear any existing sdesc to avoid leaks */ - if (GET_SHORT_DESC(ch)) - free(GET_SHORT_DESC(ch)); - /* 'line' is the remainder of the line after "Sdsc" + space */ - GET_SHORT_DESC(ch) = strdup(line); - } - else if (!strcmp(tag, "ScrW")) GET_SCREEN_WIDTH(ch) = atoi(line); - else if (!strcmp(tag, "Skil")) load_skills(fl, ch); - else if (!strcmp(tag, "SkGt")) { /* Skill Gain Timers */ - char *p = line; - for (int i = 1; i <= MAX_SKILLS; i++) { - long t = 0; + } - while (*p && isspace((unsigned char)*p)) - ++p; + arr = toml_array_in(tab, "triggers"); + if (arr && CONFIG_SCRIPT_PLAYERS) { + int count = toml_array_nelem(arr); + for (i = 0; i < count; i++) { + toml_datum_t d = toml_int_at(arr, i); + if (!d.ok) + continue; + t_rnum = real_trigger((int)d.u.i); + if (t_rnum == NOTHING) + continue; + t = read_trigger(t_rnum); + if (!SCRIPT(ch)) + CREATE(SCRIPT(ch), struct script_data, 1); + add_trigger(SCRIPT(ch), t, -1); + } + } - if (*p) { - char *endptr = p; - t = strtol(p, &endptr, 10); - if (endptr == p) - t = 0; + arr = toml_array_in(tab, "skill_gain_next"); + toml_read_long_array(arr, long_values, MAX_SKILLS, 0); + for (i = 0; i < MAX_SKILLS; i++) + GET_SKILL_NEXT_GAIN(ch, i + 1) = (time_t)long_values[i]; + + arr = toml_array_in(tab, "skill"); + if (arr) { + int count = toml_array_nelem(arr); + for (i = 0; i < count; i++) { + toml_table_t *skill = toml_table_at(arr, i); + int skill_id, skill_level; + + if (!skill) + continue; + skill_id = toml_get_int_default(skill, "id", 0); + skill_level = toml_get_int_default(skill, "level", 0); + if (skill_id < 1 || skill_id > MAX_SKILLS) + continue; + if (IS_NPC(ch)) + ch->mob_specials.skills[skill_id] = skill_level; else - p = endptr; - } - - GET_SKILL_NEXT_GAIN(ch, i) = (time_t)t; - } - } - else if (!strcmp(tag, "Str ")) load_HMVS(ch, line, LOAD_STRENGTH); - break; - - case 'T': - if (!strcmp(tag, "Thir")) GET_COND(ch, THIRST) = atoi(line); - else if (!strcmp(tag, "Thr1")) GET_SAVE(ch, 0) = atoi(line); - else if (!strcmp(tag, "Thr2")) GET_SAVE(ch, 1) = atoi(line); - else if (!strcmp(tag, "Thr3")) GET_SAVE(ch, 2) = atoi(line); - else if (!strcmp(tag, "Thr4")) GET_SAVE(ch, 3) = atoi(line); - else if (!strcmp(tag, "Thr5")) GET_SAVE(ch, 4) = atoi(line); - else if (!strcmp(tag, "Trig") && CONFIG_SCRIPT_PLAYERS) { - if ((t_rnum = real_trigger(atoi(line))) != NOTHING) { - t = read_trigger(t_rnum); - if (!SCRIPT(ch)) - CREATE(SCRIPT(ch), struct script_data, 1); - add_trigger(SCRIPT(ch), t, -1); - } - } - break; - - case 'V': - if (!strcmp(tag, "Vars")) read_saved_vars_ascii(fl, ch, atoi(line)); - break; - - case 'W': - if (!strcmp(tag, "Wate")) GET_WEIGHT(ch) = atoi(line); - else if (!strcmp(tag, "Wimp")) GET_WIMP_LEV(ch) = atoi(line); - else if (!strcmp(tag, "Wis ")) ch->real_abils.wis = atoi(line); - break; - - default: - sprintf(buf, "SYSERR: Unknown tag %s in pfile %s", tag, name); + ch->player_specials->saved.skills[skill_id] = skill_level; } } + + arr = toml_array_in(tab, "affect"); + if (arr) { + int count = toml_array_nelem(arr); + for (i = 0; i < count; i++) { + struct affected_type af; + toml_table_t *aff_tab = toml_table_at(arr, i); + + if (!aff_tab) + continue; + + new_affect(&af); + af.spell = toml_get_int_default(aff_tab, "spell", 0); + af.duration = toml_get_int_default(aff_tab, "duration", 0); + af.modifier = toml_get_int_default(aff_tab, "modifier", 0); + af.location = toml_get_int_default(aff_tab, "location", 0); + toml_read_int_array(toml_array_in(aff_tab, "bitvector"), int_values, AF_ARRAY_MAX, 0); + af.bitvector[0] = int_values[0]; + af.bitvector[1] = int_values[1]; + af.bitvector[2] = int_values[2]; + af.bitvector[3] = int_values[3]; + if (af.spell > 0) + affect_to_char(ch, &af); + } + } + + arr = toml_array_in(tab, "alias"); + if (arr) { + int count = toml_array_nelem(arr); + for (i = 0; i < count; i++) { + toml_table_t *alias_tab = toml_table_at(arr, i); + char *alias = NULL; + char *replacement = NULL; + int type; + struct alias_data *temp; + + if (!alias_tab) + continue; + alias = toml_get_string_dup(alias_tab, "alias"); + replacement = toml_get_string_dup(alias_tab, "replacement"); + type = toml_get_int_default(alias_tab, "type", 0); + if (!alias || !replacement) { + if (alias) + free(alias); + if (replacement) + free(replacement); + continue; + } + + CREATE(temp, struct alias_data, 1); + temp->alias = alias; + temp->replacement = replacement; + temp->type = type; + temp->next = GET_ALIASES(ch); + GET_ALIASES(ch) = temp; + } + } + + arr = toml_array_in(tab, "var"); + if (arr) { + int count = toml_array_nelem(arr); + for (i = 0; i < count; i++) { + toml_table_t *var_tab = toml_table_at(arr, i); + char *varname = NULL; + char *value = NULL; + long context; + + if (!var_tab) + continue; + varname = toml_get_string_dup(var_tab, "name"); + value = toml_get_string_dup(var_tab, "value"); + context = toml_get_long_default(var_tab, "context", 0); + if (!varname || !value) { + if (varname) + free(varname); + if (value) + free(value); + continue; + } + if (!SCRIPT(ch)) + CREATE(SCRIPT(ch), struct script_data, 1); + add_var(&(SCRIPT(ch)->global_vars), varname, value, context); + free(varname); + free(value); + } + } + + toml_free(tab); } update_roleplay_age(ch); @@ -567,17 +784,18 @@ int load_char(const char *name, struct char_data *ch) GET_COND(ch, THIRST) = -1; GET_COND(ch, DRUNK) = -1; } - fclose(fl); return(id); } /* Write the vital data of a player to the player file. */ -/* This is the ASCII Player Files save routine. */ +/* This is the TOML Player Files save routine. */ void save_char(struct char_data * ch) { FILE *fl; - char filename[40], buf[MAX_STRING_LENGTH], bits[127], bits2[127], bits3[127], bits4[127]; + char filename[40], buf[MAX_STRING_LENGTH]; int i, j, id, save_index = FALSE; + int save_throws[NUM_OF_SAVING_THROWS]; + long gain_times[MAX_SKILLS]; struct affected_type *aff, tmp_aff[MAX_AFFECT]; struct obj_data *char_eq[NUM_WEARS]; trig_data *t; @@ -650,154 +868,157 @@ void save_char(struct char_data * ch) ch->aff_abils = ch->real_abils; /* end char_to_store code */ - if (GET_NAME(ch)) fprintf(fl, "Name: %s\n", GET_NAME(ch)); - if (GET_SHORT_DESC(ch) && *GET_SHORT_DESC(ch)) fprintf(fl, "Sdsc: %s\n", GET_SHORT_DESC(ch)); - if (GET_PASSWD(ch)) fprintf(fl, "Pass: %s\n", GET_PASSWD(ch)); - if (GET_ACCOUNT(ch) && *GET_ACCOUNT(ch)) fprintf(fl, "Acct: %s\n", GET_ACCOUNT(ch)); + if (GET_NAME(ch)) + toml_write_kv_string(fl, "name", GET_NAME(ch)); + toml_write_kv_string_opt(fl, "short_desc", GET_SHORT_DESC(ch)); + if (GET_PASSWD(ch) && *GET_PASSWD(ch)) + toml_write_kv_string(fl, "password", GET_PASSWD(ch)); + toml_write_kv_string_opt(fl, "account", GET_ACCOUNT(ch)); if (ch->player.description && *ch->player.description) { - strcpy(buf, ch->player.description); + strlcpy(buf, ch->player.description, sizeof(buf)); strip_cr(buf); - fprintf(fl, "Desc:\n%s~\n", buf); + toml_write_kv_string(fl, "description", buf); } if (ch->player.background && *ch->player.background) { - strcpy(buf, ch->player.background); + strlcpy(buf, ch->player.background, sizeof(buf)); strip_cr(buf); - fprintf(fl, "Back:\n%s~\n", buf); + toml_write_kv_string(fl, "background", buf); } - if (POOFIN(ch)) fprintf(fl, "PfIn: %s\n", POOFIN(ch)); - if (POOFOUT(ch)) fprintf(fl, "PfOt: %s\n", POOFOUT(ch)); - if (GET_SEX(ch) != PFDEF_SEX) fprintf(fl, "Sex : %d\n", GET_SEX(ch)); - if (GET_CLASS(ch) != PFDEF_CLASS) fprintf(fl, "Clas: %d\n", GET_CLASS(ch)); - if (GET_SPECIES(ch) != PFDEF_SPECIES) fprintf(fl, "Spec: %d\n", GET_SPECIES(ch)); - if (GET_LEVEL(ch) != PFDEF_LEVEL) fprintf(fl, "Levl: %d\n", GET_LEVEL(ch)); + toml_write_kv_string_opt(fl, "poofin", POOFIN(ch)); + toml_write_kv_string_opt(fl, "poofout", POOFOUT(ch)); - fprintf(fl, "Id : %ld\n", GET_IDNUM(ch)); - fprintf(fl, "Brth: %ld\n", (long)ch->player.time.birth); - fprintf(fl, "Age : %d\n", GET_ROLEPLAY_AGE(ch)); - fprintf(fl, "AgYr: %d\n", GET_ROLEPLAY_AGE_YEAR(ch)); - fprintf(fl, "Plyd: %d\n", ch->player.time.played); - fprintf(fl, "Last: %ld\n", (long)ch->player.time.logon); + fprintf(fl, "sex = %d\n", GET_SEX(ch)); + fprintf(fl, "class = %d\n", GET_CLASS(ch)); + fprintf(fl, "species = %d\n", GET_SPECIES(ch)); + fprintf(fl, "level = %d\n", GET_LEVEL(ch)); - if (GET_LAST_MOTD(ch) != PFDEF_LASTMOTD) - fprintf(fl, "Lmot: %d\n", (int)GET_LAST_MOTD(ch)); - if (GET_LAST_NEWS(ch) != PFDEF_LASTNEWS) - fprintf(fl, "Lnew: %d\n", (int)GET_LAST_NEWS(ch)); - if (GET_REROLL_USED(ch) != PFDEF_REROLL_USED) - fprintf(fl, "RrUs: %d\n", (int)GET_REROLL_USED(ch)); - if (GET_REROLL_EXPIRES(ch) != PFDEF_REROLL_EXPIRES) { - fprintf(fl, "RrTm: %ld\n", (long)GET_REROLL_EXPIRES(ch)); - fprintf(fl, "RrAb: %d %d %d %d %d %d\n", - GET_REROLL_OLD_ABILS(ch).str, - GET_REROLL_OLD_ABILS(ch).intel, - GET_REROLL_OLD_ABILS(ch).wis, - GET_REROLL_OLD_ABILS(ch).dex, - GET_REROLL_OLD_ABILS(ch).con, - GET_REROLL_OLD_ABILS(ch).cha); - } + fprintf(fl, "id = %ld\n", GET_IDNUM(ch)); + fprintf(fl, "birth = %ld\n", (long)ch->player.time.birth); + fprintf(fl, "age = %d\n", GET_ROLEPLAY_AGE(ch)); + fprintf(fl, "age_year = %d\n", GET_ROLEPLAY_AGE_YEAR(ch)); + fprintf(fl, "played = %d\n", ch->player.time.played); + fprintf(fl, "logon = %ld\n", (long)ch->player.time.logon); + fprintf(fl, "last_motd = %d\n", (int)GET_LAST_MOTD(ch)); + fprintf(fl, "last_news = %d\n", (int)GET_LAST_NEWS(ch)); + fprintf(fl, "reroll_used = %d\n", (int)GET_REROLL_USED(ch)); + fprintf(fl, "reroll_expires = %ld\n", (long)GET_REROLL_EXPIRES(ch)); + fprintf(fl, "reroll_old_abils = [%d, %d, %d, %d, %d, %d]\n", + GET_REROLL_OLD_ABILS(ch).str, + GET_REROLL_OLD_ABILS(ch).intel, + GET_REROLL_OLD_ABILS(ch).wis, + GET_REROLL_OLD_ABILS(ch).dex, + GET_REROLL_OLD_ABILS(ch).con, + GET_REROLL_OLD_ABILS(ch).cha); - if (GET_HOST(ch)) fprintf(fl, "Host: %s\n", GET_HOST(ch)); - if (GET_HEIGHT(ch) != PFDEF_HEIGHT) fprintf(fl, "Hite: %d\n", GET_HEIGHT(ch)); - if (GET_WEIGHT(ch) != PFDEF_WEIGHT) fprintf(fl, "Wate: %d\n", GET_WEIGHT(ch)); + toml_write_kv_string_opt(fl, "host", GET_HOST(ch)); + fprintf(fl, "height = %d\n", GET_HEIGHT(ch)); + fprintf(fl, "weight = %d\n", GET_WEIGHT(ch)); + toml_write_int_array(fl, "act_flags", PLR_FLAGS(ch), PM_ARRAY_MAX); + toml_write_int_array(fl, "aff_flags", AFF_FLAGS(ch), AF_ARRAY_MAX); + toml_write_int_array(fl, "pref_flags", PRF_FLAGS(ch), PR_ARRAY_MAX); - sprintascii(bits, PLR_FLAGS(ch)[0]); - sprintascii(bits2, PLR_FLAGS(ch)[1]); - sprintascii(bits3, PLR_FLAGS(ch)[2]); - sprintascii(bits4, PLR_FLAGS(ch)[3]); - fprintf(fl, "Act : %s %s %s %s\n", bits, bits2, bits3, bits4); + for (i = 0; i < NUM_OF_SAVING_THROWS; i++) + save_throws[i] = GET_SAVE(ch, i); + toml_write_int_array(fl, "saving_throws", save_throws, NUM_OF_SAVING_THROWS); - sprintascii(bits, AFF_FLAGS(ch)[0]); - sprintascii(bits2, AFF_FLAGS(ch)[1]); - sprintascii(bits3, AFF_FLAGS(ch)[2]); - sprintascii(bits4, AFF_FLAGS(ch)[3]); - fprintf(fl, "Aff : %s %s %s %s\n", bits, bits2, bits3, bits4); + fprintf(fl, "wimp = %d\n", GET_WIMP_LEV(ch)); + fprintf(fl, "freeze = %d\n", GET_FREEZE_LEV(ch)); + fprintf(fl, "invis = %d\n", GET_INVIS_LEV(ch)); + fprintf(fl, "load_room = %d\n", GET_LOADROOM(ch)); + fprintf(fl, "bad_passwords = %d\n", GET_BAD_PWS(ch)); + fprintf(fl, "conditions = { hunger = %d, thirst = %d, drunk = %d }\n", + GET_COND(ch, HUNGER), GET_COND(ch, THIRST), GET_COND(ch, DRUNK)); + fprintf(fl, "hmv = { hit = %d, max_hit = %d, mana = %d, max_mana = %d, stamina = %d, max_stamina = %d }\n", + GET_HIT(ch), GET_MAX_HIT(ch), GET_MANA(ch), GET_MAX_MANA(ch), + GET_STAMINA(ch), GET_MAX_STAMINA(ch)); + fprintf(fl, "abilities = { str = %d, int = %d, wis = %d, dex = %d, con = %d, cha = %d }\n", + GET_STR(ch), GET_INT(ch), GET_WIS(ch), GET_DEX(ch), GET_CON(ch), GET_CHA(ch)); - sprintascii(bits, PRF_FLAGS(ch)[0]); - sprintascii(bits2, PRF_FLAGS(ch)[1]); - sprintascii(bits3, PRF_FLAGS(ch)[2]); - sprintascii(bits4, PRF_FLAGS(ch)[3]); - fprintf(fl, "Pref: %s %s %s %s\n", bits, bits2, bits3, bits4); + fprintf(fl, "ac = %d\n", GET_AC(ch)); + fprintf(fl, "coins = %d\n", GET_COINS(ch)); + fprintf(fl, "bank_coins = %d\n", GET_BANK_COINS(ch)); + fprintf(fl, "exp = %d\n", GET_EXP(ch)); + fprintf(fl, "olc_zone = %d\n", GET_OLC_ZONE(ch)); + fprintf(fl, "page_length = %d\n", GET_PAGE_LENGTH(ch)); + fprintf(fl, "screen_width = %d\n", GET_SCREEN_WIDTH(ch)); + fprintf(fl, "quest_points = %d\n", GET_QUESTPOINTS(ch)); + fprintf(fl, "quest_counter = %d\n", GET_QUEST_COUNTER(ch)); + fprintf(fl, "current_quest = %d\n", GET_QUEST(ch)); - if (GET_SAVE(ch, 0) != PFDEF_SAVETHROW) fprintf(fl, "Thr1: %d\n", GET_SAVE(ch, 0)); - if (GET_SAVE(ch, 1) != PFDEF_SAVETHROW) fprintf(fl, "Thr2: %d\n", GET_SAVE(ch, 1)); - if (GET_SAVE(ch, 2) != PFDEF_SAVETHROW) fprintf(fl, "Thr3: %d\n", GET_SAVE(ch, 2)); - if (GET_SAVE(ch, 3) != PFDEF_SAVETHROW) fprintf(fl, "Thr4: %d\n", GET_SAVE(ch, 3)); - if (GET_SAVE(ch, 4) != PFDEF_SAVETHROW) fprintf(fl, "Thr5: %d\n", GET_SAVE(ch, 4)); - - if (GET_WIMP_LEV(ch) != PFDEF_WIMPLEV) fprintf(fl, "Wimp: %d\n", GET_WIMP_LEV(ch)); - if (GET_FREEZE_LEV(ch) != PFDEF_FREEZELEV) fprintf(fl, "Frez: %d\n", GET_FREEZE_LEV(ch)); - if (GET_INVIS_LEV(ch) != PFDEF_INVISLEV) fprintf(fl, "Invs: %d\n", GET_INVIS_LEV(ch)); - if (GET_LOADROOM(ch) != PFDEF_LOADROOM) fprintf(fl, "Room: %d\n", GET_LOADROOM(ch)); - - if (GET_BAD_PWS(ch) != PFDEF_BADPWS) fprintf(fl, "Badp: %d\n", GET_BAD_PWS(ch)); - - if (GET_COND(ch, HUNGER) != PFDEF_HUNGER && GET_LEVEL(ch) < LVL_IMMORT) fprintf(fl, "Hung: %d\n", GET_COND(ch, HUNGER)); - if (GET_COND(ch, THIRST) != PFDEF_THIRST && GET_LEVEL(ch) < LVL_IMMORT) fprintf(fl, "Thir: %d\n", GET_COND(ch, THIRST)); - if (GET_COND(ch, DRUNK) != PFDEF_DRUNK && GET_LEVEL(ch) < LVL_IMMORT) fprintf(fl, "Drnk: %d\n", GET_COND(ch, DRUNK)); - - if (GET_HIT(ch) != PFDEF_HIT || GET_MAX_HIT(ch) != PFDEF_MAXHIT) fprintf(fl, "Hit : %d/%d\n", GET_HIT(ch), GET_MAX_HIT(ch)); - if (GET_MANA(ch) != PFDEF_MANA || GET_MAX_MANA(ch) != PFDEF_MAXMANA) fprintf(fl, "Mana: %d/%d\n", GET_MANA(ch), GET_MAX_MANA(ch)); - if (GET_STAMINA(ch) != PFDEF_STAMINA || GET_MAX_STAMINA(ch) != PFDEF_MAXSTAMINA) fprintf(fl, "Stam: %d/%d\n", GET_STAMINA(ch), GET_MAX_STAMINA(ch)); - - if (GET_STR(ch) != PFDEF_STR) fprintf(fl, "Str : %d\n", GET_STR(ch)); - if (GET_INT(ch) != PFDEF_INT) fprintf(fl, "Int : %d\n", GET_INT(ch)); - if (GET_WIS(ch) != PFDEF_WIS) fprintf(fl, "Wis : %d\n", GET_WIS(ch)); - if (GET_DEX(ch) != PFDEF_DEX) fprintf(fl, "Dex : %d\n", GET_DEX(ch)); - if (GET_CON(ch) != PFDEF_CON) fprintf(fl, "Con : %d\n", GET_CON(ch)); - if (GET_CHA(ch) != PFDEF_CHA) fprintf(fl, "Cha : %d\n", GET_CHA(ch)); - - if (GET_AC(ch) != PFDEF_AC) fprintf(fl, "Ac : %d\n", GET_AC(ch)); - if (GET_COINS(ch) != PFDEF_COINS) fprintf(fl, "Coin: %d\n", GET_COINS(ch)); - if (GET_BANK_COINS(ch) != PFDEF_BANK_COINS) fprintf(fl, "BankCoins: %d\n", GET_BANK_COINS(ch)); - if (GET_EXP(ch) != PFDEF_EXP) fprintf(fl, "Exp : %d\n", GET_EXP(ch)); - if (GET_OLC_ZONE(ch) != PFDEF_OLC) fprintf(fl, "Olc : %d\n", GET_OLC_ZONE(ch)); - if (GET_PAGE_LENGTH(ch) != PFDEF_PAGELENGTH) fprintf(fl, "Page: %d\n", GET_PAGE_LENGTH(ch)); - if (GET_SCREEN_WIDTH(ch) != PFDEF_SCREENWIDTH) fprintf(fl, "ScrW: %d\n", GET_SCREEN_WIDTH(ch)); - if (GET_QUESTPOINTS(ch) != PFDEF_QUESTPOINTS) fprintf(fl, "Qstp: %d\n", GET_QUESTPOINTS(ch)); - if (GET_QUEST_COUNTER(ch)!= PFDEF_QUESTCOUNT) fprintf(fl, "Qcnt: %d\n", GET_QUEST_COUNTER(ch)); - if (GET_NUM_QUESTS(ch) != PFDEF_COMPQUESTS) { - fprintf(fl, "Qest:\n"); - for (i = 0; i < GET_NUM_QUESTS(ch); i++) - fprintf(fl, "%d\n", ch->player_specials->saved.completed_quests[i]); - fprintf(fl, "%d\n", NOTHING); - } - if (GET_QUEST(ch) != PFDEF_CURRQUEST) fprintf(fl, "Qcur: %d\n", GET_QUEST(ch)); - - if (SCRIPT(ch)) { - for (t = TRIGGERS(SCRIPT(ch)); t; t = t->next) - fprintf(fl, "Trig: %d\n",GET_TRIG_VNUM(t)); -} - - /* Save skills */ - if (GET_LEVEL(ch) < LVL_IMMORT) { - fprintf(fl, "Skil:\n"); - for (i = 1; i <= MAX_SKILLS; i++) { - if (GET_SKILL(ch, i)) - fprintf(fl, "%d %d\n", i, GET_SKILL(ch, i)); + if (GET_NUM_QUESTS(ch) > 0) { + fprintf(fl, "completed_quests = ["); + for (i = 0; i < GET_NUM_QUESTS(ch); i++) { + if (i > 0) + fputs(", ", fl); + fprintf(fl, "%d", ch->player_specials->saved.completed_quests[i]); } - fprintf(fl, "0 0\n"); + fprintf(fl, "]\n"); } - /* Write per-skill next gain times as epoch seconds. */ - fprintf(fl, "SkGt:"); /* Skill Gain Timer */ - for (int i = 1; i <= MAX_SKILLS; i++) - fprintf(fl, " %ld", (long)GET_SKILL_NEXT_GAIN(ch, i)); - fputc('\n', fl); + if (SCRIPT(ch)) { + int trig_count = 0; + for (t = TRIGGERS(SCRIPT(ch)); t; t = t->next) + trig_count++; + if (trig_count > 0) { + fprintf(fl, "triggers = ["); + for (t = TRIGGERS(SCRIPT(ch)), i = 0; t; t = t->next, i++) { + if (i > 0) + fputs(", ", fl); + fprintf(fl, "%d", GET_TRIG_VNUM(t)); + } + fprintf(fl, "]\n"); + } + } + + for (i = 1; i <= MAX_SKILLS; i++) + gain_times[i - 1] = (long)GET_SKILL_NEXT_GAIN(ch, i); + toml_write_long_array(fl, "skill_gain_next", gain_times, MAX_SKILLS); + + if (GET_LEVEL(ch) < LVL_IMMORT) { + for (i = 1; i <= MAX_SKILLS; i++) { + if (!GET_SKILL(ch, i)) + continue; + fprintf(fl, "\n[[skill]]\n"); + fprintf(fl, "id = %d\n", i); + fprintf(fl, "level = %d\n", GET_SKILL(ch, i)); + } + } - /* Save affects */ if (tmp_aff[0].spell > 0) { - fprintf(fl, "Affs:\n"); for (i = 0; i < MAX_AFFECT; i++) { aff = &tmp_aff[i]; - if (aff->spell) - fprintf(fl, "%d %d %d %d %d %d %d %d\n", aff->spell, aff->duration, - aff->modifier, aff->location, aff->bitvector[0], aff->bitvector[1], aff->bitvector[2], aff->bitvector[3]); + if (!aff->spell) + continue; + fprintf(fl, "\n[[affect]]\n"); + fprintf(fl, "spell = %d\n", aff->spell); + fprintf(fl, "duration = %d\n", aff->duration); + fprintf(fl, "modifier = %d\n", aff->modifier); + fprintf(fl, "location = %d\n", aff->location); + fprintf(fl, "bitvector = [%d, %d, %d, %d]\n", + aff->bitvector[0], aff->bitvector[1], aff->bitvector[2], aff->bitvector[3]); } - fprintf(fl, "0 0 0 0 0 0 0 0\n"); } - write_aliases_ascii(fl, ch); - save_char_vars_ascii(fl, ch); + for (struct alias_data *temp = GET_ALIASES(ch); temp; temp = temp->next) { + fprintf(fl, "\n[[alias]]\n"); + toml_write_kv_string(fl, "alias", temp->alias); + toml_write_kv_string(fl, "replacement", temp->replacement); + fprintf(fl, "type = %d\n", temp->type); + } + + if (SCRIPT(ch) && ch->script->global_vars) { + struct trig_var_data *vars; + + for (vars = ch->script->global_vars; vars; vars = vars->next) { + if (*vars->name == '-') + continue; + fprintf(fl, "\n[[var]]\n"); + toml_write_kv_string(fl, "name", vars->name); + fprintf(fl, "context = %ld\n", vars->context); + toml_write_kv_string(fl, "value", vars->value); + } + } fclose(fl); @@ -933,6 +1154,7 @@ void clean_pfiles(void) /* load_affects function now handles both 32-bit and 128-bit affect bitvectors for backward compatibility */ +#if 0 static void load_affects(FILE *fl, struct char_data *ch) { int num = 0, num2 = 0, num3 = 0, num4 = 0, num5 = 0, num6 = 0, num7 = 0, num8 = 0, i, n_vars; @@ -1085,3 +1307,4 @@ static void read_aliases_ascii(FILE *file, struct char_data *ch, int count) } } } +#endif diff --git a/src/set.c b/src/set.c index d1eb75d..7d63fb4 100644 --- a/src/set.c +++ b/src/set.c @@ -35,6 +35,7 @@ #include "genmob.h" #include "dg_scripts.h" #include "fight.h" +#include "toml.h" #include "set.h" @@ -2928,7 +2929,7 @@ ACMD(do_rsave) return; } - /* Save the owning zone's .wld file so the room data persists */ + /* Save the owning zone's .toml file so the room data persists */ ok = save_rooms(znum); if (ok) ok = RoomSave_now(rnum); @@ -2946,16 +2947,16 @@ ACMD(do_rsave) GET_ROOM_VNUM(rnum), zone_table[znum].number); mudlog(CMP, GET_LEVEL(ch), TRUE, - "RSAVE OK: %s room %d (rnum=%d) -> world/wld/%d.wld", + "RSAVE OK: %s room %d (rnum=%d) -> world/wld/%d.toml", GET_NAME(ch), GET_ROOM_VNUM(rnum), rnum, zone_table[znum].number); } -/* Write saved rooms under lib/world/rsv/.rsv (like wld/ zon/ obj/). */ +/* Write saved rooms under lib/world/rsv/.toml (like wld/ zon/ obj/). */ #ifndef ROOMSAVE_PREFIX #define ROOMSAVE_PREFIX LIB_WORLD "rsv/" #endif #ifndef ROOMSAVE_EXT -#define ROOMSAVE_EXT ".rsv" +#define ROOMSAVE_EXT ".toml" #endif static unsigned char *roomsave_dirty = NULL; @@ -2980,154 +2981,811 @@ room_rnum RoomSave_room_of_obj(struct obj_data *o) { return o->in_room; } -/* --- helper: read a list of objects until '.' or 'E' and return the head --- */ -/* Context-aware implementation: stop_on_E = 1 for nested B..E, 0 for top-level. */ -static struct obj_data *roomsave_read_list_ctx(FILE *fl, int stop_on_E) +static struct obj_data *RS_create_obj_by_vnum(obj_vnum ov); +static struct char_data *RS_create_mob_by_vnum(mob_vnum mv); +static void RS_apply_inventory_loadout(struct char_data *mob); + +/* --- RoomSave TOML helpers --- */ +static int roomsave_toml_get_int_default(toml_table_t *tab, const char *key, int def) { - char line[256]; - struct obj_data *head = NULL, *tail = NULL; + toml_datum_t v = toml_int_in(tab, key); + if (v.ok) + return (int)v.u.i; + return def; +} - while (fgets(line, sizeof(line), fl)) { - if (line[0] == '.') { - /* End of this list scope */ - break; - } +static int roomsave_toml_get_int_array(toml_table_t *tab, const char *key, int *out, int out_len) +{ + toml_array_t *arr; + int i, n; - if (stop_on_E && line[0] == 'E') { - /* End of nested (B..E) scope */ - break; - } + if (!tab || !out || out_len <= 0) + return 0; - /* For top-level reads (stop_on_E==0), or any non-'O', push back - so the outer #R reader can handle M/E/G/P or '.' */ - if (line[0] != 'O') { - long back = -((long)strlen(line)); - fseek(fl, back, SEEK_CUR); - break; - } + arr = toml_array_in(tab, key); + if (!arr) + return 0; + n = toml_array_nelem(arr); + if (n > out_len) + n = out_len; + for (i = 0; i < n; i++) { + toml_datum_t v = toml_int_at(arr, i); + if (v.ok) + out[i] = (int)v.u.i; + } + return n; +} - /* Parse object header: O vnum timer weight cost unused */ - int vnum, timer, weight, cost, unused_cost; - if (sscanf(line, "O %d %d %d %d %d", &vnum, &timer, &weight, &cost, &unused_cost) != 5) - continue; +struct roomsave_obj { + int vnum; + int timer; + int weight; + int cost; + int cost_per_day; + int extra_flags[EF_ARRAY_MAX]; + int wear_flags[TW_ARRAY_MAX]; + int values[NUM_OBJ_VAL_POSITIONS]; + int full; + struct roomsave_obj *contents; + struct roomsave_obj *next; +}; - /* IMPORTANT: read by VNUM (VIRTUAL), not real index */ - struct obj_data *obj = read_object((obj_vnum)vnum, VIRTUAL); - if (!obj) { - mudlog(NRM, LVL_IMMORT, TRUE, "RoomSave: read_object(vnum=%d) failed.", vnum); - /* Skip to next object/header or end-of-scope */ - long backpos; - while (fgets(line, sizeof(line), fl)) { - if (line[0] == 'O' || line[0] == '.' || (stop_on_E && line[0] == 'E')) { - backpos = -((long)strlen(line)); - fseek(fl, backpos, SEEK_CUR); - break; - } - } - continue; - } +struct roomsave_mob_item { + int vnum; + int wear_pos; + struct roomsave_obj *contents; + struct roomsave_mob_item *next; +}; - /* Apply core scalars */ - GET_OBJ_TIMER(obj) = timer; - GET_OBJ_WEIGHT(obj) = weight; - GET_OBJ_COST(obj) = cost; - GET_OBJ_COST_PER_DAY(obj) = 0; +struct roomsave_mob { + int vnum; + struct roomsave_mob_item *equipment; + struct roomsave_mob_item *inventory; + struct roomsave_mob *next; +}; - /* Clear array flags so missing slots don't keep proto bits */ -#ifdef EF_ARRAY_MAX -# ifdef GET_OBJ_EXTRA_AR - for (int i = 0; i < EF_ARRAY_MAX; i++) GET_OBJ_EXTRA_AR(obj, i) = 0; -# else - for (int i = 0; i < EF_ARRAY_MAX; i++) GET_OBJ_EXTRA(obj)[i] = 0; -# endif -#endif -#ifdef TW_ARRAY_MAX - for (int i = 0; i < TW_ARRAY_MAX; i++) GET_OBJ_WEAR(obj)[i] = 0; -#endif +struct roomsave_room { + int vnum; + long saved_at; + struct roomsave_obj *objects; + struct roomsave_mob *mobs; + struct roomsave_room *next; +}; - /* Read per-object lines until next 'O' or '.' or 'E'(when nested) */ - long backpos; - while (fgets(line, sizeof(line), fl)) { - if (line[0] == 'V') { - int idx, val; - if (sscanf(line, "V %d %d", &idx, &val) == 2) { -#ifdef NUM_OBJ_VAL_POSITIONS - if (idx >= 0 && idx < NUM_OBJ_VAL_POSITIONS) GET_OBJ_VAL(obj, idx) = val; -#else - if (idx >= 0 && idx < 6) GET_OBJ_VAL(obj, idx) = val; -#endif - } - continue; - } else if (line[0] == 'X') { /* extra flags */ - int idx, val; - if (sscanf(line, "X %d %d", &idx, &val) == 2) { -#if defined(EF_ARRAY_MAX) && defined(GET_OBJ_EXTRA_AR) - if (idx >= 0 && idx < EF_ARRAY_MAX) GET_OBJ_EXTRA_AR(obj, idx) = val; -#elif defined(EF_ARRAY_MAX) - if (idx >= 0 && idx < EF_ARRAY_MAX) GET_OBJ_EXTRA(obj)[idx] = val; -#else - if (idx == 0) GET_OBJ_EXTRA(obj) = val; -#endif - } - continue; - } else if (line[0] == 'W') { /* wear flags */ - int idx, val; - if (sscanf(line, "W %d %d", &idx, &val) == 2) { -#ifdef TW_ARRAY_MAX - if (idx >= 0 && idx < TW_ARRAY_MAX) GET_OBJ_WEAR(obj)[idx] = val; -#else - if (idx == 0) GET_OBJ_WEAR(obj) = val; -#endif - } - continue; - } else if (line[0] == 'B') { - /* Nested contents until matching 'E' */ - struct obj_data *child_head = roomsave_read_list_ctx(fl, 1 /* stop_on_E */); +static void roomsave_free_obj_list(struct roomsave_obj *obj) +{ + while (obj) { + struct roomsave_obj *next = obj->next; + if (obj->contents) + roomsave_free_obj_list(obj->contents); + free(obj); + obj = next; + } +} - /* Detach each node before obj_to_obj(), otherwise we lose siblings */ - for (struct obj_data *it = child_head, *next; it; it = next) { - next = it->next_content; /* remember original sibling */ - it->next_content = NULL; /* detach from temp list */ - obj_to_obj(it, obj); /* push into container (LIFO) */ - } - continue; - } else if (line[0] == 'O' || line[0] == '.' || (stop_on_E && line[0] == 'E')) { - /* Next object / end-of-scope: rewind one line for outer loop to see it */ - backpos = -((long)strlen(line)); - fseek(fl, backpos, SEEK_CUR); - break; - } else { - /* ignore unknown lines */ - continue; - } - } +static void roomsave_free_mob_items(struct roomsave_mob_item *item) +{ + while (item) { + struct roomsave_mob_item *next = item->next; + if (item->contents) + roomsave_free_obj_list(item->contents); + free(item); + item = next; + } +} - /* Append to this scope's list */ - if (GET_OBJ_TYPE(obj) == ITEM_MONEY) - update_money_obj(obj); +static void roomsave_free_mobs(struct roomsave_mob *mob) +{ + while (mob) { + struct roomsave_mob *next = mob->next; + roomsave_free_mob_items(mob->equipment); + roomsave_free_mob_items(mob->inventory); + free(mob); + mob = next; + } +} - obj->next_content = NULL; - if (!head) head = tail = obj; - else { tail->next_content = obj; tail = obj; } +static void roomsave_free_rooms(struct roomsave_room *room) +{ + while (room) { + struct roomsave_room *next = room->next; + roomsave_free_obj_list(room->objects); + roomsave_free_mobs(room->mobs); + free(room); + room = next; + } +} + +static struct roomsave_obj *roomsave_parse_obj_table(toml_table_t *tab, int full) +{ + struct roomsave_obj *obj; + toml_array_t *arr; + int i; + + if (!tab) + return NULL; + + CREATE(obj, struct roomsave_obj, 1); + obj->vnum = roomsave_toml_get_int_default(tab, "vnum", -1); + obj->full = full; + if (obj->vnum <= 0) { + free(obj); + return NULL; } + if (full) { + obj->timer = roomsave_toml_get_int_default(tab, "timer", 0); + obj->weight = roomsave_toml_get_int_default(tab, "weight", 0); + obj->cost = roomsave_toml_get_int_default(tab, "cost", 0); + obj->cost_per_day = roomsave_toml_get_int_default(tab, "cost_per_day", 0); + for (i = 0; i < EF_ARRAY_MAX; i++) + obj->extra_flags[i] = 0; + for (i = 0; i < TW_ARRAY_MAX; i++) + obj->wear_flags[i] = 0; + for (i = 0; i < NUM_OBJ_VAL_POSITIONS; i++) + obj->values[i] = 0; + roomsave_toml_get_int_array(tab, "extra_flags", obj->extra_flags, EF_ARRAY_MAX); + roomsave_toml_get_int_array(tab, "wear_flags", obj->wear_flags, TW_ARRAY_MAX); + roomsave_toml_get_int_array(tab, "values", obj->values, NUM_OBJ_VAL_POSITIONS); + } + + arr = toml_array_in(tab, "contents"); + if (arr) { + struct roomsave_obj *tail = NULL; + int n = toml_array_nelem(arr); + for (i = 0; i < n; i++) { + toml_table_t *ctab = toml_table_at(arr, i); + struct roomsave_obj *child = roomsave_parse_obj_table(ctab, full); + if (!child) + continue; + child->next = NULL; + if (!obj->contents) { + obj->contents = tail = child; + } else { + tail->next = child; + tail = child; + } + } + } + + return obj; +} + +static struct roomsave_mob_item *roomsave_parse_mob_item(toml_table_t *tab, int with_wear) +{ + struct roomsave_mob_item *item; + toml_array_t *arr; + int i; + + if (!tab) + return NULL; + + CREATE(item, struct roomsave_mob_item, 1); + item->vnum = roomsave_toml_get_int_default(tab, "vnum", -1); + item->wear_pos = with_wear ? roomsave_toml_get_int_default(tab, "wear_pos", 0) : -1; + if (item->vnum <= 0) { + free(item); + return NULL; + } + + arr = toml_array_in(tab, "contents"); + if (arr) { + struct roomsave_obj *tail = NULL; + int n = toml_array_nelem(arr); + for (i = 0; i < n; i++) { + toml_table_t *ctab = toml_table_at(arr, i); + struct roomsave_obj *child = roomsave_parse_obj_table(ctab, 0); + if (!child) + continue; + child->next = NULL; + if (!item->contents) { + item->contents = tail = child; + } else { + tail->next = child; + tail = child; + } + } + } + + return item; +} + +static struct roomsave_mob *roomsave_parse_mob_table(toml_table_t *tab) +{ + struct roomsave_mob *mob; + toml_array_t *arr; + int i; + + if (!tab) + return NULL; + + CREATE(mob, struct roomsave_mob, 1); + mob->vnum = roomsave_toml_get_int_default(tab, "vnum", -1); + if (mob->vnum <= 0) { + free(mob); + return NULL; + } + + arr = toml_array_in(tab, "equipment"); + if (arr) { + struct roomsave_mob_item *tail = NULL; + int n = toml_array_nelem(arr); + for (i = 0; i < n; i++) { + toml_table_t *itab = toml_table_at(arr, i); + struct roomsave_mob_item *item = roomsave_parse_mob_item(itab, 1); + if (!item) + continue; + item->next = NULL; + if (!mob->equipment) { + mob->equipment = tail = item; + } else { + tail->next = item; + tail = item; + } + } + } + + arr = toml_array_in(tab, "inventory"); + if (arr) { + struct roomsave_mob_item *tail = NULL; + int n = toml_array_nelem(arr); + for (i = 0; i < n; i++) { + toml_table_t *itab = toml_table_at(arr, i); + struct roomsave_mob_item *item = roomsave_parse_mob_item(itab, 0); + if (!item) + continue; + item->next = NULL; + if (!mob->inventory) { + mob->inventory = tail = item; + } else { + tail->next = item; + tail = item; + } + } + } + + return mob; +} + +static struct roomsave_room *roomsave_parse_room_table(toml_table_t *tab) +{ + struct roomsave_room *room; + toml_array_t *arr; + int i; + + if (!tab) + return NULL; + + CREATE(room, struct roomsave_room, 1); + room->vnum = roomsave_toml_get_int_default(tab, "vnum", -1); + room->saved_at = (long)roomsave_toml_get_int_default(tab, "saved_at", 0); + if (room->vnum <= 0) { + free(room); + return NULL; + } + + arr = toml_array_in(tab, "object"); + if (arr) { + struct roomsave_obj *tail = NULL; + int n = toml_array_nelem(arr); + for (i = 0; i < n; i++) { + toml_table_t *otab = toml_table_at(arr, i); + struct roomsave_obj *obj = roomsave_parse_obj_table(otab, 1); + if (!obj) + continue; + obj->next = NULL; + if (!room->objects) { + room->objects = tail = obj; + } else { + tail->next = obj; + tail = obj; + } + } + } + + arr = toml_array_in(tab, "mob"); + if (arr) { + struct roomsave_mob *tail = NULL; + int n = toml_array_nelem(arr); + for (i = 0; i < n; i++) { + toml_table_t *mtab = toml_table_at(arr, i); + struct roomsave_mob *mob = roomsave_parse_mob_table(mtab); + if (!mob) + continue; + mob->next = NULL; + if (!room->mobs) { + room->mobs = tail = mob; + } else { + tail->next = mob; + tail = mob; + } + } + } + + return room; +} + +static struct roomsave_room *roomsave_load_file_toml(const char *path) +{ + FILE *fp; + toml_table_t *tab; + toml_array_t *arr; + char errbuf[200]; + struct roomsave_room *head = NULL, *tail = NULL; + int i, n; + + fp = fopen(path, "r"); + if (!fp) + return NULL; + + tab = toml_parse_file(fp, errbuf, sizeof(errbuf)); + fclose(fp); + if (!tab) { + mudlog(NRM, LVL_IMMORT, TRUE, "RoomSave: parsing %s: %s", path, errbuf); + return NULL; + } + + arr = toml_array_in(tab, "room"); + if (!arr) { + toml_free(tab); + return NULL; + } + + n = toml_array_nelem(arr); + for (i = 0; i < n; i++) { + toml_table_t *rtab = toml_table_at(arr, i); + struct roomsave_room *room = roomsave_parse_room_table(rtab); + if (!room) + continue; + room->next = NULL; + if (!head) { + head = tail = room; + } else { + tail->next = room; + tail = room; + } + } + + toml_free(tab); return head; } -/* Keep your existing one-arg API for callers: top-level semantics (stop_on_E = 0). */ -static struct obj_data *roomsave_read_list(FILE *fl) +static void roomsave_write_inline_int_array(FILE *fp, const int *vals, int len) { - return roomsave_read_list_ctx(fl, 0); + int i; + fputc('[', fp); + for (i = 0; i < len; i++) { + if (i) + fputs(", ", fp); + fprintf(fp, "%d", vals[i]); + } + fputc(']', fp); } -/* ---------- Minimal line format ---------- -#R -O -V ; repeated for all value slots present on this obj -B ; begin contents of this object (container) -E ; end contents of this object -. ; end of room ------------------------------------------- */ +static void roomsave_write_int_array(FILE *fp, const char *key, const int *vals, int len) +{ + fprintf(fp, "%s = ", key); + roomsave_write_inline_int_array(fp, vals, len); + fputc('\n', fp); +} + +static void roomsave_write_inline_obj(FILE *fp, struct roomsave_obj *obj) +{ + fputs("{ vnum = ", fp); + fprintf(fp, "%d", obj->vnum); + if (obj->full) { + fprintf(fp, ", timer = %d, weight = %d, cost = %d, cost_per_day = %d", + obj->timer, obj->weight, obj->cost, obj->cost_per_day); + fputs(", extra_flags = ", fp); + roomsave_write_inline_int_array(fp, obj->extra_flags, EF_ARRAY_MAX); + fputs(", wear_flags = ", fp); + roomsave_write_inline_int_array(fp, obj->wear_flags, TW_ARRAY_MAX); + fputs(", values = ", fp); + roomsave_write_inline_int_array(fp, obj->values, NUM_OBJ_VAL_POSITIONS); + } + fputs(", contents = [", fp); + if (obj->contents) { + struct roomsave_obj *child; + int first = 1; + for (child = obj->contents; child; child = child->next) { + if (!first) + fputs(", ", fp); + roomsave_write_inline_obj(fp, child); + first = 0; + } + } + fputs("] }", fp); +} + +static void roomsave_write_room_objects(FILE *fp, struct roomsave_obj *obj) +{ + for (; obj; obj = obj->next) { + fprintf(fp, "\n[[room.object]]\n"); + fprintf(fp, "vnum = %d\n", obj->vnum); + fprintf(fp, "timer = %d\n", obj->timer); + fprintf(fp, "weight = %d\n", obj->weight); + fprintf(fp, "cost = %d\n", obj->cost); + fprintf(fp, "cost_per_day = %d\n", obj->cost_per_day); + roomsave_write_int_array(fp, "extra_flags", obj->extra_flags, EF_ARRAY_MAX); + roomsave_write_int_array(fp, "wear_flags", obj->wear_flags, TW_ARRAY_MAX); + roomsave_write_int_array(fp, "values", obj->values, NUM_OBJ_VAL_POSITIONS); + fputs("contents = [", fp); + if (obj->contents) { + struct roomsave_obj *child; + int first = 1; + for (child = obj->contents; child; child = child->next) { + if (!first) + fputs(", ", fp); + roomsave_write_inline_obj(fp, child); + first = 0; + } + } + fputs("]\n", fp); + } +} + +static void roomsave_write_mob_items(FILE *fp, const char *table_name, struct roomsave_mob_item *item) +{ + for (; item; item = item->next) { + fprintf(fp, "\n[[room.mob.%s]]\n", table_name); + if (!strcmp(table_name, "equipment")) + fprintf(fp, "wear_pos = %d\n", item->wear_pos); + fprintf(fp, "vnum = %d\n", item->vnum); + fputs("contents = [", fp); + if (item->contents) { + struct roomsave_obj *child; + int first = 1; + for (child = item->contents; child; child = child->next) { + if (!first) + fputs(", ", fp); + roomsave_write_inline_obj(fp, child); + first = 0; + } + } + fputs("]\n", fp); + } +} + +static void roomsave_write_rooms(FILE *fp, struct roomsave_room *room) +{ + if (!room) { + fputs("room = []\n", fp); + return; + } + + for (; room; room = room->next) { + fprintf(fp, "[[room]]\n"); + fprintf(fp, "vnum = %d\n", room->vnum); + fprintf(fp, "saved_at = %ld\n", room->saved_at); + roomsave_write_room_objects(fp, room->objects); + if (room->mobs) { + struct roomsave_mob *mob; + for (mob = room->mobs; mob; mob = mob->next) { + fprintf(fp, "\n[[room.mob]]\n"); + fprintf(fp, "vnum = %d\n", mob->vnum); + roomsave_write_mob_items(fp, "equipment", mob->equipment); + roomsave_write_mob_items(fp, "inventory", mob->inventory); + } + } + fputs("\n", fp); + } +} + +static void roomsave_apply_obj_fields(struct obj_data *obj, struct roomsave_obj *rs) +{ + int i; + + if (!obj || !rs || !rs->full) + return; + + GET_OBJ_TIMER(obj) = rs->timer; + GET_OBJ_WEIGHT(obj) = rs->weight; + GET_OBJ_COST(obj) = rs->cost; + GET_OBJ_COST_PER_DAY(obj) = rs->cost_per_day; + +#if defined(EF_ARRAY_MAX) && defined(GET_OBJ_EXTRA_AR) + for (i = 0; i < EF_ARRAY_MAX; i++) + GET_OBJ_EXTRA_AR(obj, i) = rs->extra_flags[i]; +#elif defined(EF_ARRAY_MAX) + for (i = 0; i < EF_ARRAY_MAX; i++) + GET_OBJ_EXTRA(obj)[i] = rs->extra_flags[i]; +#else + GET_OBJ_EXTRA(obj) = rs->extra_flags[0]; +#endif + +#ifdef TW_ARRAY_MAX + for (i = 0; i < TW_ARRAY_MAX; i++) + GET_OBJ_WEAR(obj)[i] = rs->wear_flags[i]; +#else + GET_OBJ_WEAR(obj) = rs->wear_flags[0]; +#endif + +#ifdef NUM_OBJ_VAL_POSITIONS + for (i = 0; i < NUM_OBJ_VAL_POSITIONS; i++) + GET_OBJ_VAL(obj, i) = rs->values[i]; +#else + for (i = 0; i < 6; i++) + GET_OBJ_VAL(obj, i) = rs->values[i]; +#endif +} + +static void roomsave_restore_obj_contents(struct obj_data *parent, struct roomsave_obj *list) +{ + struct roomsave_obj *it; + + if (!parent) + return; + + for (it = list; it; it = it->next) { + struct obj_data *obj = RS_create_obj_by_vnum((obj_vnum)it->vnum); + if (!obj) + continue; + roomsave_apply_obj_fields(obj, it); + obj_to_obj(obj, parent); + if (it->contents) + roomsave_restore_obj_contents(obj, it->contents); + if (GET_OBJ_TYPE(obj) == ITEM_MONEY) + update_money_obj(obj); + } +} + +static int roomsave_restore_room_objects(struct roomsave_room *room, room_rnum rnum) +{ + struct roomsave_obj *it; + int count = 0; + + for (it = room->objects; it; it = it->next) { + struct obj_data *obj = RS_create_obj_by_vnum((obj_vnum)it->vnum); + if (!obj) + continue; + roomsave_apply_obj_fields(obj, it); + if (it->contents) + roomsave_restore_obj_contents(obj, it->contents); + obj_to_room(obj, rnum); + if (GET_OBJ_TYPE(obj) == ITEM_MONEY) + update_money_obj(obj); + count++; + } + + return count; +} + +static void roomsave_restore_mob_items(struct char_data *mob, struct roomsave_mob_item *items, int is_equipment) +{ + struct roomsave_mob_item *it; + + for (it = items; it; it = it->next) { + struct obj_data *obj = RS_create_obj_by_vnum((obj_vnum)it->vnum); + if (!obj) + continue; + + if (is_equipment) { + int pos = it->wear_pos; + if (pos < 0 || pos >= NUM_WEARS) + pos = WEAR_HOLD; + equip_char(mob, obj, pos); + } else { + obj_to_char(obj, mob); + } + + if (it->contents) + roomsave_restore_obj_contents(obj, it->contents); + } +} + +static int roomsave_restore_room_mobs(struct roomsave_room *room, room_rnum rnum) +{ + struct roomsave_mob *it; + int count = 0; + + for (it = room->mobs; it; it = it->next) { + struct char_data *mob = RS_create_mob_by_vnum((mob_vnum)it->vnum); + int saw_inventory = 0; + + if (!mob) + continue; + + char_to_room(mob, rnum); + if (IN_ROOM(mob) != rnum) + char_to_room(mob, rnum); + + roomsave_restore_mob_items(mob, it->equipment, 1); + roomsave_restore_mob_items(mob, it->inventory, 0); + + if (it->inventory) + saw_inventory = 1; + + if (!saw_inventory) + RS_apply_inventory_loadout(mob); + + count++; + } + + return count; +} + +static struct roomsave_obj *roomsave_build_obj(struct obj_data *obj, int full) +{ + struct roomsave_obj *rs; + int i; + + if (!obj || GET_OBJ_VNUM(obj) <= 0) + return NULL; + + CREATE(rs, struct roomsave_obj, 1); + rs->vnum = (int)GET_OBJ_VNUM(obj); + rs->full = full; + + if (full) { + rs->timer = GET_OBJ_TIMER(obj); + rs->weight = GET_OBJ_WEIGHT(obj); + rs->cost = GET_OBJ_COST(obj); + rs->cost_per_day = GET_OBJ_COST_PER_DAY(obj); +#if defined(EF_ARRAY_MAX) && defined(GET_OBJ_EXTRA_AR) + for (i = 0; i < EF_ARRAY_MAX; i++) + rs->extra_flags[i] = GET_OBJ_EXTRA_AR(obj, i); +#elif defined(EF_ARRAY_MAX) + for (i = 0; i < EF_ARRAY_MAX; i++) + rs->extra_flags[i] = GET_OBJ_EXTRA(obj)[i]; +#else + rs->extra_flags[0] = GET_OBJ_EXTRA(obj); +#endif +#ifdef TW_ARRAY_MAX + for (i = 0; i < TW_ARRAY_MAX; i++) + rs->wear_flags[i] = GET_OBJ_WEAR(obj)[i]; +#else + rs->wear_flags[0] = GET_OBJ_WEAR(obj); +#endif +#ifdef NUM_OBJ_VAL_POSITIONS + for (i = 0; i < NUM_OBJ_VAL_POSITIONS; i++) + rs->values[i] = GET_OBJ_VAL(obj, i); +#else + for (i = 0; i < 6; i++) + rs->values[i] = GET_OBJ_VAL(obj, i); +#endif + } + + if (obj->contains) { + struct roomsave_obj *tail = NULL; + struct obj_data *child; + for (child = obj->contains; child; child = child->next_content) { + struct roomsave_obj *child_rs = roomsave_build_obj(child, full); + if (!child_rs) + continue; + child_rs->next = NULL; + if (!rs->contents) { + rs->contents = tail = child_rs; + } else { + tail->next = child_rs; + tail = child_rs; + } + } + } + + return rs; +} + +static struct roomsave_mob_item *roomsave_build_mob_item(struct obj_data *obj, int wear_pos) +{ + struct roomsave_mob_item *item; + struct obj_data *child; + struct roomsave_obj *tail = NULL; + + if (!obj || GET_OBJ_VNUM(obj) <= 0) + return NULL; + + CREATE(item, struct roomsave_mob_item, 1); + item->vnum = (int)GET_OBJ_VNUM(obj); + item->wear_pos = wear_pos; + + for (child = obj->contains; child; child = child->next_content) { + struct roomsave_obj *child_rs = roomsave_build_obj(child, 0); + if (!child_rs) + continue; + child_rs->next = NULL; + if (!item->contents) { + item->contents = tail = child_rs; + } else { + tail->next = child_rs; + tail = child_rs; + } + } + + return item; +} + +static struct roomsave_mob *roomsave_build_mob(struct char_data *mob) +{ + struct roomsave_mob *rs; + int w; + struct obj_data *obj; + struct roomsave_mob_item *tail; + + if (!mob || !IS_NPC(mob) || GET_MOB_VNUM(mob) <= 0) + return NULL; + + CREATE(rs, struct roomsave_mob, 1); + rs->vnum = (int)GET_MOB_VNUM(mob); + + tail = NULL; + for (w = 0; w < NUM_WEARS; w++) { + obj = GET_EQ(mob, w); + if (!obj) + continue; + { + struct roomsave_mob_item *item = roomsave_build_mob_item(obj, w); + if (!item) + continue; + item->next = NULL; + if (!rs->equipment) { + rs->equipment = tail = item; + } else { + tail->next = item; + tail = item; + } + } + } + + tail = NULL; + for (obj = mob->carrying; obj; obj = obj->next_content) { + struct roomsave_mob_item *item = roomsave_build_mob_item(obj, -1); + if (!item) + continue; + item->next = NULL; + if (!rs->inventory) { + rs->inventory = tail = item; + } else { + tail->next = item; + tail = item; + } + } + + return rs; +} + +static struct roomsave_room *roomsave_build_room(room_rnum rnum) +{ + struct roomsave_room *room; + struct roomsave_obj *tail_obj = NULL; + struct roomsave_mob *tail_mob = NULL; + struct obj_data *obj; + struct char_data *mob; + + if (rnum == NOWHERE) + return NULL; + + CREATE(room, struct roomsave_room, 1); + room->vnum = world[rnum].number; + room->saved_at = time(0); + + for (obj = world[rnum].contents; obj; obj = obj->next_content) { + struct roomsave_obj *rs = roomsave_build_obj(obj, 1); + if (!rs) + continue; + rs->next = NULL; + if (!room->objects) { + room->objects = tail_obj = rs; + } else { + tail_obj->next = rs; + tail_obj = rs; + } + } + + for (mob = world[rnum].people; mob; mob = mob->next_in_room) { + struct roomsave_mob *rs = roomsave_build_mob(mob); + if (!rs) + continue; + rs->next = NULL; + if (!room->mobs) { + room->mobs = tail_mob = rs; + } else { + tail_mob->next = rs; + tail_mob = rs; + } + } + + return room; +} static void ensure_dir_exists(const char *path) { if (mkdir(path, 0775) == -1 && errno != EEXIST) { @@ -3143,68 +3801,17 @@ static int roomsave_zone_for_rnum(room_rnum rnum) { return zone_table[znum].number; /* e.g., 1 for rooms 100–199, 2 for 200–299, etc. */ } -/* lib/world/rsv/.rsv */ +/* lib/world/rsv/.toml */ static void roomsave_zone_filename(int zone_vnum, char *out, size_t outsz) { snprintf(out, outsz, "%s%d%s", ROOMSAVE_PREFIX, zone_vnum, ROOMSAVE_EXT); } -/* Write one object (and its recursive contents) */ -static void write_one_object(FILE *fl, struct obj_data *obj) { - int i; - - /* Core scalars (flags printed separately per-slot) */ - fprintf(fl, "O %d %d %d %d %d\n", - GET_OBJ_VNUM(obj), - GET_OBJ_TIMER(obj), - GET_OBJ_WEIGHT(obj), - GET_OBJ_COST(obj), - GET_OBJ_COST_PER_DAY(obj)); - -/* Extra flags array */ -#if defined(EF_ARRAY_MAX) && defined(GET_OBJ_EXTRA_AR) - for (i = 0; i < EF_ARRAY_MAX; i++) - fprintf(fl, "X %d %d\n", i, GET_OBJ_EXTRA_AR(obj, i)); -#elif defined(EF_ARRAY_MAX) - for (i = 0; i < EF_ARRAY_MAX; i++) - fprintf(fl, "X %d %d\n", i, GET_OBJ_EXTRA(obj)[i]); -#else - fprintf(fl, "X %d %d\n", 0, GET_OBJ_EXTRA(obj)); -#endif - -/* Wear flags array */ -#ifdef TW_ARRAY_MAX - for (i = 0; i < TW_ARRAY_MAX; i++) - fprintf(fl, "W %d %d\n", i, GET_OBJ_WEAR(obj)[i]); -#else - fprintf(fl, "W %d %d\n", 0, GET_OBJ_WEAR(obj)); -#endif - - /* Values[] (durability, liquids, charges, etc.) */ -#ifdef NUM_OBJ_VAL_POSITIONS - for (i = 0; i < NUM_OBJ_VAL_POSITIONS; i++) - fprintf(fl, "V %d %d\n", i, GET_OBJ_VAL(obj, i)); -#else - for (i = 0; i < 6; i++) - fprintf(fl, "V %d %d\n", i, GET_OBJ_VAL(obj, i)); -#endif - - /* Contents (recursive) */ - if (obj->contains) { - struct obj_data *cont; - fprintf(fl, "B\n"); - for (cont = obj->contains; cont; cont = cont->next_content) - write_one_object(fl, cont); - fprintf(fl, "E\n"); - } -} - -/* Forward declaration for RoomSave_now*/ -static void RS_write_room_mobs(FILE *out, room_rnum rnum); - /* Public: write the entire room’s contents */ int RoomSave_now(room_rnum rnum) { - char path[PATH_MAX], tmp[PATH_MAX], line[512]; - FILE *in = NULL, *out = NULL; + char path[PATH_MAX], tmp[PATH_MAX]; + FILE *out = NULL; + struct roomsave_room *rooms = NULL, *it = NULL, *prev = NULL; + struct roomsave_room *new_room = NULL; room_vnum rvnum; int zvnum; @@ -3228,73 +3835,61 @@ int RoomSave_now(room_rnum rnum) { } } + rooms = roomsave_load_file_toml(path); + new_room = roomsave_build_room(rnum); + if (!new_room) + return 0; + + for (it = rooms; it; it = it->next) { + if (it->vnum == (int)rvnum) + break; + prev = it; + } + + if (it) { + roomsave_free_obj_list(it->objects); + roomsave_free_mobs(it->mobs); + it->saved_at = new_room->saved_at; + it->objects = new_room->objects; + it->mobs = new_room->mobs; + free(new_room); + } else { + if (!rooms) { + rooms = new_room; + } else if (prev) { + prev->next = new_room; + } + } + if (!(out = fopen(tmp, "w"))) { mudlog(NRM, LVL_IMMORT, TRUE, "SYSERR: RoomSave: fopen(%s) failed: %s", tmp, strerror(errno)); + roomsave_free_rooms(rooms); return 0; } - if ((in = fopen(path, "r")) != NULL) { - while (fgets(line, sizeof(line), in)) { - if (strncmp(line, "#R ", 3) == 0) { - int file_rvnum; - long ts; - if (sscanf(line, "#R %d %ld", &file_rvnum, &ts) == 2) { - if (file_rvnum == (int)rvnum) { - /* Skip old block completely until and including '.' line */ - while (fgets(line, sizeof(line), in)) { - if (line[0] == '.') { - /* consume it and break */ - break; - } - } - continue; /* do NOT write skipped lines */ - } - } - } - fputs(line, out); /* keep unrelated lines */ - } - fclose(in); - } - - /* Append new block */ - fprintf(out, "#R %d %ld\n", rvnum, (long)time(0)); - - RS_write_room_mobs(out, rnum); - - for (struct obj_data *obj = world[rnum].contents; obj; obj = obj->next_content) - write_one_object(out, obj); - - /* Always terminate block */ - fprintf(out, ".\n"); + roomsave_write_rooms(out, rooms); if (fclose(out) != 0) { mudlog(NRM, LVL_IMMORT, TRUE, "SYSERR: RoomSave: fclose(%s) failed: %s", tmp, strerror(errno)); + roomsave_free_rooms(rooms); return 0; } if (rename(tmp, path) != 0) { mudlog(NRM, LVL_IMMORT, TRUE, "SYSERR: RoomSave: rename(%s -> %s) failed: %s", tmp, path, strerror(errno)); + roomsave_free_rooms(rooms); return 0; } + roomsave_free_rooms(rooms); return 1; } -/* --- M/E/G/P load helpers (mob restore) -------------------------------- */ - -struct rs_load_ctx { - room_rnum rnum; - struct char_data *cur_mob; /* last mob spawned by 'M' */ - struct obj_data *stack[16]; /* container stack by depth for 'P' */ - int saw_inventory; /* saw any inventory lines (G/P from inventory) */ - int last_item_was_inventory; /* last item source was inventory (G) */ -}; - static struct obj_data *RS_create_obj_by_vnum(obj_vnum ov) { obj_rnum ornum; if (ov <= 0) return NULL; @@ -3364,33 +3959,6 @@ static void RS_apply_inventory_loadout(struct char_data *mob) { } } -static void RS_finalize_mob_loadout(struct rs_load_ctx *ctx) { - if (!ctx || !ctx->cur_mob) - return; - if (!ctx->saw_inventory) - RS_apply_inventory_loadout(ctx->cur_mob); -} - - -/* Reset the loader context before reading a new #R block */ -static void RS_ctx_clear(struct rs_load_ctx *ctx) { - if (!ctx) - return; - - /* DO NOT reset ctx->rnum — each #R block sets this explicitly - * before parsing mobs or objects. Resetting it causes cross-room - * bleed (e.g., mobs from one room spawning in another). - */ - - ctx->cur_mob = NULL; - ctx->saw_inventory = 0; - ctx->last_item_was_inventory = 0; - - /* Clear all container stack pointers */ - for (int i = 0; i < 16; i++) - ctx->stack[i] = NULL; -} - /* Optional autosave hook (invoked by limits.c:point_update). */ void RoomSave_autosave_tick(void) { /* Iterate all rooms; only save flagged ones. */ @@ -3400,98 +3968,6 @@ void RoomSave_autosave_tick(void) { } } -/* Forward decl so RS_parse_mob_line can use it without implicit declaration */ -static void RS_stack_clear(struct rs_load_ctx *ctx); - -/* Handle one line inside a #R block. Returns 1 if handled here. */ -static int RS_parse_mob_line(struct rs_load_ctx *ctx, char *line) -{ - if (!line) return 0; - while (*line == ' ' || *line == '\t') ++line; - if (!*line) return 0; - - switch (line[0]) { - case 'M': { - mob_vnum mv; - if (sscanf(line+1, " %d", (int *)&mv) != 1) return 0; - - RS_finalize_mob_loadout(ctx); - ctx->cur_mob = RS_create_mob_by_vnum(mv); - if (!ctx->cur_mob) return 1; - - /* Place in the block's room */ - char_to_room(ctx->cur_mob, ctx->rnum); - - /* Safety: if anything put it elsewhere, force it back */ - if (IN_ROOM(ctx->cur_mob) != ctx->rnum) - char_to_room(ctx->cur_mob, ctx->rnum); - - RS_stack_clear(ctx); /* clear only container stack */ - ctx->saw_inventory = 0; - ctx->last_item_was_inventory = 0; - return 1; - } - - case 'E': { /* E */ - int pos; obj_vnum ov; struct obj_data *obj; - if (!ctx->cur_mob) return 1; /* orphan -> ignore */ - if (sscanf(line+1, " %d %d", &pos, (int *)&ov) != 2) return 0; - obj = RS_create_obj_by_vnum(ov); - if (!obj) return 1; - - if (pos < 0 || pos >= NUM_WEARS) pos = WEAR_HOLD; /* clamp */ - equip_char(ctx->cur_mob, obj, pos); - - /* Reset ONLY container stack for following P-lines; keep cur_mob */ - RS_stack_clear(ctx); - ctx->stack[0] = obj; - ctx->last_item_was_inventory = 0; - return 1; - } - - case 'G': { /* G */ - obj_vnum ov; struct obj_data *obj; - if (!ctx->cur_mob) return 1; /* orphan -> ignore */ - if (sscanf(line+1, " %d", (int *)&ov) != 1) return 0; - obj = RS_create_obj_by_vnum(ov); - if (!obj) return 1; - - obj_to_char(obj, ctx->cur_mob); - - RS_stack_clear(ctx); - ctx->stack[0] = obj; - ctx->saw_inventory = 1; - ctx->last_item_was_inventory = 1; - return 1; - } - - case 'P': { /* P : put into last obj at (depth-1) */ - int depth; obj_vnum ov; struct obj_data *parent, *obj; - if (sscanf(line+1, " %d %d", &depth, (int *)&ov) != 2) return 0; - if (depth <= 0 || depth >= (int)(sizeof(ctx->stack)/sizeof(ctx->stack[0]))) - return 1; - parent = ctx->stack[depth-1]; - if (!parent) return 1; - - obj = RS_create_obj_by_vnum(ov); - if (!obj) return 1; - obj_to_obj(obj, parent); - - ctx->stack[depth] = obj; - { int d; for (d = depth+1; d < (int)(sizeof(ctx->stack)/sizeof(ctx->stack[0])); ++d) ctx->stack[d] = NULL; } - if (ctx->last_item_was_inventory) - ctx->saw_inventory = 1; - return 1; - } - - default: - return 0; - } -} - -/* Forward decls for mob restore helpers */ -static void RS_stack_clear(struct rs_load_ctx *ctx); - void RoomSave_boot(void) { DIR *dirp; @@ -3506,15 +3982,18 @@ void RoomSave_boot(void) return; } - log("RoomSave: scanning %s for *.rsv", ROOMSAVE_PREFIX); + log("RoomSave: scanning %s for *.toml", ROOMSAVE_PREFIX); while ((dp = readdir(dirp))) { size_t n = strlen(dp->d_name); - if (n < 5) continue; /* skip . and .. */ - if (strcmp(dp->d_name + n - 4, ROOMSAVE_EXT) != 0) continue; + size_t extlen = strlen(ROOMSAVE_EXT); + if (n <= extlen) continue; /* skip . and .. */ + if (strcmp(dp->d_name + n - extlen, ROOMSAVE_EXT) != 0) continue; { char path[PATH_MAX]; + struct roomsave_room *rooms; + struct roomsave_room *room; int wn = snprintf(path, sizeof(path), "%s%s", ROOMSAVE_PREFIX, dp->d_name); if (wn < 0 || wn >= (int)sizeof(path)) { mudlog(NRM, LVL_IMMORT, TRUE, @@ -3523,123 +4002,49 @@ void RoomSave_boot(void) continue; } - FILE *fl = fopen(path, "r"); - if (!fl) { - mudlog(NRM, LVL_IMMORT, TRUE, - "SYSERR: RoomSave_boot: fopen(%s) failed: %s", - path, strerror(errno)); - continue; - } - log("RoomSave: reading %s", path); + rooms = roomsave_load_file_toml(path); + if (!rooms) + continue; + int blocks = 0; int restored_objs_total = 0; int restored_mobs_total = 0; - /* Outer loop: read every #R block in this .rsv file */ - char line[512]; - while (fgets(line, sizeof(line), fl)) { - - /* Skip until a valid #R header */ - if (strncmp(line, "#R ", 3) != 0) - continue; - - /* Parse header line */ - int rvnum; long ts; - if (sscanf(line, "#R %d %ld", &rvnum, &ts) != 2) { - mudlog(NRM, LVL_IMMORT, TRUE, - "RoomSave: malformed #R header in %s: %s", path, line); - /* Skip malformed block */ - while (fgets(line, sizeof(line), fl)) - if (line[0] == '.') break; - continue; - } + for (room = rooms; room; room = room->next) { + room_rnum rnum = real_room((room_vnum)room->vnum); + int count_objs = 0; + int count_mobs = 0; blocks++; - /* Resolve the room for this block */ - room_rnum rnum = real_room((room_vnum)rvnum); if (rnum == NOWHERE) { mudlog(NRM, LVL_IMMORT, FALSE, "RoomSave: unknown room vnum %d in %s (skipping)", - rvnum, path); - /* Skip to next block */ - while (fgets(line, sizeof(line), fl)) - if (line[0] == '.') break; + room->vnum, path); continue; } - /* Clear this room's ground contents before restoring */ while (world[rnum].contents) extract_obj(world[rnum].contents); - /* Clear and set mob context for this block */ - struct rs_load_ctx mctx; - RS_ctx_clear(&mctx); - mctx.rnum = rnum; - - /* Per-block counts */ - int count_objs = 0, count_mobs = 0; - char inner[512]; - - /* Inner loop: read this #R block until '.' */ - while (fgets(inner, sizeof(inner), fl)) { - - /* Trim spaces */ - while (inner[0] == ' ' || inner[0] == '\t') - memmove(inner, inner + 1, strlen(inner)); - - /* Stop at end of block */ - if (inner[0] == '.') - break; - - /* Defensive: stop if another #R starts (malformed file) */ - if (!strncmp(inner, "#R ", 3)) { - fseek(fl, -((long)strlen(inner)), SEEK_CUR); - break; - } - - /* Handle object blocks */ - if (inner[0] == 'O') { - long pos = ftell(fl); - fseek(fl, pos - strlen(inner), SEEK_SET); - struct obj_data *list = roomsave_read_list(fl); - for (struct obj_data *it = list, *next; it; it = next) { - next = it->next_content; - it->next_content = NULL; - obj_to_room(it, rnum); - count_objs++; - } - continue; - } - - /* Handle mob & equipment/inventory */ - if (RS_parse_mob_line(&mctx, inner)) { - if (inner[0] == 'M') - count_mobs++; - continue; - } - - /* Unknown token: ignore gracefully */ - } - - RS_finalize_mob_loadout(&mctx); + count_objs = roomsave_restore_room_objects(room, rnum); + count_mobs = roomsave_restore_room_mobs(room, rnum); restored_objs_total += count_objs; restored_mobs_total += count_mobs; if (count_mobs > 0) log("RoomSave: room %d <- %d object(s) and %d mob(s)", - rvnum, count_objs, count_mobs); + room->vnum, count_objs, count_mobs); else - log("RoomSave: room %d <- %d object(s)", rvnum, count_objs); + log("RoomSave: room %d <- %d object(s)", room->vnum, count_objs); } log("RoomSave: finished %s (blocks=%d, objects=%d, mobs=%d)", path, blocks, restored_objs_total, restored_mobs_total); - - fclose(fl); + roomsave_free_rooms(rooms); } } @@ -3647,62 +4052,3 @@ void RoomSave_boot(void) } /* ======== MOB SAVE: write NPCs and their equipment/inventory ========== */ - -/* Depth-aware writer for container contents under a parent object. - * Writes: P - * depth starts at 1 for direct children. */ -static void RS_write_P_chain(FILE *fp, struct obj_data *parent, int depth) { - struct obj_data *c; - for (c = parent->contains; c; c = c->next_content) { - obj_vnum cv = GET_OBJ_VNUM(c); - if (cv <= 0) continue; /* skip non-proto / invalid */ - fprintf(fp, "P %d %d\n", depth, (int)cv); - if (c->contains) - RS_write_P_chain(fp, c, depth + 1); - } -} - -/* Writes: E (then P-chain) */ -static void RS_write_mob_equipment(FILE *fp, struct char_data *mob) { - int w; - for (w = 0; w < NUM_WEARS; ++w) { - struct obj_data *eq = GET_EQ(mob, w); - if (!eq) continue; - if (GET_OBJ_VNUM(eq) <= 0) continue; - fprintf(fp, "E %d %d\n", w, (int)GET_OBJ_VNUM(eq)); - if (eq->contains) RS_write_P_chain(fp, eq, 1); - } -} - -/* Writes: G for inventory items (then P-chain) */ -static void RS_write_mob_inventory(FILE *fp, struct char_data *mob) { - struct obj_data *o; - for (o = mob->carrying; o; o = o->next_content) { - if (GET_OBJ_VNUM(o) <= 0) continue; - fprintf(fp, "G %d\n", (int)GET_OBJ_VNUM(o)); - if (o->contains) RS_write_P_chain(fp, o, 1); - } -} - -/* Top-level writer: for each NPC in room, emit: - * M - * [E ...]* - * [G ...]* - * (Players are ignored.) */ -static void RS_write_room_mobs(FILE *out, room_rnum rnum) { - struct char_data *mob; - for (mob = world[rnum].people; mob; mob = mob->next_in_room) { - if (!IS_NPC(mob)) continue; - if (GET_MOB_VNUM(mob) <= 0) continue; - fprintf(out, "M %d\n", (int)GET_MOB_VNUM(mob)); - RS_write_mob_equipment(out, mob); - RS_write_mob_inventory(out, mob); - } -} - -/* Clear only the container stack, NOT cur_mob */ -static void RS_stack_clear(struct rs_load_ctx *ctx) { - int i; - for (i = 0; i < (int)(sizeof(ctx->stack)/sizeof(ctx->stack[0])); ++i) - ctx->stack[i] = NULL; -} diff --git a/src/shop.c b/src/shop.c index 44dd985..bcf1111 100644 --- a/src/shop.c +++ b/src/shop.c @@ -25,6 +25,17 @@ #include "modify.h" #include "spells.h" /* for skill_name() */ #include "screen.h" +#include "toml.h" + +static char *toml_get_string_dup(toml_table_t *tab, const char *key) +{ + toml_datum_t d = toml_string_in(tab, key); + + if (!d.ok) + return NULL; + + return d.u.s; +} /* Global variables definitions used externally */ /* Constant list for printing out who we sell to */ @@ -57,9 +68,11 @@ static int top(struct stack_data *stack); /**< @todo Move to utils.c */ static int pop(struct stack_data *stack); /**< @todo Move to utils.c */ static char *list_object(struct obj_data *obj, int cnt, int oindex, int shop_nr, struct char_data *keeper, struct char_data *seller); static void sort_keeper_objs(struct char_data *keeper, int shop_nr); +#if 0 static char *read_shop_message(int mnum, room_vnum shr, FILE *shop_f, const char *why); static int read_type_list(FILE *shop_f, struct shop_buy_data *list, int new_format, int max); static int read_list(FILE *shop_f, struct shop_buy_data *list, int new_format, int max, int type); +#endif static void shopping_list(char *arg, struct char_data *ch, struct char_data *keeper, int shop_nr); static void shopping_value(char *arg, struct char_data *ch, struct char_data *keeper, int shop_nr); static void shopping_sell(char *arg, struct char_data *ch, struct char_data *keeper, int shop_nr); @@ -78,6 +91,22 @@ static int is_ok(struct char_data *keeper, struct char_data *ch, int shop_nr); static void evaluate_operation(struct stack_data *ops, struct stack_data *vals); static int find_oper_num(char token); static int evaluate_expression(struct obj_data *obj, char *expr); + +static float toml_get_float_default(toml_table_t *tab, const char *key, float def) +{ + toml_datum_t v = toml_double_in(tab, key); + if (!v.ok) + return def; + return (float)v.u.d; +} + +static int toml_get_int_default(toml_table_t *tab, const char *key, int def) +{ + toml_datum_t v = toml_int_in(tab, key); + if (!v.ok) + return def; + return (int)v.u.i; +} static int trade_with(struct obj_data *item, int shop_nr); static int same_obj(struct obj_data *obj1, struct obj_data *obj2); static int shop_producing(struct obj_data *item, int shop_nr); @@ -86,9 +115,11 @@ static char *times_message(struct obj_data *obj, char *name, int num); static int buy_price(struct obj_data *obj, int shop_nr, struct char_data *keeper, struct char_data *buyer); static int sell_price(struct obj_data *obj, int shop_nr, struct char_data *keeper, struct char_data *seller); static int ok_shop_room(int shop_nr, room_vnum room); +#if 0 static int add_to_shop_list(struct shop_buy_data *list, int type, int *len, int *val); static int end_read_list(struct shop_buy_data *list, int len, int error); static void read_line(FILE *shop_f, const char *string, void *data); +#endif /* Local file scope only variables */ static int cmd_say; @@ -1104,6 +1135,7 @@ int ok_damage_shopkeeper(struct char_data *ch, struct char_data *victim) } /* val == obj_vnum and obj_rnum (?) */ +#if 0 static int add_to_shop_list(struct shop_buy_data *list, int type, int *len, int *val) { if (*val != NOTHING && *val >= 0) { /* necessary after changing to unsigned v/rnums -- Welcor */ @@ -1140,7 +1172,9 @@ static void read_line(FILE *shop_f, const char *string, void *data) exit(1); } } +#endif +#if 0 static int read_list(FILE *shop_f, struct shop_buy_data *list, int new_format, int max, int type) { @@ -1160,7 +1194,9 @@ static int read_list(FILE *shop_f, struct shop_buy_data *list, int new_format, } return (end_read_list(list, len, error)); } +#endif +#if 0 /* END_OF inefficient. */ static int read_type_list(FILE *shop_f, struct shop_buy_data *list, int new_format, int max) @@ -1251,76 +1287,172 @@ static char *read_shop_message(int mnum, room_vnum shr, FILE *shop_f, const char } return (tbuf); } +#endif + +static char *dup_shop_message(int mnum, room_vnum shr, const char *msg) +{ + int cht, ss = 0, ds = 0, err = 0; + char *tbuf; + + if (!msg || !*msg) + return NULL; + + tbuf = strdup(msg); + for (cht = 0; tbuf[cht]; cht++) { + if (tbuf[cht] != '%') + continue; + + if (tbuf[cht + 1] == 's') + ss++; + else if (tbuf[cht + 1] == 'd' && (mnum == 5 || mnum == 6)) { + if (ss == 0) { + log("SYSERR: Shop #%d has %%d before %%s, message #%d.", shr, mnum); + err++; + } + ds++; + } else if (tbuf[cht + 1] != '%') { + log("SYSERR: Shop #%d has invalid format '%%%c' in message #%d.", shr, tbuf[cht + 1], mnum); + err++; + } + } + + if (ss > 1 || ds > 1) { + log("SYSERR: Shop #%d has too many specifiers for message #%d. %%s=%d %%d=%d", shr, mnum, ss, ds); + err++; + } + + if (err) { + free(tbuf); + return NULL; + } + return (tbuf); +} void boot_the_shops(FILE *shop_f, char *filename, int rec_count) { - char *buf, buf2[256]; - int temp, count, new_format = FALSE; - struct shop_buy_data list[MAX_SHOP_OBJ + 1]; - int done = FALSE; + FILE *fp; + toml_table_t *tab; + toml_array_t *shops; + char errbuf[200]; + int i; - snprintf(buf2, sizeof(buf2), "beginning of shop file %s", filename); + (void)shop_f; - while (!done) { - buf = fread_string(shop_f, buf2); - if (*buf == '#') { /* New shop */ - sscanf(buf, "#%d\n", &temp); - snprintf(buf2, sizeof(buf2), "shop #%d in shop file %s", temp, filename); - free(buf); /* Plug memory leak! */ - top_shop++; - if (!top_shop) - CREATE(shop_index, struct shop_data, rec_count); - SHOP_NUM(top_shop) = temp; - temp = read_list(shop_f, list, new_format, MAX_PROD, LIST_PRODUCE); - CREATE(shop_index[top_shop].producing, obj_vnum, temp); - for (count = 0; count < temp; count++) - SHOP_PRODUCT(top_shop, count) = BUY_TYPE(list[count]); - - read_line(shop_f, "%f", &SHOP_BUYPROFIT(top_shop)); - read_line(shop_f, "%f", &SHOP_SELLPROFIT(top_shop)); - - temp = read_type_list(shop_f, list, new_format, MAX_TRADE); - CREATE(shop_index[top_shop].type, struct shop_buy_data, temp); - for (count = 0; count < temp; count++) { - SHOP_BUYTYPE(top_shop, count) = BUY_TYPE(list[count]); - SHOP_BUYWORD(top_shop, count) = BUY_WORD(list[count]); - } - - shop_index[top_shop].no_such_item1 = read_shop_message(0, SHOP_NUM(top_shop), shop_f, buf2); - shop_index[top_shop].no_such_item2 = read_shop_message(1, SHOP_NUM(top_shop), shop_f, buf2); - shop_index[top_shop].do_not_buy = read_shop_message(2, SHOP_NUM(top_shop), shop_f, buf2); - shop_index[top_shop].missing_cash1 = read_shop_message(3, SHOP_NUM(top_shop), shop_f, buf2); - shop_index[top_shop].missing_cash2 = read_shop_message(4, SHOP_NUM(top_shop), shop_f, buf2); - shop_index[top_shop].message_buy = read_shop_message(5, SHOP_NUM(top_shop), shop_f, buf2); - shop_index[top_shop].message_sell = read_shop_message(6, SHOP_NUM(top_shop), shop_f, buf2); - read_line(shop_f, "%d", &SHOP_BROKE_TEMPER(top_shop)); - read_line(shop_f, "%ld", &SHOP_BITVECTOR(top_shop)); - read_line(shop_f, "%d", &SHOP_KEEPER(top_shop)); - - SHOP_KEEPER(top_shop) = real_mobile(SHOP_KEEPER(top_shop)); - read_line(shop_f, "%d", &SHOP_TRADE_WITH(top_shop)); - - temp = read_list(shop_f, list, new_format, 1, LIST_ROOM); - CREATE(shop_index[top_shop].in_room, room_vnum, temp); - for (count = 0; count < temp; count++) - SHOP_ROOM(top_shop, count) = BUY_TYPE(list[count]); - - read_line(shop_f, "%d", &SHOP_OPEN1(top_shop)); - read_line(shop_f, "%d", &SHOP_CLOSE1(top_shop)); - read_line(shop_f, "%d", &SHOP_OPEN2(top_shop)); - read_line(shop_f, "%d", &SHOP_CLOSE2(top_shop)); - - SHOP_BANK(top_shop) = 0; - SHOP_SORT(top_shop) = 0; - SHOP_FUNC(top_shop) = NULL; - } else { - if (*buf == '$') /* EOF */ - done = TRUE; - else if (strstr(buf, VERSION3_TAG)) /* New format marker */ - new_format = TRUE; - free(buf); /* Plug memory leak! */ - } + fp = fopen(filename, "r"); + if (!fp) { + log("SYSERR: %s: %s", filename, strerror(errno)); + exit(1); } + + tab = toml_parse_file(fp, errbuf, sizeof(errbuf)); + fclose(fp); + if (!tab) { + log("SYSERR: parsing file '%s': %s", filename, errbuf); + exit(1); + } + + shops = toml_array_in(tab, "shop"); + if (!shops) { + toml_free(tab); + log("SYSERR: TOML file '%s' missing 'shop' array.", filename); + exit(1); + } + + for (i = 0; i < toml_array_nelem(shops); i++) { + toml_table_t *shop_tab = toml_table_at(shops, i); + toml_array_t *arr; + toml_table_t *msgs; + int count, temp; + + if (!shop_tab) + continue; + + top_shop++; + if (!top_shop) + CREATE(shop_index, struct shop_data, rec_count); + + SHOP_NUM(top_shop) = toml_get_int_default(shop_tab, "vnum", 0); + + arr = toml_array_in(shop_tab, "products"); + temp = arr ? toml_array_nelem(arr) : 0; + CREATE(shop_index[top_shop].producing, obj_vnum, temp + 1); + for (count = 0; count < temp; count++) { + toml_datum_t v = toml_int_at(arr, count); + SHOP_PRODUCT(top_shop, count) = v.ok ? (obj_vnum)v.u.i : NOTHING; + } + SHOP_PRODUCT(top_shop, temp) = NOTHING; + + SHOP_BUYPROFIT(top_shop) = toml_get_float_default(shop_tab, "buy_profit", 0.0f); + SHOP_SELLPROFIT(top_shop) = toml_get_float_default(shop_tab, "sell_profit", 0.0f); + + arr = toml_array_in(shop_tab, "buy_type"); + temp = arr ? toml_array_nelem(arr) : 0; + CREATE(shop_index[top_shop].type, struct shop_buy_data, temp + 1); + for (count = 0; count < temp; count++) { + toml_table_t *bt_tab = toml_table_at(arr, count); + int t = NOTHING; + char *word = NULL; + if (bt_tab) { + t = toml_get_int_default(bt_tab, "type", NOTHING); + word = toml_get_string_dup(bt_tab, "keyword"); + } + SHOP_BUYTYPE(top_shop, count) = t; + SHOP_BUYWORD(top_shop, count) = word; + } + SHOP_BUYTYPE(top_shop, temp) = NOTHING; + SHOP_BUYWORD(top_shop, temp) = NULL; + + msgs = toml_table_in(shop_tab, "messages"); + if (msgs) { + char *msg; + msg = toml_get_string_dup(msgs, "no_such_item1"); + shop_index[top_shop].no_such_item1 = dup_shop_message(0, SHOP_NUM(top_shop), msg); + if (msg) free(msg); + msg = toml_get_string_dup(msgs, "no_such_item2"); + shop_index[top_shop].no_such_item2 = dup_shop_message(1, SHOP_NUM(top_shop), msg); + if (msg) free(msg); + msg = toml_get_string_dup(msgs, "do_not_buy"); + shop_index[top_shop].do_not_buy = dup_shop_message(2, SHOP_NUM(top_shop), msg); + if (msg) free(msg); + msg = toml_get_string_dup(msgs, "missing_cash1"); + shop_index[top_shop].missing_cash1 = dup_shop_message(3, SHOP_NUM(top_shop), msg); + if (msg) free(msg); + msg = toml_get_string_dup(msgs, "missing_cash2"); + shop_index[top_shop].missing_cash2 = dup_shop_message(4, SHOP_NUM(top_shop), msg); + if (msg) free(msg); + msg = toml_get_string_dup(msgs, "message_buy"); + shop_index[top_shop].message_buy = dup_shop_message(5, SHOP_NUM(top_shop), msg); + if (msg) free(msg); + msg = toml_get_string_dup(msgs, "message_sell"); + shop_index[top_shop].message_sell = dup_shop_message(6, SHOP_NUM(top_shop), msg); + if (msg) free(msg); + } + + SHOP_BROKE_TEMPER(top_shop) = toml_get_int_default(shop_tab, "broke_temper", 0); + SHOP_BITVECTOR(top_shop) = (long)toml_get_int_default(shop_tab, "bitvector", 0); + SHOP_KEEPER(top_shop) = toml_get_int_default(shop_tab, "keeper", NOBODY); + SHOP_KEEPER(top_shop) = real_mobile(SHOP_KEEPER(top_shop)); + SHOP_TRADE_WITH(top_shop) = toml_get_int_default(shop_tab, "trade_with", 0); + + arr = toml_array_in(shop_tab, "rooms"); + temp = arr ? toml_array_nelem(arr) : 0; + CREATE(shop_index[top_shop].in_room, room_vnum, temp + 1); + for (count = 0; count < temp; count++) { + toml_datum_t v = toml_int_at(arr, count); + SHOP_ROOM(top_shop, count) = v.ok ? (room_vnum)v.u.i : NOWHERE; + } + SHOP_ROOM(top_shop, temp) = NOWHERE; + + SHOP_OPEN1(top_shop) = toml_get_int_default(shop_tab, "open1", 0); + SHOP_CLOSE1(top_shop) = toml_get_int_default(shop_tab, "close1", 0); + SHOP_OPEN2(top_shop) = toml_get_int_default(shop_tab, "open2", 0); + SHOP_CLOSE2(top_shop) = toml_get_int_default(shop_tab, "close2", 0); + SHOP_BANK(top_shop) = toml_get_int_default(shop_tab, "bank", 0); + SHOP_SORT(top_shop) = toml_get_int_default(shop_tab, "sort", 0); + SHOP_FUNC(top_shop) = NULL; + } + + toml_free(tab); } void assign_the_shopkeepers(void) diff --git a/src/toml_utils.c b/src/toml_utils.c new file mode 100644 index 0000000..2fbafdb --- /dev/null +++ b/src/toml_utils.c @@ -0,0 +1,58 @@ +#include "conf.h" +#include "sysdep.h" +#include "toml_utils.h" + +static void toml_write_escaped(FILE *fp, const char *value) +{ + const unsigned char *p = (const unsigned char *)value; + + while (p && *p) { + switch (*p) { + case '\\': + fputs("\\\\", fp); + break; + case '"': + fputs("\\\"", fp); + break; + case '\n': + fputs("\\n", fp); + break; + case '\r': + fputs("\\r", fp); + break; + case '\t': + fputs("\\t", fp); + break; + default: + if (*p < 0x20) { + fprintf(fp, "\\u%04x", (unsigned int)*p); + } else { + fputc(*p, fp); + } + break; + } + p++; + } +} + +void toml_write_string(FILE *fp, const char *value) +{ + fputc('"', fp); + if (value && *value) + toml_write_escaped(fp, value); + fputc('"', fp); +} + +void toml_write_kv_string(FILE *fp, const char *key, const char *value) +{ + fprintf(fp, "%s = ", key); + toml_write_string(fp, value); + fputc('\n', fp); +} + +void toml_write_kv_string_opt(FILE *fp, const char *key, const char *value) +{ + if (!value || !*value) + return; + toml_write_kv_string(fp, key, value); +} diff --git a/src/toml_utils.h b/src/toml_utils.h new file mode 100644 index 0000000..f495f14 --- /dev/null +++ b/src/toml_utils.h @@ -0,0 +1,10 @@ +#ifndef TOML_UTILS_H +#define TOML_UTILS_H + +#include + +void toml_write_string(FILE *fp, const char *value); +void toml_write_kv_string(FILE *fp, const char *key, const char *value); +void toml_write_kv_string_opt(FILE *fp, const char *key, const char *value); + +#endif diff --git a/third_party/tomlc99/.editorconfig b/third_party/tomlc99/.editorconfig new file mode 100644 index 0000000..804650e --- /dev/null +++ b/third_party/tomlc99/.editorconfig @@ -0,0 +1,14 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = false +trim_trailing_whitespace = true + +[*.{c,h}] +indent_style = space +indent_size = 2 + +[Makefile] +indent_style = tab +indent_size = 4 diff --git a/third_party/tomlc99/.gitignore b/third_party/tomlc99/.gitignore new file mode 100644 index 0000000..3017290 --- /dev/null +++ b/third_party/tomlc99/.gitignore @@ -0,0 +1,38 @@ +*~ + +# Object files +*.o +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex +toml_cat +toml_json +toml_sample + +# Debug files +*.dSYM/ +*.su diff --git a/third_party/tomlc99/LICENSE b/third_party/tomlc99/LICENSE new file mode 100644 index 0000000..bb09e49 --- /dev/null +++ b/third_party/tomlc99/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) CK Tan +https://github.com/cktan/tomlc99 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/tomlc99/Makefile b/third_party/tomlc99/Makefile new file mode 100644 index 0000000..599f7db --- /dev/null +++ b/third_party/tomlc99/Makefile @@ -0,0 +1,53 @@ +prefix ?= /usr/local +HFILES = toml.h +CFILES = toml.c +OBJ = $(CFILES:.c=.o) +EXEC = toml_json toml_cat toml_sample +PCFILE = libtoml.pc + +CFLAGS = -std=c99 -Wall -Wextra -fpic +LIB_VERSION = 1.0 +LIB = libtoml.a +LIB_SHARED = libtoml.so.$(LIB_VERSION) + +# to compile for debug: make DEBUG=1 +# to compile for no debug: make +ifdef DEBUG + CFLAGS += -O0 -g +else + CFLAGS += -O2 -DNDEBUG +endif + + +all: $(LIB) $(LIB_SHARED) $(EXEC) + +*.o: $(HFILES) + +libtoml.a: toml.o + ar -rcs $@ $^ + +libtoml.so.$(LIB_VERSION): toml.o + $(CC) -shared -o $@ $^ + +$(EXEC): $(LIB) + +install: all + install -d ${prefix}/include ${prefix}/lib + install toml.h ${prefix}/include + install $(LIB) ${prefix}/lib + install $(LIB_SHARED) ${prefix}/lib +ifeq "$(prefix)" "/usr/local" +ifneq ("$(wildcard $(PCFILE))","") + install $(PCFILE) /usr/local/lib/pkgconfig +endif +endif + + +clean: + rm -f *.o $(EXEC) $(LIB) $(LIB_SHARED) + + +format: + clang-format -i $(shell find . -name '*.[ch]') + +.PHONY: all clean install format diff --git a/third_party/tomlc99/README.md b/third_party/tomlc99/README.md new file mode 100644 index 0000000..13ecd5e --- /dev/null +++ b/third_party/tomlc99/README.md @@ -0,0 +1,198 @@ +# tomlc99 + +> +> **Note: OBSOLETED. There is a newer version of this library available at [tomlc17](https://github.com/cktan/tomlc17), which is compatible with c99.** +> + +TOML in c99; v1.0 compliant. + +If you are looking for a C++ library, you might try this wrapper: [https://github.com/cktan/tomlcpp](https://github.com/cktan/tomlcpp). + +* Compatible with [TOML v1.0.0](https://toml.io/en/v1.0.0). +* Tested with multiple test suites, including +[toml-lang/toml-test](https://github.com/toml-lang/toml-test) and +[iarna/toml-spec-tests](https://github.com/iarna/toml-spec-tests). +* Provides very simple and intuitive interface. + + +## Usage + +Please see the `toml.h` file for details. The following is a simple example that +parses this config file: + +```toml +[server] + host = "www.example.com" + port = [ 8080, 8181, 8282 ] +``` + +These are the usual steps for getting values from a file: + +1. Parse the TOML file. +2. Traverse and locate a table in TOML. +3. Extract values from the table. +4. Free up allocated memory. + +Below is an example of parsing the values from the example table. + +```c +#include +#include +#include +#include +#include "toml.h" + +static void error(const char* msg, const char* msg1) +{ + fprintf(stderr, "ERROR: %s%s\n", msg, msg1?msg1:""); + exit(1); +} + + +int main() +{ + FILE* fp; + char errbuf[200]; + + // 1. Read and parse toml file + fp = fopen("sample.toml", "r"); + if (!fp) { + error("cannot open sample.toml - ", strerror(errno)); + } + + toml_table_t* conf = toml_parse_file(fp, errbuf, sizeof(errbuf)); + fclose(fp); + + if (!conf) { + error("cannot parse - ", errbuf); + } + + // 2. Traverse to a table. + toml_table_t* server = toml_table_in(conf, "server"); + if (!server) { + error("missing [server]", ""); + } + + // 3. Extract values + toml_datum_t host = toml_string_in(server, "host"); + if (!host.ok) { + error("cannot read server.host", ""); + } + + toml_array_t* portarray = toml_array_in(server, "port"); + if (!portarray) { + error("cannot read server.port", ""); + } + + printf("host: %s\n", host.u.s); + printf("port: "); + for (int i = 0; ; i++) { + toml_datum_t port = toml_int_at(portarray, i); + if (!port.ok) break; + printf("%d ", (int)port.u.i); + } + printf("\n"); + + // 4. Free memory + free(host.u.s); + toml_free(conf); + return 0; +} +``` + +#### Accessing Table Content + +TOML tables are dictionaries where lookups are done using string keys. In +general, all access functions on tables are named `toml_*_in(...)`. + +In the normal case, you know the key and its content type, and retrievals can be done +using one of these functions: +```c +toml_string_in(tab, key); +toml_bool_in(tab, key); +toml_int_in(tab, key); +toml_double_in(tab, key); +toml_timestamp_in(tab, key); +toml_table_in(tab, key); +toml_array_in(tab, key); +``` + +You can also interrogate the keys in a table using an integer index: +```c +toml_table_t* tab = toml_parse_file(...); +for (int i = 0; ; i++) { + const char* key = toml_key_in(tab, i); + if (!key) break; + printf("key %d: %s\n", i, key); +} +``` + +#### Accessing Array Content + +TOML arrays can be deref-ed using integer indices. In general, all access methods on arrays are named `toml_*_at()`. + +To obtain the size of an array: +```c +int size = toml_array_nelem(arr); +``` + +To obtain the content of an array, use a valid index and call one of these functions: +```c +toml_string_at(arr, idx); +toml_bool_at(arr, idx); +toml_int_at(arr, idx); +toml_double_at(arr, idx); +toml_timestamp_at(arr, idx); +toml_table_at(arr, idx); +toml_array_at(arr, idx); +``` + +#### toml_datum_t + +Some `toml_*_at` and `toml_*_in` functions return a toml_datum_t +structure. The `ok` flag in the structure indicates if the function +call was successful. If so, you may proceed to read the value +corresponding to the type of the content. + +For example: +``` +toml_datum_t host = toml_string_in(tab, "host"); +if (host.ok) { + printf("host: %s\n", host.u.s); + free(host.u.s); /* FREE applies to string and timestamp types only */ +} +``` + +** IMPORTANT: if the accessed value is a string or a timestamp, you must call `free(datum.u.s)` or `free(datum.u.ts)` respectively after usage. ** + +## Building and installing + +A normal *make* suffices. You can also simply include the +`toml.c` and `toml.h` files in your project. + +Invoking `make install` will install the header and library files into +/usr/local/{include,lib}. + +Alternatively, specify `make install prefix=/a/file/path` to install into +/a/file/path/{include,lib}. + +## Testing + +To test against the standard test set provided by toml-lang/toml-test: + +```sh +% make +% cd test1 +% bash build.sh # do this once +% bash run.sh # this will run the test suite +``` + + +To test against the standard test set provided by iarna/toml: + +```sh +% make +% cd test2 +% bash build.sh # do this once +% bash run.sh # this will run the test suite +``` diff --git a/third_party/tomlc99/libtoml.pc.sample b/third_party/tomlc99/libtoml.pc.sample new file mode 100644 index 0000000..e343634 --- /dev/null +++ b/third_party/tomlc99/libtoml.pc.sample @@ -0,0 +1,11 @@ +prefix=/usr/local +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: libtoml +URL: https://github.com/cktan/tomlc99/ +Description: TOML C library in c99. +Version: v1.0 +Libs: -L${libdir} -ltoml +Cflags: -I${includedir} diff --git a/third_party/tomlc99/sample.toml b/third_party/tomlc99/sample.toml new file mode 100644 index 0000000..e613d17 --- /dev/null +++ b/third_party/tomlc99/sample.toml @@ -0,0 +1,3 @@ +[server] + host = "example.com" + port = [ 8080, 8181, 8282 ] diff --git a/third_party/tomlc99/stdex/.gitignore b/third_party/tomlc99/stdex/.gitignore new file mode 100644 index 0000000..e87afd9 --- /dev/null +++ b/third_party/tomlc99/stdex/.gitignore @@ -0,0 +1 @@ +/*.out diff --git a/third_party/tomlc99/stdex/RUN.sh b/third_party/tomlc99/stdex/RUN.sh new file mode 100644 index 0000000..8395baf --- /dev/null +++ b/third_party/tomlc99/stdex/RUN.sh @@ -0,0 +1,15 @@ +rm -f *.out +for i in *.toml; do + echo -n $i + ../toml_cat $i >& $i.out + if [ -f $i.res ]; then + if $(diff $i.out $i.res >& /dev/null); then + echo " [OK]" + else + echo " [FAILED]" + fi + else + echo " [?????]" + fi + +done diff --git a/third_party/tomlc99/stdex/arr1.toml b/third_party/tomlc99/stdex/arr1.toml new file mode 100644 index 0000000..ce497e6 --- /dev/null +++ b/third_party/tomlc99/stdex/arr1.toml @@ -0,0 +1,13 @@ +integers = [ 1, 2, 3 ] +colors = [ "red", "yellow", "green" ] +nested_arrays_of_ints = [ [ 1, 2 ], [3, 4, 5] ] +nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ] +string_array = [ "all", 'strings', """are the same""", '''type''' ] + +# Mixed-type arrays are allowed +numbers = [ 0.1, 0.2, 0.5, 1, 2, 5 ] +contributors = [ + "Foo Bar ", + { name = "Baz Qux", email = "bazqux@example.com", url = "https://example.com/bazqux" } +] + diff --git a/third_party/tomlc99/stdex/arr1.toml.res b/third_party/tomlc99/stdex/arr1.toml.res new file mode 100644 index 0000000..a3177b0 --- /dev/null +++ b/third_party/tomlc99/stdex/arr1.toml.res @@ -0,0 +1,56 @@ +{ + integers = [ + 1, + 2, + 3, + ], + colors = [ + "red", + "yellow", + "green", + ], + nested_arrays_of_ints = [ + [ + 1, + 2, + ], + [ + 3, + 4, + 5, + ], + ], + nested_mixed_array = [ + [ + 1, + 2, + ], + [ + "a", + "b", + "c", + ], + ], + string_array = [ + "all", + "strings", + "are the same", + "type", + ], + numbers = [ + 0.100000, + 0.200000, + 0.500000, + 1, + 2, + 5, + ], + contributors = [ + "Foo Bar ", + { + name = "Baz Qux", + email = "bazqux@example.com", + url = "https://example.com/bazqux", + }, + ], +} diff --git a/third_party/tomlc99/stdex/arr2.toml b/third_party/tomlc99/stdex/arr2.toml new file mode 100644 index 0000000..6acd9ca --- /dev/null +++ b/third_party/tomlc99/stdex/arr2.toml @@ -0,0 +1,8 @@ +integers2 = [ + 1, 2, 3 +] + +integers3 = [ + 1, + 2, # this is ok +] diff --git a/third_party/tomlc99/stdex/arr2.toml.res b/third_party/tomlc99/stdex/arr2.toml.res new file mode 100644 index 0000000..af381cf --- /dev/null +++ b/third_party/tomlc99/stdex/arr2.toml.res @@ -0,0 +1,11 @@ +{ + integers2 = [ + 1, + 2, + 3, + ], + integers3 = [ + 1, + 2, + ], +} diff --git a/third_party/tomlc99/stdex/arrtab1.toml b/third_party/tomlc99/stdex/arrtab1.toml new file mode 100644 index 0000000..544d142 --- /dev/null +++ b/third_party/tomlc99/stdex/arrtab1.toml @@ -0,0 +1,11 @@ +[[products]] +name = "Hammer" +sku = 738594937 + +[[products]] # empty table within the array + +[[products]] +name = "Nail" +sku = 284758393 + +color = "gray" diff --git a/third_party/tomlc99/stdex/arrtab1.toml.res b/third_party/tomlc99/stdex/arrtab1.toml.res new file mode 100644 index 0000000..4e94d8e --- /dev/null +++ b/third_party/tomlc99/stdex/arrtab1.toml.res @@ -0,0 +1,15 @@ +{ + products = [ + { + name = "Hammer", + sku = 738594937, + }, + { + }, + { + name = "Nail", + sku = 284758393, + color = "gray", + }, + ], +} diff --git a/third_party/tomlc99/stdex/arrtab2.toml b/third_party/tomlc99/stdex/arrtab2.toml new file mode 100644 index 0000000..7a3971f --- /dev/null +++ b/third_party/tomlc99/stdex/arrtab2.toml @@ -0,0 +1,19 @@ +[[fruits]] +name = "apple" + +[fruits.physical] # subtable +color = "red" +shape = "round" + +[[fruits.varieties]] # nested array of tables +name = "red delicious" + +[[fruits.varieties]] +name = "granny smith" + + +[[fruits]] +name = "banana" + +[[fruits.varieties]] +name = "plantain" diff --git a/third_party/tomlc99/stdex/arrtab2.toml.res b/third_party/tomlc99/stdex/arrtab2.toml.res new file mode 100644 index 0000000..d889196 --- /dev/null +++ b/third_party/tomlc99/stdex/arrtab2.toml.res @@ -0,0 +1,27 @@ +{ + fruits = [ + { + name = "apple", + varieties = [ + { + name = "red delicious", + }, + { + name = "granny smith", + }, + ], + physical = { + color = "red", + shape = "round", + }, + }, + { + name = "banana", + varieties = [ + { + name = "plantain", + }, + ], + }, + ], +} diff --git a/third_party/tomlc99/stdex/arrtab3.toml b/third_party/tomlc99/stdex/arrtab3.toml new file mode 100644 index 0000000..703130e --- /dev/null +++ b/third_party/tomlc99/stdex/arrtab3.toml @@ -0,0 +1,8 @@ +# INVALID TOML DOC +[fruit.physical] # subtable, but to which parent element should it belong? +color = "red" +shape = "round" + +[[fruit]] # parser must throw an error upon discovering that "fruit" is + # an array rather than a table +name = "apple" diff --git a/third_party/tomlc99/stdex/arrtab3.toml.res b/third_party/tomlc99/stdex/arrtab3.toml.res new file mode 100644 index 0000000..b57e247 --- /dev/null +++ b/third_party/tomlc99/stdex/arrtab3.toml.res @@ -0,0 +1 @@ +ERROR: line 6: key exists diff --git a/third_party/tomlc99/stdex/arrtab4.toml b/third_party/tomlc99/stdex/arrtab4.toml new file mode 100644 index 0000000..65b2505 --- /dev/null +++ b/third_party/tomlc99/stdex/arrtab4.toml @@ -0,0 +1,4 @@ +# INVALID TOML DOC +fruits = [] + +[[fruits]] # Not allowed diff --git a/third_party/tomlc99/stdex/arrtab4.toml.res b/third_party/tomlc99/stdex/arrtab4.toml.res new file mode 100644 index 0000000..4a89d92 --- /dev/null +++ b/third_party/tomlc99/stdex/arrtab4.toml.res @@ -0,0 +1 @@ +ERROR: line 4: array mismatch diff --git a/third_party/tomlc99/stdex/arrtab5.toml b/third_party/tomlc99/stdex/arrtab5.toml new file mode 100644 index 0000000..b36540e --- /dev/null +++ b/third_party/tomlc99/stdex/arrtab5.toml @@ -0,0 +1,11 @@ +# INVALID TOML DOC +[[fruits]] +name = "apple" + +[[fruits.varieties]] +name = "red delicious" + +# INVALID: This table conflicts with the previous array of tables +[fruits.varieties] +name = "granny smith" + diff --git a/third_party/tomlc99/stdex/arrtab5.toml.res b/third_party/tomlc99/stdex/arrtab5.toml.res new file mode 100644 index 0000000..617cdf1 --- /dev/null +++ b/third_party/tomlc99/stdex/arrtab5.toml.res @@ -0,0 +1 @@ +ERROR: line 9: key exists diff --git a/third_party/tomlc99/stdex/arrtab6.toml b/third_party/tomlc99/stdex/arrtab6.toml new file mode 100644 index 0000000..8324c88 --- /dev/null +++ b/third_party/tomlc99/stdex/arrtab6.toml @@ -0,0 +1,14 @@ +# INVALID TOML DOC +[[fruits]] +name = "apple" + +[[fruits.varieties]] +name = "red delicious" + +[fruits.physical] +color = "red" +shape = "round" + +# INVALID: This array of tables conflicts with the previous table +[[fruits.physical]] +color = "green" diff --git a/third_party/tomlc99/stdex/arrtab6.toml.res b/third_party/tomlc99/stdex/arrtab6.toml.res new file mode 100644 index 0000000..b9a224d --- /dev/null +++ b/third_party/tomlc99/stdex/arrtab6.toml.res @@ -0,0 +1 @@ +ERROR: line 13: key exists diff --git a/third_party/tomlc99/stdex/arrtab7.toml b/third_party/tomlc99/stdex/arrtab7.toml new file mode 100644 index 0000000..ba5584a --- /dev/null +++ b/third_party/tomlc99/stdex/arrtab7.toml @@ -0,0 +1,3 @@ +points = [ { x = 1, y = 2, z = 3 }, + { x = 7, y = 8, z = 9 }, + { x = 2, y = 4, z = 8 } ] diff --git a/third_party/tomlc99/stdex/arrtab7.toml.res b/third_party/tomlc99/stdex/arrtab7.toml.res new file mode 100644 index 0000000..aa0ef41 --- /dev/null +++ b/third_party/tomlc99/stdex/arrtab7.toml.res @@ -0,0 +1,19 @@ +{ + points = [ + { + x = 1, + y = 2, + z = 3, + }, + { + x = 7, + y = 8, + z = 9, + }, + { + x = 2, + y = 4, + z = 8, + }, + ], +} diff --git a/third_party/tomlc99/stdex/bool1.toml b/third_party/tomlc99/stdex/bool1.toml new file mode 100644 index 0000000..6a53ecc --- /dev/null +++ b/third_party/tomlc99/stdex/bool1.toml @@ -0,0 +1,3 @@ +bool1 = true +bool2 = false + diff --git a/third_party/tomlc99/stdex/bool1.toml.res b/third_party/tomlc99/stdex/bool1.toml.res new file mode 100644 index 0000000..76a0816 --- /dev/null +++ b/third_party/tomlc99/stdex/bool1.toml.res @@ -0,0 +1,4 @@ +{ + bool1 = true, + bool2 = false, +} diff --git a/third_party/tomlc99/stdex/comment.toml b/third_party/tomlc99/stdex/comment.toml new file mode 100644 index 0000000..fae57c6 --- /dev/null +++ b/third_party/tomlc99/stdex/comment.toml @@ -0,0 +1,3 @@ +# This is a full-line comment +key = "value" # This is a comment at the end of a line +another = "# This is not a comment" diff --git a/third_party/tomlc99/stdex/comment.toml.res b/third_party/tomlc99/stdex/comment.toml.res new file mode 100644 index 0000000..ae1a16e --- /dev/null +++ b/third_party/tomlc99/stdex/comment.toml.res @@ -0,0 +1,4 @@ +{ + key = "value", + another = "# This is not a comment", +} diff --git a/third_party/tomlc99/stdex/float1.toml b/third_party/tomlc99/stdex/float1.toml new file mode 100644 index 0000000..faa5c4f --- /dev/null +++ b/third_party/tomlc99/stdex/float1.toml @@ -0,0 +1,13 @@ +# fractional +flt1 = +1.0 +flt2 = 3.1415 +flt3 = -0.01 + +# exponent +flt4 = 5e+22 +flt5 = 1e06 +flt6 = -2E-2 + +# both +flt7 = 6.626e-34 + diff --git a/third_party/tomlc99/stdex/float1.toml.res b/third_party/tomlc99/stdex/float1.toml.res new file mode 100644 index 0000000..a01bf36 --- /dev/null +++ b/third_party/tomlc99/stdex/float1.toml.res @@ -0,0 +1,9 @@ +{ + flt1 = 1.000000, + flt2 = 3.141500, + flt3 = -0.010000, + flt4 = 49999999999999995805696.000000, + flt5 = 1000000.000000, + flt6 = -0.020000, + flt7 = 0.000000, +} diff --git a/third_party/tomlc99/stdex/float2.toml b/third_party/tomlc99/stdex/float2.toml new file mode 100644 index 0000000..7744e4f --- /dev/null +++ b/third_party/tomlc99/stdex/float2.toml @@ -0,0 +1 @@ +invalid_float_1 = .7 diff --git a/third_party/tomlc99/stdex/float2.toml.res b/third_party/tomlc99/stdex/float2.toml.res new file mode 100644 index 0000000..9af2622 --- /dev/null +++ b/third_party/tomlc99/stdex/float2.toml.res @@ -0,0 +1,2 @@ +{ +ERROR: unable to decode value in table diff --git a/third_party/tomlc99/stdex/float3.toml b/third_party/tomlc99/stdex/float3.toml new file mode 100644 index 0000000..508260e --- /dev/null +++ b/third_party/tomlc99/stdex/float3.toml @@ -0,0 +1,2 @@ +invalid_float_2 = 7. + diff --git a/third_party/tomlc99/stdex/float3.toml.res b/third_party/tomlc99/stdex/float3.toml.res new file mode 100644 index 0000000..9af2622 --- /dev/null +++ b/third_party/tomlc99/stdex/float3.toml.res @@ -0,0 +1,2 @@ +{ +ERROR: unable to decode value in table diff --git a/third_party/tomlc99/stdex/float4.toml b/third_party/tomlc99/stdex/float4.toml new file mode 100644 index 0000000..c50ef3b --- /dev/null +++ b/third_party/tomlc99/stdex/float4.toml @@ -0,0 +1 @@ +invalid_float_3 = 3.e+20 diff --git a/third_party/tomlc99/stdex/float4.toml.res b/third_party/tomlc99/stdex/float4.toml.res new file mode 100644 index 0000000..9af2622 --- /dev/null +++ b/third_party/tomlc99/stdex/float4.toml.res @@ -0,0 +1,2 @@ +{ +ERROR: unable to decode value in table diff --git a/third_party/tomlc99/stdex/float5.toml b/third_party/tomlc99/stdex/float5.toml new file mode 100644 index 0000000..3c8f0aa --- /dev/null +++ b/third_party/tomlc99/stdex/float5.toml @@ -0,0 +1 @@ +flt8 = 224_617.445_991_228 diff --git a/third_party/tomlc99/stdex/float5.toml.res b/third_party/tomlc99/stdex/float5.toml.res new file mode 100644 index 0000000..1ffb208 --- /dev/null +++ b/third_party/tomlc99/stdex/float5.toml.res @@ -0,0 +1,3 @@ +{ + flt8 = 224617.445991, +} diff --git a/third_party/tomlc99/stdex/float6.toml b/third_party/tomlc99/stdex/float6.toml new file mode 100644 index 0000000..2e77cef --- /dev/null +++ b/third_party/tomlc99/stdex/float6.toml @@ -0,0 +1,10 @@ +# infinity +sf1 = inf # positive infinity +sf2 = +inf # positive infinity +sf3 = -inf # negative infinity + +# not a number +sf4 = nan # actual sNaN/qNaN encoding is implementation-specific +sf5 = +nan # same as `nan` +sf6 = -nan # valid, actual encoding is implementation-specific + diff --git a/third_party/tomlc99/stdex/float6.toml.res b/third_party/tomlc99/stdex/float6.toml.res new file mode 100644 index 0000000..95f0a7f --- /dev/null +++ b/third_party/tomlc99/stdex/float6.toml.res @@ -0,0 +1,8 @@ +{ + sf1 = inf, + sf2 = inf, + sf3 = -inf, + sf4 = nan, + sf5 = nan, + sf6 = nan, +} diff --git a/third_party/tomlc99/stdex/inlinetab1.toml b/third_party/tomlc99/stdex/inlinetab1.toml new file mode 100644 index 0000000..26062b5 --- /dev/null +++ b/third_party/tomlc99/stdex/inlinetab1.toml @@ -0,0 +1,3 @@ +name = { first = "Tom", last = "Preston-Werner" } +point = { x = 1, y = 2 } +animal = { type.name = "pug" } diff --git a/third_party/tomlc99/stdex/inlinetab1.toml.res b/third_party/tomlc99/stdex/inlinetab1.toml.res new file mode 100644 index 0000000..7553b02 --- /dev/null +++ b/third_party/tomlc99/stdex/inlinetab1.toml.res @@ -0,0 +1,15 @@ +{ + name = { + first = "Tom", + last = "Preston-Werner", + }, + point = { + x = 1, + y = 2, + }, + animal = { + type = { + name = "pug", + }, + }, +} diff --git a/third_party/tomlc99/stdex/inlinetab2.toml b/third_party/tomlc99/stdex/inlinetab2.toml new file mode 100644 index 0000000..7689e14 --- /dev/null +++ b/third_party/tomlc99/stdex/inlinetab2.toml @@ -0,0 +1,3 @@ +[product] +type = { name = "Nail" } +type.edible = false # INVALID diff --git a/third_party/tomlc99/stdex/inlinetab2.toml.res b/third_party/tomlc99/stdex/inlinetab2.toml.res new file mode 100644 index 0000000..9684a3d --- /dev/null +++ b/third_party/tomlc99/stdex/inlinetab2.toml.res @@ -0,0 +1 @@ +ERROR: line 3: cannot insert new entry into existing table diff --git a/third_party/tomlc99/stdex/inlinetab3.toml b/third_party/tomlc99/stdex/inlinetab3.toml new file mode 100644 index 0000000..d6937eb --- /dev/null +++ b/third_party/tomlc99/stdex/inlinetab3.toml @@ -0,0 +1,3 @@ +[product] +type.name = "Nail" +type = { edible = false } # INVALID diff --git a/third_party/tomlc99/stdex/inlinetab3.toml.res b/third_party/tomlc99/stdex/inlinetab3.toml.res new file mode 100644 index 0000000..0a6a07c --- /dev/null +++ b/third_party/tomlc99/stdex/inlinetab3.toml.res @@ -0,0 +1 @@ +ERROR: line 3: key exists diff --git a/third_party/tomlc99/stdex/int0.toml b/third_party/tomlc99/stdex/int0.toml new file mode 100644 index 0000000..107a6b7 --- /dev/null +++ b/third_party/tomlc99/stdex/int0.toml @@ -0,0 +1,9 @@ +int1 = +99 +int2 = 42 +int3 = 0 +int4 = -17 +int5 = 1_000 +int6 = 5_349_221 +int7 = 53_49_221 # Indian number system grouping +int8 = 1_2_3_4_5 # VALID but discouraged + diff --git a/third_party/tomlc99/stdex/int0.toml.res b/third_party/tomlc99/stdex/int0.toml.res new file mode 100644 index 0000000..4e350ca --- /dev/null +++ b/third_party/tomlc99/stdex/int0.toml.res @@ -0,0 +1,10 @@ +{ + int1 = 99, + int2 = 42, + int3 = 0, + int4 = -17, + int5 = 1000, + int6 = 5349221, + int7 = 5349221, + int8 = 12345, +} diff --git a/third_party/tomlc99/stdex/int1.toml b/third_party/tomlc99/stdex/int1.toml new file mode 100644 index 0000000..03bf25b --- /dev/null +++ b/third_party/tomlc99/stdex/int1.toml @@ -0,0 +1,12 @@ +# hexadecimal with prefix `0x` +hex1 = 0xDEADBEEF +hex2 = 0xdeadbeef +hex3 = 0xdead_beef + +# octal with prefix `0o` +oct1 = 0o01234567 +oct2 = 0o755 # useful for Unix file permissions + +# binary with prefix `0b` +bin1 = 0b11010110 + diff --git a/third_party/tomlc99/stdex/int1.toml.res b/third_party/tomlc99/stdex/int1.toml.res new file mode 100644 index 0000000..3e8e1bd --- /dev/null +++ b/third_party/tomlc99/stdex/int1.toml.res @@ -0,0 +1,8 @@ +{ + hex1 = 3735928559, + hex2 = 3735928559, + hex3 = 3735928559, + oct1 = 342391, + oct2 = 493, + bin1 = 214, +} diff --git a/third_party/tomlc99/stdex/keys00.toml b/third_party/tomlc99/stdex/keys00.toml new file mode 100644 index 0000000..657ff24 --- /dev/null +++ b/third_party/tomlc99/stdex/keys00.toml @@ -0,0 +1,4 @@ +key = "value" +bare_key = "value" +bare-key = "value" +1234 = "value" diff --git a/third_party/tomlc99/stdex/keys00.toml.res b/third_party/tomlc99/stdex/keys00.toml.res new file mode 100644 index 0000000..b28b39e --- /dev/null +++ b/third_party/tomlc99/stdex/keys00.toml.res @@ -0,0 +1,6 @@ +{ + key = "value", + bare_key = "value", + bare-key = "value", + 1234 = "value", +} diff --git a/third_party/tomlc99/stdex/keys01.toml b/third_party/tomlc99/stdex/keys01.toml new file mode 100644 index 0000000..cc2d1d0 --- /dev/null +++ b/third_party/tomlc99/stdex/keys01.toml @@ -0,0 +1,5 @@ +"127.0.0.1" = "value" +"character encoding" = "value" +"ʎǝʞ" = "value" +'key2' = "value" +'quoted "value"' = "value" diff --git a/third_party/tomlc99/stdex/keys01.toml.res b/third_party/tomlc99/stdex/keys01.toml.res new file mode 100644 index 0000000..5fc4091 --- /dev/null +++ b/third_party/tomlc99/stdex/keys01.toml.res @@ -0,0 +1,7 @@ +{ + 127.0.0.1 = "value", + character encoding = "value", + ʎǝʞ = "value", + key2 = "value", + quoted "value" = "value", +} diff --git a/third_party/tomlc99/stdex/keys02.toml b/third_party/tomlc99/stdex/keys02.toml new file mode 100644 index 0000000..cd9fa90 --- /dev/null +++ b/third_party/tomlc99/stdex/keys02.toml @@ -0,0 +1 @@ += "no key name" # INVALID diff --git a/third_party/tomlc99/stdex/keys02.toml.res b/third_party/tomlc99/stdex/keys02.toml.res new file mode 100644 index 0000000..13a97f7 --- /dev/null +++ b/third_party/tomlc99/stdex/keys02.toml.res @@ -0,0 +1 @@ +ERROR: line 1: syntax error diff --git a/third_party/tomlc99/stdex/keys03.toml b/third_party/tomlc99/stdex/keys03.toml new file mode 100644 index 0000000..5aea8ee --- /dev/null +++ b/third_party/tomlc99/stdex/keys03.toml @@ -0,0 +1 @@ +"" = "blank" # VALID but discouraged diff --git a/third_party/tomlc99/stdex/keys03.toml.res b/third_party/tomlc99/stdex/keys03.toml.res new file mode 100644 index 0000000..1478e36 --- /dev/null +++ b/third_party/tomlc99/stdex/keys03.toml.res @@ -0,0 +1,3 @@ +{ + = "blank", +} diff --git a/third_party/tomlc99/stdex/keys04.toml b/third_party/tomlc99/stdex/keys04.toml new file mode 100644 index 0000000..8f83571 --- /dev/null +++ b/third_party/tomlc99/stdex/keys04.toml @@ -0,0 +1,4 @@ +name = "Orange" +physical.color = "orange" +physical.shape = "round" +site."google.com" = true diff --git a/third_party/tomlc99/stdex/keys04.toml.res b/third_party/tomlc99/stdex/keys04.toml.res new file mode 100644 index 0000000..8bedb21 --- /dev/null +++ b/third_party/tomlc99/stdex/keys04.toml.res @@ -0,0 +1,10 @@ +{ + name = "Orange", + physical = { + color = "orange", + shape = "round", + }, + site = { + google.com = true, + }, +} diff --git a/third_party/tomlc99/stdex/keys05.toml b/third_party/tomlc99/stdex/keys05.toml new file mode 100644 index 0000000..75bf90b --- /dev/null +++ b/third_party/tomlc99/stdex/keys05.toml @@ -0,0 +1,3 @@ +fruit.name = "banana" # this is best practice +fruit. color = "yellow" # same as fruit.color +fruit . flavor = "banana" # same as fruit.flavor diff --git a/third_party/tomlc99/stdex/keys05.toml.res b/third_party/tomlc99/stdex/keys05.toml.res new file mode 100644 index 0000000..33db031 --- /dev/null +++ b/third_party/tomlc99/stdex/keys05.toml.res @@ -0,0 +1,7 @@ +{ + fruit = { + name = "banana", + color = "yellow", + flavor = "banana", + }, +} diff --git a/third_party/tomlc99/stdex/keys06.toml b/third_party/tomlc99/stdex/keys06.toml new file mode 100644 index 0000000..7847bd4 --- /dev/null +++ b/third_party/tomlc99/stdex/keys06.toml @@ -0,0 +1,3 @@ +# DO NOT DO THIS +name = "Tom" +name = "Pradyun" diff --git a/third_party/tomlc99/stdex/keys06.toml.res b/third_party/tomlc99/stdex/keys06.toml.res new file mode 100644 index 0000000..0a6a07c --- /dev/null +++ b/third_party/tomlc99/stdex/keys06.toml.res @@ -0,0 +1 @@ +ERROR: line 3: key exists diff --git a/third_party/tomlc99/stdex/keys07.toml b/third_party/tomlc99/stdex/keys07.toml new file mode 100644 index 0000000..acff1cc --- /dev/null +++ b/third_party/tomlc99/stdex/keys07.toml @@ -0,0 +1,3 @@ +# THIS WILL NOT WORK +spelling = "favorite" +"spelling" = "favourite" diff --git a/third_party/tomlc99/stdex/keys07.toml.res b/third_party/tomlc99/stdex/keys07.toml.res new file mode 100644 index 0000000..0a6a07c --- /dev/null +++ b/third_party/tomlc99/stdex/keys07.toml.res @@ -0,0 +1 @@ +ERROR: line 3: key exists diff --git a/third_party/tomlc99/stdex/keys08.toml b/third_party/tomlc99/stdex/keys08.toml new file mode 100644 index 0000000..b4d1b37 --- /dev/null +++ b/third_party/tomlc99/stdex/keys08.toml @@ -0,0 +1,5 @@ +# This makes the key "fruit" into a table. +fruit.apple.smooth = true + +# So then you can add to the table "fruit" like so: +fruit.orange = 2 diff --git a/third_party/tomlc99/stdex/keys08.toml.res b/third_party/tomlc99/stdex/keys08.toml.res new file mode 100644 index 0000000..df743ec --- /dev/null +++ b/third_party/tomlc99/stdex/keys08.toml.res @@ -0,0 +1,8 @@ +{ + fruit = { + orange = 2, + apple = { + smooth = true, + }, + }, +} diff --git a/third_party/tomlc99/stdex/keys09.toml b/third_party/tomlc99/stdex/keys09.toml new file mode 100644 index 0000000..a6c5ea6 --- /dev/null +++ b/third_party/tomlc99/stdex/keys09.toml @@ -0,0 +1,8 @@ +# THE FOLLOWING IS INVALID + +# This defines the value of fruit.apple to be an integer. +fruit.apple = 1 + +# But then this treats fruit.apple like it's a table. +# You can't turn an integer into a table. +fruit.apple.smooth = true diff --git a/third_party/tomlc99/stdex/keys09.toml.res b/third_party/tomlc99/stdex/keys09.toml.res new file mode 100644 index 0000000..9365cbc --- /dev/null +++ b/third_party/tomlc99/stdex/keys09.toml.res @@ -0,0 +1 @@ +ERROR: line 8: key exists diff --git a/third_party/tomlc99/stdex/keys10.toml b/third_party/tomlc99/stdex/keys10.toml new file mode 100644 index 0000000..dc94a8c --- /dev/null +++ b/third_party/tomlc99/stdex/keys10.toml @@ -0,0 +1,10 @@ +# VALID BUT DISCOURAGED + +apple.type = "fruit" +orange.type = "fruit" + +apple.skin = "thin" +orange.skin = "thick" + +apple.color = "red" +orange.color = "orange" diff --git a/third_party/tomlc99/stdex/keys10.toml.res b/third_party/tomlc99/stdex/keys10.toml.res new file mode 100644 index 0000000..fac0f25 --- /dev/null +++ b/third_party/tomlc99/stdex/keys10.toml.res @@ -0,0 +1,12 @@ +{ + apple = { + type = "fruit", + skin = "thin", + color = "red", + }, + orange = { + type = "fruit", + skin = "thick", + color = "orange", + }, +} diff --git a/third_party/tomlc99/stdex/keys11.toml b/third_party/tomlc99/stdex/keys11.toml new file mode 100644 index 0000000..705686c --- /dev/null +++ b/third_party/tomlc99/stdex/keys11.toml @@ -0,0 +1,9 @@ +# RECOMMENDED + +apple.type = "fruit" +apple.skin = "thin" +apple.color = "red" + +orange.type = "fruit" +orange.skin = "thick" +orange.color = "orange" diff --git a/third_party/tomlc99/stdex/keys11.toml.res b/third_party/tomlc99/stdex/keys11.toml.res new file mode 100644 index 0000000..fac0f25 --- /dev/null +++ b/third_party/tomlc99/stdex/keys11.toml.res @@ -0,0 +1,12 @@ +{ + apple = { + type = "fruit", + skin = "thin", + color = "red", + }, + orange = { + type = "fruit", + skin = "thick", + color = "orange", + }, +} diff --git a/third_party/tomlc99/stdex/keys12.toml b/third_party/tomlc99/stdex/keys12.toml new file mode 100644 index 0000000..2efd659 --- /dev/null +++ b/third_party/tomlc99/stdex/keys12.toml @@ -0,0 +1 @@ +3.14159 = "pi" diff --git a/third_party/tomlc99/stdex/keys12.toml.res b/third_party/tomlc99/stdex/keys12.toml.res new file mode 100644 index 0000000..3f59e3a --- /dev/null +++ b/third_party/tomlc99/stdex/keys12.toml.res @@ -0,0 +1,5 @@ +{ + 3 = { + 14159 = "pi", + }, +} diff --git a/third_party/tomlc99/stdex/kvpair0.toml b/third_party/tomlc99/stdex/kvpair0.toml new file mode 100644 index 0000000..e5b34eb --- /dev/null +++ b/third_party/tomlc99/stdex/kvpair0.toml @@ -0,0 +1 @@ +key = "value" diff --git a/third_party/tomlc99/stdex/kvpair0.toml.res b/third_party/tomlc99/stdex/kvpair0.toml.res new file mode 100644 index 0000000..4b640b6 --- /dev/null +++ b/third_party/tomlc99/stdex/kvpair0.toml.res @@ -0,0 +1,3 @@ +{ + key = "value", +} diff --git a/third_party/tomlc99/stdex/kvpair1.toml b/third_party/tomlc99/stdex/kvpair1.toml new file mode 100644 index 0000000..56f085a --- /dev/null +++ b/third_party/tomlc99/stdex/kvpair1.toml @@ -0,0 +1 @@ +key = # INVALID diff --git a/third_party/tomlc99/stdex/kvpair1.toml.res b/third_party/tomlc99/stdex/kvpair1.toml.res new file mode 100644 index 0000000..13a97f7 --- /dev/null +++ b/third_party/tomlc99/stdex/kvpair1.toml.res @@ -0,0 +1 @@ +ERROR: line 1: syntax error diff --git a/third_party/tomlc99/stdex/kvpair2.toml b/third_party/tomlc99/stdex/kvpair2.toml new file mode 100644 index 0000000..e05c47c --- /dev/null +++ b/third_party/tomlc99/stdex/kvpair2.toml @@ -0,0 +1 @@ +first = "Tom" last = "Preston-Werner" # INVALID diff --git a/third_party/tomlc99/stdex/kvpair2.toml.res b/third_party/tomlc99/stdex/kvpair2.toml.res new file mode 100644 index 0000000..a307757 --- /dev/null +++ b/third_party/tomlc99/stdex/kvpair2.toml.res @@ -0,0 +1 @@ +ERROR: line 1: extra chars after value diff --git a/third_party/tomlc99/stdex/string0.toml b/third_party/tomlc99/stdex/string0.toml new file mode 100644 index 0000000..b611549 --- /dev/null +++ b/third_party/tomlc99/stdex/string0.toml @@ -0,0 +1 @@ +str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF." diff --git a/third_party/tomlc99/stdex/string0.toml.res b/third_party/tomlc99/stdex/string0.toml.res new file mode 100644 index 0000000..8b6e2e5 --- /dev/null +++ b/third_party/tomlc99/stdex/string0.toml.res @@ -0,0 +1,3 @@ +{ + str = "I'm a string. \"You can quote me\". Name\tJos\0xc3\0xa9\nLocation\tSF.", +} diff --git a/third_party/tomlc99/stdex/string1.toml b/third_party/tomlc99/stdex/string1.toml new file mode 100644 index 0000000..74c2f51 --- /dev/null +++ b/third_party/tomlc99/stdex/string1.toml @@ -0,0 +1,9 @@ +str1 = """ +Roses are red +Violets are blue""" + +# On a Unix system, the above multi-line string will most likely be the same as: +str2 = "Roses are red\nViolets are blue" + +# On a Windows system, it will most likely be equivalent to: +str3 = "Roses are red\r\nViolets are blue" diff --git a/third_party/tomlc99/stdex/string1.toml.res b/third_party/tomlc99/stdex/string1.toml.res new file mode 100644 index 0000000..364437d --- /dev/null +++ b/third_party/tomlc99/stdex/string1.toml.res @@ -0,0 +1,5 @@ +{ + str1 = "Roses are red\nViolets are blue", + str2 = "Roses are red\nViolets are blue", + str3 = "Roses are red\r\nViolets are blue", +} diff --git a/third_party/tomlc99/stdex/string3.toml b/third_party/tomlc99/stdex/string3.toml new file mode 100644 index 0000000..20b4aa7 --- /dev/null +++ b/third_party/tomlc99/stdex/string3.toml @@ -0,0 +1,15 @@ +# The following strings are byte-for-byte equivalent: +str1 = "The quick brown fox jumps over the lazy dog." + +str2 = """ +The quick brown \ + + + fox jumps over \ + the lazy dog.""" + +str3 = """\ + The quick brown \ + fox jumps over \ + the lazy dog.\ + """ diff --git a/third_party/tomlc99/stdex/string3.toml.res b/third_party/tomlc99/stdex/string3.toml.res new file mode 100644 index 0000000..f2f6886 --- /dev/null +++ b/third_party/tomlc99/stdex/string3.toml.res @@ -0,0 +1,5 @@ +{ + str1 = "The quick brown fox jumps over the lazy dog.", + str2 = "The quick brown fox jumps over the lazy dog.", + str3 = "The quick brown fox jumps over the lazy dog.", +} diff --git a/third_party/tomlc99/stdex/string4.toml b/third_party/tomlc99/stdex/string4.toml new file mode 100644 index 0000000..8f51b11 --- /dev/null +++ b/third_party/tomlc99/stdex/string4.toml @@ -0,0 +1,7 @@ +str4 = """Here are two quotation marks: "". Simple enough.""" +# str5 = """Here are three quotation marks: """.""" # INVALID +str5 = """Here are three quotation marks: ""\".""" +str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" + +# "This," she said, "is just a pointless statement." +str7 = """"This," she said, "is just a pointless statement."""" diff --git a/third_party/tomlc99/stdex/string4.toml.res b/third_party/tomlc99/stdex/string4.toml.res new file mode 100644 index 0000000..e6289b2 --- /dev/null +++ b/third_party/tomlc99/stdex/string4.toml.res @@ -0,0 +1,6 @@ +{ + str4 = "Here are two quotation marks: \"\". Simple enough.", + str5 = "Here are three quotation marks: \"\"\".", + str6 = "Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\".", + str7 = "\"This,\" she said, \"is just a pointless statement.\"", +} diff --git a/third_party/tomlc99/stdex/string5.toml b/third_party/tomlc99/stdex/string5.toml new file mode 100644 index 0000000..36772bb --- /dev/null +++ b/third_party/tomlc99/stdex/string5.toml @@ -0,0 +1,5 @@ +# What you see is what you get. +winpath = 'C:\Users\nodejs\templates' +winpath2 = '\\ServerX\admin$\system32\' +quoted = 'Tom "Dubs" Preston-Werner' +regex = '<\i\c*\s*>' diff --git a/third_party/tomlc99/stdex/string5.toml.res b/third_party/tomlc99/stdex/string5.toml.res new file mode 100644 index 0000000..31a4447 --- /dev/null +++ b/third_party/tomlc99/stdex/string5.toml.res @@ -0,0 +1,6 @@ +{ + winpath = "C:\\Users\\nodejs\\templates", + winpath2 = "\\\\ServerX\\admin$\\system32\\", + quoted = "Tom \"Dubs\" Preston-Werner", + regex = "<\\i\\c*\\s*>", +} diff --git a/third_party/tomlc99/stdex/string6.toml b/third_party/tomlc99/stdex/string6.toml new file mode 100644 index 0000000..bc88494 --- /dev/null +++ b/third_party/tomlc99/stdex/string6.toml @@ -0,0 +1,7 @@ +regex2 = '''I [dw]on't need \d{2} apples''' +lines = ''' +The first newline is +trimmed in raw strings. + All other whitespace + is preserved. +''' diff --git a/third_party/tomlc99/stdex/string6.toml.res b/third_party/tomlc99/stdex/string6.toml.res new file mode 100644 index 0000000..7ed3a14 --- /dev/null +++ b/third_party/tomlc99/stdex/string6.toml.res @@ -0,0 +1,4 @@ +{ + regex2 = "I [dw]on't need \\d{2} apples", + lines = "The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n", +} diff --git a/third_party/tomlc99/stdex/string7.toml b/third_party/tomlc99/stdex/string7.toml new file mode 100644 index 0000000..f87153e --- /dev/null +++ b/third_party/tomlc99/stdex/string7.toml @@ -0,0 +1,4 @@ +quot15 = '''Here are fifteen quotation marks: """""""""""""""''' + +# 'That,' she said, 'is still pointless.' +str = ''''That,' she said, 'is still pointless.'''' diff --git a/third_party/tomlc99/stdex/string7.toml.res b/third_party/tomlc99/stdex/string7.toml.res new file mode 100644 index 0000000..f194093 --- /dev/null +++ b/third_party/tomlc99/stdex/string7.toml.res @@ -0,0 +1,4 @@ +{ + quot15 = "Here are fifteen quotation marks: \"\"\"\"\"\"\"\"\"\"\"\"\"\"\"", + str = "'That,' she said, 'is still pointless.'", +} diff --git a/third_party/tomlc99/stdex/string8.toml b/third_party/tomlc99/stdex/string8.toml new file mode 100644 index 0000000..fd74bf2 --- /dev/null +++ b/third_party/tomlc99/stdex/string8.toml @@ -0,0 +1,3 @@ +# apos15 = '''Here are fifteen apostrophes: '''''''''''''''''' # INVALID +apos15 = "Here are fifteen apostrophes: '''''''''''''''" + diff --git a/third_party/tomlc99/stdex/string8.toml.res b/third_party/tomlc99/stdex/string8.toml.res new file mode 100644 index 0000000..cb2526f --- /dev/null +++ b/third_party/tomlc99/stdex/string8.toml.res @@ -0,0 +1 @@ +ERROR: line 2: triple-s-quote inside string lit diff --git a/third_party/tomlc99/stdex/tab01.toml b/third_party/tomlc99/stdex/tab01.toml new file mode 100644 index 0000000..6b4884c --- /dev/null +++ b/third_party/tomlc99/stdex/tab01.toml @@ -0,0 +1,7 @@ +[table-1] +key1 = "some string" +key2 = 123 + +[table-2] +key1 = "another string" +key2 = 456 diff --git a/third_party/tomlc99/stdex/tab01.toml.res b/third_party/tomlc99/stdex/tab01.toml.res new file mode 100644 index 0000000..0418849 --- /dev/null +++ b/third_party/tomlc99/stdex/tab01.toml.res @@ -0,0 +1,10 @@ +{ + table-1 = { + key1 = "some string", + key2 = 123, + }, + table-2 = { + key1 = "another string", + key2 = 456, + }, +} diff --git a/third_party/tomlc99/stdex/tab02.toml b/third_party/tomlc99/stdex/tab02.toml new file mode 100644 index 0000000..32cbe79 --- /dev/null +++ b/third_party/tomlc99/stdex/tab02.toml @@ -0,0 +1,2 @@ +[dog."tater.man"] +type.name = "pug" diff --git a/third_party/tomlc99/stdex/tab02.toml.res b/third_party/tomlc99/stdex/tab02.toml.res new file mode 100644 index 0000000..82cfae1 --- /dev/null +++ b/third_party/tomlc99/stdex/tab02.toml.res @@ -0,0 +1,9 @@ +{ + dog = { + tater.man = { + type = { + name = "pug", + }, + }, + }, +} diff --git a/third_party/tomlc99/stdex/tab03.toml b/third_party/tomlc99/stdex/tab03.toml new file mode 100644 index 0000000..0b10db3 --- /dev/null +++ b/third_party/tomlc99/stdex/tab03.toml @@ -0,0 +1,4 @@ +[a.b.c] # this is best practice +[ d.e.f ] # same as [d.e.f] +[ g . h . i ] # same as [g.h.i] +[ j . "ʞ" . 'l' ] # same as [j."ʞ".'l'] diff --git a/third_party/tomlc99/stdex/tab03.toml.res b/third_party/tomlc99/stdex/tab03.toml.res new file mode 100644 index 0000000..983d6cb --- /dev/null +++ b/third_party/tomlc99/stdex/tab03.toml.res @@ -0,0 +1,26 @@ +{ + a = { + b = { + c = { + }, + }, + }, + d = { + e = { + f = { + }, + }, + }, + g = { + h = { + i = { + }, + }, + }, + j = { + ʞ = { + l = { + }, + }, + }, +} diff --git a/third_party/tomlc99/stdex/tab04.toml b/third_party/tomlc99/stdex/tab04.toml new file mode 100644 index 0000000..256e6c0 --- /dev/null +++ b/third_party/tomlc99/stdex/tab04.toml @@ -0,0 +1,6 @@ +# [x] you +# [x.y] don't +# [x.y.z] need these +[x.y.z.w] # for this to work + +[x] # defining a super-table afterward is ok diff --git a/third_party/tomlc99/stdex/tab04.toml.res b/third_party/tomlc99/stdex/tab04.toml.res new file mode 100644 index 0000000..718d94a --- /dev/null +++ b/third_party/tomlc99/stdex/tab04.toml.res @@ -0,0 +1,10 @@ +{ + x = { + y = { + z = { + w = { + }, + }, + }, + }, +} diff --git a/third_party/tomlc99/stdex/tab05.toml b/third_party/tomlc99/stdex/tab05.toml new file mode 100644 index 0000000..0704ba7 --- /dev/null +++ b/third_party/tomlc99/stdex/tab05.toml @@ -0,0 +1,7 @@ +# DO NOT DO THIS + +[fruit] +apple = "red" + +[fruit] +orange = "orange" diff --git a/third_party/tomlc99/stdex/tab05.toml.res b/third_party/tomlc99/stdex/tab05.toml.res new file mode 100644 index 0000000..b57e247 --- /dev/null +++ b/third_party/tomlc99/stdex/tab05.toml.res @@ -0,0 +1 @@ +ERROR: line 6: key exists diff --git a/third_party/tomlc99/stdex/tab06.toml b/third_party/tomlc99/stdex/tab06.toml new file mode 100644 index 0000000..e58bf8e --- /dev/null +++ b/third_party/tomlc99/stdex/tab06.toml @@ -0,0 +1,7 @@ +# DO NOT DO THIS EITHER + +[fruit] +apple = "red" + +[fruit.apple] +texture = "smooth" diff --git a/third_party/tomlc99/stdex/tab06.toml.res b/third_party/tomlc99/stdex/tab06.toml.res new file mode 100644 index 0000000..b57e247 --- /dev/null +++ b/third_party/tomlc99/stdex/tab06.toml.res @@ -0,0 +1 @@ +ERROR: line 6: key exists diff --git a/third_party/tomlc99/stdex/tab07.toml b/third_party/tomlc99/stdex/tab07.toml new file mode 100644 index 0000000..03fe3a3 --- /dev/null +++ b/third_party/tomlc99/stdex/tab07.toml @@ -0,0 +1,4 @@ +# VALID BUT DISCOURAGED +[fruit.apple] +[animal] +[fruit.orange] diff --git a/third_party/tomlc99/stdex/tab07.toml.res b/third_party/tomlc99/stdex/tab07.toml.res new file mode 100644 index 0000000..239ced9 --- /dev/null +++ b/third_party/tomlc99/stdex/tab07.toml.res @@ -0,0 +1,10 @@ +{ + fruit = { + apple = { + }, + orange = { + }, + }, + animal = { + }, +} diff --git a/third_party/tomlc99/stdex/tab08.toml b/third_party/tomlc99/stdex/tab08.toml new file mode 100644 index 0000000..c57a229 --- /dev/null +++ b/third_party/tomlc99/stdex/tab08.toml @@ -0,0 +1,8 @@ +# Top-level table begins. +name = "Fido" +breed = "pug" + +# Top-level table ends. +[owner] +name = "Regina Dogman" +member_since = 1999-08-04 diff --git a/third_party/tomlc99/stdex/tab08.toml.res b/third_party/tomlc99/stdex/tab08.toml.res new file mode 100644 index 0000000..64371ae --- /dev/null +++ b/third_party/tomlc99/stdex/tab08.toml.res @@ -0,0 +1,8 @@ +{ + name = "Fido", + breed = "pug", + owner = { + name = "Regina Dogman", + member_since = 1999-08-04, + }, +} diff --git a/third_party/tomlc99/stdex/tab09.toml b/third_party/tomlc99/stdex/tab09.toml new file mode 100644 index 0000000..d5a2c11 --- /dev/null +++ b/third_party/tomlc99/stdex/tab09.toml @@ -0,0 +1,7 @@ +fruit.apple.color = "red" +# Defines a table named fruit +# Defines a table named fruit.apple + +fruit.apple.taste.sweet = true +# Defines a table named fruit.apple.taste +# fruit and fruit.apple were already created diff --git a/third_party/tomlc99/stdex/tab09.toml.res b/third_party/tomlc99/stdex/tab09.toml.res new file mode 100644 index 0000000..9346469 --- /dev/null +++ b/third_party/tomlc99/stdex/tab09.toml.res @@ -0,0 +1,10 @@ +{ + fruit = { + apple = { + color = "red", + taste = { + sweet = true, + }, + }, + }, +} diff --git a/third_party/tomlc99/stdex/tab10.toml b/third_party/tomlc99/stdex/tab10.toml new file mode 100644 index 0000000..9eac5f6 --- /dev/null +++ b/third_party/tomlc99/stdex/tab10.toml @@ -0,0 +1,5 @@ +[fruit] +apple.color = "red" +apple.taste.sweet = true + +[fruit.apple] # INVALID diff --git a/third_party/tomlc99/stdex/tab10.toml.res b/third_party/tomlc99/stdex/tab10.toml.res new file mode 100644 index 0000000..eb9b163 --- /dev/null +++ b/third_party/tomlc99/stdex/tab10.toml.res @@ -0,0 +1 @@ +ERROR: line 5: key exists diff --git a/third_party/tomlc99/stdex/tab11.toml b/third_party/tomlc99/stdex/tab11.toml new file mode 100644 index 0000000..ae2d65b --- /dev/null +++ b/third_party/tomlc99/stdex/tab11.toml @@ -0,0 +1,9 @@ +[fruit] +apple.color = "red" +apple.taste.sweet = true + +# [fruit.apple] # INVALID +# [fruit.apple.taste] # INVALID + +[fruit.apple.texture] # you can add sub-tables +smooth = true diff --git a/third_party/tomlc99/stdex/tab11.toml.res b/third_party/tomlc99/stdex/tab11.toml.res new file mode 100644 index 0000000..914481b --- /dev/null +++ b/third_party/tomlc99/stdex/tab11.toml.res @@ -0,0 +1,13 @@ +{ + fruit = { + apple = { + color = "red", + taste = { + sweet = true, + }, + texture = { + smooth = true, + }, + }, + }, +} diff --git a/third_party/tomlc99/stdex/ts1.toml b/third_party/tomlc99/stdex/ts1.toml new file mode 100644 index 0000000..9c15c6b --- /dev/null +++ b/third_party/tomlc99/stdex/ts1.toml @@ -0,0 +1,5 @@ +odt1 = 1979-05-27T07:32:00Z +odt2 = 1979-05-27T00:32:00-07:00 +odt3 = 1979-05-27T00:32:00.999999-07:00 +odt4 = 1979-05-27T00:32:00.01-07:00 + diff --git a/third_party/tomlc99/stdex/ts1.toml.res b/third_party/tomlc99/stdex/ts1.toml.res new file mode 100644 index 0000000..2df13b0 --- /dev/null +++ b/third_party/tomlc99/stdex/ts1.toml.res @@ -0,0 +1,6 @@ +{ + odt1 = 1979-05-27T07:32:00Z, + odt2 = 1979-05-27T00:32:00-07:00, + odt3 = 1979-05-27T00:32:00.999-07:00, + odt4 = 1979-05-27T00:32:00.010-07:00, +} diff --git a/third_party/tomlc99/stdex/ts2.toml b/third_party/tomlc99/stdex/ts2.toml new file mode 100644 index 0000000..da9bd3b --- /dev/null +++ b/third_party/tomlc99/stdex/ts2.toml @@ -0,0 +1 @@ +odt4 = 1979-05-27 07:32:00Z diff --git a/third_party/tomlc99/stdex/ts2.toml.res b/third_party/tomlc99/stdex/ts2.toml.res new file mode 100644 index 0000000..1b5d0a8 --- /dev/null +++ b/third_party/tomlc99/stdex/ts2.toml.res @@ -0,0 +1,3 @@ +{ + odt4 = 1979-05-27T07:32:00Z, +} diff --git a/third_party/tomlc99/stdex/ts3.toml b/third_party/tomlc99/stdex/ts3.toml new file mode 100644 index 0000000..38700d2 --- /dev/null +++ b/third_party/tomlc99/stdex/ts3.toml @@ -0,0 +1,2 @@ +ldt1 = 1979-05-27T07:32:00 +ldt2 = 1979-05-27T00:32:00.999999 diff --git a/third_party/tomlc99/stdex/ts3.toml.res b/third_party/tomlc99/stdex/ts3.toml.res new file mode 100644 index 0000000..54a0438 --- /dev/null +++ b/third_party/tomlc99/stdex/ts3.toml.res @@ -0,0 +1,4 @@ +{ + ldt1 = 1979-05-27T07:32:00, + ldt2 = 1979-05-27T00:32:00.999, +} diff --git a/third_party/tomlc99/stdex/ts4.toml b/third_party/tomlc99/stdex/ts4.toml new file mode 100644 index 0000000..6670e5d --- /dev/null +++ b/third_party/tomlc99/stdex/ts4.toml @@ -0,0 +1 @@ +ld1 = 1979-05-27 diff --git a/third_party/tomlc99/stdex/ts4.toml.res b/third_party/tomlc99/stdex/ts4.toml.res new file mode 100644 index 0000000..473b6c2 --- /dev/null +++ b/third_party/tomlc99/stdex/ts4.toml.res @@ -0,0 +1,3 @@ +{ + ld1 = 1979-05-27, +} diff --git a/third_party/tomlc99/stdex/ts5.toml b/third_party/tomlc99/stdex/ts5.toml new file mode 100644 index 0000000..dbd058a --- /dev/null +++ b/third_party/tomlc99/stdex/ts5.toml @@ -0,0 +1,2 @@ +lt1 = 07:32:00 +lt2 = 00:32:00.999999 diff --git a/third_party/tomlc99/stdex/ts5.toml.res b/third_party/tomlc99/stdex/ts5.toml.res new file mode 100644 index 0000000..cd5b62e --- /dev/null +++ b/third_party/tomlc99/stdex/ts5.toml.res @@ -0,0 +1,4 @@ +{ + lt1 = 07:32:00, + lt2 = 00:32:00.999, +} diff --git a/third_party/tomlc99/test1/.gitignore b/third_party/tomlc99/test1/.gitignore new file mode 100644 index 0000000..f39c742 --- /dev/null +++ b/third_party/tomlc99/test1/.gitignore @@ -0,0 +1,3 @@ +/goworkspace +/toml-test +/toml-test-decoder diff --git a/third_party/tomlc99/test1/README.md b/third_party/tomlc99/test1/README.md new file mode 100644 index 0000000..1a83616 --- /dev/null +++ b/third_party/tomlc99/test1/README.md @@ -0,0 +1,22 @@ +How to run the tests +=== + +``` +% bash build.sh +% bash run.sh +Test: array-mixed-types-arrays-and-ints (invalid) + +Expected an error, but no error was reported. +------------------------------------------------------------------------------- +Test: array-mixed-types-ints-and-floats (invalid) + +Expected an error, but no error was reported. +------------------------------------------------------------------------------- +Test: array-mixed-types-strings-and-ints (invalid) + +Expected an error, but no error was reported. + +129 passed, 3 failed +``` + +Note: toml version 1.0 allows mixed types in arrays. diff --git a/third_party/tomlc99/test1/build.sh b/third_party/tomlc99/test1/build.sh new file mode 100755 index 0000000..496806a --- /dev/null +++ b/third_party/tomlc99/test1/build.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +export GOBIN=$DIR +go install github.com/toml-lang/toml-test/cmd/toml-test@latest # install test suite \ No newline at end of file diff --git a/third_party/tomlc99/test1/extra/array_of_tables.toml b/third_party/tomlc99/test1/extra/array_of_tables.toml new file mode 100644 index 0000000..86ed1f0 --- /dev/null +++ b/third_party/tomlc99/test1/extra/array_of_tables.toml @@ -0,0 +1 @@ +x = [ {'a'= 1}, {'a'= 2} ] diff --git a/third_party/tomlc99/test1/extra/inline_array.toml b/third_party/tomlc99/test1/extra/inline_array.toml new file mode 100644 index 0000000..a0a971d --- /dev/null +++ b/third_party/tomlc99/test1/extra/inline_array.toml @@ -0,0 +1 @@ +x = [1,2,3] diff --git a/third_party/tomlc99/test1/extra/inline_table.toml b/third_party/tomlc99/test1/extra/inline_table.toml new file mode 100644 index 0000000..7673f2e --- /dev/null +++ b/third_party/tomlc99/test1/extra/inline_table.toml @@ -0,0 +1 @@ +x = {'a'= 1, 'b'= 2 } diff --git a/third_party/tomlc99/test1/run.sh b/third_party/tomlc99/test1/run.sh new file mode 100755 index 0000000..2e50ba4 --- /dev/null +++ b/third_party/tomlc99/test1/run.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +$DIR/toml-test $DIR/../toml_json diff --git a/third_party/tomlc99/test2/.gitignore b/third_party/tomlc99/test2/.gitignore new file mode 100644 index 0000000..21335a0 --- /dev/null +++ b/third_party/tomlc99/test2/.gitignore @@ -0,0 +1 @@ +/toml-spec-tests \ No newline at end of file diff --git a/third_party/tomlc99/test2/Note.txt b/third_party/tomlc99/test2/Note.txt new file mode 100644 index 0000000..6ca7347 --- /dev/null +++ b/third_party/tomlc99/test2/Note.txt @@ -0,0 +1,6 @@ +Note that we utilize the 'jq' command to normalize json files for comparison. Depending the +verson of jq on your system, some tests may generate error that looks like this: + + parse error: Exceeds depth limit for parsing at line 1 + +This is an error in 'jq', and it does not indicate that the tests themselves fail. diff --git a/third_party/tomlc99/test2/build.sh b/third_party/tomlc99/test2/build.sh new file mode 100755 index 0000000..1f25f8c --- /dev/null +++ b/third_party/tomlc99/test2/build.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -e + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +[ -d toml-spec-tests ] || git clone https://github.com/cktan/toml-spec-tests.git diff --git a/third_party/tomlc99/test2/run.sh b/third_party/tomlc99/test2/run.sh new file mode 100755 index 0000000..f67995a --- /dev/null +++ b/third_party/tomlc99/test2/run.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +if ! (which jq >& /dev/null); then + echo "ERROR: please install the 'jq' utility" + exit 1 +fi + +# +# POSITIVE tests +# +for i in toml-spec-tests/values/*.toml; do + fname="$i" + ext="${fname##*.}" + fname="${fname%.*}" + echo -n $fname ' ' + res='[OK]' + if (../toml_json $fname.toml >& $fname.json.out); then + jq -S . $fname.json.out > t.json + mv t.json $fname.json.out + if [ -f $fname.json ]; then + if ! (diff $fname.json $fname.json.out >& /dev/null); then + res='[FAILED]' + else + rm -f $fname.json.out + fi + else + res='[??]' + fi + fi + echo ... $res +done + + +# +# NEGATIVE tests +# +for i in toml-spec-tests/errors/*.toml; do + echo -n $i ' ' + res='[OK]' + if (../toml_json $i >& $i.json.out); then + res='[FAILED]' + fi + echo ... $res +done diff --git a/third_party/tomlc99/toml.c b/third_party/tomlc99/toml.c new file mode 100644 index 0000000..e7b878e --- /dev/null +++ b/third_party/tomlc99/toml.c @@ -0,0 +1,2392 @@ +/* + + MIT License + + Copyright (c) CK Tan + https://github.com/cktan/tomlc99 + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +*/ +#define _POSIX_C_SOURCE 200809L +#include "toml.h" +#include +#include +#include +#include +#include +#include +#include +#include + +static void *(*ppmalloc)(size_t) = malloc; +static void (*ppfree)(void *) = free; + +void toml_set_memutil(void *(*xxmalloc)(size_t), void (*xxfree)(void *)) { + if (xxmalloc) + ppmalloc = xxmalloc; + if (xxfree) + ppfree = xxfree; +} + +#define ALIGN8(sz) (((sz) + 7) & ~7) +#define MALLOC(a) ppmalloc(a) +#define FREE(a) ppfree(a) + +#define malloc(x) error - forbidden - use MALLOC instead +#define free(x) error - forbidden - use FREE instead +#define calloc(x, y) error - forbidden - use CALLOC instead + +static void *CALLOC(size_t nmemb, size_t sz) { + int nb = ALIGN8(sz) * nmemb; + void *p = MALLOC(nb); + if (p) { + memset(p, 0, nb); + } + return p; +} + +// some old platforms define strdup macro -- drop it. +#undef strdup +#define strdup(x) error - forbidden - use STRDUP instead + +static char *STRDUP(const char *s) { + int len = strlen(s); + char *p = MALLOC(len + 1); + if (p) { + memcpy(p, s, len); + p[len] = 0; + } + return p; +} + +// some old platforms define strndup macro -- drop it. +#undef strndup +#define strndup(x) error - forbiden - use STRNDUP instead + +static char *STRNDUP(const char *s, size_t n) { + size_t len = strnlen(s, n); + char *p = MALLOC(len + 1); + if (p) { + memcpy(p, s, len); + p[len] = 0; + } + return p; +} + +/** + * Convert a char in utf8 into UCS, and store it in *ret. + * Return #bytes consumed or -1 on failure. + */ +int toml_utf8_to_ucs(const char *orig, int len, int64_t *ret) { + const unsigned char *buf = (const unsigned char *)orig; + unsigned i = *buf++; + int64_t v; + + /* 0x00000000 - 0x0000007F: + 0xxxxxxx + */ + if (0 == (i >> 7)) { + if (len < 1) + return -1; + v = i; + return *ret = v, 1; + } + /* 0x00000080 - 0x000007FF: + 110xxxxx 10xxxxxx + */ + if (0x6 == (i >> 5)) { + if (len < 2) + return -1; + v = i & 0x1f; + for (int j = 0; j < 1; j++) { + i = *buf++; + if (0x2 != (i >> 6)) + return -1; + v = (v << 6) | (i & 0x3f); + } + return *ret = v, (const char *)buf - orig; + } + + /* 0x00000800 - 0x0000FFFF: + 1110xxxx 10xxxxxx 10xxxxxx + */ + if (0xE == (i >> 4)) { + if (len < 3) + return -1; + v = i & 0x0F; + for (int j = 0; j < 2; j++) { + i = *buf++; + if (0x2 != (i >> 6)) + return -1; + v = (v << 6) | (i & 0x3f); + } + return *ret = v, (const char *)buf - orig; + } + + /* 0x00010000 - 0x001FFFFF: + 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + if (0x1E == (i >> 3)) { + if (len < 4) + return -1; + v = i & 0x07; + for (int j = 0; j < 3; j++) { + i = *buf++; + if (0x2 != (i >> 6)) + return -1; + v = (v << 6) | (i & 0x3f); + } + return *ret = v, (const char *)buf - orig; + } + + /* 0x00200000 - 0x03FFFFFF: + 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + if (0x3E == (i >> 2)) { + if (len < 5) + return -1; + v = i & 0x03; + for (int j = 0; j < 4; j++) { + i = *buf++; + if (0x2 != (i >> 6)) + return -1; + v = (v << 6) | (i & 0x3f); + } + return *ret = v, (const char *)buf - orig; + } + + /* 0x04000000 - 0x7FFFFFFF: + 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + if (0x7e == (i >> 1)) { + if (len < 6) + return -1; + v = i & 0x01; + for (int j = 0; j < 5; j++) { + i = *buf++; + if (0x2 != (i >> 6)) + return -1; + v = (v << 6) | (i & 0x3f); + } + return *ret = v, (const char *)buf - orig; + } + return -1; +} + +/** + * Convert a UCS char to utf8 code, and return it in buf. + * Return #bytes used in buf to encode the char, or + * -1 on error. + */ +int toml_ucs_to_utf8(int64_t code, char buf[6]) { + /* http://stackoverflow.com/questions/6240055/manually-converting-unicode-codepoints-into-utf-8-and-utf-16 + */ + /* The UCS code values 0xd800–0xdfff (UTF-16 surrogates) as well + * as 0xfffe and 0xffff (UCS noncharacters) should not appear in + * conforming UTF-8 streams. + */ + if (0xd800 <= code && code <= 0xdfff) + return -1; + if (0xfffe <= code && code <= 0xffff) + return -1; + + /* 0x00000000 - 0x0000007F: + 0xxxxxxx + */ + if (code < 0) + return -1; + if (code <= 0x7F) { + buf[0] = (unsigned char)code; + return 1; + } + + /* 0x00000080 - 0x000007FF: + 110xxxxx 10xxxxxx + */ + if (code <= 0x000007FF) { + buf[0] = (unsigned char)(0xc0 | (code >> 6)); + buf[1] = (unsigned char)(0x80 | (code & 0x3f)); + return 2; + } + + /* 0x00000800 - 0x0000FFFF: + 1110xxxx 10xxxxxx 10xxxxxx + */ + if (code <= 0x0000FFFF) { + buf[0] = (unsigned char)(0xe0 | (code >> 12)); + buf[1] = (unsigned char)(0x80 | ((code >> 6) & 0x3f)); + buf[2] = (unsigned char)(0x80 | (code & 0x3f)); + return 3; + } + + /* 0x00010000 - 0x001FFFFF: + 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + if (code <= 0x001FFFFF) { + buf[0] = (unsigned char)(0xf0 | (code >> 18)); + buf[1] = (unsigned char)(0x80 | ((code >> 12) & 0x3f)); + buf[2] = (unsigned char)(0x80 | ((code >> 6) & 0x3f)); + buf[3] = (unsigned char)(0x80 | (code & 0x3f)); + return 4; + } + + /* 0x00200000 - 0x03FFFFFF: + 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + if (code <= 0x03FFFFFF) { + buf[0] = (unsigned char)(0xf8 | (code >> 24)); + buf[1] = (unsigned char)(0x80 | ((code >> 18) & 0x3f)); + buf[2] = (unsigned char)(0x80 | ((code >> 12) & 0x3f)); + buf[3] = (unsigned char)(0x80 | ((code >> 6) & 0x3f)); + buf[4] = (unsigned char)(0x80 | (code & 0x3f)); + return 5; + } + + /* 0x04000000 - 0x7FFFFFFF: + 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + if (code <= 0x7FFFFFFF) { + buf[0] = (unsigned char)(0xfc | (code >> 30)); + buf[1] = (unsigned char)(0x80 | ((code >> 24) & 0x3f)); + buf[2] = (unsigned char)(0x80 | ((code >> 18) & 0x3f)); + buf[3] = (unsigned char)(0x80 | ((code >> 12) & 0x3f)); + buf[4] = (unsigned char)(0x80 | ((code >> 6) & 0x3f)); + buf[5] = (unsigned char)(0x80 | (code & 0x3f)); + return 6; + } + + return -1; +} + +/* + * TOML has 3 data structures: value, array, table. + * Each of them can have identification key. + */ +typedef struct toml_keyval_t toml_keyval_t; +struct toml_keyval_t { + const char *key; /* key to this value */ + const char *val; /* the raw value */ +}; + +typedef struct toml_arritem_t toml_arritem_t; +struct toml_arritem_t { + int valtype; /* for value kind: 'i'nt, 'd'ouble, 'b'ool, 's'tring, 't'ime, + 'D'ate, 'T'imestamp */ + char *val; + toml_array_t *arr; + toml_table_t *tab; +}; + +struct toml_array_t { + const char *key; /* key to this array */ + int kind; /* element kind: 'v'alue, 'a'rray, or 't'able, 'm'ixed */ + int type; /* for value kind: 'i'nt, 'd'ouble, 'b'ool, 's'tring, 't'ime, + 'D'ate, 'T'imestamp, 'm'ixed */ + + int nitem; /* number of elements */ + toml_arritem_t *item; +}; + +struct toml_table_t { + const char *key; /* key to this table */ + bool implicit; /* table was created implicitly */ + bool readonly; /* no more modification allowed */ + + /* key-values in the table */ + int nkval; + toml_keyval_t **kval; + + /* arrays in the table */ + int narr; + toml_array_t **arr; + + /* tables in the table */ + int ntab; + toml_table_t **tab; +}; + +static inline void xfree(const void *x) { + if (x) + FREE((void *)(intptr_t)x); +} + +enum tokentype_t { + INVALID, + DOT, + COMMA, + EQUAL, + LBRACE, + RBRACE, + NEWLINE, + LBRACKET, + RBRACKET, + STRING, +}; +typedef enum tokentype_t tokentype_t; + +typedef struct token_t token_t; +struct token_t { + tokentype_t tok; + int lineno; + char *ptr; /* points into context->start */ + int len; + int eof; +}; + +typedef struct context_t context_t; +struct context_t { + char *start; + char *stop; + char *errbuf; + int errbufsz; + + token_t tok; + toml_table_t *root; + toml_table_t *curtab; + + struct { + int top; + char *key[10]; + token_t tok[10]; + } tpath; +}; + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) +#define FLINE __FILE__ ":" TOSTRING(__LINE__) + +static int next_token(context_t *ctx, int dotisspecial); + +/* + Error reporting. Call when an error is detected. Always return -1. +*/ +static int e_outofmemory(context_t *ctx, const char *fline) { + snprintf(ctx->errbuf, ctx->errbufsz, "ERROR: out of memory (%s)", fline); + return -1; +} + +static int e_internal(context_t *ctx, const char *fline) { + snprintf(ctx->errbuf, ctx->errbufsz, "internal error (%s)", fline); + return -1; +} + +static int e_syntax(context_t *ctx, int lineno, const char *msg) { + snprintf(ctx->errbuf, ctx->errbufsz, "line %d: %s", lineno, msg); + return -1; +} + +static int e_badkey(context_t *ctx, int lineno) { + snprintf(ctx->errbuf, ctx->errbufsz, "line %d: bad key", lineno); + return -1; +} + +static int e_keyexists(context_t *ctx, int lineno) { + snprintf(ctx->errbuf, ctx->errbufsz, "line %d: key exists", lineno); + return -1; +} + +static int e_forbid(context_t *ctx, int lineno, const char *msg) { + snprintf(ctx->errbuf, ctx->errbufsz, "line %d: %s", lineno, msg); + return -1; +} + +static void *expand(void *p, int sz, int newsz) { + void *s = MALLOC(newsz); + if (!s) + return 0; + + if (p) { + memcpy(s, p, sz); + FREE(p); + } + return s; +} + +static void **expand_ptrarr(void **p, int n) { + void **s = MALLOC((n + 1) * sizeof(void *)); + if (!s) + return 0; + + s[n] = 0; + if (p) { + memcpy(s, p, n * sizeof(void *)); + FREE(p); + } + return s; +} + +static toml_arritem_t *expand_arritem(toml_arritem_t *p, int n) { + toml_arritem_t *pp = expand(p, n * sizeof(*p), (n + 1) * sizeof(*p)); + if (!pp) + return 0; + + memset(&pp[n], 0, sizeof(pp[n])); + return pp; +} + +static char *norm_lit_str(const char *src, int srclen, int multiline, + char *errbuf, int errbufsz) { + char *dst = 0; /* will write to dst[] and return it */ + int max = 0; /* max size of dst[] */ + int off = 0; /* cur offset in dst[] */ + const char *sp = src; + const char *sq = src + srclen; + int ch; + + /* scan forward on src */ + for (;;) { + if (off >= max - 10) { /* have some slack for misc stuff */ + int newmax = max + 50; + char *x = expand(dst, max, newmax); + if (!x) { + xfree(dst); + snprintf(errbuf, errbufsz, "out of memory"); + return 0; + } + dst = x; + max = newmax; + } + + /* finished? */ + if (sp >= sq) + break; + + ch = *sp++; + /* control characters other than tab is not allowed */ + if ((0 <= ch && ch <= 0x08) || (0x0a <= ch && ch <= 0x1f) || (ch == 0x7f)) { + if (!(multiline && (ch == '\r' || ch == '\n'))) { + xfree(dst); + snprintf(errbuf, errbufsz, "invalid char U+%04x", ch); + return 0; + } + } + + // a plain copy suffice + dst[off++] = ch; + } + + dst[off++] = 0; + return dst; +} + +/* + * Convert src to raw unescaped utf-8 string. + * Returns NULL if error with errmsg in errbuf. + */ +static char *norm_basic_str(const char *src, int srclen, int multiline, + char *errbuf, int errbufsz) { + char *dst = 0; /* will write to dst[] and return it */ + int max = 0; /* max size of dst[] */ + int off = 0; /* cur offset in dst[] */ + const char *sp = src; + const char *sq = src + srclen; + int ch; + + /* scan forward on src */ + for (;;) { + if (off >= max - 10) { /* have some slack for misc stuff */ + int newmax = max + 50; + char *x = expand(dst, max, newmax); + if (!x) { + xfree(dst); + snprintf(errbuf, errbufsz, "out of memory"); + return 0; + } + dst = x; + max = newmax; + } + + /* finished? */ + if (sp >= sq) + break; + + ch = *sp++; + if (ch != '\\') { + /* these chars must be escaped: U+0000 to U+0008, U+000A to U+001F, U+007F + */ + if ((0 <= ch && ch <= 0x08) || (0x0a <= ch && ch <= 0x1f) || + (ch == 0x7f)) { + if (!(multiline && (ch == '\r' || ch == '\n'))) { + xfree(dst); + snprintf(errbuf, errbufsz, "invalid char U+%04x", ch); + return 0; + } + } + + // a plain copy suffice + dst[off++] = ch; + continue; + } + + /* ch was backslash. we expect the escape char. */ + if (sp >= sq) { + snprintf(errbuf, errbufsz, "last backslash is invalid"); + xfree(dst); + return 0; + } + + /* for multi-line, we want to kill line-ending-backslash ... */ + if (multiline) { + + // if there is only whitespace after the backslash ... + if (sp[strspn(sp, " \t\r")] == '\n') { + /* skip all the following whitespaces */ + sp += strspn(sp, " \t\r\n"); + continue; + } + } + + /* get the escaped char */ + ch = *sp++; + switch (ch) { + case 'u': + case 'U': { + int64_t ucs = 0; + int nhex = (ch == 'u' ? 4 : 8); + for (int i = 0; i < nhex; i++) { + if (sp >= sq) { + snprintf(errbuf, errbufsz, "\\%c expects %d hex chars", ch, nhex); + xfree(dst); + return 0; + } + ch = *sp++; + int v = ('0' <= ch && ch <= '9') + ? ch - '0' + : (('A' <= ch && ch <= 'F') ? ch - 'A' + 10 : -1); + if (-1 == v) { + snprintf(errbuf, errbufsz, "invalid hex chars for \\u or \\U"); + xfree(dst); + return 0; + } + ucs = ucs * 16 + v; + } + int n = toml_ucs_to_utf8(ucs, &dst[off]); + if (-1 == n) { + snprintf(errbuf, errbufsz, "illegal ucs code in \\u or \\U"); + xfree(dst); + return 0; + } + off += n; + } + continue; + + case 'b': + ch = '\b'; + break; + case 't': + ch = '\t'; + break; + case 'n': + ch = '\n'; + break; + case 'f': + ch = '\f'; + break; + case 'r': + ch = '\r'; + break; + case '"': + ch = '"'; + break; + case '\\': + ch = '\\'; + break; + default: + snprintf(errbuf, errbufsz, "illegal escape char \\%c", ch); + xfree(dst); + return 0; + } + + dst[off++] = ch; + } + + // Cap with NUL and return it. + dst[off++] = 0; + return dst; +} + +/* Normalize a key. Convert all special chars to raw unescaped utf-8 chars. */ +static char *normalize_key(context_t *ctx, token_t strtok) { + const char *sp = strtok.ptr; + const char *sq = strtok.ptr + strtok.len; + int lineno = strtok.lineno; + char *ret; + int ch = *sp; + char ebuf[80]; + + /* handle quoted string */ + if (ch == '\'' || ch == '\"') { + /* if ''' or """, take 3 chars off front and back. Else, take 1 char off. */ + int multiline = 0; + if (sp[1] == ch && sp[2] == ch) { + sp += 3, sq -= 3; + multiline = 1; + } else + sp++, sq--; + + if (ch == '\'') { + /* for single quote, take it verbatim. */ + if (!(ret = STRNDUP(sp, sq - sp))) { + e_outofmemory(ctx, FLINE); + return 0; + } + } else { + /* for double quote, we need to normalize */ + ret = norm_basic_str(sp, sq - sp, multiline, ebuf, sizeof(ebuf)); + if (!ret) { + e_syntax(ctx, lineno, ebuf); + return 0; + } + } + + /* newlines are not allowed in keys */ + if (strchr(ret, '\n')) { + xfree(ret); + e_badkey(ctx, lineno); + return 0; + } + return ret; + } + + /* for bare-key allow only this regex: [A-Za-z0-9_-]+ */ + const char *xp; + for (xp = sp; xp != sq; xp++) { + int k = *xp; + if (isalnum(k)) + continue; + if (k == '_' || k == '-') + continue; + e_badkey(ctx, lineno); + return 0; + } + + /* dup and return it */ + if (!(ret = STRNDUP(sp, sq - sp))) { + e_outofmemory(ctx, FLINE); + return 0; + } + return ret; +} + +/* + * Look up key in tab. Return 0 if not found, or + * 'v'alue, 'a'rray or 't'able depending on the element. + */ +static int check_key(toml_table_t *tab, const char *key, + toml_keyval_t **ret_val, toml_array_t **ret_arr, + toml_table_t **ret_tab) { + int i; + void *dummy; + + if (!ret_tab) + ret_tab = (toml_table_t **)&dummy; + if (!ret_arr) + ret_arr = (toml_array_t **)&dummy; + if (!ret_val) + ret_val = (toml_keyval_t **)&dummy; + + *ret_tab = 0; + *ret_arr = 0; + *ret_val = 0; + + for (i = 0; i < tab->nkval; i++) { + if (0 == strcmp(key, tab->kval[i]->key)) { + *ret_val = tab->kval[i]; + return 'v'; + } + } + for (i = 0; i < tab->narr; i++) { + if (0 == strcmp(key, tab->arr[i]->key)) { + *ret_arr = tab->arr[i]; + return 'a'; + } + } + for (i = 0; i < tab->ntab; i++) { + if (0 == strcmp(key, tab->tab[i]->key)) { + *ret_tab = tab->tab[i]; + return 't'; + } + } + return 0; +} + +static int key_kind(toml_table_t *tab, const char *key) { + return check_key(tab, key, 0, 0, 0); +} + +/* Create a keyval in the table. + */ +static toml_keyval_t *create_keyval_in_table(context_t *ctx, toml_table_t *tab, + token_t keytok) { + /* first, normalize the key to be used for lookup. + * remember to free it if we error out. + */ + char *newkey = normalize_key(ctx, keytok); + if (!newkey) + return 0; + + /* if key exists: error out. */ + toml_keyval_t *dest = 0; + if (key_kind(tab, newkey)) { + xfree(newkey); + e_keyexists(ctx, keytok.lineno); + return 0; + } + + /* make a new entry */ + int n = tab->nkval; + toml_keyval_t **base; + if (0 == (base = (toml_keyval_t **)expand_ptrarr((void **)tab->kval, n))) { + xfree(newkey); + e_outofmemory(ctx, FLINE); + return 0; + } + tab->kval = base; + + if (0 == (base[n] = (toml_keyval_t *)CALLOC(1, sizeof(*base[n])))) { + xfree(newkey); + e_outofmemory(ctx, FLINE); + return 0; + } + dest = tab->kval[tab->nkval++]; + + /* save the key in the new value struct */ + dest->key = newkey; + return dest; +} + +/* Create a table in the table. + */ +static toml_table_t *create_keytable_in_table(context_t *ctx, toml_table_t *tab, + token_t keytok) { + /* first, normalize the key to be used for lookup. + * remember to free it if we error out. + */ + char *newkey = normalize_key(ctx, keytok); + if (!newkey) + return 0; + + /* if key exists: error out */ + toml_table_t *dest = 0; + if (check_key(tab, newkey, 0, 0, &dest)) { + xfree(newkey); /* don't need this anymore */ + + /* special case: if table exists, but was created implicitly ... */ + if (dest && dest->implicit) { + /* we make it explicit now, and simply return it. */ + dest->implicit = false; + return dest; + } + e_keyexists(ctx, keytok.lineno); + return 0; + } + + /* create a new table entry */ + int n = tab->ntab; + toml_table_t **base; + if (0 == (base = (toml_table_t **)expand_ptrarr((void **)tab->tab, n))) { + xfree(newkey); + e_outofmemory(ctx, FLINE); + return 0; + } + tab->tab = base; + + if (0 == (base[n] = (toml_table_t *)CALLOC(1, sizeof(*base[n])))) { + xfree(newkey); + e_outofmemory(ctx, FLINE); + return 0; + } + dest = tab->tab[tab->ntab++]; + + /* save the key in the new table struct */ + dest->key = newkey; + return dest; +} + +/* Create an array in the table. + */ +static toml_array_t *create_keyarray_in_table(context_t *ctx, toml_table_t *tab, + token_t keytok, char kind) { + /* first, normalize the key to be used for lookup. + * remember to free it if we error out. + */ + char *newkey = normalize_key(ctx, keytok); + if (!newkey) + return 0; + + /* if key exists: error out */ + if (key_kind(tab, newkey)) { + xfree(newkey); /* don't need this anymore */ + e_keyexists(ctx, keytok.lineno); + return 0; + } + + /* make a new array entry */ + int n = tab->narr; + toml_array_t **base; + if (0 == (base = (toml_array_t **)expand_ptrarr((void **)tab->arr, n))) { + xfree(newkey); + e_outofmemory(ctx, FLINE); + return 0; + } + tab->arr = base; + + if (0 == (base[n] = (toml_array_t *)CALLOC(1, sizeof(*base[n])))) { + xfree(newkey); + e_outofmemory(ctx, FLINE); + return 0; + } + toml_array_t *dest = tab->arr[tab->narr++]; + + /* save the key in the new array struct */ + dest->key = newkey; + dest->kind = kind; + return dest; +} + +static toml_arritem_t *create_value_in_array(context_t *ctx, + toml_array_t *parent) { + const int n = parent->nitem; + toml_arritem_t *base = expand_arritem(parent->item, n); + if (!base) { + e_outofmemory(ctx, FLINE); + return 0; + } + parent->item = base; + parent->nitem++; + return &parent->item[n]; +} + +/* Create an array in an array + */ +static toml_array_t *create_array_in_array(context_t *ctx, + toml_array_t *parent) { + const int n = parent->nitem; + toml_arritem_t *base = expand_arritem(parent->item, n); + if (!base) { + e_outofmemory(ctx, FLINE); + return 0; + } + toml_array_t *ret = (toml_array_t *)CALLOC(1, sizeof(toml_array_t)); + if (!ret) { + e_outofmemory(ctx, FLINE); + return 0; + } + base[n].arr = ret; + parent->item = base; + parent->nitem++; + return ret; +} + +/* Create a table in an array + */ +static toml_table_t *create_table_in_array(context_t *ctx, + toml_array_t *parent) { + int n = parent->nitem; + toml_arritem_t *base = expand_arritem(parent->item, n); + if (!base) { + e_outofmemory(ctx, FLINE); + return 0; + } + toml_table_t *ret = (toml_table_t *)CALLOC(1, sizeof(toml_table_t)); + if (!ret) { + e_outofmemory(ctx, FLINE); + return 0; + } + base[n].tab = ret; + parent->item = base; + parent->nitem++; + return ret; +} + +static int skip_newlines(context_t *ctx, int isdotspecial) { + while (ctx->tok.tok == NEWLINE) { + if (next_token(ctx, isdotspecial)) + return -1; + if (ctx->tok.eof) + break; + } + return 0; +} + +static int parse_keyval(context_t *ctx, toml_table_t *tab); + +static inline int eat_token(context_t *ctx, tokentype_t typ, int isdotspecial, + const char *fline) { + if (ctx->tok.tok != typ) + return e_internal(ctx, fline); + + if (next_token(ctx, isdotspecial)) + return -1; + + return 0; +} + +/* We are at '{ ... }'. + * Parse the table. + */ +static int parse_inline_table(context_t *ctx, toml_table_t *tab) { + if (eat_token(ctx, LBRACE, 1, FLINE)) + return -1; + + for (;;) { + if (ctx->tok.tok == NEWLINE) + return e_syntax(ctx, ctx->tok.lineno, + "newline not allowed in inline table"); + + /* until } */ + if (ctx->tok.tok == RBRACE) + break; + + if (ctx->tok.tok != STRING) + return e_syntax(ctx, ctx->tok.lineno, "expect a string"); + + if (parse_keyval(ctx, tab)) + return -1; + + if (ctx->tok.tok == NEWLINE) + return e_syntax(ctx, ctx->tok.lineno, + "newline not allowed in inline table"); + + /* on comma, continue to scan for next keyval */ + if (ctx->tok.tok == COMMA) { + if (eat_token(ctx, COMMA, 1, FLINE)) + return -1; + continue; + } + break; + } + + if (eat_token(ctx, RBRACE, 1, FLINE)) + return -1; + + tab->readonly = 1; + + return 0; +} + +static int valtype(const char *val) { + toml_timestamp_t ts; + if (*val == '\'' || *val == '"') + return 's'; + if (0 == toml_rtob(val, 0)) + return 'b'; + if (0 == toml_rtoi(val, 0)) + return 'i'; + if (0 == toml_rtod(val, 0)) + return 'd'; + if (0 == toml_rtots(val, &ts)) { + if (ts.year && ts.hour) + return 'T'; /* timestamp */ + if (ts.year) + return 'D'; /* date */ + return 't'; /* time */ + } + return 'u'; /* unknown */ +} + +/* We are at '[...]' */ +static int parse_array(context_t *ctx, toml_array_t *arr) { + if (eat_token(ctx, LBRACKET, 0, FLINE)) + return -1; + + for (;;) { + if (skip_newlines(ctx, 0)) + return -1; + + /* until ] */ + if (ctx->tok.tok == RBRACKET) + break; + + switch (ctx->tok.tok) { + case STRING: { + /* set array kind if this will be the first entry */ + if (arr->kind == 0) + arr->kind = 'v'; + else if (arr->kind != 'v') + arr->kind = 'm'; + + char *val = ctx->tok.ptr; + int vlen = ctx->tok.len; + + /* make a new value in array */ + toml_arritem_t *newval = create_value_in_array(ctx, arr); + if (!newval) + return e_outofmemory(ctx, FLINE); + + if (!(newval->val = STRNDUP(val, vlen))) + return e_outofmemory(ctx, FLINE); + + newval->valtype = valtype(newval->val); + + /* set array type if this is the first entry */ + if (arr->nitem == 1) + arr->type = newval->valtype; + else if (arr->type != newval->valtype) + arr->type = 'm'; /* mixed */ + + if (eat_token(ctx, STRING, 0, FLINE)) + return -1; + break; + } + + case LBRACKET: { /* [ [array], [array] ... ] */ + /* set the array kind if this will be the first entry */ + if (arr->kind == 0) + arr->kind = 'a'; + else if (arr->kind != 'a') + arr->kind = 'm'; + + toml_array_t *subarr = create_array_in_array(ctx, arr); + if (!subarr) + return -1; + if (parse_array(ctx, subarr)) + return -1; + break; + } + + case LBRACE: { /* [ {table}, {table} ... ] */ + /* set the array kind if this will be the first entry */ + if (arr->kind == 0) + arr->kind = 't'; + else if (arr->kind != 't') + arr->kind = 'm'; + + toml_table_t *subtab = create_table_in_array(ctx, arr); + if (!subtab) + return -1; + if (parse_inline_table(ctx, subtab)) + return -1; + break; + } + + default: + return e_syntax(ctx, ctx->tok.lineno, "syntax error"); + } + + if (skip_newlines(ctx, 0)) + return -1; + + /* on comma, continue to scan for next element */ + if (ctx->tok.tok == COMMA) { + if (eat_token(ctx, COMMA, 0, FLINE)) + return -1; + continue; + } + break; + } + + if (eat_token(ctx, RBRACKET, 1, FLINE)) + return -1; + return 0; +} + +/* handle lines like these: + key = "value" + key = [ array ] + key = { table } +*/ +static int parse_keyval(context_t *ctx, toml_table_t *tab) { + if (tab->readonly) { + return e_forbid(ctx, ctx->tok.lineno, + "cannot insert new entry into existing table"); + } + + token_t key = ctx->tok; + if (eat_token(ctx, STRING, 1, FLINE)) + return -1; + + if (ctx->tok.tok == DOT) { + /* handle inline dotted key. + e.g. + physical.color = "orange" + physical.shape = "round" + */ + toml_table_t *subtab = 0; + { + char *subtabstr = normalize_key(ctx, key); + if (!subtabstr) + return -1; + + subtab = toml_table_in(tab, subtabstr); + xfree(subtabstr); + } + if (!subtab) { + subtab = create_keytable_in_table(ctx, tab, key); + if (!subtab) + return -1; + } + if (next_token(ctx, 1)) + return -1; + if (parse_keyval(ctx, subtab)) + return -1; + return 0; + } + + if (ctx->tok.tok != EQUAL) { + return e_syntax(ctx, ctx->tok.lineno, "missing ="); + } + + if (next_token(ctx, 0)) + return -1; + + switch (ctx->tok.tok) { + case STRING: { /* key = "value" */ + toml_keyval_t *keyval = create_keyval_in_table(ctx, tab, key); + if (!keyval) + return -1; + token_t val = ctx->tok; + + assert(keyval->val == 0); + if (!(keyval->val = STRNDUP(val.ptr, val.len))) + return e_outofmemory(ctx, FLINE); + + if (next_token(ctx, 1)) + return -1; + + return 0; + } + + case LBRACKET: { /* key = [ array ] */ + toml_array_t *arr = create_keyarray_in_table(ctx, tab, key, 0); + if (!arr) + return -1; + if (parse_array(ctx, arr)) + return -1; + return 0; + } + + case LBRACE: { /* key = { table } */ + toml_table_t *nxttab = create_keytable_in_table(ctx, tab, key); + if (!nxttab) + return -1; + if (parse_inline_table(ctx, nxttab)) + return -1; + return 0; + } + + default: + return e_syntax(ctx, ctx->tok.lineno, "syntax error"); + } + return 0; +} + +typedef struct tabpath_t tabpath_t; +struct tabpath_t { + int cnt; + token_t key[10]; +}; + +/* at [x.y.z] or [[x.y.z]] + * Scan forward and fill tabpath until it enters ] or ]] + * There will be at least one entry on return. + */ +static int fill_tabpath(context_t *ctx) { + int lineno = ctx->tok.lineno; + int i; + + /* clear tpath */ + for (i = 0; i < ctx->tpath.top; i++) { + char **p = &ctx->tpath.key[i]; + xfree(*p); + *p = 0; + } + ctx->tpath.top = 0; + + for (;;) { + if (ctx->tpath.top >= 10) + return e_syntax(ctx, lineno, + "table path is too deep; max allowed is 10."); + + if (ctx->tok.tok != STRING) + return e_syntax(ctx, lineno, "invalid or missing key"); + + char *key = normalize_key(ctx, ctx->tok); + if (!key) + return -1; + ctx->tpath.tok[ctx->tpath.top] = ctx->tok; + ctx->tpath.key[ctx->tpath.top] = key; + ctx->tpath.top++; + + if (next_token(ctx, 1)) + return -1; + + if (ctx->tok.tok == RBRACKET) + break; + + if (ctx->tok.tok != DOT) + return e_syntax(ctx, lineno, "invalid key"); + + if (next_token(ctx, 1)) + return -1; + } + + if (ctx->tpath.top <= 0) + return e_syntax(ctx, lineno, "empty table selector"); + + return 0; +} + +/* Walk tabpath from the root, and create new tables on the way. + * Sets ctx->curtab to the final table. + */ +static int walk_tabpath(context_t *ctx) { + /* start from root */ + toml_table_t *curtab = ctx->root; + + for (int i = 0; i < ctx->tpath.top; i++) { + const char *key = ctx->tpath.key[i]; + + toml_keyval_t *nextval = 0; + toml_array_t *nextarr = 0; + toml_table_t *nexttab = 0; + switch (check_key(curtab, key, &nextval, &nextarr, &nexttab)) { + case 't': + /* found a table. nexttab is where we will go next. */ + break; + + case 'a': + /* found an array. nexttab is the last table in the array. */ + if (nextarr->kind != 't') + return e_internal(ctx, FLINE); + + if (nextarr->nitem == 0) + return e_internal(ctx, FLINE); + + nexttab = nextarr->item[nextarr->nitem - 1].tab; + break; + + case 'v': + return e_keyexists(ctx, ctx->tpath.tok[i].lineno); + + default: { /* Not found. Let's create an implicit table. */ + int n = curtab->ntab; + toml_table_t **base = + (toml_table_t **)expand_ptrarr((void **)curtab->tab, n); + if (0 == base) + return e_outofmemory(ctx, FLINE); + + curtab->tab = base; + + if (0 == (base[n] = (toml_table_t *)CALLOC(1, sizeof(*base[n])))) + return e_outofmemory(ctx, FLINE); + + if (0 == (base[n]->key = STRDUP(key))) + return e_outofmemory(ctx, FLINE); + + nexttab = curtab->tab[curtab->ntab++]; + + /* tabs created by walk_tabpath are considered implicit */ + nexttab->implicit = true; + } break; + } + + /* switch to next tab */ + curtab = nexttab; + } + + /* save it */ + ctx->curtab = curtab; + + return 0; +} + +/* handle lines like [x.y.z] or [[x.y.z]] */ +static int parse_select(context_t *ctx) { + assert(ctx->tok.tok == LBRACKET); + + /* true if [[ */ + int llb = (ctx->tok.ptr + 1 < ctx->stop && ctx->tok.ptr[1] == '['); + /* need to detect '[[' on our own because next_token() will skip whitespace, + and '[ [' would be taken as '[[', which is wrong. */ + + /* eat [ or [[ */ + if (eat_token(ctx, LBRACKET, 1, FLINE)) + return -1; + if (llb) { + assert(ctx->tok.tok == LBRACKET); + if (eat_token(ctx, LBRACKET, 1, FLINE)) + return -1; + } + + if (fill_tabpath(ctx)) + return -1; + + /* For [x.y.z] or [[x.y.z]], remove z from tpath. + */ + token_t z = ctx->tpath.tok[ctx->tpath.top - 1]; + xfree(ctx->tpath.key[ctx->tpath.top - 1]); + ctx->tpath.top--; + + /* set up ctx->curtab */ + if (walk_tabpath(ctx)) + return -1; + + if (!llb) { + /* [x.y.z] -> create z = {} in x.y */ + toml_table_t *curtab = create_keytable_in_table(ctx, ctx->curtab, z); + if (!curtab) + return -1; + ctx->curtab = curtab; + } else { + /* [[x.y.z]] -> create z = [] in x.y */ + toml_array_t *arr = 0; + { + char *zstr = normalize_key(ctx, z); + if (!zstr) + return -1; + arr = toml_array_in(ctx->curtab, zstr); + xfree(zstr); + } + if (!arr) { + arr = create_keyarray_in_table(ctx, ctx->curtab, z, 't'); + if (!arr) + return -1; + } + if (arr->kind != 't') + return e_syntax(ctx, z.lineno, "array mismatch"); + + /* add to z[] */ + toml_table_t *dest; + { + toml_table_t *t = create_table_in_array(ctx, arr); + if (!t) + return -1; + + if (0 == (t->key = STRDUP("__anon__"))) + return e_outofmemory(ctx, FLINE); + + dest = t; + } + + ctx->curtab = dest; + } + + if (ctx->tok.tok != RBRACKET) { + return e_syntax(ctx, ctx->tok.lineno, "expects ]"); + } + if (llb) { + if (!(ctx->tok.ptr + 1 < ctx->stop && ctx->tok.ptr[1] == ']')) { + return e_syntax(ctx, ctx->tok.lineno, "expects ]]"); + } + if (eat_token(ctx, RBRACKET, 1, FLINE)) + return -1; + } + + if (eat_token(ctx, RBRACKET, 1, FLINE)) + return -1; + + if (ctx->tok.tok != NEWLINE) + return e_syntax(ctx, ctx->tok.lineno, "extra chars after ] or ]]"); + + return 0; +} + +toml_table_t *toml_parse(char *conf, char *errbuf, int errbufsz) { + context_t ctx; + + // clear errbuf + if (errbufsz <= 0) + errbufsz = 0; + if (errbufsz > 0) + errbuf[0] = 0; + + // init context + memset(&ctx, 0, sizeof(ctx)); + ctx.start = conf; + ctx.stop = ctx.start + strlen(conf); + ctx.errbuf = errbuf; + ctx.errbufsz = errbufsz; + + // start with an artificial newline of length 0 + ctx.tok.tok = NEWLINE; + ctx.tok.lineno = 1; + ctx.tok.ptr = conf; + ctx.tok.len = 0; + + // make a root table + if (0 == (ctx.root = CALLOC(1, sizeof(*ctx.root)))) { + e_outofmemory(&ctx, FLINE); + // Do not goto fail, root table not set up yet + return 0; + } + + // set root as default table + ctx.curtab = ctx.root; + + /* Scan forward until EOF */ + for (token_t tok = ctx.tok; !tok.eof; tok = ctx.tok) { + switch (tok.tok) { + + case NEWLINE: + if (next_token(&ctx, 1)) + goto fail; + break; + + case STRING: + if (parse_keyval(&ctx, ctx.curtab)) + goto fail; + + if (ctx.tok.tok != NEWLINE) { + e_syntax(&ctx, ctx.tok.lineno, "extra chars after value"); + goto fail; + } + + if (eat_token(&ctx, NEWLINE, 1, FLINE)) + goto fail; + break; + + case LBRACKET: /* [ x.y.z ] or [[ x.y.z ]] */ + if (parse_select(&ctx)) + goto fail; + break; + + default: + e_syntax(&ctx, tok.lineno, "syntax error"); + goto fail; + } + } + + /* success */ + for (int i = 0; i < ctx.tpath.top; i++) + xfree(ctx.tpath.key[i]); + return ctx.root; + +fail: + // Something bad has happened. Free resources and return error. + for (int i = 0; i < ctx.tpath.top; i++) + xfree(ctx.tpath.key[i]); + toml_free(ctx.root); + return 0; +} + +toml_table_t *toml_parse_file(FILE *fp, char *errbuf, int errbufsz) { + int bufsz = 0; + char *buf = 0; + int off = 0; + + /* read from fp into buf */ + while (!feof(fp)) { + + if (off == bufsz) { + int xsz = bufsz + 1000; + char *x = expand(buf, bufsz, xsz); + if (!x) { + snprintf(errbuf, errbufsz, "out of memory"); + xfree(buf); + return 0; + } + buf = x; + bufsz = xsz; + } + + errno = 0; + int n = fread(buf + off, 1, bufsz - off, fp); + if (ferror(fp)) { + snprintf(errbuf, errbufsz, "%s", + errno ? strerror(errno) : "Error reading file"); + xfree(buf); + return 0; + } + off += n; + } + + /* tag on a NUL to cap the string */ + if (off == bufsz) { + int xsz = bufsz + 1; + char *x = expand(buf, bufsz, xsz); + if (!x) { + snprintf(errbuf, errbufsz, "out of memory"); + xfree(buf); + return 0; + } + buf = x; + bufsz = xsz; + } + buf[off] = 0; + + /* parse it, cleanup and finish */ + toml_table_t *ret = toml_parse(buf, errbuf, errbufsz); + xfree(buf); + return ret; +} + +static void xfree_kval(toml_keyval_t *p) { + if (!p) + return; + xfree(p->key); + xfree(p->val); + xfree(p); +} + +static void xfree_tab(toml_table_t *p); + +static void xfree_arr(toml_array_t *p) { + if (!p) + return; + + xfree(p->key); + const int n = p->nitem; + for (int i = 0; i < n; i++) { + toml_arritem_t *a = &p->item[i]; + if (a->val) + xfree(a->val); + else if (a->arr) + xfree_arr(a->arr); + else if (a->tab) + xfree_tab(a->tab); + } + xfree(p->item); + xfree(p); +} + +static void xfree_tab(toml_table_t *p) { + int i; + + if (!p) + return; + + xfree(p->key); + + for (i = 0; i < p->nkval; i++) + xfree_kval(p->kval[i]); + xfree(p->kval); + + for (i = 0; i < p->narr; i++) + xfree_arr(p->arr[i]); + xfree(p->arr); + + for (i = 0; i < p->ntab; i++) + xfree_tab(p->tab[i]); + xfree(p->tab); + + xfree(p); +} + +void toml_free(toml_table_t *tab) { xfree_tab(tab); } + +static void set_token(context_t *ctx, tokentype_t tok, int lineno, char *ptr, + int len) { + token_t t; + t.tok = tok; + t.lineno = lineno; + t.ptr = ptr; + t.len = len; + t.eof = 0; + ctx->tok = t; +} + +static void set_eof(context_t *ctx, int lineno) { + set_token(ctx, NEWLINE, lineno, ctx->stop, 0); + ctx->tok.eof = 1; +} + +/* Scan p for n digits compositing entirely of [0-9] */ +static int scan_digits(const char *p, int n) { + int ret = 0; + for (; n > 0 && isdigit(*p); n--, p++) { + ret = 10 * ret + (*p - '0'); + } + return n ? -1 : ret; +} + +static int scan_date(const char *p, int *YY, int *MM, int *DD) { + int year, month, day; + year = scan_digits(p, 4); + month = (year >= 0 && p[4] == '-') ? scan_digits(p + 5, 2) : -1; + day = (month >= 0 && p[7] == '-') ? scan_digits(p + 8, 2) : -1; + if (YY) + *YY = year; + if (MM) + *MM = month; + if (DD) + *DD = day; + return (year >= 0 && month >= 0 && day >= 0) ? 0 : -1; +} + +static int scan_time(const char *p, int *hh, int *mm, int *ss) { + int hour, minute, second; + hour = scan_digits(p, 2); + minute = (hour >= 0 && p[2] == ':') ? scan_digits(p + 3, 2) : -1; + second = (minute >= 0 && p[5] == ':') ? scan_digits(p + 6, 2) : -1; + if (hh) + *hh = hour; + if (mm) + *mm = minute; + if (ss) + *ss = second; + return (hour >= 0 && minute >= 0 && second >= 0) ? 0 : -1; +} + +static int scan_string(context_t *ctx, char *p, int lineno, int dotisspecial) { + char *orig = p; + if (0 == strncmp(p, "'''", 3)) { + char *q = p + 3; + + while (1) { + q = strstr(q, "'''"); + if (0 == q) { + return e_syntax(ctx, lineno, "unterminated triple-s-quote"); + } + while (q[3] == '\'') + q++; + break; + } + + set_token(ctx, STRING, lineno, orig, q + 3 - orig); + return 0; + } + + if (0 == strncmp(p, "\"\"\"", 3)) { + char *q = p + 3; + + while (1) { + q = strstr(q, "\"\"\""); + if (0 == q) { + return e_syntax(ctx, lineno, "unterminated triple-d-quote"); + } + if (q[-1] == '\\') { + q++; + continue; + } + while (q[3] == '\"') + q++; + break; + } + + // the string is [p+3, q-1] + + int hexreq = 0; /* #hex required */ + int escape = 0; + for (p += 3; p < q; p++) { + if (escape) { + escape = 0; + if (strchr("btnfr\"\\", *p)) + continue; + if (*p == 'u') { + hexreq = 4; + continue; + } + if (*p == 'U') { + hexreq = 8; + continue; + } + if (p[strspn(p, " \t\r")] == '\n') + continue; /* allow for line ending backslash */ + return e_syntax(ctx, lineno, "bad escape char"); + } + if (hexreq) { + hexreq--; + if (strchr("0123456789ABCDEF", *p)) + continue; + return e_syntax(ctx, lineno, "expect hex char"); + } + if (*p == '\\') { + escape = 1; + continue; + } + } + if (escape) + return e_syntax(ctx, lineno, "expect an escape char"); + if (hexreq) + return e_syntax(ctx, lineno, "expected more hex char"); + + set_token(ctx, STRING, lineno, orig, q + 3 - orig); + return 0; + } + + if ('\'' == *p) { + for (p++; *p && *p != '\n' && *p != '\''; p++) + ; + if (*p != '\'') { + return e_syntax(ctx, lineno, "unterminated s-quote"); + } + + set_token(ctx, STRING, lineno, orig, p + 1 - orig); + return 0; + } + + if ('\"' == *p) { + int hexreq = 0; /* #hex required */ + int escape = 0; + for (p++; *p; p++) { + if (escape) { + escape = 0; + if (strchr("btnfr\"\\", *p)) + continue; + if (*p == 'u') { + hexreq = 4; + continue; + } + if (*p == 'U') { + hexreq = 8; + continue; + } + return e_syntax(ctx, lineno, "bad escape char"); + } + if (hexreq) { + hexreq--; + if (strchr("0123456789ABCDEF", *p)) + continue; + return e_syntax(ctx, lineno, "expect hex char"); + } + if (*p == '\\') { + escape = 1; + continue; + } + if (*p == '\'') { + if (p[1] == '\'' && p[2] == '\'') { + return e_syntax(ctx, lineno, "triple-s-quote inside string lit"); + } + continue; + } + if (*p == '\n') + break; + if (*p == '"') + break; + } + if (*p != '"') { + return e_syntax(ctx, lineno, "unterminated quote"); + } + + set_token(ctx, STRING, lineno, orig, p + 1 - orig); + return 0; + } + + /* check for timestamp without quotes */ + if (0 == scan_date(p, 0, 0, 0) || 0 == scan_time(p, 0, 0, 0)) { + // forward thru the timestamp + p += strspn(p, "0123456789.:+-Tt Zz"); + // squeeze out any spaces at end of string + for (; p[-1] == ' '; p--) + ; + // tokenize + set_token(ctx, STRING, lineno, orig, p - orig); + return 0; + } + + /* literals */ + for (; *p && *p != '\n'; p++) { + int ch = *p; + if (ch == '.' && dotisspecial) + break; + if ('A' <= ch && ch <= 'Z') + continue; + if ('a' <= ch && ch <= 'z') + continue; + if (strchr("0123456789+-_.", ch)) + continue; + break; + } + + set_token(ctx, STRING, lineno, orig, p - orig); + return 0; +} + +static int next_token(context_t *ctx, int dotisspecial) { + int lineno = ctx->tok.lineno; + char *p = ctx->tok.ptr; + int i; + + /* eat this tok */ + for (i = 0; i < ctx->tok.len; i++) { + if (*p++ == '\n') + lineno++; + } + + /* make next tok */ + while (p < ctx->stop) { + /* skip comment. stop just before the \n. */ + if (*p == '#') { + for (p++; p < ctx->stop && *p != '\n'; p++) + ; + continue; + } + + if (dotisspecial && *p == '.') { + set_token(ctx, DOT, lineno, p, 1); + return 0; + } + + switch (*p) { + case ',': + set_token(ctx, COMMA, lineno, p, 1); + return 0; + case '=': + set_token(ctx, EQUAL, lineno, p, 1); + return 0; + case '{': + set_token(ctx, LBRACE, lineno, p, 1); + return 0; + case '}': + set_token(ctx, RBRACE, lineno, p, 1); + return 0; + case '[': + set_token(ctx, LBRACKET, lineno, p, 1); + return 0; + case ']': + set_token(ctx, RBRACKET, lineno, p, 1); + return 0; + case '\n': + set_token(ctx, NEWLINE, lineno, p, 1); + return 0; + case '\r': + case ' ': + case '\t': + /* ignore white spaces */ + p++; + continue; + } + + return scan_string(ctx, p, lineno, dotisspecial); + } + + set_eof(ctx, lineno); + return 0; +} + +const char *toml_key_in(const toml_table_t *tab, int keyidx) { + if (keyidx < tab->nkval) + return tab->kval[keyidx]->key; + + keyidx -= tab->nkval; + if (keyidx < tab->narr) + return tab->arr[keyidx]->key; + + keyidx -= tab->narr; + if (keyidx < tab->ntab) + return tab->tab[keyidx]->key; + + return 0; +} + +int toml_key_exists(const toml_table_t *tab, const char *key) { + int i; + for (i = 0; i < tab->nkval; i++) { + if (0 == strcmp(key, tab->kval[i]->key)) + return 1; + } + for (i = 0; i < tab->narr; i++) { + if (0 == strcmp(key, tab->arr[i]->key)) + return 1; + } + for (i = 0; i < tab->ntab; i++) { + if (0 == strcmp(key, tab->tab[i]->key)) + return 1; + } + return 0; +} + +toml_raw_t toml_raw_in(const toml_table_t *tab, const char *key) { + int i; + for (i = 0; i < tab->nkval; i++) { + if (0 == strcmp(key, tab->kval[i]->key)) + return tab->kval[i]->val; + } + return 0; +} + +toml_array_t *toml_array_in(const toml_table_t *tab, const char *key) { + int i; + for (i = 0; i < tab->narr; i++) { + if (0 == strcmp(key, tab->arr[i]->key)) + return tab->arr[i]; + } + return 0; +} + +toml_table_t *toml_table_in(const toml_table_t *tab, const char *key) { + int i; + for (i = 0; i < tab->ntab; i++) { + if (0 == strcmp(key, tab->tab[i]->key)) + return tab->tab[i]; + } + return 0; +} + +toml_raw_t toml_raw_at(const toml_array_t *arr, int idx) { + return (0 <= idx && idx < arr->nitem) ? arr->item[idx].val : 0; +} + +char toml_array_kind(const toml_array_t *arr) { return arr->kind; } + +char toml_array_type(const toml_array_t *arr) { + if (arr->kind != 'v') + return 0; + + if (arr->nitem == 0) + return 0; + + return arr->type; +} + +int toml_array_nelem(const toml_array_t *arr) { return arr->nitem; } + +const char *toml_array_key(const toml_array_t *arr) { + return arr ? arr->key : (const char *)NULL; +} + +int toml_table_nkval(const toml_table_t *tab) { return tab->nkval; } + +int toml_table_narr(const toml_table_t *tab) { return tab->narr; } + +int toml_table_ntab(const toml_table_t *tab) { return tab->ntab; } + +const char *toml_table_key(const toml_table_t *tab) { + return tab ? tab->key : (const char *)NULL; +} + +toml_array_t *toml_array_at(const toml_array_t *arr, int idx) { + return (0 <= idx && idx < arr->nitem) ? arr->item[idx].arr : 0; +} + +toml_table_t *toml_table_at(const toml_array_t *arr, int idx) { + return (0 <= idx && idx < arr->nitem) ? arr->item[idx].tab : 0; +} + +static int parse_millisec(const char *p, const char **endp); + +int toml_rtots(toml_raw_t src_, toml_timestamp_t *ret) { + if (!src_) + return -1; + + const char *p = src_; + int must_parse_time = 0; + + memset(ret, 0, sizeof(*ret)); + + int *year = &ret->__buffer.year; + int *month = &ret->__buffer.month; + int *day = &ret->__buffer.day; + int *hour = &ret->__buffer.hour; + int *minute = &ret->__buffer.minute; + int *second = &ret->__buffer.second; + int *millisec = &ret->__buffer.millisec; + + /* parse date YYYY-MM-DD */ + if (0 == scan_date(p, year, month, day)) { + ret->year = year; + ret->month = month; + ret->day = day; + + p += 10; + if (*p) { + // parse the T or space separator + if (*p != 'T' && *p != 't' && *p != ' ') + return -1; + must_parse_time = 1; + p++; + } + } + + /* parse time HH:MM:SS */ + if (0 == scan_time(p, hour, minute, second)) { + ret->hour = hour; + ret->minute = minute; + ret->second = second; + + /* optionally, parse millisec */ + p += 8; + if (*p == '.') { + p++; /* skip '.' */ + const char *qq; + *millisec = parse_millisec(p, &qq); + ret->millisec = millisec; + p = qq; + } + + if (*p) { + /* parse and copy Z */ + char *z = ret->__buffer.z; + ret->z = z; + if (*p == 'Z' || *p == 'z') { + *z++ = 'Z'; + p++; + *z = 0; + + } else if (*p == '+' || *p == '-') { + *z++ = *p++; + + if (!(isdigit(p[0]) && isdigit(p[1]))) + return -1; + *z++ = *p++; + *z++ = *p++; + + if (*p == ':') { + *z++ = *p++; + + if (!(isdigit(p[0]) && isdigit(p[1]))) + return -1; + *z++ = *p++; + *z++ = *p++; + } + + *z = 0; + } + } + } + if (*p != 0) + return -1; + + if (must_parse_time && !ret->hour) + return -1; + + return 0; +} + +/* Raw to boolean */ +int toml_rtob(toml_raw_t src, int *ret_) { + if (!src) + return -1; + int dummy; + int *ret = ret_ ? ret_ : &dummy; + + if (0 == strcmp(src, "true")) { + *ret = 1; + return 0; + } + if (0 == strcmp(src, "false")) { + *ret = 0; + return 0; + } + return -1; +} + +/* Raw to integer */ +int toml_rtoi(toml_raw_t src, int64_t *ret_) { + if (!src) + return -1; + + char buf[100]; + char *p = buf; + char *q = p + sizeof(buf); + const char *s = src; + int base = 0; + int64_t dummy; + int64_t *ret = ret_ ? ret_ : &dummy; + + /* allow +/- */ + if (s[0] == '+' || s[0] == '-') + *p++ = *s++; + + /* disallow +_100 */ + if (s[0] == '_') + return -1; + + /* if 0* ... */ + if ('0' == s[0]) { + switch (s[1]) { + case 'x': + base = 16; + s += 2; + break; + case 'o': + base = 8; + s += 2; + break; + case 'b': + base = 2; + s += 2; + break; + case '\0': + return *ret = 0, 0; + default: + /* ensure no other digits after it */ + if (s[1]) + return -1; + } + } + + /* just strip underscores and pass to strtoll */ + while (*s && p < q) { + int ch = *s++; + if (ch == '_') { + // disallow '__' + if (s[0] == '_') + return -1; + // numbers cannot end with '_' + if (s[0] == '\0') + return -1; + continue; /* skip _ */ + } + *p++ = ch; + } + + // if not at end-of-string or we ran out of buffer ... + if (*s || p == q) + return -1; + + /* cap with NUL */ + *p = 0; + + /* Run strtoll on buf to get the integer */ + char *endp; + errno = 0; + *ret = strtoll(buf, &endp, base); + return (errno || *endp) ? -1 : 0; +} + +int toml_rtod_ex(toml_raw_t src, double *ret_, char *buf, int buflen) { + if (!src) + return -1; + + char *p = buf; + char *q = p + buflen; + const char *s = src; + double dummy; + double *ret = ret_ ? ret_ : &dummy; + + /* allow +/- */ + if (s[0] == '+' || s[0] == '-') + *p++ = *s++; + + /* disallow +_1.00 */ + if (s[0] == '_') + return -1; + + /* decimal point, if used, must be surrounded by at least one digit on each + * side */ + { + char *dot = strchr(s, '.'); + if (dot) { + if (dot == s || !isdigit(dot[-1]) || !isdigit(dot[1])) + return -1; + } + } + + /* zero must be followed by . or 'e', or NUL */ + if (s[0] == '0' && s[1] && !strchr("eE.", s[1])) + return -1; + + /* just strip underscores and pass to strtod */ + while (*s && p < q) { + int ch = *s++; + if (ch == '_') { + // disallow '__' + if (s[0] == '_') + return -1; + // disallow last char '_' + if (s[0] == 0) + return -1; + continue; /* skip _ */ + } + *p++ = ch; + } + if (*s || p == q) + return -1; /* reached end of string or buffer is full? */ + + /* cap with NUL */ + *p = 0; + + /* Run strtod on buf to get the value */ + char *endp; + errno = 0; + *ret = strtod(buf, &endp); + return (errno || *endp) ? -1 : 0; +} + +int toml_rtod(toml_raw_t src, double *ret_) { + char buf[100]; + return toml_rtod_ex(src, ret_, buf, sizeof(buf)); +} + +int toml_rtos(toml_raw_t src, char **ret) { + int multiline = 0; + const char *sp; + const char *sq; + + *ret = 0; + if (!src) + return -1; + + // for strings, first char must be a s-quote or d-quote + int qchar = src[0]; + int srclen = strlen(src); + if (!(qchar == '\'' || qchar == '"')) { + return -1; + } + + // triple quotes? + if (qchar == src[1] && qchar == src[2]) { + multiline = 1; // triple-quote implies multiline + sp = src + 3; // first char after quote + sq = src + srclen - 3; // first char of ending quote + + if (!(sp <= sq && sq[0] == qchar && sq[1] == qchar && sq[2] == qchar)) { + // last 3 chars in src must be qchar + return -1; + } + + /* skip new line immediate after qchar */ + if (sp[0] == '\n') + sp++; + else if (sp[0] == '\r' && sp[1] == '\n') + sp += 2; + + } else { + sp = src + 1; // first char after quote + sq = src + srclen - 1; // ending quote + if (!(sp <= sq && *sq == qchar)) { + /* last char in src must be qchar */ + return -1; + } + } + + // at this point: + // sp points to first valid char after quote. + // sq points to one char beyond last valid char. + // string len is (sq - sp). + if (qchar == '\'') { + *ret = norm_lit_str(sp, sq - sp, multiline, 0, 0); + } else { + *ret = norm_basic_str(sp, sq - sp, multiline, 0, 0); + } + + return *ret ? 0 : -1; +} + +toml_datum_t toml_string_at(const toml_array_t *arr, int idx) { + toml_datum_t ret; + memset(&ret, 0, sizeof(ret)); + ret.ok = (0 == toml_rtos(toml_raw_at(arr, idx), &ret.u.s)); + return ret; +} + +toml_datum_t toml_bool_at(const toml_array_t *arr, int idx) { + toml_datum_t ret; + memset(&ret, 0, sizeof(ret)); + ret.ok = (0 == toml_rtob(toml_raw_at(arr, idx), &ret.u.b)); + return ret; +} + +toml_datum_t toml_int_at(const toml_array_t *arr, int idx) { + toml_datum_t ret; + memset(&ret, 0, sizeof(ret)); + ret.ok = (0 == toml_rtoi(toml_raw_at(arr, idx), &ret.u.i)); + return ret; +} + +toml_datum_t toml_double_at(const toml_array_t *arr, int idx) { + toml_datum_t ret; + memset(&ret, 0, sizeof(ret)); + ret.ok = (0 == toml_rtod(toml_raw_at(arr, idx), &ret.u.d)); + return ret; +} + +toml_datum_t toml_timestamp_at(const toml_array_t *arr, int idx) { + toml_timestamp_t ts; + toml_datum_t ret; + memset(&ret, 0, sizeof(ret)); + ret.ok = (0 == toml_rtots(toml_raw_at(arr, idx), &ts)); + if (ret.ok) { + ret.ok = !!(ret.u.ts = MALLOC(sizeof(*ret.u.ts))); + if (ret.ok) { + *ret.u.ts = ts; + if (ret.u.ts->year) + ret.u.ts->year = &ret.u.ts->__buffer.year; + if (ret.u.ts->month) + ret.u.ts->month = &ret.u.ts->__buffer.month; + if (ret.u.ts->day) + ret.u.ts->day = &ret.u.ts->__buffer.day; + if (ret.u.ts->hour) + ret.u.ts->hour = &ret.u.ts->__buffer.hour; + if (ret.u.ts->minute) + ret.u.ts->minute = &ret.u.ts->__buffer.minute; + if (ret.u.ts->second) + ret.u.ts->second = &ret.u.ts->__buffer.second; + if (ret.u.ts->millisec) + ret.u.ts->millisec = &ret.u.ts->__buffer.millisec; + if (ret.u.ts->z) + ret.u.ts->z = ret.u.ts->__buffer.z; + } + } + return ret; +} + +toml_datum_t toml_string_in(const toml_table_t *arr, const char *key) { + toml_datum_t ret; + memset(&ret, 0, sizeof(ret)); + toml_raw_t raw = toml_raw_in(arr, key); + if (raw) { + ret.ok = (0 == toml_rtos(raw, &ret.u.s)); + } + return ret; +} + +toml_datum_t toml_bool_in(const toml_table_t *arr, const char *key) { + toml_datum_t ret; + memset(&ret, 0, sizeof(ret)); + ret.ok = (0 == toml_rtob(toml_raw_in(arr, key), &ret.u.b)); + return ret; +} + +toml_datum_t toml_int_in(const toml_table_t *arr, const char *key) { + toml_datum_t ret; + memset(&ret, 0, sizeof(ret)); + ret.ok = (0 == toml_rtoi(toml_raw_in(arr, key), &ret.u.i)); + return ret; +} + +toml_datum_t toml_double_in(const toml_table_t *arr, const char *key) { + toml_datum_t ret; + memset(&ret, 0, sizeof(ret)); + ret.ok = (0 == toml_rtod(toml_raw_in(arr, key), &ret.u.d)); + return ret; +} + +toml_datum_t toml_timestamp_in(const toml_table_t *arr, const char *key) { + toml_timestamp_t ts; + toml_datum_t ret; + memset(&ret, 0, sizeof(ret)); + ret.ok = (0 == toml_rtots(toml_raw_in(arr, key), &ts)); + if (ret.ok) { + ret.ok = !!(ret.u.ts = MALLOC(sizeof(*ret.u.ts))); + if (ret.ok) { + *ret.u.ts = ts; + if (ret.u.ts->year) + ret.u.ts->year = &ret.u.ts->__buffer.year; + if (ret.u.ts->month) + ret.u.ts->month = &ret.u.ts->__buffer.month; + if (ret.u.ts->day) + ret.u.ts->day = &ret.u.ts->__buffer.day; + if (ret.u.ts->hour) + ret.u.ts->hour = &ret.u.ts->__buffer.hour; + if (ret.u.ts->minute) + ret.u.ts->minute = &ret.u.ts->__buffer.minute; + if (ret.u.ts->second) + ret.u.ts->second = &ret.u.ts->__buffer.second; + if (ret.u.ts->millisec) + ret.u.ts->millisec = &ret.u.ts->__buffer.millisec; + if (ret.u.ts->z) + ret.u.ts->z = ret.u.ts->__buffer.z; + } + } + return ret; +} + +static int parse_millisec(const char *p, const char **endp) { + int ret = 0; + int unit = 100; /* unit in millisec */ + for (; '0' <= *p && *p <= '9'; p++, unit /= 10) { + ret += (*p - '0') * unit; + } + *endp = p; + return ret; +} diff --git a/third_party/tomlc99/toml.h b/third_party/tomlc99/toml.h new file mode 100644 index 0000000..c6aabd0 --- /dev/null +++ b/third_party/tomlc99/toml.h @@ -0,0 +1,175 @@ +/* + MIT License + + Copyright (c) CK Tan + https://github.com/cktan/tomlc99 + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#ifndef TOML_H +#define TOML_H + +#ifdef _MSC_VER +#pragma warning(disable : 4996) +#endif + +#include +#include + +#ifdef __cplusplus +#define TOML_EXTERN extern "C" +#else +#define TOML_EXTERN extern +#endif + +typedef struct toml_timestamp_t toml_timestamp_t; +typedef struct toml_table_t toml_table_t; +typedef struct toml_array_t toml_array_t; +typedef struct toml_datum_t toml_datum_t; + +/* Parse a file. Return a table on success, or 0 otherwise. + * Caller must toml_free(the-return-value) after use. + */ +TOML_EXTERN toml_table_t *toml_parse_file(FILE *fp, char *errbuf, int errbufsz); + +/* Parse a string containing the full config. + * Return a table on success, or 0 otherwise. + * Caller must toml_free(the-return-value) after use. + */ +TOML_EXTERN toml_table_t *toml_parse(char *conf, /* NUL terminated, please. */ + char *errbuf, int errbufsz); + +/* Free the table returned by toml_parse() or toml_parse_file(). Once + * this function is called, any handles accessed through this tab + * directly or indirectly are no longer valid. + */ +TOML_EXTERN void toml_free(toml_table_t *tab); + +/* Timestamp types. The year, month, day, hour, minute, second, z + * fields may be NULL if they are not relevant. e.g. In a DATE + * type, the hour, minute, second and z fields will be NULLs. + */ +struct toml_timestamp_t { + struct { /* internal. do not use. */ + int year, month, day; + int hour, minute, second, millisec; + char z[10]; + } __buffer; + int *year, *month, *day; + int *hour, *minute, *second, *millisec; + char *z; +}; + +/*----------------------------------------------------------------- + * Enhanced access methods + */ +struct toml_datum_t { + int ok; + union { + toml_timestamp_t *ts; /* ts must be freed after use */ + char *s; /* string value. s must be freed after use */ + int b; /* bool value */ + int64_t i; /* int value */ + double d; /* double value */ + } u; +}; + +/* on arrays: */ +/* ... retrieve size of array. */ +TOML_EXTERN int toml_array_nelem(const toml_array_t *arr); +/* ... retrieve values using index. */ +TOML_EXTERN toml_datum_t toml_string_at(const toml_array_t *arr, int idx); +TOML_EXTERN toml_datum_t toml_bool_at(const toml_array_t *arr, int idx); +TOML_EXTERN toml_datum_t toml_int_at(const toml_array_t *arr, int idx); +TOML_EXTERN toml_datum_t toml_double_at(const toml_array_t *arr, int idx); +TOML_EXTERN toml_datum_t toml_timestamp_at(const toml_array_t *arr, int idx); +/* ... retrieve array or table using index. */ +TOML_EXTERN toml_array_t *toml_array_at(const toml_array_t *arr, int idx); +TOML_EXTERN toml_table_t *toml_table_at(const toml_array_t *arr, int idx); + +/* on tables: */ +/* ... retrieve the key in table at keyidx. Return 0 if out of range. */ +TOML_EXTERN const char *toml_key_in(const toml_table_t *tab, int keyidx); +/* ... returns 1 if key exists in tab, 0 otherwise */ +TOML_EXTERN int toml_key_exists(const toml_table_t *tab, const char *key); +/* ... retrieve values using key. */ +TOML_EXTERN toml_datum_t toml_string_in(const toml_table_t *arr, + const char *key); +TOML_EXTERN toml_datum_t toml_bool_in(const toml_table_t *arr, const char *key); +TOML_EXTERN toml_datum_t toml_int_in(const toml_table_t *arr, const char *key); +TOML_EXTERN toml_datum_t toml_double_in(const toml_table_t *arr, + const char *key); +TOML_EXTERN toml_datum_t toml_timestamp_in(const toml_table_t *arr, + const char *key); +/* .. retrieve array or table using key. */ +TOML_EXTERN toml_array_t *toml_array_in(const toml_table_t *tab, + const char *key); +TOML_EXTERN toml_table_t *toml_table_in(const toml_table_t *tab, + const char *key); + +/*----------------------------------------------------------------- + * lesser used + */ +/* Return the array kind: 't'able, 'a'rray, 'v'alue, 'm'ixed */ +TOML_EXTERN char toml_array_kind(const toml_array_t *arr); + +/* For array kind 'v'alue, return the type of values + i:int, d:double, b:bool, s:string, t:time, D:date, T:timestamp, 'm'ixed + 0 if unknown +*/ +TOML_EXTERN char toml_array_type(const toml_array_t *arr); + +/* Return the key of an array */ +TOML_EXTERN const char *toml_array_key(const toml_array_t *arr); + +/* Return the number of key-values in a table */ +TOML_EXTERN int toml_table_nkval(const toml_table_t *tab); + +/* Return the number of arrays in a table */ +TOML_EXTERN int toml_table_narr(const toml_table_t *tab); + +/* Return the number of sub-tables in a table */ +TOML_EXTERN int toml_table_ntab(const toml_table_t *tab); + +/* Return the key of a table*/ +TOML_EXTERN const char *toml_table_key(const toml_table_t *tab); + +/*-------------------------------------------------------------- + * misc + */ +TOML_EXTERN int toml_utf8_to_ucs(const char *orig, int len, int64_t *ret); +TOML_EXTERN int toml_ucs_to_utf8(int64_t code, char buf[6]); +TOML_EXTERN void toml_set_memutil(void *(*xxmalloc)(size_t), + void (*xxfree)(void *)); + +/*-------------------------------------------------------------- + * deprecated + */ +/* A raw value, must be processed by toml_rto* before using. */ +typedef const char *toml_raw_t; +TOML_EXTERN toml_raw_t toml_raw_in(const toml_table_t *tab, const char *key); +TOML_EXTERN toml_raw_t toml_raw_at(const toml_array_t *arr, int idx); +TOML_EXTERN int toml_rtos(toml_raw_t s, char **ret); +TOML_EXTERN int toml_rtob(toml_raw_t s, int *ret); +TOML_EXTERN int toml_rtoi(toml_raw_t s, int64_t *ret); +TOML_EXTERN int toml_rtod(toml_raw_t s, double *ret); +TOML_EXTERN int toml_rtod_ex(toml_raw_t s, double *ret, char *buf, int buflen); +TOML_EXTERN int toml_rtots(toml_raw_t s, toml_timestamp_t *ret); + +#endif /* TOML_H */ diff --git a/third_party/tomlc99/toml_cat.c b/third_party/tomlc99/toml_cat.c new file mode 100644 index 0000000..6fd5ec1 --- /dev/null +++ b/third_party/tomlc99/toml_cat.c @@ -0,0 +1,322 @@ +/* + MIT License + + Copyright (c) 2017 CK Tan + https://github.com/cktan/tomlc99 + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#ifdef NDEBUG +#undef NDEBUG +#endif + +#include "toml.h" +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct node_t node_t; +struct node_t { + const char *key; + toml_table_t *tab; +}; + +node_t stack[20]; +int stacktop = 0; +int indent = 0; + +static void prindent() { + for (int i = 0; i < indent; i++) + printf(" "); +} + +static void print_string(const char *s) { + int ok = 1; + for (const char *p = s; *p && ok; p++) { + int ch = *p; + ok = isprint(ch) && ch != '"' && ch != '\\'; + } + + if (ok) { + printf("\"%s\"", s); + return; + } + + int len = strlen(s); + + printf("\""); + for (; len; len--, s++) { + int ch = *s; + if (isprint(ch) && ch != '"' && ch != '\\') { + putchar(ch); + continue; + } + + switch (ch) { + case 0x8: + printf("\\b"); + continue; + case 0x9: + printf("\\t"); + continue; + case 0xa: + printf("\\n"); + continue; + case 0xc: + printf("\\f"); + continue; + case 0xd: + printf("\\r"); + continue; + case '"': + printf("\\\""); + continue; + case '\\': + printf("\\\\"); + continue; + default: + printf("\\0x%02x", ch & 0xff); + continue; + } + } + printf("\""); +} + +static void print_array(toml_array_t *arr); + +static void print_timestamp(toml_datum_t d) { + if (d.u.ts->year) { + printf("%04d-%02d-%02d%s", *d.u.ts->year, *d.u.ts->month, *d.u.ts->day, + d.u.ts->hour ? "T" : ""); + } + if (d.u.ts->hour) { + printf("%02d:%02d:%02d", *d.u.ts->hour, *d.u.ts->minute, *d.u.ts->second); + if (d.u.ts->millisec) { + printf(".%03d", *d.u.ts->millisec); + } + if (d.u.ts->z) { + printf("%s", d.u.ts->z); + } + } +} + +static void print_table(toml_table_t *curtab) { + toml_datum_t d; + int i; + const char *key; + toml_array_t *arr; + toml_table_t *tab; + + for (i = 0; 0 != (key = toml_key_in(curtab, i)); i++) { + + if (0 != (arr = toml_array_in(curtab, key))) { + prindent(); + printf("%s = [\n", key); + indent++; + print_array(arr); + indent--; + prindent(); + printf("],\n"); + continue; + } + + if (0 != (tab = toml_table_in(curtab, key))) { + stack[stacktop].key = key; + stack[stacktop].tab = tab; + stacktop++; + prindent(); + printf("%s = {\n", key); + indent++; + print_table(tab); + indent--; + prindent(); + printf("},\n"); + stacktop--; + continue; + } + + d = toml_string_in(curtab, key); + if (d.ok) { + prindent(); + printf("%s = ", key); + print_string(d.u.s); + printf(",\n"); + free(d.u.s); + continue; + } + + d = toml_bool_in(curtab, key); + if (d.ok) { + prindent(); + printf("%s = %s,\n", key, d.u.b ? "true" : "false"); + continue; + } + + d = toml_int_in(curtab, key); + if (d.ok) { + prindent(); + printf("%s = %" PRId64 ",\n", key, d.u.i); + continue; + } + + d = toml_double_in(curtab, key); + if (d.ok) { + prindent(); + printf("%s = %f,\n", key, d.u.d); + continue; + } + + d = toml_timestamp_in(curtab, key); + if (d.ok) { + prindent(); + printf("%s = ", key); + print_timestamp(d); + printf(",\n"); + free(d.u.ts); + continue; + } + + fflush(stdout); + fprintf(stderr, "ERROR: unable to decode value in table\n"); + exit(1); + } +} + +static void print_array(toml_array_t *curarr) { + toml_datum_t d; + toml_array_t *arr; + toml_table_t *tab; + const int n = toml_array_nelem(curarr); + + for (int i = 0; i < n; i++) { + + if (0 != (arr = toml_array_at(curarr, i))) { + prindent(); + printf("[\n"); + indent++; + print_array(arr); + indent--; + prindent(); + printf("],\n"); + continue; + } + + if (0 != (tab = toml_table_at(curarr, i))) { + prindent(); + printf("{\n"); + indent++; + print_table(tab); + indent--; + prindent(); + printf("},\n"); + continue; + } + + d = toml_string_at(curarr, i); + if (d.ok) { + prindent(); + print_string(d.u.s); + printf(",\n"); + free(d.u.s); + continue; + } + + d = toml_bool_at(curarr, i); + if (d.ok) { + prindent(); + printf("%s,\n", d.u.b ? "true" : "false"); + continue; + } + + d = toml_int_at(curarr, i); + if (d.ok) { + prindent(); + printf("%" PRId64 ",\n", d.u.i); + continue; + } + + d = toml_double_at(curarr, i); + if (d.ok) { + prindent(); + printf("%f,\n", d.u.d); + continue; + } + + d = toml_timestamp_at(curarr, i); + if (d.ok) { + prindent(); + print_timestamp(d); + printf(",\n"); + free(d.u.ts); + continue; + } + + fflush(stdout); + fprintf(stderr, "ERROR: unable to decode value in array\n"); + exit(1); + } +} + +static void cat(FILE *fp) { + char errbuf[200]; + + toml_table_t *tab = toml_parse_file(fp, errbuf, sizeof(errbuf)); + if (!tab) { + fprintf(stderr, "ERROR: %s\n", errbuf); + return; + } + + stack[stacktop].tab = tab; + stack[stacktop].key = ""; + stacktop++; + printf("{\n"); + indent++; + print_table(tab); + indent--; + printf("}\n"); + stacktop--; + + toml_free(tab); +} + +int main(int argc, const char *argv[]) { + int i; + if (argc == 1) { + cat(stdin); + } else { + for (i = 1; i < argc; i++) { + + FILE *fp = fopen(argv[i], "r"); + if (!fp) { + fprintf(stderr, "ERROR: cannot open %s: %s\n", argv[i], + strerror(errno)); + exit(1); + } + cat(fp); + fclose(fp); + } + } + return 0; +} diff --git a/third_party/tomlc99/toml_json.c b/third_party/tomlc99/toml_json.c new file mode 100644 index 0000000..936252d --- /dev/null +++ b/third_party/tomlc99/toml_json.c @@ -0,0 +1,225 @@ +/* + MIT License + + Copyright (c) 2017 CK Tan + https://github.com/cktan/tomlc99 + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ +#ifdef NDEBUG +#undef NDEBUG +#endif + +#include "toml.h" +#include +#include +#include +#include +#include + +static void print_escape_string(const char *s) { + for (; *s; s++) { + int ch = *s; + switch (ch) { + case '\b': + printf("\\b"); + break; + case '\t': + printf("\\t"); + break; + case '\n': + printf("\\n"); + break; + case '\f': + printf("\\f"); + break; + case '\r': + printf("\\r"); + break; + case '"': + printf("\\\""); + break; + case '\\': + printf("\\\\"); + break; + default: + printf("%c", ch); + break; + } + } +} + +static void print_raw(const char *s) { + char *sval; + int64_t ival; + int bval; + double dval; + toml_timestamp_t ts; + char dbuf[100]; + + if (0 == toml_rtos(s, &sval)) { + printf("{\"type\":\"string\",\"value\":\""); + print_escape_string(sval); + printf("\"}"); + free(sval); + } else if (0 == toml_rtoi(s, &ival)) { + printf("{\"type\":\"integer\",\"value\":\"%" PRId64 "\"}", ival); + } else if (0 == toml_rtob(s, &bval)) { + printf("{\"type\":\"bool\",\"value\":\"%s\"}", bval ? "true" : "false"); + } else if (0 == toml_rtod_ex(s, &dval, dbuf, sizeof(dbuf))) { + printf("{\"type\":\"float\",\"value\":\"%s\"}", dbuf); + } else if (0 == toml_rtots(s, &ts)) { + char millisec[10]; + if (ts.millisec) + sprintf(millisec, ".%03d", *ts.millisec); + else + millisec[0] = 0; + if (ts.year && ts.hour) { + printf("{\"type\":\"%s\",\"value\":\"%04d-%02d-%02dT%02d:%02d:%02d%" + "s%s\"}", + (ts.z ? "datetime" : "datetime-local"), + *ts.year, *ts.month, *ts.day, *ts.hour, *ts.minute, *ts.second, + millisec, (ts.z ? ts.z : "")); + } else if (ts.year) { + printf("{\"type\":\"date-local\",\"value\":\"%04d-%02d-%02d\"}", *ts.year, + *ts.month, *ts.day); + } else if (ts.hour) { + printf("{\"type\":\"time-local\",\"value\":\"%02d:%02d:%02d%s\"}", *ts.hour, + *ts.minute, *ts.second, millisec); + } + } else { + fprintf(stderr, "unknown type\n"); + exit(1); + } +} + +static void print_array(toml_array_t *arr); +static void print_table(toml_table_t *curtab) { + int i; + const char *key; + const char *raw; + toml_array_t *arr; + toml_table_t *tab; + + printf("{"); + for (i = 0; 0 != (key = toml_key_in(curtab, i)); i++) { + + printf("%s\"", i > 0 ? "," : ""); + print_escape_string(key); + printf("\":"); + + if (0 != (raw = toml_raw_in(curtab, key))) { + print_raw(raw); + } else if (0 != (arr = toml_array_in(curtab, key))) { + print_array(arr); + } else if (0 != (tab = toml_table_in(curtab, key))) { + print_table(tab); + } else { + abort(); + } + } + printf("}"); +} + +static void print_table_array(toml_array_t *curarr) { + int i; + toml_table_t *tab; + + printf("["); + for (i = 0; 0 != (tab = toml_table_at(curarr, i)); i++) { + printf("%s", i > 0 ? "," : ""); + print_table(tab); + } + printf("]"); +} + +static void print_array(toml_array_t *curarr) { + if (toml_array_kind(curarr) == 't') { + print_table_array(curarr); + return; + } + + printf("["); + + const char *raw; + toml_array_t *arr; + toml_table_t *tab; + + const int n = toml_array_nelem(curarr); + for (int i = 0; i < n; i++) { + printf("%s", i > 0 ? "," : ""); + + if (0 != (arr = toml_array_at(curarr, i))) { + print_array(arr); + continue; + } + + if (0 != (tab = toml_table_at(curarr, i))) { + print_table(tab); + continue; + } + + raw = toml_raw_at(curarr, i); + if (raw) { + print_raw(raw); + continue; + } + + fflush(stdout); + fprintf(stderr, "ERROR: unable to decode value in array\n"); + exit(1); + } + + printf("]"); +} + +static void cat(FILE *fp) { + char errbuf[200]; + + toml_table_t *tab = toml_parse_file(fp, errbuf, sizeof(errbuf)); + if (!tab) { + fprintf(stderr, "ERROR: %s\n", errbuf); + exit(1); + } + + print_table(tab); + printf("\n"); + + toml_free(tab); +} + +int main(int argc, const char *argv[]) { + int i; + if (argc == 1) { + cat(stdin); + } else { + for (i = 1; i < argc; i++) { + + FILE *fp = fopen(argv[i], "r"); + if (!fp) { + fprintf(stderr, "ERROR: cannot open %s: %s\n", argv[i], + strerror(errno)); + exit(1); + } + cat(fp); + fclose(fp); + } + } + return 0; +} diff --git a/third_party/tomlc99/toml_sample.c b/third_party/tomlc99/toml_sample.c new file mode 100644 index 0000000..a8c3b9d --- /dev/null +++ b/third_party/tomlc99/toml_sample.c @@ -0,0 +1,60 @@ +#include "toml.h" +#include +#include +#include +#include + +static void fatal(const char *msg, const char *msg1) { + fprintf(stderr, "ERROR: %s%s\n", msg, msg1 ? msg1 : ""); + exit(1); +} + +int main() { + FILE *fp; + char errbuf[200]; + + // 1. Read and parse toml file + fp = fopen("sample.toml", "r"); + if (!fp) { + fatal("cannot open sample.toml - ", strerror(errno)); + } + + toml_table_t *conf = toml_parse_file(fp, errbuf, sizeof(errbuf)); + fclose(fp); + + if (!conf) { + fatal("cannot parse - ", errbuf); + } + + // 2. Traverse to a table. + toml_table_t *server = toml_table_in(conf, "server"); + if (!server) { + fatal("missing [server]", ""); + } + + // 3. Extract values + toml_datum_t host = toml_string_in(server, "host"); + if (!host.ok) { + fatal("cannot read server.host", ""); + } + + toml_array_t *portarray = toml_array_in(server, "port"); + if (!portarray) { + fatal("cannot read server.port", ""); + } + + printf("host: %s\n", host.u.s); + printf("port: "); + for (int i = 0;; i++) { + toml_datum_t port = toml_int_at(portarray, i); + if (!port.ok) + break; + printf("%d ", (int)port.u.i); + } + printf("\n"); + + // 4. Free memory + free(host.u.s); + toml_free(conf); + return 0; +} diff --git a/third_party/tomlc99/unittest/Makefile b/third_party/tomlc99/unittest/Makefile new file mode 100644 index 0000000..0fb102f --- /dev/null +++ b/third_party/tomlc99/unittest/Makefile @@ -0,0 +1,11 @@ +CFLAGS = -g -I.. + +TESTS = t1 + +all: $(TESTS) + +t1: t1.c ../toml.c + +clean: + rm -f $(TESTS) + diff --git a/third_party/tomlc99/unittest/t1.c b/third_party/tomlc99/unittest/t1.c new file mode 100644 index 0000000..3da3901 --- /dev/null +++ b/third_party/tomlc99/unittest/t1.c @@ -0,0 +1,73 @@ +#include "../toml.h" +#include +#include +#include +#include + +int main(int argc, const char *argv[]) { + char xxbuf[6], buf[6]; + int64_t xxcode, code; + int xxsize; + + xxsize = 2, xxcode = 0x80; + memcpy(xxbuf, "\xc2\x80", xxsize); + assert(toml_ucs_to_utf8(xxcode, buf) == xxsize && + 0 == memcmp(buf, xxbuf, xxsize)); + assert(toml_utf8_to_ucs(buf, xxsize, &code) == xxsize && code == xxcode); + + xxsize = 2, xxcode = 0x7ff; + memcpy(xxbuf, "\xdf\xbf", xxsize); + assert(toml_ucs_to_utf8(xxcode, buf) == xxsize && + 0 == memcmp(buf, xxbuf, xxsize)); + assert(toml_utf8_to_ucs(buf, xxsize, &code) == xxsize && code == xxcode); + + xxsize = 3, xxcode = 0x800; + memcpy(xxbuf, "\xe0\xa0\x80", xxsize); + assert(toml_ucs_to_utf8(xxcode, buf) == xxsize && + 0 == memcmp(buf, xxbuf, xxsize)); + assert(toml_utf8_to_ucs(buf, xxsize, &code) == xxsize && code == xxcode); + + xxsize = 3, xxcode = 0xfffd; + memcpy(xxbuf, "\xef\xbf\xbd", xxsize); + assert(toml_ucs_to_utf8(xxcode, buf) == xxsize && + 0 == memcmp(buf, xxbuf, xxsize)); + assert(toml_utf8_to_ucs(buf, xxsize, &code) == xxsize && code == xxcode); + + xxsize = 4, xxcode = 0x10000; + memcpy(xxbuf, "\xf0\x90\x80\x80", xxsize); + assert(toml_ucs_to_utf8(xxcode, buf) == xxsize && + 0 == memcmp(buf, xxbuf, xxsize)); + assert(toml_utf8_to_ucs(buf, xxsize, &code) == xxsize && code == xxcode); + + xxsize = 4, xxcode = 0x1fffff; + memcpy(xxbuf, "\xf7\xbf\xbf\xbf", xxsize); + assert(toml_ucs_to_utf8(xxcode, buf) == xxsize && + 0 == memcmp(buf, xxbuf, xxsize)); + assert(toml_utf8_to_ucs(buf, xxsize, &code) == xxsize && code == xxcode); + + xxsize = 5, xxcode = 0x200000; + memcpy(xxbuf, "\xf8\x88\x80\x80\x80", xxsize); + assert(toml_ucs_to_utf8(xxcode, buf) == xxsize && + 0 == memcmp(buf, xxbuf, xxsize)); + assert(toml_utf8_to_ucs(buf, xxsize, &code) == xxsize && code == xxcode); + + xxsize = 5, xxcode = 0x3ffffff; + memcpy(xxbuf, "\xfb\xbf\xbf\xbf\xbf", xxsize); + assert(toml_ucs_to_utf8(xxcode, buf) == xxsize && + 0 == memcmp(buf, xxbuf, xxsize)); + assert(toml_utf8_to_ucs(buf, xxsize, &code) == xxsize && code == xxcode); + + xxsize = 6, xxcode = 0x4000000; + memcpy(xxbuf, "\xfc\x84\x80\x80\x80\x80", xxsize); + assert(toml_ucs_to_utf8(xxcode, buf) == xxsize && + 0 == memcmp(buf, xxbuf, xxsize)); + assert(toml_utf8_to_ucs(buf, xxsize, &code) == xxsize && code == xxcode); + + xxsize = 6, xxcode = 0x7fffffff; + memcpy(xxbuf, "\xfd\xbf\xbf\xbf\xbf\xbf", xxsize); + assert(toml_ucs_to_utf8(xxcode, buf) == xxsize && + 0 == memcmp(buf, xxbuf, xxsize)); + assert(toml_utf8_to_ucs(buf, xxsize, &code) == xxsize && code == xxcode); + + return 0; +}