diff --git a/source/app.d b/source/app.d index ac9a020..e3946a5 100644 --- a/source/app.d +++ b/source/app.d @@ -15,100 +15,100 @@ import config; void main() { - auto router = new URLRouter; - router.get("*", reverseProxyRequest(serverHost, serverPort)); - router.post("/api/v1/*", &onAPIRequest); + auto router = new URLRouter; + router.get("*", reverseProxyRequest(serverHost, serverPort)); + router.post("/api/v1/*", &onAPIRequest); - auto settings = new HTTPServerSettings; - settings.port = proxyPortNumber; - settings.options = HTTPServerOption.parseURL | HTTPServerOption.errorStackTraces; - listenHTTP(settings, router); + auto settings = new HTTPServerSettings; + settings.port = proxyPortNumber; + settings.options = HTTPServerOption.parseURL | HTTPServerOption.errorStackTraces; + listenHTTP(settings, router); - runApplication(); + runApplication(); } void onAPIRequest(HTTPServerRequest request, HTTPServerResponse response) { - auto apiEndpoint = request.path.split("/api/v1")[1]; - auto nonce = request.headers["auth_nonce"]; + auto apiEndpoint = request.path.split("/api/v1")[1]; + auto nonce = request.headers["auth_nonce"]; - auto requestBody = request.bodyReader.readAll(); - auto requestJson = getRequestJson(requestBody, apiEndpoint, nonce); - handleRequest(apiEndpoint, requestJson); + auto requestBody = request.bodyReader.readAll(); + auto requestJson = getRequestJson(requestBody, apiEndpoint, nonce); + handleRequest(apiEndpoint, requestJson); - //TODO: Allow request editing (requires recalculation of signature) - //NOTE: This would not be a very useful feature, so this might be removed later. - // One thing it would be useful for is creating a bot for keeping track of API changes. - // This would also require quite a bit of refactoring however. - auto apiResponse = sendRequest(apiEndpoint, request.headers.dup, requestBody); + //TODO: Allow request editing (requires recalculation of signature) + //NOTE: This would not be a very useful feature, so this might be removed later. + // One thing it would be useful for is creating a bot for keeping track of API changes. + // This would also require quite a bit of refactoring however. + auto apiResponse = sendRequest(apiEndpoint, request.headers.dup, requestBody); - auto responseBody = apiResponse.bodyReader.readAll(); - auto responseJson = getResponseJson(responseBody, apiEndpoint, nonce); + auto responseBody = apiResponse.bodyReader.readAll(); + auto responseJson = getResponseJson(responseBody, apiEndpoint, nonce); - auto finalResponseJson = handleResponse(apiEndpoint, responseJson); - auto finalResponseBody = constructResponseBody(apiEndpoint, finalResponseJson, nonce); + auto finalResponseJson = handleResponse(apiEndpoint, responseJson); + auto finalResponseBody = constructResponseBody(apiEndpoint, finalResponseJson, nonce); - sendResponse(response, apiResponse, finalResponseBody); + sendResponse(response, apiResponse, finalResponseBody); - import std.stdio : writeln; - writeln(apiEndpoint); + import std.stdio : writeln; + writeln(apiEndpoint); - import std.file : append; - struct LogEntry - { - string endpoint; - Json request; - Json response; - } - append("api.log", LogEntry(apiEndpoint, requestJson, finalResponseJson).serializeToJson.toPrettyString ~ "\n\n"); + import std.file : append; + struct LogEntry + { + string endpoint; + Json request; + Json response; + } + append("api.log", LogEntry(apiEndpoint, requestJson, finalResponseJson).serializeToJson.toPrettyString ~ "\n\n"); } Json getRequestJson(ubyte[] input, string endpoint, string nonce) { - auto originalRequestPipe = Pipe - ( - new Base64Decoder, - getCipher - ( - cipherName, - SymmetricKey((cast(ubyte[]) keyString).ptr, keyString.length), - InitializationVector((cast(ubyte[]) nonce).ptr, nonce.length), - DECRYPTION - ) - ); + auto originalRequestPipe = Pipe + ( + new Base64Decoder, + getCipher + ( + cipherName, + SymmetricKey((cast(ubyte[]) keyString).ptr, keyString.length), + InitializationVector((cast(ubyte[]) nonce).ptr, nonce.length), + DECRYPTION + ) + ); - originalRequestPipe.processMsg(input.ptr, input.length); + originalRequestPipe.processMsg(input.ptr, input.length); - auto requestJsonString = originalRequestPipe.toString; - return requestJsonString.parseJson; + auto requestJsonString = originalRequestPipe.toString; + return requestJsonString.parseJson; } Json getResponseJson(ubyte[] input, string endpoint, string nonce) { - import std.zlib : uncompress; + import std.zlib : uncompress; - switch(endpoint) - { - case "/config/getGameConfig": - return getRequestJson(input, endpoint, nonce); - case "/master/getMaster": - auto decompressedInput = cast(ubyte[])uncompress(input); //TODO: Use pipe instead (how?) + switch(endpoint) + { + case "/config/getGameConfig": + return getRequestJson(input, endpoint, nonce); + case "/master/getMaster": + auto decompressedInput = cast(ubyte[])uncompress(input); //TODO: Use pipe instead (how?) - auto responseJsonString = cast(string) decompressedInput; - return responseJsonString.parseJson; - default: - auto decompressedInput = cast(ubyte[])uncompress(input); //TODO: Use pipe instead (how?) + auto responseJsonString = cast(string) decompressedInput; + return responseJsonString.parseJson; + default: + auto decompressedInput = cast(ubyte[])uncompress(input); //TODO: Use pipe instead (how?) - auto responsePipe = Pipe - ( - new Base64Decoder - ); + auto responsePipe = Pipe + ( + new Base64Decoder + ); - responsePipe.processMsg(decompressedInput.ptr, decompressedInput.length); + responsePipe.processMsg(decompressedInput.ptr, decompressedInput.length); - auto responseJsonString = responsePipe.toString; - return responseJsonString.parseJson; - } + auto responseJsonString = responsePipe.toString; + return responseJsonString.parseJson; + } } void handleRequest(string endpoint, Json json) @@ -118,127 +118,127 @@ void handleRequest(string endpoint, Json json) Json handleResponse(string endpoint, Json json) { - if(endpoint == "/master/getMaster") - { - import models.master; - auto getMasterResponse = json.deserializeJson!GetMasterResponse; + if(endpoint == "/master/getMaster") + { + import models.master; + auto getMasterResponse = json.deserializeJson!GetMasterResponse; - import dataMixins : applyMasterTranslation; + import dataMixins : applyMasterTranslation; - mixin(applyMasterTranslation!("Character", 0, - 5, "characterNames")); + mixin(applyMasterTranslation!("Character", 0, + 5, "characterNames")); - mixin(applyMasterTranslation!("CharacterSkill", 0, - 1, "skillNames", - 6, "skillDescriptions")); + mixin(applyMasterTranslation!("CharacterSkill", 0, + 1, "skillNames", + 6, "skillDescriptions")); - mixin(applyMasterTranslation!("Stage", 0, - 1, "stageNames")); + mixin(applyMasterTranslation!("Stage", 0, + 1, "stageNames")); - //TODO: Figure out a way to find the wrong ones - mixin(applyMasterTranslation!("Mission", 0, - 1, "missionNames", - 2, "missionDescriptions", - 3, "missionRewards")); + //TODO: Figure out a way to find the wrong ones + mixin(applyMasterTranslation!("Mission", 0, + 1, "missionNames", + 2, "missionDescriptions", + 3, "missionRewards")); - mixin(applyMasterTranslation!("Dungeon", 10, - 12, "dungeonNames")); + mixin(applyMasterTranslation!("Dungeon", 10, + 12, "dungeonNames")); - mixin(applyMasterTranslation!("Guest", 0, - 1, "guestPartyNames")); + mixin(applyMasterTranslation!("Guest", 0, + 1, "guestPartyNames")); - mixin(applyMasterTranslation!("Item", 0, - 1, "itemNames")); + mixin(applyMasterTranslation!("Item", 0, + 1, "itemNames")); - mixin(applyMasterTranslation!("WorldMap", 0, - 3, "worldMapLocationNames", - 4, "worldMapLocationCaptions", - 5, "worldMapLocationDescriptions")); + mixin(applyMasterTranslation!("WorldMap", 0, + 3, "worldMapLocationNames", + 4, "worldMapLocationCaptions", + 5, "worldMapLocationDescriptions")); - mixin(applyMasterTranslation!("CharacterTextResource", 0, - 3, "characterTextResource")); + mixin(applyMasterTranslation!("CharacterTextResource", 0, + 3, "characterTextResource")); - mixin(applyMasterTranslation!("CharacterLeaderSkill", 0, - 1, "characterLeaderSkillNames", - 14, "characterLeaderSkillDescriptions")); + mixin(applyMasterTranslation!("CharacterLeaderSkill", 0, + 1, "characterLeaderSkillNames", + 14, "characterLeaderSkillDescriptions")); - mixin(applyMasterTranslation!("CharacterBook", 0, - 5, "characterBookFlowerLanguage")); + mixin(applyMasterTranslation!("CharacterBook", 0, + 5, "characterBookFlowerLanguage")); - mixin(applyMasterTranslation!("CharacterCategory", 0, - 1, "characterCategories")); + mixin(applyMasterTranslation!("CharacterCategory", 0, + 1, "characterCategories")); - return getMasterResponse.serializeToJson; - } + return getMasterResponse.serializeToJson; + } - return json; + return json; } ubyte[] constructResponseBody(string endpoint, Json json, string nonce) { - import std.zlib : compress; + import std.zlib : compress; - auto jsonStringBytes = cast(ubyte[]) json.toString; + auto jsonStringBytes = cast(ubyte[]) json.toString; - switch(endpoint) - { - case "/config/getGameConfig": - auto responsePipe = Pipe - ( - getCipher - ( - cipherName, - SymmetricKey((cast(ubyte[]) keyString).ptr, keyString.length), - InitializationVector((cast(ubyte[]) nonce).ptr, nonce.length), - ENCRYPTION - ), - new Base64Encoder - ); + switch(endpoint) + { + case "/config/getGameConfig": + auto responsePipe = Pipe + ( + getCipher + ( + cipherName, + SymmetricKey((cast(ubyte[]) keyString).ptr, keyString.length), + InitializationVector((cast(ubyte[]) nonce).ptr, nonce.length), + ENCRYPTION + ), + new Base64Encoder + ); - //TODO: Refactor this - if(jsonStringBytes.length % 16 != 0) - { - jsonStringBytes.length += 16 - jsonStringBytes.length % 16; - } + //TODO: Refactor this + if(jsonStringBytes.length % 16 != 0) + { + jsonStringBytes.length += 16 - jsonStringBytes.length % 16; + } - responsePipe.processMsg(jsonStringBytes.ptr, jsonStringBytes.length); + responsePipe.processMsg(jsonStringBytes.ptr, jsonStringBytes.length); - return cast(ubyte[]) responsePipe.toString; - case "/master/getMaster": - return compress(jsonStringBytes); //TODO: Use pipe instead (how?) - default: - auto responsePipe = Pipe - ( - new Base64Encoder - ); + return cast(ubyte[]) responsePipe.toString; + case "/master/getMaster": + return compress(jsonStringBytes); //TODO: Use pipe instead (how?) + default: + auto responsePipe = Pipe + ( + new Base64Encoder + ); - responsePipe.processMsg(jsonStringBytes.ptr, jsonStringBytes.length); + responsePipe.processMsg(jsonStringBytes.ptr, jsonStringBytes.length); - return compress(responsePipe.toString); - } + return compress(responsePipe.toString); + } } HTTPClientResponse sendRequest(string endpoint, InetHeaderMap headers, ubyte[] requestBody) { - return requestHTTP("http://" ~ serverHost ~ "/api/v1" ~ endpoint, - (scope HTTPClientRequest r) - { - r.method = HTTPMethod.POST; - r.headers = headers.dup; - r.writeBody(requestBody, r.headers["Content-Type"]); - } - ); + return requestHTTP("http://" ~ serverHost ~ "/api/v1" ~ endpoint, + (scope HTTPClientRequest r) + { + r.method = HTTPMethod.POST; + r.headers = headers.dup; + r.writeBody(requestBody, r.headers["Content-Type"]); + } + ); } void sendResponse(HTTPServerResponse response, HTTPClientResponse apiResponse, ubyte[] responseBody) { - //HACK:This is necessary because of something, but I don't remember what - if("Transfer-Encoding" in apiResponse.headers) - { - apiResponse.headers.remove("Transfer-Encoding"); - } - response.statusCode = apiResponse.statusCode; - response.statusPhrase = apiResponse.statusPhrase; - response.headers = apiResponse.headers.dup; - response.writeBody(responseBody); + //HACK:This is necessary because of something, but I don't remember what + if("Transfer-Encoding" in apiResponse.headers) + { + apiResponse.headers.remove("Transfer-Encoding"); + } + response.statusCode = apiResponse.statusCode; + response.statusPhrase = apiResponse.statusPhrase; + response.headers = apiResponse.headers.dup; + response.writeBody(responseBody); } diff --git a/source/dataMixins.d b/source/dataMixins.d index f07eaf9..3d4520f 100644 --- a/source/dataMixins.d +++ b/source/dataMixins.d @@ -2,34 +2,34 @@ module dataMixins; template applyMasterTranslation(string Table, uint IdFieldIndex, Translations...) { - static assert(Translations.length >= 2 && Translations.length % 2 == 0); + static assert(Translations.length >= 2 && Translations.length % 2 == 0); - import std.conv : to; - import std.uni : toLower; - private template getMasterColumnTranslations(Translations...) - { - static if(Translations.length > 0) - { - enum getMasterColumnTranslations = "row[" ~ Translations[0].to!string ~ "] = " ~ Translations[1] ~ "[id]; " - ~ getMasterColumnTranslations!(Translations[2 .. $]); - } - else - { - enum getMasterColumnTranslations = ""; - } - } + import std.conv : to; + import std.uni : toLower; + private template getMasterColumnTranslations(Translations...) + { + static if(Translations.length > 0) + { + enum getMasterColumnTranslations = "row[" ~ Translations[0].to!string ~ "] = " ~ Translations[1] ~ "[id]; " + ~ getMasterColumnTranslations!(Translations[2 .. $]); + } + else + { + enum getMasterColumnTranslations = ""; + } + } - enum applyMasterTranslation = " - foreach(row; getMasterResponse.master" ~ Table ~ ") - { - import std.conv : to; - auto id = row[" ~ IdFieldIndex.to!string ~ "].to!uint; + enum applyMasterTranslation = " + foreach(row; getMasterResponse.master" ~ Table ~ ") + { + import std.conv : to; + auto id = row[" ~ IdFieldIndex.to!string ~ "].to!uint; - import translations." ~ Table[0].toLower.to!string ~ Table[1 .. $] ~ "; + import translations." ~ Table[0].toLower.to!string ~ Table[1 .. $] ~ "; - if(id in " ~ Translations[1] ~ ") - { - " ~ getMasterColumnTranslations!(Translations) ~ " - } - }"; + if(id in " ~ Translations[1] ~ ") + { + " ~ getMasterColumnTranslations!(Translations) ~ " + } + }"; } diff --git a/source/models/master.d b/source/models/master.d index 0b5d594..023ab14 100644 --- a/source/models/master.d +++ b/source/models/master.d @@ -6,92 +6,92 @@ import std.typecons : Nullable; class GetMasterResponse : Response { - Nullable!Base64CSV masterTutorial; - Nullable!Base64CSV masterStory; - Nullable!Base64CSV masterPanelMission; - Nullable!Base64CSV masterEquipmentPurificationGroups; - Nullable!Base64CSV masterBossAi; - Nullable!Base64CSV masterAlbumDungeon; - Nullable!Base64CSV masterMissionItem; - Nullable!Base64CSV masterNavigation; - Nullable!Base64CSV masterCharacterParticular; - Nullable!Base64CSV masterCharacterTrainableLeaderSkill; - Nullable!Base64CSV masterDefenseSetting; - Nullable!Base64CSV masterGardenSpecialCharacter; - Nullable!Base64CSV masterCharacterEquipmentEvolve; - Nullable!Base64CSV masterCharacterSkill; - Nullable!Base64CSV masterCollaboSymphony; - Nullable!Base64CSV masterCharacterEquipmentLevelGroup; - Nullable!Base64CSV masterCharacterQuestItem; - Nullable!Base64CSV masterSurvey; - Nullable!Base64CSV masterCharacterMyPagePattern; - Nullable!Base64CSV masterGift; - Nullable!Base64CSV masterDungeonRecommendPrioritys; - Nullable!Base64CSV masterAlbumQuestClear; - Nullable!Base64CSV masterStageEvaluationItem; - Nullable!Base64CSV masterItem; - Nullable!Base64CSV masterCharacterFlowerings; - Nullable!Base64CSV masterPlant; - Nullable!Base64CSV masterCharacterTrainableLeaderSkillAcquireCondition; - Nullable!Base64CSV masterEquipmentPurifications; - Nullable!Base64CSV masterCharacterEquipment; - Nullable!Base64CSV masterCharacterTextResource; - Nullable!Base64CSV masterAlbumTheaterGroup; - Nullable!Base64CSV masterMission; - Nullable!Base64CSV masterCharacterMypageVoiceResourceGroup; - Nullable!Base64CSV masterGuestCharacter; - Nullable!Base64CSV masterCharacterLeaderSkill; - Nullable!Base64CSV masterCharacterLeaderSkillDescription; - Nullable!Base64CSV masterCharacterSkinNotExistVoice; - Nullable!Base64CSV masterStage; - Nullable!Base64CSV masterCampaignStaminaDungeon; - Nullable!Base64CSV masterGardenMakeoverItemShop; - Nullable!Base64CSV masterGardenMakeoverItem; - Nullable!Base64CSV masterWhaleHomeBanner; - Nullable!Base64CSV masterSwanBoatRaceGroup; - Nullable!Base64CSV masterSynopsis; - Nullable!Base64CSV masterCharacterLevel; - Nullable!Base64CSV masterPayment; - Nullable!Base64CSV masterCharacterBook; - Nullable!Base64CSV masterAchievement; - Nullable!Base64CSV masterCharacterCategory; - Nullable!Base64CSV masterAvailableItem; - Nullable!Base64CSV masterSwanBoatRaceSchedule; - Nullable!Base64CSV masterCharacterMypageVoiceResource; - Nullable!Base64CSV masterCharacterLevelGroup; - Nullable!Base64CSV masterCharacterQuest; - Nullable!Base64CSV masterCharacterEvolveGroup; - Nullable!Base64CSV masterCharacterEvolve; - Nullable!Base64CSV masterKizunaPoint; - Nullable!Base64CSV masterGiftPurifications; - Nullable!Base64CSV masterHomeBgm; - Nullable!Base64CSV masterLetter; - Nullable!Base64CSV masterPaymentCampaign; - Nullable!Base64CSV masterLevel; - Nullable!Base64CSV masterHomeBanner; - Nullable!Base64CSV masterEquipmentPurificationMaterialItems; - Nullable!Base64CSV masterGardenPlantFlower; - Nullable!Base64CSV masterWorldMap; - Nullable!Base64CSV masterBoost; - Nullable!Base64CSV masterCharacterEquipmentEvolveGroup; - Nullable!Base64CSV masterGardenPlantFlowerBook; - Nullable!Base64CSV masterCharacterFloweringGroups; - Nullable!Base64CSV masterGardenPlantInsectBook; - Nullable!Base64CSV masterWhaleEvent; - Nullable!Base64CSV masterCharacterTrainableLeaderSkillLevel; - Nullable!Base64CSV masterCharacterPurification; - Nullable!Base64CSV masterDungeon; - Nullable!Base64CSV masterShop; - Nullable!Base64CSV masterGuest; - Nullable!Base64CSV masterAlbumTheater; - Nullable!Base64CSV masterCharacter; - Nullable!Base64CSV masterCharacterEquipmentLevel; - Nullable!Base64CSV masterStageConcept; - Nullable!Base64CSV masterDungeonRecommendTotalPowerGroup; - Nullable!Base64CSV masterCharacterSkin; - Nullable!Base64CSV masterGardenPlantPot; - Nullable!Base64CSV masterItemPurification; - Nullable!Base64CSV masterStageTimeSchedule; - Nullable!Base64CSV masterGardenFlowerItem; - Nullable!Base64CSV masterPlantItem; + Nullable!Base64CSV masterTutorial; + Nullable!Base64CSV masterStory; + Nullable!Base64CSV masterPanelMission; + Nullable!Base64CSV masterEquipmentPurificationGroups; + Nullable!Base64CSV masterBossAi; + Nullable!Base64CSV masterAlbumDungeon; + Nullable!Base64CSV masterMissionItem; + Nullable!Base64CSV masterNavigation; + Nullable!Base64CSV masterCharacterParticular; + Nullable!Base64CSV masterCharacterTrainableLeaderSkill; + Nullable!Base64CSV masterDefenseSetting; + Nullable!Base64CSV masterGardenSpecialCharacter; + Nullable!Base64CSV masterCharacterEquipmentEvolve; + Nullable!Base64CSV masterCharacterSkill; + Nullable!Base64CSV masterCollaboSymphony; + Nullable!Base64CSV masterCharacterEquipmentLevelGroup; + Nullable!Base64CSV masterCharacterQuestItem; + Nullable!Base64CSV masterSurvey; + Nullable!Base64CSV masterCharacterMyPagePattern; + Nullable!Base64CSV masterGift; + Nullable!Base64CSV masterDungeonRecommendPrioritys; + Nullable!Base64CSV masterAlbumQuestClear; + Nullable!Base64CSV masterStageEvaluationItem; + Nullable!Base64CSV masterItem; + Nullable!Base64CSV masterCharacterFlowerings; + Nullable!Base64CSV masterPlant; + Nullable!Base64CSV masterCharacterTrainableLeaderSkillAcquireCondition; + Nullable!Base64CSV masterEquipmentPurifications; + Nullable!Base64CSV masterCharacterEquipment; + Nullable!Base64CSV masterCharacterTextResource; + Nullable!Base64CSV masterAlbumTheaterGroup; + Nullable!Base64CSV masterMission; + Nullable!Base64CSV masterCharacterMypageVoiceResourceGroup; + Nullable!Base64CSV masterGuestCharacter; + Nullable!Base64CSV masterCharacterLeaderSkill; + Nullable!Base64CSV masterCharacterLeaderSkillDescription; + Nullable!Base64CSV masterCharacterSkinNotExistVoice; + Nullable!Base64CSV masterStage; + Nullable!Base64CSV masterCampaignStaminaDungeon; + Nullable!Base64CSV masterGardenMakeoverItemShop; + Nullable!Base64CSV masterGardenMakeoverItem; + Nullable!Base64CSV masterWhaleHomeBanner; + Nullable!Base64CSV masterSwanBoatRaceGroup; + Nullable!Base64CSV masterSynopsis; + Nullable!Base64CSV masterCharacterLevel; + Nullable!Base64CSV masterPayment; + Nullable!Base64CSV masterCharacterBook; + Nullable!Base64CSV masterAchievement; + Nullable!Base64CSV masterCharacterCategory; + Nullable!Base64CSV masterAvailableItem; + Nullable!Base64CSV masterSwanBoatRaceSchedule; + Nullable!Base64CSV masterCharacterMypageVoiceResource; + Nullable!Base64CSV masterCharacterLevelGroup; + Nullable!Base64CSV masterCharacterQuest; + Nullable!Base64CSV masterCharacterEvolveGroup; + Nullable!Base64CSV masterCharacterEvolve; + Nullable!Base64CSV masterKizunaPoint; + Nullable!Base64CSV masterGiftPurifications; + Nullable!Base64CSV masterHomeBgm; + Nullable!Base64CSV masterLetter; + Nullable!Base64CSV masterPaymentCampaign; + Nullable!Base64CSV masterLevel; + Nullable!Base64CSV masterHomeBanner; + Nullable!Base64CSV masterEquipmentPurificationMaterialItems; + Nullable!Base64CSV masterGardenPlantFlower; + Nullable!Base64CSV masterWorldMap; + Nullable!Base64CSV masterBoost; + Nullable!Base64CSV masterCharacterEquipmentEvolveGroup; + Nullable!Base64CSV masterGardenPlantFlowerBook; + Nullable!Base64CSV masterCharacterFloweringGroups; + Nullable!Base64CSV masterGardenPlantInsectBook; + Nullable!Base64CSV masterWhaleEvent; + Nullable!Base64CSV masterCharacterTrainableLeaderSkillLevel; + Nullable!Base64CSV masterCharacterPurification; + Nullable!Base64CSV masterDungeon; + Nullable!Base64CSV masterShop; + Nullable!Base64CSV masterGuest; + Nullable!Base64CSV masterAlbumTheater; + Nullable!Base64CSV masterCharacter; + Nullable!Base64CSV masterCharacterEquipmentLevel; + Nullable!Base64CSV masterStageConcept; + Nullable!Base64CSV masterDungeonRecommendTotalPowerGroup; + Nullable!Base64CSV masterCharacterSkin; + Nullable!Base64CSV masterGardenPlantPot; + Nullable!Base64CSV masterItemPurification; + Nullable!Base64CSV masterStageTimeSchedule; + Nullable!Base64CSV masterGardenFlowerItem; + Nullable!Base64CSV masterPlantItem; } diff --git a/source/models/response.d b/source/models/response.d index d275d87..8c0e7db 100644 --- a/source/models/response.d +++ b/source/models/response.d @@ -10,36 +10,36 @@ import std.typecons : Nullable; class Response { - string resultCode; - string buildVersion; - Nullable!string serverTime; - Nullable!string version_; + string resultCode; + string buildVersion; + Nullable!string serverTime; + Nullable!string version_; } struct Base64CSV { - string[][] records; + string[][] records; - alias records this; + alias records this; - @safe //I guess - string toRepresentation() const - { - auto csvString = records.map!(r => r.join(",")).array.join("\n").idup ~ "\n"; + @safe //I guess + string toRepresentation() const + { + auto csvString = records.map!(r => r.join(",")).array.join("\n").idup ~ "\n"; - return Base64.encode(cast(const(ubyte[])) csvString); - } + return Base64.encode(cast(const(ubyte[])) csvString); + } - @safe //I guess - static Base64CSV fromRepresentation(string input) - { - auto inputString = cast(string) Base64.decode(input).idup; + @safe //I guess + static Base64CSV fromRepresentation(string input) + { + auto inputString = cast(string) Base64.decode(input).idup; - auto records = inputString.splitLines.map!(line => line.split(',')).array; - auto base64csv = Base64CSV(records); + auto records = inputString.splitLines.map!(line => line.split(',')).array; + auto base64csv = Base64CSV(records); - assert(base64csv.toRepresentation == input); + assert(base64csv.toRepresentation == input); - return base64csv; - } + return base64csv; + } } diff --git a/source/translations/character.d b/source/translations/character.d index eac1d96..23c72a5 100644 --- a/source/translations/character.d +++ b/source/translations/character.d @@ -4,5 +4,5 @@ string[uint] characterNames; static this() { - mixin(import("characterNames.data.d")); + mixin(import("characterNames.data.d")); } diff --git a/source/translations/characterBook.d b/source/translations/characterBook.d index f0708a9..ca947ac 100644 --- a/source/translations/characterBook.d +++ b/source/translations/characterBook.d @@ -4,5 +4,5 @@ string[uint] characterBookFlowerLanguage; static this() { - mixin(import("characterBookFlowerLanguage.data.d")); + mixin(import("characterBookFlowerLanguage.data.d")); } diff --git a/source/translations/characterCategory.d b/source/translations/characterCategory.d index e0101ae..03987cf 100644 --- a/source/translations/characterCategory.d +++ b/source/translations/characterCategory.d @@ -4,5 +4,5 @@ string[uint] characterCategories; static this() { - mixin(import("characterCategories.data.d")); + mixin(import("characterCategories.data.d")); } diff --git a/source/translations/characterLeaderSkill.d b/source/translations/characterLeaderSkill.d index c1713c3..34c4ef2 100644 --- a/source/translations/characterLeaderSkill.d +++ b/source/translations/characterLeaderSkill.d @@ -5,6 +5,6 @@ string[uint] characterLeaderSkillDescriptions; static this() { - mixin(import("characterLeaderSkillNames.data.d")); - mixin(import("characterLeaderSkillDescriptions.data.d")); + mixin(import("characterLeaderSkillNames.data.d")); + mixin(import("characterLeaderSkillDescriptions.data.d")); } diff --git a/source/translations/characterSkill.d b/source/translations/characterSkill.d index dd22428..a0b2f0d 100644 --- a/source/translations/characterSkill.d +++ b/source/translations/characterSkill.d @@ -5,6 +5,6 @@ string[uint] skillDescriptions; static this() { - mixin(import("skillNames.data.d")); - mixin(import("skillDescriptions.data.d")); + mixin(import("skillNames.data.d")); + mixin(import("skillDescriptions.data.d")); } diff --git a/source/translations/characterTextResource.d b/source/translations/characterTextResource.d index ec08345..bc3d8b0 100644 --- a/source/translations/characterTextResource.d +++ b/source/translations/characterTextResource.d @@ -4,5 +4,5 @@ string[uint] characterTextResource; static this() { - mixin(import("characterTextResource.data.d")); + mixin(import("characterTextResource.data.d")); } diff --git a/source/translations/dungeon.d b/source/translations/dungeon.d index 639614d..9981068 100644 --- a/source/translations/dungeon.d +++ b/source/translations/dungeon.d @@ -4,5 +4,5 @@ string[uint] dungeonNames; static this() { - mixin(import("dungeonNames.data.d")); + mixin(import("dungeonNames.data.d")); } diff --git a/source/translations/guest.d b/source/translations/guest.d index ad20397..1c3b68d 100644 --- a/source/translations/guest.d +++ b/source/translations/guest.d @@ -4,5 +4,5 @@ string[uint] guestPartyNames; static this() { - mixin(import("guestPartyNames.data.d")); + mixin(import("guestPartyNames.data.d")); } diff --git a/source/translations/item.d b/source/translations/item.d index 086d23a..151e968 100644 --- a/source/translations/item.d +++ b/source/translations/item.d @@ -4,5 +4,5 @@ string[uint] itemNames; static this() { - mixin(import("itemNames.data.d")); + mixin(import("itemNames.data.d")); } diff --git a/source/translations/mission.d b/source/translations/mission.d index df226a0..f2e5bfa 100644 --- a/source/translations/mission.d +++ b/source/translations/mission.d @@ -6,7 +6,7 @@ string[uint] missionRewards; static this() { - mixin(import("missionNames.data.d")); - mixin(import("missionDescriptions.data.d")); - mixin(import("missionRewards.data.d")); + mixin(import("missionNames.data.d")); + mixin(import("missionDescriptions.data.d")); + mixin(import("missionRewards.data.d")); } diff --git a/source/translations/stage.d b/source/translations/stage.d index 6613e47..1004b9b 100644 --- a/source/translations/stage.d +++ b/source/translations/stage.d @@ -4,5 +4,5 @@ string[uint] stageNames; static this() { - mixin(import("stageNames.data.d")); + mixin(import("stageNames.data.d")); } diff --git a/source/translations/worldMap.d b/source/translations/worldMap.d index 045dc76..cf256ca 100644 --- a/source/translations/worldMap.d +++ b/source/translations/worldMap.d @@ -6,7 +6,7 @@ string[uint] worldMapLocationDescriptions; static this() { - mixin(import("worldMapLocationNames.data.d")); - mixin(import("worldMapLocationCaptions.data.d")); - mixin(import("worldMapLocationDescriptions.data.d")); + mixin(import("worldMapLocationNames.data.d")); + mixin(import("worldMapLocationCaptions.data.d")); + mixin(import("worldMapLocationDescriptions.data.d")); }