diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp index 6c40ec819..cd8a8ed7c 100644 --- a/library/DataDefs.cpp +++ b/library/DataDefs.cpp @@ -197,6 +197,16 @@ std::string DFHack::bitfieldToString(const void *p, int size, const bitfield_ite return res; } +int DFHack::findBitfieldField(const std::string &name, int size, const bitfield_item_info *items) +{ + for (int i = 0; i < size*8; i++) { + if (items[i].name && items[i].name == name) + return i; + } + + return -1; +} + #define SIMPLE_GLOBAL(name,tname) \ tname *df::global::name = NULL; #define GLOBAL(name,tname) SIMPLE_GLOBAL(name,df::tname) diff --git a/library/MiscUtils.cpp b/library/MiscUtils.cpp index 72e3eae28..aa141313b 100644 --- a/library/MiscUtils.cpp +++ b/library/MiscUtils.cpp @@ -34,6 +34,7 @@ distribution. #include #endif +#include #include std::string stl_sprintf(const char *fmt, ...) { @@ -59,6 +60,45 @@ std::string stl_vsprintf(const char *fmt, va_list args) { } } +bool split_string(std::vector *out, + const std::string &str, const std::string &separator, bool squash_empty) +{ + out->clear(); + + size_t start = 0, pos; + + if (!separator.empty()) + { + while ((pos = str.find(separator,start)) != std::string::npos) + { + if (pos > start || !squash_empty) + out->push_back(str.substr(start, pos-start)); + start = pos + separator.size(); + } + } + + if (start < str.size() || !squash_empty) + out->push_back(str.substr(start)); + + return out->size() > 1; +} + +std::string toUpper(const std::string &str) +{ + std::string rv(str.size(),' '); + for (unsigned i = 0; i < str.size(); ++i) + rv[i] = toupper(str[i]); + return rv; +} + +std::string toLower(const std::string &str) +{ + std::string rv(str.size(),' '); + for (unsigned i = 0; i < str.size(); ++i) + rv[i] = tolower(str[i]); + return rv; +} + #ifdef LINUX_BUILD // Linux uint64_t GetTimeMs64() { diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index a7fb956ed..b4bc516d0 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -143,6 +143,12 @@ namespace DFHack }; DFHACK_EXPORT std::string bitfieldToString(const void *p, int size, const bitfield_item_info *items); + DFHACK_EXPORT int findBitfieldField(const std::string &name, int size, const bitfield_item_info *items); + + template + inline int findBitfieldField(const T &val, const std::string &name) { + return findBitfieldField(name, sizeof(val.whole), val.get_items()); + } template inline std::string bitfieldToString(const T &val) { diff --git a/library/include/MiscUtils.h b/library/include/MiscUtils.h index 5897994e3..c744f587c 100644 --- a/library/include/MiscUtils.h +++ b/library/include/MiscUtils.h @@ -163,6 +163,28 @@ inline bool vector_contains(const std::vector &vec, FT CT::*field, FT key) return binsearch_index(vec, field, key) >= 0; } +template +inline T vector_get(const std::vector &vec, unsigned idx, const T &defval = T()) +{ + if (idx < vec.size()) + return vec[idx]; + else + return defval; +} + +template +inline void vector_insert_at(std::vector &vec, unsigned idx, const T &val) +{ + vec.insert(vec.begin()+idx, val); +} + +template +inline void vector_erase_at(std::vector &vec, unsigned idx) +{ + if (idx < vec.size()) + vec.erase(vec.begin()+idx); +} + template unsigned insert_into_vector(std::vector &vec, FT key, bool *inserted = NULL) { @@ -170,7 +192,7 @@ unsigned insert_into_vector(std::vector &vec, FT key, bool *inserted = NULL) bool to_ins = (pos >= vec.size() || vec[pos] != key); if (inserted) *inserted = to_ins; if (to_ins) - vec.insert(vec.begin()+pos,key); + vector_insert_at(vec, pos, key); return pos; } @@ -181,7 +203,7 @@ unsigned insert_into_vector(std::vector &vec, FT CT::*field, CT *obj, bool bool to_ins = (pos >= vec.size() || vec[pos] != obj); if (inserted) *inserted = to_ins; if (to_ins) - vec.insert(vec.begin()+pos,obj); + vector_insert_at(vec, pos, obj); return pos; } @@ -213,10 +235,28 @@ Link *linked_list_append(Link *head, Link *tail) return tail; } +template +Link *linked_list_insert_after(Link *pos, Link *link) +{ + link->next = pos->next; + if (pos->next) + pos->next->prev = link; + link->prev = pos; + pos->next = link; + return link; +} + /* * MISC */ +DFHACK_EXPORT bool split_string(std::vector *out, + const std::string &str, const std::string &separator, + bool squash_empty = false); + +DFHACK_EXPORT std::string toUpper(const std::string &str); +DFHACK_EXPORT std::string toLower(const std::string &str); + /** * Returns the amount of milliseconds elapsed since the UNIX epoch. * Works on both windows and linux. diff --git a/library/include/modules/Job.h b/library/include/modules/Job.h index 35f4fdc39..5534a9d4f 100644 --- a/library/include/modules/Job.h +++ b/library/include/modules/Job.h @@ -35,6 +35,7 @@ namespace df struct job; struct job_item; struct job_item_filter; + struct building; } namespace DFHack @@ -49,6 +50,10 @@ namespace DFHack DFHACK_EXPORT bool operator== (const df::job &a, const df::job &b); DFHACK_EXPORT void printJobDetails(Core *c, df::job *job); + + DFHACK_EXPORT df::building *getJobHolder(df::job *job); + + DFHACK_EXPORT bool linkJobIntoWorld(df::job *job, bool new_id = true); } #endif diff --git a/library/include/modules/Materials.h b/library/include/modules/Materials.h index 25becc400..e1455c158 100644 --- a/library/include/modules/Materials.h +++ b/library/include/modules/Materials.h @@ -101,7 +101,8 @@ namespace DFHack return ptr ? decode(ptr->mat_type, ptr->mat_index) : decode(-1); } - bool find(const std::string &token, const std::string &subtoken = std::string()); + bool find(const std::string &token); + bool findBuiltin(const std::string &token); bool findInorganic(const std::string &token); bool findPlant(const std::string &token, const std::string &subtoken); @@ -121,6 +122,8 @@ namespace DFHack bool matches(const df::job_item &item); }; + DFHACK_EXPORT bool parseJobMaterialCategory(df::job_material_category *cat, const std::string &token); + inline bool operator== (const MaterialInfo &a, const MaterialInfo &b) { return a.type == b.type && a.index == b.index; } diff --git a/library/modules/Job.cpp b/library/modules/Job.cpp index a91146c98..3e3016e1f 100644 --- a/library/modules/Job.cpp +++ b/library/modules/Job.cpp @@ -33,6 +33,7 @@ using namespace std; #include "Core.h" #include "PluginManager.h" +#include "MiscUtils.h" #include "modules/Job.h" #include "modules/Materials.h" @@ -45,6 +46,7 @@ using namespace std; #include #include #include +#include using namespace DFHack; using namespace df::enums; @@ -69,7 +71,7 @@ df::job *DFHack::cloneJobStruct(df::job *job) df::general_ref *ref = pnew->references[i]; if (virtual_cast(ref)) - pnew->references.erase(pnew->references.begin()+i); + vector_erase_at(pnew->references, i); else pnew->references[i] = ref->clone(); } @@ -199,3 +201,44 @@ void DFHack::printJobDetails(Core *c, df::job *job) for (unsigned i = 0; i < job->job_items.size(); i++) print_job_item_details(c, job, i, job->job_items[i]); } + +df::building *DFHack::getJobHolder(df::job *job) +{ + for (unsigned i = 0; i < job->references.size(); i++) + { + VIRTUAL_CAST_VAR(ref, df::general_ref_building_holderst, job->references[i]); + if (ref) + return ref->getBuilding(); + } + + return NULL; +} + +bool DFHack::linkJobIntoWorld(df::job *job, bool new_id) +{ + using df::global::world; + using df::global::job_next_id; + + assert(!job->list_link); + + if (new_id) { + job->id = (*job_next_id)++; + + job->list_link = new df::job_list_link(); + job->list_link->item = job; + linked_list_append(&world->job_list, job->list_link); + return true; + } else { + df::job_list_link *ins_pos = &world->job_list; + while (ins_pos->next && ins_pos->next->item->id < job->id) + ins_pos = ins_pos->next; + + if (ins_pos->next && ins_pos->next->item->id == job->id) + return false; + + job->list_link = new df::job_list_link(); + job->list_link->item = job; + linked_list_insert_after(ins_pos, job->list_link); + return true; + } +} diff --git a/library/modules/Materials.cpp b/library/modules/Materials.cpp index 92149342f..32145cc90 100644 --- a/library/modules/Materials.cpp +++ b/library/modules/Materials.cpp @@ -143,27 +143,40 @@ bool MaterialInfo::decode(int16_t type, int32_t index) return (material != NULL); } -bool MaterialInfo::find(const std::string &token, const std::string &subtoken) +bool MaterialInfo::find(const std::string &token) { - if (findBuiltin(token)) - return true; - if (subtoken.empty()) + std::vector items; + split_string(&items, token, ":"); + + if (items[0] == "INORGANIC") + return findInorganic(vector_get(items,1)); + if (items[0] == "CREATURE_MAT" || items[0] == "CREATURE") + return findCreature(vector_get(items,1), vector_get(items,2)); + if (items[0] == "PLANT_MAT" || items[0] == "PLANT") + return findPlant(vector_get(items,1), vector_get(items,2)); + + if (items.size() == 1) { - if (findInorganic(token)) + if (findBuiltin(items[0])) + return true; + if (findInorganic(items[0])) return true; } - else + else if (items.size() == 2) { - if (findPlant(token, subtoken)) + if (findPlant(items[0], items[1])) return true; - if (findCreature(token, subtoken)) + if (findCreature(items[0], items[1])) return true; } + return false; } bool MaterialInfo::findBuiltin(const std::string &token) { + if (token.empty()) + return decode(-1); df::world_raws &raws = df::global::world->raws; for (int i = 1; i < NUM_BUILTIN; i++) if (raws.mat_table.builtin[i]->id == token) @@ -173,6 +186,8 @@ bool MaterialInfo::findBuiltin(const std::string &token) bool MaterialInfo::findInorganic(const std::string &token) { + if (token.empty()) + return decode(-1); df::world_raws &raws = df::global::world->raws; for (unsigned i = 0; i < raws.inorganics.size(); i++) { @@ -185,6 +200,8 @@ bool MaterialInfo::findInorganic(const std::string &token) bool MaterialInfo::findPlant(const std::string &token, const std::string &subtoken) { + if (token.empty() || subtoken.empty()) + return decode(-1); df::world_raws &raws = df::global::world->raws; for (unsigned i = 0; i < raws.plants.all.size(); i++) { @@ -203,6 +220,8 @@ bool MaterialInfo::findPlant(const std::string &token, const std::string &subtok bool MaterialInfo::findCreature(const std::string &token, const std::string &subtoken) { + if (token.empty() || subtoken.empty()) + return decode(-1); df::world_raws &raws = df::global::world->raws; for (unsigned i = 0; i < raws.creatures.all.size(); i++) { @@ -393,6 +412,25 @@ void MaterialInfo::getMatchBits(df::job_item_flags3 &ok, df::job_item_flags3 &ma #undef FLAG #undef TEST +bool DFHack::parseJobMaterialCategory(df::job_material_category *cat, const std::string &token) +{ + cat->whole = 0; + + std::vector items; + split_string(&items, toLower(token), ",", true); + + for (unsigned i = 0; i < items.size(); i++) + { + int id = findBitfieldField(*cat, items[i]); + if (id < 0) + return false; + + cat->whole |= (1 << id); + } + + return true; +} + Module* DFHack::createMaterials() { return new Materials(); diff --git a/library/modules/World.cpp b/library/modules/World.cpp index 4dea79c2b..31dfb7e6a 100644 --- a/library/modules/World.cpp +++ b/library/modules/World.cpp @@ -265,7 +265,7 @@ PersistentDataItem World::AddPersistentData(const std::string &key) hfig->id = new_id; hfig->name.has_name = true; hfig->name.first_name = key; - memset(hfig->name.words, 0, sizeof(hfig->name.words)); + memset(hfig->name.words, 0xFF, sizeof(hfig->name.words)); hfvec.insert(hfvec.begin(), hfig); return dataFromHFig(hfig); diff --git a/plugins/jobutils.cpp b/plugins/jobutils.cpp index a89fa99cd..a908b7607 100644 --- a/plugins/jobutils.cpp +++ b/plugins/jobutils.cpp @@ -64,7 +64,7 @@ DFhackCExport command_result plugin_init (Core *c, std::vector & " Print details of the current job.\n" " job list\n" " Print details of all jobs in the workshop.\n" - " job item-material [submaterial]\n" + " job item-material \n" " Replace the exact material id in the job item.\n" ) ); @@ -249,21 +249,6 @@ static command_result job_material(Core * c, vector & parameters) /* job-duplicate implementation */ -static df::job *clone_job(df::job *job) -{ - df::job *pnew = cloneJobStruct(job); - - pnew->id = (*job_next_id)++; - - // Link the job into the global list - pnew->list_link = new df::job_list_link(); - pnew->list_link->item = pnew; - - linked_list_append(&world->job_list, pnew->list_link); - - return pnew; -} - static command_result job_duplicate(Core * c, vector & parameters) { if (!parameters.empty()) @@ -287,10 +272,10 @@ static command_result job_duplicate(Core * c, vector & parameters) } // Actually clone - df::job *pnew = clone_job(job); + df::job *pnew = cloneJobStruct(job); - int pos = ++*ui_workshop_job_cursor; - building->jobs.insert(building->jobs.begin()+pos, pnew); + linkJobIntoWorld(pnew); + vector_insert_at(building->jobs, ++*ui_workshop_job_cursor, pnew); return CR_OK; } @@ -336,9 +321,8 @@ static command_result job_cmd(Core * c, vector & parameters) df::job_item *item = job->job_items[v-1]; - std::string subtoken = (parameters.size()>3 ? parameters[3] : ""); MaterialInfo info; - if (!info.find(parameters[2], subtoken)) { + if (!info.find(parameters[2])) { c->con.printerr("Could not find the specified material.\n"); return CR_FAILURE; } diff --git a/plugins/workflow.cpp b/plugins/workflow.cpp index 99d7dc411..0a1319d2c 100644 --- a/plugins/workflow.cpp +++ b/plugins/workflow.cpp @@ -98,18 +98,6 @@ DFhackCExport command_result plugin_onstatechange(Core* c, state_change_event ev /*******************************/ -static df::building *getJobHolder(df::job *job) -{ - for (unsigned i = 0; i < job->references.size(); i++) - { - VIRTUAL_CAST_VAR(ref, df::general_ref_building_holderst, job->references[i]); - if (ref) - return ref->getBuilding(); - } - - return NULL; -}; - struct ProtectedJob { int id; int building_id; @@ -253,8 +241,6 @@ static int *find_protected_id_slot(Core *c, int key) if (key == -1) { protected_cfg.push_back(c->getWorld()->AddPersistentData("workflow/protected-jobs")); PersistentDataItem &item = protected_cfg.back(); - for (int j = 0; j < PersistentDataItem::NumInts; j++) - item.ival(j) = -1; return &item.ival(0); } @@ -408,7 +394,7 @@ DFhackCExport command_result plugin_onupdate(Core* c) { for (int i = pending_recover.size()-1; i >= 0; i--) if (recover_job(c, pending_recover[i])) - pending_recover.erase(pending_recover.begin()+i); + vector_erase_at(pending_recover, i); check_lost_jobs(c); }