Skip to content

Commit

Permalink
YY plugin: Adjusted to changes in GameMaker file format
Browse files Browse the repository at this point in the history
An update to GameMaker has broken compatibility in several ways:

* It now requires a "type tag field" at the start of applicable JSON
  records.

* The name is expected to follow the "type tag field" as a "%Name"
  field, in addition to the regular "name" field.

* The JSON fields are now required to be ordered alphabetically
  (case-insentively).

Other minor differences include:

* The "tags" field is left out when there are no tags.
* There is no longer a space included after JSON field names.
* Some new fields were added.

Also, the plugin now uses SaveFile again rather than QFile, because it
seems that recent GameMaker versions will reload fine when using safe
writing of files.

Closes #4132
  • Loading branch information
bjorn committed Jan 17, 2025
1 parent 9591005 commit 18e7b0e
Show file tree
Hide file tree
Showing 3 changed files with 581 additions and 422 deletions.
73 changes: 50 additions & 23 deletions src/plugins/yy/jsonwriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,16 @@ void JsonWriter::writeStartScope(Scope scope, const char *name)
m_valueWritten = false;
}

void JsonWriter::writeEndScope(Scope scope)
void JsonWriter::writeEndScope(Scope scope, bool forceNewLine)
{
Q_ASSERT(m_scopes.last() == scope);
m_scopes.pop();
if (m_valueWritten) {
write(m_valueSeparator); // This is not JSON-conform, but it's what GameMaker does
writeNewline();

// GameMaker minimization logic
if (m_scopes.size() < 2 || forceNewLine)
writeNewline(forceNewLine);
}
write(scope == Object ? '}' : ']');
m_newLine = false;
Expand All @@ -74,17 +77,10 @@ void JsonWriter::writeEndScope(Scope scope)

void JsonWriter::writeValue(double value)
{
if (qIsFinite(value)) {
// Force at least one decimal to avoid double values from being written
// as integer, which may confuse GameMaker.
if (std::ceil(value) == value) {
writeUnquotedValue(QByteArray::number(value, 'f', 1));
} else {
writeUnquotedValue(QByteArray::number(value, 'g', QLocale::FloatingPointShortest));
}
} else {
if (qIsFinite(value))
writeUnquotedValue(QByteArray::number(value, 'g', QLocale::FloatingPointShortest));
else
writeUnquotedValue("null"); // +INF || -INF || NaN (see RFC4627#section2.4)
}
}

void JsonWriter::writeValue(const QByteArray &value)
Expand Down Expand Up @@ -114,20 +110,51 @@ void JsonWriter::writeValue(const QJsonValue &value)
writeValue(value.toString());
break;
case QJsonValue::Array: {
writeStartArray();
const QJsonArray array = value.toArray();

bool arrayContainedObject = false;
qsizetype index = 0;

writeStartArray();
for (auto v : array) {
prepareNewLine();
arrayContainedObject |= v.isObject();

if (m_tileSerialiseWidth > 0) {
// force new line when starting a new row of tiles
prepareNewLine(index % m_tileSerialiseWidth == 0);
} else {
// force new line if value is an object
prepareNewLine(v.isObject());
}

writeValue(v);
++index;
}
writeEndArray();
writeEndArray(arrayContainedObject || m_tileSerialiseWidth > 0);
break;
}
case QJsonValue::Object: {
writeStartObject();
const QJsonObject object = value.toObject();
for (auto it = object.begin(); it != object.end(); ++it)
writeMember(it.key().toLatin1().constData(), it.value());

// GameMaker 2024 requires the keys to be sorted case-insensitively
auto keys = object.keys();
keys.sort(Qt::CaseInsensitive);

writeStartObject();
for (const auto &key : keys) {
const auto value = object.value(key);

const bool writingTiles = key == QLatin1String("tiles");
if (writingTiles) {
const auto tilesObject = value.toObject();
m_tileSerialiseWidth = tilesObject.value(QLatin1String("SerialiseWidth")).toInt();
}

writeMember(key.toLatin1().constData(), value);

if (writingTiles)
m_tileSerialiseWidth = 0;
}
writeEndObject();
break;
}
Expand Down Expand Up @@ -215,13 +242,13 @@ QString JsonWriter::quote(const QString &str)
return quoted;
}

void JsonWriter::prepareNewLine()
void JsonWriter::prepareNewLine(bool forceNewLine)
{
if (m_valueWritten) {
write(m_valueSeparator);
m_valueWritten = false;
}
writeNewline();
writeNewline(forceNewLine);
}

void JsonWriter::prepareNewValue()
Expand All @@ -238,10 +265,10 @@ void JsonWriter::writeIndent()
write(" ");
}

void JsonWriter::writeNewline()
void JsonWriter::writeNewline(bool force)
{
if (!m_newLine) {
if (!m_minimize && !m_suppressNewlines) {
if (force || (!m_minimize && !m_suppressNewlines && m_scopes.size() < 3)) {
write('\n');
writeIndent();
}
Expand All @@ -255,7 +282,7 @@ void JsonWriter::writeKey(const char *key)
prepareNewLine();
write('"');
write(key);
write(m_minimize ? "\":" : "\": ");
write("\":");
}

void JsonWriter::write(const char *bytes, qint64 length)
Expand Down
9 changes: 5 additions & 4 deletions src/plugins/yy/jsonwriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class JsonWriter

void writeStartArray() { writeStartScope(Array); }
void writeStartArray(const char *name) { writeStartScope(Array, name); }
void writeEndArray() { writeEndScope(Array); }
void writeEndArray(bool forceNewLine = false) { writeEndScope(Array, forceNewLine); }

void writeValue(int value);
void writeValue(unsigned value);
Expand Down Expand Up @@ -83,7 +83,7 @@ class JsonWriter
void setMinimize(bool minimize);
bool minimize() const;

void prepareNewLine();
void prepareNewLine(bool forceNewLine = false);

bool hasError() const { return m_error; }

Expand All @@ -92,12 +92,12 @@ class JsonWriter
private:
void writeStartScope(Scope scope);
void writeStartScope(Scope scope, const char *name);
void writeEndScope(Scope scope);
void writeEndScope(Scope scope, bool forceNewLine = false);

void prepareNewValue();
void writeIndent();

void writeNewline();
void writeNewline(bool force = false);
void writeKey(const char *key);
void write(const char *bytes, qint64 length);
void write(const char *bytes);
Expand All @@ -113,6 +113,7 @@ class JsonWriter
bool m_newLine { true };
bool m_valueWritten { false };
bool m_error { false };
int m_tileSerialiseWidth { 0 };
};

inline void JsonWriter::writeValue(int value)
Expand Down
Loading

0 comments on commit 18e7b0e

Please sign in to comment.