Skip to content

Commit

Permalink
Clean up template storing, restoring, saving, and loading.
Browse files Browse the repository at this point in the history
Fixes off-by-one loading bug. Fixes crash on restoring user-stored templates.
Fixes lack of saving store status. Reduces code duplication.

Breaks savegame compatibility.

Closes ticket:4406
  • Loading branch information
perim committed May 22, 2016
1 parent 85ac044 commit 73dba8a
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 111 deletions.
27 changes: 18 additions & 9 deletions lib/framework/wzconfig.cpp
Expand Up @@ -270,7 +270,7 @@ void WzConfig::endGroup()
}
}

bool WzConfig::beginArray(const QString &name)
void WzConfig::beginArray(const QString &name)
{
ASSERT(mArray.isEmpty(), "beginArray() cannot be nested");
mObjNameStack.append(mName);
Expand All @@ -284,18 +284,14 @@ bool WzConfig::beginArray(const QString &name)
{
if (!contains(name)) // handled in this way for backwards compatibility
{
mName = mObjNameStack.takeLast();
mObj = mObjStack.takeLast();
return false;
return;
}
QJsonValue value = mObj.value(name);
ASSERT(value.isArray(), "beginArray() on non-array key \"%s\"", name.toUtf8().constData());
mArray = value.toArray();
ASSERT(mArray.first().isObject(), "beginArray() on non-object array \"%s\"", name.toUtf8().constData());
mObj = mArray.first().toObject();
mArray.removeFirst();
}
return true;
}

void WzConfig::nextArrayItem()
Expand All @@ -307,8 +303,15 @@ void WzConfig::nextArrayItem()
}
else
{
mObj = mArray.first().toObject();
mArray.removeFirst();
if (mArray.size() > 0)
{
mObj = mArray.first().toObject();
}
else
{
mObj = QJsonObject();
}
}
}

Expand All @@ -321,9 +324,15 @@ void WzConfig::endArray()
{
if (mWarning == ReadAndWrite)
{
mArray.push_back(mObj);
if (!mObj.isEmpty())
{
mArray.push_back(mObj);
}
mObj = mObjStack.takeLast();
mObj[mName] = mArray;
if (!mArray.isEmpty())
{
mObj[mName] = mArray;
}
mName = mObjNameStack.takeLast();
}
else
Expand Down
2 changes: 1 addition & 1 deletion lib/framework/wzconfig.h
Expand Up @@ -65,7 +65,7 @@ class WzConfig
QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
QJsonValue json(const QString &key, const QJsonValue &defaultValue = QJsonValue()) const;

bool beginArray(const QString &name);
void beginArray(const QString &name);
void nextArrayItem();
void endArray();
int remainingArrayItems();
Expand Down
4 changes: 2 additions & 2 deletions src/design.cpp
Expand Up @@ -4180,12 +4180,12 @@ void updateStoreButton(bool isStored)
if (isStored)
{
imageset = PACKDWORD_TRI(0, IMAGE_DES_DELETEH, IMAGE_DES_DELETE);
widgGetFromID(psWScreen, IDDES_STOREBUTTON)->setTip(_("Delete Template"));
widgGetFromID(psWScreen, IDDES_STOREBUTTON)->setTip(_("Do Not Store Design"));
}
else
{
imageset = PACKDWORD_TRI(0, IMAGE_DES_SAVEH, IMAGE_DES_SAVE);
widgGetFromID(psWScreen, IDDES_STOREBUTTON)->setTip(_("Store Template"));
widgGetFromID(psWScreen, IDDES_STOREBUTTON)->setTip(_("Store Design"));
}

widgSetUserData2(psWScreen, IDDES_STOREBUTTON, imageset);
Expand Down
65 changes: 19 additions & 46 deletions src/game.cpp
Expand Up @@ -5683,44 +5683,27 @@ bool loadSaveTemplate(const char *pFileName)
WzConfig ini(pFileName, WzConfig::ReadOnly);
QStringList list = ini.childGroups();

int version = ini.value("version", 0).toInt();
if (version == 0)
{
return false;
}
for (int i = 0; i < list.size(); ++i)
{
ini.beginGroup(list[i]);
int player = getPlayer(ini);
bool beta1_compat_hack = false; // TBD remove before release!
beta1_compat_hack = !ini.beginArray("templates");
while (ini.remainingArrayItems() > 0 || beta1_compat_hack)
{
DROID_TEMPLATE *psTemplate = new DROID_TEMPLATE;
psTemplate->name = ini.value("name").toString();
psTemplate->ref = ini.value("ref").toInt();
psTemplate->droidType = (DROID_TYPE)ini.value("droidType").toInt();
psTemplate->multiPlayerID = ini.value("multiPlayerID").toInt();
psTemplate->asParts[COMP_BODY] = getCompFromName(COMP_BODY, ini.value("body", "ZNULLBODY").toString());
psTemplate->asParts[COMP_BRAIN] = getCompFromName(COMP_BRAIN, ini.value("brain", "ZNULLBRAIN").toString());
psTemplate->asParts[COMP_PROPULSION] = getCompFromName(COMP_PROPULSION, ini.value("propulsion", "ZNULLPROP").toString());
psTemplate->asParts[COMP_REPAIRUNIT] = getCompFromName(COMP_REPAIRUNIT, ini.value("repair", "ZNULLREPAIR").toString());
psTemplate->asParts[COMP_ECM] = getCompFromName(COMP_ECM, ini.value("ecm", "ZNULLECM").toString());
psTemplate->asParts[COMP_SENSOR] = getCompFromName(COMP_SENSOR, ini.value("sensor", "ZNULLSENSOR").toString());
psTemplate->asParts[COMP_CONSTRUCT] = getCompFromName(COMP_CONSTRUCT, ini.value("construct", "ZNULLCONSTRUCT").toString());
psTemplate->asWeaps[0] = getCompFromName(COMP_WEAPON, ini.value("weapon/1", "ZNULLWEAPON").toString());
psTemplate->asWeaps[1] = getCompFromName(COMP_WEAPON, ini.value("weapon/2", "ZNULLWEAPON").toString());
psTemplate->asWeaps[2] = getCompFromName(COMP_WEAPON, ini.value("weapon/3", "ZNULLWEAPON").toString());
psTemplate->numWeaps = ini.value("weapons").toInt();
psTemplate->enabled = ini.value("enabled").toBool();
psTemplate->prefab = false; // not AI template
addTemplate(player, psTemplate);
if (beta1_compat_hack)
{
break;
}
ini.beginArray("templates");
while (ini.remainingArrayItems() > 0)
{
DROID_TEMPLATE t = loadTemplateCommon(ini);
t.name = ini.value("name").toString();
t.multiPlayerID = ini.value("multiPlayerID", generateNewObjectId()).toInt();
t.enabled = ini.value("enabled", false).toBool();
t.stored = ini.value("stored", false).toBool();
t.prefab = ini.value("prefab", false).toBool();
copyTemplate(player, &t);
ini.nextArrayItem();
}
if (beta1_compat_hack)
{
ini.endGroup();
continue;
}
ini.endArray();
ini.endGroup();
}
Expand All @@ -5737,6 +5720,7 @@ bool writeTemplateFile(const char *pFileName)
{
WzConfig ini(pFileName, WzConfig::ReadAndWrite);

ini.setValue("version", 1);
for (int player = 0; player < MAX_PLAYERS; player++)
{
if (!apsDroidLists[player] && !apsStructLists[player]) // only write out templates of players that are still 'alive'
Expand All @@ -5749,23 +5733,12 @@ bool writeTemplateFile(const char *pFileName)
for (auto &keyvaluepair : droidTemplates[player])
{
DROID_TEMPLATE *psCurr = keyvaluepair.second;
ini.setValue("name", psCurr->name);
saveTemplateCommon(ini, psCurr);
ini.setValue("ref", psCurr->ref);
ini.setValue("droidType", psCurr->droidType);
ini.setValue("multiPlayerID", psCurr->multiPlayerID);
ini.setValue("body", (asBodyStats + psCurr->asParts[COMP_BODY])->id);
ini.setValue("propulsion", (asPropulsionStats + psCurr->asParts[COMP_PROPULSION])->id);
ini.setValue("brain", (asBrainStats + psCurr->asParts[COMP_BRAIN])->id);
ini.setValue("repair", (asRepairStats + psCurr->asParts[COMP_REPAIRUNIT])->id);
ini.setValue("ecm", (asECMStats + psCurr->asParts[COMP_ECM])->id);
ini.setValue("sensor", (asSensorStats + psCurr->asParts[COMP_SENSOR])->id);
ini.setValue("construct", (asConstructStats + psCurr->asParts[COMP_CONSTRUCT])->id);
ini.setValue("weapons", psCurr->numWeaps);
ini.setValue("enabled", psCurr->enabled);
for (int j = 0; j < psCurr->numWeaps; j++)
{
ini.setValue("weapon/" + QString::number(j + 1), (asWeaponStats + psCurr->asWeaps[j])->id);
}
ini.setValue("stored", psCurr->stored);
ini.setValue("prefab", psCurr->prefab);
ini.nextArrayItem();
}
ini.endArray();
Expand Down
141 changes: 88 additions & 53 deletions src/template.cpp
Expand Up @@ -94,12 +94,28 @@ bool researchedTemplate(const DROID_TEMPLATE *psCurr, int player, bool allowRedu
return researchedEverything;
}

static DROID_TEMPLATE loadTemplateCommon(WzConfig &ini)
DROID_TEMPLATE loadTemplateCommon(WzConfig &ini)
{
DROID_TEMPLATE design;
QString droidType = ini.value("type").toString();

if (droidType == "PERSON")
if (droidType == "ECM")
{
design.droidType = DROID_ECM;
}
else if (droidType == "SENSOR")
{
design.droidType = DROID_SENSOR;
}
else if (droidType == "CONSTRUCT")
{
design.droidType = DROID_CONSTRUCT;
}
else if (droidType == "WEAPON")
{
design.droidType = DROID_WEAPON;
}
else if (droidType == "PERSON")
{
design.droidType = DROID_PERSON;
}
Expand Down Expand Up @@ -164,10 +180,14 @@ bool initTemplates()
debug(LOG_WZ, "Could not open %s", ini.fileName().toUtf8().constData());
return false;
}
QStringList list = ini.childGroups();
for (int i = 0; i < list.size(); ++i)
int version = ini.value("version", 0).toInt();
if (version == 0)
{
return true; // too old version
}
ini.beginArray("templates");
while (ini.remainingArrayItems())
{
ini.beginGroup(list[i]);
DROID_TEMPLATE design = loadTemplateCommon(ini);
design.multiPlayerID = generateNewObjectId();
design.prefab = false; // not AI template
Expand All @@ -183,13 +203,13 @@ bool initTemplates()
|| (design.numWeaps > 1 && !(asWeaponStats + design.asWeaps[1])->designable)
|| (design.numWeaps > 2 && !(asWeaponStats + design.asWeaps[2])->designable))
{
debug(LOG_ERROR, "Template %d / %s from stored templates cannot be designed", i, list[i].toUtf8().constData());
debug(LOG_ERROR, "Template %s from stored templates cannot be designed", design.name.toUtf8().constData());
continue;
}
bool valid = intValidTemplate(&design, ini.value("name").toString().toUtf8().constData(), false, selectedPlayer);
if (!valid)
{
debug(LOG_ERROR, "Invalid template %d / %s from stored templates", i, list[i].toUtf8().constData());
debug(LOG_ERROR, "Invalid template %s from stored templates", design.name.toUtf8().constData());
continue;
}
DROID_TEMPLATE *psDestTemplate = NULL;
Expand Down Expand Up @@ -218,17 +238,70 @@ bool initTemplates()
if (psDestTemplate)
{
psDestTemplate->stored = true; // assimilate it
ini.endGroup();
ini.nextArrayItem();
continue; // next!
}
design.enabled = allowDesign;
copyTemplate(selectedPlayer, &design);
localTemplates.push_back(design);
ini.endGroup();
ini.nextArrayItem();
}
ini.endArray();
return true;
}

void saveTemplateCommon(WzConfig &ini, DROID_TEMPLATE *psCurr)
{
ini.setValue("name", psCurr->name);
switch (psCurr->droidType)
{
case DROID_ECM: ini.setValue("type", "ECM"); break;
case DROID_SENSOR: ini.setValue("type", "SENSOR"); break;
case DROID_CONSTRUCT: ini.setValue("type", "CONSTRUCT"); break;
case DROID_WEAPON: ini.setValue("type", "WEAPON"); break;
case DROID_PERSON: ini.setValue("type", "PERSON"); break;
case DROID_CYBORG: ini.setValue("type", "CYBORG"); break;
case DROID_CYBORG_SUPER: ini.setValue("type", "CYBORG_SUPER"); break;
case DROID_CYBORG_CONSTRUCT: ini.setValue("type", "CYBORG_CONSTRUCT"); break;
case DROID_CYBORG_REPAIR: ini.setValue("type", "CYBORG_REPAIR"); break;
case DROID_TRANSPORTER: ini.setValue("type", "TRANSPORTER"); break;
case DROID_SUPERTRANSPORTER: ini.setValue("type", "SUPERTRANSPORTER"); break;
case DROID_DEFAULT: ini.setValue("type", "DROID"); break;
default: ASSERT(false, "No such droid type \"%d\" for %s", psCurr->droidType, psCurr->name.toUtf8().constData());
}
ini.setValue("body", (asBodyStats + psCurr->asParts[COMP_BODY])->id);
ini.setValue("propulsion", (asPropulsionStats + psCurr->asParts[COMP_PROPULSION])->id);
if (psCurr->asParts[COMP_BRAIN] != 0)
{
ini.setValue("brain", (asBrainStats + psCurr->asParts[COMP_BRAIN])->id);
}
if ((asRepairStats + psCurr->asParts[COMP_REPAIRUNIT])->location == LOC_TURRET) // avoid auto-repair...
{
ini.setValue("repair", (asRepairStats + psCurr->asParts[COMP_REPAIRUNIT])->id);
}
if ((asECMStats + psCurr->asParts[COMP_ECM])->location == LOC_TURRET)
{
ini.setValue("ecm", (asECMStats + psCurr->asParts[COMP_ECM])->id);
}
if ((asSensorStats + psCurr->asParts[COMP_SENSOR])->location == LOC_TURRET)
{
ini.setValue("sensor", (asSensorStats + psCurr->asParts[COMP_SENSOR])->id);
}
if (psCurr->asParts[COMP_CONSTRUCT] != 0)
{
ini.setValue("construct", (asConstructStats + psCurr->asParts[COMP_CONSTRUCT])->id);
}
QStringList weapons;
for (int j = 0; j < psCurr->numWeaps; j++)
{
weapons += (asWeaponStats + psCurr->asWeaps[j])->id;
}
if (weapons.size())
{
ini.setValue("weapons", weapons);
}
}

bool storeTemplates()
{
// Write stored templates (back) to file
Expand All @@ -238,56 +311,18 @@ bool storeTemplates()
debug(LOG_ERROR, "Could not open %s", ini.fileName().toUtf8().constData());
return false;
}
ini.setValue("version", 1); // for breaking backwards compatibility in a nice way
ini.beginArray("templates");
for (auto &keyvaluepair : droidTemplates[selectedPlayer])
{
DROID_TEMPLATE *psCurr = keyvaluepair.second;
if (!psCurr->stored)
{
continue; // not stored
}
ini.beginGroup("template_" + QString::number(psCurr->multiPlayerID));
ini.setValue("name", psCurr->name);
switch (psCurr->droidType)
{
case DROID_PERSON: ini.setValue("type", "PERSON"); break;
case DROID_CYBORG: ini.setValue("type", "CYBORG"); break;
case DROID_CYBORG_SUPER: ini.setValue("type", "CYBORG_SUPER"); break;
case DROID_CYBORG_CONSTRUCT: ini.setValue("type", "CYBORG_CONSTRUCT"); break;
case DROID_CYBORG_REPAIR: ini.setValue("type", "CYBORG_REPAIR"); break;
case DROID_TRANSPORTER: ini.setValue("type", "TRANSPORTER"); break;
case DROID_SUPERTRANSPORTER: ini.setValue("type", "SUPERTRANSPORTER"); break;
case DROID_DEFAULT: ini.setValue("type", "DROID"); break;
default: ASSERT(false, "No such droid type \"%d\" for %s", psCurr->droidType, psCurr->name.toUtf8().constData());
}
ini.setValue("body", (asBodyStats + psCurr->asParts[COMP_BODY])->id);
ini.setValue("propulsion", (asPropulsionStats + psCurr->asParts[COMP_PROPULSION])->id);
if (psCurr->asParts[COMP_BRAIN] != 0)
{
ini.setValue("brain", (asBrainStats + psCurr->asParts[COMP_BRAIN])->id);
}
if ((asRepairStats + psCurr->asParts[COMP_REPAIRUNIT])->location == LOC_TURRET) // avoid auto-repair...
{
ini.setValue("repair", (asRepairStats + psCurr->asParts[COMP_REPAIRUNIT])->id);
}
if ((asECMStats + psCurr->asParts[COMP_ECM])->location == LOC_TURRET)
{
ini.setValue("ecm", (asECMStats + psCurr->asParts[COMP_ECM])->id);
}
if ((asSensorStats + psCurr->asParts[COMP_SENSOR])->location == LOC_TURRET)
{
ini.setValue("sensor", (asSensorStats + psCurr->asParts[COMP_SENSOR])->id);
}
if (psCurr->asParts[COMP_CONSTRUCT] != 0)
if (psCurr->stored)
{
ini.setValue("construct", (asConstructStats + psCurr->asParts[COMP_CONSTRUCT])->id);
saveTemplateCommon(ini, psCurr);
ini.nextArrayItem();
}
ini.setValue("weapons", psCurr->numWeaps);
for (int j = 0; j < psCurr->numWeaps; j++)
{
ini.setValue("weapon/" + QString::number(j + 1), (asWeaponStats + psCurr->asWeaps[j])->id);
}
ini.endGroup();
}
ini.endArray();
return true;
}

Expand Down
4 changes: 4 additions & 0 deletions src/template.h
@@ -1,6 +1,7 @@
#ifndef TEMPLATE_H
#define TEMPLATE_H

#include "lib/framework/wzconfig.h"
#include "droiddef.h"

//storage
Expand Down Expand Up @@ -41,4 +42,7 @@ bool researchedTemplate(const DROID_TEMPLATE *psCurr, int player, bool allowRedu

void listTemplates();

void saveTemplateCommon(WzConfig &ini, DROID_TEMPLATE *psCurr);
DROID_TEMPLATE loadTemplateCommon(WzConfig &ini);

#endif // TEMPLATE_H

0 comments on commit 73dba8a

Please sign in to comment.