From d8e91982809a3fe8e244cff45739e4608dbd0b3d Mon Sep 17 00:00:00 2001 From: Omar Aglan Date: Sat, 3 Oct 2026 18:42:34 +0300 Subject: [PATCH 1/2] bugfix(worldbuilder): Fix script player import (#3408) --- .../Tools/WorldBuilder/src/ScriptDialog.cpp | 27 +++++++--- .../Tools/WorldBuilder/src/ScriptDialog.cpp | 50 ++++++++++++++----- 2 files changed, 57 insertions(+), 20 deletions(-) diff --git a/Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp b/Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp index 2d88de1aa93..374a44e7f08 100644 --- a/Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp +++ b/Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp @@ -1374,8 +1374,16 @@ void ScriptDialog::OnLoad() file.registerParser( "WaypointsList", AsciiString::TheEmptyString, ParseWaypointDataChunk ); file.registerParser( "ScriptTeams", AsciiString::TheEmptyString, ParseTeamsDataChunk ); file.registerParser( "ScriptsPlayers", AsciiString::TheEmptyString, ParsePlayersDataChunk ); - if (!file.parse(this)) { - throw(ERROR_CORRUPT_FILE_FORMAT); + // TheSuperHackers @bugfix OmarAglan Restore sides and teams when script parsing fails. + SidesList sidesBeforeImport; + sidesBeforeImport = m_sides; + try { + if (!file.parse(this)) { + throw(ERROR_CORRUPT_FILE_FORMAT); + } + } catch(...) { + m_sides = sidesBeforeImport; + throw; } pDoc->setNextWaypointID(m_maxWaypoint); @@ -1409,9 +1417,10 @@ void ScriptDialog::OnLoad() curSide = m_curSelection.m_playerIndex; } else { Int j; + // TheSuperHackers @bugfix OmarAglan Match each imported player to the current map side by name. for (j=0; jgetDict()->getAsciiString(TheKey_playerName); - if (name == m_readPlayerNames[j]) { + AsciiString name = m_sides.getSideInfo(j)->getDict()->getAsciiString(TheKey_playerName); + if (name == m_readPlayerNames[i]) { curSide = j; break; } @@ -1463,7 +1472,7 @@ void ScriptDialog::OnLoad() } } catch(...) { - DEBUG_CRASH(("threw exception in ScriptDialog::OnLoad")); + ::AfxMessageBox("Unable to import scripts. The file contains invalid data or exceeds the player limit.", MB_OK); } } @@ -1633,13 +1642,15 @@ Bool ScriptDialog::ParsePlayersDataChunk(DataChunkInput &file, DataChunkInfo *in { ScriptDialog *pThis = (ScriptDialog *)userData; Int numNames = file.readInt(); + // TheSuperHackers @bugfix OmarAglan Reject player counts that cannot fit in the import array. + if (numNames < 0 || numNames > MAX_PLAYER_COUNT) { + return false; + } Int i; for (i=0; i=MAX_PLAYER_COUNT) break; pThis->m_readPlayerNames[i] = file.readAsciiString(); } - DEBUG_ASSERTCRASH(file.atEndOfChunk(), ("Unexpected data left over.")); - return true; + return file.atEndOfChunk(); } /** diff --git a/GeneralsMD/Code/Tools/WorldBuilder/src/ScriptDialog.cpp b/GeneralsMD/Code/Tools/WorldBuilder/src/ScriptDialog.cpp index 13715f4a395..170944e41c7 100644 --- a/GeneralsMD/Code/Tools/WorldBuilder/src/ScriptDialog.cpp +++ b/GeneralsMD/Code/Tools/WorldBuilder/src/ScriptDialog.cpp @@ -1541,8 +1541,16 @@ void ScriptDialog::OnLoad() file.registerParser( "WaypointsList", AsciiString::TheEmptyString, ParseWaypointDataChunk ); file.registerParser( "ScriptTeams", AsciiString::TheEmptyString, ParseTeamsDataChunk ); file.registerParser( "ScriptsPlayers", AsciiString::TheEmptyString, ParsePlayersDataChunk ); - if (!file.parse(this)) { - throw(ERROR_CORRUPT_FILE_FORMAT); + // TheSuperHackers @bugfix OmarAglan Restore sides and teams when script parsing fails. + SidesList sidesBeforeImport; + sidesBeforeImport = m_sides; + try { + if (!file.parse(this)) { + throw(ERROR_CORRUPT_FILE_FORMAT); + } + } catch(...) { + m_sides = sidesBeforeImport; + throw; } pDoc->setNextWaypointID(m_maxWaypoint); @@ -1552,6 +1560,11 @@ void ScriptDialog::OnLoad() REF_PTR_RELEASE(pUndo); // belongs to pDoc now. m_sides = *TheSidesList; + // TheSuperHackers @bugfix OmarAglan Add imported players at their map indices after parsing succeeds. + for (Int sideIndex = sidesBeforeImport.getNumSides(); sideIndex < m_sides.getNumSides(); sideIndex++) { + addPlayer(sideIndex); + } + if (m_firstReadObject) { AddObjectUndoable *pUndo = new AddObjectUndoable(pDoc, m_firstReadObject); pDoc->AddAndDoUndoable(pUndo); @@ -1576,10 +1589,10 @@ void ScriptDialog::OnLoad() curSide = m_curSelection.m_playerIndex; } else { Int j; + // TheSuperHackers @bugfix OmarAglan Match each imported player to the current map side by name. for (j=0; jgetDict()->getAsciiString(TheKey_playerName); - if (name == m_readPlayerNames[j]) { + AsciiString name = m_sides.getSideInfo(j)->getDict()->getAsciiString(TheKey_playerName); + if (name == m_readPlayerNames[i]) { curSide = j; break; } @@ -1639,7 +1652,7 @@ void ScriptDialog::OnLoad() } catch(...) { - DEBUG_CRASH(("threw exception in ScriptDialog::OnLoad")); + ::AfxMessageBox("Unable to import scripts. The file contains invalid data or exceeds the player limit.", MB_OK); } } @@ -1817,12 +1830,21 @@ Bool ScriptDialog::ParsePlayersDataChunk(DataChunkInput &file, DataChunkInfo *in readDicts = file.readInt(); } Int numNames = file.readInt(); + // TheSuperHackers @bugfix OmarAglan Reject player counts that cannot fit in the import array. + if (numNames < 0 || numNames > MAX_PLAYER_COUNT) { + return false; + } Int i; for (i=0; i=MAX_PLAYER_COUNT) break; pThis->m_readPlayerNames[i] = file.readAsciiString(); if (readDicts) { Dict sideDict = file.readDict(); + // TheSuperHackers @bugfix OmarAglan Validate the dictionary identity before adding a player. + Bool hasPlayerName; + AsciiString playerName = sideDict.getAsciiString(TheKey_playerName, &hasPlayerName); + if (!hasPlayerName || playerName != pThis->m_readPlayerNames[i]) { + return false; + } bool nameFound = false; for (Int j=0; j < pThis->m_sides.getNumSides(); j++) { AsciiString name = pThis->m_sides.getSideInfo(j)->getDict()->getAsciiString(TheKey_playerName); @@ -1834,19 +1856,23 @@ Bool ScriptDialog::ParsePlayersDataChunk(DataChunkInput &file, DataChunkInfo *in } } if (nameFound == false) { + // TheSuperHackers @bugfix OmarAglan Check capacity before adding a side and using its script list. + if (pThis->m_sides.getNumSides() >= MAX_PLAYER_COUNT) { + return false; + } // This side doesn't currently exist, so add it. pThis->m_sides.addSide(&sideDict); + SidesInfo* sides = pThis->m_sides.findSideInfo(playerName); + if (sides == nullptr) { + return false; + } ScriptList* pList = newInstance(ScriptList); - SidesInfo* sides = pThis->m_sides.findSideInfo(pThis->m_readPlayerNames[i]); // A script list must be created. sides->setScriptList(pList); - // Update the dialog. - pThis->addPlayer(i); } } } - DEBUG_ASSERTCRASH(file.atEndOfChunk(), ("Unexpected data left over.")); - return true; + return file.atEndOfChunk(); } /** From f16cf9ae9e62c47f6aedeb17e45ca5daafa3009a Mon Sep 17 00:00:00 2001 From: Omar Aglan Date: Sat, 3 Oct 2026 18:53:42 +0300 Subject: [PATCH 2/2] unify(worldbuilder): Align script import and export (#3409) --- .../include/ExportScriptsOptions.h | 2 + .../Tools/WorldBuilder/res/WorldBuilder.rc | 19 ++-- .../Code/Tools/WorldBuilder/res/resource.h | 3 +- .../WorldBuilder/src/ExportScriptsOptions.cpp | 11 +++ .../Tools/WorldBuilder/src/ScriptDialog.cpp | 86 +++++++++++++++++-- .../Tools/WorldBuilder/res/WorldBuilder.rc | 7 +- .../WorldBuilder/src/ExportScriptsOptions.cpp | 5 ++ .../Tools/WorldBuilder/src/ScriptDialog.cpp | 13 ++- 8 files changed, 126 insertions(+), 20 deletions(-) diff --git a/Generals/Code/Tools/WorldBuilder/include/ExportScriptsOptions.h b/Generals/Code/Tools/WorldBuilder/include/ExportScriptsOptions.h index c78869d01ae..9a9f97d2aeb 100644 --- a/Generals/Code/Tools/WorldBuilder/include/ExportScriptsOptions.h +++ b/Generals/Code/Tools/WorldBuilder/include/ExportScriptsOptions.h @@ -50,12 +50,14 @@ class ExportScriptsOptions : public CDialog static Bool m_waypoints; static Bool m_triggers; static Bool m_allScripts; + static Bool m_sides; public: Bool getDoUnits() {return m_units;} Bool getDoWaypoints() {return m_waypoints;} Bool getDoTriggers() {return m_triggers;} Bool getDoAllScripts() {return m_allScripts;} + Bool getDoSides() {return m_sides;} protected: diff --git a/Generals/Code/Tools/WorldBuilder/res/WorldBuilder.rc b/Generals/Code/Tools/WorldBuilder/res/WorldBuilder.rc index 4755a373920..c5040a9abac 100644 --- a/Generals/Code/Tools/WorldBuilder/res/WorldBuilder.rc +++ b/Generals/Code/Tools/WorldBuilder/res/WorldBuilder.rc @@ -1374,13 +1374,14 @@ BEGIN 225,117,8 END -IDD_EXPORT_SCRIPTS_OPTIONS DIALOG DISCARDABLE 0, 0, 186, 153 +// TheSuperHackers @fix OmarAglan Keep the export buttons inside the expanded dialog. +IDD_EXPORT_SCRIPTS_OPTIONS DIALOG DISCARDABLE 0, 0, 186, 173 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Export Script Options" FONT 8, "MS Sans Serif" BEGIN - DEFPUSHBUTTON "OK",IDOK,69,132,50,14 - PUSHBUTTON "Cancel",IDCANCEL,129,132,50,14 + DEFPUSHBUTTON "OK",IDOK,69,152,50,14 + PUSHBUTTON "Cancel",IDCANCEL,129,152,50,14 CONTROL "Include waypoints and waypoint paths.",IDC_WAYPOINTS, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,20,150,8 CONTROL "Include trigger areas.",IDC_TRIGGERS,"Button", @@ -1388,12 +1389,14 @@ BEGIN CONTROL "Include units and buildings.",IDC_UNITS,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,12,61,154,9 GROUPBOX "Include items referenced in the scripts:",IDC_STATIC,7, - 7,172,73 + 7,172,88 CONTROL "Export all scripts.",IDC_ALL_SCRIPTS,"Button", - BS_AUTORADIOBUTTON,15,94,97,8 + BS_AUTORADIOBUTTON,15,115,97,8 CONTROL "Export selected scripts.",IDC_SELECTED_SCRIPTS,"Button", - BS_AUTORADIOBUTTON,15,106,99,8 - GROUPBOX "Export Mode:",IDC_STATIC,7,83,172,39 + BS_AUTORADIOBUTTON,15,127,99,8 + GROUPBOX "Export Mode:",IDC_STATIC,7,104,172,39 + CONTROL "Include sides.",IDC_SIDES,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,12,79,154,9 END IDD_GROVE_OPTIONS DIALOG DISCARDABLE 0, 0, 200, 173 @@ -2089,7 +2092,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 179 TOPMARGIN, 7 - BOTTOMMARGIN, 146 + BOTTOMMARGIN, 166 END IDD_GROVE_OPTIONS, DIALOG diff --git a/Generals/Code/Tools/WorldBuilder/res/resource.h b/Generals/Code/Tools/WorldBuilder/res/resource.h index af7dd6d8432..03ec3cc550e 100644 --- a/Generals/Code/Tools/WorldBuilder/res/resource.h +++ b/Generals/Code/Tools/WorldBuilder/res/resource.h @@ -517,6 +517,7 @@ #define IDC_SCRIPT_PREFIX15 1313 #define IDC_SCRIPT_PREFIX16 1314 #define IDC_TeamGeneric_Script1 1315 +#define IDC_SIDES 1329 #define ID_BRUSH_TOOL 32771 #define IDM_ShowGrid 32772 #define ID_FEATHERTOOL 32791 @@ -697,7 +698,7 @@ #define _APS_3D_CONTROLS 1 #define _APS_NEXT_RESOURCE_VALUE 232 #define _APS_NEXT_COMMAND_VALUE 33344 -#define _APS_NEXT_CONTROL_VALUE 1326 +#define _APS_NEXT_CONTROL_VALUE 1330 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/Generals/Code/Tools/WorldBuilder/src/ExportScriptsOptions.cpp b/Generals/Code/Tools/WorldBuilder/src/ExportScriptsOptions.cpp index 1fcad1a76eb..19b883f1d35 100644 --- a/Generals/Code/Tools/WorldBuilder/src/ExportScriptsOptions.cpp +++ b/Generals/Code/Tools/WorldBuilder/src/ExportScriptsOptions.cpp @@ -29,6 +29,7 @@ Bool ExportScriptsOptions::m_units = true; Bool ExportScriptsOptions::m_waypoints = true; Bool ExportScriptsOptions::m_triggers = true; Bool ExportScriptsOptions::m_allScripts = false; +Bool ExportScriptsOptions::m_sides = true; ExportScriptsOptions::ExportScriptsOptions(CWnd* pParent /*=nullptr*/) : CDialog(ExportScriptsOptions::IDD, pParent) @@ -68,6 +69,9 @@ void ExportScriptsOptions::OnOK() m_triggers = pButton->GetCheck()==1; pButton = (CButton*)GetDlgItem(IDC_ALL_SCRIPTS); m_allScripts = pButton->GetCheck()==1; + pButton = (CButton*)GetDlgItem(IDC_SIDES); + m_sides = pButton->GetCheck()==1; + CDialog::OnOK(); } @@ -86,6 +90,13 @@ BOOL ExportScriptsOptions::OnInitDialog() pButton->SetCheck(m_allScripts?1:0); pButton = (CButton*)GetDlgItem(IDC_SELECTED_SCRIPTS); pButton->SetCheck(m_allScripts?0:1); + pButton = (CButton*)GetDlgItem(IDC_SIDES); +#if RTS_GENERALS && RETAIL_COMPATIBLE_DATA + // Retail Generals script files do not store side dictionaries. + m_sides = false; + pButton->EnableWindow(FALSE); +#endif + pButton->SetCheck(m_sides?1:0); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE diff --git a/Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp b/Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp index 374a44e7f08..699823ced6b 100644 --- a/Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp +++ b/Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp @@ -1100,6 +1100,8 @@ void ScriptDialog::scanForWaypointsAndTeams(Script *pScript, Bool doUnits, Bool } #define K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_1 1 +// Added in Zero Hour +#define K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_2 2 /** Write out selected scripts, and possibly waypoints, trigger areas & teams. */ void ScriptDialog::OnSave() @@ -1108,6 +1110,7 @@ void ScriptDialog::OnSave() Bool doTriggerAreas = true; Bool doUnits = true; Bool doAllScripts = true; + Bool doSides = true; Int i; ExportScriptsOptions optionsDlg; @@ -1118,6 +1121,7 @@ void ScriptDialog::OnSave() doUnits = optionsDlg.getDoUnits(); doTriggerAreas = optionsDlg.getDoTriggers(); doAllScripts = optionsDlg.getDoAllScripts(); + doSides = optionsDlg.getDoSides(); Script *pScript = getCurScript(); ScriptGroup *pGroup = getCurGroup(); @@ -1211,12 +1215,27 @@ void ScriptDialog::OnSave() ScriptList::WriteScriptsDataChunk(chunkWriter, scripts, numScriptLists); /***************Players DATA ***************/ - chunkWriter.openDataChunk("ScriptsPlayers", K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_1); - if (doAllScripts) { +#if RTS_GENERALS && RETAIL_COMPATIBLE_DATA + const DataChunkVersionType playersVersion = K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_1; + doSides = false; +#else + const DataChunkVersionType playersVersion = K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_2; +#endif + chunkWriter.openDataChunk("ScriptsPlayers", playersVersion); + if (playersVersion >= K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_2) { + chunkWriter.writeInt(doSides); + } + if (doAllScripts || doSides) { chunkWriter.writeInt(m_sides.getNumSides()); for (i=0; igetDict()->getAsciiString(TheKey_playerName); chunkWriter.writeAsciiString(name); + + if (doSides) { + // The user has requested that the sides get exported. + chunkWriter.writeDict(*m_sides.getSideInfo(i)->getDict()); + } + } } else { chunkWriter.writeInt(1); @@ -1393,6 +1412,11 @@ void ScriptDialog::OnLoad() REF_PTR_RELEASE(pUndo); // belongs to pDoc now. m_sides = *TheSidesList; + // TheSuperHackers @bugfix OmarAglan Add imported players at their map indices after parsing succeeds. + for (Int sideIndex = sidesBeforeImport.getNumSides(); sideIndex < m_sides.getNumSides(); sideIndex++) { + addPlayer(sideIndex); + } + if (m_firstReadObject) { AddObjectUndoable *pUndo = new AddObjectUndoable(pDoc, m_firstReadObject); pDoc->AddAndDoUndoable(pUndo); @@ -1464,13 +1488,21 @@ void ScriptDialog::OnLoad() scripts[i]->discard(); /* Frees the script list, but none of it's children, as they have been copied into the current scripts. */ scripts[i] = nullptr; - reloadPlayer(curSide, pSL); + //reloadPlayer(curSide, pSL); } else { deleteInstance(scripts[i]); scripts[i] = nullptr; } } + for (i = 0; i < m_sides.getNumSides(); i++) { + // Make sure that the dialog tree is updated. + ScriptList *pSL = m_sides.getSideInfo(i)->getScriptList(); + reloadPlayer(i, pSL); + updateIcons(TVI_ROOT); + } + + } catch(...) { ::AfxMessageBox("Unable to import scripts. The file contains invalid data or exceeds the player limit.", MB_OK); } @@ -1616,15 +1648,19 @@ Bool ScriptDialog::ParseTeamsDataChunk(DataChunkInput &file, DataChunkInfo *info TeamsInfo ti; ti.init(&teamDict); CFixTeamOwnerDialog fix(&ti, &pThis->m_sides); + bool nameSet = false; if (fix.DoModal() == IDOK) { if (fix.pickedValidTeam()) { teamDict.setAsciiString(TheKey_teamOwner, fix.getSelectedOwner()); + nameSet = true; } } - AsciiString neutralPlayerName; // neutral player name is empty string - // player doesn't exist, so add it to the neutral player. - teamDict.setAsciiString(TheKey_teamOwner, neutralPlayerName); + if (nameSet == false) { + AsciiString neutralPlayerName; // neutral player name is empty string + // player doesn't exist, so add it to the neutral player. + teamDict.setAsciiString(TheKey_teamOwner, neutralPlayerName); + } pThis->m_sides.addTeam(&teamDict); } } @@ -1641,6 +1677,10 @@ Bool ScriptDialog::ParseTeamsDataChunk(DataChunkInput &file, DataChunkInfo *info Bool ScriptDialog::ParsePlayersDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData) { ScriptDialog *pThis = (ScriptDialog *)userData; + Int readDicts = 0; + if (info->version >= K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_2) { + readDicts = file.readInt(); + } Int numNames = file.readInt(); // TheSuperHackers @bugfix OmarAglan Reject player counts that cannot fit in the import array. if (numNames < 0 || numNames > MAX_PLAYER_COUNT) { @@ -1649,6 +1689,40 @@ Bool ScriptDialog::ParsePlayersDataChunk(DataChunkInput &file, DataChunkInfo *in Int i; for (i=0; im_readPlayerNames[i] = file.readAsciiString(); + if (readDicts) { + Dict sideDict = file.readDict(); + // TheSuperHackers @bugfix OmarAglan Validate the dictionary identity before adding a player. + Bool hasPlayerName; + AsciiString playerName = sideDict.getAsciiString(TheKey_playerName, &hasPlayerName); + if (!hasPlayerName || playerName != pThis->m_readPlayerNames[i]) { + return false; + } + bool nameFound = false; + for (Int j=0; j < pThis->m_sides.getNumSides(); j++) { + AsciiString name = pThis->m_sides.getSideInfo(j)->getDict()->getAsciiString(TheKey_playerName); + + if (name == pThis->m_readPlayerNames[i]) { + // The side already exists so don't add it or overwrite the old data. + nameFound = true; + break; + } + } + if (nameFound == false) { + // TheSuperHackers @bugfix OmarAglan Check capacity before adding a side and using its script list. + if (pThis->m_sides.getNumSides() >= MAX_PLAYER_COUNT) { + return false; + } + // This side doesn't currently exist, so add it. + pThis->m_sides.addSide(&sideDict); + SidesInfo* sides = pThis->m_sides.findSideInfo(playerName); + if (sides == nullptr) { + return false; + } + ScriptList* pList = newInstance(ScriptList); + // A script list must be created. + sides->setScriptList(pList); + } + } } return file.atEndOfChunk(); } diff --git a/GeneralsMD/Code/Tools/WorldBuilder/res/WorldBuilder.rc b/GeneralsMD/Code/Tools/WorldBuilder/res/WorldBuilder.rc index 6cbb57e5ac1..cc93f64a628 100644 --- a/GeneralsMD/Code/Tools/WorldBuilder/res/WorldBuilder.rc +++ b/GeneralsMD/Code/Tools/WorldBuilder/res/WorldBuilder.rc @@ -1322,7 +1322,8 @@ BEGIN 225,117,8 END -IDD_EXPORT_SCRIPTS_OPTIONS DIALOG DISCARDABLE 0, 0, 186, 153 +// TheSuperHackers @fix OmarAglan Keep the export buttons inside the expanded dialog. +IDD_EXPORT_SCRIPTS_OPTIONS DIALOG DISCARDABLE 0, 0, 186, 173 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Export Script Options" FONT 8, "MS Sans Serif" @@ -1342,7 +1343,7 @@ BEGIN CONTROL "Export selected scripts.",IDC_SELECTED_SCRIPTS,"Button", BS_AUTORADIOBUTTON,15,127,99,8 GROUPBOX "Export Mode:",IDC_STATIC,7,104,172,39 - CONTROL "Include sides.",IDC_SIDES,"Button",BS_AUTOCHECKBOX | + CONTROL "Include sides.",IDC_SIDES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,79,154,9 END @@ -2208,7 +2209,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 179 TOPMARGIN, 7 - BOTTOMMARGIN, 146 + BOTTOMMARGIN, 166 END IDD_GROVE_OPTIONS, DIALOG diff --git a/GeneralsMD/Code/Tools/WorldBuilder/src/ExportScriptsOptions.cpp b/GeneralsMD/Code/Tools/WorldBuilder/src/ExportScriptsOptions.cpp index 227df718459..25526588604 100644 --- a/GeneralsMD/Code/Tools/WorldBuilder/src/ExportScriptsOptions.cpp +++ b/GeneralsMD/Code/Tools/WorldBuilder/src/ExportScriptsOptions.cpp @@ -91,6 +91,11 @@ BOOL ExportScriptsOptions::OnInitDialog() pButton = (CButton*)GetDlgItem(IDC_SELECTED_SCRIPTS); pButton->SetCheck(m_allScripts?0:1); pButton = (CButton*)GetDlgItem(IDC_SIDES); +#if RTS_GENERALS && RETAIL_COMPATIBLE_DATA + // Retail Generals script files do not store side dictionaries. + m_sides = false; + pButton->EnableWindow(FALSE); +#endif pButton->SetCheck(m_sides?1:0); return TRUE; // return TRUE unless you set the focus to a control diff --git a/GeneralsMD/Code/Tools/WorldBuilder/src/ScriptDialog.cpp b/GeneralsMD/Code/Tools/WorldBuilder/src/ScriptDialog.cpp index 170944e41c7..2a235830d47 100644 --- a/GeneralsMD/Code/Tools/WorldBuilder/src/ScriptDialog.cpp +++ b/GeneralsMD/Code/Tools/WorldBuilder/src/ScriptDialog.cpp @@ -1257,6 +1257,7 @@ void ScriptDialog::scanForWaypointsAndTeams(Script *pScript, Bool doUnits, Bool } #define K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_1 1 +// Added in Zero Hour #define K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_2 2 /** Write out selected scripts, and possibly waypoints, trigger areas & teams. */ @@ -1371,8 +1372,16 @@ void ScriptDialog::OnSave() ScriptList::WriteScriptsDataChunk(chunkWriter, scripts, numScriptLists); /***************Players DATA ***************/ - chunkWriter.openDataChunk("ScriptsPlayers", K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_2); - chunkWriter.writeInt(doSides); +#if RTS_GENERALS && RETAIL_COMPATIBLE_DATA + const DataChunkVersionType playersVersion = K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_1; + doSides = false; +#else + const DataChunkVersionType playersVersion = K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_2; +#endif + chunkWriter.openDataChunk("ScriptsPlayers", playersVersion); + if (playersVersion >= K_PLAYERS_NAMES_FOR_SCRIPTS_VERSION_2) { + chunkWriter.writeInt(doSides); + } if (doAllScripts || doSides) { chunkWriter.writeInt(m_sides.getNumSides()); for (i=0; i