diff --git a/.gitignore b/.gitignore index 5410b14..3afad25 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ _deps .idea/ build/ -cmake-build-debug/ \ No newline at end of file +cmake-build-debug/ +cmake-build-debug-wsl/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index bc917c0..bea22bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ add_subdirectory(deps) set(CMAKE_CXX_STANDARD 20) set(PROJECT_SOURCES + src/api/api.cpp src/api/api.h src/cu_submitter.cpp src/chgen/chgen.cpp src/chgen/chgen.h src/data/changelog.cpp src/data/changelog.h @@ -14,11 +15,16 @@ set(PROJECT_SOURCES src/utils/print.cpp src/utils/print.h src/transfer/transfer.cpp src/transfer/transfer.h src/utils/utils.cpp src/utils/utils.h - src/submit/submit.cpp src/submit/submit.h) + src/submit/submit.cpp src/submit/submit.h +) + +include_directories(${CMAKE_BINARY_DIR}/_deps/rapidjson-src/include) add_executable(cu_submitter - ${PROJECT_SOURCES}) + ${PROJECT_SOURCES} +) target_link_libraries(cu_submitter lcf - pistache) + pistache +) diff --git a/PROTOCOL.md b/PROTOCOL.md new file mode 100644 index 0000000..8bb96c8 --- /dev/null +++ b/PROTOCOL.md @@ -0,0 +1,13 @@ +API Protocol +======== + +| Endpoint URL | Request type | Request data type | Response data type | Description | +|---------------------|--------------|-------------------|---------------------|----------------------------------------------------------------------------| +| / | GET | | string | Check if server is up | +| /chgen | POST | JSON | JSON | Scan builds for changes, returns changelog and exports it into a text file | +| /transfer | POST | JSON | JSON | Scan builds for transfer changelog, returns changelog | +| /transfer/confirm | POST | JSON | string | Executes transfer based on last scanned transfer changelog | +| /transfer/changelog | GET | | JSON | Returns last scanned transfer changelog | +| /submit | POST | JSON | JSON | Scan builds for submit changelog, returns changelog | +| /submit/confirm | GET | JSON | string | Executes submit based on last scanned submission changelog | +| /submit/changelog | GET | | JSON | Returns last scanned submit changelog | diff --git a/README.md b/README.md index 55c0cc7..a51c37c 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,12 @@ -# CU Submitter +CU Submitter +======== + A small application designed to help Collective Unconscious developers submit their updates -# Install required librairies +# Requirements Cmake > 3.14 -# Build +# Build instructions ``` cd git clone https://github.com/noahrav/cu_submitter.git @@ -19,6 +21,7 @@ The executable is build/cu_submitter ## CLI ./cu_submitter --help | --usage : prints the usage\ +./cu_submitter [-p ] : opens backend server on specific port; 3000 by default\ ./cu_submitter --chgen : generates a changelog text file\ ./cu_submitter --transfer : transfers the modified files to the destination path\ -./cu_submitter --submit [] : copy the modified files to a submission archive +./cu_submitter --submit [] : copy the modified files to a submission folder diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 2a0cffc..c73a019 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -2,6 +2,7 @@ include(FetchContent) set(LIBLCF_VERSION 0.8) set(PISTACHE_VERSION 0.0.5) +set(RAPIDJSON_VERSION 1.1.0) find_package(liblcf) @@ -15,8 +16,6 @@ if (NOT liblcf_FOUND) FetchContent_MakeAvailable(liblcf) endif() -#find_package(pistache) - if (NOT pistache_FOUND) FetchContent_Declare( pistache @@ -25,3 +24,10 @@ if (NOT pistache_FOUND) FetchContent_MakeAvailable(pistache) endif() + +FetchContent_Declare( + rapidjson + URL "https://github.com/Tencent/rapidjson/archive/v${RAPIDJSON_VERSION}.zip" +) + +FetchContent_MakeAvailable(rapidjson) diff --git a/src/api/api.cpp b/src/api/api.cpp new file mode 100644 index 0000000..073ebf1 --- /dev/null +++ b/src/api/api.cpp @@ -0,0 +1,317 @@ +#include "api.h" + +namespace CUSubmitterService { + + Service::Service(Pistache::Address addr) + : server(std::make_shared(addr)), + port(addr.port()) { + } + + void Service::run(size_t thr) { + log("Starting server on port " + port.toString() + " with " + std::to_string(thr) + " threads"); + + auto opts = Pistache::Http::Endpoint::options().threads(static_cast(thr)); + + server->init(opts); + + configureRoutes(); + server->setHandler(router.handler()); + + server->serve(); + } + + void Service::shutdown() { + log("Shutting server down"); + + server->shutdown(); + } + + void Service::configureRoutes() { + using namespace Pistache::Rest; + + Routes::Get(router, "/", Routes::bind(&Service::ready, this)); + Routes::Post(router, "/chgen", Routes::bind(&Service::generateChangelog, this)); + Routes::Post(router, "/transfer", Routes::bind(&Service::generateTransferChangelog, this)); + Routes::Post(router, "/transfer/confirm", Routes::bind(&Service::transfer, this)); + Routes::Get(router, "/transfer/changelog", Routes::bind(&Service::lastTransferChangelog, this)); + Routes::Post(router, "/submit", Routes::bind(&Service::generateSubmissionChangelog, this)); + Routes::Post(router, "/submit/confirm", Routes::bind(&Service::submit, this)); + Routes::Get(router, "/submit/changelog", Routes::bind(&Service::lastSubmissionChangelog, this)); + } + + void Service::logRequest(const Request &request) { + log("Received request from " + request.address().host() + ":" + std::to_string(request.address().port()) + " " + + Pistache::Http::methodString(request.method()) + " " + request.resource()); + } + + void Service::ready(const Request &request, Response response) { + try { + logRequest(request); + response.send(Pistache::Http::Code::Ok, "CU Submitter is up and running\n", MIME(Text, Plain)); + } catch (const std::runtime_error &e) { + log("Error: " + std::string(e.what())); + response.send(Pistache::Http::Code::Not_Found, e.what(), MIME(Text, Plain)); + } catch (const std::exception &e) { + log("Error: " + std::string(e.what())); + response.send(Pistache::Http::Code::Internal_Server_Error, e.what(), MIME(Text, Plain)); + } + } + + void Service::generateChangelog(const Service::Request &request, Service::Response response) { + try { + logRequest(request); + + const std::string& body = request.body(); + log("Raw content :\n" + body); + + rapidjson::Document document; + document.Parse(body.c_str()); + + if (!(document.HasMember("base_path") && document.HasMember("modified_path"))) { + error("Incorrect arguments"); + response.send(Pistache::Http::Code::Bad_Request); + return; + } + + const std::string base_path = document["base_path"].GetString(); + const std::string modified_path = document["modified_path"].GetString(); + + log("Parameter base_path : " + base_path); + log("Parameter modified_path : " + modified_path); + + const auto changelog = chgen::ChangelogGenerator::scan(base_path, modified_path); + if (changelog == nullptr) { + response.send(Pistache::Http::Code::Bad_Request, "Could not generate changelog", MIME(Text, Plain)); + return; + } + + chgen::ChangelogGenerator::generate(changelog); + + rapidjson::StringBuffer sb; + rapidjson::Writer writer(sb); + + changelog->Serialize(writer); + + const std::string string_changelog = sb.GetString(); + response.send(Pistache::Http::Code::Ok, string_changelog, MIME(Application, Json)); + } catch (const std::runtime_error &e) { + log("Error: " + std::string(e.what())); + response.send(Pistache::Http::Code::Not_Found, e.what(), MIME(Text, Plain)); + } catch (const std::exception &e) { + log("Error: " + std::string(e.what())); + response.send(Pistache::Http::Code::Internal_Server_Error, e.what(), MIME(Text, Plain)); + } + } + + void Service::generateTransferChangelog(const Service::Request &request, Service::Response response) { + try { + logRequest(request); + + const std::string& body = request.body(); + log("Raw content :\n" + body); + + rapidjson::Document document; + document.Parse(body.c_str()); + + if (!(document.HasMember("unmodified_copy_path") && document.HasMember("modified_copy_path"))) { + error("Incorrect arguments"); + response.send(Pistache::Http::Code::Bad_Request); + return; + } + + const std::string unmodified_copy_path = document["unmodified_copy_path"].GetString(); + const std::string modified_copy_path = document["modified_copy_path"].GetString(); + + log("Parameter unmodified_copy_path : " + unmodified_copy_path); + log("Parameter modified_copy_path : " + modified_copy_path); + + const auto changelog = transfer::DevbuildTransferer::getTransferChangelog(unmodified_copy_path, modified_copy_path); + if (changelog == nullptr) { + response.send(Pistache::Http::Code::Bad_Request, "Could not generate changelog", MIME(Text, Plain)); + return; + } + + rapidjson::StringBuffer sb; + rapidjson::Writer writer(sb); + + changelog->Serialize(writer); + + const std::string string_changelog = sb.GetString(); + response.send(Pistache::Http::Code::Ok, string_changelog, MIME(Application, Json)); + } catch (const std::runtime_error &e) { + log("Error: " + std::string(e.what())); + response.send(Pistache::Http::Code::Not_Found, e.what(), MIME(Text, Plain)); + } catch (const std::exception &e) { + log("Error: " + std::string(e.what())); + response.send(Pistache::Http::Code::Internal_Server_Error, e.what(), MIME(Text, Plain)); + } + } + + void Service::transfer(const Service::Request &request, Service::Response response) { + try { + logRequest(request); + + const std::string& body = request.body(); + log("Raw content :\n" + body); + + rapidjson::Document document; + document.Parse(body.c_str()); + + if (!document.HasMember("destination_path")) { + error("Incorrect arguments"); + response.send(Pistache::Http::Code::Bad_Request); + return; + } + + const std::string destination_path = document["destination_path"].GetString(); + + log("Parameter destination_path : " + destination_path); + + transfer::DevbuildTransferer::transfer(destination_path); + transfer::DevbuildTransferer::exportChangelog(); + + response.send(Pistache::Http::Code::Ok); + } catch (const std::runtime_error &e) { + log("Error: " + std::string(e.what())); + response.send(Pistache::Http::Code::Not_Found, e.what(), MIME(Text, Plain)); + } catch (const std::exception &e) { + log("Error: " + std::string(e.what())); + response.send(Pistache::Http::Code::Internal_Server_Error, e.what(), MIME(Text, Plain)); + } + } + + void Service::lastTransferChangelog(const Service::Request &request, Service::Response response) { + try { + logRequest(request); + + const auto changelog = transfer::DevbuildTransferer::getTransferChangelog(); + if (changelog == nullptr) { + response.send(Pistache::Http::Code::Bad_Request, "Could not generate changelog", MIME(Text, Plain)); + return; + } + + rapidjson::StringBuffer sb; + rapidjson::Writer writer(sb); + + changelog->Serialize(writer); + + const std::string string_changelog = sb.GetString(); + response.send(Pistache::Http::Code::Ok, string_changelog, MIME(Application, Json)); + } catch (const std::runtime_error &e) { + log("Error: " + std::string(e.what())); + response.send(Pistache::Http::Code::Not_Found, e.what(), MIME(Text, Plain)); + } catch (const std::exception &e) { + log("Error: " + std::string(e.what())); + response.send(Pistache::Http::Code::Internal_Server_Error, e.what(), MIME(Text, Plain)); + } + } + + void Service::generateSubmissionChangelog(const Service::Request &request, Service::Response response) { + try { + logRequest(request); + + const std::string& body = request.body(); + log("Raw content :\n" + body); + + rapidjson::Document document; + document.Parse(body.c_str()); + + if (!(document.HasMember("unmodified_copy_path") && document.HasMember("modified_copy_path"))) { + error("Incorrect arguments"); + response.send(Pistache::Http::Code::Bad_Request); + return; + } + + const std::string unmodified_copy_path = document["unmodified_copy_path"].GetString(); + const std::string modified_copy_path = document["modified_copy_path"].GetString(); + std::string archive_path; + + log("Parameter unmodified_copy_path : " + unmodified_copy_path); + log("Parameter modified_copy_path : " + modified_copy_path); + + if(document.HasMember("archive_path")) { + archive_path = document["archive_path"].GetString(); + log("Parameter archive_path : " + archive_path); + } + + const auto changelog = submit::SubmissionBuilder::getSubmissionChangelog(unmodified_copy_path, modified_copy_path); + if (changelog == nullptr) { + response.send(Pistache::Http::Code::Bad_Request, "Could not generate changelog", MIME(Text, Plain)); + return; + } + + rapidjson::StringBuffer sb; + rapidjson::Writer writer(sb); + + changelog->Serialize(writer); + + const std::string string_changelog = sb.GetString(); + response.send(Pistache::Http::Code::Ok, string_changelog, MIME(Application, Json)); + } catch (const std::runtime_error &e) { + log("Error: " + std::string(e.what())); + response.send(Pistache::Http::Code::Not_Found, e.what(), MIME(Text, Plain)); + } catch (const std::exception &e) { + log("Error: " + std::string(e.what())); + response.send(Pistache::Http::Code::Internal_Server_Error, e.what(), MIME(Text, Plain)); + } + } + + void Service::submit(const Service::Request &request, Service::Response response) { + try { + logRequest(request); + + const std::string& body = request.body(); + log("Raw content :\n" + body); + + rapidjson::Document document; + document.Parse(body.c_str()); + + std::string archive_path; + + if(document.HasMember("archive_path")) { + archive_path = document["archive_path"].GetString(); + log("Parameter archive_path : " + archive_path); + + submit::SubmissionBuilder::submit(archive_path); + } else { + submit::SubmissionBuilder::submit(); + } + + response.send(Pistache::Http::Code::Ok); + } catch (const std::runtime_error &e) { + log("Error: " + std::string(e.what())); + response.send(Pistache::Http::Code::Not_Found, e.what(), MIME(Text, Plain)); + } catch (const std::exception &e) { + log("Error: " + std::string(e.what())); + response.send(Pistache::Http::Code::Internal_Server_Error, e.what(), MIME(Text, Plain)); + } + } + + void Service::lastSubmissionChangelog(const Service::Request &request, Service::Response response) { + try { + logRequest(request); + + const auto changelog = submit::SubmissionBuilder::getSubmissionChangelog(); + if (changelog == nullptr) { + response.send(Pistache::Http::Code::Bad_Request, "Could not generate changelog", MIME(Text, Plain)); + return; + } + + rapidjson::StringBuffer sb; + rapidjson::Writer writer(sb); + + changelog->Serialize(writer); + + const std::string string_changelog = sb.GetString(); + response.send(Pistache::Http::Code::Ok, string_changelog, MIME(Application, Json)); + } catch (const std::runtime_error &e) { + log("Error: " + std::string(e.what())); + response.send(Pistache::Http::Code::Not_Found, e.what(), MIME(Text, Plain)); + } catch (const std::exception &e) { + log("Error: " + std::string(e.what())); + response.send(Pistache::Http::Code::Internal_Server_Error, e.what(), MIME(Text, Plain)); + } + } + +} // CUSubmitterService + diff --git a/src/api/api.h b/src/api/api.h new file mode 100644 index 0000000..a4148d0 --- /dev/null +++ b/src/api/api.h @@ -0,0 +1,50 @@ +#ifndef CU_SUBMITTER_API_H +#define CU_SUBMITTER_API_H + +#include +#include +#include +#include +#include + +#include "../chgen/chgen.h" +#include "../data/changelog.h" +#include "../transfer/transfer.h" +#include "../submit/submit.h" +#include "../utils/log.h" + +namespace CUSubmitterService { + + class Service { + public: + explicit Service(Pistache::Address addr); + + void run(size_t thr = std::thread::hardware_concurrency()); + + void shutdown(); + + private: + void configureRoutes(); + + using Request = Pistache::Rest::Request; + using Response = Pistache::Http::ResponseWriter; + + void ready(const Request& request, Response response); + void generateChangelog(const Request& request, Response response); + void generateTransferChangelog(const Request& request, Response response); + void transfer(const Request& request, Response response); + void lastTransferChangelog(const Request& request, Response response); + void generateSubmissionChangelog(const Request& request, Response response); + void submit(const Request& request, Response response); + void lastSubmissionChangelog(const Request& request, Response response); + + static void logRequest(const Request& request); + + std::shared_ptr server; + Pistache::Rest::Router router; + Pistache::Port port; + }; + +} // CUSubmitterService + +#endif //CU_SUBMITTER_API_H \ No newline at end of file diff --git a/src/cu_submitter.cpp b/src/cu_submitter.cpp index abfef12..21ab14f 100644 --- a/src/cu_submitter.cpp +++ b/src/cu_submitter.cpp @@ -1,31 +1,30 @@ #include -#include +#include "api/api.h" #include "chgen/chgen.h" #include "transfer/transfer.h" #include "submit/submit.h" #include "utils/error.h" -#include "utils/log.h" #include "utils/print.h" /** * @program cu_submitter * @brief The main entry point for the program. */ - int main(int argc, char* argv[]) { - if (argc >= 2) { + if (argc >= 2 && std::string(argv[1]) != "-p") { //CLI mode const std::string option = argv[1]; if (option == "--help" || option == "--usage") { std::string usage_message = "USAGE\n"; usage_message += "-----\n"; + usage_message += "[-p ] : opens backend server on specific port; 3000 by default\n"; usage_message += "--help | --usage : prints this message\n"; usage_message += "--chgen : generates a changelog text file\n"; usage_message += "--transfer : transfers the modified files to the destination path\n"; - usage_message += "--submit [] : compress the modified files to a submission archive\n"; + usage_message += "--submit [] : copy the modified files to a submission folder\n"; print(usage_message); } else if (option == "--chgen") { @@ -119,9 +118,17 @@ int main(int argc, char* argv[]) return 0; } + std::string port = "3000"; + if (argc >= 2 && std::string(argv[1]) == "-p" && argc < 3) { + error("Invalid arguments"); + return 1; + } else if (argc >= 2 && std::string(argv[1]) == "-p") { + port = argv[2]; + } + Pistache::Address addr(Pistache::Ipv4::any(), Pistache::Port(stoi(port))); - - return 0; + CUSubmitterService::Service service(addr); + service.run(); } diff --git a/src/data/changelog.cpp b/src/data/changelog.cpp index 1326062..49bdd38 100644 --- a/src/data/changelog.cpp +++ b/src/data/changelog.cpp @@ -6,6 +6,18 @@ namespace data { return "(" + std::to_string(x) + "," + std::to_string(y) + ")"; } + void Coordinates::Serialize(Writer& writer) const { + writer.StartObject(); + + writer.String("x"); + writer.Int(x); + + writer.String("y"); + writer.Int(y); + + writer.EndObject(); + } + std::string status_string(Status status) { switch (status) { case ADDED: @@ -32,14 +44,56 @@ namespace data { return s; } + void BGMEvent::Serialize(Writer& writer) const { + writer.StartObject(); + + writer.String("coordinates"); + coordinates_.Serialize(writer); + + writer.String("track_name"); + serializeString(writer, track_name_); + + writer.String("volume"); + writer.Int(volume_); + + writer.String("speed"); + writer.Int(speed_); + + writer.EndObject(); + } + std::string OpenConnection::stringify() { return status_string(status_) + " Open connection at " + coordinates_.stringify(); } + void OpenConnection::Serialize(Writer& writer) const { + writer.StartObject(); + + writer.String("status"); + serializeString(writer, status_string(status_)); + + writer.String("coordinates"); + coordinates_.Serialize(writer); + + writer.EndObject(); + } + std::string ClosedConnection::stringify() { return status_string(status_) + " Closed connection at " + coordinates_.stringify(); } + void ClosedConnection::Serialize(Writer& writer) const { + writer.StartObject(); + + writer.String("status"); + serializeString(writer, status_string(status_)); + + writer.String("coordinates"); + coordinates_.Serialize(writer); + + writer.EndObject(); + } + std::string id_string(unsigned int id) { std::string s; @@ -89,6 +143,81 @@ namespace data { return s; } + void serializeMusic(Writer& writer, const lcf::rpg::Music& music) { + writer.StartObject(); + + writer.String("name"); + serializeString(writer, music.name); + + writer.String("fadein"); + writer.Int(music.fadein); + + writer.String("volume"); + writer.Int(music.volume); + + writer.String("tempo"); + writer.Int(music.tempo); + + writer.String("balance"); + writer.Int(music.balance); + + writer.EndObject(); + } + + void Map::Serialize(Writer& writer) const { + writer.StartObject(); + + writer.String("id"); + writer.Uint(id_); + + writer.String("status"); + serializeString(writer, status_string(status_)); + + writer.String("name"); + serializeString(writer, name_); + + writer.String("notes"); + writer.StartArray(); + + for (auto ¬e: notes_) { + serializeString(writer, note); + } + + writer.EndArray(); + + writer.String("bgm_events"); + writer.StartArray(); + + for (auto &bgm_event: bgm_events_) { + bgm_event.Serialize(writer); + } + + writer.EndArray(); + + writer.String("open_connections"); + writer.StartArray(); + + for (auto &open_connection: open_connections_) { + open_connection.Serialize(writer); + } + + writer.EndArray(); + + writer.String("closed_connections"); + writer.StartArray(); + + for (auto &closed_connection: closed_connections_) { + closed_connection.Serialize(writer); + } + + writer.EndArray(); + + writer.String("main_music"); + serializeMusic(writer, main_music_); + + writer.EndObject(); + } + std::string connection_type_string(ConnectionType connection_type) { switch (connection_type) { case ONEWAY: @@ -118,6 +247,39 @@ namespace data { return s; } + void Connection::Serialize(Writer& writer) const { + writer.StartObject(); + + writer.String("status"); + serializeString(writer, status_string(status_)); + + writer.String("from_map"); + from_map_.Serialize(writer); + + writer.String("from_coordinates"); + from_coordinates_.Serialize(writer); + + writer.String("to_map"); + to_map_.Serialize(writer); + + writer.String("to_coordinates"); + to_coordinates_.Serialize(writer); + + writer.String("type"); + serializeString(writer, connection_type_string(type_)); + + writer.String("notes"); + writer.StartArray(); + + for (auto ¬e: notes_) { + serializeString(writer, note); + } + + writer.EndArray(); + + writer.EndObject(); + } + std::string CommonEvent::stringify() { std::string s = status_string(status_) + " CE[" + id_string(id_) + "] - " + name_; @@ -130,6 +292,30 @@ namespace data { return s; } + void CommonEvent::Serialize(Writer& writer) const { + writer.StartObject(); + + writer.String("status"); + serializeString(writer, status_string(status_)); + + writer.String("id"); + writer.Uint(id_); + + writer.String("name"); + serializeString(writer, name_); + + writer.String("notes"); + writer.StartArray(); + + for (auto ¬e: notes_) { + serializeString(writer, note); + } + + writer.EndArray(); + + writer.EndObject(); + } + std::string TilesetInfo::stringify() { std::string s = status_string(status_) + " Tileset[" + id_string(id_) + "] - " + name_; @@ -146,6 +332,33 @@ namespace data { return s; } + void TilesetInfo::Serialize(Writer& writer) const { + writer.StartObject(); + + writer.String("status"); + serializeString(writer, status_string(status_)); + + writer.String("id"); + writer.Uint(id_); + + writer.String("name"); + serializeString(writer, name_); + + writer.String("chipset_name"); + serializeString(writer, chipset_name_); + + writer.String("notes"); + writer.StartArray(); + + for (auto ¬e: notes_) { + serializeString(writer, note); + } + + writer.EndArray(); + + writer.EndObject(); + } + std::string Switch::stringify() { std::string s = status_string(status_) + " Switch[" + id_string(id_) + "] - " + name_; @@ -158,6 +371,30 @@ namespace data { return s; } + void Switch::Serialize(Writer& writer) const { + writer.StartObject(); + + writer.String("status"); + serializeString(writer, status_string(status_)); + + writer.String("id"); + writer.Uint(id_); + + writer.String("name"); + serializeString(writer, name_); + + writer.String("notes"); + writer.StartArray(); + + for (auto ¬e: notes_) { + serializeString(writer, note); + } + + writer.EndArray(); + + writer.EndObject(); + } + std::string Variable::stringify() { std::string s = status_string(status_) + " Variable[" + id_string(id_) + "] - " + name_; @@ -170,6 +407,30 @@ namespace data { return s; } + void Variable::Serialize(Writer& writer) const { + writer.StartObject(); + + writer.String("status"); + serializeString(writer, status_string(status_)); + + writer.String("id"); + writer.Uint(id_); + + writer.String("name"); + serializeString(writer, name_); + + writer.String("notes"); + writer.StartArray(); + + for (auto ¬e: notes_) { + serializeString(writer, note); + } + + writer.EndArray(); + + writer.EndObject(); + } + std::string Animation::stringify() { std::string s = status_string(status_) + " Animation[" + id_string(id_) + "] - " + name_; @@ -186,6 +447,33 @@ namespace data { return s; } + void Animation::Serialize(Writer& writer) const { + writer.StartObject(); + + writer.String("status"); + serializeString(writer, status_string(status_)); + + writer.String("id"); + writer.Uint(id_); + + writer.String("name"); + serializeString(writer, name_); + + writer.String("animation_name"); + serializeString(writer, animation_name_); + + writer.String("notes"); + writer.StartArray(); + + for (auto ¬e: notes_) { + serializeString(writer, note); + } + + writer.EndArray(); + + writer.EndObject(); + } + std::string Asset::stringify() { std::string s = status_string(status_); @@ -231,6 +519,35 @@ namespace data { return s; } + void Asset::Serialize(Writer& writer) const { + writer.StartObject(); + + writer.String("status"); + serializeString(writer, status_string(status_)); + + // We ignore the category because it is already indicated in the JSON structure. + + writer.String("name"); + serializeString(writer, name_); + + writer.String("filename"); + serializeString(writer, filename_); + + writer.String("notes"); + writer.StartArray(); + + for (auto ¬e: notes_) { + serializeString(writer, note); + } + + writer.EndArray(); + + writer.String("contributors"); + serializeString(writer, contributors_); + + writer.EndObject(); + } + std::string date_string(tm* date) { std::string s; @@ -284,6 +601,21 @@ namespace data { return s; } + void serializeDate(Writer& writer, tm* date) { + writer.StartObject(); + + writer.String("day"); + writer.Int(date->tm_mday); + + writer.String("month"); + writer.Int(date->tm_mon + 1); + + writer.String("year"); + writer.Int(date->tm_year + 1900); + + writer.EndObject(); + } + std::string Changelog::stringify() { const std::string separator = "---------------------------------------------------\n"; @@ -443,4 +775,164 @@ namespace data { return s; } + void Changelog::Serialize(Writer& writer) const { + writer.StartObject(); + + writer.String("developer"); + serializeString(writer, developer_); + + writer.String("date"); + serializeDate(writer, date_); + + writer.String("summary"); + serializeString(writer, summary_); + + writer.String("map_policy"); + serializeString(writer, map_policy_); + + writer.String("asset_policy"); + serializeString(writer, asset_policy_); + + writer.String("maps"); + writer.StartArray(); + + for (auto &map: maps_) { + map.Serialize(writer); + } + + writer.EndArray(); + + writer.String("connections"); + writer.StartArray(); + + for (auto &connection: connections_) { + connection.Serialize(writer); + } + + writer.EndArray(); + + writer.String("common_events"); + writer.StartArray(); + + for (auto &common_event: common_events_) { + common_event.Serialize(writer); + } + + writer.EndArray(); + + writer.String("tilesets"); + writer.StartArray(); + + for (auto &tileset: tilesets_) { + tileset.Serialize(writer); + } + + writer.EndArray(); + + writer.String("switches"); + writer.StartArray(); + + for (auto &sw: switches_) { + sw.Serialize(writer); + } + + writer.EndArray(); + + writer.String("variables"); + writer.StartArray(); + + for (auto &var: variables_) { + var.Serialize(writer); + } + + writer.EndArray(); + + writer.String("animations"); + writer.StartArray(); + + for (auto &animation: animations_) { + animation.Serialize(writer); + } + + writer.EndArray(); + + writer.String("menu_themes"); + writer.StartArray(); + + for (auto &menu_theme: menu_themes_) { + menu_theme.Serialize(writer); + } + + writer.EndArray(); + + writer.String("charsets"); + writer.StartArray(); + + for (auto &charset: charsets_) { + charset.Serialize(writer); + } + + writer.EndArray(); + + writer.String("chipsets"); + writer.StartArray(); + + for (auto &chipset: chipsets_) { + chipset.Serialize(writer); + } + + writer.EndArray(); + + writer.String("musics"); + writer.StartArray(); + + for (auto &music: musics_) { + music.Serialize(writer); + } + + writer.EndArray(); + + writer.String("sounds"); + writer.StartArray(); + + for (auto &sound: sounds_) { + sound.Serialize(writer); + } + + writer.EndArray(); + + writer.String("panoramas"); + writer.StartArray(); + + for (auto &panorama: panoramas_) { + panorama.Serialize(writer); + } + + writer.EndArray(); + + writer.String("pictures"); + writer.StartArray(); + + for (auto &picture: pictures_) { + picture.Serialize(writer); + } + + writer.EndArray(); + + writer.String("animation_files"); + writer.StartArray(); + + for (auto &animation: animation_files_) { + animation.Serialize(writer); + } + + writer.EndArray(); + + writer.EndObject(); + } + + void serializeString(Writer& writer, const std::string& str) { + writer.String(str.c_str(), static_cast(str.length())); + } + } // data \ No newline at end of file diff --git a/src/data/changelog.h b/src/data/changelog.h index 4036c50..c371404 100644 --- a/src/data/changelog.h +++ b/src/data/changelog.h @@ -5,9 +5,15 @@ #include #include #include +#include +#include +#include + namespace data { + using Writer = rapidjson::Writer; + /** * @brief Data structure used to represent wether an entry was added, removed or modified. */ @@ -25,12 +31,15 @@ namespace data { int y; std::string stringify(); + + void Serialize(Writer &writer) const; }; - inline bool operator==(const Coordinates& lhs, const Coordinates& rhs) { + inline bool operator==(const Coordinates &lhs, const Coordinates &rhs) { return lhs.x == rhs.x && lhs.y == rhs.y; } - inline bool operator!=(const Coordinates& lhs, const Coordinates& rhs) { + + inline bool operator!=(const Coordinates &lhs, const Coordinates &rhs) { return !(lhs == rhs); } @@ -45,12 +54,16 @@ namespace data { int speed_; std::string stringify(); + + void Serialize(Writer &writer) const; }; - inline bool operator==(const BGMEvent& lhs, const BGMEvent& rhs) { - return lhs.coordinates_ == rhs.coordinates_ && lhs.track_name_ == rhs.track_name_ && lhs.volume_ == rhs.volume_ && lhs.speed_ == rhs.speed_; + inline bool operator==(const BGMEvent &lhs, const BGMEvent &rhs) { + return lhs.coordinates_ == rhs.coordinates_ && lhs.track_name_ == rhs.track_name_ && + lhs.volume_ == rhs.volume_ && lhs.speed_ == rhs.speed_; } - inline bool operator!=(const BGMEvent& lhs, const BGMEvent& rhs) { + + inline bool operator!=(const BGMEvent &lhs, const BGMEvent &rhs) { return !(lhs == rhs); } @@ -62,12 +75,15 @@ namespace data { Coordinates coordinates_; std::string stringify(); + + void Serialize(Writer &writer) const; }; - inline bool operator==(const OpenConnection& lhs, const OpenConnection& rhs) { + inline bool operator==(const OpenConnection &lhs, const OpenConnection &rhs) { return lhs.coordinates_ == rhs.coordinates_; } - inline bool operator!=(const OpenConnection& lhs, const OpenConnection& rhs) { + + inline bool operator!=(const OpenConnection &lhs, const OpenConnection &rhs) { return !(lhs == rhs); } @@ -79,12 +95,15 @@ namespace data { Coordinates coordinates_; std::string stringify(); + + void Serialize(Writer &writer) const; }; - inline bool operator==(const ClosedConnection& lhs, const ClosedConnection& rhs) { + inline bool operator==(const ClosedConnection &lhs, const ClosedConnection &rhs) { return lhs.coordinates_ == rhs.coordinates_; } - inline bool operator!=(const ClosedConnection& lhs, const ClosedConnection& rhs) { + + inline bool operator!=(const ClosedConnection &lhs, const ClosedConnection &rhs) { return !(lhs == rhs); } @@ -95,6 +114,8 @@ namespace data { */ std::string id_string(unsigned int id); + void serializeMusic(Writer& writer, const lcf::rpg::Music& music); + /** * @brief Data structure used to represent a map. */ @@ -111,12 +132,17 @@ namespace data { lcf::rpg::Music main_music_; std::string stringify(); + + void Serialize(Writer &writer) const; }; - inline bool operator==(const Map& lhs, const Map& rhs) { - return lhs.id_ == rhs.id_ && lhs.name_ == rhs.name_ && lhs.bgm_events_ == rhs.bgm_events_ && lhs.open_connections_ == rhs.open_connections_ && lhs.closed_connections_ == rhs.closed_connections_ && lhs.main_music_ == rhs.main_music_; + inline bool operator==(const Map &lhs, const Map &rhs) { + return lhs.id_ == rhs.id_ && lhs.name_ == rhs.name_ && lhs.bgm_events_ == rhs.bgm_events_ && + lhs.open_connections_ == rhs.open_connections_ && lhs.closed_connections_ == rhs.closed_connections_ && + lhs.main_music_ == rhs.main_music_; } - inline bool operator!=(const Map& lhs, const Map& rhs) { + + inline bool operator!=(const Map &lhs, const Map &rhs) { return !(lhs == rhs); } @@ -135,7 +161,6 @@ namespace data { */ struct Connection { Status status_; - std::vector notes_; Map from_map_; Coordinates from_coordinates_; @@ -145,13 +170,19 @@ namespace data { ConnectionType type_; + std::vector notes_; + std::string stringify(); + + void Serialize(Writer &writer) const; }; - inline bool operator==(const Connection& lhs, const Connection& rhs) { - return lhs.from_map_ == rhs.from_map_ && lhs.from_coordinates_ == rhs.from_coordinates_ && lhs.to_map_ == rhs.to_map_ && lhs.to_coordinates_ == rhs.to_coordinates_ && lhs.type_ == rhs.type_; + inline bool operator==(const Connection &lhs, const Connection &rhs) { + return lhs.from_map_ == rhs.from_map_ && lhs.from_coordinates_ == rhs.from_coordinates_ && + lhs.to_map_ == rhs.to_map_ && lhs.to_coordinates_ == rhs.to_coordinates_ && lhs.type_ == rhs.type_; } - inline bool operator!=(const Connection& lhs, const Connection& rhs) { + + inline bool operator!=(const Connection &lhs, const Connection &rhs) { return !(lhs == rhs); } @@ -165,12 +196,15 @@ namespace data { std::vector notes_; std::string stringify(); + + void Serialize(Writer &writer) const; }; - inline bool operator==(const CommonEvent& lhs, const CommonEvent& rhs) { + inline bool operator==(const CommonEvent &lhs, const CommonEvent &rhs) { return lhs.id_ == rhs.id_ && lhs.name_ == rhs.name_; } - inline bool operator!=(const CommonEvent& lhs, const CommonEvent& rhs) { + + inline bool operator!=(const CommonEvent &lhs, const CommonEvent &rhs) { return !(lhs == rhs); } @@ -185,12 +219,15 @@ namespace data { std::vector notes_; std::string stringify(); + + void Serialize(Writer &writer) const; }; - inline bool operator==(const TilesetInfo& lhs, const TilesetInfo& rhs) { + inline bool operator==(const TilesetInfo &lhs, const TilesetInfo &rhs) { return lhs.id_ == rhs.id_ && lhs.name_ == rhs.name_ && lhs.chipset_name_ == rhs.chipset_name_; } - inline bool operator!=(const TilesetInfo& lhs, const TilesetInfo& rhs) { + + inline bool operator!=(const TilesetInfo &lhs, const TilesetInfo &rhs) { return !(lhs == rhs); } @@ -204,12 +241,15 @@ namespace data { std::vector notes_; std::string stringify(); + + void Serialize(Writer &writer) const; }; - inline bool operator==(const Switch& lhs, const Switch& rhs) { + inline bool operator==(const Switch &lhs, const Switch &rhs) { return lhs.id_ == rhs.id_ && lhs.name_ == rhs.name_; } - inline bool operator!=(const Switch& lhs, const Switch& rhs) { + + inline bool operator!=(const Switch &lhs, const Switch &rhs) { return !(lhs == rhs); } @@ -223,12 +263,15 @@ namespace data { std::vector notes_; std::string stringify(); + + void Serialize(Writer &writer) const; }; - inline bool operator==(const Variable& lhs, const Variable& rhs) { + inline bool operator==(const Variable &lhs, const Variable &rhs) { return lhs.id_ == rhs.id_ && lhs.name_ == rhs.name_; } - inline bool operator!=(const Variable& lhs, const Variable& rhs) { + + inline bool operator!=(const Variable &lhs, const Variable &rhs) { return !(lhs == rhs); } @@ -243,12 +286,15 @@ namespace data { std::vector notes_; std::string stringify(); + + void Serialize(Writer &writer) const; }; - inline bool operator==(const Animation& lhs, const Animation& rhs) { + inline bool operator==(const Animation &lhs, const Animation &rhs) { return lhs.id_ == rhs.id_ && lhs.name_ == rhs.name_ && lhs.animation_name_ == rhs.animation_name_; } - inline bool operator!=(const Animation& lhs, const Animation& rhs) { + + inline bool operator!=(const Animation &lhs, const Animation &rhs) { return !(lhs == rhs); } @@ -280,12 +326,15 @@ namespace data { std::string contributors_; std::string stringify(); + + void Serialize(Writer &writer) const; }; - inline bool operator==(const Asset& lhs, const Asset& rhs) { + inline bool operator==(const Asset &lhs, const Asset &rhs) { return lhs.category_ == rhs.category_ && lhs.name_ == rhs.name_; } - inline bool operator!=(const Asset& lhs, const Asset& rhs) { + + inline bool operator!=(const Asset &lhs, const Asset &rhs) { return !(lhs == rhs); } @@ -294,20 +343,22 @@ namespace data { * @param date * @return A string representation of the date. */ - std::string date_string(tm* date); + std::string date_string(tm *date); + + void serializeDate(Writer& writer, tm* date); /** * @brief Data structure used to generate the changelog. */ struct Changelog { - /** - * @brief Name of the developer - */ + /** + * @brief Name of the developer + */ std::string developer_; /** * @brief Date of submit */ - tm* date_{}; + tm *date_{}; /** * @brief Summary of the submit @@ -399,17 +450,28 @@ namespace data { std::string stringify(); + void Serialize(Writer &writer) const; + Changelog() = default; }; - inline bool operator==(const Changelog& lhs, const Changelog& rhs) { - return lhs.developer_ == rhs.developer_ && lhs.summary_ == rhs.summary_ && lhs.map_policy_ == rhs.map_policy_ && lhs.asset_policy_ == rhs.asset_policy_ && lhs.maps_ == rhs.maps_ && lhs.connections_ == rhs.connections_ && lhs.common_events_ == rhs.common_events_ && lhs.tilesets_ == rhs.tilesets_ && lhs.switches_ == rhs.switches_ && lhs.variables_ == rhs.variables_ && lhs.animations_ == rhs.animations_ && lhs.menu_themes_ == rhs.menu_themes_ && lhs.charsets_ == rhs.charsets_ && lhs.chipsets_ == rhs.chipsets_ && lhs.musics_ == rhs.musics_ && lhs.sounds_ == rhs.sounds_ && lhs.panoramas_ == rhs.panoramas_ && lhs.pictures_ == rhs.pictures_ && lhs.animation_files_ == rhs.animation_files_; + inline bool operator==(const Changelog &lhs, const Changelog &rhs) { + return lhs.developer_ == rhs.developer_ && lhs.summary_ == rhs.summary_ && lhs.map_policy_ == rhs.map_policy_ && + lhs.asset_policy_ == rhs.asset_policy_ && lhs.maps_ == rhs.maps_ && + lhs.connections_ == rhs.connections_ && lhs.common_events_ == rhs.common_events_ && + lhs.tilesets_ == rhs.tilesets_ && lhs.switches_ == rhs.switches_ && lhs.variables_ == rhs.variables_ && + lhs.animations_ == rhs.animations_ && lhs.menu_themes_ == rhs.menu_themes_ && + lhs.charsets_ == rhs.charsets_ && lhs.chipsets_ == rhs.chipsets_ && lhs.musics_ == rhs.musics_ && + lhs.sounds_ == rhs.sounds_ && lhs.panoramas_ == rhs.panoramas_ && lhs.pictures_ == rhs.pictures_ && + lhs.animation_files_ == rhs.animation_files_; } - inline bool operator!=(const Changelog& lhs, const Changelog& rhs) { + + inline bool operator!=(const Changelog &lhs, const Changelog &rhs) { return !(lhs == rhs); } -} + void serializeString(Writer& writer, const std::string& str); +} // data #endif //CU_SUBMITTER_CHANGELOG_H diff --git a/src/utils/error.cpp b/src/utils/error.cpp index 2eb34df..5846507 100644 --- a/src/utils/error.cpp +++ b/src/utils/error.cpp @@ -1,5 +1,21 @@ #include "error.h" +inline std::string digit_to_string(int digit) { + std::string str; + if (digit < 10) { + str += '0'; + } + + return str + std::to_string(digit); +} + void error(const std::string& message) { - std::cerr << "Error: " << message << '\n'; + auto now = time(nullptr); + const auto date = localtime(&now); + + std::string date_string = "[" + digit_to_string(date->tm_mday) + "/" + digit_to_string(date->tm_mon + 1) + "/" + std::to_string(date->tm_year + 1900); + date_string += " - " + digit_to_string(date->tm_hour) + ":" + digit_to_string(date->tm_min) + ":" + digit_to_string(date->tm_sec) + "] "; + + + std::cerr << date_string + "Error: " + message << '\n'; } diff --git a/src/utils/log.cpp b/src/utils/log.cpp index d32a4c9..2b47932 100644 --- a/src/utils/log.cpp +++ b/src/utils/log.cpp @@ -1,5 +1,20 @@ #include "log.h" +inline std::string digit_to_string(int digit) { + std::string str; + if (digit < 10) { + str += '0'; + } + + return str + std::to_string(digit); +} + void log(const std::string& message) { - std::cout << " - " << message << '\n'; + auto now = time(nullptr); + const auto date = localtime(&now); + + std::string date_string = "[" + digit_to_string(date->tm_mday) + "/" + digit_to_string(date->tm_mon + 1) + "/" + std::to_string(date->tm_year + 1900); + date_string += " - " + digit_to_string(date->tm_hour) + ":" + digit_to_string(date->tm_min) + ":" + digit_to_string(date->tm_sec) + "] "; + + std::cout << date_string + message << '\n'; }