#pragma once // Editing the two INI files a tower is configured by - config.ini and // hardware.ini - without breaking them. Pure functions on file contents, so // every rule is unit-testable; tools/fgc_config.cpp does the file IO around them. // // The rules exist because each has already broken a real file: // - inih SPLITS a line longer than its buffer instead of truncating it, and the // tail then parses as a bogus line of its own (see ini.h INI_MAX_LINE). A // generated comment of exactly 200 characters once took out a whole config. // - A value left in config.ini that hardware.ini also sets is dead, and warned // about at every start. A placeholder left there is worse: deploy.sh's guard // greps the raw text, so even a commented-out DEV_0000000000 trips it. #include #include #include #include #include namespace fgc { // The longest line content inih reads whole. Its buffer is INI_MAX_LINE bytes // and holds the newline and the terminator too. std::size_t iniMaxLineContent(); // 1-based number of the first line too long for inih to read whole, or 0. int firstOverlongIniLine(const std::string& contents); // "Camera.rgb.id" -> {"Camera.rgb", "id"}: the key is after the LAST dot, since // section names may contain dots and key names never do. nullopt when there is // no dot, or either side is empty. std::optional> splitIniKey(const std::string& flat); // Every key the contents set, as "Section.key" -> value, exactly as the program // reads them. Throws std::runtime_error naming the line if inih rejects it. std::map parseIniString(const std::string& contents); // Set "Section.key" = value pairs. Grouped per section and applied through // updateIniSectionKeys(), so comments and order survive, a missing key is // appended, and a non-empty `provenance` keeps one ";prev" generation per // changed key. Throws std::invalid_argument on a malformed key. std::string applyIniSets(const std::string& contents, const std::vector>& sets, const std::string& provenance); // Replace each live `key = value` line whose "Section.key" is in `drops` with a // one-line comment "; ", where note is the map's value. The VALUE is // deliberately not kept: a dead placeholder in a comment still trips deploy.sh's // raw-text guard, and a dead real value invites someone to "fix" the wrong file. // Returns the new contents; `dropped` (if given) receives what was replaced. std::string dropIniKeys(const std::string& contents, const std::map& drops, std::vector* dropped = nullptr); } // namespace fgc