// // Created on: 4/30/21 #include "MultiMul.hpp" #include #include #include #include #include #include #include #include "mapstuff.h" using namespace std::string_literals ; //========================================================= // multi_item //========================================================= multi_item::multi_item() :tileid(0xFFFF),altitude(0),offsetx(0),offsety(0),flag(0){ info = nullptr ; } //========================================================= // collection_item //========================================================= //========================================================= auto collection_item::itemsAt(int offsetx,int offsety) ->std::vector { std::vector rvalue ; for (auto &item:items){ if ( (item.offsetx == static_cast(offsetx)) && (item.offsety == static_cast(offsety))){ rvalue.push_back(item); } } return rvalue ; } //========================================================= collection_item::collection_item() :min_y(0),min_x(0),max_x(0),max_y(0),min_alt(0),max_alt(0){ } //========================================================= auto collection_item::name(int multi_id) ->std::string { try { auto name = collection_names.at(multi_id); return name ; } catch(...) { return "unknown - "s + std::to_string(multi_id); } } //========================================================= const std::unordered_map collection_item::collection_names = { {0,"Small Boat [north]"s},{1,"Small Boat [east]"s}, {2,"Small Boat [south]"s},{3,"Small Boat [west]"s}, {4,"Small Dragon Boat [north]"s},{5,"Small Dragon Boat [east]"s}, {6,"Small Dragon Boat [south]"s},{7,"Small Dragon Boat [west]"s}, {8,"Medium Boat [north]"s},{9,"Medium Boat [east]"s}, {10,"Medium Boat [south]"s},{11,"Medium Boat [west]"s}, {12,"Medium Dragon Boat [north]"s},{13,"Medium Dragon Boat [east]"s}, {14,"Medium Dragon Boat [south]"s},{15,"Medium Dragon Boat [west]"s}, {16,"Large Boat [north]"s},{17,"Large Boat [east]"s}, {18,"Large Boat [soutch]"s},{19,"Large Boat [west]"s}, {20,"Large Dragon Boat [north]"s},{21,"Large Dragon Boat [east]"s}, {22,"Large Dragon Boat [south]"s},{23,"Large Dragon Boat [west]"s}, {24,"Orcish Galleon [north]"s},{25,"Orcish Galleon [east]"s}, {26,"Orcish Galleon [south]"s},{27,"Orcish Galleon [west]"s}, {28,"Orcish Galleon [damaged][north]"s},{29,"Orcish Galleon [damaged][east]"s}, {30,"Orcish Galleon [damaged][south]"s},{31,"Orcish Galleon [damaged][west]"s}, {32,"Orcish Galleon [destroyed][north]"s},{33,"Orcish Galleon [destroyed][east]"s}, {34,"Orcish Galleon [destroyed][south]"s},{35,"Orcish Galleon [destroyed][west]"s}, {36,"Gargish Galleon [north]"s},{37,"Gargish Galleon [east]"s}, {38,"Gargish Galleon [south]"s},{39,"Gargish Galleon [west]"s}, {40,"Gargish Galleon [damaged][north]"s},{41,"Gargish Galleon [damaged][east]"s}, {42,"Gargish Galleon [damaged][south]"s},{43,"Gargish Galleon [damaged][west]"s}, {44,"Gargish Galleon [destroyed][north]"s},{45,"Gargish Galleon [destroyed][east]"s}, {46,"Gargish Galleon [destroyed][south]"s},{47,"Gargish Galleon [destroyed][west]"s}, {48,"Tokuno Galleon [north]"s},{49,"Tokuno Galleon [east]"s}, {50,"Tokuno Galleon [south]"s},{51,"Tokuno Galleon [west]"s}, {52,"Tokuno Galleon [damaged][north]"s},{53,"Tokuno Galleon [damaged][east]"s}, {54,"Tokuno Galleon [damaged][south]"s},{55,"Tokuno Galleon [damaged][west]"s}, {56,"Tokuno Galleon [destroyed][north]"s},{57,"Tokuno Galleon [destroyed][east]"s}, {58,"Tokuno Galleon [destroyed][south]"s},{59,"Tokuno Galleon [destroyed][west]"s}, {60,"Rowboat [north]"s},{61,"Rowboat [east]"s}, {62,"Rowboat [south]"s},{63,"Rowboat [west]"s}, {64,"British Galleon [north]"s},{65,"British Galleon [east]"s}, {66,"British Galleon [south]"s},{67,"British Galleon [west]"s}, {68,"British Galleon [damaged][north]"s},{69,"British Galleon [damaged][east]"s}, {70,"British Galleon [damaged][south]"s},{71,"British Galleon [damaged][west]"s}, {100,"Small Stone and Plaster House"s},{101,"Small Stone and Plaster House"s}, {102,"Small Fieldstone House"s},{103,"Small Fieldstone House"s}, {104,"Small Brick House"s},{105,"Small Brick House"s}, {106,"Small Wood House"s},{107,"Small Wood House"s}, {108,"Small Wood and Plaster House"s},{109,"Small Wood and Plaster House"s}, {110,"Small Thatched Roof Cottage"s},{111,"Small Thatched Roof Cottage"s}, {112,"Tent [blue]"s},{113,"Tent [blue]"s}, {114,"Tent [green]"s},{115,"Tent [green]"s}, {116,"Large Brick House"s},{117,"Large Brick House"s}, {118,"Two Story Wood and Plaster House"s},{119,"Two Story Wood and Plaster House"s}, {120,"Two Story Stone and Plaster House"s},{121,"Two Story Stone and Plaster House"s}, {122,"Large Tower"s},{123,"Large Tower"s}, {124,"Stone Keep"s},{125,"Stone Keep"s}, {126,"Castle"s},{127,"Castle"s}, {128,"Large Patio House"s},{129,"Large Patio House"s}, {141,"Large Patio House"s},{150,"Large Marble Patio House"s}, {152,"Small Tower"s},{154,"Log Cabin"s}, {156,"Sandstone Patio House"s},{158,"Two-Story Villa"s}, {160,"Small Stone Workshop"s},{162,"Small Marble Workshop"s}, {201,"Mine's Elevator"s},{202,"Lifting Bridge"s}, {500,"Wandering Healer Camp"s},{501,"Wandering Mage Camp"s}, {502,"Wandering Bank Camp"s}, {1000,"Treasure Pile"s},{1001,"Treasure Pile"s}, {1002,"Treasure Pile"s},{1003,"Treasure Pile"s}, {2000,"Wooden Supports"s},{2001,"Wooden Supports"s}, {2002,"Wooden Supports"s},{2003,"Wooden Supports"s}, {2004,"Wooden Supports"s},{2005,"Wooden Supports"s}, {2006,"Wooden Supports"s},{2007,"Wooden Supports"s}, {3000,"Farmer's Cabin"s}, {4000,"Exodus Generator"s},{4001,"Exodus Generator"s}, {4002,"Exodus Control Pylon"s}, {4003,"Nystul's Tower (stage 1)"s},{4004,"Nystul's Tower (stage 2)"s}, {4005,"Nystul's Tower (stage 3)"s},{4006,"Nystul's Tower (stage 4)"s}, {4007,"Nystul's Tower (stage 5)"s},{4008,"Nystul's Tower (stage 6)"s}, {4009,"Scaffold and Trailer"s},{4010,"Exodus Energy Tower"s}, {4011,"Exodus Barrier"s},{4012,"Exodus Control Device"s}, {5000,"Minax Stronghold"s}, {5100,"Foundation [7x7]"s},{5101,"Foundation [7x8]"s}, {5102,"Foundation [7x9]"s},{5103,"Foundation [7x10]"s}, {5104,"Foundation [7x11]"s},{5105,"Foundation [7x12]"s}, {5112,"Foundation [8x7]"s},{5113,"Foundation [8x8]"s}, {5114,"Foundation [8x9]"s},{5115,"Foundation [8x10]"s}, {5116,"Foundation [8x11]"s},{5117,"Foundation [8x12]"s}, {5118,"Foundation [8x13]"s}, {5124,"Foundation [9x7]"s},{5125,"Foundation [9x8]"s}, {5126,"Foundation [9x9]"s},{5127,"Foundation [9x10]"s}, {5128,"Foundation [9x11]"s},{5129,"Foundation [9x12]"s}, {5130,"Foundation [9x13]"s},{5131,"Foundation [9x14]"s}, {5136,"Foundation [10x7]"s},{5137,"Foundation [10x8]"s}, {5138,"Foundation [10x9]"s},{5139,"Foundation [10x10]"s}, {5140,"Foundation [10x11]"s},{5141,"Foundation [10x12]"s}, {5142,"Foundation [10x13]"s},{5143,"Foundation [10x14]"s}, {5144,"Foundation [10x15]"s}, {5148,"Foundation [11x7]"s},{5149,"Foundation [11x8]"s}, {5150,"Foundation [11x9]"s},{5151,"Foundation [11x10]"s}, {5152,"Foundation [11x11]"s},{5153,"Foundation [11x12]"s}, {5154,"Foundation [11x13]"s},{5155,"Foundation [11x14]"s}, {5156,"Foundation [11x15]"s},{5157,"Foundation [11x16]"s}, {5160,"Foundation [12x7]"s},{5161,"Foundation [12x8]"s}, {5162,"Foundation [12x9]"s},{5163,"Foundation [12x10]"s}, {5164,"Foundation [12x11]"s},{5165,"Foundation [12x12]"s}, {5166,"Foundation [12x13]"s},{5167,"Foundation [12x14]"s}, {5168,"Foundation [12x15]"s},{5169,"Foundation [12x16]"s}, {5170,"Foundation [12x17]"s}, {5173,"Foundation [13x8]"s}, {5174,"Foundation [13x9]"s}, {5175,"Foundation [13x10]"s},{5176,"Foundation [13x11]"s}, {5177,"Foundation [13x12]"s},{5178,"Foundation [13x13]"s}, {5179,"Foundation [13x14]"s},{5180,"Foundation [13x15]"s}, {5181,"Foundation [13x16]"s},{5182,"Foundation [13x17]"s}, {5183,"Foundation [13x18]"s}, {5186,"Foundation [14x9]"s}, {5187,"Foundation [14x10]"s}, {5188,"Foundation [14x11]"s},{5189,"Foundation [14x12]"s}, {5190,"Foundation [14x13]"s},{5191,"Foundation [14x14]"s}, {5192,"Foundation [14x15]"s},{5193,"Foundation [14x16]"s}, {5194,"Foundation [14x17]"s},{5195,"Foundation [14x18]"s}, {5199,"Foundation [15x10]"s},{5200,"Foundation [15x11]"s}, {5201,"Foundation [15x12]"s},{5202,"Foundation [15x13]"s}, {5203,"Foundation [15x14]"s},{5204,"Foundation [15x15]"s}, {5205,"Foundation [15x16]"s},{5206,"Foundation [15x17]"s}, {5207,"Foundation [15x18]"s}, {5212,"Foundation [16x11]"s},{5213,"Foundation [16x12]"s}, {5214,"Foundation [16x13]"s},{5215,"Foundation [16x14]"s}, {5216,"Foundation [16x15]"s},{5217,"Foundation [16x16]"s}, {5218,"Foundation [16x17]"s},{5219,"Foundation [16x18]"s}, {5225,"Foundation [17x12]"s},{5226,"Foundation [17x13]"s}, {5227,"Foundation [17x14]"s},{5228,"Foundation [17x15]"s}, {5229,"Foundation [17x16]"s},{5230,"Foundation [17x17]"s}, {5231,"Foundation [17x18]"s}, {5238,"Foundation [18x13]"s},{5239,"Foundation [18x14]"s}, {5240,"Foundation [18x15]"s},{5241,"Foundation [18x16]"s}, {5242,"Foundation [18x17]"s},{5243,"Foundation [18x18]"s}, {7500,"New Player Quest Camp"s} }; //========================================================= // multicollection //========================================================= //========================================================= multicollection::multicollection(const std::filesystem::path &uodir,tileinfo *info ){ this->info = info ; if (!uodir.empty()){ load(uodir,info); } } //========================================================= auto multicollection::load(const std::filesystem::path &uodir,const std::string &housingbin,tileinfo *info) ->bool { _multis.clear(); auto multifile = uodir / std::filesystem::path("MultiCollection.uop"); auto hash = "build/multicollection/%.6u.bin"s; _housingbin = housingbin ; this->info = info ; return loadUOP(multifile.string(), 0x10000, hash); } //========================================================= auto multicollection::loadMul(const std::filesystem::path &uodir,tileinfo *info)->bool { _multis.clear(); this->info = info ; auto rvalue = false ; auto idxfile = uodir / std::filesystem::path("multi.idx") ; auto mulfile = uodir / std::filesystem::path("multi.mul") ; auto isHS = true ; if (std::filesystem::exists(mulfile)){ isHS = (std::filesystem::file_size(mulfile) >= hssize ? true:false); } auto idx = std::ifstream(idxfile, std::ios::binary); auto mul = std::ifstream(mulfile, std::ios::binary); if (mul.is_open() && idx.is_open()){ rvalue = true ; auto offset = std::uint32_t(0); auto length = std::uint32_t(0) ; auto mulid = 0; while (idx.good() && !idx.eof()){ idx.read(reinterpret_cast(&offset),4) ; idx.read(reinterpret_cast(&length),4) ; if (idx.gcount()== 4){ idx.seekg(4,std::ios::cur); if ((offset <= 0xFFFFFFFE) && (length >0) && (length < 0xFFFFFFFF)){ // This is a valid entry ; mul.seekg(offset,std::ios::beg) ; auto data = std::vector(length,0) ; mul.read(reinterpret_cast(data.data()),length) ; processData(isHS, mulid, data); } } mulid += 1 ; } } return rvalue ; } //========================================================= auto multicollection::load(const std::filesystem::path &uodir,tileinfo *info) ->bool { auto rvalue = false ; // First, see if the uop exists? auto multifile = uodir / std::filesystem::path("MultiCollection.uop"); if (std::filesystem::exists(multifile)) { // It does, so lets load the uop rvalue = load(uodir,"",info) ; } else { rvalue = loadMul(uodir,info); } return rvalue ; } //========================================================= auto multicollection::processHash(std::uint64_t hash,std::size_t entry , std::vector &data) ->bool { if (hash == 0x126D1E99DDEDEE0ALL){ if (!_housingbin.empty()){ auto output = std::ofstream(_housingbin,std::ios::binary); if (output.is_open()){ output.write(reinterpret_cast(data.data()),data.size()); } } return false ; // This is the hash for the housing.bin file } return true ; } //========================================================= auto multicollection::processEntry(std::size_t entry, std::size_t index, std::vector &data) ->bool { if (data.size()>8) { auto offset = std::uint32_t(4) ; // We skip the first 32 bit word (why offset is 4). // The next 32 bit word is the number of entries in this data ; auto count = std::int32_t(0) ; std::copy(data.data()+offset,data.data()+offset+4,reinterpret_cast(&count)); offset += 4; ; // we increment offfset now that we have read the count // at this point,we are now looking at the data for each entry ; // before we start looping, make some dummy read space we can get data // out of the vector auto flag = std::int16_t(0) ; auto clilocs = std::uint32_t(0) ; auto multi = collection_item() ; multi.items.resize(count); auto first = true ; for (auto entry=0 ; entry < count ; ++entry) { auto item = multi_item() ; // Get the tileid std::copy(data.begin()+offset, data.begin()+offset+2,reinterpret_cast(&item.tileid)); offset += 2 ; if (info){ item.info = &info->art(item.tileid); } // Now get the xoffset -- std::copy(data.begin()+offset, data.begin()+offset+2,reinterpret_cast(&flag)); item.offsetx = static_cast(flag) ; offset +=2 ; // Now get the yoffset std::copy(data.begin()+offset, data.begin()+offset+2,reinterpret_cast(&flag)); item.offsety = static_cast(flag) ; offset +=2 ; // Now get the z std::copy(data.begin()+offset, data.begin()+offset+2,reinterpret_cast(&flag)); item.altitude = static_cast(flag) ; offset +=2 ; // now we need to get the flag std::copy(data.begin()+offset, data.begin()+offset+2,reinterpret_cast(&flag)); offset +=2 ; switch (flag){ default: case 0: item.flag=1 ; break; case 1: break; case 257: item.flag = 0x800 ; break; } if (flag ==0){ if (first){ multi.max_y = item.offsety ; multi.min_y = item.offsety ; multi.max_x = item.offsetx ; multi.min_x = item.offsetx ; multi.max_alt = item.altitude ; multi.min_alt = item.altitude ; first = false ; } else { multi.max_y = std::max(item.offsety,multi.max_y); multi.min_y = std::max(item.offsety,multi.min_y); multi.max_x = std::max(item.offsetx,multi.max_x); multi.min_x = std::max(item.offsetx,multi.min_x); multi.max_alt = std::max(item.altitude,multi.max_alt); multi.min_alt = std::max(item.altitude,multi.min_alt); } } // we now have some clilocs to read past, have to find out how many std::copy(data.begin()+offset, data.begin()+offset+4,reinterpret_cast(&clilocs)); offset += 4 ; // Now skip past them; offset += (clilocs*4) ; multi.items[entry] = std::move(item) ; } _multis.insert_or_assign(static_cast(index), std::move(multi)); } return true ; } //======================================================== auto multicollection::processData(bool isHS,int index, std::vector &data) ->void { auto size = (isHS?16:12) ; auto count = static_cast(data.size()) / size ; auto multi = collection_item() ; auto first = true ; for (auto entry = 0; entry < count ; ++entry){ auto item = multi_item() ; std::copy(data.data()+(entry*size),data.data()+(entry*size)+2,reinterpret_cast(&item.tileid)); if (info) { item.info = &(info->art(item.tileid)); } auto value = std::int16_t(0) ; std::copy(data.data()+(entry*size)+2,data.data()+(entry*size)+2+2,reinterpret_cast(&value)); item.offsetx = value ; std::copy(data.data()+(entry*size)+4,data.data()+(entry*size)+4+2,reinterpret_cast(&value)); item.offsety = value ; std::copy(data.data()+(entry*size)+6,data.data()+(entry*size)+6+2,reinterpret_cast(&value)); item.altitude = value ; if (isHS) { auto flag = std::uint64_t(0); std::copy(data.data()+(entry*size)+8,data.data()+(entry*size)+8+8,reinterpret_cast(&flag)); item.flag = static_cast(flag) ; } else { auto flag = std::uint32_t(0); std::copy(data.data()+(entry*size)+4,data.data()+(entry*size)+8+4,reinterpret_cast(&flag)); item.flag = static_cast(flag) ; } if (first){ multi.max_y = item.offsety ; multi.min_y = item.offsety ; multi.max_x = item.offsetx ; multi.min_x = item.offsetx ; multi.max_alt = item.altitude ; multi.min_alt = item.altitude ; first = false ; } else { multi.max_y = std::max(item.offsety,multi.max_y); multi.min_y = std::max(item.offsety,multi.min_y); multi.max_x = std::max(item.offsetx,multi.max_x); multi.min_x = std::max(item.offsetx,multi.min_x); multi.max_alt = std::max(item.altitude,multi.max_alt); multi.min_alt = std::max(item.altitude,multi.min_alt); } multi.items.push_back(item); } _multis[index] = multi ; } //========================================================= auto multicollection::size() const ->size_t { return _multis.size(); } //========================================================= auto multicollection::multis() const -> const std::unordered_map& { return _multis; } //========================================================= auto multicollection::multis() -> std::unordered_map& { return _multis; } //========================================================= auto multicollection::multi(std::uint16_t multid) ->collection_item& { return _multis.at(static_cast(multid)) ; } //========================================================= auto multicollection::multi(std::uint16_t multid) const ->const collection_item&{ return _multis.at(static_cast(multid)) ; } //========================================================= auto multicollection::exists(std::uint16_t multid) const ->bool { auto rvalue = false ; auto iter = _multis.find(multid) ; if (iter != _multis.end()){ rvalue = true ;; } return rvalue ; }