Files
GDevelop/Core/GDCore/Events/Serialization.cpp

371 lines
15 KiB
C++

/*
* GDevelop Core
* Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#include "GDCore/Events/Serialization.h"
#include "GDCore/CommonTools.h"
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/Events/Instruction.h"
#include "GDCore/Events/InstructionsList.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Serialization/Serializer.h"
#include "GDCore/Serialization/SerializerElement.h"
#include "GDCore/Tools/Log.h"
#include "GDCore/Tools/VersionWrapper.h"
using namespace std;
namespace {
bool AddQuotesToFunctionCall(gd::String& expressionStr,
const gd::String& functionName) {
bool changedSomething = false;
size_t functionCallPos = expressionStr.find(functionName + "(");
while (functionCallPos != gd::String::npos) {
size_t pos = functionCallPos + functionName.size() + 1;
// Skip whitespace
while (pos < expressionStr.size() && expressionStr[pos] == ' ') pos++;
if (pos < expressionStr.size()) {
changedSomething = true;
// Insert the first quote
expressionStr.insert(pos, "\"");
pos++;
// Escape the argument
while (pos < expressionStr.size() && expressionStr[pos] != ')') {
if (expressionStr[pos] == '"') {
expressionStr.insert(pos,
"\\"); // Insert a backslash to escape the quote
pos++;
}
pos++;
}
// Insert the last quote
if (pos < expressionStr.size() && expressionStr[pos] == ')') {
expressionStr.insert(pos, "\"");
pos++;
}
}
functionCallPos = expressionStr.find(functionName + "(", pos + 1);
}
return changedSomething;
}
} // namespace
namespace gd {
void EventsListSerialization::UpdateInstructionsFromGD4097(
gd::Project& project, gd::InstructionsList& list) {
for (std::size_t i = 0; i < list.size(); ++i) {
gd::Instruction& instr = list[i];
for (std::size_t j = 0; j < instr.GetParametersCount(); ++j) {
gd::String expressionStr = instr.GetParameter(j).GetPlainString();
bool changedSomething = false;
changedSomething |= AddQuotesToFunctionCall(expressionStr, "PointX");
changedSomething |= AddQuotesToFunctionCall(expressionStr, "PointY");
if (changedSomething) {
std::cout << "(Debug) Converted \""
<< instr.GetParameter(j).GetPlainString() << "\" to \""
<< expressionStr << "\"" << std::endl;
instr.SetParameter(j, gd::Expression(expressionStr));
}
}
}
}
void EventsListSerialization::UpdateInstructionsFromGD31x(
gd::Project& project, gd::InstructionsList& list) {
for (std::size_t i = 0; i < list.size(); ++i) {
gd::Instruction& instr = list[i];
if (instr.GetType() == "VarScene" || instr.GetType() == "VarSceneTxt" ||
instr.GetType() == "VarGlobal" || instr.GetType() == "VarGlobalTxt" ||
instr.GetType() == "ModVarScene" ||
instr.GetType() == "ModVarSceneTxt" ||
instr.GetType() == "ModVarGlobal" ||
instr.GetType() == "ModVarGlobalTxt") {
std::vector<gd::Expression> parameters = instr.GetParameters();
if (parameters.size() >= 1) parameters.erase(parameters.begin() + 0);
instr.SetParameters(parameters);
}
if (instr.GetType() == "VarSceneDef" || instr.GetType() == "VarGlobalDef" ||
instr.GetType() == "VarObjetDef") {
instr.SetParameter(
1,
gd::Expression("\"" + instr.GetParameter(1).GetPlainString() + "\""));
}
}
}
void EventsListSerialization::UpdateInstructionsFromGD2x(
gd::Project& project,
gd::InstructionsList& list,
bool instructionsAreActions) {
for (std::size_t i = 0; i < list.size(); ++i) {
gd::Instruction& instr = list[i];
const gd::InstructionMetadata& metadata =
instructionsAreActions
? MetadataProvider::GetActionMetadata(project.GetCurrentPlatform(),
instr.GetType())
: MetadataProvider::GetConditionMetadata(
project.GetCurrentPlatform(), instr.GetType());
// Specific updates for some instructions
if (instr.GetType() == "LinkedObjects::LinkObjects" ||
instr.GetType() == "LinkedObjects::RemoveLinkBetween") {
instr.SetParameter(1, instr.GetParameter(3));
instr.SetParameter(2, instr.GetParameter(4));
} else if (instr.GetType() == "LinkedObjects::RemoveAllLinksOf") {
instr.SetParameter(1, instr.GetParameter(2));
} else if (instr.GetType() == "LinkedObjects::PickObjectsLinkedTo") {
instr.SetParameter(1, instr.GetParameter(5));
instr.SetParameter(2, instr.GetParameter(3));
} else if (instr.GetType() ==
"PhysicsBehavior::AddRevoluteJointBetweenObjects") {
instr.SetParameter(4, instr.GetParameter(5));
instr.SetParameter(5, instr.GetParameter(6));
} else if (instr.GetType() == "FixCamera" ||
instr.GetType() == "CentreCamera") {
std::vector<gd::Expression> parameters = instr.GetParameters();
if (parameters.size() >= 3) parameters.erase(parameters.begin() + 2);
instr.SetParameters(parameters);
} else if (instr.GetType() == "AjoutObjConcern" ||
instr.GetType() == "AjoutHasard") {
instr.SetParameter(1, instr.GetParameter(3));
} else if (instr.GetType() == "SeDirige" ||
instr.GetType() == "EstTourne") {
std::vector<gd::Expression> parameters = instr.GetParameters();
if (parameters.size() >= 3) parameters.erase(parameters.begin() + 2);
if (parameters.size() >= 3) parameters.erase(parameters.begin() + 2);
instr.SetParameters(parameters);
} else if (instr.GetType() == "Create") {
std::vector<gd::Expression> parameters = instr.GetParameters();
if (parameters.size() >= 2) parameters.erase(parameters.begin() + 1);
if (parameters.size() >= 2) parameters.erase(parameters.begin() + 1);
instr.SetParameters(parameters);
} else if (instr.GetType() == "CreateByName") {
std::vector<gd::Expression> parameters = instr.GetParameters();
if (parameters.size() >= 2) parameters.erase(parameters.begin() + 1);
instr.SetParameters(parameters);
} else if (instr.GetType() == "NbObjet") {
std::vector<gd::Expression> parameters = instr.GetParameters();
if (parameters.size() >= 2) parameters.erase(parameters.begin() + 1);
instr.SetParameters(parameters);
} else if (instr.GetType() == "Distance") {
std::vector<gd::Expression> parameters = instr.GetParameters();
if (parameters.size() >= 3) parameters.erase(parameters.begin() + 2);
if (parameters.size() >= 3) parameters.erase(parameters.begin() + 2);
if (parameters.size() >= 4 && (parameters[3].GetPlainString() == ">=" ||
parameters[3].GetPlainString() == ">")) {
instr.SetInverted(true);
} else {
instr.SetInverted(false);
}
instr.SetParameters(parameters);
}
// Common updates for some parameters
const std::vector<gd::Expression>& parameters = instr.GetParameters();
for (std::size_t j = 0;
j < parameters.size() && j < metadata.parameters.GetParametersCount();
++j) {
if (metadata.parameters.GetParameter(j).GetType() == "relationalOperator" ||
metadata.parameters.GetParameter(j).GetType() == "operator") {
if (j == parameters.size() - 1) {
std::cout << "ERROR: No more parameters after a [relational]operator "
"when trying to update an instruction from GD2.x";
} else {
// Exchange parameters
gd::String op = parameters[j + 1].GetPlainString();
instr.SetParameter(j + 1, parameters[j]);
instr.SetParameter(j, gd::Expression(op));
}
}
}
// UpdateInstructionsFromGD2x(project, instr.GetSubInstructions(),
// instructionsAreActions);
}
}
void EventsListSerialization::UnserializeEventsFrom(
gd::Project& project, EventsList& list, const SerializerElement& events) {
list.Clear();
events.ConsiderAsArrayOf("event", "Event");
for (std::size_t i = 0; i < events.GetChildrenCount(); ++i) {
SerializerElement& eventElem = events.GetChild(i);
gd::String type =
eventElem.GetChild("type", 0, "Type").GetValue().GetString();
gd::BaseEventSPtr event = project.CreateEvent(type);
if (event != std::shared_ptr<gd::BaseEvent>())
event->UnserializeFrom(project, eventElem);
else {
std::cout << "WARNING: Unknown event of type " << type << std::endl;
event = std::make_shared<EmptyEvent>();
}
event->SetDisabled(eventElem.GetBoolAttribute("disabled", false));
event->SetFolded(eventElem.GetBoolAttribute("folded", false));
list.InsertEvent(event, list.GetEventsCount());
}
}
void EventsListSerialization::SerializeEventsTo(const EventsList& list,
SerializerElement& events) {
events.ConsiderAsArrayOf("event");
for (std::size_t j = 0; j < list.size(); j++) {
const gd::BaseEvent& event = list.GetEvent(j);
SerializerElement& eventElem = events.AddChild("event");
if (event.IsDisabled())
eventElem.SetAttribute("disabled", event.IsDisabled());
if (event.IsFolded()) eventElem.SetAttribute("folded", event.IsFolded());
eventElem.AddChild("type").SetValue(event.GetType());
event.SerializeTo(eventElem);
}
}
using namespace std;
void gd::EventsListSerialization::UnserializeInstructionsFrom(
gd::Project& project,
gd::InstructionsList& instructions,
const SerializerElement& elem) {
elem.ConsiderAsArrayOf("instruction");
// Compatibility with GD <= 4.0.95
if (elem.HasChild("condition", "Condition"))
elem.ConsiderAsArrayOf("condition", "Condition");
else if (elem.HasChild("action", "Action"))
elem.ConsiderAsArrayOf("action", "Action");
// end of compatibility code
for (std::size_t i = 0; i < elem.GetChildrenCount(); ++i) {
gd::Instruction instruction;
const SerializerElement& instrElement = elem.GetChild(i);
instruction.SetType(
instrElement.GetChild("type", 0, "Type")
.GetStringAttribute("value")
.FindAndReplace("Automatism",
"Behavior")); // Compatibility with GD <= 4
instruction.SetInverted(
instrElement.GetChild("type", 0, "Type")
.GetBoolAttribute("inverted", false, "Contraire"));
instruction.SetAwaited(
instrElement.GetChild("type", 0, "Type").GetBoolAttribute("await"));
// Read parameters
vector<gd::Expression> parameters;
// Compatibility with GD <= 3.3
if (instrElement.HasChild("Parametre")) {
for (std::size_t j = 0; j < instrElement.GetChildrenCount("Parametre");
++j)
parameters.push_back(gd::Expression(
instrElement.GetChild("Parametre", j).GetValue().GetString()));
}
// end of compatibility code
else {
const SerializerElement& parametersElem =
instrElement.GetChild("parameters");
parametersElem.ConsiderAsArrayOf("parameter");
for (std::size_t j = 0; j < parametersElem.GetChildrenCount(); ++j)
parameters.push_back(
gd::Expression(parametersElem.GetChild(j).GetValue().GetString()));
}
instruction.SetParameters(parameters);
// Read sub instructions
if (instrElement.HasChild("subInstructions"))
UnserializeInstructionsFrom(project,
instruction.GetSubInstructions(),
instrElement.GetChild("subInstructions"));
// Compatibility with GD <= 4.0.95
if (instrElement.HasChild("subConditions", "SubConditions"))
UnserializeInstructionsFrom(
project,
instruction.GetSubInstructions(),
instrElement.GetChild("subConditions", 0, "SubConditions"));
if (instrElement.HasChild("subActions", "SubActions"))
UnserializeInstructionsFrom(
project,
instruction.GetSubInstructions(),
instrElement.GetChild("subActions", 0, "SubActions"));
// end of compatibility code
instructions.Insert(instruction);
}
// Compatibility with GD <= 3.1
if (project.GetLastSaveGDMajorVersion() < 3 ||
(project.GetLastSaveGDMajorVersion() == 3 &&
project.GetLastSaveGDMinorVersion() <= 1))
UpdateInstructionsFromGD31x(project, instructions);
if (project.GetLastSaveGDMajorVersion() < 3)
UpdateInstructionsFromGD2x(
project, instructions, elem.HasChild("action", "Action"));
// Compatibility with GD <= 4.0.97
if (VersionWrapper::IsOlderOrEqual(project.GetLastSaveGDMajorVersion(),
project.GetLastSaveGDMinorVersion(),
project.GetLastSaveGDBuildVersion(),
0,
4,
0,
97,
0)) {
UpdateInstructionsFromGD4097(project, instructions);
}
// end of compatibility code
}
void gd::EventsListSerialization::SerializeInstructionsTo(
const gd::InstructionsList& list, SerializerElement& instructions) {
instructions.ConsiderAsArrayOf("instruction");
for (std::size_t k = 0; k < list.size(); k++) {
SerializerElement& instruction = instructions.AddChild("instruction");
instruction.AddChild("type").SetAttribute("value", list[k].GetType());
if (list[k].IsInverted())
instruction.GetChild("type").SetAttribute("inverted", true);
if (list[k].IsAwaited())
instruction.GetChild("type").SetAttribute("await", true);
// Parameters
SerializerElement& parameters = instruction.AddChild("parameters");
parameters.ConsiderAsArrayOf("parameter");
for (std::size_t l = 0; l < list[k].GetParameters().size(); l++)
parameters.AddChild("parameter")
.SetValue(list[k].GetParameter(l).GetPlainString());
// Sub instructions
if (!list[k].GetSubInstructions().empty()) {
SerializeInstructionsTo(list[k].GetSubInstructions(),
instruction.AddChild("subInstructions"));
}
}
}
} // namespace gd