#include "irons/gameFramework/Game.hpp" #include "nlohmann/json.hpp" #include #include namespace irons::gameFramework { NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Item,uid,itemId,count,x,y,rotated,borrowed) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Run,id,active,seed,stage,branch,biome,chassis,equipment,armor,energy,podUsed,objectiveDone,routeCompleted,upgrades,elapsed,escortArmor,cargo,safeCase,installed,destructionPending,searched) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Profile,version,format,warehouseWidth,warehouseHeight,warehouse,recoveryInbox,sorties,extractions,completed,unlocked,receipts,run) Result validate(const Profile& p)noexcept{ try{ if(p.version!=1)return Result::fail(Code::Unsupported); if(p.format!="grid-warehouse"||p.warehouseWidth<1||p.warehouseWidth>64||p.warehouseHeight<1||p.warehouseHeight>200||p.sorties<0||p.extractions<0||p.completed<0)return Result::fail(Code::Corrupt); const auto& r=p.run; if(r.chassis<0||r.chassis>2||r.stage<0||r.stage>4||!std::isfinite(r.armor)||!std::isfinite(r.energy)||!std::isfinite(r.elapsed)||!std::isfinite(r.escortArmor)||r.armor<0||r.energy<0||r.energy>100||r.elapsed<0)return Result::fail(Code::Corrupt); std::set ids; auto check=[&](const Item& i){return !i.uid.empty()&&ids.insert(i.uid).second&&Grid::known(i.itemId)&&i.count>0&&i.count<=(Grid::material(i.itemId)?10:1);}; for(const auto& i:p.warehouse)if(!check(i)||i.borrowed||!Grid::fits(p.warehouse,p.warehouseWidth,p.warehouseHeight,i,i.x,i.y,i.rotated))return Result::fail(Code::Corrupt); for(const auto& i:p.recoveryInbox)if(!check(i)||i.borrowed)return Result::fail(Code::Corrupt); for(const auto& i:r.cargo)if(!check(i)||!Grid::fits(r.cargo,4,4,i,i.x,i.y,i.rotated))return Result::fail(Code::Corrupt); for(const auto& i:r.safeCase)if(!check(i)||!Grid::fits(r.safeCase,2,2,i,i.x,i.y,i.rotated))return Result::fail(Code::Corrupt); std::set slots; for(const auto& i:r.installed){int slot=Grid::slot(i.itemId);if(!check(i)||slot<0||!slots.insert(slot).second||Grid::id(slot,r.equipment[slot])!=i.itemId)return Result::fail(Code::Corrupt);} if(r.active){if(r.id.empty())return Result::fail(Code::Corrupt);for(int s=0;s<4;s++)if(!slots.contains(s)&&r.equipment[s]!=-1)return Result::fail(Code::Corrupt);} else if(r.destructionPending||!r.cargo.empty()||!r.safeCase.empty()||!r.installed.empty())return Result::fail(Code::Corrupt); if(std::set(p.receipts.begin(),p.receipts.end()).size()!=p.receipts.size()||std::set(p.unlocked.begin(),p.unlocked.end()).size()!=p.unlocked.size())return Result::fail(Code::Corrupt); return {}; }catch(...){return Result::fail(Code::Corrupt);} } std::string encode(const Profile& profile){return nlohmann::json(profile).dump(2);} Result decode(std::string_view text,Profile& out)noexcept{ try{ const auto j=nlohmann::json::parse(text); if(!j.is_object()||!j.contains("format")||!j.contains("warehouseWidth")||!j.contains("recoveryInbox")||!j.contains("version"))return Result::fail(Code::Corrupt); Profile candidate=j.get();auto result=validate(candidate);if(result)out=std::move(candidate);return result; }catch(...){return Result::fail(Code::Corrupt);} } }