diff --git a/.gitignore b/.gitignore index 4d44048..abf6e72 100644 --- a/.gitignore +++ b/.gitignore @@ -129,25 +129,25 @@ bin-release/ !*.code-workspace # Repo-specific UI/resource assets that should not enter source history -*.dfm -*.fmx -*.nfm -*.ddp -*.xfm -*.vlb -*.ui -*.res -*.dcr -*.ico -*.bmp -*.gif -*.png -*.jpg -*.jpeg -*Form*.pas -*Form*.dfm -*Form*.ddp -ReadMe, really.pdf +#*.dfm +#*.fmx +#*.nfm +#*.ddp +#*.xfm +#*.vlb +#*.ui +#*.res +#*.dcr +#*.ico +#*.bmp +#*.gif +#*.png +#*.jpg +#*.jpeg +#*Form*.pas +#*Form*.dfm +#*Form*.ddp +#ReadMe, really.pdf # Built Visual Studio Code Extensions *.vsix diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e8f25dd --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "wiki"] + path = wiki + url = https://github.com/OpenKotOR/ChangeEdit.wiki.git diff --git a/.vscode/launch.json b/.vscode/launch.json index b88a9be..7fea4fa 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,13 +4,15 @@ // Pascal extension is language/build support only, so native binaries still // run through VS Code's native debugger adapters. // On Linux/macOS, launching is proxied through Wine to run the Windows .exe. + // Inputs are intentionally sourced from changeEdit.* settings so launch is + // non-interactive by default and can run in automation contexts. "version": "0.2.0", "configurations": [ { "name": "ChangeEdit: Launch built executable", "type": "cppdbg", "request": "launch", - "program": "${workspaceFolder}\\${input:changeEditExecutable}", + "program": "${workspaceFolder}\\${config:changeEdit.executable}", "args": [], "cwd": "${workspaceFolder}", "stopAtEntry": false, @@ -22,16 +24,16 @@ "linux": { "program": "/usr/bin/env", "args": [ - "${input:wineCommand}", - "${workspaceFolder}/${input:changeEditExecutable}" + "${config:changeEdit.wineCommand}", + "${workspaceFolder}/${config:changeEdit.executable}" ], "MIMode": "gdb" }, "osx": { "program": "/usr/bin/env", "args": [ - "${input:wineCommand}", - "${workspaceFolder}/${input:changeEditExecutable}" + "${config:changeEdit.wineCommand}", + "${workspaceFolder}/${config:changeEdit.executable}" ], "MIMode": "lldb" } @@ -50,19 +52,5 @@ "order": 2 } } - ], - "inputs": [ - { - "id": "changeEditExecutable", - "type": "promptString", - "description": "Path to the built ChangeEdit executable, relative to the workspace root", - "default": "ChangEd.exe" - }, - { - "id": "wineCommand", - "type": "promptString", - "description": "Wine launcher to use on Linux/macOS", - "default": "wine" - } ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index cb26e6d..08722a0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -39,5 +39,8 @@ "/c" ] }, + "changeEdit.delphiCompiler": "dcc32.exe", + "changeEdit.executable": "ChangEd.exe", + "changeEdit.wineCommand": "wine", "omnipascal.defaultDevelopmentEnvironment": "Delphi" } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index dbaf800..ebb10fc 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,5 +1,7 @@ { // Repo-matched task surface for the Delphi 7 ChangeEdit workflow. + // Task defaults come from changeEdit.* workspace settings to avoid prompt + // inputs and keep runs deterministic in local and CI-like automation. "version": "2.0.0", "windows": { "options": { diff --git a/BUILD.md b/BUILD.md index feeb266..20221d1 100644 --- a/BUILD.md +++ b/BUILD.md @@ -63,6 +63,8 @@ The compiler will produce `ChangEd.exe` and `.dcu` files in the project director If the compiler cannot be found, the task fails with a configuration message telling you to update `changeEdit.delphiCompiler`. +Both task and launch profiles are intentionally non-interactive: they read from `changeEdit.*` workspace settings instead of prompting for runtime inputs. + --- ## Compiler options (summary) diff --git a/ChangEd.res b/ChangEd.res new file mode 100644 index 0000000..12c386e Binary files /dev/null and b/ChangEd.res differ diff --git a/README.md b/README.md index 693be2f..ca9b25f 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ A `changes.ini` file tells TSLPatcher which game files to modify and how. ChangeEdit exposes every supported operation as a point-and-click form: | Section | Operations | -|---------|-----------| +| --- | --- | | **Settings** | Patch caption, confirmation text, log style, mode, backup count, required-file checks | | **TLK** | Map `StrRef` tokens to localised dialog strings, optional sound overrides | | **2DA** | Add rows, modify cells, copy rows, add columns — for binary `.2da` game tables | @@ -28,7 +28,7 @@ A `changes.ini` file tells TSLPatcher which game files to modify and how. Change ## File formats handled | Format | Extension(s) | Handler unit | -|--------|-------------|-------------| +| --- | --- | --- | | 2DA Binary v2.b | `.2da` | `U2DAEdit` | | GFF v3.2 | `.gff`, `.uti`, `.utc`, `.dlg`, `.ssf`, … | `UGFFFile` | | Dialog strings | `.tlk` | `UTLKFile` | @@ -38,7 +38,7 @@ A `changes.ini` file tells TSLPatcher which game files to modify and how. Change ## Quick start -1. Build the project with Delphi 7 on Windows (see [BUILD.md](BUILD.md)). +1. Build the project with Delphi 7 on Windows, or run the VS Code task wrapper with a Wine-hosted Delphi 7 compiler on Linux/macOS (see [BUILD.md](BUILD.md)). 2. Launch `ChangEd.exe`. 3. **File → Open** to load an existing `changes.ini`, or **File → New** to start from scratch. 4. Navigate the left-hand tree to the section you want to edit. @@ -49,7 +49,7 @@ A `changes.ini` file tells TSLPatcher which game files to modify and how. Change ## Project structure -``` +```text ChangEd.dpr Program entry point (registers all forms) UMainForm.pas/.dfm Main window (tree + stacked panels) U2DAEdit.pas Binary 2DA v2.b reader/writer @@ -66,11 +66,20 @@ Full module-by-module breakdown: [ARCHITECTURE.md](ARCHITECTURE.md) --- +## Wiki + +The project wiki is published on GitHub and versioned in the top-level `wiki/` submodule of this repository. + +- Browse the published pages: +- After cloning, fetch the local wiki working tree with `git submodule update --init wiki` + +--- + ## Requirements -- **Build host**: Windows (Delphi 7 compiler — see [BUILD.md](BUILD.md)) +- **Build host**: Windows with Delphi 7, or Linux/macOS through Wine when the VS Code build task is pointed at a Wine-visible `dcc32.exe` (see [BUILD.md](BUILD.md)) - **Runtime**: Windows 9x / NT 4+ (XP visual styles via `TXPManifest`) -- **Linux**: Run the compiled `.exe` under Wine +- **Linux / macOS**: Run the compiled `.exe` under Wine --- diff --git a/UAddColForm.ddp b/UAddColForm.ddp new file mode 100644 index 0000000..4370276 Binary files /dev/null and b/UAddColForm.ddp differ diff --git a/UAddColForm.dfm b/UAddColForm.dfm new file mode 100644 index 0000000..365503d Binary files /dev/null and b/UAddColForm.dfm differ diff --git a/UAddColForm.pas b/UAddColForm.pas new file mode 100644 index 0000000..0a7b44f --- /dev/null +++ b/UAddColForm.pas @@ -0,0 +1,617 @@ +unit UAddColForm; + +interface + +uses + Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, + StdCtrls, Buttons, Grids, ExtCtrls, UST_IniFile; + +type + TAdd2daColForm = class(TForm) + Panel1: TPanel; + lblHeadline: TLabel; + grid2daMod: TStringGrid; + btnDeleteKey: TSpeedButton; + lblGridHeading: TLabel; + Label4: TLabel; + Label5: TLabel; + Label6: TLabel; + Label7: TLabel; + Label8: TLabel; + Label9: TLabel; + Label10: TLabel; + Label11: TLabel; + GroupBox1: TGroupBox; + edNewLabel: TEdit; + edDefaultVal: TEdit; + Label1: TLabel; + Label2: TLabel; + GroupBox2: TGroupBox; + cbRowIdent: TComboBox; + edRowNum: TEdit; + Label3: TLabel; + cbRowValue: TComboBox; + btnClose: TButton; + btnAddToList: TSpeedButton; + btnEditColValue: TSpeedButton; + Label12: TLabel; + Label13: TLabel; + Label14: TLabel; + procedure btnDeleteKeyClick(Sender: TObject); + procedure btnEditColValueClick(Sender: TObject); + procedure btnAddToListClick(Sender: TObject); + procedure btnLoadTokensClick(Sender: TObject); + procedure btnCloseClick(Sender: TObject); + procedure edRowNumKeyPress(Sender: TObject; var Key: Char); + procedure cbRowIdentChange(Sender: TObject); + procedure edRowNumKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure cbRowValueKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure cbRowIdentKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure grid2daModKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure cbRowValueDropDown(Sender: TObject); + procedure grid2daModDblClick(Sender: TObject); + private + { Private declarations } + public + { Public declarations } + box_2daname : string; + box_section : string; + box_type : string; + box_ini : TST_IniFile; + + procedure LoadIniData(); + procedure UpdateColLabel(sDef : string; sVal : string); + + function ShowInfoBox(sMessage : string) : word; + function ShowAlertBox(sMessage : string) : word; + end; + +var + Add2daColForm: TAdd2daColForm; + +implementation + +{$R *.DFM} + +function TAdd2daColForm.ShowInfoBox(sMessage : string) : word; +begin + result := MessageDlgPos(sMessage, mtInformation, [mbOK], 0, Left + 64, Top + 128); +end; + +function TAdd2daColForm.ShowAlertBox(sMessage : string) : word; +begin + result := MessageDlg(sMessage, mtWarning, [mbOK], 0); +end; + +procedure TAdd2daColForm.UpdateColLabel(sDef : string; sVal : string); +var + oList : TStringList; + sKey : string; + sLabel : string; + sValue : string; + i : integer; +begin + sKey := 'ColumnLabel'; + oList := TStringList.Create(); + try + box_ini.readsection(box_section, oList); + sLabel := oList.strings[0]; + sValue := box_ini.readstring(box_section, sLabel, ''); + + // The specified identifier is already at the top of the list, + // or the list is empty, just assign the new value. + if (oList.count = 0) or ((sLabel = sKey) and (oList.strings[1] = 'DefaultValue')) then begin + box_ini.writestring(box_section, sKey, sVal); + box_ini.writestring(box_section, 'DefaultValue', sDef); + end + // The specified identifier is not at the top of the list, remove + // all other keys so it can be added at the top + else begin + // Copy all the other keys to a temporary section briefly.... + for i := 0 to (oList.count - 1) do begin + sLabel := oList.strings[i]; + sValue := box_ini.readstring(box_section, sLabel, ''); + + if (sLabel <> sKey) and (sLabel <> 'DefaultValue') then + box_ini.writestring('aaTempWorkSectionaa', sLabel, sValue); + + box_ini.deletekey(box_section, sLabel); + end; + + // Clear the current section and then write the identifier to it. + oList.clear(); + // box_ini.EraseSection(box_section); + box_ini.writestring(box_section, sKey, sVal); + box_ini.writestring(box_section, 'DefaultValue', sDef); + + // Write the values back to the section from the temporary section... + box_ini.readsection('aaTempWorkSectionaa', oList); + for i := 0 to (oList.count - 1) do begin + sLabel := oList.strings[i]; + sValue := box_ini.readstring('aaTempWorkSectionaa', sLabel, ''); + box_ini.writestring(box_section, sLabel, sValue); + end; + + // Delete the temporary section... + box_ini.EraseSection('aaTempWorkSectionaa'); + end; + edNewLabel.text := sVal; + edDefaultVal.text := sDef; + + finally + oList.free(); + end; + // ------------------------------------------------------------------------- +end; + +procedure TAdd2daColForm.LoadIniData(); +var + oList : TStringList; + i : integer; + iCnt : integer; + sKey : string; + sVal : string; + sDef : string; +begin + grid2daMod.cols[0].clear(); + grid2daMod.cols[1].clear(); + grid2daMod.rowcount := 1; + grid2daMod.colwidths[0] := 82; + grid2daMod.colwidths[1] := grid2daMod.width - (grid2daMod.colwidths[0] + 6); + + edNewLabel.clear(); + edDefaultVal.clear(); + cbRowIdent.ItemIndex := 0; + edRowNum.clear(); + cbRowValue.clear(); + + oList := TStringList.Create(); + try + box_ini.readsection(box_section, oList); + + if (oList.Count > 0) then begin + // If a Column identifier doesn't exist in the first position one + // must be added. So do it. :) + sKey := oList.strings[0]; + sVal := box_ini.readstring(box_section, sKey, ''); + sDef := box_ini.readstring(box_section, 'DefaultValue', '****'); + + if (sKey <> 'ColumnLabel') then begin + UpdateColLabel(sDef, 'NewColumn'); + sKey := 'ColumnLabel'; + sVal := 'NewColumn'; + end; + + if (sKey = 'ColumnLabel') then begin + edNewLabel.text := sVal; + end; + + edDefaultVal.text := sDef; + + // Populate the column modifier grid with existing keys.... + iCnt := 0; + for i := 0 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sVal := box_ini.readstring(box_section, sKey, ''); + + if (sVal <> '') + and (sKey <> 'ColumnLabel') + and (sKey <> 'DefaultValue') + then begin + grid2daMod.rowcount := grid2daMod.rowcount + 1; + grid2daMod.cells[0, iCnt] := sKey; + grid2daMod.cells[1, iCnt] := sVal; + inc(iCnt); + end; + end; + grid2daMod.rowcount := grid2daMod.rowcount - 1; + end + else begin + // Make sure a row identifier exists at the top... + box_ini.writestring(box_section, 'ColumnLabel', 'NewColumn'); + box_ini.writestring(box_section, 'DefaultValue', '****'); + edNewLabel.text := 'NewColumn'; + edDefaultVal.text := '****'; + end; + finally + oList.free(); + end; +end; + +procedure TAdd2daColForm.btnDeleteKeyClick(Sender: TObject); +var + i : integer; +begin + // FIX(2005-05-24) Ta inte bort om raden är tom... + if (grid2daMod.cells[0, grid2daMod.Selection.top] = '') then + exit; + + if (grid2daMod.rowcount > 0) then begin + box_ini.deletekey(box_section, grid2daMod.cells[0, grid2daMod.Selection.top]); + + for i := grid2daMod.Selection.top to (grid2daMod.rowcount-1) do begin + grid2daMod.Rows[i] := grid2daMod.Rows[i+1]; + end; + grid2daMod.RowCount := grid2daMod.rowcount - 1; + end; +end; + +procedure TAdd2daColForm.btnEditColValueClick(Sender: TObject); +var + sText : string; +begin + sText := grid2daMod.cells[0, grid2daMod.Selection.Top]; + //cbColLabel.text := sText; + + if (lowercase(copy(sText, 1, 1)) = 'l') then begin + cbRowIdent.itemindex := 1; + edRowNum.text := copy(sText, 2, Length(sText)-1); + end + else if (lowercase(copy(sText, 1, 1)) = 'i') then begin + cbRowIdent.itemindex := 0; + edRowNum.text := copy(sText, 2, Length(sText)-1); + end + else if (copy(sText, 1, 9) = '2DAMEMORY') then begin + cbRowIdent.itemindex := 2; + edRowNum.text := sText; + end; + + cbRowValue.text := grid2daMod.cells[1, grid2daMod.Selection.Top]; + cbRowValue.setfocus; +end; + +procedure TAdd2daColForm.btnAddToListClick(Sender: TObject); +var + i : integer; + bFound : boolean; + sRow : string; +begin + sRow := ''; + if (cbRowIdent.itemindex = 0) then + sRow := 'I' + else if (cbRowIdent.itemindex = 1) then + sRow := 'L'; + + sRow := sRow + edRowNum.text; + + if (Length(sRow) <= 1) then begin + ShowAlertBox('You must specify a valid identifier for the row to have its value changed!'); + exit; + end; + + if (Length(cbRowValue.text) < 1) then begin + ShowAlertBox('You must specify a value the row should be changed to. Set it to "****" if you want a blank entry.'); + exit; + end; + + bFound := False; + for i := 0 to (grid2daMod.rowcount-1) do begin + if (sRow = grid2daMod.cells[0, i]) then begin + grid2daMod.cells[1, i] := cbRowValue.text; + grid2daMod.row := i; + box_ini.WriteString(box_section, sRow, cbRowValue.text); + edRowNum.clear(); + cbRowValue.text := ''; + cbRowIdent.itemindex := 0; + edRowNum.setfocus(); + bFound := True; + break; + end; + end; + + if not bFound then begin + i := grid2daMod.rowcount; + grid2daMod.rowcount := i + 1; + + if (grid2daMod.cells[0, i-1] = '') and (grid2daMod.cells[0, i-1] = '') then begin + i := i - 1; + grid2daMod.rowcount := grid2daMod.rowcount - 1; + end; + + grid2daMod.cells[0, i] := sRow; + grid2daMod.cells[1, i] := cbRowValue.text; + grid2daMod.row := i; + + box_ini.WriteString(box_section, sRow, cbRowValue.text); + + edRowNum.clear(); + cbRowValue.text := ''; + cbRowIdent.itemindex := 0; + edRowNum.setfocus(); + end; +end; + +procedure TAdd2daColForm.btnLoadTokensClick(Sender: TObject); +var + oList : TStringList; + oMods : TStringList; + oCols : TStringList; + i : integer; + n : integer; + j : integer; + sKey : string; + sVal : string; + sFile : string; + sMod : string; + sLabel : string; + bStop : boolean; +begin + // 1. Fetch all StringReference Tokens. + // 2. Fetch all 2DAMEMORY tokens that have had a value assigned to them thus far... + + // Clear ze box of any existing values, but keep anything the user has written + // or selected previously.... + sVal := cbRowValue.text; + cbRowValue.clear(); + cbRowValue.text := sVal; + + oList := TStringList.Create(); + box_ini.readsection('TLKList', oList); + + // Load the StrRef tokens... + for i := 0 to (oList.Count - 1) do begin + sKey := oList.Strings[i]; + sVal := box_ini.readstring('TLKList', sKey, ''); + + if (sKey <> '') and (sVal <> '') then begin + if (copy(sKey, 1, 6) = 'StrRef') then begin + cbRowValue.Items.Add(sKey); + end; + end; + end; + + oList.free(); + oList := TStringList.Create(); + box_ini.readsection('2DAList', oList); + bStop := False; + + // Check through all 2DA file modifiers for tokens being set... + for i := 0 to (oList.Count - 1) do begin + sFile := box_ini.readstring('2DAList', oList.Strings[i], ''); + if (sFile <> '') then begin + oMods := TStringList.Create(); + try + // Check all modifiers for this particular file... + box_ini.readsection(sFile, oMods); + for n := 0 to (oMods.count - 1) do begin + sMod := oMods.Strings[n]; + sLabel := box_ini.readstring(sFile, sMod, ''); + + if (sLabel <> '') then begin + oCols := TStringList.Create(); + try + // Check each column label for this Modifier... + box_ini.ReadSection(sLabel, oCols); + for j := 0 to (oCols.count-1) do begin + sKey := oCols.strings[j]; + sVal := box_ini.readstring(sLabel, sKey, ''); + + // Check if the KEY is a unique 2DAMEMORY token + // instead of a column label. If so add to list. + if (sKey <> '') + and (copy(sKey, 1, 9) = '2DAMEMORY') + and (cbRowValue.Items.IndexOf(sKey) = -1) + then begin + cbRowValue.Items.Add(sKey); + end; + end; + finally + oCols.free(); + end; + end; + + // Don't parse tokens that are set after this modifier has been executed. + // Modifier sections coming after the current one should be skipped. + if (sLabel = box_section) then begin + bStop := True; + break; + end; + end; + finally + oMods.free(); + end; + end; + + // Don't parse tokens that are set after this modifier has been executed. + // Files coming after the current one should be skipped. + if (bStop = True) or (sFile = box_2daname) then begin + break; + end; + end; + + ShowInfoBox('Relevant tokens have been loaded into the dropdown list of the Value box.'); + cbRowValue.setfocus; +end; + +procedure TAdd2daColForm.btnCloseClick(Sender: TObject); +var + sCol : string; + sDef : string; +begin + sCol := edNewLabel.text; + sDef := edDefaultVal.text; + + // make sure changes to the row identifier are saved... + if (sCol <> '') and (sDef <> '') then begin + UpdateColLabel(sDef, sCol); + + end; +end; + +procedure TAdd2daColForm.edRowNumKeyPress(Sender: TObject; var Key: Char); +var + sTmp : String; +begin + if(cbRowIdent.itemindex = 0) then begin + if not (Key in [#8, '0'..'9']) then begin + Key := #0; + beep(); + end; + end + else if(cbRowIdent.itemindex = 2) then begin + sTmp := uppercase(Key); + Key := sTmp[1]; + end; +end; + +procedure TAdd2daColForm.cbRowIdentChange(Sender: TObject); +begin + if (cbRowIdent.itemindex = 2) then begin + if (copy(edRowNum.text, 1, 9) <> '2DAMEMORY') then begin + edRowNum.clear(); + edRowNum.text := '2DAMEMORY'; + edRowNum.setfocus(); + edRowNum.selstart := 9; + end; + end; +end; + +procedure TAdd2daColForm.edRowNumKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (Shift = [ssCtrl, ssShift]) and (key = VK_RIGHT) then begin + btnAddToListClick(btnAddToList); + end; +end; + +procedure TAdd2daColForm.cbRowValueKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (Shift = [ssCtrl, ssShift]) and (Key = VK_RIGHT) then begin + btnAddToListClick(btnAddToList); + end; +end; + +procedure TAdd2daColForm.cbRowIdentKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (Shift = [ssCtrl, ssShift]) and (key = VK_RIGHT) then begin + btnAddToListClick(btnAddToList); + end; +end; + +procedure TAdd2daColForm.grid2daModKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (Shift = [ssCtrl, ssShift]) and (key = VK_LEFT) then begin + btnEditColValueClick(btnEditColValue); + end; +end; + +procedure TAdd2daColForm.cbRowValueDropDown(Sender: TObject); +var + oList : TStringList; + oMods : TStringList; + oCols : TStringList; + i : integer; + n : integer; + j : integer; + sKey : string; + sVal : string; + sFile : string; + sMod : string; + sLabel : string; + bStop : boolean; + oOldCursor : TCursor; +begin + // 1. Fetch all StringReference Tokens. + // 2. Fetch all 2DAMEMORY tokens that have had a value assigned to them thus far... + + // Clear ze box of any existing values, but keep anything the user has written + // or selected previously.... + sVal := cbRowValue.text; + cbRowValue.clear(); + cbRowValue.text := sVal; + + oOldCursor := Screen.Cursor; + Screen.Cursor := crHourglass; + Application.ProcessMessages(); + try + oList := TStringList.Create(); + box_ini.readsection('TLKList', oList); + + // Load the StrRef tokens... + for i := 0 to (oList.Count - 1) do begin + sKey := oList.Strings[i]; + sVal := box_ini.readstring('TLKList', sKey, ''); + + if (sKey <> '') and (sVal <> '') then begin + if (copy(sKey, 1, 6) = 'StrRef') then begin + cbRowValue.Items.Add(sKey); + end; + end; + end; + + oList.free(); + oList := TStringList.Create(); + box_ini.readsection('2DAList', oList); + bStop := False; + + // Check through all 2DA file modifiers for tokens being set... + for i := 0 to (oList.Count - 1) do begin + sFile := box_ini.readstring('2DAList', oList.Strings[i], ''); + if (sFile <> '') then begin + oMods := TStringList.Create(); + try + // Check all modifiers for this particular file... + box_ini.readsection(sFile, oMods); + for n := 0 to (oMods.count - 1) do begin + sMod := oMods.Strings[n]; + sLabel := box_ini.readstring(sFile, sMod, ''); + + if (sLabel <> '') then begin + oCols := TStringList.Create(); + try + // Check each column label for this Modifier... + box_ini.ReadSection(sLabel, oCols); + for j := 0 to (oCols.count-1) do begin + sKey := oCols.strings[j]; + sVal := box_ini.readstring(sLabel, sKey, ''); + + // Check if the KEY is a unique 2DAMEMORY token + // instead of a column label. If so add to list. + if (sKey <> '') + and (copy(sKey, 1, 9) = '2DAMEMORY') + and (cbRowValue.Items.IndexOf(sKey) = -1) + then begin + cbRowValue.Items.Add(sKey); + end; + end; + finally + oCols.free(); + end; + end; + + // Don't parse tokens that are set after this modifier has been executed. + // Modifier sections coming after the current one should be skipped. + if (sLabel = box_section) then begin + bStop := True; + break; + end; + end; + finally + oMods.free(); + end; + end; + + // Don't parse tokens that are set after this modifier has been executed. + // Files coming after the current one should be skipped. + if (bStop = True) or (sFile = box_2daname) then begin + break; + end; + end; + finally + Screen.Cursor := oOldCursor; + end; +end; + +procedure TAdd2daColForm.grid2daModDblClick(Sender: TObject); +begin + btnEditColValueClick(Sender); +end; + +end. diff --git a/UChange2daRowForm.ddp b/UChange2daRowForm.ddp new file mode 100644 index 0000000..4370276 Binary files /dev/null and b/UChange2daRowForm.ddp differ diff --git a/UChange2daRowForm.dfm b/UChange2daRowForm.dfm new file mode 100644 index 0000000..ba60056 Binary files /dev/null and b/UChange2daRowForm.dfm differ diff --git a/UChange2daRowForm.pas b/UChange2daRowForm.pas new file mode 100644 index 0000000..4dbc376 --- /dev/null +++ b/UChange2daRowForm.pas @@ -0,0 +1,708 @@ +unit UChange2daRowForm; + +interface + +uses + Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, + StdCtrls, Grids, Buttons, ExtCtrls, UST_IniFile, U2DAEdit; + +type + TChange2daForm = class(TForm) + Panel1: TPanel; + lblHeadline: TLabel; + boxChangeValue: TGroupBox; + btnClose: TButton; + Label1: TLabel; + Label2: TLabel; + cbColLabel: TComboBox; + cbColValue: TComboBox; + btnLoadCols: TSpeedButton; + btnAddToList: TSpeedButton; + btnEditColValue: TSpeedButton; + grid2daMod: TStringGrid; + lblGridHeading: TLabel; + btnDeleteKey: TSpeedButton; + Label4: TLabel; + Label5: TLabel; + Label6: TLabel; + Label7: TLabel; + Label8: TLabel; + Label9: TLabel; + Label10: TLabel; + Label11: TLabel; + boxIdentifier: TGroupBox; + cbIdxType: TComboBox; + edIdx: TEdit; + OpenDlg: TOpenDialog; + btnSetIdx: TButton; + btnCopyInfo: TSpeedButton; + procedure btnLoadTokensClick(Sender: TObject); + procedure btnLoadColsClick(Sender: TObject); + procedure btnDeleteKeyClick(Sender: TObject); + procedure btnAddToListClick(Sender: TObject); + procedure btnEditColValueClick(Sender: TObject); + procedure btnSetIdxClick(Sender: TObject); + procedure btnCloseClick(Sender: TObject); + procedure edIdxKeyPress(Sender: TObject; var Key: Char); + procedure FormClose(Sender: TObject; var Action: TCloseAction); + procedure btnCopyInfoClick(Sender: TObject); + procedure cbColValueKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure cbColLabelKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure grid2daModKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure cbColValueDropDown(Sender: TObject); + procedure grid2daModDblClick(Sender: TObject); + private + { Private declarations } + public + { Public declarations } + box_2daname : string; + box_labels : string; + box_section : string; + box_type : string; + box_ini : TST_IniFile; + + procedure LoadIniData(); + procedure UpdateRowIdentifier(sKey : string; sVal : string); + function ShowInfoBox(sMessage : string) : word; + function ShowAlertBox(sMessage : string) : word; + + end; + +var + Change2daForm: TChange2daForm; + +implementation + +uses UInfoCopyForm; + +{$R *.DFM} + +function TChange2daForm.ShowInfoBox(sMessage : string) : word; +begin + result := MessageDlgPos(sMessage, mtInformation, [mbOK], 0, Left + 64, Top + 128); +end; + +function TChange2daForm.ShowAlertBox(sMessage : string) : word; +begin + result := MessageDlg(sMessage, mtWarning, [mbOK], 0); +end; + +procedure TChange2daForm.UpdateRowIdentifier(sKey : string; sVal : string); +var + oList : TStringList; + sLabel : string; + sValue : string; + i : integer; +begin + oList := TStringList.Create(); + try + box_ini.readsection(box_section, oList); + sLabel := oList.strings[0]; + sValue := box_ini.readstring(box_section, sLabel, ''); + + // The specified identifier is already at the top of the list, + // or the list is empty, just assign the new value. + if (oList.count = 0) or (sLabel = sKey) then begin + box_ini.writestring(box_section, sKey, sVal); + end + // The specified identifier is not at the top of the list, remove + // all other keys so it can be added at the top + else begin + // If the other identifier key exists, delete it... + // FIX(2005-06-13) Added "label" as possible identifier. + if ((sLabel = 'RowIndex') or (sLabel = 'LabelIndex')) and (sKey = 'RowLabel') then + box_ini.deletekey(box_section, sLabel) + else if ((sLabel = 'RowLabel') or (sLabel = 'LabelIndex')) and (sKey = 'RowIndex') then + box_ini.deletekey(box_section, sLabel) + else if ((sLabel = 'RowIndex') or (sLabel = 'RowLabel')) and (sKey = 'LabelIndex') then + box_ini.deletekey(box_section, sLabel); + + // Copy all the other keys to a temporary section briefly.... + for i := 0 to (oList.count - 1) do begin + sLabel := oList.strings[i]; + sValue := box_ini.readstring(box_section, sLabel, ''); + + // FUgly and inefficient solution, fix some time when I feel like it. + // FIX(2005-06-13) Added check to skip a label identifier + // on the first position. + if (sLabel <> 'RowIndex') + and (sLabel <> 'RowLabel') + and (sLabel <> 'LabelIndex') + then + box_ini.writestring('aaTempWorkSectionaa', sLabel, sValue); + + box_ini.deletekey(box_section, sLabel); + end; + + // Clear the current section and then write the identifier to it. + oList.clear(); + // box_ini.EraseSection(box_section); + box_ini.writestring(box_section, sKey, sVal); + + // Write the values back to the section from the temporary section... + box_ini.readsection('aaTempWorkSectionaa', oList); + for i := 0 to (oList.count - 1) do begin + sLabel := oList.strings[i]; + sValue := box_ini.readstring('aaTempWorkSectionaa', sLabel, ''); + box_ini.writestring(box_section, sLabel, sValue); + end; + + // Delete the temporary section... + box_ini.EraseSection('aaTempWorkSectionaa'); + end; + + if (sKey = 'RowIndex') then begin + cbIdxType.ItemIndex := 0; + edIdx.text := sVal; + end + else if (sKey = 'RowLabel') then begin + cbIdxType.ItemIndex := 1; + edIdx.text := sVal; + end + // FIX(2005-06-13) Added support for Label Index.... + else if (sKey = 'LabelIndex') then begin + cbIdxType.ItemIndex := 2; + edIdx.text := sVal; + end; + finally + oList.free(); + end; + // ------------------------------------------------------------------------- +end; + +procedure TChange2daForm.LoadIniData(); +var + oList : TStringList; + i : integer; + sKey : string; + sVal : string; +begin + grid2daMod.cols[0].clear(); + grid2daMod.cols[1].clear(); + grid2daMod.rowcount := 1; + grid2daMod.colwidths[0] := 82; + grid2daMod.colwidths[1] := grid2daMod.width - (grid2daMod.colwidths[0] + 6); + + if (box_type = 'CopyRow') then + btnCopyInfo.Visible := True + else + btnCopyInfo.Visible := False; + + edIdx.clear(); + cbColValue.clear(); + + if (box_2daname <> box_labels) then begin + cbColLabel.Color := clWindow; + cbColLabel.clear(); + end; + + // FIX(2005-06-13) LabelIndex is only valid for CHANGE modifiers. + cbIdxType.items.clear(); + cbIdxType.items.add('RowIndex'); + cbIdxType.items.add('RowLabel'); + if (box_type <> 'CopyRow') then + cbIdxType.items.add('LabelIndex'); + + cbIdxType.itemindex := 0; + + + oList := TStringList.Create(); + try + box_ini.readsection(box_section, oList); + + if (oList.Count > 0) then begin + // If a Row identifier doesn't exist in the first position one + // must be added. So do it. :) + sKey := oList.strings[0]; + sVal := box_ini.readstring(box_section, sKey, ''); + if (sKey <> 'RowIndex') and (sKey <> 'RowLabel') and (sKey <> 'LabelIndex') then begin + UpdateRowIdentifier('RowIndex', '0'); + sKey := 'RowIndex'; + sVal := ''; + end; + + if (sKey = 'RowIndex') then begin + cbIdxType.ItemIndex := 0; + edIdx.text := sVal; + end + else if (sKey = 'RowLabel') then begin + cbIdxType.ItemIndex := 1; + edIdx.text := sVal; + end + else if (sKey = 'LabelIndex') then begin + cbIdxType.ItemIndex := 2; + edIdx.text := sVal; + end; + + // Populate the column modifier grid with existing keys.... + for i := 1 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sVal := box_ini.readstring(box_section, sKey, ''); + + if (sVal <> '') then begin + grid2daMod.rowcount := grid2daMod.rowcount + 1; + grid2daMod.cells[0, i-1] := sKey; + grid2daMod.cells[1, i-1] := sVal; + end; + end; + grid2daMod.rowcount := grid2daMod.rowcount - 1; + end + else begin + // Make sure a row identifier exists at the top... + box_ini.writestring(box_section, 'RowIndex', '0'); + end; + finally + oList.free(); + end; +end; + +procedure TChange2daForm.btnLoadTokensClick(Sender: TObject); +var + oList : TStringList; + oMods : TStringList; + oCols : TStringList; + i : integer; + n : integer; + j : integer; + sKey : string; + sVal : string; + sFile : string; + sMod : string; + sLabel : string; + bStop : boolean; +begin + // 1. Fetch all StringReference Tokens. + // 2. Fetch all 2DAMEMORY tokens that have had a value assigned to them thus far... + + // Clear ze box of any existing values, but keep anything the user has written + // or selected previously.... + sVal := cbColValue.text; + cbColValue.clear(); + cbColValue.text := sVal; + + oList := TStringList.Create(); + box_ini.readsection('TLKList', oList); + + if (box_type = 'CopyRow') then begin + cbColValue.Items.Add('high()'); + cbColValue.Items.Add('inc(#)'); + end; + + // Load the StrRef tokens... + for i := 0 to (oList.Count - 1) do begin + sKey := oList.Strings[i]; + sVal := box_ini.readstring('TLKList', sKey, ''); + + if (sKey <> '') and (sVal <> '') then begin + if (copy(sKey, 1, 6) = 'StrRef') then begin + cbColValue.Items.Add(sKey); + end; + end; + end; + + oList.free(); + oList := TStringList.Create(); + box_ini.readsection('2DAList', oList); + bStop := False; + + // Check through all 2DA file modifiers for tokens being set... + for i := 0 to (oList.Count - 1) do begin + sFile := box_ini.readstring('2DAList', oList.Strings[i], ''); + if (sFile <> '') then begin + oMods := TStringList.Create(); + try + // Check all modifiers for this particular file... + box_ini.readsection(sFile, oMods); + for n := 0 to (oMods.count - 1) do begin + sMod := oMods.Strings[n]; + sLabel := box_ini.readstring(sFile, sMod, ''); + + if (sLabel <> '') then begin + oCols := TStringList.Create(); + try + // Check each column label for this Modifier... + box_ini.ReadSection(sLabel, oCols); + for j := 0 to (oCols.count-1) do begin + sKey := oCols.strings[j]; + sVal := box_ini.readstring(sLabel, sKey, ''); + + // Check if the KEY is a unique 2DAMEMORY token + // instead of a column label. If so add to list. + if (sKey <> '') + and (copy(sKey, 1, 9) = '2DAMEMORY') + and (cbColValue.Items.IndexOf(sKey) = -1) + then begin + cbColValue.Items.Add(sKey); + end; + end; + finally + oCols.free(); + end; + end; + + // Don't parse tokens that are set after this modifier has been executed. + // Modifier sections coming after the current one should be skipped. + if (sLabel = box_section) then begin + bStop := True; + break; + end; + end; + finally + oMods.free(); + end; + end; + + // Don't parse tokens that are set after this modifier has been executed. + // Files coming after the current one should be skipped. + if (bStop = True) or (sFile = box_2daname) then begin + break; + end; + end; + + ShowInfoBox('Relevant tokens have been loaded into the dropdown list of the Value box.'); + cbColValue.setfocus; +end; + +procedure TChange2daForm.btnLoadColsClick(Sender: TObject); +var + o2da : T2DAHandler; + iCnt : integer; + i : integer; + sFile : string; + oOldCursor : TCursor; +begin + sFile := ''; + + // If a file with this name already exists in the same folder as the changes.ini, + // use that file instead of asking for one. + if (box_ini.FileName <> '') + and SysUtils.FileExists(box_ini.FileName) + and SysUtils.FileExists(ExtractFilePath(box_ini.FileName) + box_2daname) + then begin + sFile := ExtractFilePath(box_ini.FileName) + box_2daname; + end + else begin + OpenDlg.Filter := '2DA file (*.2da)|*.2da'; + OpenDlg.DefaultExt := '2da'; + OpenDlg.FileName := box_2daname; + OpenDlg.Title := 'Open ' + box_2daname + ' to load column labels from.'; + + if OpenDlg.Execute then begin + sFile := OpenDlg.FileName; + end; + end; + + if (sFile <> '') then begin + oOldCursor := Screen.Cursor; + Screen.Cursor := crHourglass; + Application.ProcessMessages(); + o2da := T2DAHandler.Create(); + try + o2da.Load2daFile(sFile); + box_labels := box_2daname; + iCnt := 0; + cbColLabel.items.clear(); + + if (box_type = 'CopyRow') then begin + cbColLabel.Items.Add('ExclusiveColumn'); + cbColLabel.Items.Add('NewRowLabel'); + end; + + for i := 0 to (o2da.colcount-1) do begin + cbColLabel.Items.Add(o2da.clabels[i]); + inc(iCnt); + end; + + if (iCnt > 0) then + cbColLabel.itemindex := 0; + + cbColLabel.Color := clMoneyGreen; + cbColLabel.setfocus; + + finally + o2da.free(); + Screen.Cursor := oOldCursor; + end; + end; +end; + +procedure TChange2daForm.btnDeleteKeyClick(Sender: TObject); +var + i : integer; +begin + // FIX(2005-05-24) Ta inte bort om raden är tom... + if (grid2daMod.cells[0, grid2daMod.Selection.top] = '') then + exit; + + if (grid2daMod.rowcount > 0) then begin + box_ini.deletekey(box_section, grid2daMod.cells[0, grid2daMod.Selection.top]); + + for i := grid2daMod.Selection.top to (grid2daMod.rowcount-1) do begin + grid2daMod.Rows[i] := grid2daMod.Rows[i+1]; + end; + grid2daMod.RowCount := grid2daMod.rowcount - 1; + end; +end; + +procedure TChange2daForm.btnAddToListClick(Sender: TObject); +var + i : integer; + bFound : boolean; +begin + // FIX(2005-05-24) Lägg inte till om nåt av fälten är tomma... + if (cbColLabel.text = '') or (cbColValue.text = '') then + exit; + + bFound := False; + for i := 0 to (grid2daMod.rowcount-1) do begin + if (cbColLabel.text = grid2daMod.cells[0, i]) then begin + grid2daMod.cells[1, i] := cbColValue.text; + grid2daMod.row := i; + box_ini.WriteString(box_section, cbColLabel.text, cbColValue.text); + cbColLabel.text := ''; + cbColValue.text := ''; + cbColLabel.setfocus(); + bFound := True; + break; + end; + end; + + if not bFound then begin + i := grid2daMod.rowcount; + grid2daMod.rowcount := i + 1; + + // Fult hack, eftersom man inte kan få grid-eländet att börja på rad 0... :/ + if (grid2daMod.cells[0, i-1] = '') and (grid2daMod.cells[0, i-1] = '') then begin + i := i - 1; + grid2daMod.rowcount := grid2daMod.rowcount - 1; + end; + + grid2daMod.cells[0, i] := cbColLabel.text; + grid2daMod.cells[1, i] := cbColValue.text; + grid2daMod.row := i; + + box_ini.WriteString(box_section, cbColLabel.text, cbColValue.text); + + cbColLabel.text := ''; + cbColValue.text := ''; + cbColLabel.setfocus(); + + end; +end; + +procedure TChange2daForm.btnEditColValueClick(Sender: TObject); +var + sText : string; + i : integer; +begin + sText := grid2daMod.cells[0, grid2daMod.Selection.Top]; + cbColLabel.text := sText; + cbColValue.text := grid2daMod.cells[1, grid2daMod.Selection.Top]; + + for i := 0 to (cbColLabel.items.count-1) do begin + if (cbColLabel.Items[i] = sText) then begin + cbColLabel.itemindex := i; + break; + end; + end; + cbColValue.setfocus; +end; + +procedure TChange2daForm.btnSetIdxClick(Sender: TObject); +var + sKey : string; + sVal : string; +begin + sKey := cbIdxType.text; + sVal := edIdx.text; + + if ((sKey = 'RowIndex') or (sKey = 'RowLabel') or (sKey = 'LabelIndex')) and (sVal <> '') then begin + UpdateRowIdentifier(sKey, sVal); + end; +end; + + +procedure TChange2daForm.btnCloseClick(Sender: TObject); +var + sKey : string; + sVal : string; +begin + sKey := cbIdxType.text; + sVal := edIdx.text; + + // make sure changes to the row identifier are saved... + if ((sKey = 'RowIndex') or (sKey = 'RowLabel') or (sKey = 'LabelIndex')) and (sVal <> '') then begin + UpdateRowIdentifier(sKey, sVal); + + end; +end; + +procedure TChange2daForm.edIdxKeyPress(Sender: TObject; var Key: Char); +begin + if(cbIdxType.itemindex = 0) then begin + if not (Key in [#8, '0'..'9']) then begin + Key := #0; + beep(); + end; + end; +end; + +procedure TChange2daForm.FormClose(Sender: TObject; + var Action: TCloseAction); +begin + InfoCopyForm.Hide(); +end; + +procedure TChange2daForm.btnCopyInfoClick(Sender: TObject); +begin + if not InfoCopyForm.Visible then + InfoCopyForm.Show() + else if InfoCopyForm.Visible then + InfoCopyForm.Hide(); +end; + +procedure TChange2daForm.cbColValueKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (Shift = [ssCtrl, ssShift]) and (key = VK_RIGHT) then begin + btnAddToListClick(btnAddToList); + end; +end; + +procedure TChange2daForm.cbColLabelKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (Shift = [ssCtrl, ssShift]) and (key = VK_RIGHT) then begin + btnAddToListClick(btnAddToList); + end; +end; + +procedure TChange2daForm.grid2daModKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (Shift = [ssCtrl, ssShift]) and (key = VK_LEFT) then begin + btnEditColValueClick(btnEditColValue); + end; +end; + +procedure TChange2daForm.cbColValueDropDown(Sender: TObject); +var + oList : TStringList; + oMods : TStringList; + oCols : TStringList; + i : integer; + n : integer; + j : integer; + sKey : string; + sVal : string; + sFile : string; + sMod : string; + sLabel : string; + bStop : boolean; + oOldCursor : TCursor; +begin + // 1. Fetch all StringReference Tokens. + // 2. Fetch all 2DAMEMORY tokens that have had a value assigned to them thus far... + + // Clear ze box of any existing values, but keep anything the user has written + // or selected previously.... + sVal := cbColValue.text; + cbColValue.clear(); + cbColValue.text := sVal; + + oOldCursor := Screen.Cursor; + Screen.Cursor := crHourglass; + Application.ProcessMessages(); + try + oList := TStringList.Create(); + box_ini.readsection('TLKList', oList); + + if (box_type = 'CopyRow') then begin + cbColValue.Items.Add('high()'); + cbColValue.Items.Add('inc(#)'); + end; + + // Load the StrRef tokens... + for i := 0 to (oList.Count - 1) do begin + sKey := oList.Strings[i]; + sVal := box_ini.readstring('TLKList', sKey, ''); + + if (sKey <> '') and (sVal <> '') then begin + if (copy(sKey, 1, 6) = 'StrRef') then begin + cbColValue.Items.Add(sKey); + end; + end; + end; + + oList.free(); + oList := TStringList.Create(); + box_ini.readsection('2DAList', oList); + bStop := False; + + // Check through all 2DA file modifiers for tokens being set... + for i := 0 to (oList.Count - 1) do begin + sFile := box_ini.readstring('2DAList', oList.Strings[i], ''); + if (sFile <> '') then begin + oMods := TStringList.Create(); + try + // Check all modifiers for this particular file... + box_ini.readsection(sFile, oMods); + for n := 0 to (oMods.count - 1) do begin + sMod := oMods.Strings[n]; + sLabel := box_ini.readstring(sFile, sMod, ''); + + if (sLabel <> '') then begin + oCols := TStringList.Create(); + try + // Check each column label for this Modifier... + box_ini.ReadSection(sLabel, oCols); + for j := 0 to (oCols.count-1) do begin + sKey := oCols.strings[j]; + sVal := box_ini.readstring(sLabel, sKey, ''); + + // Check if the KEY is a unique 2DAMEMORY token + // instead of a column label. If so add to list. + if (sKey <> '') + and (copy(sKey, 1, 9) = '2DAMEMORY') + and (cbColValue.Items.IndexOf(sKey) = -1) + then begin + cbColValue.Items.Add(sKey); + end; + end; + finally + oCols.free(); + end; + end; + + // Don't parse tokens that are set after this modifier has been executed. + // Modifier sections coming after the current one should be skipped. + if (sLabel = box_section) then begin + bStop := True; + break; + end; + end; + finally + oMods.free(); + end; + end; + + // Don't parse tokens that are set after this modifier has been executed. + // Files coming after the current one should be skipped. + if (bStop = True) or (sFile = box_2daname) then begin + break; + end; + end; + finally + Screen.Cursor := oOldCursor; + end; +end; + +procedure TChange2daForm.grid2daModDblClick(Sender: TObject); +begin + btnEditColValueClick(Sender); +end; + +end. diff --git a/UCopy2daRowForm.ddp b/UCopy2daRowForm.ddp new file mode 100644 index 0000000..4370276 Binary files /dev/null and b/UCopy2daRowForm.ddp differ diff --git a/UCopy2daRowForm.dfm b/UCopy2daRowForm.dfm new file mode 100644 index 0000000..2435f8d Binary files /dev/null and b/UCopy2daRowForm.dfm differ diff --git a/UCopy2daRowForm.pas b/UCopy2daRowForm.pas new file mode 100644 index 0000000..96dfaad --- /dev/null +++ b/UCopy2daRowForm.pas @@ -0,0 +1,783 @@ +unit UCopy2daRowForm; + +interface + +uses + Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, + StdCtrls, Grids, Buttons, ExtCtrls, UST_IniFile, U2DAEdit; + +type + TCopy2daForm = class(TForm) + Panel1: TPanel; + lblHeadline: TLabel; + boxChangeValue: TGroupBox; + btnClose: TButton; + Label1: TLabel; + Label2: TLabel; + cbColLabel: TComboBox; + cbColValue: TComboBox; + btnLoadCols: TSpeedButton; + btnAddToList: TSpeedButton; + btnEditColValue: TSpeedButton; + grid2daMod: TStringGrid; + lblGridHeading: TLabel; + btnDeleteKey: TSpeedButton; + Label4: TLabel; + Label5: TLabel; + Label6: TLabel; + Label7: TLabel; + Label8: TLabel; + Label9: TLabel; + Label10: TLabel; + Label11: TLabel; + boxIdentifier: TGroupBox; + cbIdxType: TComboBox; + edIdx: TEdit; + OpenDlg: TOpenDialog; + btnSetIdx: TButton; + btnCopyInfo: TSpeedButton; + GroupBox2: TGroupBox; + btnLoad2da: TSpeedButton; + Label12: TLabel; + cbExclusiveLabel: TComboBox; + procedure btnLoadTokensClick(Sender: TObject); + procedure btnLoadColsClick(Sender: TObject); + procedure btnDeleteKeyClick(Sender: TObject); + procedure btnAddToListClick(Sender: TObject); + procedure btnEditColValueClick(Sender: TObject); + procedure btnSetIdxClick(Sender: TObject); + procedure btnCloseClick(Sender: TObject); + procedure edIdxKeyPress(Sender: TObject; var Key: Char); + procedure FormClose(Sender: TObject; var Action: TCloseAction); + procedure btnCopyInfoClick(Sender: TObject); + procedure cbColValueKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure cbColLabelKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure grid2daModKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure cbColValueDropDown(Sender: TObject); + procedure grid2daModDblClick(Sender: TObject); + procedure cbExclusiveLabelChange(Sender: TObject); + private + { Private declarations } + public + { Public declarations } + box_2daname : string; + box_labels : string; + box_section : string; + box_type : string; + box_ini : TST_IniFile; + + procedure LoadIniData(); + procedure UpdateRowIdentifier(sKey : string; sVal : string); + procedure LoadColumnLabels(bPrompt : boolean; Sender : TObject); + function ShowInfoBox(sMessage : string) : word; + function ShowAlertBox(sMessage : string) : word; + + end; + +var + Copy2daForm: TCopy2daForm; + +implementation + +uses UInfoCopyForm; + +{$R *.DFM} + +function TCopy2daForm.ShowInfoBox(sMessage : string) : word; +begin + result := MessageDlgPos(sMessage, mtInformation, [mbOK], 0, Left + 64, Top + 128); +end; + +function TCopy2daForm.ShowAlertBox(sMessage : string) : word; +begin + result := MessageDlg(sMessage, mtWarning, [mbOK], 0); +end; + +procedure TCopy2daForm.UpdateRowIdentifier(sKey : string; sVal : string); +var + oList : TStringList; + sLabel : string; + sValue : string; + i : integer; +begin + oList := TStringList.Create(); + try + box_ini.readsection(box_section, oList); + sLabel := oList.strings[0]; + sValue := box_ini.readstring(box_section, sLabel, ''); + + // The specified identifier is already at the top of the list, + // or the list is empty, just assign the new value. + if (oList.count = 0) or (sLabel = sKey) then begin + box_ini.writestring(box_section, sKey, sVal); + end + // The specified identifier is not at the top of the list, remove + // all other keys so it can be added at the top + else begin + // If the other identifier key exists, delete it... + // FIX(2005-06-13) Added "label" as possible identifier. + if ((sLabel = 'RowIndex') or (sLabel = 'LabelIndex')) and (sKey = 'RowLabel') then + box_ini.deletekey(box_section, sLabel) + else if ((sLabel = 'RowLabel') or (sLabel = 'LabelIndex')) and (sKey = 'RowIndex') then + box_ini.deletekey(box_section, sLabel) + else if ((sLabel = 'RowIndex') or (sLabel = 'RowLabel')) and (sKey = 'LabelIndex') then + box_ini.deletekey(box_section, sLabel); + + // Copy all the other keys to a temporary section briefly.... + for i := 0 to (oList.count - 1) do begin + sLabel := oList.strings[i]; + sValue := box_ini.readstring(box_section, sLabel, ''); + + // FUgly and inefficient solution, fix some time when I feel like it. (Like the rest of this app) + // FIX(2005-06-13) Added check to skip a label identifier + // on the first position. + if (sLabel <> 'RowIndex') + and (sLabel <> 'RowLabel') + and (sLabel <> 'LabelIndex') + then + box_ini.writestring('aaTempWorkSectionaa', sLabel, sValue); + + box_ini.deletekey(box_section, sLabel); + end; + + // Clear the current section and then write the identifier to it. + oList.clear(); + // box_ini.EraseSection(box_section); + box_ini.writestring(box_section, sKey, sVal); + + // Write the values back to the section from the temporary section... + box_ini.readsection('aaTempWorkSectionaa', oList); + for i := 0 to (oList.count - 1) do begin + sLabel := oList.strings[i]; + sValue := box_ini.readstring('aaTempWorkSectionaa', sLabel, ''); + box_ini.writestring(box_section, sLabel, sValue); + end; + + // Delete the temporary section... + box_ini.EraseSection('aaTempWorkSectionaa'); + end; + + if (sKey = 'RowIndex') then begin + cbIdxType.ItemIndex := 0; + edIdx.text := sVal; + end + else if (sKey = 'RowLabel') then begin + cbIdxType.ItemIndex := 1; + edIdx.text := sVal; + end + // FIX(2005-06-13) Added support for Label Index.... + else if (sKey = 'LabelIndex') then begin + cbIdxType.ItemIndex := 2; + edIdx.text := sVal; + end; + finally + oList.free(); + end; + // ------------------------------------------------------------------------- +end; + +procedure TCopy2daForm.LoadIniData(); +var + oList : TStringList; + i : integer; + iCnt : integer; + sKey : string; + sVal : string; +begin + grid2daMod.cols[0].clear(); + grid2daMod.cols[1].clear(); + grid2daMod.rowcount := 1; + grid2daMod.colwidths[0] := 82; + grid2daMod.colwidths[1] := grid2daMod.width - (grid2daMod.colwidths[0] + 6); + + if (box_type = 'CopyRow') then + btnCopyInfo.Visible := True + else + btnCopyInfo.Visible := False; + + edIdx.clear(); + cbColValue.clear(); + + if (box_2daname <> box_labels) then begin + cbColLabel.Color := clWindow; + cbExclusiveLabel.Color := clWindow; + cbColLabel.clear(); + cbExclusiveLabel.clear(); + + // ADDED(2006-07-08) Load column labels if possible when window opens + LoadColumnLabels(false, nil); + end; + + // FIX(2005-06-13) LabelIndex is only valid for CHANGE modifiers. + cbIdxType.items.clear(); + cbIdxType.items.add('RowIndex'); + cbIdxType.items.add('RowLabel'); + if (box_type <> 'CopyRow') then + cbIdxType.items.add('LabelIndex'); + + cbIdxType.itemindex := 0; + + // - - - - - - - - - - - - - + // ADDED(2006-07-08) Load "ExclusiveColumn" separately. + cbExclusiveLabel.Text := box_ini.ReadString(box_section, 'ExclusiveColumn', ''); + if (cbExclusiveLabel.Text <> '') then begin + for i := 0 to (cbExclusiveLabel.Items.Count - 1) do begin + if (cbExclusiveLabel.Items[i] = cbExclusiveLabel.Text) then begin + cbExclusiveLabel.ItemIndex := i; + break; + end; + end; + end; + // - - - - - - - - - - - - - + + + oList := TStringList.Create(); + try + box_ini.readsection(box_section, oList); + + if (oList.Count > 0) then begin + // If a Row identifier doesn't exist in the first position one + // must be added. So do it. :) + sKey := oList.strings[0]; + sVal := box_ini.readstring(box_section, sKey, ''); + if (sKey <> 'RowIndex') + and (sKey <> 'RowLabel') + and (sKey <> 'LabelIndex') + then begin + UpdateRowIdentifier('RowIndex', '0'); + sKey := 'RowIndex'; + sVal := ''; + end; + + if (sKey = 'RowIndex') then begin + cbIdxType.ItemIndex := 0; + edIdx.text := sVal; + end + else if (sKey = 'RowLabel') then begin + cbIdxType.ItemIndex := 1; + edIdx.text := sVal; + end + else if (sKey = 'LabelIndex') then begin + cbIdxType.ItemIndex := 2; + edIdx.text := sVal; + end; + + // Populate the column modifier grid with existing keys.... + iCnt := 0; + for i := 1 to (oList.count - 1) do begin + sKey := oList.strings[i]; + // CHANGED(2006-07-08) Added check to not list ExclusiveCol in the grid. + if (sKey <> 'ExclusiveColumn') then begin + sVal := box_ini.readstring(box_section, sKey, ''); + + if (sVal <> '') then begin + grid2daMod.rowcount := grid2daMod.rowcount + 1; + grid2daMod.cells[0, iCnt] := sKey; + grid2daMod.cells[1, iCnt] := sVal; + inc(iCnt); + end; + end; + end; + grid2daMod.rowcount := grid2daMod.rowcount - 1; + end + else begin + // Make sure a row identifier exists at the top... + box_ini.writestring(box_section, 'RowIndex', '0'); + end; + finally + oList.free(); + end; +end; + +procedure TCopy2daForm.btnLoadTokensClick(Sender: TObject); +var + oList : TStringList; + oMods : TStringList; + oCols : TStringList; + i : integer; + n : integer; + j : integer; + sKey : string; + sVal : string; + sFile : string; + sMod : string; + sLabel : string; + bStop : boolean; +begin + // 1. Fetch all StringReference Tokens. + // 2. Fetch all 2DAMEMORY tokens that have had a value assigned to them thus far... + + // Clear ze box of any existing values, but keep anything the user has written + // or selected previously.... + sVal := cbColValue.text; + cbColValue.clear(); + cbColValue.text := sVal; + + oList := TStringList.Create(); + box_ini.readsection('TLKList', oList); + + if (box_type = 'CopyRow') then begin + cbColValue.Items.Add('high()'); + cbColValue.Items.Add('inc(#)'); + end; + + // Load the StrRef tokens... + for i := 0 to (oList.Count - 1) do begin + sKey := oList.Strings[i]; + sVal := box_ini.readstring('TLKList', sKey, ''); + + if (sKey <> '') and (sVal <> '') then begin + if (copy(sKey, 1, 6) = 'StrRef') then begin + cbColValue.Items.Add(sKey); + end; + end; + end; + + oList.free(); + oList := TStringList.Create(); + box_ini.readsection('2DAList', oList); + bStop := False; + + // Check through all 2DA file modifiers for tokens being set... + for i := 0 to (oList.Count - 1) do begin + sFile := box_ini.readstring('2DAList', oList.Strings[i], ''); + if (sFile <> '') then begin + oMods := TStringList.Create(); + try + // Check all modifiers for this particular file... + box_ini.readsection(sFile, oMods); + for n := 0 to (oMods.count - 1) do begin + sMod := oMods.Strings[n]; + sLabel := box_ini.readstring(sFile, sMod, ''); + + if (sLabel <> '') then begin + oCols := TStringList.Create(); + try + // Check each column label for this Modifier... + box_ini.ReadSection(sLabel, oCols); + for j := 0 to (oCols.count-1) do begin + sKey := oCols.strings[j]; + sVal := box_ini.readstring(sLabel, sKey, ''); + + // Check if the KEY is a unique 2DAMEMORY token + // instead of a column label. If so add to list. + if (sKey <> '') + and (copy(sKey, 1, 9) = '2DAMEMORY') + and (cbColValue.Items.IndexOf(sKey) = -1) + then begin + cbColValue.Items.Add(sKey); + end; + end; + finally + oCols.free(); + end; + end; + + // Don't parse tokens that are set after this modifier has been executed. + // Modifier sections coming after the current one should be skipped. + if (sLabel = box_section) then begin + bStop := True; + break; + end; + end; + finally + oMods.free(); + end; + end; + + // Don't parse tokens that are set after this modifier has been executed. + // Files coming after the current one should be skipped. + if (bStop = True) or (sFile = box_2daname) then begin + break; + end; + end; + + ShowInfoBox('Relevant tokens have been loaded into the dropdown list of the Value box.'); + cbColValue.setfocus; +end; + +procedure TCopy2daForm.btnLoadColsClick(Sender: TObject); +begin + LoadColumnLabels(true, Sender); +end; + +procedure TCopy2daForm.LoadColumnLabels(bPrompt : boolean; Sender : TObject); +var + o2da : T2DAHandler; + iCnt : integer; + i : integer; + sFile : string; + sTmp : string; +begin + sFile := ''; + sTmp := cbExclusiveLabel.Text; + + // If a file with this name already exists in the same folder as the changes.ini, + // use that file instead of asking for one. + if (box_ini.FileName <> '') + and SysUtils.FileExists(box_ini.FileName) + and SysUtils.FileExists(ExtractFilePath(box_ini.FileName) + box_2daname) + then begin + sFile := ExtractFilePath(box_ini.FileName) + box_2daname; + end + else if bPrompt then begin + OpenDlg.Filter := '2DA file (*.2da)|*.2da'; + OpenDlg.DefaultExt := '2da'; + OpenDlg.FileName := box_2daname; + OpenDlg.Title := 'Open ' + box_2daname + ' to load column labels from.'; + + if OpenDlg.Execute then begin + sFile := OpenDlg.FileName; + end; + end + else if not bPrompt then begin + exit; + end; + + if (sFile <> '') then begin + Screen.Cursor := crHourglass; + if (Sender <> nil) and (Sender is TControl) then + TControl(Sender).Enabled := false; + Application.ProcessMessages(); + + o2da := T2DAHandler.Create(); + try + cbColLabel.Enabled := false; + cbExclusiveLabel.Enabled := false; + o2da.Load2daFile(sFile); + box_labels := box_2daname; + iCnt := 0; + cbColLabel.items.clear(); + + cbExclusiveLabel.Items.clear(); + cbExclusiveLabel.Text := sTmp; + + if (box_type = 'CopyRow') then begin + // REMOVED(2006-07-08) This uses a separate textbox now... + // cbColLabel.Items.Add('ExclusiveColumn'); + cbColLabel.Items.Add('NewRowLabel'); + cbExclusiveLabel.Items.Add(''); + end; + + for i := 0 to (o2da.colcount-1) do begin + cbColLabel.Items.Add(o2da.clabels[i]); + + // CHANGED(2006-07-08) Fill the ExclusiveCol list here as well. + cbExclusiveLabel.Items.Add(o2da.clabels[i]); + if (o2da.clabels[i] = sTmp) then + cbExclusiveLabel.ItemIndex := iCnt; + inc(iCnt); + end; + + if (iCnt > 0) then begin + cbColLabel.itemindex := 0; + cbExclusiveLabel.itemindex := 0; + end; + + cbExclusiveLabel.Color := clMoneyGreen; + cbColLabel.Color := clMoneyGreen; +// cbColLabel.setfocus; + + finally + o2da.free(); + Screen.Cursor := crDefault; + cbColLabel.Enabled := true; + cbExclusiveLabel.Enabled := true; + cbExclusiveLabel.Text := sTmp; + + if (Sender <> nil) and (Sender is TControl) then + TControl(Sender).Enabled := true; + end; + end; +end; + +procedure TCopy2daForm.btnDeleteKeyClick(Sender: TObject); +var + i : integer; +begin + // FIX(2005-05-24) Ta inte bort om raden är tom... + if (grid2daMod.cells[0, grid2daMod.row] = '') then + exit; + + if (grid2daMod.rowcount > 0) then begin + box_ini.deletekey(box_section, grid2daMod.cells[0, grid2daMod.row]); + + for i := grid2daMod.Selection.top to (grid2daMod.rowcount-1) do begin + grid2daMod.Rows[i] := grid2daMod.Rows[i+1]; + end; + grid2daMod.RowCount := grid2daMod.rowcount - 1; + end; +end; + +procedure TCopy2daForm.btnAddToListClick(Sender: TObject); +var + i : integer; + bFound : boolean; +begin + // FIX(2005-05-24) Lägg inte till om nåt av fälten är tomma... + if (cbColLabel.text = '') or (cbColValue.text = '') then + exit; + + bFound := False; + for i := 0 to (grid2daMod.rowcount-1) do begin + if (cbColLabel.text = grid2daMod.cells[0, i]) then begin + grid2daMod.cells[1, i] := cbColValue.text; + grid2daMod.row := i; + box_ini.WriteString(box_section, cbColLabel.text, cbColValue.text); + cbColLabel.text := ''; + cbColValue.text := ''; + cbColLabel.setfocus(); + bFound := True; + break; + end; + end; + + if not bFound then begin + i := grid2daMod.rowcount; + grid2daMod.rowcount := i + 1; + + // Fult hack, eftersom man inte kan få grid-eländet att börja på rad 0... :/ + if (grid2daMod.cells[0, i-1] = '') and (grid2daMod.cells[0, i-1] = '') then begin + i := i - 1; + grid2daMod.rowcount := grid2daMod.rowcount - 1; + end; + + grid2daMod.cells[0, i] := cbColLabel.text; + grid2daMod.cells[1, i] := cbColValue.text; + grid2daMod.row := i; + + box_ini.WriteString(box_section, cbColLabel.text, cbColValue.text); + + cbColLabel.text := ''; + cbColValue.text := ''; + cbColLabel.setfocus(); + + end; +end; + +procedure TCopy2daForm.btnEditColValueClick(Sender: TObject); +var + sText : string; + i : integer; +begin + sText := grid2daMod.cells[0, grid2daMod.Selection.Top]; + cbColLabel.text := sText; + cbColValue.text := grid2daMod.cells[1, grid2daMod.Selection.Top]; + + for i := 0 to (cbColLabel.items.count-1) do begin + if (cbColLabel.Items[i] = sText) then begin + cbColLabel.itemindex := i; + break; + end; + end; + cbColValue.setfocus; +end; + +procedure TCopy2daForm.btnSetIdxClick(Sender: TObject); +var + sKey : string; + sVal : string; +begin + sKey := cbIdxType.text; + sVal := edIdx.text; + + if ((sKey = 'RowIndex') or (sKey = 'RowLabel') or (sKey = 'LabelIndex')) and (sVal <> '') then begin + UpdateRowIdentifier(sKey, sVal); + end; +end; + + +procedure TCopy2daForm.btnCloseClick(Sender: TObject); +var + sKey : string; + sVal : string; +begin + sKey := cbIdxType.text; + sVal := edIdx.text; + + // make sure changes to the row identifier are saved... + if ((sKey = 'RowIndex') or (sKey = 'RowLabel') or (sKey = 'LabelIndex')) and (sVal <> '') then begin + UpdateRowIdentifier(sKey, sVal); + + end; +end; + +procedure TCopy2daForm.edIdxKeyPress(Sender: TObject; var Key: Char); +begin + if(cbIdxType.itemindex = 0) then begin + if not (Key in [#8, '0'..'9']) then begin + Key := #0; + beep(); + end; + end; +end; + +procedure TCopy2daForm.FormClose(Sender: TObject; + var Action: TCloseAction); +begin + InfoCopyForm.Hide(); +end; + +procedure TCopy2daForm.btnCopyInfoClick(Sender: TObject); +begin + if not InfoCopyForm.Visible then + InfoCopyForm.Show() + else if InfoCopyForm.Visible then + InfoCopyForm.Hide(); +end; + +procedure TCopy2daForm.cbColValueKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (Shift = [ssCtrl, ssShift]) and (key = VK_RIGHT) then begin + btnAddToListClick(btnAddToList); + end; +end; + +procedure TCopy2daForm.cbColLabelKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (Shift = [ssCtrl, ssShift]) and (key = VK_RIGHT) then begin + btnAddToListClick(btnAddToList); + end; +end; + +procedure TCopy2daForm.grid2daModKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (Shift = [ssCtrl, ssShift]) and (key = VK_LEFT) then begin + btnEditColValueClick(btnEditColValue); + end; +end; + +procedure TCopy2daForm.cbColValueDropDown(Sender: TObject); +var + oList : TStringList; + oMods : TStringList; + oCols : TStringList; + i : integer; + n : integer; + j : integer; + sKey : string; + sVal : string; + sFile : string; + sMod : string; + sLabel : string; + bStop : boolean; + oOldCursor : TCursor; +begin + // 1. Fetch all StringReference Tokens. + // 2. Fetch all 2DAMEMORY tokens that have had a value assigned to them thus far... + + // Clear ze box of any existing values, but keep anything the user has written + // or selected previously.... + sVal := cbColValue.text; + cbColValue.clear(); + cbColValue.text := sVal; + + oOldCursor := Screen.Cursor; + Screen.Cursor := crHourglass; + Application.ProcessMessages(); + try + oList := TStringList.Create(); + box_ini.readsection('TLKList', oList); + + if (box_type = 'CopyRow') then begin + cbColValue.Items.Add('high()'); + cbColValue.Items.Add('inc(#)'); + end; + + // Load the StrRef tokens... + for i := 0 to (oList.Count - 1) do begin + sKey := oList.Strings[i]; + sVal := box_ini.readstring('TLKList', sKey, ''); + + if (sKey <> '') and (sVal <> '') then begin + if (copy(sKey, 1, 6) = 'StrRef') then begin + cbColValue.Items.Add(sKey); + end; + end; + end; + + oList.free(); + oList := TStringList.Create(); + box_ini.readsection('2DAList', oList); + bStop := False; + + // Check through all 2DA file modifiers for tokens being set... + for i := 0 to (oList.Count - 1) do begin + sFile := box_ini.readstring('2DAList', oList.Strings[i], ''); + if (sFile <> '') then begin + oMods := TStringList.Create(); + try + // Check all modifiers for this particular file... + box_ini.readsection(sFile, oMods); + for n := 0 to (oMods.count - 1) do begin + sMod := oMods.Strings[n]; + sLabel := box_ini.readstring(sFile, sMod, ''); + + if (sLabel <> '') then begin + oCols := TStringList.Create(); + try + // Check each column label for this Modifier... + box_ini.ReadSection(sLabel, oCols); + for j := 0 to (oCols.count-1) do begin + sKey := oCols.strings[j]; + sVal := box_ini.readstring(sLabel, sKey, ''); + + // Check if the KEY is a unique 2DAMEMORY token + // instead of a column label. If so add to list. + if (sKey <> '') + and (copy(sKey, 1, 9) = '2DAMEMORY') + and (cbColValue.Items.IndexOf(sKey) = -1) + then begin + cbColValue.Items.Add(sKey); + end; + end; + finally + oCols.free(); + end; + end; + + // Don't parse tokens that are set after this modifier has been executed. + // Modifier sections coming after the current one should be skipped. + if (sLabel = box_section) then begin + bStop := True; + break; + end; + end; + finally + oMods.free(); + end; + end; + + // Don't parse tokens that are set after this modifier has been executed. + // Files coming after the current one should be skipped. + if (bStop = True) or (sFile = box_2daname) then begin + break; + end; + end; + finally + Screen.Cursor := oOldCursor; + end; +end; + +procedure TCopy2daForm.grid2daModDblClick(Sender: TObject); +begin + btnEditColValueClick(Sender); +end; + +procedure TCopy2daForm.cbExclusiveLabelChange(Sender: TObject); +begin + if (cbExclusiveLabel.Text = '') then begin + box_ini.DeleteKey(box_section, 'ExclusiveColumn'); + end + else begin + box_ini.WriteString(box_section, 'ExclusiveColumn', cbExclusiveLabel.Text); + end; +end; + +end. diff --git a/UEditTokenForm.ddp b/UEditTokenForm.ddp new file mode 100644 index 0000000..4370276 Binary files /dev/null and b/UEditTokenForm.ddp differ diff --git a/UEditTokenForm.dfm b/UEditTokenForm.dfm new file mode 100644 index 0000000..4e6046d Binary files /dev/null and b/UEditTokenForm.dfm differ diff --git a/UEditTokenForm.pas b/UEditTokenForm.pas new file mode 100644 index 0000000..0b38f14 --- /dev/null +++ b/UEditTokenForm.pas @@ -0,0 +1,42 @@ +unit UEditTokenForm; + +interface + +uses + Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, + StdCtrls, ExtCtrls; + +type + TEditTokenForm = class(TForm) + Panel1: TPanel; + Label1: TLabel; + Label2: TLabel; + edToken: TEdit; + edStrRef: TEdit; + btnCancel: TButton; + btnSave: TButton; + Label3: TLabel; + Label4: TLabel; + procedure edStrRefKeyPress(Sender: TObject; var Key: Char); + private + { Private declarations } + public + { Public declarations } + end; + +var + EditTokenForm: TEditTokenForm; + +implementation + +{$R *.DFM} + +procedure TEditTokenForm.edStrRefKeyPress(Sender: TObject; var Key: Char); +begin + if not (Key in [#8, '0'..'9']) then begin + Key := #0; + beep(); + end; +end; + +end. diff --git a/UFormFilename.dfm b/UFormFilename.dfm new file mode 100644 index 0000000..0a04051 Binary files /dev/null and b/UFormFilename.dfm differ diff --git a/UFormFilename.pas b/UFormFilename.pas new file mode 100644 index 0000000..5cf1c64 --- /dev/null +++ b/UFormFilename.pas @@ -0,0 +1,67 @@ +unit UFormFilename; + +interface + +uses + Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, + Buttons, StdCtrls, ExtCtrls; + +type + TFormFilename = class(TForm) + Panel1: TPanel; + lblTitle: TLabel; + edFilename: TEdit; + btnOK: TButton; + btnCancel: TButton; + btnOpen: TSpeedButton; + Label2: TLabel; + lblDescription: TLabel; + dlgOpen: TOpenDialog; + procedure btnOpenClick(Sender: TObject); + procedure FormShow(Sender: TObject); + private + { Private declarations } + f_type : string; + + procedure SetFileType(sType : string); + function GetFilename() : string; + public + { Public declarations } + + property Filename : string read GetFilename; + property Filetype : string read f_type write SetFileType; + end; + +var + FormFilename: TFormFilename; + +implementation + +{$R *.DFM} + +function TFormFilename.GetFilename() : string; +begin + result := edFilename.text; +end; + +procedure TFormFilename.btnOpenClick(Sender: TObject); +begin + if dlgOpen.Execute() then begin + edFilename.text := ExtractFileName(dlgOpen.FileName); + end; +end; + +procedure TFormFilename.SetFileType(sType : string); +begin + f_type := sType; + + lblDescription.caption := 'Please specify the name, with file-extension, of a ' + sType + ' file to add modifiers for:'; + lblTitle.caption := 'Add ' + sType + ' file'; +end; + +procedure TFormFilename.FormShow(Sender: TObject); +begin + edFilename.clear(); +end; + +end. diff --git a/UFormNewGFF.ddp b/UFormNewGFF.ddp new file mode 100644 index 0000000..4370276 Binary files /dev/null and b/UFormNewGFF.ddp differ diff --git a/UFormNewGFF.dfm b/UFormNewGFF.dfm new file mode 100644 index 0000000..5cf95e0 Binary files /dev/null and b/UFormNewGFF.dfm differ diff --git a/UFormNewGFF.pas b/UFormNewGFF.pas new file mode 100644 index 0000000..2ffa21c --- /dev/null +++ b/UFormNewGFF.pas @@ -0,0 +1,1315 @@ +unit UFormNewGFF; + +interface + +uses + Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, + Grids, StdCtrls, ExtCtrls, Buttons, UST_inifile, UST_Common, UStrTok; + +type + TFormNewGFF = class(TForm) + Panel1: TPanel; + Label9: TLabel; + gridFields: TStringGrid; + btnDelete: TSpeedButton; + GroupBox1: TGroupBox; + cbFieldType: TComboBox; + lblType: TLabel; + lblLabel: TLabel; + edLabel: TEdit; + lblPath: TLabel; + edPath: TEdit; + edValue: TEdit; + lblValue: TLabel; + edStrRef: TEdit; + lblStrRef: TLabel; + edTypeId: TEdit; + lblTypeId: TLabel; + lblFields: TLabel; + gridLocStr: TStringGrid; + lblLocStr1: TLabel; + lblLocStr2: TLabel; + btnAddStr: TButton; + btnSave: TSpeedButton; + btnEdit: TSpeedButton; + btnOK: TButton; + btnDelStr: TSpeedButton; + lblSubFields: TLabel; + btnAddField: TButton; + btnNew: TSpeedButton; + edMemToken: TEdit; + lblMemToken: TLabel; + btnNewFieldInfo: TSpeedButton; + lblPathToken: TLabel; + edPathToken: TEdit; + procedure cbFieldTypeChange(Sender: TObject); + procedure btnAddFieldClick(Sender: TObject); + procedure FormShow(Sender: TObject); + procedure btnNewClick(Sender: TObject); + procedure btnEditClick(Sender: TObject); + procedure btnAddStrClick(Sender: TObject); + procedure btnDelStrClick(Sender: TObject); + procedure btnDeleteClick(Sender: TObject); + procedure btnSaveClick(Sender: TObject); + procedure gridFieldsDblClick(Sender: TObject); + procedure btnNewFieldInfoClick(Sender: TObject); + private + { Private declarations } + f_parentlist : boolean; + f_issubfield : boolean; // Set this to TRUE if it's a section called from within a STRUCT/LIST section. + f_parent : string; // Set this to the section of this window's field list. + f_section : string; // Set this to the section currently being edited. + f_ini : TST_IniFile; + + procedure SetupSimpleField(); + public + { Public declarations } + procedure LoadFieldGrid(iSelected : integer = -1); + procedure LoadFieldData(); + procedure ResetInputFields(); + procedure EnableFieldsByType(sType : string); + procedure RemoveSectionAndSubSections(sSection : string); + procedure SelectFieldType(sType : string); + function ShowAlertBox(sMessage : string) : word; + function ShowConfirmBox(sMessage : string) : word; + function ValidateType(sType, sValue : string) : string; + function ValidateFieldLabel(sLabel : string) : boolean; + function GetNextFieldKey(sSection : string) : string; + function SectionKeyExists(sSection, sParent : string) : boolean; + function SaveFieldChanges() : boolean; + + property IsParentList : boolean read f_parentlist write f_parentlist; + property IsSubField : boolean read f_issubfield write f_issubfield; + property ParentSection : string read f_parent write f_parent; + property IniFile : TST_IniFile read f_ini write f_ini; + end; + +var + FormNewGFF: TFormNewGFF; + +implementation + +uses + UModLabelForm, UInfoForm; + +{$R *.DFM} + + +function TFormNewGFF.ShowAlertBox(sMessage : string) : word; +begin + result := MessageDlg(sMessage, mtWarning, [mbOK], 0); +end; + +function TFormNewGFF.ShowConfirmBox(sMessage : string) : word; +begin + result := MessageDlg(sMessage, mtConfirmation, [mbYes, mbNo], 0); +end; + +procedure TFormNewGFF.LoadFieldGrid(iSelected : integer = -1); +var + oList : TStringList; + i : integer; + iCnt : integer; + sKey : string; + sVal : string; +begin + gridFields.cols[0].clear(); + gridFields.cols[1].clear(); + gridFields.cols[2].clear(); + gridFields.cols[3].clear(); + + gridFields.colwidths[0] := 70; + gridFields.colwidths[1] := 120; + gridFields.colwidths[2] := 80; + gridFields.colwidths[3] := 110; + gridFields.rowcount := 2; + + gridFields.cells[0, 0] := 'Modifier'; + gridFields.cells[1, 0] := 'Modifier Label'; + gridFields.cells[2, 0] := 'Field Type'; + gridFields.cells[3, 0] := 'Field Label'; + + oList := TStringList.Create(); + try + f_ini.ReadSection(f_parent, oList); + iCnt := 1; + for i := 0 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sVal := f_ini.ReadString(f_parent, sKey, ''); + + if (sVal <> '') and (lowercase(copy(sKey, 1, 8)) = 'addfield') then begin + gridFields.rowcount := gridFields.rowcount + 1; + gridFields.cells[0, iCnt] := sKey; + gridFields.cells[1, iCnt] := sVal; + gridFields.cells[2, iCnt] := f_ini.ReadString(sVal, 'FieldType', ''); + gridFields.cells[3, iCnt] := f_ini.ReadString(sVal, 'Label', ''); + inc(iCnt); + end; + end; + + if (iCnt > 0) then begin + if (gridFields.rowcount > 2) then + gridFields.rowcount := gridFields.rowcount - 1; + btnEdit.enabled := true; + end; + finally + oList.free(); + end; + + // FIX(2006-03-25) Keep track of selected row when just refreshing... + if (iSelected > -1) then + gridFields.Row := iSelected; +end; + +procedure TFormNewGFF.SelectFieldType(sType : string); +var + i : integer; +begin + for i := 0 to (cbFieldType.items.count - 1) do begin + if (cbFieldType.items[i] = sType) then begin + cbFieldType.itemindex := i; + break; + end; + end; +end; + + +procedure TFormNewGFF.LoadFieldData(); +var + oList : TStringList; + sType : string; + sKey : string; + sVal : string; + sId : string; + i : integer; + iCnt : integer; +begin + sType := f_ini.ReadString(f_section, 'FieldType', ''); + + if (sType = '') then begin + ShowAlertBox('Unable to load field type data! Something is probably wrong with the data format in changes.ini...'); + exit; + end; + + ResetInputFields(); + EnableFieldsByType(sType); + + // ADDED(2006-07-25) Look for !FieldPath assigned 2DAMEM tokens... + oList := TStringList.Create(); + try + f_ini.ReadSection(f_section, oList); + for i := 1 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sVal := f_ini.ReadString(f_section, sKey, ''); + + if (copy(sKey, 1, 9) = '2DAMEMORY') + and GetIsNumber(copy(sKey, 10, 1)) + and (lowercase(sVal) = '!fieldpath') + then begin + edPathToken.Text := sKey; + break; + end; + end; + finally + oList.free(); + end; + // - - - - - - - - - - - - - - - - - - + + if ((sType = 'Byte') + or (sType = 'Char') + or (sType = 'Word') + or (sType = 'Short') + or (sType = 'DWORD') + or (sType = 'Int') + or (sType = 'Int64') + or (sType = 'Float') + or (sType = 'Double') + or (sType = 'ExoString') + or (sType = 'ResRef') + or (sType = 'Orientation') + or (sType = 'Position')) + then begin + SelectFieldType(sType); + edLabel.text := f_ini.ReadString(f_section, 'Label', ''); + + if not f_issubfield then + edPath.text := f_ini.ReadString(f_section, 'Path', ''); + + edValue.text := f_ini.ReadString(f_section, 'Value', ''); + end + else if (sType = 'ExoLocString') then begin + SelectFieldType(sType); + edLabel.text := f_ini.ReadString(f_section, 'Label', ''); + + if not f_issubfield then + edPath.text := f_ini.ReadString(f_section, 'Path', ''); + + edStrRef.text := f_ini.ReadString(f_section, 'StrRef', '-1'); + + oList := TStringList.Create(); + iCnt := 1; + try + f_ini.ReadSection(f_section, oList); + for i := 1 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sVal := f_ini.ReadString(f_section, sKey, ''); + + if (lowercase(copy(sKey, 1, 4)) = 'lang') then begin + sId := copy(sKey, 5, Length(sKey) - 4); + if GetIsNumber(sId) then begin + gridLocStr.RowCount := gridLocStr.rowcount + 1; + gridLocStr.cells[0, iCnt] := sId; + gridLocStr.cells[1, iCnt] := sVal; + inc(iCnt); + end; + end; + end; + if (iCnt > 1) then + gridLocStr.RowCount := gridLocStr.rowcount - 1; + finally + oList.free(); + end; + + end + else if (sType = 'Struct') then begin + SelectFieldType(sType); + edLabel.text := f_ini.ReadString(f_section, 'Label', ''); + + if not f_issubfield then + edPath.text := f_ini.ReadString(f_section, 'Path', ''); + + edTypeId.text := f_ini.ReadString(f_section, 'TypeId', ''); + + // ADDED(2006-01-16) Added 2DAMEMORY <--> ListIndex field. + // Look for 2DAMEMORY token with ListIndex assigned, and fetch the Token. + if (edMemToken.enabled) then begin + oList := TStringList.Create(); + try + f_ini.ReadSection(f_section, oList); + for i := 1 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sVal := f_ini.ReadString(f_section, sKey, ''); + + if (copy(sKey, 1, 9) = '2DAMEMORY') + and GetIsNumber(copy(sKey, 10, 1)) + and (lowercase(sVal) = 'listindex') + then begin + edMemToken.text := sKey; + break; + end; + end; + finally + oList.free(); + end; + end; + end + else if (sType = 'List') then begin + SelectFieldType(sType); + edLabel.text := f_ini.ReadString(f_section, 'Label', ''); + + if not f_issubfield then + edPath.text := f_ini.ReadString(f_section, 'Path', ''); + end + else begin + ShowAlertBox('Invalid Field Type data "' + sType + '" encountered! Not a valid field type!'); + exit; + end; + +end; + + +procedure TFormNewGFF.ResetInputFields(); +begin + if f_parentlist then begin + cbFieldType.enabled := true; + cbFieldType.text := 'Struct'; + SelectFieldType('Struct'); + cbFieldType.enabled := false; + end + else begin + cbFieldType.text := ''; + cbFieldType.ItemIndex := -1; + end; + + edLabel.clear(); + edLabel.enabled := false; + lblLabel.enabled := false; + + edPath.clear(); + edPath.enabled := false; + lblPath.enabled := false; + + edValue.clear(); + edValue.enabled := true; + lblValue.enabled := true; + + edStrRef.clear(); + edStrRef.enabled := false; + lblStrRef.enabled := false; + + edTypeId.clear(); + edTypeId.enabled := false; + lblTypeId.enabled := false; + + gridLocStr.rowcount := 2; + gridLocStr.cols[0].clear(); + gridLocStr.cols[1].clear(); + gridLocStr.cells[0,0] := 'LanguageId'; + gridLocStr.cells[1,0] := 'Text'; + gridLocStr.enabled := false; + btnAddStr.enabled := false; + btnDelStr.enabled := false; + lblLocStr1.enabled := false; + lblLocStr2.enabled := false; + + btnAddField.enabled := false; + lblSubFields.enabled := false; + + // ADDED(2006-01-16) Added 2DAMEMORY <--> ListIndex field... + // ADDED(2006-07-25) Added 2DAMEMORY <--> !FieldPath field... + edMemToken.clear(); + edPathToken.clear(); + edMemToken.enabled := false; + lblMemToken.enabled := false; +end; + +procedure TFormNewGFF.SetupSimpleField(); +begin + edLabel.enabled := true; + lblLabel.enabled := true; + + edPath.enabled := not f_issubfield; + lblPath.enabled := not f_issubfield; + + edValue.enabled := true; + lblValue.enabled := true; + + edStrRef.enabled := false; + lblStrRef.enabled := false; + + edTypeId.enabled := false; + lblTypeId.enabled := false; + + gridLocStr.enabled := false; + btnAddStr.enabled := false; + btnDelStr.enabled := false; + lblLocStr1.enabled := false; + lblLocStr2.enabled := false; + + btnAddField.enabled := false; + lblSubFields.enabled := false; + + edMemToken.enabled := false; + lblMemToken.enabled := false; + + // ADDED(2006-07-25) Added new 2DAMEM <--> FieldPath box... + edPathToken.Enabled := true; + lblPathToken.Enabled := true; +end; + + +procedure TFormNewGFF.EnableFieldsByType(sType : string); +begin + if not f_parentlist then begin + cbFieldType.enabled := true; + lblType.enabled := true; + end + else begin + cbFieldType.enabled := false; + lblType.enabled := false; + end; + + if (sType = 'Byte') then begin + SetupSimpleField(); + end + else if (sType = 'Char') then begin + SetupSimpleField(); + end + else if (sType = 'Word') then begin + SetupSimpleField(); + end + else if (sType = 'Short') then begin + SetupSimpleField(); + end + else if (sType = 'DWORD') then begin + SetupSimpleField(); + end + else if (sType = 'Int') then begin + SetupSimpleField(); + end + else if (sType = 'Int64') then begin + SetupSimpleField(); + end + else if (sType = 'Float') then begin + SetupSimpleField(); + end + else if (sType = 'Double') then begin + SetupSimpleField(); + end + else if (sType = 'ExoString') then begin + SetupSimpleField(); + end + else if (sType = 'ResRef') then begin + SetupSimpleField(); + end + else if (sType = 'ExoLocString') then begin + edLabel.enabled := true; + lblLabel.enabled := true; + + edPath.enabled := not f_issubfield; + lblPath.enabled := not f_issubfield; + + edValue.enabled := false; + lblValue.enabled := false; + + edStrRef.enabled := true; + lblStrRef.enabled := true; + + edTypeId.enabled := false; + lblTypeId.enabled := false; + + gridLocStr.enabled := true; + btnAddStr.enabled := true; + btnDelStr.enabled := true; + lblLocStr1.enabled := true; + lblLocStr2.enabled := true; + + btnAddField.enabled := false; + lblSubFields.enabled := false; + end + else if (sType = 'Orientation') then begin + SetupSimpleField(); + end + else if (sType = 'Position') then begin + SetupSimpleField(); + end + else if (sType = 'Struct') then begin + edLabel.enabled := true; + lblLabel.enabled := true; + + edPath.enabled := not f_issubfield; + lblPath.enabled := not f_issubfield; + + edValue.enabled := false; + lblValue.enabled := false; + + edStrRef.enabled := false; + lblStrRef.enabled := false; + + edTypeId.enabled := true; + lblTypeId.enabled := true; + + gridLocStr.enabled := false; + btnAddStr.enabled := false; + btnDelStr.enabled := false; + lblLocStr1.enabled := false; + lblLocStr2.enabled := false; + + btnAddField.enabled := true; + lblSubFields.enabled := true; + + // ADDED(2006-01-16) Added 2DAMEMORY <--> ListIndex field. + // Only enable field if parent is list. Can only do this check for + // sub-fields though since the App can't know the parent type for + // non-subfields. + if f_issubfield and not f_parentlist then begin + edMemToken.enabled := false; + lblMemToken.enabled := false; + end + else begin + edMemToken.enabled := true; + lblMemToken.enabled := true; + end; + end + else if (sType = 'List') then begin + edLabel.enabled := true; + lblLabel.enabled := true; + + edPath.enabled := not f_issubfield; + lblPath.enabled := not f_issubfield; + + edValue.enabled := false; + lblValue.enabled := false; + + edStrRef.enabled := false; + lblStrRef.enabled := false; + + edTypeId.enabled := false; + lblTypeId.enabled := false; + + gridLocStr.enabled := false; + btnAddStr.enabled := false; + btnDelStr.enabled := false; + lblLocStr1.enabled := false; + lblLocStr2.enabled := false; + + btnAddField.enabled := true; + lblSubFields.enabled := true; + + edMemToken.enabled := false; + lblMemToken.enabled := false; + end; +end; + +procedure TFormNewGFF.cbFieldTypeChange(Sender: TObject); +var + sType : string; +begin + sType := cbFieldType.Text; + + // Nothing selected, stop here... + if (sType = '') then + exit; + + // Clear any old values from the fields when switching type... + edLabel.clear(); + edPath.clear(); + edValue.clear(); + edStrRef.clear(); + edTypeId.clear(); + + gridLocStr.cols[0].clear(); + gridLocStr.cols[1].clear(); + gridLocStr.rowcount := 2; + gridLocStr.cells[0,0] := 'LanguageId'; + gridLocStr.cells[1,0] := 'Text'; + + EnableFieldsByType(sType); +end; + +procedure TFormNewGFF.btnAddFieldClick(Sender: TObject); +var + oForm : TFormNewGFF; +begin + if not SaveFieldChanges() then + exit; + + oForm := TFormNewGFF.Create(self); + try + oForm.ParentSection := f_section; + oForm.IsSubField := true; + oForm.IniFile := f_ini; + oForm.Top := self.Top + 10; + oForm.Left := self.Left + 10; + oForm.IsParentList := (cbFieldType.text = 'List'); + + oForm.ShowModal(); + finally + if (oForm <> nil) then + oForm.free(); + end; +end; + +procedure TFormNewGFF.FormShow(Sender: TObject); +begin + LoadFieldGrid(); + self.Caption := 'Add GFF Field [' + f_parent + ' / ' + f_section + ']'; +end; + +procedure TFormNewGFF.btnNewClick(Sender: TObject); +var + sLabel : string; +begin + NewLabelForm.edModLabel.text := ''; + NewLabelForm.lblTitle.caption := 'New Modifier Label'; + NewLabelForm.Caption := 'Add GFF Field - Specify Modifier Label'; + + if (NewLabelForm.ShowModal = mrOk) then + sLabel := NewLabelForm.edModLabel.text + else + exit; + + if (sLabel <> '') then begin + if (f_ini.SectionExists(sLabel)) then begin + ShowAlertBox('The entered modifier label is not unique! Please try again with another label that is not already in use.'); + exit; + end; + + f_section := sLabel; + + ResetInputFields(); + + lblType.enabled := true; + + if not f_parentlist then begin + cbFieldType.enabled := true; + cbFieldType.setfocus(); + end + else begin + cbFieldType.enabled := true; + SelectFieldType('Struct'); + cbFieldType.enabled := false; + EnableFieldsByType('Struct'); + end; + + btnSave.enabled := true; + self.Caption := 'Add GFF Field [' + f_parent + ' / ' + f_section + ']'; + end; +end; + +procedure TFormNewGFF.btnEditClick(Sender: TObject); +begin + btnSave.enabled := true; + f_section := gridFields.cells[1, gridFields.Row]; + self.Caption := 'Add GFF Field [' + f_parent + ' / ' + f_section + ']'; + LoadFieldData(); +end; + +procedure TFormNewGFF.btnAddStrClick(Sender: TObject); +begin + gridLocStr.RowCount := gridLocStr.Rowcount + 1; + gridLocStr.Row := (gridLocStr.rowcount - 1); +end; + +procedure TFormNewGFF.btnDelStrClick(Sender: TObject); +var + i : integer; + n : integer; +begin + for i := gridLocStr.row to (gridLocStr.rowcount - 1) do begin + n := i + 1; + if (n < gridLocStr.rowcount) then begin + gridLocStr.cells[0, i] := gridLocStr.cells[0, n]; + gridLocStr.cells[1, i] := gridLocStr.cells[1, n]; + end; + end; + gridLocStr.cells[0, (gridLocStr.rowcount - 1)] := ''; + gridLocStr.cells[1, (gridLocStr.rowcount - 1)] := ''; + gridLocStr.rowcount := gridLocStr.rowcount - 1; + gridLocStr.row := 1; +end; + +procedure TFormNewGFF.RemoveSectionAndSubSections(sSection : string); +var + oList : TStringList; + i : integer; + sKey : string; + sVal : string; +begin + // If the section doesn't exist for some reason, no point in continuing... + if not f_ini.SectionExists(sSection) then + exit; + + // Find any references to other sections and remove those too... + oList := TStringList.Create(); + try + f_ini.ReadSection(sSection, oList); + for i := 0 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sVal := f_ini.ReadString(sSection, sKey, ''); + + if (copy(sKey, 1, 8) = 'AddField') then begin + RemoveSectionAndSubSections(sVal); + f_ini.deletekey(sSection, sKey); + end; + end; + + // Remove this section... + f_ini.EraseSection(sSection); + finally + oList.free(); + end; +end; + +procedure TFormNewGFF.btnDeleteClick(Sender: TObject); +var + sKey : string; + sVal : string; +begin + // Look for any AddField-keys and delete the sub-sections they point to + // recursively, dealing with any AddField-keys they in turn may hold. + + sKey := gridFields.cells[0, gridFields.row]; + sVal := gridFields.cells[1, gridFields.row]; + + if (sKey = '') then + exit; + + if (ShowConfirmBox('Are you sure you wish to remove the selected Field modifier "' + sVal + '"?') = mrYes) then begin + RemoveSectionAndSubSections(sVal); + + // Remove the reference to this section... + f_ini.deletekey(f_parent, sKey); + + LoadFieldGrid(); + end; +end; + +function TFormNewGFF.ValidateType(sType, sValue : string) : string; +var + oTokens : TStringTokenizer; + iIntVal : longint; + iBigVal : Int64; + sResult : string; + i : integer; +begin + sResult := ''; + + // FIX(2006-01-18) if it's one of the memory token types, then + // don't validate it... Duh... + if (lowercase(copy(sValue, 1, 9)) = '2damemory') + or (lowercase(copy(sValue, 1, 6)) = 'strref') + then begin + result := sResult; + exit; + end; + + if (sType = 'Byte') then begin + if GetIsNumber(sValue) then begin + iIntVal := StrToInt(sValue); + if (iIntVal < 0) or (iIntVal > 255) then + sResult := 'It must be a number between 0 and 255!'; + end + else begin + sResult := 'It must be a number!'; + end; + end + else if (sType = 'Char') then begin + if (Length(sValue) > 1) then + sResult := 'It must be a single character!'; + end + else if (sType = 'Word') then begin + if GetIsNumber(sValue) then begin + iIntVal := StrToInt(sValue); + if (iIntVal < 0) or (iIntVal > 65535) then + sResult := 'It must be a number between 0 and 65535!'; + end + else begin + sResult := 'It must be a number!'; + end; + end + else if (sType = 'Short') then begin + if GetIsNumberSigned(sValue) then begin + iIntVal := StrToInt(sValue); + if (iIntVal < -32768) or (iIntVal > 32767) then + sResult := 'It must be a number between -32768 and 32767!'; + end + else begin + sResult := 'It must be a number!'; + end; + end + else if (sType = 'DWORD') then begin + if GetIsNumber(sValue) then begin + iBigVal := StrToInt(sValue); + if (iBigVal < 0) or (iBigVal > 4294967295) then + sResult := 'It must be a number between 0 and 4294967296!'; + end + else begin + sResult := 'It must be a number!'; + end; + end + else if (sType = 'Int') then begin + if GetIsNumberSigned(sValue) then begin + iIntVal := StrToInt(sValue); + + if (iIntVal < -2147483647) or (iIntVal > 2147483646) then + sResult := 'It must be a number between -2147483647 and 2147483647!'; + end + else begin + sResult := 'It must be a number!'; + end; + end + else if (sType = 'Int64') then begin + if not GetIsNumberSigned(sValue) then + sResult := 'It must be a number!'; + end + else if (sType = 'Float') then begin + if not GetIsFloat(sValue) then + sResult := 'It must be a decimal number ("." is decimal separator)!'; + end + else if (sType = 'Double') then begin + if not GetIsFloat(sValue) then + sResult := 'It must be a decimal number ("." is decimal separator)!'; + end + else if (sType = 'ResRef') then begin + if (Length(sValue) > 16) then + sResult := 'A ResRef can be at most 16 characters long!'; + end + else if (sType = 'ExoLocString') then begin + if (GetIsNumberSigned(sValue)) then begin + if (sValue <> '-1') then begin + iBigVal := StrToInt(sValue); + if (iBigVal < 0) or (iBigVal > 4294967295) then + sResult := 'A StrRef must be a number between -1 and 4294967295!'; + end; + end + else begin + sResult := 'A StrRef must be a number!'; + end; + end + else if (sType = 'Orientation') then begin + oTokens := TStringTokenizer.Create(sValue, '|'); + try + if (oTokens.count < 4) then begin + sResult := 'An Orientation field must contain four decimal values separated by a "|" character!'; + end + else begin + for i := 0 to (oTokens.count - 1) do begin + if not GetIsFloat(oTokens[i]) then begin + sResult := 'An Orientation field must contain four decimal values separated by a "|" character!'; + break; + end; + end; + end; + finally + oTokens.free(); + end; + end + else if (sType = 'Position') then begin + oTokens := TStringTokenizer.Create(sValue, '|'); + try + if (oTokens.count < 3) then begin + sResult := 'A Position field must contain three decimal values separated by a "|" character!'; + end + else begin + for i := 0 to (oTokens.count - 1) do begin + if not GetIsFloat(oTokens[i]) then begin + sResult := 'A Position field must contain three decimal values separated by a "|" character!'; + break; + end; + end; + end; + finally + oTokens.free(); + end; + end + else if (sType = 'Struct') then begin + if GetIsNumber(sValue) then begin + iBigVal := StrToInt(sValue); + if (iBigVal < 0) or (iBigVal > 4294967295) then + sResult := 'The Type ID must be a number between 0 and 4294967296!'; + end + else begin + sResult := 'The Type ID must be a number!'; + end; + end; + + if (Length(sResult) > 0) then + result := ' ' + sResult + else + result := sResult; +end; + +function TFormNewGFF.GetNextFieldKey(sSection : string) : string; +var + oList : TStringList; + sKey : string; + bFound : boolean; + i, n : integer; +begin + // Find the lowest free Key of the specific type. + n := 0; + sKey := ''; + bFound := True; + oList := TStringList.Create(); + f_ini.ReadSection(sSection, oList); + try + while bFound do begin + bFound := False; + + sKey := 'AddField' + IntToStr(n); + inc(n); + + for i := 0 to (oList.count - 1) do begin + if (lowercase(oList.strings[i]) = lowercase(sKey)) then begin + bFound := True; + break; + end; + end; + end; + finally + oList.free(); + end; + + result := sKey; +end; + +function TFormNewGFF.ValidateFieldLabel(sLabel : string) : boolean; +var + oList : TStringList; + i : integer; + sKey : string; + sVal : string; + sLbl : string; + sPath : string; +begin + result := true; + + if (Length(sLabel) > 16) then begin + ShowAlertBox('The field label can be at most 16 characters long!'); + result := false; + exit; + end + else if (Length(sLabel) < 1) then begin + ShowAlertBox('You must specify a field label!'); + result := false; + exit; + end; + + oList := TStringList.Create(); + try + f_ini.ReadSection(f_parent, oList); + for i := 0 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sVal := f_ini.ReadString(f_parent, sKey, ''); + + if (lowercase(copy(sKey, 1, 8)) = 'addfield') + and f_ini.SectionExists(sVal) + and (sVal <> f_section) + then begin + sLbl := f_ini.ReadString(sVal, 'Label', ''); + if f_issubfield and (sLbl = sLabel) then begin + ShowAlertBox('Another field at the same tree-level has already been added with this label!'); + result := false; + exit; + end + else if not f_issubfield then begin + sPath := f_ini.ReadString(sVal, 'Label', ''); + if ((sPath = edPath.text) and (sLbl = sLabel)) then begin + ShowAlertBox('Another field at the same tree-level has already been added with this label!'); + result := false; + exit; + end; + end; + end; + end; + finally + oList.free(); + end; +end; + +function TFormNewGFF.SectionKeyExists(sSection, sParent : string) : boolean; +var + oList : TStringList; + i : integer; + sKey : string; + sVal : string; +begin + result := false; + oList := TStringList.Create(); + try + f_ini.ReadSection(sParent, oList); + for i := 0 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sVal := f_ini.ReadString(sParent, sKey, ''); + if (sVal = sSection) then begin + result := true; + exit; + end; + end; + finally + oList.free(); + end; +end; + +procedure TFormNewGFF.btnSaveClick(Sender: TObject); +begin + SaveFieldChanges(); +end; + +function TFormNewGFF.SaveFieldChanges() : boolean; +var + oList : TStringList; + sType : string; + sError : string; + sKey : string; + sVal : string; + i : integer; +begin + result := false; + if (f_section = '') then begin + ShowAlertBox('Error! I seem to have lost track of which section is being edited! Oh oh..'); + exit; + end; + + sType := cbFieldType.text; + + // - - - - STRAIGHT DATA TYPES - - - - - - - - - - - - - - - - - - - - - - + if ((sType = 'Byte') + or (sType = 'Char') + or (sType = 'Word') + or (sType = 'Short') + or (sType = 'DWORD') + or (sType = 'Int') + or (sType = 'Int64') + or (sType = 'Float') + or (sType = 'Double') + or (sType = 'ExoString') + or (sType = 'ResRef') + or (sType = 'Orientation') + or (sType = 'Position')) + then begin + // Make sure the Value is in the proper format... + sError := ValidateType(sType, edValue.text); + if (sError <> '') then begin + ShowAlertBox('Invalid value specified!' + sError); + if edValue.enabled then + edValue.setfocus(); + exit; + end; + + // Validate that the Label isn't already in use! + if not ValidateFieldLabel(edLabel.text) then begin + if edLabel.enabled then + edLabel.setfocus(); + exit; + end; + + // If reference to new section hasn't been added yet to parent, do it. + if not SectionKeyExists(f_section, f_parent) then begin + sKey := GetNextFieldKey(f_parent); + f_ini.WriteString(f_parent, sKey, f_section); + end; + + // Save values of the input fields to the INI file... + f_ini.WriteString(f_section, 'FieldType', cbFieldType.text); + + if not f_issubfield then + f_ini.WriteString(f_section, 'Path', edPath.text); + + f_ini.WriteString(f_section, 'Label', edLabel.text); + f_ini.WriteString(f_section, 'Value', edValue.text); + end + // - - - - EXOLOCSTRING TYPE - - - - - - - - - - - - - - - - - - - - - - - + else if (sType = 'ExoLocString') then begin + // Make sure the Value is in the proper format... + sError := ValidateType(sType, edStrRef.text); + if (sError <> '') then begin + ShowAlertBox('Invalid StrRef specified!' + sError); + if edStrRef.enabled then + edStrRef.setfocus(); + exit; + end; + + // Validate that the Label isn't already in use! + if not ValidateFieldLabel(edLabel.text) then begin + if edLabel.enabled then + edLabel.setfocus(); + exit; + end; + + // If reference to new section hasn't been added yet to parent, do it. + if not SectionKeyExists(f_section, f_parent) then begin + sKey := GetNextFieldKey(f_parent); + f_ini.WriteString(f_parent, sKey, f_section); + end; + + // Save values of the input fields to the INI file... + f_ini.WriteString(f_section, 'FieldType', cbFieldType.text); + + if not f_issubfield then + f_ini.WriteString(f_section, 'Path', edPath.text); + + f_ini.WriteString(f_section, 'Label', edLabel.text); + f_ini.WriteString(f_section, 'StrRef', edStrRef.text); + + // Remove any old localized string entries first... + // (Otherwise we won't get rid of deleted entries in the list) + oList := TStringList.create(); + try + f_ini.ReadSection(f_section, oList); + for i := 0 to (oList.count - 1) do begin + sKey := oList.strings[i]; + if (lowercase(copy(sKey, 1, 4)) = 'lang') and GetIsNumber(copy(sKey, 5, 1)) then begin + f_ini.DeleteKey(f_section, sKey); + end; + end; + finally + oList.free(); + end; + + // Write the Localized substrings to the INI file... + for i := 1 to (gridLocStr.rowcount - 1) do begin + sKey := gridLocStr.cells[0, i]; + sVal := gridLocStr.cells[1, i]; + + if (GetIsNumber(sKey)) then + f_ini.WriteString(f_section, 'lang' + sKey, sVal); + end; + + end + // - - - - STRUCT CONTAINER TYPE - - - - - - - - - - - - - - - - - - - - - + else if (sType = 'Struct') then begin + // Make sure the Value is in the proper format... + // FIX(2006-01-18) Added check to skip validation if edTypeId is set to "ListIndex". + if (lowercase(edTypeId.text) <> 'listindex') then begin + sError := ValidateType(sType, edTypeId.text); + if (sError <> '') then begin + ShowAlertBox('Invalid Type ID specified!' + sError); + if edTypeId.enabled then + edTypeId.setfocus(); + exit; + end; + end; + + // ADDED(2006-01-18) Add extra check for new ListIndex token to verify + // that it is used properly. + if ((lowercase(edTypeId.text) = 'listindex') and f_issubfield and not f_parentlist) then begin + ShowAlertBox('Invalid Type ID specified! Only STRUCTs added to a LIST have a List Index.'); + if edTypeId.enabled then + edTypeId.setfocus(); + exit; + end; + + // Can't validate field label here any more than this since the field + // label will not be present if the parent field is a LIST. + // And we don't know the parent type if it isn't a sub-field. + if (Length(edLabel.text) > 16) then begin + ShowAlertBox('The Field Label can be at most 16 characters long!'); + if edLabel.enabled then + edLabel.setfocus(); + exit; + end; + + // If reference to new section hasn't been added yet to parent, do it. + if not SectionKeyExists(f_section, f_parent) then begin + sKey := GetNextFieldKey(f_parent); + f_ini.WriteString(f_parent, sKey, f_section); + end; + + // Save values of the input fields to the INI file... + f_ini.WriteString(f_section, 'FieldType', cbFieldType.text); + + if not f_issubfield then + f_ini.WriteString(f_section, 'Path', edPath.text); + + f_ini.WriteString(f_section, 'Label', edLabel.text); + f_ini.WriteString(f_section, 'TypeId', edTypeId.text); + + // ADDED(2006-01-16) Added 2DAMEMORY <--> ListIndex field. + if (Length(edMemToken.text) > 0) then begin + if (copy(edMemToken.text, 1, 9) = '2DAMEMORY') + and GetIsNumber(copy(edMemToken.text, 10, 1)) + then begin + // First remove existing ListIndex 2DAMEMORY keys, since the key needs + // to be recreated since the value is the KEY and not the VALUE part. :) + oList := TStringList.Create(); + try + f_ini.ReadSection(f_section, oList); + for i := 0 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sVal := f_ini.ReadString(f_section, sKey, ''); + + if (copy(sKey, 1, 9) = '2DAMEMORY') + and GetIsNumber(copy(sKey, 10, 1)) + and (lowercase(sVal) = 'listindex') + then begin + f_ini.DeleteKey(f_section, sKey); + end; + end; + finally + oList.free(); + end; + + // Write the new 2DAMEMORY key... + f_ini.WriteString(f_section, edMemToken.text, 'ListIndex'); + end + else begin + ShowAlertBox('Index Token field must either be blank or contain a 2DAMEMORY# token! ListIndex token not saved!'); + if (edMemToken.enabled) then + edMemToken.setfocus(); + end; + end; + end + // - - - - LIST CONTAINER TYPE - - - - - - - - - - - - - - - - - - - - - - + else if (sType = 'List') then begin + // Validate that the Label isn't already in use! + if not ValidateFieldLabel(edLabel.text) then begin + if edLabel.enabled then + edLabel.setfocus(); + exit; + end; + + // If reference to new section hasn't been added yet to parent, do it. + if not SectionKeyExists(f_section, f_parent) then begin + sKey := GetNextFieldKey(f_parent); + f_ini.WriteString(f_parent, sKey, f_section); + end; + + // Save values of the input fields to the INI file... + f_ini.WriteString(f_section, 'FieldType', cbFieldType.text); + + if not f_issubfield then + f_ini.WriteString(f_section, 'Path', edPath.text); + + f_ini.WriteString(f_section, 'Label', edLabel.text); + end + else begin + ShowAlertBox('No valid Field Type has been set!'); + if cbFieldType.enabled then + cbFieldType.setfocus(); + exit; + end; + + // ADDED(2006-07-25) Save value in new 2DAMEM <--> FieldPath box + if (Length(edPathToken.text) > 0) then begin + if (copy(edPathToken.text, 1, 9) = '2DAMEMORY') + and GetIsNumber(copy(edPathToken.text, 10, 1)) + then begin + // First remove existing ListIndex 2DAMEMORY keys, since the key needs + // to be recreated since the value is the KEY and not the VALUE part. :) + oList := TStringList.Create(); + try + f_ini.ReadSection(f_section, oList); + for i := 0 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sVal := f_ini.ReadString(f_section, sKey, ''); + + if (copy(sKey, 1, 9) = '2DAMEMORY') + and GetIsNumber(copy(sKey, 10, 1)) + and (lowercase(sVal) = '!fieldpath') + then begin + f_ini.DeleteKey(f_section, sKey); + end; + end; + finally + oList.free(); + end; + + // Write the new 2DAMEMORY key... + f_ini.WriteString(f_section, edPathToken.text, '!FieldPath'); + end + else begin + ShowAlertBox('Path Token field must either be blank or contain a 2DAMEMORY# token! FieldPath token not saved!'); + if (edPathToken.enabled) then begin + edPathToken.setfocus(); + edPathToken.SelectAll(); + end; + end; + end; + // - - - - - - - - - - - - - - - - - - - + + + // Refresh the listing, in case new data is added... + LoadFieldGrid(gridFields.row); + result := true; + +end; + +procedure TFormNewGFF.gridFieldsDblClick(Sender: TObject); +begin + btnEditClick(btnEdit); +end; + +procedure TFormNewGFF.btnNewFieldInfoClick(Sender: TObject); +begin + if not InfoForm.Visible then begin + InfoForm.txtInfo.visible := False; + InfoForm.txtInstallInfo.visible := False; + InfoForm.txtScriptInfo.visible := False; + InfoForm.txtGFFInfo.visible := True; + InfoForm.Show(); + end + else if not InfoForm.txtInstallInfo.visible then begin + InfoForm.txtInfo.visible := False; + InfoForm.txtInstallInfo.visible := False; + InfoForm.txtScriptInfo.visible := False; + InfoForm.txtGFFInfo.visible := True; + end + else if InfoForm.Visible and InfoForm.txtInstallInfo.Visible then begin + InfoForm.Hide(); + InfoForm.txtGFFInfo.visible := False; + end; +end; + +end. diff --git a/UInfoCopyForm.ddp b/UInfoCopyForm.ddp new file mode 100644 index 0000000..4370276 Binary files /dev/null and b/UInfoCopyForm.ddp differ diff --git a/UInfoCopyForm.dfm b/UInfoCopyForm.dfm new file mode 100644 index 0000000..7579382 Binary files /dev/null and b/UInfoCopyForm.dfm differ diff --git a/UInfoCopyForm.pas b/UInfoCopyForm.pas new file mode 100644 index 0000000..099fb54 --- /dev/null +++ b/UInfoCopyForm.pas @@ -0,0 +1,33 @@ +unit UInfoCopyForm; + +interface + +uses + Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, + StdCtrls, ExtCtrls; + +type + TInfoCopyForm = class(TForm) + Panel1: TPanel; + Memo1: TMemo; + btnClose: TButton; + procedure btnCloseClick(Sender: TObject); + private + { Private declarations } + public + { Public declarations } + end; + +var + InfoCopyForm: TInfoCopyForm; + +implementation + +{$R *.DFM} + +procedure TInfoCopyForm.btnCloseClick(Sender: TObject); +begin + InfoCopyFOrm.Hide(); +end; + +end. diff --git a/UInfoForm.ddp b/UInfoForm.ddp new file mode 100644 index 0000000..4370276 Binary files /dev/null and b/UInfoForm.ddp differ diff --git a/UInfoForm.dfm b/UInfoForm.dfm new file mode 100644 index 0000000..215bca5 Binary files /dev/null and b/UInfoForm.dfm differ diff --git a/UInfoForm.pas b/UInfoForm.pas new file mode 100644 index 0000000..e1f5994 --- /dev/null +++ b/UInfoForm.pas @@ -0,0 +1,36 @@ +unit UInfoForm; + +interface + +uses + Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, + StdCtrls, ExtCtrls; + +type + TInfoForm = class(TForm) + Panel1: TPanel; + txtInfo: TMemo; + Button1: TButton; + txtInstallInfo: TMemo; + txtScriptInfo: TMemo; + txtGFFInfo: TMemo; + procedure Button1Click(Sender: TObject); + private + { Private declarations } + public + { Public declarations } + end; + +var + InfoForm: TInfoForm; + +implementation + +{$R *.DFM} + +procedure TInfoForm.Button1Click(Sender: TObject); +begin + InfoForm.Hide(); +end; + +end. diff --git a/UMainForm.ddp b/UMainForm.ddp new file mode 100644 index 0000000..4370276 Binary files /dev/null and b/UMainForm.ddp differ diff --git a/UMainForm.dfm b/UMainForm.dfm new file mode 100644 index 0000000..10780db Binary files /dev/null and b/UMainForm.dfm differ diff --git a/UMainForm.pas b/UMainForm.pas new file mode 100644 index 0000000..376c515 --- /dev/null +++ b/UMainForm.pas @@ -0,0 +1,4992 @@ +unit UMainForm; + +// ============================================================================= +// ChangeEdit v1.0.3a - Main application window +// ----------------------------------------------------------------------------- +// Quick GUI CHANGES.INI file Editor for the TSLPatcher. +// +// Warning: This is a textbook example of an unplanned, unstructured hack of +// an application. At least it gets the job done for now... +// If you have any Computer Science or programming experience, reading +// this file will make you cringe uneasily. You have been warned. +// ----------------------------------------------------------------------------- +// Last Changed: 2006-07-21 +// ============================================================================= +// 2006-07-21 - Added Open Dialog buttons to the namespace INI/RTF fields. +// +interface + +uses + Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, + ImgList, ComCtrls, Menus, ExtCtrls, StdCtrls, Buttons, UST_IniFile, UTLKFile, + Grids, U2DAEdit, UST_Common, UGFFFile, XPMan; + +type + TMoveDir = (moveUp, moveDown); + TMainForm = class(TForm) + Panel1: TPanel; + bar: TStatusBar; + MainMenu: TMainMenu; + File1: TMenuItem; + mnuNew: TMenuItem; + mnuOpen: TMenuItem; + N2: TMenuItem; + mnuQuit: TMenuItem; + tree: TTreeView; + Icons: TImageList; + TreeMenu: TPopupMenu; + Add2dafile1: TMenuItem; + AddGFFFile1: TMenuItem; + OpenBox: TOpenDialog; + SaveBox: TSaveDialog; + paneSettings: TPanel; + Label1: TLabel; + Label2: TLabel; + edSetCaption: TEdit; + edSetConfirm: TEdit; + bxSetLog: TComboBox; + Label3: TLabel; + Label4: TLabel; + paneTLK: TPanel; + Label5: TLabel; + btnAddStrRef: TSpeedButton; + Label6: TLabel; + Label7: TLabel; + btnOpenTlk: TSpeedButton; + btnDelToken: TSpeedButton; + pane2da: TPanel; + paneGFF: TPanel; + Label8: TLabel; + lbl2daLabel1: TLabel; + btn2daAddRow: TButton; + btn2daModify: TButton; + btn2daDelete: TSpeedButton; + btn2daCopy: TButton; + btn2daNewCol: TButton; + grid2daMod: TStringGrid; + gridTlk: TStringGrid; + gridTokens: TStringGrid; + btnSetSave: TButton; + btnSetReset: TButton; + Label9: TLabel; + gridGffMod: TStringGrid; + btnGffDelete: TSpeedButton; + lblGffHeading: TLabel; + GroupBox1: TGroupBox; + Label11: TLabel; + Label12: TLabel; + cbGffValue: TComboBox; + btnEditField: TSpeedButton; + btnStoreField: TSpeedButton; + btnInfo: TSpeedButton; + N1: TMenuItem; + Delete2dafile1: TMenuItem; + DeleteGFFfile1: TMenuItem; + Actions1: TMenuItem; + mnuAdd2da: TMenuItem; + mnuAddGff: TMenuItem; + lblBackground: TLabel; + cbMode: TComboBox; + cbBackups: TComboBox; + Label13: TLabel; + Label14: TLabel; + paneInstall: TPanel; + gridInstall: TStringGrid; + Label15: TLabel; + GroupBox2: TGroupBox; + edFolderName: TEdit; + edFileName: TEdit; + Label16: TLabel; + Label17: TLabel; + btnAddInstall: TSpeedButton; + btnEditInstall: TSpeedButton; + btnInstallDelete: TSpeedButton; + BtnInstallInfo: TSpeedButton; + Label18: TLabel; + btnGetFileName: TSpeedButton; + cbFileReplace: TCheckBox; + txtEntry: TMemo; + btn2daCompare: TSpeedButton; + cbLogstyle: TComboBox; + Label10: TLabel; + btnMassAdd: TSpeedButton; + Label19: TLabel; + edSetReqFile: TEdit; + edSetReqMsg: TEdit; + Label20: TLabel; + btnNewField: TSpeedButton; + Label21: TLabel; + paneScript: TPanel; + Label22: TLabel; + Label23: TLabel; + gridScript: TStringGrid; + btnAddScript: TSpeedButton; + btnEditScript: TSpeedButton; + GroupBox3: TGroupBox; + Label25: TLabel; + btnGetScriptName: TSpeedButton; + edScriptName: TEdit; + cbScriptReplace: TCheckBox; + btnDelScript: TSpeedButton; + btnScriptInfo: TSpeedButton; + cbReplaceGff: TCheckBox; + edScriptDest: TEdit; + Label24: TLabel; + btnGetDestName: TSpeedButton; + edGffDest: TEdit; + Label26: TLabel; + btnGetGffDest: TSpeedButton; + btnGffDestSet: TButton; + AddSSFFile1: TMenuItem; + DeleteSSFFile1: TMenuItem; + AddSSFFile2: TMenuItem; + paneSSF: TPanel; + Label27: TLabel; + btnDelSSF: TSpeedButton; + lblSSFHeading: TLabel; + btnEditSSF: TSpeedButton; + btnAddSSF: TSpeedButton; + gridSSF: TStringGrid; + GroupBox4: TGroupBox; + Label30: TLabel; + Label31: TLabel; + cbSSFValue: TComboBox; + chSSFReplace: TCheckBox; + cbSSFEntry: TComboBox; + btnGffLoadFields: TSpeedButton; + edGffField: TComboBox; + btnCopy2daMod: TSpeedButton; + btnCompareGff: TSpeedButton; + Setups1: TMenuItem; + N3: TMenuItem; + SetupsNew: TMenuItem; + SetupsOpen: TMenuItem; + XPManifest1: TXPManifest; + cbLookup: TComboBox; + cbGameVer: TComboBox; + cbDebugScripts: TCheckBox; + btnMoveGffModUp: TSpeedButton; + btnMoveGffModDown: TSpeedButton; + procedure treeMouseDown(Sender: TObject; Button: TMouseButton; + Shift: TShiftState; X, Y: Integer); + procedure FormShow(Sender: TObject); + procedure mnuQuitClick(Sender: TObject); + procedure mnuOpenClick(Sender: TObject); + procedure btnOpenTlkClick(Sender: TObject); + procedure btnAddStrRefClick(Sender: TObject); + procedure btnDelTokenClick(Sender: TObject); + procedure treeChange(Sender: TObject; Node: TTreeNode); + procedure mnuNewClick(Sender: TObject); + procedure gridTokensDblClick(Sender: TObject); + procedure btnSetResetClick(Sender: TObject); + procedure btnSetSaveClick(Sender: TObject); + procedure btn2daAddRowClick(Sender: TObject); + procedure grid2daModDblClick(Sender: TObject); + procedure btn2daDeleteClick(Sender: TObject); + procedure btn2daModifyClick(Sender: TObject); + procedure btn2daCopyClick(Sender: TObject); + procedure btn2daNewColClick(Sender: TObject); + procedure btnLoadTokensClick(Sender: TObject); + procedure btnGffDeleteClick(Sender: TObject); + procedure btnEditFieldClick(Sender: TObject); + procedure btnStoreFieldClick(Sender: TObject); + procedure btnInfoClick(Sender: TObject); + procedure Add2dafile1Click(Sender: TObject); + procedure AddGFFFile1Click(Sender: TObject); + procedure Delete2dafile1Click(Sender: TObject); + procedure DeleteGFFfile1Click(Sender: TObject); + procedure BtnInstallInfoClick(Sender: TObject); + procedure btnInstallDeleteClick(Sender: TObject); + procedure btnEditInstallClick(Sender: TObject); + procedure btnAddInstallClick(Sender: TObject); + procedure edFolderNameKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure gridInstallKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure edFileNameKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure edGffFieldKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure cbGffValueKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure gridGffModKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure btnGetFileNameClick(Sender: TObject); + procedure gridTlkClick(Sender: TObject); + procedure gridTlkKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure btn2daCompareClick(Sender: TObject); + procedure btnMassAddClick(Sender: TObject); + procedure btnNewFieldClick(Sender: TObject); + procedure btnGetScriptNameClick(Sender: TObject); + procedure btnEditScriptClick(Sender: TObject); + procedure btnAddScriptClick(Sender: TObject); + procedure btnDelScriptClick(Sender: TObject); + procedure btnScriptInfoClick(Sender: TObject); + procedure edScriptNameKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure gridScriptKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure gridInstallDblClick(Sender: TObject); + procedure gridScriptDblClick(Sender: TObject); + procedure cbReplaceGffClick(Sender: TObject); + procedure btnGetDestNameClick(Sender: TObject); + procedure btnGetGffDestClick(Sender: TObject); + procedure btnGffDestSetClick(Sender: TObject); + procedure AddSSFFile1Click(Sender: TObject); + procedure DeleteSSFFile1Click(Sender: TObject); + procedure cbSSFEntryChange(Sender: TObject); + procedure btnLoadTokSSFClick(Sender: TObject); + procedure btnEditSSFClick(Sender: TObject); + procedure btnAddSSFClick(Sender: TObject); + procedure chSSFReplaceClick(Sender: TObject); + procedure btnDelSSFClick(Sender: TObject); + procedure cbSSFValueDropDown(Sender: TObject); + procedure cbGffValueDropDown(Sender: TObject); + procedure gridSSFDblClick(Sender: TObject); + procedure gridGffModDblClick(Sender: TObject); + procedure gridTlkDblClick(Sender: TObject); + procedure btnGffLoadFieldsClick(Sender: TObject); + procedure btnCopy2daModClick(Sender: TObject); + procedure btnCompareGffClick(Sender: TObject); + procedure SetupsNewClick(Sender: TObject); + procedure SetupsOpenClick(Sender: TObject); + procedure cbLookupChange(Sender: TObject); + procedure btnMoveGffModUpClick(Sender: TObject); + procedure btnMoveGffModDownClick(Sender: TObject); + private + { Private declarations } + nodeChanges : TTreeNode; + nodeSettings : TTreeNode; + nodeTLK : TTreeNode; + node2DA : TTreeNode; + nodeGFF : TTreeNode; + nodeInstall : TTreeNode; + nodeScript : TTreeNode; + nodeSSF : TTreeNode; + + ini : TST_IniFile; + + l_tokencount : integer; + l_fileloaded : boolean; + l_tokens : string; + l_currsection : string; + // l_lastnode : TTreeNode; + + function GetUniqueModLabel(sLabel : string) : string; + procedure RefreshSSFGrid(); + public + { Public declarations } + function ShowAlertBox(sMessage : string) : word; + function ShowInfoBox(sMessage : string) : word; + function ShowConfirmBox(sMessage : string) : word; + procedure LoadGFFEntries(sSection : string); + procedure LoadSSFEntries(sSection : string); // ADDED(2006-03-09) + procedure Load2DAEntries(sSection : string); + procedure LoadTLKEntries(); + procedure LoadSettings(); + procedure LoadInstallEntries(); + procedure LoadScriptEntries(); + procedure Reset(); + procedure CompareGffFiles(sFile1 : string; sFile2 : string); + function UpdateGffFieldOrder(sMoveKey : string; enMoveDir : TMoveDir) : integer; + end; + +var + MainForm: TMainForm; + +implementation + +uses UEditTokenForm, UMod2DARowForm, UModLabelForm, UChange2daRowForm, + UAddColForm, UInfoForm, UMassAddForm, UFormNewGFF, UFormFilename, UNamespaceForm, UCopy2daRowForm; + +{$R *.DFM} + +// Support function, strip spaces from string and make lowercase. +function Labelize(sLabel : string) : string; +var + iIdx : integer; +begin + iIdx := Pos(' ', sLabel); + while (iIdx <> 0) do begin + sLabel := copy(sLabel, 1, iIdx-1) + '_' + copy(sLabel, iIdx+1, Length(sLabel) - iIdx); + iIdx := Pos(' ', sLabel); + end; + + result := lowercase(sLabel); +end; + + +procedure TMainForm.Reset(); +var + oNode : TTreeNode; + i : integer; +begin + l_fileloaded := False; + l_tokencount := 0; + l_currsection := 'Changes'; + + // Reset the TreeView.... + nodeChanges.selected := True; + + // Delete the existing non-permanent nodes in the TreeView + // with some arcane looping... If there aren't any bugs in here + // somewhere I'll get quite surprised... + i := tree.Items.count - 1; + oNode := tree.Items[i]; + while (oNode <> nil) and (i < tree.Items.count) do begin + if (oNode.parent = nodeSettings) + or (oNode.parent = nodeTLK) + or (oNode.parent = node2DA) + or (oNode.parent = nodeGFF) + or (oNode.parent = nodeInstall) + or (oNode.parent = nodeScript) + or (oNode.parent = nodeSSF) // ADDED(2006-03-09) + then begin + tree.items.delete(oNode); + + // FIX(2006-01-26) This seems to be causing Access Violations for + // no discernable reason. Better to remove it for now. + //if (oNode <> nil) then + // oNode.delete(); + + i := tree.Items.count - 1; + end + else begin + i := i - 1; + end; + + if (i >= 0) then begin + // FIX(2006-01-26) Extra validity check... + if (i < tree.Items.count) then begin + oNode := tree.Items[i]; + end + else begin + i := tree.items.count - 1; + oNode := tree.Items[i]; + end; + end + else + break; + end; + + // Reset the StringGrids... + // It this necessary? The Load*() functions should already do this when needed... + grid2daMod.cols[0].clear(); + grid2daMod.cols[1].clear(); + grid2daMod.rowcount := 1; + + gridTlk.cols[0].clear(); + gridTlk.cols[1].clear(); + gridTlk.rowcount := 1; + + gridTokens.cols[0].clear(); + gridTokens.cols[1].clear(); + gridTokens.rowcount := 1; + + gridGffMod.cols[0].clear(); + gridGffMod.cols[1].clear(); + gridGffMod.rowcount := 1; + + gridInstall.cols[0].clear(); + gridInstall.cols[1].clear(); + gridInstall.rowcount := 1; + + // Reset the input boxes... + edFolderName.clear(); + edFileName.clear(); + cbFileReplace.checked := False; + edSetCaption.clear(); + edSetConfirm.clear(); + edGffField.clear(); + cbGffValue.clear(); + bxSetLog.itemindex := 0; + + edGffField.Color := clWindow; + + // Hide all the panels... + paneSettings.Visible := False; + paneTLK.Visible := False; + pane2da.Visible := False; + paneGFF.Visible := False; + paneInstall.Visible := False; + paneScript.Visible := False; + paneSSF.Visible := False; // ADDED(2006-03-09) + + // Reset some captions and visibility... + Caption := 'TSLPatcher Config Editor'; + lblBackground.caption := 'Open or create a new changes.ini file in the Menu.'; + tree.Enabled := False; + Actions1.enabled := False; + InfoForm.Hide(); +end; + +procedure TMainForm.LoadSettings(); +var + iVal : integer; + bVal : boolean; +begin + if ini.SectionExists('Settings') then begin + edSetConfirm.text := ini.ReadString('Settings', 'ConfirmMessage', 'N/A'); + edSetCaption.text := ini.ReadString('Settings', 'WindowCaption', ''); + iVal := ini.ReadInteger('Settings', 'LogLevel', 3); + if (iVal > 4) then + iVal := 4 + else if (iVal < 0) then + iVal := 0; + + bxSetLog.itemindex := iVal; + + bVal := ini.ReadBool('Settings', 'InstallerMode', True); + if (bVal = True) then + cbMode.itemindex := 0 + else + cbMode.itemindex := 1; + + bVal := ini.ReadBool('Settings', 'BackupFiles', True); + if (bVal = True) then + cbBackups.itemindex := 0 + else + cbBackups.itemindex := 1; + + // ADDED(2005-08-02) Added GUI for the Fallback log style... + bVal := ini.ReadBool('Settings', 'PlaintextLog', False); + if (bVal = True) then + cbLogstyle.itemindex := 1 + else + cbLogstyle.itemindex := 0; + + // ADDED(2005-10-04) Added GUI for the REQUIRED settings. + edSetReqFile.text := ini.ReadString('Settings', 'Required', ''); + edSetReqMsg.text := ini.ReadString('Settings', 'RequiredMsg', ''); + + // ADDED(2006-05-28) Added new field for setting game folder lookup + version for registry lookups. + iVal := ini.ReadInteger('Settings', 'LookupGameNumber', 2); + bVal := ini.ReadBool('Settings', 'LookupGameFolder', false); + if (iVal = 1) then + cbGameVer.ItemIndex := 1 + else + cbGameVer.ItemIndex := 0; + + cbGameVer.Enabled := bVal; + + if bVal then + cbLookup.ItemIndex := 1 + else + cbLookup.ItemIndex := 0; + + // ADDED(2006-07-21) Added script debug toggle... + cbDebugScripts.Checked := ini.ReadBool('Settings', 'SaveProcessedScripts', false); + + // ADDED(2006-01-14) Added GUI for Script Compiler Commandline params + // REMOVED(2006-05-28) Will probably just cause confusion. use tk102s compiler instead + // which will not need any extra parameters. + // edSetParams.text := ini.ReadString('Settings', 'ScriptCompilerFlags', ''); + + end; + l_currsection := 'Settings'; +end; + + +procedure TMainForm.LoadScriptEntries(); +var + oList : TStringList; + i : integer; + sKey : string; + sVal : string; +begin + // Clear out any old data... + gridScript.cols[0].clear(); + gridScript.cols[1].clear(); + + // Reset grid dimensions... + gridScript.rowcount := 1; + gridScript.colwidths[0] := 170; + gridScript.colwidths[1] := (gridScript.width - (gridScript.colwidths[0] + 6)); + + // No point in continuing if the section isn't in the INI file, nothing to load... + if not ini.SectionExists('CompileList') then + exit; + + // Read entries from the list in the INI file and add to grid... + oList := TStringList.Create(); + try + ini.readsection('CompileList', oList); + + for i := 0 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sVal := ini.ReadString('CompileList', sKey, ''); + + gridScript.rowcount := gridScript.rowcount + 1; + gridScript.cells[0, i] := sKey; + gridScript.cells[1, i] := sVal; + end; + gridScript.rowcount := gridScript.rowcount - 1; + finally + oList.free(); + end; + +end; + + +procedure TMainForm.LoadInstallEntries(); +var + oList : TStringList; + oFiles : TStringList; + i, n : integer; + iRow : integer; + sLabel : string; + sFolder : string; + sFile : string; + sKey : string; +begin + gridInstall.cols[0].clear(); + gridInstall.cols[1].clear(); + gridInstall.cols[2].clear(); // ADDED(2005-06-10) Added Replace column. + gridInstall.rowcount := 1; + gridInstall.colwidths[0] := 168; + gridInstall.colwidths[2] := 32; // ADDED(2005-06-10) Added Replace column. + gridInstall.colwidths[1] := (gridInstall.width - (gridInstall.colwidths[0] + gridInstall.colwidths[2] + 24)); + + if ini.SectionExists('InstallList') then begin + l_currsection := 'InstallList'; + iRow := 0; + + oList := TStringList.Create(); + try + ini.readsection('InstallList', oList); + + for i := 0 to (oList.count - 1) do begin + sLabel := oList.Strings[i]; + sFolder := ini.ReadString('InstallList', sLabel, ''); + if (sLabel <> '') and (sFolder <> '') and ini.SectionExists(sLabel) then begin + oFiles := TStringList.Create(); + try + ini.readsection(sLabel, oFiles); + for n := 0 to (oFiles.count - 1) do begin + sKey := oFiles.Strings[n]; + sFile := ini.ReadString(sLabel, sKey, ''); + if (sFile <> '') then begin + gridInstall.rowcount := gridInstall.rowcount + 1; + gridInstall.cells[0, iRow] := sFolder; + gridInstall.cells[1, iRow] := sFile; + + // ADDED(2005-06-10) Fill in value in new column. + if (lowercase(copy(sKey, 1, 7)) = 'replace') then + gridInstall.cells[2, iRow] := 'Rep.' + else + gridInstall.cells[2, iRow] := 'Copy'; + + inc(iRow); + end; + end; + finally + oFiles.free(); + end; + end; + end; + gridInstall.rowcount := gridInstall.rowcount - 1; + finally + oList.free(); + end; + end; +end; + +procedure TMainForm.LoadTLKEntries(); +var + oList : TStringList; + i : integer; + sKey : string; + sVal : string; +begin + if ini.SectionExists('TLKList') then begin + l_currsection := 'TLKList'; + oList := TStringList.Create(); + try + ini.readsection('TLKList', oList); + + gridTokens.cols[0].clear; + gridTokens.cols[1].clear; + gridTokens.rowcount := 1; + l_tokencount := 0; + + for i := 0 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sVal := ini.ReadString('TLKList', sKey, ''); + if (sKey <> '') then begin + gridTokens.rowcount := gridTokens.rowcount + 1; + gridTokens.cells[0, i] := sKey; + gridTokens.cells[1, i] := sVal; + inc(l_tokencount); + end; + end; + gridTokens.rowcount := gridTokens.rowcount - 1; + finally + oList.free(); + end; + end; + + gridTokens.colwidths[0] := 72; + gridTokens.colwidths[1] := (gridTokens.width - (gridTokens.colwidths[0] + 6)); + gridTlk.colwidths[0] := 48; + gridTlk.colwidths[1] := (gridTlk.width - (gridTlk.colwidths[0] + 6)); + + txtEntry.Clear(); +end; + +// ADDED(2006-03-09) +procedure TMainForm.LoadSSFEntries(sSection : string); +var + oList : TStringList; + i : integer; + sKey : string; + sVal : string; +begin + l_currsection := sSection; + lblSSFHeading.caption := 'Modified sound entries in file ' + sSection + ': '; + + RefreshSSFGrid(); + + cbSSFEntry.text := ''; + cbSSFEntry.ItemIndex := -1; + cbSSFValue.text := ''; + + oList := TStringList.Create(); + ini.ReadSection('SSFList', oList); + + for i := 0 to (oList.count - 1) do begin + sKey := oList.Strings[i]; + sVal := ini.ReadString('SSFList', sKey, ''); + if (sVal = sSection) then begin + chSSFReplace.Checked := (copy(sKey, 1, 7) = 'Replace'); + break; + end; + end; +end; + +procedure TMainForm.LoadGFFEntries(sSection : string); +var + oList : TStringList; + sKey : string; + sVal : string; + i : integer; + iCnt : integer; +begin + l_currsection := sSection; + lblGffHeading.caption := 'Modified fields in ' + sSection + ': '; + + // ADDED(2006-02-03) Read the optional !Destination key and display its + // value in the box. + if ini.SectionExists(l_currsection) then begin + edGffDest.text := ini.ReadString(l_currsection, '!Destination', 'override'); + end + else begin + edGffDest.text := 'override'; + end; + // - - - - - - - - + + // ADDED(2006-03-25) - Clear the Value boxes when switching files... + cbGffValue.clear(); + if (l_tokens <> l_currsection) then begin + edGffField.clear(); + edGffField.Color := clWindow; + end; + + + oList := TStringList.Create(); + try + gridGffMod.cols[0].clear(); + gridGffMod.cols[1].clear(); + gridGffMod.rowcount := 1; + gridGffMod.colwidths[0] := 200; + gridGffMod.colwidths[1] := (gridGffMod.width - (gridGffMod.colwidths[0] + 6)); + + if not ini.SectionExists(sSection) then + exit; + + // ADDED(2006-01-26) Read the stored value for the "Replace" checkbox for this file. + cbReplaceGff.Checked := ini.ReadBool(sSection, '!ReplaceFile', false); + + ini.ReadSection(sSection, oList); + if (oList.count > 0) then begin + iCnt := 0; + for i := 0 to (oList.count-1) do begin + sKey := oList.strings[i]; + sVal := ini.ReadString(sSection, sKey, ''); + + // CHANGED(2006-01-11) Don't show AddField modifiers in this list... + // CHANGED(2006-01-26) Don't show the !ReplaceFile key in this list... + // CHANGED(2006-02-03) Don't show the !Destination key in this list... + // CHANGED(2006-07-25) I changed my mind. :) Do show AddField Modifiers... + if (sVal <> '') + // and (lowercase(copy(sKey, 1, 8)) <> 'addfield') + and (lowercase(copy(sKey, 1, 12)) <> '!replacefile') + and (lowercase(copy(sKey, 1, 12)) <> '!destination') + then begin + gridGffMod.rowcount := gridGffMod.rowcount + 1; + gridGffMod.cells[0, iCnt] := sKey; + gridGffMod.cells[1, iCnt] := sVal; + inc(iCnt); + end; + end; + gridGffMod.rowcount := gridGffMod.rowcount - 1; + end; + + finally + oList.free(); + end; +end; + + +procedure TMainForm.Load2DAEntries(sSection : string); +var + oList : TStringList; + sKey : string; + sVal : string; + i : integer; +begin + l_currsection := sSection; + oList := TStringList.Create(); + try + grid2daMod.cols[0].clear(); + grid2daMod.cols[1].clear(); + grid2daMod.rowcount := 1; + grid2daMod.colwidths[0] := 82; + grid2daMod.colwidths[1] := (grid2daMod.width - (grid2daMod.colwidths[0] + 6)); + + if not ini.SectionExists(sSection) then + exit; + + ini.ReadSection(sSection, oList); + if (oList.count > 0) then begin + for i := 0 to (oList.count-1) do begin + sKey := oList.strings[i]; + sVal := ini.ReadString(sSection, sKey, ''); + if (sVal <> '') then begin + grid2daMod.rowcount := grid2daMod.rowcount + 1; + grid2daMod.cells[0, i] := sKey; + grid2daMod.cells[1, i] := sVal; + end; + end; + grid2daMod.rowcount := grid2daMod.rowcount - 1; + end; + + finally + oList.free(); + end; +end; + +function TMainForm.ShowInfoBox(sMessage : string) : word; +begin + result := MessageDlg(sMessage, mtInformation, [mbOK], 0); +end; + +function TMainForm.ShowAlertBox(sMessage : string) : word; +begin + result := MessageDlg(sMessage, mtWarning, [mbOK], 0); +end; + +function TMainForm.ShowConfirmBox(sMessage : string) : word; +begin + result := MessageDlg(sMessage, mtConfirmation, [mbYes, mbNo], 0); +end; + +procedure TMainForm.treeMouseDown(Sender: TObject; + Button: TMouseButton; Shift: TShiftState; X, Y: Integer); +begin + if (Button = mbRight) then begin + if tree.Selected = nil then Exit; + + tree.popupmenu := nil; + Add2dafile1.enabled := False; + AddGFFFile1.enabled := False; + AddSSFFile1.enabled := False; // ADDED(2006-03-09) + Delete2dafile1.enabled := False; + DeleteGFFfile1.enabled := False; + DeleteSSFFile1.enabled := False; // ADDED(2006-03-09) + + if (tree.Selected = node2DA) then begin + tree.popupmenu := TreeMenu; + Add2dafile1.enabled := True; + end + else if (tree.Selected.Parent = node2DA) then begin + tree.popupmenu := TreeMenu; + Delete2dafile1.enabled := True; + end + else if (tree.Selected = nodeGFF) then begin + tree.popupmenu := TreeMenu; + AddGFFFile1.enabled := True; + end + else if (tree.Selected.Parent = nodeGFF) then begin + tree.popupmenu := TreeMenu; + DeleteGFFfile1.enabled := True; + end + else if (tree.Selected = nodeSSF) then begin // ADDED(2006-03-09) + tree.popupmenu := TreeMenu; + AddSSFFile1.enabled := True; + end + else if (tree.Selected.Parent = nodeSSF) then begin // ADDED(2006-03-09) + tree.popupmenu := TreeMenu; + DeleteSSFFile1.enabled := True; + end; + end; +end; + +procedure TMainForm.FormShow(Sender: TObject); +begin + if (tree <> nil) and (tree.items <> nil) then begin + // Store the base categories in the treeview so they + // can be identified later... + if (tree.items.count = 8) then begin // CHANGED(2006-03-09) + nodeChanges := tree.items[0]; + nodeSettings := tree.items[1]; + nodeTLK := tree.items[2]; + node2DA := tree.items[3]; + nodeGFF := tree.items[4]; + nodeScript := tree.items[5]; + nodeSSF := tree.items[6]; // ADDED(2006-03-09) + nodeInstall := tree.items[7]; // CHANGED(2006-03-25) Moved down 2 steps. + end; + end; +end; + +procedure TMainForm.mnuQuitClick(Sender: TObject); +begin + Application.terminate; +end; + +procedure TMainForm.mnuOpenClick(Sender: TObject); +var + oList : TStringList; + oNode : TTreeNode; + i : integer; +// iVal : integer; + sKey : string; + sVal : string; + sSec : string; +begin + OpenBox.Filter := 'INI file (*.ini)|*.ini'; + OpenBox.DefaultExt := 'ini'; + OpenBox.FileName := 'changes.ini'; + OpenBox.Title := 'Select a changes.ini file to open...'; + OpenBox.Options := [ofHideReadOnly,ofPathMustExist,ofFileMustExist,ofEnableSizing]; + + if not OpenBox.execute then + exit; + + MainForm.Reset(); + + if (ini <> nil) then + ini.free(); + + ini := TST_IniFile.Create(OpenBox.FileName); + + if ini.SectionExists('Settings') then begin + // FIX(2006-01-26) - Unnessecary, this gets run when the Settings node it + // selected in the treeview below anyway. + // LoadSettings(); + end + else begin + ShowAlertBox('Incorrectly formatted changes.ini, the Settings section is missing!'); + exit; + end; + + Caption := 'TSLPatcher Config Editor [' + OpenBox.FileName + ']'; + l_fileloaded := True; + tree.Enabled := True; + Actions1.enabled := True; + lblBackground.caption := 'Select a category in the list to the left.'; + nodeChanges.text := ExtractFileName(OpenBox.FileName); + + if ini.SectionExists('TLKList') then begin + oList := TStringList.Create(); + try + ini.readsection('TLKList', oList); + + gridTokens.cols[0].clear; + gridTokens.cols[1].clear; + gridTokens.rowcount := 1; + + for i := 0 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sVal := ini.ReadString('TLKList', sKey, ''); + if (sKey <> '') then begin + gridTokens.rowcount := gridTokens.rowcount + 1; + gridTokens.cells[0, i] := sKey; + gridTokens.cells[1, i] := sVal; + + inc(l_tokencount); + end; + end; + finally + oList.free(); + end; + end; + + if ini.SectionExists('2DAList') then begin + oList := TStringList.Create(); + try + ini.readsection('2DAList', oList); + for i := 0 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sSec := ini.ReadString('2DAList', sKey, ''); + oNode := tree.items.addchild(node2DA, sSec); + oNode.ImageIndex := 7; + oNode.SelectedIndex := 7; + end; + finally + oList.free(); + end; + end; + + if ini.SectionExists('GFFList') then begin + oList := TStringList.Create(); + try + ini.readsection('GFFList', oList); + for i := 0 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sSec := ini.ReadString('GFFList', sKey, ''); + oNode := tree.items.addchild(nodeGFF, sSec); + oNode.ImageIndex := 7; + oNode.SelectedIndex := 7; + end; + finally + oList.free(); + end; + end; + + // ADDED(2006-03-09) - Load the SSF file list into the treeview. + if ini.SectionExists('SSFList') then begin + oList := TStringList.Create(); + try + ini.readsection('SSFList', oList); + for i := 0 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sSec := ini.ReadString('SSFList', sKey, ''); + oNode := tree.items.addchild(nodeSSF, sSec); + oNode.ImageIndex := 7; + oNode.SelectedIndex := 7; + end; + finally + oList.free(); + end; + end; + + if (nodeSettings <> nil) then + nodeSettings.selected := True; +end; + +procedure TMainForm.btnOpenTlkClick(Sender: TObject); +var + tlkFile : TTLKFileHandler; + oTlkEntry : TTLKString; + iCount : integer; + oOldCursor : TCursor; +begin + OpenBox.Filter := 'TLK file (*.tlk)|*.tlk'; + OpenBox.DefaultExt := 'tlk'; + OpenBox.FileName := 'append.tlk'; + OpenBox.Title := 'Select an append.tlk file to use...'; + OpenBox.Options := [ofHideReadOnly,ofPathMustExist,ofFileMustExist,ofEnableSizing]; + + if not OpenBox.execute then + exit; + + gridTlk.cols[0].clear(); + gridTlk.cols[1].clear(); + gridTlk.rowcount := 1; + gridTlk.colwidths[0] := 48; + gridTlk.colwidths[1] := (gridTlk.width - (gridTlk.colwidths[0] + 6)); + + tlkFile := TTLKFileHandler.Create(OpenBox.filename); + oOldCursor := Screen.Cursor; + Screen.Cursor := crHourglass; + Application.ProcessMessages(); + try + iCount := 0; + oTlkEntry := tlkFile.strings.first(); + + while ((oTlkEntry <> nil) and (DWORD(iCount) < tlkFile.count)) do begin + gridTlk.rowcount := gridTlk.rowcount + 1; + gridTlk.cells[0, iCount] := IntToStr(oTlkEntry.strref); + gridTlk.cells[1, iCount] := oTlkEntry.strtext; + inc(iCount); + oTlkEntry := tlkFile.strings.next(); + end; + + if (iCount > 0) then begin + gridTlk.rowcount := gridTlk.rowcount - 1; + btnAddStrRef.enabled := True; + end; + finally + Screen.Cursor := oOldCursor; + tlkFile.free(); + end; +end; + +procedure TMainForm.btnAddStrRefClick(Sender: TObject); +var + sRef : string; + nCnt : integer; +begin + if (gridTlk.rowcount > 0) then begin + sRef := gridTlk.cells[0, gridTlk.Selection.top]; + nCnt := gridTokens.rowcount; + gridTokens.rowcount := nCnt + 1; + + // Fult hack, eftersom man inte kan få grid-eländet att börja på rad 0... :/ + if (gridTokens.cells[0, nCnt-1] = '') and (gridTokens.cells[0, nCnt-1] = '') then begin + nCnt := nCnt - 1; + gridTokens.rowcount := gridTokens.rowcount - 1; + end; + + gridTokens.cells[0, nCnt] := 'StrRef' + IntToStr(l_tokencount); + gridTokens.cells[1, nCnt] := sRef; + gridTokens.row := nCnt; + ini.writestring('TLKList', 'StrRef' + IntToStr(l_tokencount), sRef); + inc(l_tokencount); + end; +end; + +procedure TMainForm.btnDelTokenClick(Sender: TObject); +var + i : integer; + sKey : string; +begin + sKey := gridTokens.cells[0, gridTokens.Selection.top]; + + if (gridTokens.rowcount > 0) and (sKey <> '') then begin + ini.deletekey('TLKList', sKey); + + for i := gridTokens.Selection.top to (gridTokens.rowcount-1) do begin + gridTokens.Rows[i] := gridTokens.Rows[i+1]; + end; + gridTokens.RowCount := gridtokens.rowcount - 1; + end; +end; + + +procedure TMainForm.treeChange(Sender: TObject; Node: TTreeNode); +begin + if (not l_fileloaded) then + exit; + + // 1. Hide all the panels: + paneSettings.Visible := False; + paneTLK.Visible := False; + pane2da.Visible := False; + paneGFF.Visible := False; + paneInstall.Visible := False; + paneScript.Visible := False; + paneSSF.Visible := False; // ADDED(2006-03-09) + + // 2. Show the panel for the node that was selected: + if (tree.Selected = nodeSettings) then begin + paneSettings.Visible := True; + LoadSettings(); + lblBackground.caption := 'Select a category in the list to the left.'; + lblBackground.Visible := false; + end + else if (tree.Selected = nodeTLK) then begin + paneTLK.Visible := True; + LoadTLKEntries(); + lblBackground.caption := 'Select a category in the list to the left.'; + lblBackground.Visible := false; + end + else if (tree.Selected.Parent = node2DA) then begin + pane2da.Visible := True; + Load2DAEntries(tree.selected.text); + lbl2daLabel1.caption := 'Modifiers for ' + tree.Selected.text + ': '; + lblBackground.Visible := true; + lblBackground.caption := 'Select a 2DA file to edit in the list to the left.'; + end + else if (tree.Selected.Parent = nodeGFF) then begin + paneGFF.Visible := True; + LoadGFFEntries(tree.selected.text); + lblBackground.Visible := true; + lblBackground.caption := 'Select a GFF file to edit in the list to the left.'; + end + else if (tree.Selected = node2DA) then begin + lblBackground.Visible := true; + if (node2DA.count = 0) then + lblBackground.caption := 'Use the Modifiers menu to add new 2DA files..' + else + lblBackground.caption := 'Select a 2DA file to edit in the list to the left.'; + end + else if (tree.Selected = nodeGFF) then begin + lblBackground.Visible := true; + if (nodeGFF.count = 0) then + lblBackground.caption := 'Use the Modifiers menu to add new GFF files..' + else + lblBackground.caption := 'Select a GFF file to edit in the list to the left.'; + end + else if (tree.Selected = nodeChanges) then begin + lblBackground.caption := 'Select a category in the list to the left.'; + end + else if (tree.Selected = nodeInstall) then begin + paneInstall.Visible := True; + LoadInstallEntries(); + lblBackground.caption := 'Select a category in the list to the left.'; + lblBackground.Visible := false; + end + else if (tree.Selected = nodeScript) then begin + paneScript.Visible := True; + lblBackground.Visible := false; + LoadScriptEntries(); + end + else if (tree.Selected = nodeSSF) then begin // ADDED(2006-03-09) + lblBackground.Visible := true; + if (nodeSSF.count = 0) then + lblBackground.caption := 'Use the Modifiers menu to add new SSF files..' + else + lblBackground.caption := 'Select a SSF file to edit in the list to the left.'; + end + else if (tree.Selected.Parent = nodeSSF) then begin // ADDED(2006-03-09) + lblBackground.Visible := true; + paneSSF.Visible := True; + LoadSSFEntries(tree.selected.text); + lblBackground.caption := 'Select a SSF file to edit in the list to the left.'; + end; +end; + +procedure TMainForm.mnuNewClick(Sender: TObject); +var + F : TextFile; + sFilename : string; +begin + if not SaveBox.execute then + exit; + + MainForm.Reset(); + sFilename := SaveBox.FileName; + + // Skriv de fasta sektionerna i förväg så de inte automatgenereras och hamnar + // i den otrevliga röran som INI-write funktionerna skapar... + AssignFile(F, sFilename); + Rewrite(F); + Writeln(F, '; =====================================================[v1.0.5b1]===='); + Writeln(F, '; TSLPATCHER - GENERATED MODIFICATIONS FILE (' + DateToStr(Date) + ')'); + Writeln(F, '; ==================================================================='); + Writeln(F, '; This file is automatically generated and as such has no formatting'); + Writeln(F, '; to speak of. You can insert blank lines between sections (but NOT'); + Writeln(F, '; between keys within a section!) and add comment lines starting'); + Writeln(F, '; with semicolon to make it more readable without breaking anything.'); + Writeln(F, '; -------------------------------------------------------------------'); + Writeln(F, ''); + Writeln(F, '[Settings]'); + Writeln(F, ''); + Writeln(F, ''); + Writeln(F, '[TLKList]'); + Writeln(F, ''); + Writeln(F, ''); + Writeln(F, '[InstallList]'); + Writeln(F, ''); + Writeln(F, ''); + Writeln(F, '[2DAList]'); + Writeln(F, ''); + Writeln(F, ''); + Writeln(F, '[GFFList]'); + Writeln(F, ''); + Writeln(F, ''); + Writeln(F, '[CompileList]'); + Writeln(F, ''); + Writeln(F, ''); + Writeln(F, '[SSFList]'); + Writeln(F, ''); + Writeln(F, ''); + Writeln(F, '; ==================================================================='); + Writeln(F, ''); + CloseFile(F); + + // Frigör en gammal INI-hanterare om den redan är initialiserad.... + if (ini <> nil) then + ini.free(); + + ini := TST_IniFile.Create(sFilename); + ini.WriteInteger('Settings', 'FileExists', 1); + + Caption := 'TSLPatcher Config Editor [' + sFilename + ']'; + l_fileloaded := True; + tree.Enabled := True; + Actions1.enabled := True; + lblBackground.caption := 'Select a category in the list to the left.'; + nodeChanges.text := ExtractFileName(sFilename); + + // Börja på settings-panelen för enkelhets skull... + if (nodeSettings <> nil) then + nodeSettings.selected := True; +end; + +procedure TMainForm.gridTokensDblClick(Sender: TObject); +var + iSel : LongInt; + sOld : string; + sTok : string; +begin + // Öppna edit-ruta om användaren dubbelklickar i token-listan... + iSel := gridTokens.Row; + sOld := gridTokens.cells[0, iSel]; + sTok := gridTokens.cells[0, iSel]; + // FIX(2006-03-25) Strip Token number and show it in the box.... + EditTokenForm.edToken.text := copy(sTok, 7, (Length(sTok)+1) - 7); + EditTokenForm.edStrRef.text := gridTokens.cells[1, iSel]; + + if (EditTokenForm.ShowModal = mrOk) then begin + // FIX(2006-03-25) Add the StrRef part back to the Token number, but only + // attempt to save changes if numeric values were specified in both fields. + if GetIsNumber(EditTokenForm.edToken.text) and GetIsNumber(EditTokenForm.edStrRef.text) then begin + sTok := 'StrRef' + EditTokenForm.edToken.text; + gridTokens.cells[0, iSel] := sTok; + gridTokens.cells[1, iSel] := EditTokenForm.edStrRef.text; + gridTokens.row := iSel; + + ini.writestring('TLKList', gridTokens.cells[0, iSel], gridTokens.cells[1, iSel]); + + // FIX(2006-03-25) Don't try to delete key if a cell with no valid + // keyname was found. + if (sOld <> '') and (sOld <> EditTokenForm.edToken.text) then + ini.DeleteKey('TLKList', sOld); + end; + end; +end; + +procedure TMainForm.btnSetResetClick(Sender: TObject); +begin + LoadSettings(); +end; + +procedure TMainForm.btnSetSaveClick(Sender: TObject); +begin + // Spara ändringar i settings... Lite fult sätt, men vafan, orkar inte... :) + if (edSetCaption.text = '') then + ini.DeleteKey('Settings', 'WindowCaption') + else + ini.WriteString('Settings', 'WindowCaption', edSetCaption.text); + + if (edSetConfirm.text = '') then + ini.DeleteKey('Settings', 'ConfirmMessage') + else + ini.WriteString('Settings', 'ConfirmMessage', edSetConfirm.text); + + ini.WriteInteger('Settings', 'LogLevel', bxSetLog.itemindex); + + if (cbMode.itemindex = 0) then + ini.WriteBool('Settings', 'InstallerMode', True) + else + ini.WriteBool('Settings', 'InstallerMode', False); + + if (cbBackups.itemindex = 0) then + ini.WriteBool('Settings', 'BackupFiles', True) + else + ini.WriteBool('Settings', 'BackupFiles', False); + + // ADDED(2005-08-02) - Added GUI for the fallback log style... + if (cbLogstyle.itemindex = 0) then + ini.WriteBool('Settings', 'PlaintextLog', False) + else + ini.WriteBool('Settings', 'PlaintextLog', True); + + // ADDED(2005-10-04) - Added GUI for the optional REQUIRED settings + if (edSetReqFile.text = '') then + ini.DeleteKey('Settings', 'Required') + else + ini.WriteString('Settings', 'Required', edSetReqFile.text); + + // ADDED(2005-10-04) - Added GUI for the optional REQUIRED settings + if (edSetReqMsg.text = '') then + ini.DeleteKey('Settings', 'RequiredMsg') + else + ini.WriteString('Settings', 'RequiredMsg', edSetReqMsg.text); + + // ADDED(2006-05-28) Added new settings field.... + ini.WriteBool('Settings', 'LookupGameFolder', (cbLookup.itemindex <> 0)); + + // ADDED(2006-05-28) Added new settings field + if (cbGameVer.ItemIndex = 1) then + ini.WriteInteger('Settings', 'LookupGameNumber', 1) + else + ini.WriteInteger('Settings', 'LookupGameNumber', 2); + + // ADDED(2006-07-21) Added debug toggle... + ini.WriteBool('Settings', 'SaveProcessedScripts', cbDebugScripts.Checked); + + + // ADDED(2006-01-14) - Added GUI for compiler commandline params + // REMOVED(2006-05-28) - Should no longer be needed... + //if (edSetParams.text = '') then + // ini.DeleteKey('Settings', 'ScriptCompilerFlags') + //else + // ini.WriteString('Settings', 'ScriptCompilerFlags', edSetParams.text); + + + ShowInfoBox('The settings have been stored in the open changes.ini file!'); +end; + +procedure TMainForm.btn2daAddRowClick(Sender: TObject); +var + sInput : string; + sKey : string; + i, n : integer; + bFound : boolean; +begin + NewLabelForm.edModLabel.text := ''; + NewLabelForm.lblTitle.caption := 'Add new 2DA Modifier'; + NewLabelForm.Caption := 'Specify modifier label'; + + if (NewLabelForm.ShowModal = mrOk) then begin + sInput := NewLabelForm.edModLabel.text; + + if (ini.SectionExists(sInput)) then begin + ShowAlertBox('The specified modifier label already exists for another modifier. Try again.'); + exit; + end; + + // Ingen giltig label angavs, så avbryt... + if (sInput = 'N/A') or (sInput = '') or (Pos(' ', sInput) > 0) then begin + ShowAlertBox('Invalid modifier label specified. It must be at least 1 character and may contain no spaces. Try again.'); + exit; + end; + + // Hitta första lediga AddRow# nyckel att använda... + bFound := True; + sKey := 'AddRow0'; + i := 0; + while bFound do begin + bFound := False; + for n := 0 to (grid2daMod.rowcount-1) do begin + if (lowercase(grid2daMod.cells[0, n]) = lowercase(sKey)) then begin + bFound := True; + break; + end; + end; + + if not bFound then begin + sKey := 'AddRow' + IntToStr(i); + break; + end + else begin + sKey := 'AddRow' + IntToStr(i+1); + inc(i); + end; + end; + + if (sKey = '') then begin + ShowAlertBox('ERROR! Failed to generate a modifier key!'); + exit; + end; + + ini.WriteString(l_currsection, sKey, sInput); + + Mod2DARowForm.Caption := 'Add new line in file ' + l_currsection + ' [' + sInput + ']'; + Mod2DARowForm.box_ini := ini; + Mod2DARowForm.box_section := sInput; + Mod2DARowForm.box_2daname := l_currsection; + Mod2DARowForm.LoadIniData(); + Mod2DARowForm.ShowModal; + Load2DAEntries(l_currsection); + + for i := 0 to (grid2daMod.rowcount - 1) do begin + if (grid2daMod.cells[0, i] = sKey) then begin + grid2daMod.row := i; + break; + end; + end; + end; +end; + +procedure TMainForm.grid2daModDblClick(Sender: TObject); +var + sMod : string; + sType : string; + iRow : integer; +begin + // Användaren dubbelklickade i modifier-listan, öppna fönster i Edit-läge + // för den typ av modifierare som det klickades på. + sMod := grid2daMod.cells[1, grid2daMod.row]; + sType := grid2daMod.cells[0, grid2daMod.row]; + iRow := grid2daMod.row; + if (copy(sType, 1, 6) = 'AddRow') then begin + Mod2DARowForm.Caption := 'Add line in file ' + l_currsection + ' [' + sMod + ']'; + Mod2DARowForm.box_ini := ini; + Mod2DARowForm.box_2daname := l_currsection; + Mod2DARowForm.box_section := sMod; + + Mod2DARowForm.LoadIniData(); + Mod2DARowForm.ShowModal(); + Load2DAEntries(l_currsection); + grid2daMod.row := iRow; + end + else if (copy(sType, 1, 9) = 'ChangeRow') then begin + Change2daForm.Caption := 'Change line in file ' + l_currsection + ' [' + sMod + ']'; + Change2daForm.box_ini := ini; + Change2daForm.box_2daname := l_currsection; + Change2daForm.box_type := 'ChangeRow'; + Change2daForm.box_section := sMod; + + Change2daForm.boxIdentifier.Caption := ' Set line to modify '; + Change2daForm.boxChangeValue.Caption := ' Change column value '; + Change2daForm.lblHeadline.Caption := 'Modify 2DA line'; + Change2daForm.lblGridHeading.caption := 'Modified column values for line:'; + + Change2daForm.LoadIniData(); + Change2daForm.ShowModal(); + Load2DAEntries(l_currsection); + grid2daMod.row := iRow; + end + else if (copy(sType, 1, 7) = 'CopyRow') then begin + Copy2daForm.Caption := 'Copy line in file ' + l_currsection + ' [' + sMod + ']'; + Copy2daForm.box_ini := ini; + Copy2daForm.box_2daname := l_currsection; + Copy2daForm.box_type := 'CopyRow'; + Copy2daForm.box_section := sMod; + + Copy2daForm.boxIdentifier.Caption := ' Set existing line to copy '; + Copy2daForm.boxChangeValue.Caption := ' Change column value '; + Copy2daForm.lblHeadline.Caption := 'Copy 2DA line'; + Copy2daForm.lblGridHeading.caption := 'New column values for copied line:'; + + Copy2daForm.LoadIniData(); + Copy2daForm.ShowModal(); + Load2DAEntries(l_currsection); + grid2daMod.row := iRow; + end + else if (copy(sType, 1, 9) = 'AddColumn') then begin + Add2daColForm.Caption := 'Add column in file ' + l_currsection + ' [' + sMod + ']'; + Add2daColForm.box_ini := ini; + Add2daColForm.box_2daname := l_currsection; + Add2daColForm.box_type := 'AddColumn'; + Add2daColForm.box_section := sMod; + + Add2daColForm.LoadIniData(); + Add2daColForm.ShowModal(); + Load2DAEntries(l_currsection); + grid2daMod.row := iRow; + end; +end; + +procedure TMainForm.btn2daDeleteClick(Sender: TObject); +var + sMod : string; + sType : string; +begin + sType := grid2daMod.cells[0, grid2daMod.Selection.top]; + sMod := grid2daMod.cells[1, grid2daMod.Selection.top]; + + if (sType = '') then + exit; + + if (ShowConfirmBox('Are you sure you wish to permanently delete the modifier "' + sMod + '" for the 2da file "' + l_currsection + '"') = mrYes) then begin + // Ta bort både nyckel från filens sektion och hela Modifier sektionen. + ini.EraseSection(sMod); + ini.DeleteKey(l_currsection, sType); + + // Ladda om list-innehållet. + Load2DAEntries(l_currsection); + end; +end; + +procedure TMainForm.btn2daModifyClick(Sender: TObject); +var + sInput : string; + sKey : string; + i, n : integer; + bFound : boolean; +begin + NewLabelForm.edModLabel.text := ''; + NewLabelForm.lblTitle.caption := 'Add new 2DA Modifier'; + NewLabelForm.Caption := 'Specify modifier label'; + + if (NewLabelForm.ShowModal = mrOk) then begin + sInput := NewLabelForm.edModLabel.text; + + if (ini.SectionExists(sInput)) then begin + ShowAlertBox('The specified modifier label already exists for another modifier. Try again.'); + exit; + end; + + // Inget giltigt värde inmatat, avbryt... + if (sInput = 'N/A') or (sInput = '') or (Pos(' ', sInput) > 0) then begin + ShowAlertBox('Invalid modifier label specified. It must be at least 1 character and may contain no spaces. Try again.'); + exit; + end; + + // Hitta första lediga ChangeRow# nyckeln att använda... + sKey := 'ChangeRow0'; + i := 0; + bFound := True; + while bFound do begin + bFound := False; + for n := 0 to (grid2daMod.rowcount-1) do begin + if (lowercase(grid2daMod.cells[0, n]) = lowercase(sKey)) then begin + bFound := True; + break; + end; + end; + + if not bFound then begin + sKey := 'ChangeRow' + IntToStr(i); + break; + end + else begin + sKey := 'ChangeRow' + IntToStr(i+1); + inc(i); + end; + end; + + if (sKey = '') then begin + ShowAlertBox('ERROR! Failed to generate a modifier key!'); + exit; + end; + + ini.WriteString(l_currsection, sKey, sInput); + + Change2daForm.Caption := 'Change line in file ' + l_currsection + ' [' + sInput + ']'; + Change2daForm.box_ini := ini; + Change2daForm.box_2daname := l_currsection; + Change2daForm.box_type := 'ChangeRow'; + Change2daForm.box_section := sInput; + + Change2daForm.boxIdentifier.Caption := ' Set line to modify '; + Change2daForm.boxChangeValue.Caption := ' Change column value '; + Change2daForm.lblHeadline.Caption := 'Modify 2DA line'; + Change2daForm.lblGridHeading.caption := 'Modified column values for line:'; + + // Ladda relevant info i fönstret, visa det och refresha sen listan... + // Load() funktionen anropas inte förren den modala dialogen stängts... + Change2daForm.LoadIniData(); + Change2daForm.ShowModal(); + Load2DAEntries(l_currsection); + + // Försök välja tillagd rad i Griden... + for i := 0 to (grid2daMod.rowcount - 1) do begin + if (grid2daMod.cells[0, i] = sKey) then begin + grid2daMod.row := i; + break; + end; + end; + end; +end; + +procedure TMainForm.btn2daCopyClick(Sender: TObject); +var + sInput : string; + sKey : string; + i, n : integer; + bFound : boolean; +begin + NewLabelForm.edModLabel.text := ''; + NewLabelForm.lblTitle.caption := 'Add new 2DA Modifier'; + NewLabelForm.Caption := 'Specify modifier label'; + + if (NewLabelForm.ShowModal = mrOk) then begin + sInput := NewLabelForm.edModLabel.text; + + if (ini.SectionExists(sInput)) then begin + ShowAlertBox('The specified modifier label already exists for another modifier. Try again.'); + exit; + end; + + // No valid value was entered, abort.... + if (sInput = 'N/A') or (sInput = '') or (Pos(' ', sInput) > 0) then begin + ShowAlertBox('Invalid modifier label specified. It must be at least 1 character and may contain no spaces. Try again.'); + exit; + end; + + // Find the first unused CopyRow# key to use.... + sKey := 'CopyRow0'; + i := 0; + bFound := True; + while bFound do begin + bFound := False; + for n := 0 to (grid2daMod.rowcount-1) do begin + if (lowercase(grid2daMod.cells[0, n]) = lowercase(sKey)) then begin + bFound := True; + break; + end; + end; + + if not bFound then begin + sKey := 'CopyRow' + IntToStr(i); + break; + end + else begin + sKey := 'CopyRow' + IntToStr(i+1); + inc(i); + end; + end; + + if (sKey = '') then begin + ShowAlertBox('ERROR! Failed to generate a modifier key!'); + exit; + end; + + ini.WriteString(l_currsection, sKey, sInput); + + Copy2daForm.Caption := 'Copy line in file ' + l_currsection + ' [' + sInput + ']'; + Copy2daForm.box_ini := ini; + Copy2daForm.box_2daname := l_currsection; + Copy2daForm.box_type := 'CopyRow'; + Copy2daForm.box_section := sInput; + + Copy2daForm.boxIdentifier.Caption := ' Set existing line to copy '; + Copy2daForm.boxChangeValue.Caption := ' Change column value '; + Copy2daForm.lblHeadline.Caption := 'Copy 2DA line'; + Copy2daForm.lblGridHeading.caption := 'New column values for copied line:'; + + Copy2daForm.LoadIniData(); + Copy2daForm.ShowModal(); + Load2DAEntries(l_currsection); + + for i := 0 to (grid2daMod.rowcount - 1) do begin + if (grid2daMod.cells[0, i] = sKey) then begin + grid2daMod.row := i; + break; + end; + end; + end; +end; + +procedure TMainForm.btn2daNewColClick(Sender: TObject); +var + sInput : string; + sKey : string; + i, n : integer; + bFound : boolean; +begin + NewLabelForm.edModLabel.text := ''; + NewLabelForm.lblTitle.caption := 'Add new 2DA Modifier'; + NewLabelForm.Caption := 'Specify modifier label'; + + if (NewLabelForm.ShowModal = mrOk) then begin + sInput := NewLabelForm.edModLabel.text; + + if (ini.SectionExists(sInput)) then begin + ShowAlertBox('The specified modifier label already exists for another modifier. Try again.'); + exit; + end; + + // No valid value was entered, abort.... + if (sInput = 'N/A') or (sInput = '') or (Pos(' ', sInput) > 0) then begin + ShowAlertBox('Invalid modifier label specified. It must be at least 1 character and may contain no spaces. Try again.'); + exit; + end; + + // Find the first unused AddColumn# key to use.... + sKey := 'AddColumn0'; + bFound := True; + i := 0; + while bFound do begin + bFound := False; + for n := 0 to (grid2daMod.rowcount-1) do begin + if (lowercase(grid2daMod.cells[0, n]) = lowercase(sKey)) then begin + bFound := True; + break; + end; + end; + + if not bFound then begin + sKey := 'AddColumn' + IntToStr(i); + break; + end + else begin + sKey := 'AddColumn' + IntToStr(i+1); + inc(i); + end; + end; + + if (sKey = '') then begin + ShowAlertBox('ERROR! Failed to generate a modifier key!'); + exit; + end; + + ini.WriteString(l_currsection, sKey, sInput); + + Add2daColForm.Caption := 'Add column in file ' + l_currsection + ' [' + sInput + ']'; + Add2daColForm.box_ini := ini; + Add2daColForm.box_2daname := l_currsection; + Add2daColForm.box_type := 'AddColumn'; + Add2daColForm.box_section := sInput; + + Add2daColForm.LoadIniData(); + Add2daColForm.ShowModal(); + Load2DAEntries(l_currsection); + + for i := 0 to (grid2daMod.rowcount - 1) do begin + if (grid2daMod.cells[0, i] = sKey) then begin + grid2daMod.row := i; + break; + end; + end; + end; +end; + +procedure TMainForm.btnLoadTokensClick(Sender: TObject); +var + oList : TStringList; + oMods : TStringList; + oCols : TStringList; + i : integer; + n : integer; + j : integer; + sKey : string; + sVal : string; + sFile : string; + sMod : string; + sLabel : string; +begin + // Rensa alla entries i dropdown-boxen, men bevara det som användaren ev. + // har knappat in själv. + sVal := cbGffValue.text; + cbGffValue.clear(); + cbGffValue.text := sVal; + + oList := TStringList.Create(); + ini.readsection('TLKList', oList); + + // Ladda in alla angivna StrRef-tokens i dropdownlistan. + for i := 0 to (oList.Count - 1) do begin + sKey := oList.Strings[i]; + sVal := ini.readstring('TLKList', sKey, ''); + + if (sKey <> '') and (sVal <> '') then begin + if (copy(sKey, 1, 6) = 'StrRef') then begin + cbGffValue.Items.Add(sKey); + end; + end; + end; + + oList.free(); + oList := TStringList.Create(); + ini.readsection('2DAList', oList); + + // Kolla efter alla 2DAMEMORY-tokens som fått värde tilldelade till sig + // så här långt i filen. + for i := 0 to (oList.Count - 1) do begin + sFile := ini.readstring('2DAList', oList.Strings[i], ''); + if (sFile <> '') then begin + oMods := TStringList.Create(); + try + // Check all modifiers for this particular file... + ini.readsection(sFile, oMods); + for n := 0 to (oMods.count - 1) do begin + sMod := oMods.Strings[n]; + sLabel := ini.readstring(sFile, sMod, ''); + + if (sLabel <> '') then begin + oCols := TStringList.Create(); + try + // Check each column label for this Modifier... + ini.ReadSection(sLabel, oCols); + for j := 0 to (oCols.count-1) do begin + sKey := oCols.strings[j]; + sVal := ini.readstring(sLabel, sKey, ''); + + // Check if the KEY is a unique 2DAMEMORY token + // instead of a column label. If so add to list. + if (sKey <> '') + and (copy(sKey, 1, 9) = '2DAMEMORY') + and (cbGffValue.Items.IndexOf(sKey) = -1) + then begin + cbGffValue.Items.Add(sKey); + end; + end; + finally + oCols.free(); + end; + end; + end; + finally + oMods.free(); + end; + end; + end; + + // ShowInfoBox('Relevant tokens have been loaded into the dropdown list of the Value box.'); + cbGffValue.setfocus; +end; + +procedure TMainForm.btnGffDeleteClick(Sender: TObject); +var + sMod : string; + sType : string; +begin + sType := gridGffMod.cells[0, gridGffMod.row]; + sMod := gridGffMod.cells[1, gridGffMod.row]; + + if (sType = '') then + exit; + + // ADDED(2006-07-25) Don't allow deleting new fields here... + if (lowercase(copy(sType, 1, 8)) = 'addfield') then begin + ShowAlertBox('New fields cannot be deleted here. Please use the "Manage new GFF fields" button to delete modifiers for adding new fields to a GFF file.'); + exit; + end; + + if (ShowConfirmBox('Are you sure you wish to delete the changes to the field "' + sType + '" for the gff file "' + l_currsection + '"?') = mrYes) then begin + // Delete the key from the INI file... + ini.DeleteKey(l_currsection, sType); + + // Refresh the grid... + LoadGFFEntries(l_currsection); + end; +end; + +procedure TMainForm.btnEditFieldClick(Sender: TObject); +begin + // ADDED(2006-07-25) Added check to disallow editing AddField modifiers directly here. + if (lowercase(copy(gridGffMod.cells[0, gridGffMod.row], 1, 8)) = 'addfield') then begin + // Set it to start out at the top section for the GFF file in question. + FormNewGFF.ParentSection := l_currsection; + FormNewGFF.IsSubField := false; + FormNewGFF.IsParentList := false; + FormNewGFF.IniFile := ini; + + // Open new window. It should do all necessary operations locally. + FormNewGFF.ShowModal(); + end + else begin + edGffField.text := gridGffMod.cells[0, gridGffMod.row]; + cbGffValue.text := gridGffMod.cells[1, gridGffMod.row]; + end; +end; + + +procedure TMainForm.btnStoreFieldClick(Sender: TObject); +var + i : integer; + bFound : boolean; + sKey : string; + sVal : string; +begin + sKey := edGffField.text; + sVal := cbGffValue.text; + bFound := False; + + if (sKey = '') or (sVal = '') then + exit; + + // ADDED(2006-07-25) Added foolproofing, don't allow modifying fields with + // an "AddField" label... + if (lowercase(copy(sKey, 1, 8)) = 'addfield') then begin + ShowAlertBox('"AddField" is a reserved keyword and cannot be used as a field label. If there actually is a field with that label you need to modify please let me know so I can change it.'); + exit; + end; + + for i := 0 to (gridGffMod.rowcount-1) do begin + if (sKey = gridGffMod.cells[0, i]) then begin + gridGffMod.cells[1, i] := sVal; + gridGffMod.row := i; + ini.WriteString(l_currsection, sKey, sVal); + edGffField.text := ''; + cbGffValue.text := ''; + edGffField.setfocus(); + bFound := True; + break; + end; + end; + + if not bFound then begin + i := gridGffMod.rowcount; + gridGffMod.rowcount := i + 1; + + if (gridGffMod.cells[0, i-1] = '') and (gridGffMod.cells[0, i-1] = '') then begin + i := i - 1; + gridGffMod.rowcount := gridGffMod.rowcount - 1; + end; + + gridGffMod.cells[0, i] := sKey; + gridGffMod.cells[1, i] := sVal; + gridGffMod.row := i; + + ini.WriteString(l_currsection, sKey, sVal); + + edGffField.text := ''; + cbGffValue.text := ''; + edGffField.setfocus(); + end; +end; + +procedure TMainForm.btnInfoClick(Sender: TObject); +begin + if not InfoForm.Visible then begin + InfoForm.txtInfo.visible := True; + InfoForm.txtInstallInfo.visible := False; + InfoForm.txtScriptInfo.visible := False; + InfoForm.txtGFFInfo.visible := False; + InfoForm.Show(); + end + else if not InfoForm.txtInfo.visible then begin + InfoForm.txtInfo.visible := True; + InfoForm.txtInstallInfo.visible := False; + InfoForm.txtScriptInfo.visible := False; + InfoForm.txtGFFInfo.visible := False; + end + else if InfoForm.Visible and InfoForm.txtInfo.visible then begin + InfoForm.Hide(); + InfoForm.txtInfo.visible := False; + end; +end; + +procedure TMainForm.Add2dafile1Click(Sender: TObject); +var + oList : TStringList; + oNode : TTreeNode; + sFile : string; + sKey : string; + i, n : integer; + bFound : boolean; +begin + // 1. Input box to get name of new file.... + // 2. Verify that 2DAList-KEY and SECTION of that name not already exists + // 3. Add KEY to 2DAList.... + // 4. Set l_currsection to input value... + // 5. Show & Load the 2DA Panel.... + + FormFilename.Filetype := '2DA'; + if (FormFilename.ShowModal() <> mrOk) then + exit; + + sFile := FormFilename.Filename; + + if (sFile = 'N/A') or (sFile = '') then + exit; + + if (ini.SectionExists(sFile)) then begin + ShowAlertBox('A file with this name is already used in this changes.ini file!'); + exit; + end; + + oList := TStringList.Create(); + ini.ReadSection('2DAList', oList); + + for i := 0 to (oList.count - 1) do begin + if (oList.strings[i] = sFile) then begin + ShowAlertBox('A file with this name is already used in this changes.ini file!'); + exit; + end; + end; + + n := 0; + sKey := ''; + bFound := True; + while bFound do begin + bFound := False; + sKey := 'Table' + IntToStr(n); + inc(n); + + for i := 0 to (oList.count - 1) do begin + if (oList.strings[i] = sKey) then begin + bFound := True; + break; + end; + end; + end; + + if (sKey = '') then begin + ShowAlertBox('Error! Unable to find next free Table key!'); + exit; + end; + + ini.writestring('2DAList', sKey, sFile); + l_currsection := sFile; + + oNode := tree.items.addchild(node2DA, sFile); + oNode.ImageIndex := 7; + oNode.SelectedIndex := 7; + oNode.Selected := True; + + oList.free(); +end; + +procedure TMainForm.AddGFFFile1Click(Sender: TObject); +var + oList : TStringList; + oNode : TTreeNode; + sFile : string; + sKey : string; + i, n : integer; + bFound : boolean; +begin + FormFilename.Filetype := 'GFF'; + if (FormFilename.ShowModal() <> mrOk) then + exit; + + sFile := FormFilename.Filename; + + if (sFile = 'N/A') or (sFile = '') then + exit; + + if (ini.SectionExists(sFile)) then begin + ShowAlertBox('A file with this name is already used in this changes.ini file!'); + exit; + end; + + oList := TStringList.Create(); + ini.ReadSection('GFFList', oList); + + for i := 0 to (oList.count - 1) do begin + if (oList.strings[i] = sFile) then begin + ShowAlertBox('A file with this name is already used in this changes.ini file!'); + exit; + end; + end; + + n := 0; + sKey := ''; + bFound := True; + while bFound do begin + bFound := False; + sKey := 'File' + IntToStr(n); + inc(n); + + for i := 0 to (oList.count - 1) do begin + if (oList.strings[i] = sKey) then begin + bFound := True; + break; + end; + end; + end; + + if (sKey = '') then begin + ShowAlertBox('Error! Unable to find next free File key!'); + exit; + end; + + ini.writestring('GFFList', sKey, sFile); + l_currsection := sFile; + + oNode := tree.items.addchild(nodeGFF, sFile); + oNode.ImageIndex := 7; + oNode.SelectedIndex := 7; + oNode.Selected := True; + + oList.free(); +end; + +procedure TMainForm.Delete2dafile1Click(Sender: TObject); +var + sFile : string; + sMod : string; + sKey : string; + sVal : string; + oList : TStringList; + oNode : TTreeNode; + i : integer; +begin + oNode := tree.Selected; + sFile := oNode.text; + + if (oNode = nil) or (oNode.parent <> node2DA) then begin + ShowAlertBox('No 2da file has been selected in the list to be deleted'); + exit; + end; + + if (sFile = '') then + exit; + + // Allow user chance to abort before deleting things... + if (ShowConfirmBox('Are you sure you wish to remove ' + sFile + ' from the modifier list?') <> mrYes) then + exit; + + tree.items.delete(oNode); + + if (oNode <> nil) then + oNode.delete(); + + // Delete key from the 2DAList + oList := TStringList.Create(); + ini.ReadSection('2DAList', oList); + for i := 0 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sVal := ini.readstring('2DAList', sKey, ''); + + if (sVal = sFile) then begin + ini.deletekey('2DAList', sKey); + end; + end; + + oList.free(); + + // Delete sections belonging to this file... + if ini.SectionExists(sFile) then begin + oList := TStringList.Create(); + ini.ReadSection(sFile, oList); + + for i := 0 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sMod := ini.ReadString(sFile, sKey, ''); + + if (sMod <> '') and ini.SectionExists(sMod) then begin + ini.EraseSection(sMod); + end; + end; + + ini.EraseSection(sFile); + oList.free(); + end; +end; + +procedure TMainForm.DeleteGFFfile1Click(Sender: TObject); +var + sFile : string; + sKey : string; + sVal : string; + oList : TStringList; + oNode : TTreeNode; + i : integer; +begin + oNode := tree.Selected; + sFile := oNode.text; + + if (oNode = nil) or (oNode.parent <> nodeGFF) then begin + ShowAlertBox('No GFF format file has been selected in the list to be deleted'); + exit; + end; + + if (sFile = '') then + exit; + + // Allow user chance to abort before deleting things... + if (ShowConfirmBox('Are you sure you wish to remove ' + sFile + ' from the modifier list?') <> mrYes) then + exit; + + tree.items.delete(oNode); + + if (oNode <> nil) then + oNode.delete(); + + // Delete key from the GFFList + oList := TStringList.Create(); + ini.ReadSection('GFFList', oList); + for i := 0 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sVal := ini.readstring('GFFList', sKey, ''); + + if (sVal = sFile) then begin + ini.deletekey('GFFList', sKey); + end; + end; + + oList.free(); + + // Delete sections belonging to this file... + if ini.SectionExists(sFile) then begin + ini.EraseSection(sFile); + end; +end; + +procedure TMainForm.BtnInstallInfoClick(Sender: TObject); +begin + if not InfoForm.Visible then begin + InfoForm.txtInfo.visible := False; + InfoForm.txtInstallInfo.visible := True; + InfoForm.txtScriptInfo.visible := False; + InfoForm.txtGFFInfo.visible := False; + InfoForm.Show(); + end + else if not InfoForm.txtInstallInfo.visible then begin + InfoForm.txtInfo.visible := False; + InfoForm.txtInstallInfo.visible := True; + InfoForm.txtScriptInfo.visible := False; + InfoForm.txtGFFInfo.visible := False; + end + else if InfoForm.Visible and InfoForm.txtInstallInfo.Visible then begin + InfoForm.Hide(); + InfoForm.txtInstallInfo.visible := False; + end; +end; + +procedure TMainForm.btnInstallDeleteClick(Sender: TObject); +var + oList : TStringList; + oFiles : TStringList; + sFolder : string; + sFile : string; + sLabel : string; + sIniFld : string; + sIniFile : string; + sKey : string; + bReplace : boolean; + i, n : integer; +begin + sFolder := gridInstall.cells[0, gridInstall.Selection.top]; + sFile := gridInstall.cells[1, gridInstall.Selection.top]; + bReplace := (gridInstall.cells[2, gridInstall.Selection.top][1] = 'R'); + + if (sFolder = '') or (sFile = '') then + exit; + + if (ShowConfirmBox('Are you sure you wish to delete the entry for the file "' + sFile + '" in the folder "' + sFolder + '"?') = mrYes) then begin + // Need to look up what section the File is stored in. + // sFolder corresponds to Value in the [InstallList] while sFile is a value in + // [installlist.key] section. + oList := TStringList.Create(); + try + ini.ReadSection('InstallList', oList); + for i := 0 to (oList.count - 1) do begin + sLabel := oList.strings[i]; + sIniFld := ini.ReadString('InstallList', sLabel, ''); + + if (sLabel <> '') + and (sIniFld <> '') + and (lowercase(sIniFld) = lowercase(sFolder)) + then begin + oFiles := TStringList.Create(); + try + ini.ReadSection(sLabel, oFiles); + for n := 0 to (oFiles.count - 1) do begin + sKey := oFiles.strings[n]; + sIniFile := ini.ReadString(sLabel, sKey, ''); + // ADDED(2005-06-10) Added Replace flag to the check... + if (sIniFile <> '') + and (lowercase(sIniFile) = lowercase(sFile)) + and ((lowercase(copy(sKey, 1, 7)) = 'replace') = bReplace) + then begin + ini.deletekey(sLabel, oFiles.strings[n]); + // Om bara denna filen fanns för denna mapp, ta bort sektionen. + if (oFiles.count = 1) then begin + ini.deletekey('InstallList', sLabel); + ini.EraseSection(sLabel); + end; + + break; + end; + end; + finally + oFiles.free(); + end; + break; + end; + end; + finally + oList.free(); + end; + + // Re-draw the list... + LoadInstallEntries(); + end; +end; + +procedure TMainForm.btnEditInstallClick(Sender: TObject); +begin + edFolderName.text := gridInstall.Cells[0, gridInstall.Selection.Top]; + edFileName.text := gridInstall.cells[1, gridInstall.Selection.Top]; + // ADDED(2005-06-10) Fill in the Replace checkbox.... + cbFileReplace.Checked := (gridInstall.cells[2, gridInstall.Selection.Top][1] = 'R'); +end; + +procedure TMainForm.btnAddInstallClick(Sender: TObject); +var + oFolders : TStringList; + oFiles : TStringList; + sInFile : string; + sInFolder : string; + sFolder : string; + sSection : string; + sKey : string; + sFound : string; + sReplace : string; + sRepLong : string; + sCompare : string; + bFound : boolean; + bReplace : boolean; + i, n : integer; +begin + sInFolder := edFolderName.text; + sInFile := edFileName.text; + bReplace := cbFileReplace.checked; + + // ADDED(2005-06-10) FUgly hack, but... whatever. + if bReplace then begin + sReplace := 'R'; + sRepLong := 'Rep.'; + end + else begin + sReplace := 'C'; + sRepLong := 'Copy'; + end; + + if (sInFolder = '') or (sInFile = '') then + exit; + + // If this entry already exists, don't do anything. + // FIXED(2005-06-10) Added check for the REPLACE flag. + for i := 0 to (gridInstall.RowCount - 1) do begin + if (gridInstall.cells[0, i] = sInFolder) + and (gridInstall.cells[1, i] = sInFile) + and (sRepLong = gridInstall.cells[2, i][1]) + then begin + exit; + end; + end; + + // Check if a section for this folder already exists... + sFound := ''; + oFolders := TStringList.Create(); + try + ini.ReadSection('InstallList', oFolders); + for i := 0 to (oFolders.count - 1) do begin + sSection := oFolders.strings[i]; + sFolder := ini.ReadString('InstallList', sSection, ''); + if (sFolder = sInFolder) then begin + sFound := sSection; + break; + end; + + end; + finally + oFolders.free(); + end; + + // Section didn't exist, add it to the InstallList... + if (sFound = '') then begin + n := 0; + bFound := True; + oFolders := TStringList.Create(); + ini.ReadSection('InstallList', oFolders); + while bFound do begin + bFound := False; + sFound := 'install_folder' + IntToStr(n); + inc(n); + + for i := 0 to (oFolders.count - 1) do begin + if (oFolders.strings[i] = sFound) then begin + bFound := True; + break; + end; + + end; + end; + oFolders.free(); + + ini.WriteString('InstallList', sFound, sInFolder); + end; + + // Add file to the Section... + if (sFound <> '') then begin + n := 0; + sKey := ''; + bFound := True; + oFiles := TStringList.Create(); + while bFound do begin + bFound := False; + // ADDED(2005-06-10) Different Keyword if Replace is set... + if bReplace then + sKey := 'Replace' + IntToStr(n) + else + sKey := 'File' + IntToStr(n); + inc(n); + + ini.ReadSection(sFound, oFiles); + for i := 0 to (oFiles.count - 1) do begin + if (oFiles.strings[i] = sKey) then begin + bFound := True; + break; + end; + end; + end; + + oFiles.free(); + + // ADDED(2005-06-10) Delete existing file entry in this folder. + oFiles := TStringList.Create(); + ini.ReadSection(sFound, oFiles); + for i := 0 to (oFiles.count - 1) do begin + sCompare := ini.ReadString(sFound, oFiles.strings[i], ''); + if (sCompare = sInFile) then begin + ini.DeleteKey(sFound, oFiles.strings[i]); + break; + end; + end; + + // Write the new/modified file entry to the folder section. + ini.WriteString(sFound, sKey, sInFile); + oFiles.free(); + end; + + // Update the list... + LoadInstallEntries(); + + // Select the just added/modified entry... + for i := 0 to (gridInstall.rowcount - 1) do begin + if (gridInstall.cells[0, i] = sInFolder) + and (gridInstall.cells[1, i] = sInFile) + and (gridInstall.cells[2, i] = sRepLong) + then begin + gridInstall.row := i; + break; + end; + end; + + // edFolderName.clear(); + edFileName.clear(); + edFileName.SetFocus; +end; + + +procedure TMainForm.edFolderNameKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (paneInstall.visible = True) then begin + if (Shift = [ssCtrl, ssShift]) and (key = VK_UP) then begin + btnAddInstallClick(btnAddInstall); + end; + end; +end; + +procedure TMainForm.gridInstallKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (paneInstall.visible = True) then begin + if (Shift = [ssCtrl, ssShift]) and (key = VK_DOWN) then begin + btnEditInstallClick(btnEditInstall); + end; + end; +end; + +procedure TMainForm.edFileNameKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (paneInstall.visible = True) then begin + if (Shift = [ssCtrl, ssShift]) and (key = VK_UP) then begin + btnAddInstallClick(btnAddInstall); + end; + end; +end; + +procedure TMainForm.edGffFieldKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (Shift = [ssCtrl, ssShift]) and (key = VK_UP) then begin + btnStoreFieldClick(btnStoreField); + end; +end; + +procedure TMainForm.cbGffValueKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (Shift = [ssCtrl, ssShift]) and (key = VK_UP) then begin + btnStoreFieldClick(btnStoreField); + end; +end; + +procedure TMainForm.gridGffModKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (Shift = [ssCtrl, ssShift]) and (key = VK_DOWN) then begin + btnEditFieldClick(btnEditField); + end; +end; + +procedure TMainForm.btnGetFileNameClick(Sender: TObject); +begin + OpenBox.Filter := 'All files (*.*)|*.*'; + OpenBox.DefaultExt := ''; + OpenBox.FileName := ''; + OpenBox.Title := 'Select a file to get the name of...'; + OpenBox.Options := [ofHideReadOnly,ofPathMustExist,ofFileMustExist,ofEnableSizing]; + + if OpenBox.Execute() then begin + edFileName.clear(); + edFileName.text := ExtractFileName(OpenBox.FileName); + end; + + edFileName.setfocus; +end; + +procedure TMainForm.gridTlkClick(Sender: TObject); +begin + txtEntry.clear(); + txtEntry.lines.Text := gridTlk.cells[0, gridTlk.row] + ':' + #10 + gridTlk.cells[1, gridTlk.row]; +end; + +procedure TMainForm.gridTlkKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (Shift = [ssCtrl, ssShift]) and (key = VK_LEFT) then begin + btnAddStrRefClick(btnAddStrRef); + end; +end; + + +// ----------------------------------------------------------------------------- +// +// ----------------------------------------------------------------------------- +function TMainForm.GetUniqueModLabel(sLabel : string) : string; +var + oList : TStringList; + sVal : string; + bFound : boolean; + i, n : integer; +begin + result := sLabel; + oList := TStringList.Create(); + try + // Find the first unused AddColumn# key to use.... + sVal := sLabel + '_0'; + bFound := True; + i := 0; + oList.clear(); + ini.ReadSection(l_currsection, oList); + while bFound do begin + bFound := False; + for n := 0 to (oList.count-1) do begin + if (lowercase(ini.ReadString(l_currsection, oList[n], '')) = lowercase(sVal)) then begin + bFound := True; + break; + end; + end; + + if not bFound then begin + sVal := sLabel + '_' + IntToStr(i); + result := sVal; + break; + end + else begin + inc(i); + sVal := sLabel + '_' + IntToStr(i); + end; + end; + finally + oList.free(); + end; +end; + +// ----------------------------------------------------------------------------- +// Compare two 2DA files (of the same type!) and fabricate modifiers from the +// differences found. +// +// RAD programming at its best, placing the logic straight in a button's +// OnClicked event. But whatever, it works for now, it's not like the rest of +// this app is a textbook example of software design at its best... +// +// KNOWN SHORTCOMINGS: Won't detect changes in RowLabel for existing lines, +// since in the current version of TSLPatcher the ChangeRow +// modifier can't mess with the RowLabel anyway? +// +// Won't detect deleted lines, since the TSLPatcher is +// unable to delete lines from a 2DA anyway. +// ----------------------------------------------------------------------------- +procedure TMainForm.btn2daCompareClick(Sender: TObject); +var + i, n : integer; + iCol : integer; + iRow : integer; + iLastRow : integer; + iMaxRow : integer; // ADDED(2005-08-30) + iMaxCol : integer; // ADDED(2005-08-30) + + iCntRow : integer; // ADDED(2006-03-25) + iCntMod : integer; // ADDED(2006-03-25) + iCntCol : integer; // ADDED(2006-03-25) + + sFile1 : string; + sFile2 : string; + sKey : string; + sLabel : string; + s2daname : string; + + bFound : boolean; + bHasLabel : boolean; + + oTable1 : T2DAHandler; + oTable2 : T2DAHandler; + oList : TStringList; + + oOldCursor : TCursor; +begin + // Get first file to open..... + OpenBox.Filter := '2DA file (*.2da)|*.2da'; + OpenBox.DefaultExt := '2da'; + OpenBox.FileName := l_currsection; + OpenBox.Title := 'Select ORIGINAL 2DA file to compare against:'; + OpenBox.Options := [ofHideReadOnly,ofPathMustExist,ofFileMustExist,ofEnableSizing]; + if (not OpenBox.Execute) then + exit; + + sFile1 := OpenBox.FileName; + + // Get second file to open... this should contain the changes. + OpenBox.Filter := '2DA file (*.2da)|*.2da'; + OpenBox.DefaultExt := '2da'; + OpenBox.FileName := l_currsection; + OpenBox.Title := 'Select MODIFIED 2DA file to compare against original.'; + OpenBox.Options := [ofHideReadOnly,ofPathMustExist,ofFileMustExist,ofEnableSizing]; + if (not OpenBox.Execute) then + exit; + + sFile2 := OpenBox.FileName; + + // Load the opened files... + oList := TStringList.Create(); + oTable1 := T2DAHandler.Create(); + oTable2 := T2DAHandler.Create(); + + // Reset the counter variables... + iCntRow := 0; + iCntMod := 0; + iCntCol := 0; + + // Show "Busy" cursor and dim the GUI... + oOldCursor := Screen.Cursor; + Screen.Cursor := crHourglass; + tree.enabled := False; + pane2da.enabled := False; + File1.enabled := False; + Actions1.enabled := False; + Application.ProcessMessages(); + + try // finally: Free the 2da table objects. + oTable1.Load2daFile(sFile1); + oTable2.Load2daFile(sFile2); + + // Abort if either file could not be properly loaded... + if (not oTable1.isloaded) then begin + ShowAlertBox('Unable to load the first selected 2da file! Aborting...'); + exit; + end; + + if (not oTable2.isloaded) then begin + ShowAlertBox('Unable to load the second selected 2da file! Aborting...'); + exit; + end; + + // CHANGED(2006-03-25) Use 2DA file name as part of the modifier label to + // make them more traceable in the INI file... + s2daname := lowercase(l_currsection); + s2daname := copy(s2daname, 1, Pos('.2da', s2daname)-1); + if (Length(s2daname) <= 0) then + s2daname := 'c_'; + + // Check if the modified table has new columns + if (oTable1.colcount < oTable2.colcount) then begin + for iCol := oTable1.colcount to (oTable2.colcount-1) do begin + // FIX(2005-08-30) Use ColLabel rather than number in modifier label. + sLabel := GetUniqueModLabel(s2daname + '_col_' + oTable2.clabels[iCol]); + + // Find the first unused AddColumn# key to use.... + sKey := 'AddColumn0'; + bFound := True; + i := 0; + oList.clear(); + ini.ReadSection(l_currsection, oList); + while bFound do begin + bFound := False; + for n := 0 to (oList.count-1) do begin + if (lowercase(oList[n]) = lowercase(sKey)) then begin + bFound := True; + break; + end; + end; + + if not bFound then begin + sKey := 'AddColumn' + IntToStr(i); + break; + end + else begin + sKey := 'AddColumn' + IntToStr(i+1); + inc(i); + end; + end; + + if (sKey = '') then begin + ShowAlertBox('ERROR! Failed to generate a modifier key!'); + exit; + end; + + // Add the AddColumn modifier to the [2daname] section... + ini.WriteString(l_currsection, sKey, sLabel); + + // Write column label of the new column + ini.WriteString(sLabel, 'ColumnLabel', oTable2.clabels[iCol]); + + // Write dummy default value since we don't know what would be suitable, + // and it won't really be used here since we need to set ALL rows. + ini.WriteString(sLabel, 'DefaultValue', '****'); + + // Write the values for all rows for this new column! + for iRow := 0 to (oTable2.rowcount-1) do begin + ini.WriteString(sLabel, 'I'+ IntToStr(iRow), oTable2.entry[iRow, iCol]); + end; + + // ADDED(2006-03-25) Count number of new columns. + inc(iCntCol); + + end; + end; + + Application.ProcessMessages(); + + // DONE: Adding new columns from TABLE2 + // NEXT: Check for differences on lines existing in both tables. + + // ADDED(2005-08-30): Check the number of rows in each file and warn if the + // ORIGINAL contains more than the MODIFIED file does. + if (oTable1.rowcount > oTable2.rowcount) then begin + iMaxRow := oTable2.rowcount; + ShowAlertBox('Original file contained more rows than the modified file. The extra rows will be skipped.'); + end + else begin + iMaxRow := oTable1.rowcount; + end; + + // ADDED(2005-08-30): Check the number of columns in each file and warn if the + // ORIGINAL contains more than the MODIFIED file does. + if (oTable1.colcount > oTable2.colcount) then begin + iMaxCol := oTable2.colcount; + ShowAlertBox('Original file contained more columns than the modified file. The extra columns will be skipped.'); + end + else begin + iMaxCol := oTable1.colcount; + end; + + // Check all rows shared by both files for non-matching values. + iLastRow := -1; + for iRow := 0 to (iMaxRow-1) do begin + for iCol := 0 to (iMaxCol-1) do begin + // If a cell in both tables don't contain matching values we have a difference... + if (oTable1.entry[iRow, iCol] <> oTable2.entry[iRow, iCol]) then begin + if (iRow <> iLastRow) then begin + iLastRow := iRow; + // ADDED(2005-08-30) ----------------------------------- + bHasLabel := False; + for i := 0 to (oTable2.colcount - 1) do begin + if (oTable2.clabels[i] = 'label') then begin + bHasLabel := True; + break; + end; + end; + + if bHasLabel then begin + sLabel := GetUniqueModLabel(s2daname + '_mod_' + Labelize(oTable2.entry[iRow, oTable2.GetColByLabel('label')])); + end + else begin + sLabel := GetUniqueModLabel(s2daname + '_mod_' + IntToStr(iRow)); + end; + // ----------------------------------------------------- + + // Hitta första lediga ChangeRow# nyckeln att använda... + sKey := 'ChangeRow0'; + i := 0; + bFound := True; + oList.clear(); + ini.ReadSection(l_currsection, oList); + while bFound do begin + bFound := False; + for n := 0 to (oList.count-1) do begin + if (lowercase(oList[n]) = lowercase(sKey)) then begin + bFound := True; + break; + end; + end; + + if not bFound then begin + sKey := 'ChangeRow' + IntToStr(i); + break; + end + else begin + sKey := 'ChangeRow' + IntToStr(i+1); + inc(i); + end; + end; + + if (sKey = '') then begin + ShowAlertBox('ERROR! Failed to generate a modifier key!'); + exit; + end; + + // Add ChangeRow modifier to the [2daname] section with the proper label. + ini.WriteString(l_currsection, sKey, sLabel); + + // Write the RowIndex of the line to modify at the top of the modifier list. + ini.WriteString(sLabel, 'RowIndex', IntToStr(iRow)); + end; + + // Write the differing value in the second file for this column to the + // changerow modifier list. + ini.WriteString(sLabel, oTable2.clabels[iCol], oTable2.entry[iRow, iCol]); + + // ADDED(2006-03-25) Count changes made... + inc(iCntMod); + end; + end; + end; + + Application.ProcessMessages(); + + // DONE: Adding new columns from TABLE2, Changed modified values from TABLE2 + // NEXT: Check for new lines in TABLE2 and add them. + + // If TABLE2 contains more lines than TABLE1, add the extra lines. + if (oTable1.rowcount < oTable2.rowcount) then begin + for iRow := oTable1.rowcount to (oTable2.rowcount-1) do begin + // ADDED(2005-08-30) ----------------------------------- + bHasLabel := False; + for i := 0 to (oTable2.colcount - 1) do begin + if (oTable2.clabels[i] = 'label') then begin + bHasLabel := True; + break; + end; + end; + + if bHasLabel then begin + sLabel := GetUniqueModLabel(s2daname + '_row_' + Labelize(oTable2.entry[iRow, oTable2.GetColByLabel('label')])); + end + else begin + sLabel := GetUniqueModLabel(s2daname + '_row_' + IntToStr(iRow)); + end; + // ----------------------------------------------------- + + // Hitta första lediga AddRow# nyckel att använda... + bFound := True; + sKey := 'AddRow0'; + i := 0; + oList.clear(); + ini.ReadSection(l_currsection, oList); + while bFound do begin + bFound := False; + for n := 0 to (oList.count-1) do begin + if (lowercase(oList[n]) = lowercase(sKey)) then begin + bFound := True; + break; + end; + end; + + if not bFound then begin + sKey := 'AddRow' + IntToStr(i); + break; + end + else begin + sKey := 'AddRow' + IntToStr(i+1); + inc(i); + end; + end; + + if (sKey = '') then begin + ShowAlertBox('ERROR! Failed to generate a modifier key!'); + exit; + end; + + // Write the AddRow modifier to the [2daname] section with the corresponding label. + ini.WriteString(l_currsection, sKey, sLabel); + + // Write the values for all columns for the new row + for iCol := 0 to (oTable2.colcount-1) do begin + // FIX(2006-03-25) Don't write keys that holds the default value, since + // this is set automatically for columns with no value assigned for new rows. + if (oTable2.entry[iRow, iCol] <> '****') then + ini.WriteString(sLabel, oTable2.clabels[iCol], oTable2.entry[iRow, iCol]); + end; + + // ADDED(2006-03-25) Count new rows added... + inc(iCntRow); + + end; + end; + + Application.ProcessMessages(); + + // DONE: Adding new columns from TABLE2, Changed modified values from TABLE2, Added new rows found in TABLE2 + // NEXT: Done (hopefully)! Update the GUI stringgrid with new modifiers. + + ShowInfoBox(Format('Done comparing %s files, found %d new columns, %d new rows and %d modified cells. Building modifiers...', [l_currsection, iCntCol, iCntRow, iCntMod])); + Load2DAEntries(l_currsection); + finally + oList.free(); + oTable1.free(); + oTable2.free(); + Screen.Cursor := oOldCursor; + tree.enabled := True; + pane2da.enabled := True; + File1.enabled := True; + Actions1.enabled := True; + end; +end; + + +// ----------------------------------------------------------------------------- +// ADDED(2005-10-04) - Added new button for mass-adding files to a folder. +// ----------------------------------------------------------------------------- +procedure TMainForm.btnMassAddClick(Sender: TObject); +var + sFolder : string; + sFile : string; + sCompare : string; + sSection : string; + sFound : string; + sReplace : string; + sKey : string; + + iFile : integer; + i : integer; + n : integer; + + bFound : boolean; + bReplace : boolean; + + oFolders : TStringList; + oFiles : TStringList; +begin + // Get folder name and replace setting.... + if (MassAddForm.ShowModal <> mrOk) then + exit; + + sFolder := MassAddForm.Folder; + bReplace := MassAddForm.Replace; + + if bReplace then + sReplace := 'Rep.' + else + sReplace := 'Copy'; + + if (sFolder = '') then + exit; + + // Get the names of all the files... + OpenBox.Filter := 'All files (*.*)|*.*'; + OpenBox.DefaultExt := ''; + OpenBox.FileName := ''; + OpenBox.Title := 'Select the files to get the names of (use shift to select multiple)'; + OpenBox.Options := [ofHideReadOnly,ofAllowMultiSelect,ofPathMustExist,ofFileMustExist,ofEnableSizing]; + + if not OpenBox.Execute then + exit; + + if (OpenBox.Files.Count < 1) then + exit; + + // Check if a section for this folder already exists... + sFound := ''; + oFolders := TStringList.Create(); + try + ini.ReadSection('InstallList', oFolders); + for i := 0 to (oFolders.count - 1) do begin + sSection := oFolders.strings[i]; + sCompare := ini.ReadString('InstallList', sSection, ''); + if (sCompare = sFolder) then begin + sFound := sSection; + break; + end; + + end; + finally + oFolders.free(); + end; + + // Section didn't exist, add it to the InstallList... + if (sFound = '') then begin + n := 0; + bFound := True; + oFolders := TStringList.Create(); + try + ini.ReadSection('InstallList', oFolders); + while bFound do begin + bFound := False; + sFound := 'install_folder' + IntToStr(n); + inc(n); + + for i := 0 to (oFolders.count - 1) do begin + if (oFolders.strings[i] = sFound) then begin + bFound := True; + break; + end; + + end; + end; + finally + oFolders.free(); + end; + + // Add the Sectiom + ini.WriteString('InstallList', sFound, sFolder); + end; + + // Add file to the Section... + if (sFound <> '') then begin + for iFile := 0 to (OpenBox.Files.Count-1) do begin + sFile := ExtractFileName(OpenBox.Files[iFile]); + if (sFile = '') then + continue; + + n := 0; + sKey := ''; + bFound := True; + oFiles := TStringList.Create(); + // First find the next free KEY to use... + while bFound do begin + bFound := False; + // Different Keyword if Replace is set... + if bReplace then + sKey := 'Replace' + IntToStr(n) + else + sKey := 'File' + IntToStr(n); + inc(n); + + ini.ReadSection(sFound, oFiles); + for i := 0 to (oFiles.count - 1) do begin + if (oFiles.strings[i] = sKey) then begin + bFound := True; + break; + end; + end; + end; + + oFiles.free(); + + // Delete existing file entry in this folder, if any. + oFiles := TStringList.Create(); + ini.ReadSection(sFound, oFiles); + for i := 0 to (oFiles.count - 1) do begin + sCompare := ini.ReadString(sFound, oFiles.strings[i], ''); + if (sCompare = sFile) then begin + ini.DeleteKey(sFound, oFiles.strings[i]); + break; + end; + end; + + // Write the new/modified file entry to the folder section. + ini.WriteString(sFound, sKey, sFile); + oFiles.free(); + end; + end; + + // Update the list... + LoadInstallEntries(); + + // Select the just added/modified entry... + for i := 0 to (gridInstall.rowcount - 1) do begin + if (gridInstall.cells[0, i] = sFolder) + and (gridInstall.cells[1, i] = sFile) + and (gridInstall.cells[2, i] = sReplace) + then begin + gridInstall.row := i; + break; + end; + end; + +end; + +procedure TMainForm.btnNewFieldClick(Sender: TObject); +begin + // Set it to start out at the top section for the GFF file in question. + FormNewGFF.ParentSection := l_currsection; + FormNewGFF.IsSubField := false; + FormNewGFF.IsParentList := false; + FormNewGFF.IniFile := ini; + + // Open new window. It should do all necessary operations locally. + FormNewGFF.ShowModal(); +end; + +procedure TMainForm.btnGetScriptNameClick(Sender: TObject); +begin + OpenBox.Filter := 'NWScript Source files (*.nss)|*.nss'; + OpenBox.DefaultExt := 'nss'; + OpenBox.FileName := ''; + OpenBox.Title := 'Select a script source code file to get the name of...'; + OpenBox.Options := [ofHideReadOnly,ofPathMustExist,ofFileMustExist,ofEnableSizing]; + + if OpenBox.Execute() then begin + edScriptName.clear(); + edScriptName.text := ExtractFileName(OpenBox.FileName); + end; + + edScriptName.setfocus; +end; + +procedure TMainForm.btnEditScriptClick(Sender: TObject); +var + sReplace : string; + sDest : string; +begin + // Fill in the filename field... + edScriptName.text := gridScript.cells[1, gridScript.row]; + + // Set the replace checkbox to the correct state. + sReplace := gridScript.cells[0, gridScript.row]; + cbScriptReplace.Checked := (lowercase(copy(sReplace, 1, 7)) = 'replace'); + + // ADDED(2006-02-03) Read the new optional !Destination key, if present, + // and set correct value in the box. + sDest := gridScript.cells[1, gridScript.row]; + if ini.SectionExists(sDest) then begin + edScriptDest.text := ini.ReadString(sDest, '!Destination', 'override'); + end + else begin + edScriptDest.text := 'override'; + end; +end; + +procedure TMainForm.btnAddScriptClick(Sender: TObject); +var + oFiles : TStringList; + i, n : integer; + sScript : string; + sKey : string; + sVal : string; + bReplace : boolean; + bFound : boolean; +begin + sScript := edScriptName.text; + + oFiles := TStringList.Create(); + try + ini.ReadSection('CompileList', oFiles); + + // Check if the entry already exists... + for i := 0 to (oFiles.Count - 1) do begin + sKey := oFiles.strings[i]; + sVal := ini.ReadString('CompileList', sKey, ''); + if (sVal = sScript) then begin + bReplace := (lowercase(copy(sKey, 1, 7)) = 'replace'); + + if (cbScriptReplace.checked = bReplace) then begin + // Entry already exists! No point in doing anything else... + exit; + end + else begin + // Remove old entry. The new will be created below with the + // proper key. + ini.DeleteKey('CompileList', sKey); + end; + end; + end; + + // Find the lowest free Key of the specific type. + n := 0; + sKey := ''; + bFound := True; + bReplace := cbScriptReplace.checked; + + while bFound do begin + bFound := False; + + // Pick key depending on Skip/Replace setting... + if bReplace then + sKey := 'Replace' + IntToStr(n) + else + sKey := 'File' + IntToStr(n); + inc(n); + + for i := 0 to (oFiles.count - 1) do begin + if (lowercase(oFiles.strings[i]) = lowercase(sKey)) then begin + bFound := True; + break; + end; + end; + end; + + // If a new key has been set, add the field with that key... + if not bFound and (sKey <> '') then begin + ini.WriteString('CompileList', sKey, sScript); + end; + + // ADDED(2006-02-03) + // Don't add a !Destination key if the file should be put in Override... + if (edScriptDest.text = '') or (lowercase(edScriptDest.text) = 'override') then begin + ini.DeleteKey(sScript, '!Destination'); + if (ini.SectionExists(sScript)) then begin + oFiles.clear(); + ini.ReadSection(sScript, oFiles); + if (oFiles.count <= 0) then + ini.EraseSection(sScript); + end; + end + // Add a !Destination key if a destination ERF file has been set. + else begin + ini.WriteString(sScript, '!Destination', edScriptDest.text); + end; + // - - - - - - - + + // Refresh the grid to display new entry... + LoadScriptEntries(); + + // Select the just added/modified entry in the grid... + for i := 0 to (gridScript.rowcount - 1) do begin + if (gridScript.cells[0, i] = sKey) and (gridScript.cells[1, i] = sScript) then begin + gridScript.row := i; + break; + end; + end; + + edScriptname.clear(); + edScriptName.SetFocus(); + finally + oFiles.free(); + end; + +end; + +procedure TMainForm.btnDelScriptClick(Sender: TObject); +var + sKey : string; + sVal : string; +begin + sKey := gridScript.cells[0, gridScript.row]; + sVal := gridScript.cells[1, gridScript.row]; + + if (sKey = '') then + exit; + + if (ShowConfirmBox('Are you sure you wish to remove the file "' + sVal + '" from the list?') = mrYes) then begin + // Delete the key from the INI file... + ini.DeleteKey('CompileList', sKey); + + // Refresh the grid... + LoadScriptEntries(); + end; +end; + +procedure TMainForm.btnScriptInfoClick(Sender: TObject); +begin + if not InfoForm.Visible then begin + InfoForm.txtInfo.visible := False; + InfoForm.txtInstallInfo.visible := False; + InfoForm.txtScriptInfo.visible := True; + InfoForm.txtGFFInfo.visible := False; + InfoForm.Show(); + end + else if not InfoForm.txtInstallInfo.visible then begin + InfoForm.txtInfo.visible := False; + InfoForm.txtInstallInfo.visible := False; + InfoForm.txtGFFInfo.visible := False; + InfoForm.txtScriptInfo.visible := True; + end + else if InfoForm.Visible and InfoForm.txtInstallInfo.Visible then begin + InfoForm.Hide(); + InfoForm.txtScriptInfo.visible := False; + end; +end; + +procedure TMainForm.edScriptNameKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (paneScript.visible = True) then begin + if (Shift = [ssCtrl, ssShift]) and (key = VK_UP) then begin + btnAddScriptClick(btnAddScript); + end; + end; +end; + +procedure TMainForm.gridScriptKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (paneScript.visible = True) then begin + if (Shift = [ssCtrl, ssShift]) and (key = VK_DOWN) then begin + btnEditScriptClick(btnEditScript); + end; + end; +end; + +procedure TMainForm.gridInstallDblClick(Sender: TObject); +begin + btnEditInstallClick(btnEditInstall); +end; + +procedure TMainForm.gridScriptDblClick(Sender: TObject); +begin + btnEditScriptClick(btnEditScript); +end; + +procedure TMainForm.cbReplaceGffClick(Sender: TObject); +begin + ini.WriteBool(l_currsection, '!ReplaceFile', cbReplaceGff.checked); + + if (cbReplaceGff.checked) then + cbReplaceGff.Hint := 'If this file already exists in the override folder, overwrite it with a copy from tslpatchdata before modifying it.' + else + cbReplaceGff.Hint := 'If this file already exists in the override folder, apply the modifications to the existing file.'; +end; + +procedure TMainForm.btnGetDestNameClick(Sender: TObject); +begin + OpenBox.Filter := 'ERF files (*.erf)|*.erf|MOD files (*.mod)|*.mod|SAV files (*.sav)|*.sav|HAK files (*.hak)|*.hak'; + OpenBox.DefaultExt := 'erf'; + OpenBox.FileName := ''; + OpenBox.Title := 'Select an ERF format file to get the name of...'; + OpenBox.Options := [ofHideReadOnly,ofPathMustExist,ofFileMustExist,ofEnableSizing]; + + if OpenBox.Execute() then begin + edScriptDest.clear(); + edScriptDest.text := ExtractFileName(OpenBox.FileName); + end + else begin + edScriptDest.clear(); + edScriptDest.text := 'override'; + end; + + edScriptDest.setfocus; +end; + +procedure TMainForm.btnGetGffDestClick(Sender: TObject); +begin + OpenBox.Filter := 'ERF files (*.erf)|*.erf|MOD files (*.mod)|*.mod|SAV files (*.sav)|*.sav|HAK files (*.hak)|*.hak'; + OpenBox.DefaultExt := 'erf'; + OpenBox.FileName := ''; + OpenBox.Title := 'Select an ERF format file to get the name of...'; + OpenBox.Options := [ofHideReadOnly,ofPathMustExist,ofFileMustExist,ofEnableSizing]; + + if OpenBox.Execute() then begin + edGffDest.clear(); + edGffDest.text := ExtractFileName(OpenBox.FileName); + end + else begin + edGffDest.clear(); + edGffDest.text := 'override'; + end; + + edGffDest.setfocus; +end; + +procedure TMainForm.btnGffDestSetClick(Sender: TObject); +begin + if (edGffDest.text = '') or (lowercase(edGffDest.text) = 'override') then begin + ini.DeleteKey(l_currsection, '!Destination'); + end + else begin + ini.WriteString(l_currsection, '!Destination', edGffDest.text); + end; +end; + +// ADDED(2006-03-09) +procedure TMainForm.AddSSFFile1Click(Sender: TObject); +var + oList : TStringList; + oNode : TTreeNode; + sFile : string; + sKey : string; + i, n : integer; + bFound : boolean; +begin + FormFilename.Filetype := 'SSF'; + if (FormFilename.ShowModal() <> mrOk) then + exit; + + sFile := FormFilename.Filename; + + if (sFile = 'N/A') or (sFile = '') then + exit; + + if (ini.SectionExists(sFile)) then begin + ShowAlertBox('A file with this name is already used in this changes.ini file!'); + exit; + end; + + oList := TStringList.Create(); + ini.ReadSection('SSFList', oList); + + for i := 0 to (oList.count - 1) do begin + if (oList.strings[i] = sFile) then begin + ShowAlertBox('A file with this name is already used in this changes.ini file!'); + exit; + end; + end; + + n := 0; + sKey := ''; + bFound := True; + while bFound do begin + bFound := False; + sKey := 'File' + IntToStr(n); + inc(n); + + for i := 0 to (oList.count - 1) do begin + if (oList.strings[i] = sKey) then begin + bFound := True; + break; + end; + end; + end; + + if (sKey = '') then begin + ShowAlertBox('Error! Unable to find next free File key!'); + exit; + end; + + ini.writestring('SSFList', sKey, sFile); + l_currsection := sFile; + + oNode := tree.items.addchild(nodeSSF, sFile); + oNode.ImageIndex := 7; + oNode.SelectedIndex := 7; + oNode.Selected := True; + + oList.free(); + +end; + + +// ADDED(2006-03-09) +procedure TMainForm.DeleteSSFFile1Click(Sender: TObject); +var + sFile : string; + sKey : string; + sVal : string; + oList : TStringList; + oNode : TTreeNode; + i : integer; +begin + oNode := tree.Selected; + sFile := oNode.text; + + if (oNode = nil) or (oNode.parent <> nodeSSF) then begin + ShowAlertBox('No SSF format file has been selected in the list to be deleted'); + exit; + end; + + if (sFile = '') then + exit; + + // Allow user chance to abort before deleting things... + if (ShowConfirmBox('Are you sure you wish to remove ' + sFile + ' from the modifier list?') <> mrYes) then + exit; + + tree.items.delete(oNode); + + if (oNode <> nil) then + oNode.delete(); + + // Delete key from the SSFList + oList := TStringList.Create(); + ini.ReadSection('SSFList', oList); + for i := 0 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sVal := ini.readstring('SSFList', sKey, ''); + + if (sVal = sFile) then begin + ini.deletekey('SSFList', sKey); + end; + end; + + oList.free(); + + // Delete sections belonging to this file... + if ini.SectionExists(sFile) then begin + ini.EraseSection(sFile); + end; + +end; + +procedure TMainForm.cbSSFEntryChange(Sender: TObject); +begin + +end; + + +// ADDED(2006-03-09) +procedure TMainForm.btnLoadTokSSFClick(Sender: TObject); +var + oList : TStringList; + oMods : TStringList; + oCols : TStringList; + i : integer; + n : integer; + j : integer; + sKey : string; + sVal : string; + sFile : string; + sMod : string; + sLabel : string; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + procedure ParseGFFModifier(sSection : string); + var + oModifiers : TStringList; + iField : integer; + sK : string; + sV : string; + begin + oModifiers := TStringList.Create(); + try + ini.ReadSection(sSection, oModifiers); + for iField := 0 to (oModifiers.count -1) do begin + sK := oModifiers.strings[iField]; + sV := ini.ReadString(sSection, sK, ''); + + if (sV <> '') and (sK <> '') then begin + if (copy(sK, 1, 9) = '2DAMEMORY') and (cbSSFValue.Items.IndexOf(sK) = -1) then begin + cbSSFValue.Items.Add(sK); + end + else if (lowercase(copy(sK, 1, 8)) = 'addfield') then begin + if (ini.SectionExists(sV)) then begin + ParseGFFModifier(sV); + end; + end; + end; + end; + finally + oModifiers.free(); + end; + end; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +begin + // Rensa alla entries i dropdown-boxen, men bevara det som användaren ev. + // har knappat in själv. + sVal := cbSSFValue.text; + cbSSFValue.clear(); + cbSSFValue.text := sVal; + + oList := TStringList.Create(); + ini.readsection('TLKList', oList); + + // Ladda in alla angivna StrRef-tokens i dropdownlistan. + for i := 0 to (oList.Count - 1) do begin + sKey := oList.Strings[i]; + sVal := ini.readstring('TLKList', sKey, ''); + + if (sKey <> '') and (sVal <> '') then begin + if (copy(sKey, 1, 6) = 'StrRef') then begin + cbSSFValue.Items.Add(sKey); + end; + end; + end; + + oList.free(); + oList := TStringList.Create(); + ini.readsection('2DAList', oList); + + // Kolla efter alla 2DAMEMORY-tokens som fått värde tilldelade till sig + // så här långt i filen. + for i := 0 to (oList.Count - 1) do begin + sFile := ini.readstring('2DAList', oList.Strings[i], ''); + if (sFile <> '') then begin + oMods := TStringList.Create(); + try + // Check all modifiers for this particular file... + ini.readsection(sFile, oMods); + for n := 0 to (oMods.count - 1) do begin + sMod := oMods.Strings[n]; + sLabel := ini.readstring(sFile, sMod, ''); + + if (sLabel <> '') then begin + oCols := TStringList.Create(); + try + // Check each column label for this Modifier... + ini.ReadSection(sLabel, oCols); + for j := 0 to (oCols.count-1) do begin + sKey := oCols.strings[j]; + sVal := ini.readstring(sLabel, sKey, ''); + + // Check if the KEY is a unique 2DAMEMORY token + // instead of a column label. If so add to list. + if (sKey <> '') + and (copy(sKey, 1, 9) = '2DAMEMORY') + and (cbSSFValue.Items.IndexOf(sKey) = -1) + then begin + cbSSFValue.Items.Add(sKey); + end; + end; + finally + oCols.free(); + end; + end; + end; + finally + oMods.free(); + end; + end; + end; + + // Kolla efter 2DAMEMORY-tokens i GFF-listan... + oList.free(); + oList := TStringList.Create(); + ini.readsection('GFFList', oList); + + for i := 0 to (oList.Count - 1) do begin + sFile := ini.readstring('GFFList', oList.Strings[i], ''); + if (sFile <> '') then begin + if ini.SectionExists(sFile) then begin + ParseGFFModifier(sFile); + end; + end; + end; + oList.free(); + + // ShowInfoBox('Relevant tokens have been loaded into the dropdown list of the Value box.'); + cbSSFValue.setfocus; + +end; + + +// ADDED(2006-03-09) +procedure TMainForm.btnEditSSFClick(Sender: TObject); +begin + cbSSFEntry.ItemIndex := cbSSFEntry.Items.IndexOf(gridSSF.cells[0, gridSSF.row]); + cbSSFValue.text := gridSSF.cells[1, gridSSF.row]; +end; + + +// ADDED(2006-03-09) +procedure TMainForm.btnAddSSFClick(Sender: TObject); +var + i : integer; +begin + if (cbSSFEntry.text <> '') and (cbSSFValue.text <> '') then begin + ini.WriteString(l_currsection, cbSSFEntry.text, cbSSFValue.text); + + RefreshSSFGrid(); + gridSSF.SetFocus(); + + for i := 0 to (gridSSF.RowCount - 1) do begin + if (gridSSF.Cells[0, i] = cbSSFEntry.text) then begin + gridSSF.Row := i; + break; + end; + end; + + cbSSFEntry.text := ''; + cbSSFEntry.ItemIndex := -1; + cbSSFValue.text := ''; + end; +end; + + +// ADDED(2006-03-09) +procedure TMainForm.chSSFReplaceClick(Sender: TObject); +var + oList : TStringList; + bFound : boolean; + i : integer; + sKey : string; + sVal : string; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + function GetNextFreeKey(oList : TStringList; bReplace : boolean) : string; + var + n : integer; + iCnt : integer; + sBase : string; + sType : string; + bMatch : boolean; + begin + if bReplace then + sType := 'Replace' + else + sType := 'File'; + + bMatch := True; + iCnt := 0; + sBase := sType + '0'; + while bMatch do begin + bMatch := False; + for n := 0 to (oList.count-1) do begin + if (lowercase(oList[n]) = lowercase(sBase)) then begin + bMatch := True; + break; + end; + end; + + if not bMatch then begin + sBase := sType + IntToStr(iCnt); + break; + end + else begin + sBase := sType + IntToStr(iCnt+1); + inc(iCnt); + end; + end; + result := sBase; + end; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +begin + oList := TStringList.Create(); + ini.ReadSection('SSFList', oList); + + bFound := false; + for i := 0 to (oList.count - 1) do begin + sKey := oList.Strings[i]; + sVal := ini.ReadString('SSFList', sKey, ''); + + // Substitute current key with a new one of the proper type. + if not bFound and (sVal = l_currsection) then begin + if (copy(sKey, 1, 7) = 'Replace') <> chSSFReplace.checked then begin + ini.DeleteKey('SSFList', sKey); + ini.WriteString('SSFList', GetNextFreeKey(oList, chSSFReplace.checked), sVal); + bFound := true; + end; + end + // Maintain the ordering in the key list... + else if bFound then begin + ini.DeleteKey('SSFList', sKey); + ini.WriteString('SSFList', sKey, sVal); + end; + end; + oList.free(); + +end; + + +// ADDED(2006-03-09) +procedure TMainForm.RefreshSSFGrid(); +var + oList : TStringList; + i : integer; + iRow : integer; +begin + // Clear and reset the Grid! + gridSSF.colcount := 2; + gridSSF.cols[0].clear(); + gridSSF.cols[1].clear(); + + gridSSF.rowcount := 2; + gridSSF.FixedRows := 1; + gridSSF.colwidths[0] := 200; + gridSSF.colwidths[1] := (gridSSF.width - (gridSSF.colwidths[0] + 6)); + + gridSSF.Cells[0,0] := 'Sound Entry'; + gridSSF.Cells[1,0] := 'StrRef in dialog.tlk'; + + // Abort if there is no data to be read. + if not ini.SectionExists('SSFList') then + exit; + + if not ini.SectionExists(l_currsection) then + exit; + + + // Load entry data for this file from the INI file. + oList := TStringList.Create(); + ini.ReadSection(l_currsection, oList); + iRow := 1; + for i := 0 to (oList.count - 1) do begin + gridSSF.Cells[0, iRow] := oList.Strings[i]; + gridSSF.Cells[1, iRow] := ini.ReadString(l_currsection, oList.Strings[i], ''); + inc(iRow); + + if (i < (oList.count - 1)) then + gridSSF.rowcount := gridSSF.rowcount + 1; + end; + +end; + +procedure TMainForm.btnDelSSFClick(Sender: TObject); +var + sMod : string; + sType : string; +begin + sType := gridSSF.cells[0, gridSSF.row]; + sMod := gridSSF.cells[1, gridSSF.row]; + + if (sType = '') then + exit; + + if (ShowConfirmBox('Are you sure you wish to delete the changes to the entry "' + sType + '" for the SSF file "' + l_currsection + '"?') = mrYes) then begin + // Delete the key from the INI file... + ini.DeleteKey(l_currsection, sType); + + // Refresh the grid... + RefreshSSFGrid(); + end; +end; + +procedure TMainForm.cbSSFValueDropDown(Sender: TObject); +var + oList : TStringList; + oMods : TStringList; + oCols : TStringList; + i : integer; + n : integer; + j : integer; + sKey : string; + sVal : string; + sFile : string; + sMod : string; + sLabel : string; + oOldCursor : TCursor; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + procedure ParseGFFModifier(sSection : string); + var + oModifiers : TStringList; + iField : integer; + sK : string; + sV : string; + begin + oModifiers := TStringList.Create(); + try + ini.ReadSection(sSection, oModifiers); + for iField := 0 to (oModifiers.count -1) do begin + sK := oModifiers.strings[iField]; + sV := ini.ReadString(sSection, sK, ''); + + if (sV <> '') and (sK <> '') then begin + if (copy(sK, 1, 9) = '2DAMEMORY') and (cbSSFValue.Items.IndexOf(sK) = -1) then begin + cbSSFValue.Items.Add(sK); + end + else if (lowercase(copy(sK, 1, 8)) = 'addfield') then begin + if (ini.SectionExists(sV)) then begin + ParseGFFModifier(sV); + end; + end; + end; + end; + finally + oModifiers.free(); + end; + end; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +begin + // Rensa alla entries i dropdown-boxen, men bevara det som användaren ev. + // har knappat in själv. + sVal := cbSSFValue.text; + cbSSFValue.clear(); + cbSSFValue.text := sVal; + + oOldCursor := Screen.Cursor; + Screen.Cursor := crHourglass; + Application.ProcessMessages(); + try + oList := TStringList.Create(); + ini.readsection('TLKList', oList); + + // Ladda in alla angivna StrRef-tokens i dropdownlistan. + for i := 0 to (oList.Count - 1) do begin + sKey := oList.Strings[i]; + sVal := ini.readstring('TLKList', sKey, ''); + + if (sKey <> '') and (sVal <> '') then begin + if (copy(sKey, 1, 6) = 'StrRef') then begin + cbSSFValue.Items.Add(sKey); + end; + end; + end; + + oList.free(); + oList := TStringList.Create(); + ini.readsection('2DAList', oList); + + // Kolla efter alla 2DAMEMORY-tokens som fått värde tilldelade till sig + // så här långt i filen. + for i := 0 to (oList.Count - 1) do begin + sFile := ini.readstring('2DAList', oList.Strings[i], ''); + if (sFile <> '') then begin + oMods := TStringList.Create(); + try + // Check all modifiers for this particular file... + ini.readsection(sFile, oMods); + for n := 0 to (oMods.count - 1) do begin + sMod := oMods.Strings[n]; + sLabel := ini.readstring(sFile, sMod, ''); + + if (sLabel <> '') then begin + oCols := TStringList.Create(); + try + // Check each column label for this Modifier... + ini.ReadSection(sLabel, oCols); + for j := 0 to (oCols.count-1) do begin + sKey := oCols.strings[j]; + sVal := ini.readstring(sLabel, sKey, ''); + + // Check if the KEY is a unique 2DAMEMORY token + // instead of a column label. If so add to list. + if (sKey <> '') + and (copy(sKey, 1, 9) = '2DAMEMORY') + and (cbSSFValue.Items.IndexOf(sKey) = -1) + then begin + cbSSFValue.Items.Add(sKey); + end; + end; + finally + oCols.free(); + end; + end; + end; + finally + oMods.free(); + end; + end; + end; + + // Kolla efter 2DAMEMORY-tokens i GFF-listan... + oList.free(); + oList := TStringList.Create(); + ini.readsection('GFFList', oList); + + for i := 0 to (oList.Count - 1) do begin + sFile := ini.readstring('GFFList', oList.Strings[i], ''); + if (sFile <> '') then begin + if ini.SectionExists(sFile) then begin + ParseGFFModifier(sFile); + end; + end; + end; + oList.free(); + finally + Screen.Cursor := oOldCursor; + end; +end; + +procedure TMainForm.cbGffValueDropDown(Sender: TObject); +var + oList : TStringList; + oMods : TStringList; + oCols : TStringList; + i : integer; + n : integer; + j : integer; + sKey : string; + sVal : string; + sFile : string; + sMod : string; + sLabel : string; + oOldCursor : TCursor; +begin + // Rensa alla entries i dropdown-boxen, men bevara det som användaren ev. + // har knappat in själv. + sVal := cbGffValue.text; + cbGffValue.clear(); + cbGffValue.text := sVal; + + oOldCursor := Screen.Cursor; + Screen.Cursor := crHourglass; + Application.ProcessMessages(); + try + oList := TStringList.Create(); + ini.readsection('TLKList', oList); + + // Ladda in alla angivna StrRef-tokens i dropdownlistan. + for i := 0 to (oList.Count - 1) do begin + sKey := oList.Strings[i]; + sVal := ini.readstring('TLKList', sKey, ''); + + if (sKey <> '') and (sVal <> '') then begin + if (copy(sKey, 1, 6) = 'StrRef') then begin + cbGffValue.Items.Add(sKey); + end; + end; + end; + + oList.free(); + oList := TStringList.Create(); + ini.readsection('2DAList', oList); + + // Kolla efter alla 2DAMEMORY-tokens som fått värde tilldelade till sig + // så här långt i filen. + for i := 0 to (oList.Count - 1) do begin + sFile := ini.readstring('2DAList', oList.Strings[i], ''); + if (sFile <> '') then begin + oMods := TStringList.Create(); + try + // Check all modifiers for this particular file... + ini.readsection(sFile, oMods); + for n := 0 to (oMods.count - 1) do begin + sMod := oMods.Strings[n]; + sLabel := ini.readstring(sFile, sMod, ''); + + if (sLabel <> '') then begin + oCols := TStringList.Create(); + try + // Check each column label for this Modifier... + ini.ReadSection(sLabel, oCols); + for j := 0 to (oCols.count-1) do begin + sKey := oCols.strings[j]; + sVal := ini.readstring(sLabel, sKey, ''); + + // Check if the KEY is a unique 2DAMEMORY token + // instead of a column label. If so add to list. + if (sKey <> '') + and (copy(sKey, 1, 9) = '2DAMEMORY') + and (cbGffValue.Items.IndexOf(sKey) = -1) + then begin + cbGffValue.Items.Add(sKey); + end; + end; + finally + oCols.free(); + end; + end; + end; + finally + oMods.free(); + end; + end; + end; + finally + Screen.Cursor := oOldCursor; + end; +end; + +procedure TMainForm.gridSSFDblClick(Sender: TObject); +begin + btnEditSSFClick(Sender); +end; + +procedure TMainForm.gridGffModDblClick(Sender: TObject); +begin + btnEditFieldClick(Sender); +end; + +procedure TMainForm.gridTlkDblClick(Sender: TObject); +begin + btnAddStrRefClick(Sender); +end; + +procedure TMainForm.btnGffLoadFieldsClick(Sender: TObject); +var + oGFF : TGFFFile; + oField : TGFFField; + sFile : string; + oOldCursor : TCursor; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + procedure ListFieldLabel(oField : TGFFField; sPath : string); + var + i : integer; + oStruct : TGFFStruct; + oList : TGFFList; + oExo : TGFF_CExoLocString; + begin + if (oField = nil) then + exit; + + // List the label of this field... + if (Length(oField.fieldlabel) > 0) then begin + if (Length(sPath) > 0) then + sPath := sPath + '\'; + + sPath := sPath + oField.fieldlabel; + + // Exception for ExoLocStrings, since they have sub-fields and no + // direct field that holds any assignable value. + if (oField.fieldtype <> FIELD_TYPE_CEXOLOCSTRING) then + edGffField.Items.Add(sPath); + end; + + // List all fields contained in this Struct.... + if (oField.fieldtype = FIELD_TYPE_STRUCT) then begin + oStruct := (oField as TGFFStruct); + for i := 0 to (oStruct.count-1) do + ListFieldLabel(oStruct.fields[i], sPath); + end + // List all structs contained in this List... + else if (oField.fieldtype = FIELD_TYPE_LIST) then begin + oList := (oField as TGFFList); + for i := 0 to (oList.count-1) do + ListFieldLabel(oList.structs[i], sPath + '\' + IntToStr(i)); + end + // List Localized substrings and StrRef of an ExoLocString... + else if (oField.fieldtype = FIELD_TYPE_CEXOLOCSTRING) then begin + oExo := (oField as TGFF_CExoLocString); + edGffField.Items.Add(sPath + '(strref)'); + for i := 0 to (oExo.stringcount-1) do + edGffField.Items.Add(sPath + '(lang' + IntToStr(oExo.locstrings[i].stringid) + ')'); + end; + end; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +begin + sFile := ''; + + // If a file with this name already exists in the same folder as the changes.ini, + // use that file instead of asking for one. + if (ini.FileName <> '') + and SysUtils.FileExists(ini.FileName) + and SysUtils.FileExists(ExtractFilePath(ini.FileName) + l_currsection) + then begin + sFile := ExtractFilePath(ini.FileName) + l_currsection; + end + else begin + OpenBox.Filter := 'GFF format files (*.*)|*.*'; + OpenBox.DefaultExt := ''; + OpenBox.FileName := ''; + OpenBox.Title := 'Select a GFF format file to get the field labels from...'; + OpenBox.Options := [ofHideReadOnly,ofPathMustExist,ofFileMustExist,ofEnableSizing]; + + if OpenBox.Execute() and SysUtils.FileExists(OpenBox.FileName) then begin + sFile := OpenBox.FileName; + end; + end; + + if (sFile <> '') then begin + oOldCursor := Screen.Cursor; + Screen.Cursor := crHourglass; + Application.ProcessMessages(); + + oGFF := TGFFFile.Create(); + try + // Grab all field labels/paths from the selected file... + oGFF.LoadFile(sFile); + l_tokens := l_currsection; + edGffField.Items.Clear(); + oField := oGFF.GetFirstRootField(); + while (oField <> nil) do begin + ListFieldLabel(oField, ''); + oField := oGFF.GetNextRootField(); + end; + + edGffField.Color := clMoneyGreen; + edGffField.setfocus(); + finally + Screen.Cursor := oOldCursor; + if (oGFF <> nil) then + oGFF.Free(); + end; + end; +end; + +procedure TMainForm.btnCopy2daModClick(Sender: TObject); +var + oList : TStringList; + sInput : string; + sMod : string; + sType : string; + sKey : string; + iRow : integer; + bFound : boolean; + i, n : integer; + oOldCursor : TCursor; +begin + NewLabelForm.edModLabel.text := ''; + NewLabelForm.lblTitle.caption := 'Copy 2DA Modifier'; + NewLabelForm.Caption := 'Specify modifier label for the new copy'; + + if (NewLabelForm.ShowModal = mrOk) then begin + oOldCursor := Screen.Cursor; + Screen.Cursor := crHourglass; + Application.ProcessMessages(); + try + sInput := NewLabelForm.edModLabel.text; + sKey := ''; + + if (ini.SectionExists(sInput)) then begin + ShowAlertBox('The specified modifier label already exists for another modifier. Try again.'); + exit; + end; + + // Ingen giltig label angavs, så avbryt... + if (sInput = 'N/A') or (sInput = '') or (Pos(' ', sInput) > 0) then begin + ShowAlertBox('Invalid modifier label specified. It must be at least 1 character and may contain no spaces. Try again.'); + exit; + end; + + iRow := grid2daMod.row; + sMod := grid2daMod.cells[1, iRow]; + sType := grid2daMod.cells[0, iRow]; + + if (lowercase(copy(sType, 1, 6)) = 'addrow') then begin + // Hitta första lediga AddRow# nyckel att använda... + bFound := True; + sKey := 'AddRow0'; + i := 0; + while bFound do begin + bFound := False; + for n := 0 to (grid2daMod.rowcount-1) do begin + if (lowercase(grid2daMod.cells[0, n]) = lowercase(sKey)) then begin + bFound := True; + break; + end; + end; + + if not bFound then begin + sKey := 'AddRow' + IntToStr(i); + break; + end + else begin + sKey := 'AddRow' + IntToStr(i+1); + inc(i); + end; + end; + + if (sKey = '') then begin + ShowAlertBox('ERROR! Failed to generate a modifier key!'); + exit; + end; + + // Add the new section reference to the INI file... + ini.WriteString(l_currsection, sKey, sInput); + + // Copy the keys from the original modifier to the new one... + oList := TStringList.Create(); + try + ini.ReadSection(sMod, oList); + for i := 0 to (oList.count-1) do begin + ini.WriteString(sInput, oList.Strings[i], ini.ReadString(sMod, oList.Strings[i], '')); + end; + finally + oList.free(); + end; + + // Show the edit box for the new Modifier... + Mod2DARowForm.Caption := 'Add line in file ' + l_currsection + ' [' + sInput + ']'; + Mod2DARowForm.box_ini := ini; + Mod2DARowForm.box_2daname := l_currsection; + Mod2DARowForm.box_section := sInput; + Mod2DARowForm.LoadIniData(); + Mod2DARowForm.ShowModal(); + end + else if (lowercase(copy(sType, 1, 9)) = 'changerow') then begin + // Hitta första lediga ChangeRow# nyckeln att använda... + sKey := 'ChangeRow0'; + i := 0; + bFound := True; + while bFound do begin + bFound := False; + for n := 0 to (grid2daMod.rowcount-1) do begin + if (lowercase(grid2daMod.cells[0, n]) = lowercase(sKey)) then begin + bFound := True; + break; + end; + end; + + if not bFound then begin + sKey := 'ChangeRow' + IntToStr(i); + break; + end + else begin + sKey := 'ChangeRow' + IntToStr(i+1); + inc(i); + end; + end; + + if (sKey = '') then begin + ShowAlertBox('ERROR! Failed to generate a modifier key!'); + exit; + end; + + // Add the new section reference to the INI file... + ini.WriteString(l_currsection, sKey, sInput); + + // Copy the keys from the original modifier to the new one... + oList := TStringList.Create(); + try + ini.ReadSection(sMod, oList); + for i := 0 to (oList.count-1) do begin + ini.WriteString(sInput, oList.Strings[i], ini.ReadString(sMod, oList.Strings[i], '')); + end; + finally + oList.free(); + end; + + // Show the edit box for the new Modifier... + Change2daForm.Caption := 'Change line in file ' + l_currsection + ' [' + sInput + ']'; + Change2daForm.box_ini := ini; + Change2daForm.box_2daname := l_currsection; + Change2daForm.box_type := 'ChangeRow'; + Change2daForm.box_section := sInput; + Change2daForm.boxIdentifier.Caption := ' Set line to modify '; + Change2daForm.boxChangeValue.Caption := ' Change column value '; + Change2daForm.lblHeadline.Caption := 'Modify 2DA line'; + Change2daForm.lblGridHeading.caption := 'Modified column values for line:'; + Change2daForm.LoadIniData(); + Change2daForm.ShowModal(); + end + else if (lowercase(copy(sType, 1, 7)) = 'copyrow') then begin + // Find the first unused CopyRow# key to use.... + sKey := 'CopyRow0'; + i := 0; + bFound := True; + while bFound do begin + bFound := False; + for n := 0 to (grid2daMod.rowcount-1) do begin + if (lowercase(grid2daMod.cells[0, n]) = lowercase(sKey)) then begin + bFound := True; + break; + end; + end; + + if not bFound then begin + sKey := 'CopyRow' + IntToStr(i); + break; + end + else begin + sKey := 'CopyRow' + IntToStr(i+1); + inc(i); + end; + end; + + if (sKey = '') then begin + ShowAlertBox('ERROR! Failed to generate a modifier key!'); + exit; + end; + + // Add the new section reference to the INI file... + ini.WriteString(l_currsection, sKey, sInput); + + // Copy the keys from the original modifier to the new one... + oList := TStringList.Create(); + try + ini.ReadSection(sMod, oList); + for i := 0 to (oList.count-1) do begin + ini.WriteString(sInput, oList.Strings[i], ini.ReadString(sMod, oList.Strings[i], '')); + end; + finally + oList.free(); + end; + + // Show the edit box for the new Modifier... + Copy2daForm.Caption := 'Copy line in file ' + l_currsection + ' [' + sInput + ']'; + Copy2daForm.box_ini := ini; + Copy2daForm.box_2daname := l_currsection; + Copy2daForm.box_type := 'CopyRow'; + Copy2daForm.box_section := sInput; + Copy2daForm.boxIdentifier.Caption := ' Set existing line to copy '; + Copy2daForm.boxChangeValue.Caption := ' Change column value '; + Copy2daForm.lblHeadline.Caption := 'Copy 2DA line'; + Copy2daForm.lblGridHeading.caption := 'New column values for copied line:'; + Copy2daForm.LoadIniData(); + Copy2daForm.ShowModal(); + end + else if (lowercase(copy(sType, 1, 9)) = 'addcolumn') then begin + // Find the first unused AddColumn# key to use.... + sKey := 'AddColumn0'; + bFound := True; + i := 0; + while bFound do begin + bFound := False; + for n := 0 to (grid2daMod.rowcount-1) do begin + if (lowercase(grid2daMod.cells[0, n]) = lowercase(sKey)) then begin + bFound := True; + break; + end; + end; + + if not bFound then begin + sKey := 'AddColumn' + IntToStr(i); + break; + end + else begin + sKey := 'AddColumn' + IntToStr(i+1); + inc(i); + end; + end; + + if (sKey = '') then begin + ShowAlertBox('ERROR! Failed to generate a modifier key!'); + exit; + end; + + // Add the new section reference to the INI file... + ini.WriteString(l_currsection, sKey, sInput); + + // Copy the keys from the original modifier to the new one... + oList := TStringList.Create(); + try + ini.ReadSection(sMod, oList); + for i := 0 to (oList.count-1) do begin + ini.WriteString(sInput, oList.Strings[i], ini.ReadString(sMod, oList.Strings[i], '')); + end; + finally + oList.free(); + end; + + // Show the edit box for the new Modifier... + Add2daColForm.Caption := 'Add column in file ' + l_currsection + ' [' + sInput + ']'; + Add2daColForm.box_ini := ini; + Add2daColForm.box_2daname := l_currsection; + Add2daColForm.box_type := 'AddColumn'; + Add2daColForm.box_section := sInput; + Add2daColForm.LoadIniData(); + Add2daColForm.ShowModal(); + end; + + Load2DAEntries(l_currsection); + // Select the newly added Modifier in the list. + for i := 0 to (grid2daMod.rowcount - 1) do begin + if (grid2daMod.cells[0, i] = sKey) then begin + grid2daMod.row := i; + break; + end; + end; + finally + Screen.Cursor := oOldCursor; + end; + end; +end; + + +procedure TMainForm.CompareGffFiles(sFile1 : string; sFile2 : string); +var + oGFFOrg : TGFFFile; + oGFFMod : TGFFFile; + oField : TGFFField; + iNCount : integer; + iMCount : integer; + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + function GetFieldTypeString(iType : integer) : string; + begin + result := 'UNKNOWN'; + case iType of + FIELD_TYPE_BYTE: result := 'Byte'; + FIELD_TYPE_CHAR: result := 'Char'; + FIELD_TYPE_WORD: result := 'Word'; + FIELD_TYPE_SHORT: result := 'Short'; + FIELD_TYPE_DWORD: result := 'DWORD'; + FIELD_TYPE_INT: result := 'Int'; + FIELD_TYPE_INT64: result := 'Int64'; + FIELD_TYPE_FLOAT: result := 'Float'; + FIELD_TYPE_DOUBLE: result := 'Double'; + FIELD_TYPE_CEXOSTRING: result := 'ExoString'; + FIELD_TYPE_RESREF: result := 'ResRef'; + FIELD_TYPE_CEXOLOCSTRING: result := 'ExoLocString'; + FIELD_TYPE_STRUCT: result := 'Struct'; + FIELD_TYPE_LIST: result := 'List'; + FIELD_TYPE_ORIENTATION: result := 'Orientation'; + FIELD_TYPE_POSITION: result := 'Position'; + end; + end; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + function GetUniqueLabel(sSection : string; sLabel : string) : string; + var + oList : TStringList; + sVal : string; + bFound : boolean; + i, n : integer; + begin + result := sLabel; + oList := TStringList.Create(); + try + // Find the first unused AddColumn# key to use.... + sVal := sLabel + '_0'; + bFound := True; + i := 0; + oList.clear(); + ini.ReadSection(sSection, oList); + while bFound do begin + bFound := False; + // Fix: Check if the section already exists in the INI file.... + if ini.SectionExists(sVal) then begin + bFound := True; + end; + + // Check for dead references that might hang around... + if not bFound then begin + for n := 0 to (oList.count-1) do begin + if (lowercase(ini.ReadString(sSection, oList[n], '')) = lowercase(sVal)) then begin + bFound := True; + break; + end; + end; + end; + + if not bFound then begin + sVal := sLabel + '_' + IntToStr(i); + result := sVal; + break; + end + else begin + inc(i); + sVal := sLabel + '_' + IntToStr(i); + end; + end; + finally + oList.free(); + end; + end; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + function SectionKeyExists(sSection, sParent : string) : boolean; + var + oList : TStringList; + i : integer; + sKey : string; + sVal : string; + begin + result := false; + oList := TStringList.Create(); + try + ini.ReadSection(sParent, oList); + for i := 0 to (oList.count - 1) do begin + sKey := oList.strings[i]; + sVal := ini.ReadString(sParent, sKey, ''); + if (sVal = sSection) then begin + result := true; + exit; + end; + end; + finally + oList.free(); + end; + end; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + function GetNextFieldKey(sSection : string) : string; + var + oList : TStringList; + sKey : string; + bFound : boolean; + i, n : integer; + begin + // Find the lowest free Key of the specific type. + n := 0; + sKey := ''; + bFound := True; + oList := TStringList.Create(); + ini.ReadSection(sSection, oList); + try + while bFound do begin + bFound := False; + + sKey := 'AddField' + IntToStr(n); + inc(n); + + for i := 0 to (oList.count - 1) do begin + if (lowercase(oList.strings[i]) = lowercase(sKey)) then begin + bFound := True; + break; + end; + end; + end; + finally + oList.free(); + end; + + result := sKey; + end; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + procedure CompareGffField(oField : TGFFField; oParent : TGFFField; sPath : string; sParent : string; bSubnode : boolean; iIndex : integer = -1); + var + i : integer; + oStruct : TGFFStruct; + oList : TGFFList; + oExo : TGFF_CExoLocString; + oLoc : TGFF_CSubString; + oCmp : TGFFField; + bSub : boolean; + sType : string; + sKey : string; + sLoc : string; + sTmp : string; + sSection: string; + sGffname: string; + sParentPath : string; + sParentMod : string; + begin + sParentPath := sPath; + + if (Length(oField.fieldlabel) > 0) and (iIndex = -1) then begin + if (Length(sPath) > 0) then + sPath := sPath + '\'; + + sPath := sPath + oField.fieldlabel; + end + else if (iIndex <> -1) then begin + if (Length(sPath) > 0) then + sPath := sPath + '\'; + + sPath := sPath + IntToStr(iIndex); + end; + + sGffname := lowercase(l_currsection); + sGffname := copy(sGffname, 1, Pos('.', sGffname)-1); + if (Length(sGffname) <= 0) then + sGffname := 'c_'; + + oCmp := oGFFOrg.GetFieldByLabel(sPath); + + // STRUCT ////////////////////////////////////////////////////////////// + if (oField.fieldtype = FIELD_TYPE_STRUCT) then begin + Application.ProcessMessages(); + oStruct := (oField as TGFFStruct); + sParentMod := sParent; + bSub := bSubnode; + // This struct does not exist in the original file! Add it... + if (oCmp = nil) then begin + bSub := true; + + if (oParent.fieldtype = FIELD_TYPE_LIST) then + sSection := GetUniqueLabel(sParent, 'gff_' + sGffname + '_' + oParent.fieldlabel + '_' + IntToStr(iIndex)) + else + sSection := GetUniqueLabel(sParent, 'gff_' + sGffname + '_' + oField.fieldlabel); + + sParentMod := sSection; + + // If reference to new section hasn't been added yet to parent, do it. + if not SectionKeyExists(sSection, sParent) then begin + sKey := GetNextFieldKey(sParent); + ini.WriteString(sParent, sKey, sSection); + end; + + // Save modifier keys for the Struct field values... + ini.WriteString(sSection, 'FieldType', GetFieldTypeString(oStruct.fieldtype)); + + if not bSubnode then + ini.WriteString(sSection, 'Path', sParentPath); + + ini.WriteString(sSection, 'Label', oStruct.fieldlabel); + ini.WriteString(sSection, 'TypeId', IntToStr(oStruct.typeid)); + + inc(iNCount); + end; + + // Process all fields contained within this Struct as well... + for i := 0 to (oStruct.count-1) do + CompareGffField(oStruct.fields[i], oStruct, sPath, sParentMod, bSub, -1); + end + // LIST //////////////////////////////////////////////////////////////// + else if (oField.fieldtype = FIELD_TYPE_LIST) then begin + Application.ProcessMessages(); + oList := (oField as TGFFList); + sParentMod := sParent; + bSub := bSubnode; + + // This List does not exist in the original file! Add it... + if (oCmp = nil) then begin + bSub := true; + sSection := GetUniqueLabel(sParent, 'gff_' + sGffname + '_' + oField.fieldlabel); + sParentMod := sSection; + + // If reference to new section hasn't been added yet to parent, do it. + if not SectionKeyExists(sSection, sParent) then begin + sKey := GetNextFieldKey(sParent); + ini.WriteString(sParent, sKey, sSection); + end; + + // Save values of the input fields to the INI file... + ini.WriteString(sSection, 'FieldType', GetFieldTypeString(oList.fieldtype)); + + if not bSubnode then + ini.WriteString(sSection, 'Path', sParentPath); + + ini.WriteString(sSection, 'Label', oList.fieldlabel); + + inc(iNCount); + end; + + // Process all structs contained in this List as well... + for i := 0 to (oList.count-1) do + CompareGffField(oList.structs[i], oList, sPath, sParentMod, bSub, i); + end + // EXOLOCSTRING //////////////////////////////////////////////////////// + else if (oField.fieldtype = FIELD_TYPE_CEXOLOCSTRING) then begin + oExo := (oField as TGFF_CExoLocString); + bSub := false; + + // The field exists in both files, check for differences... + if (oCmp <> nil) and (oCmp.fieldtype = FIELD_TYPE_CEXOLOCSTRING) then begin + // Check if the StrRef value differs... + if (TGFF_CExoLocString(oCmp).strref <> oExo.strref) then begin + // Fix: Store -1 properly (it's a DWORD in the GFF) + if (oExo.strref = $FFFFFFFF) then + sTmp := '-1' + else + sTmp := IntToStr(oExo.strref); + + ini.WriteString(l_currsection, sPath + '(strref)', sTmp); + bSub := true; + end; + + // Check if the Localized Substrings differ... + for i := 0 to (oExo.stringcount-1) do begin + oLoc := oExo.locstrings[i]; + sLoc := TGFF_CExoLocString(oCmp).GetStringById(oLoc.stringid); + + if (oLoc.textstring <> sLoc) then begin + ini.WriteString(l_currsection, sPath + '(lang' + IntToStr(oLoc.stringid) + ')', oLoc.textstring); + bSub := true; + end; + end; + + if bSub then + inc(iMCount); + end + // The field does not exist in the original file! Add it! + else begin + sSection := GetUniqueLabel(sParent, 'gff_' + sGffname + '_' + oField.fieldlabel); + + // If reference to new section hasn't been added yet to parent, do it. + if not SectionKeyExists(sSection, sParent) then begin + sKey := GetNextFieldKey(sParent); + ini.WriteString(sParent, sKey, sSection); + end; + + // Save values of the input fields to the INI file... + ini.WriteString(sSection, 'FieldType', GetFieldTypeString(oExo.fieldtype)); + + if not bSubnode then + ini.WriteString(sSection, 'Path', sParentPath); + + ini.WriteString(sSection, 'Label', oExo.fieldlabel); + + // Fix: Store -1 properly (it's a DWORD in the GFF) + if (oExo.strref = $FFFFFFFF) then + sTmp := '-1' + else + sTmp := IntToStr(oExo.strref); + ini.WriteString(sSection, 'StrRef', sTmp); + + // Write the Localized substrings to the INI file... + for i := 0 to (oExo.stringcount - 1) do begin + oLoc := oExo.locstrings[i]; + if (Length(oLoc.textstring) > 0) then + ini.WriteString(sSection, 'lang' + IntToStr(oLoc.stringid), oLoc.textstring); + end; + + inc(iNCount); + end; + end + // FIELD /////////////////////////////////////////////////////////////// + else begin + // The field exists in both fields.... + if (oCmp <> nil) then begin + // The value does not match! Create modifier to update to new value. + // Ignore field type since TSLPatcher currently can't change the + // data type of a field anyway. + if (oField.text <> oCmp.text) then begin + ini.WriteString(l_currsection, sPath, oField.text); + inc(iMCount); + end; + end + // The field only exists in the Modified file... Add it! + else begin + sType := GetFieldTypeString(oField.fieldtype); + if (sType <> 'UNKNOWN') then begin + sSection := GetUniqueLabel(sParent, 'gff_' + sGffname + '_' + oField.fieldlabel); + // If reference to new section hasn't been added yet to parent, do it. + if not SectionKeyExists(sSection, sParent) then begin + sKey := GetNextFieldKey(sParent); + ini.WriteString(sParent, sKey, sSection); + end; + + // Add the Modifier keys to the INI file for this field.... + ini.WriteString(sSection, 'FieldType', sType); + + if not bSubnode then + ini.WriteString(sSection, 'Path', sParentPath); + + ini.WriteString(sSection, 'Label', oField.fieldlabel); + ini.WriteString(sSection, 'Value', oField.text); + + inc(iNCount); + end; + end; + end; + end; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +begin + if not SysUtils.FileExists(sFile1)then begin + ShowAlertBox('Unable to locate the file ' + sFile1 + ', cannot continue!'); + exit; + end; + + if not SysUtils.FileExists(sFile2) then begin + ShowAlertBox('Unable to locate the file ' + sFile2 + ', cannot continue!'); + exit; + end; + + iNCount := 0; + iMCount := 0; + oGFFOrg := TGFFFile.Create(); + oGFFMod := TGFFFile.Create(); + try + oGFFOrg.LoadFile(sFile1); + oGFFMod.LoadFile(sFile2); + + oField := oGFFMod.GetFirstRootField(); + while (oField <> nil) do begin + CompareGffField(oField, oGFFMod.root, '', l_currsection, false); + oField := oGFFMod.GetNextRootField(); + end; + + ShowInfoBox(Format('Compare completed, %d fields were modified and %d fields were new in the %s file. Building modifiers...', [iMCount, iNCount, l_currsection])); + finally + if (oGFFOrg <> nil) then + oGFFOrg.free(); + + if (oGFFMod <> nil) then + oGFFMod.free(); + end; +end; + +procedure TMainForm.btnCompareGffClick(Sender: TObject); +var + oOldCursor : TCursor; + sFile1 : string; + sFile2 : string; +begin + // Get first file to open..... + OpenBox.Filter := 'GFF format file (*.*)|*.*'; + OpenBox.DefaultExt := ''; + OpenBox.FileName := l_currsection; + OpenBox.Title := 'Select ORIGINAL (Unaltered) GFF file to compare against:'; + OpenBox.Options := [ofHideReadOnly,ofPathMustExist,ofFileMustExist,ofEnableSizing]; + if (not OpenBox.Execute) or (not SysUtils.FileExists(OpenBox.Filename)) then + exit; + + sFile1 := OpenBox.FileName; + + // Get second file to open... this should contain the changes. + OpenBox.Filter := 'GFF format file (*.*)|*.*'; + OpenBox.DefaultExt := ''; + OpenBox.FileName := l_currsection; + OpenBox.Title := 'Select MODIFIED GFF file to compare against original:'; + OpenBox.Options := [ofHideReadOnly,ofPathMustExist,ofFileMustExist,ofEnableSizing]; + if (not OpenBox.Execute) or (not SysUtils.FileExists(OpenBox.Filename)) then + exit; + + sFile2 := OpenBox.FileName; + + oOldCursor := Screen.Cursor; + Screen.Cursor := crHourglass; + Application.ProcessMessages(); + try + CompareGffFiles(sFile1, sFile2); + LoadGFFEntries(l_currsection); + finally + Screen.Cursor := oOldCursor; + end; +end; + +procedure TMainForm.SetupsNewClick(Sender: TObject); +var + F : TextFile; +begin + SaveBox.FileName := 'namespaces.ini'; + if SaveBox.Execute() then begin + NamespaceForm.Filename := SaveBox.FileName; + + AssignFile(F, SaveBox.FileName); + Rewrite(F); + Writeln(F, '; ==================================================================='); + Writeln(F, '; TSLPATCHER - GENERATED NAMESPACES FILE (' + DateToStr(Date) + ')'); + Writeln(F, '; ==================================================================='); + Writeln(F, '; This file is automatically generated and as such has no formatting'); + Writeln(F, '; to speak of. You can insert blank lines between [sections] (but NOT'); + Writeln(F, '; between keys within a section!) and add comment lines starting'); + Writeln(F, '; with semicolon to make it more readable without breaking anything.'); + Writeln(F, '; -------------------------------------------------------------------'); + Writeln(F, ''); + Writeln(F, '[Namespaces]'); + Writeln(F, ''); + Writeln(F, ''); + Writeln(F, '; ==================================================================='); + CloseFile(F); + + if (lowercase(ExtractFileName(SaveBox.FileName)) <> 'namespaces.ini') then + ShowInfoBox('The Setup List file must be named "namespaces.ini" for TSLPatcher to recognize it. Remember to rename it before attempting to use it.'); + + NamespaceForm.ShowModal(); + end; +end; + +procedure TMainForm.SetupsOpenClick(Sender: TObject); +var + oNSIni : TST_IniFile; + bCheck : boolean; +begin + if OpenBox.Execute() then begin + + if not SysUtils.FileExists(OpenBox.FileName) then begin + ShowAlertBox('The selected file could not be opened. Unable to proceed.'); + exit; + end; + + oNSIni := TST_IniFile.Create(OpenBox.FileName); + bCheck := oNSIni.SectionExists('Namespaces'); + oNSIni.Free(); + + if not bCheck then begin + ShowAlertBox('The opened file is not a valid Setup/Namespace file! Unable to edit it.'); + end + else begin + NamespaceForm.Filename := OpenBox.FileName; + NamespaceForm.ShowModal(); + end; + end; +end; + +procedure TMainForm.cbLookupChange(Sender: TObject); +begin + cbGameVer.Enabled := (cbLookup.ItemIndex <> 0); +end; + +function TMainForm.UpdateGffFieldOrder(sMoveKey : string; enMoveDir : TMoveDir) : integer; +var + i : integer; + oKeys : TStringList; + oVals : TStringList; + sKey : string; + sVal : string; + bMove : boolean; +begin + oKeys := TStringList.Create(); + oVals := TStringList.Create(); + bMove := false; + result := -1; + + // Read keys/vals from the grid into stringlists and rearrange + // the entries to move the selected modifier in the desired direction. + for i := 0 to (gridGffMod.RowCount-1) do begin + sKey := gridGffMod.cells[0, i]; + sVal := gridGffMod.cells[1, i]; + + if (bMove) then begin + bMove := false; + oKeys.Add(sKey); + oVals.Add(sVal); + oKeys.Exchange(i-1, i); + oVals.Exchange(i-1, i); + result := i; + continue; + end; + + if (sKey = sMoveKey) then begin + if (i = 0) and (enMoveDir = moveUp) then begin + oKeys.Add(sKey); + oVals.Add(sVal); + result := 0; + end + else if (i = (gridGffMod.RowCount - 1)) and (enMoveDir = moveDown) then begin + oKeys.Add(sKey); + oVals.Add(sVal); + result := i; + end + else if (enMoveDir = moveUp) then begin + oKeys.Insert(i-1, sKey); + oVals.Insert(i-1, sVal); + result := i-1; + end + else if (enMoveDir = moveDown) then begin + bMove := true; + oKeys.Add(sKey); + oVals.Add(sVal); + end; + end + else begin + oKeys.Add(sKey); + oVals.Add(sVal); + end; + end; + + if (oKeys.count <> oVals.count) then begin + ShowAlertBox('Error while rearranging list, the key and value counts do not match!'); + exit; + end; + + // Clean up keys from the section... + for i := 0 to (oKeys.count-1) do begin + ini.DeleteKey(l_currsection, oKeys[i]); + end; + + // Write the keys back in the desired order. + for i := 0 to (oKeys.count-1) do begin + ini.WriteString(l_currsection, oKeys[i], oVals[i]); + end; + + if (oKeys <> nil) then + oKeys.free(); + + if (oVals <> nil) then + oVals.free(); +end; + +procedure TMainForm.btnMoveGffModUpClick(Sender: TObject); +var + iIdx : integer; +begin + if (gridGffMod.row <> -1) and (gridGffMod.Row > 0) then begin + iIdx := UpdateGffFieldOrder(gridGffMod.Cells[0, gridGffMod.row], moveUp); + LoadGFFEntries(l_currsection); + gridGffMod.row := iIdx; + end; +end; + +procedure TMainForm.btnMoveGffModDownClick(Sender: TObject); +var + iIdx : integer; +begin + if (gridGffMod.row <> -1) and (gridGffMod.Row < (gridGffMod.RowCount-1)) then begin + iIdx := UpdateGffFieldOrder(gridGffMod.Cells[0, gridGffMod.row], moveDown); + LoadGFFEntries(l_currsection); + gridGffMod.row := iIdx; + end; +end; + +end. diff --git a/UMassAddForm.ddp b/UMassAddForm.ddp new file mode 100644 index 0000000..4370276 Binary files /dev/null and b/UMassAddForm.ddp differ diff --git a/UMassAddForm.dfm b/UMassAddForm.dfm new file mode 100644 index 0000000..f77e250 Binary files /dev/null and b/UMassAddForm.dfm differ diff --git a/UMassAddForm.pas b/UMassAddForm.pas new file mode 100644 index 0000000..ddad4a9 --- /dev/null +++ b/UMassAddForm.pas @@ -0,0 +1,71 @@ +unit UMassAddForm; + +interface + +uses + Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, + StdCtrls, Buttons, ExtCtrls, UST_Common; + +type + TMassAddForm = class(TForm) + Panel1: TPanel; + edFoldername: TEdit; + SpeedButton1: TSpeedButton; + cbReplace: TCheckBox; + btnOK: TButton; + btnCancel: TButton; + Label1: TLabel; + Label2: TLabel; + Label3: TLabel; + Label4: TLabel; + Label5: TLabel; + Label6: TLabel; + procedure btnOKClick(Sender: TObject); + procedure SpeedButton1Click(Sender: TObject); + private + { Private declarations } + public + { Public declarations } + Replace : boolean; + Folder : string; + end; + +var + MassAddForm: TMassAddForm; + +implementation + +{$R *.DFM} + +procedure TMassAddForm.btnOKClick(Sender: TObject); +begin + Replace := cbReplace.checked; + Folder := edFoldername.text; + + cbReplace.checked := False; + edFoldername.clear(); + +end; + +procedure TMassAddForm.SpeedButton1Click(Sender: TObject); +var + sFolder : string; + i : integer; + iPos : integer; +begin + sFolder := OpenFolderDialog('Please select a folder to get the name of', 0); + + iPos := 0; + + for i := Length(sFolder) downto 1 do begin + if (sFolder[i] = '\') then begin + iPos := i+1; + break; + end; + end; + + sFolder := copy(sFolder, iPos, (Length(sFolder) - (iPos-1))); + edFoldername.text := sFolder; +end; + +end. diff --git a/UMod2DARowForm.ddp b/UMod2DARowForm.ddp new file mode 100644 index 0000000..4370276 Binary files /dev/null and b/UMod2DARowForm.ddp differ diff --git a/UMod2DARowForm.dfm b/UMod2DARowForm.dfm new file mode 100644 index 0000000..2a8a627 Binary files /dev/null and b/UMod2DARowForm.dfm differ diff --git a/UMod2DARowForm.pas b/UMod2DARowForm.pas new file mode 100644 index 0000000..a77906c --- /dev/null +++ b/UMod2DARowForm.pas @@ -0,0 +1,571 @@ +unit UMod2DARowForm; + +interface + +uses + Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, + Buttons, StdCtrls, Grids, ExtCtrls, U2DAEdit, UST_IniFile; + +type + TMod2DARowForm = class(TForm) + Panel1: TPanel; + grid2daMod: TStringGrid; + Button1: TButton; + GroupBox1: TGroupBox; + Label2: TLabel; + cbColLabel: TComboBox; + btnOpen2da: TSpeedButton; + Label3: TLabel; + btnAddToList: TSpeedButton; + Label1: TLabel; + btnEditColValue: TSpeedButton; + lblHeadline: TLabel; + OpenDlg: TOpenDialog; + btnDelColValue: TSpeedButton; + cbColValue: TComboBox; + Label4: TLabel; + Label5: TLabel; + Label6: TLabel; + Label7: TLabel; + Label8: TLabel; + Label9: TLabel; + Label10: TLabel; + Label11: TLabel; + GroupBox2: TGroupBox; + cbExclusiveLabel: TComboBox; + btnLoad2da: TSpeedButton; + Label12: TLabel; + procedure btnOpen2daClick(Sender: TObject); + procedure btnEditColValueClick(Sender: TObject); + procedure btnAddToListClick(Sender: TObject); + procedure btnDelColValueClick(Sender: TObject); + procedure btnTokensClick(Sender: TObject); + procedure cbColLabelKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure grid2daModKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure cbColValueKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure cbColValueDropDown(Sender: TObject); + procedure grid2daModDblClick(Sender: TObject); + procedure cbExclusiveLabelChange(Sender: TObject); + private + { Private declarations } + public + { Public declarations } + box_2daname : string; + box_labels : string; + box_section : string; + box_ini : TST_IniFile; + + procedure LoadIniData(); + procedure LoadColumnLabels(bPrompt : boolean; Sender : TObject); + function ShowInfoBox(sMessage : string) : word; + end; + +var + Mod2DARowForm: TMod2DARowForm; + +implementation + +{$R *.DFM} + +function TMod2DARowForm.ShowInfoBox(sMessage : string) : word; +begin + result := MessageDlgPos(sMessage, mtInformation, [mbOK], 0, Left + 64, Top + 128); +end; + +procedure TMod2DARowForm.LoadIniData(); +var + oList : TStringList; + i : integer; + iCnt : integer; + sKey : string; + sVal : string; +begin + grid2daMod.cols[0].clear(); + grid2daMod.cols[1].clear(); + grid2daMod.rowcount := 1; + grid2daMod.colwidths[0] := 82; + grid2daMod.colwidths[1] := grid2daMod.width - (grid2daMod.colwidths[0] + 6); + + cbColValue.clear(); + + if (box_2daname <> box_labels) then begin + cbColLabel.Color := clWindow; + cbExclusiveLabel.Color := clWindow; + cbExclusiveLabel.clear(); + cbColLabel.clear(); + + // ADDED(2006-07-08) Load column labels if possible when window opens + LoadColumnLabels(false, nil); + end; + + // - - - - - - - - - - - - - + // ADDED(2006-07-08) Load "ExclusiveColumn" separately. + cbExclusiveLabel.Text := box_ini.ReadString(box_section, 'ExclusiveColumn', ''); + if (cbExclusiveLabel.Text <> '') then begin + for i := 0 to (cbExclusiveLabel.Items.Count - 1) do begin + if (cbExclusiveLabel.Items[i] = cbExclusiveLabel.Text) then begin + cbExclusiveLabel.ItemIndex := i; + break; + end; + end; + end; + // - - - - - - - - - - - - - + + + oList := TStringList.Create(); + try + box_ini.readsection(box_section, oList); + + if (oList.Count > 0) then begin + iCnt := 0; + for i := 0 to (oList.count - 1) do begin + sKey := oList.strings[i]; + // CHANGED(2006-07-08) Skip "exclusivecolumn" key, handled separately. + if (sKey <> 'ExclusiveColumn') then begin + sVal := box_ini.readstring(box_section, sKey, ''); + if (sVal <> '') then begin + grid2daMod.rowcount := grid2daMod.rowcount + 1; + grid2daMod.cells[0, iCnt] := sKey; + grid2daMod.cells[1, iCnt] := sVal; + inc(iCnt); + end; + end; + end; + grid2daMod.rowcount := grid2daMod.rowcount - 1; + end; + finally + oList.free(); + end; +end; + +procedure TMod2DARowForm.btnOpen2daClick(Sender: TObject); +begin + LoadColumnLabels(true, Sender); +end; + + +procedure TMod2DARowForm.LoadColumnLabels(bPrompt : boolean; Sender : TObject); +var + o2da : T2DAHandler; + iCnt : integer; + i : integer; + sFile : string; + sTmp : string; +begin + sFile := ''; + sTmp := cbExclusiveLabel.Text; + + if (box_ini.FileName <> '') + and SysUtils.FileExists(box_ini.FileName) + and SysUtils.FileExists(ExtractFilePath(box_ini.FileName) + box_2daname) + then begin + sFile := ExtractFilePath(box_ini.FileName) + box_2daname; + end + else if bPrompt then begin + OpenDlg.Filter := '2DA file (*.2da)|*.2da'; + OpenDlg.DefaultExt := '2da'; + OpenDlg.FileName := box_2daname; + OpenDlg.Title := 'Open ' + box_2daname + ' to load column labels from.'; + + if OpenDlg.Execute then begin + sFile := OpenDlg.FileName; + end; + end + else if not bPrompt then begin + exit; + end; + + // cbColLabel + if (sFile <> '') then begin + Screen.Cursor := crHourglass; + if (Sender <> nil) and (Sender is TControl) then + TControl(Sender).Enabled := false; + Application.ProcessMessages(); + + o2da := T2DAHandler.Create(); + try + cbColLabel.Enabled := false; + cbExclusiveLabel.Enabled := false; + + o2da.Load2daFile(sFile); + box_labels := box_2daname; + iCnt := 0; + cbColLabel.items.clear(); + + cbExclusiveLabel.Items.clear(); + cbExclusiveLabel.Text := sTmp; + + // REMOVED(2006-07-08) Use separate box for this... + // cbColLabel.items.add('ExclusiveColumn'); + cbColLabel.Items.Add('RowLabel'); + cbExclusiveLabel.Items.Add(''); + + for i := 0 to (o2da.colcount-1) do begin + cbColLabel.Items.Add(o2da.clabels[i]); + // ADDED(2006-07-08) Update both column label dropdowns.... + cbExclusiveLabel.Items.Add(o2da.clabels[i]); + if (o2da.clabels[i] = sTmp) then + cbExclusiveLabel.ItemIndex := iCnt; + + inc(iCnt); + end; + + if (iCnt > 0) then begin + cbColLabel.itemindex := 0; + cbExclusiveLabel.itemindex := 0; + end; + + cbExclusiveLabel.Color := clMoneyGreen; + cbColLabel.Color := clMoneyGreen; + // cbColLabel.setfocus; + finally + o2da.free(); + Screen.Cursor := crDefault; + cbColLabel.Enabled := true; + cbExclusiveLabel.Enabled := true; + cbExclusiveLabel.Text := sTmp; + + if (Sender <> nil) and (Sender is TControl) then + TControl(Sender).Enabled := true; + end; + end; +end; + +procedure TMod2DARowForm.btnEditColValueClick(Sender: TObject); +var + sText : string; + i : integer; +begin + sText := grid2daMod.cells[0, grid2daMod.row]; + cbColLabel.text := sText; + cbColValue.text := grid2daMod.cells[1, grid2daMod.row]; + + for i := 0 to (cbColLabel.items.count-1) do begin + if (cbColLabel.Items[i] = sText) then begin + cbColLabel.itemindex := i; + break; + end; + end; + cbColValue.setfocus; +end; + +procedure TMod2DARowForm.btnAddToListClick(Sender: TObject); +var + i : integer; + bFound : boolean; +begin + // FIX(2005-05-24) Lägg inte till om nåt av fälten är tomma... + if (cbColLabel.text = '') or (cbColValue.text = '') then + exit; + + // Kolla om label redan finns i listan, ändra isf värdet och lägg inte + // till nån ny rad, samt uppdatera nyckelns värde i INI filen. + bFound := False; + for i := 0 to (grid2daMod.rowcount-1) do begin + if (cbColLabel.text = grid2daMod.cells[0, i]) then begin + grid2daMod.cells[1, i] := cbColValue.text; + grid2daMod.row := i; + box_ini.WriteString(box_section, cbColLabel.text, cbColValue.text); + cbColLabel.text := ''; + cbColValue.text := ''; + cbColLabel.setfocus(); + bFound := True; + break; + end; + end; + + // Ny label påträffad, lägg till den i listan och i INI filen... + if not bFound then begin + i := grid2daMod.rowcount; + grid2daMod.rowcount := i + 1; + + if (grid2daMod.cells[0, i-1] = '') and (grid2daMod.cells[0, i-1] = '') then begin + i := i - 1; + grid2daMod.rowcount := grid2daMod.rowcount - 1; + end; + + grid2daMod.cells[0, i] := cbColLabel.text; + grid2daMod.cells[1, i] := cbColValue.text; + grid2daMod.row := i; + + box_ini.WriteString(box_section, cbColLabel.text, cbColValue.text); + + cbColLabel.text := ''; + cbColValue.text := ''; + cbColLabel.setfocus(); + + end; +end; + +procedure TMod2DARowForm.btnDelColValueClick(Sender: TObject); +var + i : integer; +begin + // FIX(2005-05-24) Ta inte bort om raden är tom... + if (grid2daMod.cells[0, grid2daMod.Selection.top] = '') then + exit; + + if (grid2daMod.rowcount > 0) then begin + box_ini.deletekey(box_section, grid2daMod.cells[0, grid2daMod.Selection.top]); + + for i := grid2daMod.Selection.top to (grid2daMod.rowcount-1) do begin + grid2daMod.Rows[i] := grid2daMod.Rows[i+1]; + end; + grid2daMod.RowCount := grid2daMod.rowcount - 1; + end; +end; + +procedure TMod2DARowForm.btnTokensClick(Sender: TObject); +var + oList : TStringList; + oMods : TStringList; + oCols : TStringList; + i : integer; + n : integer; + j : integer; + sKey : string; + sVal : string; + sFile : string; + sMod : string; + sLabel : string; + bStop : boolean; +begin + // 1. Fetch all StringReference Tokens. + // 2. Fetch all 2DAMEMORY tokens that have had a value assigned to them thus far... + + // Clear ze box of any existing values, but keep anything the user has written + // or selected previously.... + sVal := cbColValue.text; + cbColValue.clear(); + cbColValue.text := sVal; + + // Add the special high() value to the list. + cbColValue.Items.Add('high()'); + + oList := TStringList.Create(); + box_ini.readsection('TLKList', oList); + + for i := 0 to (oList.Count - 1) do begin + sKey := oList.Strings[i]; + sVal := box_ini.readstring('TLKList', sKey, ''); + + if (sKey <> '') and (sVal <> '') then begin + if (copy(sKey, 1, 6) = 'StrRef') then begin + cbColValue.Items.Add(sKey); + end; + end; + end; + + oList.free(); + oList := TStringList.Create(); + box_ini.readsection('2DAList', oList); + bStop := False; + + // Check through all 2DA file modifiers for tokens being set... + for i := 0 to (oList.Count - 1) do begin + sFile := box_ini.readstring('2DAList', oList.Strings[i], ''); + if (sFile <> '') then begin + oMods := TStringList.Create(); + try + // Check all modifiers for this particular file... + box_ini.readsection(sFile, oMods); + for n := 0 to (oMods.count - 1) do begin + sMod := oMods.Strings[n]; + sLabel := box_ini.readstring(sFile, sMod, ''); + + if (sLabel <> '') then begin + oCols := TStringList.Create(); + try + // Check each column label for this Modifier... + box_ini.ReadSection(sLabel, oCols); + for j := 0 to (oCols.count-1) do begin + sKey := oCols.strings[j]; + sVal := box_ini.readstring(sLabel, sKey, ''); + + // Check if the KEY is a unique 2DAMEMORY token + // instead of a column label. If so add to list. + if (sKey <> '') + and (copy(sKey, 1, 9) = '2DAMEMORY') + and (cbColValue.Items.IndexOf(sKey) = -1) + then begin + cbColValue.Items.Add(sKey); + end; + end; + finally + oCols.free(); + end; + end; + + // Don't parse tokens that are set after this modifier has been executed. + // Modifier sections coming after the current one should be skipped. + if (sLabel = box_section) then begin + bStop := True; + break; + end; + end; + finally + oMods.free(); + end; + end; + + // Don't parse tokens that are set after this modifier has been executed. + // Files coming after the current one should be skipped. + if (bStop = True) or (sFile = box_2daname) then + break; + end; + + ShowInfoBox('Relevant tokens have been loaded into the dropdown list of the Value box.'); + cbColValue.setfocus; +end; + +procedure TMod2DARowForm.cbColLabelKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (Shift = [ssCtrl, ssShift]) and (key = VK_RIGHT) then begin + btnAddToListClick(btnAddToList); + end; +end; + +procedure TMod2DARowForm.grid2daModKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (Shift = [ssCtrl, ssShift]) and (key = VK_LEFT) then begin + btnEditColValueClick(btnEditColValue); + end; +end; + +procedure TMod2DARowForm.cbColValueKeyUp(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if (Shift = [ssCtrl, ssShift]) and (key = VK_RIGHT) then begin + btnAddToListClick(btnAddToList); + end; +end; + +procedure TMod2DARowForm.cbColValueDropDown(Sender: TObject); +var + oList : TStringList; + oMods : TStringList; + oCols : TStringList; + i : integer; + n : integer; + j : integer; + sKey : string; + sVal : string; + sFile : string; + sMod : string; + sLabel : string; + bStop : boolean; + oOldCursor : TCursor; +begin + // 1. Fetch all StringReference Tokens. + // 2. Fetch all 2DAMEMORY tokens that have had a value assigned to them thus far... + + // Clear ze box of any existing values, but keep anything the user has written + // or selected previously.... + sVal := cbColValue.text; + cbColValue.clear(); + cbColValue.text := sVal; + + // Add the special high() value to the list. + cbColValue.Items.Add('high()'); + + oOldCursor := Screen.Cursor; + Screen.Cursor := crHourglass; + Application.ProcessMessages(); + try + oList := TStringList.Create(); + box_ini.readsection('TLKList', oList); + + for i := 0 to (oList.Count - 1) do begin + sKey := oList.Strings[i]; + sVal := box_ini.readstring('TLKList', sKey, ''); + + if (sKey <> '') and (sVal <> '') then begin + if (copy(sKey, 1, 6) = 'StrRef') then begin + cbColValue.Items.Add(sKey); + end; + end; + end; + + oList.free(); + oList := TStringList.Create(); + box_ini.readsection('2DAList', oList); + bStop := False; + + // Check through all 2DA file modifiers for tokens being set... + for i := 0 to (oList.Count - 1) do begin + sFile := box_ini.readstring('2DAList', oList.Strings[i], ''); + if (sFile <> '') then begin + oMods := TStringList.Create(); + try + // Check all modifiers for this particular file... + box_ini.readsection(sFile, oMods); + for n := 0 to (oMods.count - 1) do begin + sMod := oMods.Strings[n]; + sLabel := box_ini.readstring(sFile, sMod, ''); + + if (sLabel <> '') then begin + oCols := TStringList.Create(); + try + // Check each column label for this Modifier... + box_ini.ReadSection(sLabel, oCols); + for j := 0 to (oCols.count-1) do begin + sKey := oCols.strings[j]; + sVal := box_ini.readstring(sLabel, sKey, ''); + + // Check if the KEY is a unique 2DAMEMORY token + // instead of a column label. If so add to list. + if (sKey <> '') + and (copy(sKey, 1, 9) = '2DAMEMORY') + and (cbColValue.Items.IndexOf(sKey) = -1) + then begin + cbColValue.Items.Add(sKey); + end; + end; + finally + oCols.free(); + end; + end; + + // Don't parse tokens that are set after this modifier has been executed. + // Modifier sections coming after the current one should be skipped. + if (sLabel = box_section) then begin + bStop := True; + break; + end; + end; + finally + oMods.free(); + end; + end; + + // Don't parse tokens that are set after this modifier has been executed. + // Files coming after the current one should be skipped. + if (bStop = True) or (sFile = box_2daname) then + break; + end; + finally + Screen.Cursor := oOldCursor; + end; +end; + +procedure TMod2DARowForm.grid2daModDblClick(Sender: TObject); +begin + btnEditColValueClick(Sender); +end; + +procedure TMod2DARowForm.cbExclusiveLabelChange(Sender: TObject); +begin + if (cbExclusiveLabel.Text = '') then begin + box_ini.DeleteKey(box_section, 'ExclusiveColumn'); + end + else begin + box_ini.WriteString(box_section, 'ExclusiveColumn', cbExclusiveLabel.Text); + end; +end; + +end. diff --git a/UModLabelForm.ddp b/UModLabelForm.ddp new file mode 100644 index 0000000..4370276 Binary files /dev/null and b/UModLabelForm.ddp differ diff --git a/UModLabelForm.dfm b/UModLabelForm.dfm new file mode 100644 index 0000000..2c3f8f2 Binary files /dev/null and b/UModLabelForm.dfm differ diff --git a/UModLabelForm.pas b/UModLabelForm.pas new file mode 100644 index 0000000..fe00458 --- /dev/null +++ b/UModLabelForm.pas @@ -0,0 +1,53 @@ +unit UModLabelForm; + +interface + +uses + Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, + StdCtrls, ExtCtrls; + +type + TNewLabelForm = class(TForm) + Panel1: TPanel; + lblTitle: TLabel; + Label2: TLabel; + Label3: TLabel; + Label4: TLabel; + Label5: TLabel; + Label6: TLabel; + Label7: TLabel; + Bevel1: TBevel; + Label8: TLabel; + edModLabel: TEdit; + Button1: TButton; + Button2: TButton; + procedure FormShow(Sender: TObject); + private + { Private declarations } + function GetLabel() : string; + public + { Public declarations } + + property ModLabel : string read GetLabel; + end; + +var + NewLabelForm: TNewLabelForm; + +implementation + +{$R *.DFM} + + +function TNewLabelForm.GetLabel() : string; +begin + result := edModLabel.Text; +end; + + +procedure TNewLabelForm.FormShow(Sender: TObject); +begin + edModLabel.setfocus(); +end; + +end. diff --git a/UNamespaceForm.ddp b/UNamespaceForm.ddp new file mode 100644 index 0000000..4370276 Binary files /dev/null and b/UNamespaceForm.ddp differ diff --git a/UNamespaceForm.dfm b/UNamespaceForm.dfm new file mode 100644 index 0000000..d32bc94 --- /dev/null +++ b/UNamespaceForm.dfm @@ -0,0 +1,448 @@ +object NamespaceForm: TNamespaceForm + Left = 224 + Top = 229 + BorderStyle = bsDialog + Caption = 'Setup List/Namespace Editor' + ClientHeight = 299 + ClientWidth = 594 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'MS Sans Serif' + Font.Style = [] + OldCreateOrder = False + OnCreate = FormCreate + OnHide = FormHide + OnShow = FormShow + DesignSize = ( + 594 + 299) + PixelsPerInch = 96 + TextHeight = 13 + object paneBackground: TPanel + Left = 0 + Top = 0 + Width = 594 + Height = 299 + Anchors = [akLeft, akTop, akRight, akBottom] + BevelInner = bvRaised + BevelOuter = bvLowered + TabOrder = 0 + object btnAdd: TSpeedButton + Left = 168 + Top = 8 + Width = 23 + Height = 22 + Hint = 'Add a new Setup.' + Glyph.Data = { + 76010000424D7601000000000000760000002800000020000000100000000100 + 04000000000000010000130B0000130B00001000000000000000000000000000 + 800000800000008080008000000080008000808000007F7F7F00BFBFBF000000 + FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333333333333 + 33333333FF33333333FF333993333333300033377F3333333777333993333333 + 300033F77FFF3333377739999993333333333777777F3333333F399999933333 + 33003777777333333377333993333333330033377F3333333377333993333333 + 3333333773333333333F333333333333330033333333F33333773333333C3333 + 330033333337FF3333773333333CC333333333FFFFF77FFF3FF33CCCCCCCCCC3 + 993337777777777F77F33CCCCCCCCCC3993337777777777377333333333CC333 + 333333333337733333FF3333333C333330003333333733333777333333333333 + 3000333333333333377733333333333333333333333333333333} + NumGlyphs = 2 + ParentShowHint = False + ShowHint = True + OnClick = btnAddClick + end + object btnDelete: TSpeedButton + Left = 136 + Top = 8 + Width = 23 + Height = 22 + Hint = 'Delete the selected Setup' + Glyph.Data = { + 76010000424D7601000000000000760000002800000020000000100000000100 + 04000000000000010000120B0000120B00001000000000000000000000000000 + 800000800000008080008000000080008000808000007F7F7F00BFBFBF000000 + FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333000000000 + 3333333777777777F3333330F777777033333337F3F3F3F7F3333330F0808070 + 33333337F7F7F7F7F3333330F080707033333337F7F7F7F7F3333330F0808070 + 33333337F7F7F7F7F3333330F080707033333337F7F7F7F7F3333330F0808070 + 333333F7F7F7F7F7F3F33030F080707030333737F7F7F7F7F7333300F0808070 + 03333377F7F7F7F773333330F080707033333337F7F7F7F7F333333070707070 + 33333337F7F7F7F7FF3333000000000003333377777777777F33330F88877777 + 0333337FFFFFFFFF7F3333000000000003333377777777777333333330777033 + 3333333337FFF7F3333333333000003333333333377777333333} + NumGlyphs = 2 + ParentShowHint = False + ShowHint = True + OnClick = btnDeleteClick + end + object lblbList: TLabel + Left = 8 + Top = 16 + Width = 66 + Height = 13 + Caption = 'Setup groups:' + end + object listNS: TListView + Left = 8 + Top = 32 + Width = 185 + Height = 257 + Hint = 'Select a setup to edit in this list.' + Columns = <> + HideSelection = False + HotTrackStyles = [htHandPoint] + ReadOnly = True + ParentShowHint = False + ShowHint = True + SmallImages = imgIcon + TabOrder = 0 + ViewStyle = vsList + OnSelectItem = listNSSelectItem + end + object paneEdit: TPanel + Left = 200 + Top = 32 + Width = 385 + Height = 257 + BevelInner = bvLowered + TabOrder = 1 + object lblIniName: TLabel + Left = 20 + Top = 19 + Width = 78 + Height = 13 + Hint = + 'Name of the INI file containing patcher instructions for this Se' + + 'tup.' + Alignment = taRightJustify + Caption = 'Config file name:' + ParentShowHint = False + ShowHint = True + end + object lblInfoName: TLabel + Left = 32 + Top = 51 + Width = 66 + Height = 13 + Hint = + 'Name of the RTF file containing main window instruction text for' + + ' this setup.' + Alignment = taRightJustify + Caption = 'Info file name:' + ParentShowHint = False + ShowHint = True + end + object lblName: TLabel + Left = 38 + Top = 115 + Width = 60 + Height = 13 + Hint = + 'Name of this setup, displayed in the selection menu when TSLPatc' + + 'her starts.' + Alignment = taRightJustify + Caption = 'Setup name:' + ParentShowHint = False + ShowHint = True + end + object lblDescription: TLabel + Left = 11 + Top = 147 + Width = 87 + Height = 13 + Hint = + 'Short descriptive text of this Setup, shown when the user select' + + 's this Setup in the dropdown menu in TSLPatcher.' + Alignment = taRightJustify + Caption = 'Setup Description:' + ParentShowHint = False + ShowHint = True + end + object lblDataPath: TLabel + Left = 23 + Top = 83 + Width = 75 + Height = 13 + Hint = + 'Name of a folder within the "tslpatchdata" folder where the data' + + ' files for this Setup are located. Leave blank to use tslpatchda' + + 'ta.' + Alignment = taRightJustify + Caption = 'Data sub-folder:' + ParentShowHint = False + ShowHint = True + end + object btnSelINI: TSpeedButton + Left = 344 + Top = 16 + Width = 23 + Height = 22 + Flat = True + Glyph.Data = { + 76010000424D7601000000000000760000002800000020000000100000000100 + 04000000000000010000120B0000120B00001000000000000000000000000000 + 800000800000008080008000000080008000808000007F7F7F00BFBFBF000000 + FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00555555555555 + 55555555FFFFFFFFFF55555000000000055555577777777775F55500B8B8B8B8 + B05555775F555555575F550F0B8B8B8B8B05557F75F555555575550BF0B8B8B8 + B8B0557F575FFFFFFFF7550FBF0000000000557F557777777777500BFBFBFBFB + 0555577F555555557F550B0FBFBFBFBF05557F7F555555FF75550F0BFBFBF000 + 55557F75F555577755550BF0BFBF0B0555557F575FFF757F55550FB700007F05 + 55557F557777557F55550BFBFBFBFB0555557F555555557F55550FBFBFBFBF05 + 55557FFFFFFFFF7555550000000000555555777777777755555550FBFB055555 + 5555575FFF755555555557000075555555555577775555555555} + NumGlyphs = 2 + OnClick = btnSelINIClick + end + object btnSelInfo: TSpeedButton + Left = 344 + Top = 48 + Width = 23 + Height = 22 + Flat = True + Glyph.Data = { + 76010000424D7601000000000000760000002800000020000000100000000100 + 04000000000000010000120B0000120B00001000000000000000000000000000 + 800000800000008080008000000080008000808000007F7F7F00BFBFBF000000 + FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00555555555555 + 55555555FFFFFFFFFF55555000000000055555577777777775F55500B8B8B8B8 + B05555775F555555575F550F0B8B8B8B8B05557F75F555555575550BF0B8B8B8 + B8B0557F575FFFFFFFF7550FBF0000000000557F557777777777500BFBFBFBFB + 0555577F555555557F550B0FBFBFBFBF05557F7F555555FF75550F0BFBFBF000 + 55557F75F555577755550BF0BFBF0B0555557F575FFF757F55550FB700007F05 + 55557F557777557F55550BFBFBFBFB0555557F555555557F55550FBFBFBFBF05 + 55557FFFFFFFFF7555550000000000555555777777777755555550FBFB055555 + 5555575FFF755555555557000075555555555577775555555555} + NumGlyphs = 2 + OnClick = btnSelInfoClick + end + object edIniName: TEdit + Left = 104 + Top = 16 + Width = 233 + Height = 21 + Hint = + 'Name of the INI file containing patcher instructions for this Se' + + 'tup.' + ParentShowHint = False + ShowHint = True + TabOrder = 0 + Text = 'changes.ini' + end + object edInfoName: TEdit + Left = 104 + Top = 48 + Width = 233 + Height = 21 + Hint = + 'Name of the RTF file containing main window instruction text for' + + ' this setup.' + ParentShowHint = False + ShowHint = True + TabOrder = 1 + Text = 'info.rtf' + end + object edName: TEdit + Left = 104 + Top = 112 + Width = 265 + Height = 21 + Hint = + 'Name of this setup, displayed in the selection menu when TSLPatc' + + 'her starts.' + ParentShowHint = False + ShowHint = True + TabOrder = 3 + end + object edDescription: TMemo + Left = 104 + Top = 144 + Width = 265 + Height = 65 + Hint = + 'Short descriptive text of this Setup, shown when the user select' + + 's this Setup in the dropdown menu in TSLPatcher.' + ParentShowHint = False + ShowHint = True + TabOrder = 4 + WantReturns = False + end + object btnSave: TButton + Left = 8 + Top = 224 + Width = 105 + Height = 25 + Hint = 'Save the data entered or modified for this Setup.' + Caption = '<< &Save changes' + ParentShowHint = False + ShowHint = True + TabOrder = 5 + OnClick = btnSaveClick + end + object edDataPath: TEdit + Left = 104 + Top = 80 + Width = 265 + Height = 21 + Hint = + 'Name of a folder within the "tslpatchdata" folder where the data' + + ' files for this Setup are located. Leave blank to use tslpatchda' + + 'ta.' + ParentShowHint = False + ShowHint = True + TabOrder = 2 + end + end + end + object imgIcon: TImageList + Left = 544 + Top = 8 + Bitmap = { + 494C010101000400040010001000FFFFFFFFFF10FFFFFFFFFFFFFFFF424D3600 + 0000000000003600000028000000400000001000000001002000000000000010 + 00000000000000000000000000000000000000000000E0DCDB00ADA29C007766 + 5D006A594F006D5C52007C6C6300897B7300968A8300A79D9700B7AFAA00CEC9 + C500E0DDDC000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000CFB8B800ECE8E100F4F5 + EB00E9E7DF00C9C4BC00B5ADA500A3989000877A6F0076665D00635046007868 + 5F00B2A9A400E4E0E00000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000D0B2B100F7F0CA00FFFF + D500FFFFD900FFFFDD00FFFFE200FFFFE600FFFFEB00FFFFEF00FFFFF300A296 + 9100998E8800D8D4D400EFEEEE00000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000D0B2B100F7F0CD00FAF0 + C500F9EDC400FEFCDD00FFFFE500FFFFEA00FFFFEE00FFFFF200FFFFF600A59C + 9600897E7900C6C2C300DFDEE000EDEDEE000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000D0B2B100F7F0D000FAF0 + C700F9EDC700FAF2D300FBF3D900FEFDEA00FFFFF000FFFFF500FFFFF900A196 + 9100665D5E00958F9400AFADB300DEDDDF000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000D0B2B100F7F0D200FAF2 + CF00FAF0CF00F8ECCD00F2D8B500F7E7CD00D0BFB100F9EDDD00164E95002A3D + 56004038450049465A00756E7100C5C2C5000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000D0B2B100F7F0D400F5E1 + B900F3DBB300F4DDB900F7E7CC00D5D0C6000F60A100406DA7003AB6E900216E + A60019589A002B6E9A00584D4A00918D92000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000D0B2B100F7F0D800EDC8 + 9800E9BD8A00F2D9B600F1D5B200A1BDD70050BEF2003EB0E40038B5E80028A7 + DB001DA5D9002E9CCB003B405A00777273000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000D0B2B100F7F0DA00F5E1 + BD00F3DBB700F4DEBF00FAF0DD00156DAA004FBEF20045BBEE0054ABCF003081 + AA000E8BC600098EC4002977A000807F7F000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000D0B2B100F7F0DD00FFFF + EC00FFFFEF00FFFFF400FAFDF80075D0FB0052C2F50065C7F100AAAAAA004558 + 62000E91CA000A9ED1003FACD400C1C1C1000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000D0B2B100F7F0DF00FAF0 + D800F9EDD600F9EDDA00F9EDDE00FEFCF90041A8E10063C5F000AAAAAA00475E + 67001BA6D9004596B600A9ADAF00DCDDDE000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000D0B2B100F7F0E200FFFF + F200FFFFF600FBF5EB00FBF3EA00FEFDFB00B0E3F900A0CDE100898480006067 + 690044A4CE0095B8C800DADADA00EDEEEF000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000D1B3B200F7F0E400FAF0 + DE00F9EDDD00F9EDE000F9EDE100FEFCFA000000000000000000EFECEA007F77 + 720091857F00D3CFCE00ECECEB00000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000D3B5B500ECD6CF006BB3 + CC00EAD0CF00ADC9D800CEB4B800DBECF200BAD0DD00F7FAFC00E0E8EE00ADA4 + A0009E938E00DFDBDA0000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 00000000000000000000000000000000000000000000E0D0D0006A798B003489 + A800878594002A97B500A36D74006B95AE00A596A1007EA6B8004C9CB8009680 + 8200C0BBB900E6E4E30000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000000000000000000097CAD100CDE2 + E5007CBCC500B3D9DE0054B0BE008AC8D1005DB3C10063B8C40057ACB6000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 0000000000000000000000000000000000000000000000000000000000000000 + 000000000000000000000000000000000000424D3E000000000000003E000000 + 2800000040000000100000000100010000000000800000000000000000000000 + 000000000000000000000000FFFFFF0080070000000000008003000000000000 + 8001000000000000800000000000000080000000000000008000000000000000 + 8000000000000000800000000000000080000000000000008000000000000000 + 8000000000000000800000000000000080C10000000000008003000000000000 + 8003000000000000C01F00000000000000000000000000000000000000000000 + 000000000000} + end + object dlgOpen: TOpenDialog + DefaultExt = 'ini' + Filter = 'INI files (*.ini)|*.ini' + Left = 504 + Top = 8 + end +end diff --git a/UNamespaceForm.pas b/UNamespaceForm.pas new file mode 100644 index 0000000..1275acd --- /dev/null +++ b/UNamespaceForm.pas @@ -0,0 +1,350 @@ +unit UNamespaceForm; + +interface + +uses + Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, + Dialogs, StdCtrls, ImgList, ComCtrls, ExtCtrls, Buttons, UST_IniFile; + +type + TNamespaceForm = class(TForm) + paneBackground: TPanel; + listNS: TListView; + imgIcon: TImageList; + paneEdit: TPanel; + edIniName: TEdit; + edInfoName: TEdit; + edName: TEdit; + edDescription: TMemo; + btnSave: TButton; + lblIniName: TLabel; + lblInfoName: TLabel; + lblName: TLabel; + lblDescription: TLabel; + btnAdd: TSpeedButton; + btnDelete: TSpeedButton; + edDataPath: TEdit; + lblDataPath: TLabel; + lblbList: TLabel; + dlgOpen: TOpenDialog; + btnSelINI: TSpeedButton; + btnSelInfo: TSpeedButton; + procedure FormShow(Sender: TObject); + procedure FormCreate(Sender: TObject); + procedure FormHide(Sender: TObject); + procedure listNSSelectItem(Sender: TObject; Item: TListItem; + Selected: Boolean); + procedure btnDeleteClick(Sender: TObject); + procedure btnAddClick(Sender: TObject); + procedure btnSaveClick(Sender: TObject); + procedure btnSelINIClick(Sender: TObject); + procedure btnSelInfoClick(Sender: TObject); + private + { Private declarations } + f_file : string; + f_ini : TST_IniFile; + public + { Public declarations } + function ShowAlertBox(sMessage : string) : word; + function ShowConfirmBox(sMessage : string) : word; + procedure LoadListData(); + procedure EnableEditFields(bEnable : boolean); + procedure ClearEditFields(); + + property Filename : string read f_file write f_file; + end; + +var + NamespaceForm: TNamespaceForm; + +implementation + +uses UModLabelForm; + +{$R *.dfm} + + +function TNamespaceForm.ShowAlertBox(sMessage : string) : word; +begin + result := MessageDlg(sMessage, mtWarning, [mbOK], 0); +end; + +function TNamespaceForm.ShowConfirmBox(sMessage : string) : word; +begin + result := MessageDlg(sMessage, mtConfirmation, [mbYes, mbNo], 0); +end; + + +procedure TNamespaceForm.LoadListData(); +var + oList : TStringList; + oItem : TListItem; + i : integer; + sKey : string; + sVal : string; +begin + listNS.Clear(); + + if (f_ini <> nil) then begin + oList := TStringList.Create(); + f_ini.ReadSection('Namespaces', oList); + + for i := 0 to (oList.Count - 1) do begin + sKey := oList.Strings[i]; + sVal := f_ini.ReadString('Namespaces', sKey, ''); + + if (sVal <> '') then begin + oItem := listNS.Items.Add(); + oItem.Caption := sVal; + oItem.ImageIndex := 0; + end; + end; + + oList.Free(); + end; +end; + + +procedure TNamespaceForm.EnableEditFields(bEnable : boolean); +begin + lblIniName.Enabled := bEnable; + lblInfoName.Enabled := bEnable; + lblName.Enabled := bEnable; + lblDataPath.Enabled := bEnable; + lblDescription.Enabled := bEnable; + + edIniName.Enabled := bEnable; + edInfoName.Enabled := bEnable; + edName.Enabled := bEnable; + edDataPath.Enabled := bEnable; + edDescription.Enabled := bEnable; + + btnSave.Enabled := bEnable; +end; + + +procedure TNamespaceForm.ClearEditFields(); +begin + edIniName.Clear(); + edInfoName.Clear(); + edName.Clear(); + edDataPath.Clear(); + edDescription.Clear(); +end; + + + +procedure TNamespaceForm.FormShow(Sender: TObject); +begin + if (f_file <> '') then begin + f_ini := TST_IniFile.Create(f_file); + ClearEditFields(); + LoadListData(); + EnableEditFields(false); + end; +end; + + +procedure TNamespaceForm.FormCreate(Sender: TObject); +begin + f_ini := nil; +end; + + +procedure TNamespaceForm.FormHide(Sender: TObject); +begin + if (f_ini <> nil) then begin + f_ini.Free(); + f_ini := nil; + end; +end; + + +procedure TNamespaceForm.listNSSelectItem(Sender: TObject; Item: TListItem; + Selected: Boolean); +begin + if not NamespaceForm.Visible then + exit; + + ClearEditFields(); + EnableEditFields(true); + + NamespaceForm.Caption := 'Setup List/Namespace Editor [' + Item.Caption + ']'; + + edIniName.Text := f_ini.ReadString(Item.Caption, 'IniName', ''); + edInfoName.Text := f_ini.ReadString(Item.Caption, 'InfoName', ''); + edName.Text := f_ini.ReadString(Item.Caption, 'Name', ''); + edDataPath.Text := f_ini.ReadString(Item.Caption, 'DataPath', ''); + edDescription.Text := f_ini.ReadString(Item.Caption, 'Description', ''); +end; + + +procedure TNamespaceForm.btnDeleteClick(Sender: TObject); +var + oList : TStringList; + i : integer; + sKey : string; + sVal : string; +begin + if (listNS.SelCount > 0) then begin + if (ShowConfirmBox('Are you sure you wish to delete the Setup "' + listNS.Selected.Caption + '"?') = mrYes) then begin + ClearEditFields(); + EnableEditFields(false); + + f_ini.EraseSection(listNS.Selected.Caption); + + oList := TStringList.Create(); + f_ini.ReadSection('Namespaces', oList); + + for i := 0 to (oList.Count - 1) do begin + sKey := oList.Strings[i]; + sVal := f_ini.ReadString('Namespaces', sKey, ''); + + if (sVal = listNS.Selected.Caption) then begin + f_ini.DeleteKey('Namespaces', sKey); + break; + end; + end; + + listNS.DeleteSelected(); + end; + end; +end; + +procedure TNamespaceForm.btnAddClick(Sender: TObject); +var + sLabel : string; + oList : TStringList; + oItem : TListItem; + sBase : string; + iCnt : integer; + bFound : boolean; + i : integer; +begin + if (NewLabelForm.ShowModal() = mrOk) then begin + sLabel := NewLabelForm.ModLabel; + + // Check that the selected section label isn't already used. + if f_ini.SectionExists(sLabel) then begin + ShowAlertBox('This label is already used by another Setup! Please pick another.'); + btnAddClick(btnAdd); + exit; + end; + + oList := TStringList.Create(); + f_ini.ReadSection('Namespaces', oList); + + // Find the next free key to use in the Namespaces section + iCnt := 1; + repeat + bFound := false; + sBase := 'Namespace' + IntToStr(iCnt); + for i := 0 to (oList.Count - 1) do begin + if (oList.Strings[i] = sBase) then begin + bFound := true; + break; + end; + end; + inc(iCnt); + until not bFound; + + // Add the keys to the INI file + f_ini.WriteString('Namespaces', sBase, sLabel); + f_ini.WriteString(sLabel, 'IniName', ''); + f_ini.WriteString(sLabel, 'InfoName', ''); + f_ini.WriteString(sLabel, 'DataPath', ''); + f_ini.WriteString(sLabel, 'Name', ''); + f_ini.WriteString(sLabel, 'Description', ''); + + // Add the Namespace to the GUI List. + oItem := listNS.Items.Add; + oItem.Caption := sLabel; + listNS.Selected := oItem; + listNS.ItemFocused := oItem; + edIniName.Text := 'changes.ini'; + edInfoName.Text := 'info.rtf'; + edIniName.SetFocus(); + edIniName.SelectAll(); + end; +end; + +procedure TNamespaceForm.btnSaveClick(Sender: TObject); +var + oItem : TListItem; +begin + oItem := listNS.Selected; + if (listNS.SelCount > 0) and (oItem <> nil) then begin + if (Length(edIniName.Text) < 5) then begin + ShowAlertBox('The Config File Name box must be set to the name of an INI file with TSLPatcher configuration settings!'); + exit; + end; + + if (Length(edInfoName.Text) < 5) then begin + ShowAlertBox('The Info File Name box must be set to the name of an RTF format document with ReadMe/Installation instructions for the TSLPatcher to show the user!'); + exit; + end; + + if (Length(edName.Text) < 1) then begin + ShowAlertBox('The Setup Name box must be set to a name to display to the user in the dropdown menu when they select what to install!'); + exit; + end; + + if (Length(edDescription.Text) < 1) then begin + ShowAlertBox('The Description box must be set to a short description of what this particular setup is! This will be displayed to the user when they select a Setup in the dropdown menu.'); + exit; + end; + + f_ini.WriteString(oItem.Caption, 'IniName', edIniName.Text); + f_ini.WriteString(oItem.Caption, 'InfoName', edInfoName.Text); + f_ini.WriteString(oItem.Caption, 'DataPath', edDataPath.Text); + f_ini.WriteString(oItem.Caption, 'Name', edName.Text); + f_ini.WriteString(oItem.Caption, 'Description', edDescription.Text); + + ClearEditFields(); + listNS.SetFocus(); + // ADDED(2006-07-08) Deselect current list entry to allow selecting it again. + listNS.Selected := nil; + end; +end; + +procedure TNamespaceForm.btnSelINIClick(Sender: TObject); +begin + dlgOpen.Title := 'Select an INI installer config file to use for this setup:'; + dlgOpen.Filter := 'INI files (*.ini)|*.ini'; + dlgOpen.FilterIndex := 1; + dlgOpen.DefaultExt := 'ini'; + dlgOpen.InitialDir := ExtractFilePath(f_file); + if (edIniName.Text <> '') then + dlgOpen.FileName := edIniName.Text + else + dlgOpen.FileName := 'changes.ini'; + + if dlgOpen.Execute() then begin + edIniName.Text := ExtractFileName(dlgOpen.FileName); + edIniName.SetFocus(); + edIniName.SelectAll(); + end; +end; + +procedure TNamespaceForm.btnSelInfoClick(Sender: TObject); +begin + dlgOpen.Title := 'Select an RTF file to use as main window information text:'; + dlgOpen.Filter := 'Rich Text Format files (*.rtf)|*.rtf'; + dlgOpen.FilterIndex := 1; + dlgOpen.DefaultExt := 'rtf'; + dlgOpen.InitialDir := ExtractFilePath(f_file); + dlgOpen.FileName := 'info.rtf'; + if (edInfoName.Text <> '') then + dlgOpen.FileName := edInfoName.Text + else + dlgOpen.FileName := 'info.rtf'; + + + if dlgOpen.Execute() then begin + edInfoName.Text := ExtractFileName(dlgOpen.FileName); + edInfoName.SetFocus(); + edInfoName.SelectAll(); + end; +end; + +end. diff --git a/docs/plans/2026-05-27-005-wiki-submodule-and-user-docs-plan.md b/docs/plans/2026-05-27-005-wiki-submodule-and-user-docs-plan.md new file mode 100644 index 0000000..ba7bcb9 --- /dev/null +++ b/docs/plans/2026-05-27-005-wiki-submodule-and-user-docs-plan.md @@ -0,0 +1,91 @@ +# Plan: Add Wiki Submodule and Project Wiki + +Status: completed +Date: 2026-05-28 +Scope: top-level `wiki/` submodule + thorough GitHub wiki content derived from repo evidence +Origin: direct user request to investigate the repo and create the wiki using `gh` and `git` + +## Problem Frame + +ChangeEdit already has strong repo-local documentation in root markdown files, but it does not yet expose that material through a checked-out wiki repository in the superproject. The requested work is not only to add the `wiki/` submodule, but to investigate the codebase thoroughly enough that the wiki becomes a durable contributor and user reference rather than a thin mirror of `README.md`. + +## Requirements Traceability + +1. Create and wire a GitHub wiki for this repository as a top-level submodule at `wiki/`. +2. Use GitHub CLI and git CLI for setup, verification, and publication rather than hand-waving the wiki bootstrap. +3. Investigate the codebase and existing repo docs deeply enough to cover product purpose, architecture, workflows, formats, build/run expectations, contribution guidance, and known limitations. +4. Keep repo changes focused on markdown content, submodule metadata, and any minimal repo links needed to make the wiki discoverable. +5. Push the wiki repository content and commit the superproject gitlink so collaborators can clone the wiki working tree locally. + +## Key Decisions + +1. Treat the root markdown files (`README.md`, `ARCHITECTURE.md`, `BUILD.md`, `CONTRIBUTING.md`, `CONVENTIONS.md`, `FORMATS.md`, `TODO.txt`) plus the core Delphi units as the primary evidence sources for wiki content. +2. Keep the wiki user-facing and navigable, but do not flatten important technical constraints such as Delphi 7 only support, Wine-based Linux/macOS workflows, startup-created form singletons, and known binary-format write limitations. +3. Use standard GitHub wiki entry points (`Home.md` and `_Sidebar.md`) plus topic pages instead of a single mega-page. +4. Preserve a clear separation between the superproject and the wiki repository: wiki page commits belong inside `wiki/`, while submodule pointer and `.gitmodules` changes belong in the main repository. +5. Prefer the existing root docs as source-of-truth references where practical, with the wiki acting as the curated navigation layer and public-facing knowledge surface. + +## Implementation Units + +1. IU-1 Repo investigation and evidence capture + +- Read the existing high-signal docs and the code surfaces that directly define application behavior and constraints. +- Minimum investigation set: `README.md`, `ARCHITECTURE.md`, `BUILD.md`, `CONTRIBUTING.md`, `CONVENTIONS.md`, `FORMATS.md`, `TODO.txt`, `ChangEd.dpr`, `UMainForm.pas`, `U2DAEdit.pas`, `UGFFFile.pas`, `UTLKFile.pas`, `UST_IniFile.pas`, and `UST_Common.pas`. +- Extract the facts the wiki must preserve: supported editing areas, build/runtime constraints, file-format scope, known gaps, contribution workflow, and manual validation expectations. + +1. IU-2 Wiki remote bootstrap and submodule setup + +- Verify that the GitHub repository has wiki support enabled. +- Resolve the wiki git remote from the repository origin and initialize or connect the wiki repository through git. +- Add the wiki repository as a top-level submodule at `wiki/` and verify the resulting `.gitmodules` entry and gitlink. +- If the wiki remote is empty, seed it with an initial commit before the superproject pointer is finalized. + +1. IU-3 Wiki information architecture and page set + +- Create `wiki/Home.md` as the landing page with project summary, audience, and page map. +- Create `wiki/_Sidebar.md` so GitHub renders stable wiki navigation. +- Create a page set that covers, at minimum: + - what ChangeEdit is and who it is for + - how the main window and section tree map to editing tasks + - how TLK, 2DA, GFF, SSF, script, install, and namespace editing fit into the product + - build and run expectations on Windows and on Wine-hosted Linux/macOS setups + - known architectural limits, unsupported edges, and manual testing expectations + - contributor-oriented entry points back to the root repo docs +- Keep language direct and readable, but accurate enough for maintainers and mod authors. + +1. IU-4 Cross-linking and repo discoverability + +- Add only the minimal supporting repo-side references needed so contributors can discover the checked-out wiki workflow. +- Preserve the root docs as canonical detailed references rather than duplicating every technical detail into the wiki. + +1. IU-5 Validation, publication, and handoff + +- Validate markdown structure, internal links, and navigation completeness inside the wiki working tree. +- Validate that the wiki remote contains the authored pages and that the superproject points at the expected wiki commit. +- Commit and push the wiki repository first, then commit the superproject submodule pointer and any supporting repo-side documentation updates. + +## File Plan + +- Superproject metadata: `.gitmodules`, `wiki/` (gitlink) +- Possible superproject support doc: `README.md` if a wiki-discovery link is needed +- Wiki repository pages: `wiki/Home.md`, `wiki/_Sidebar.md`, and additional topic pages under `wiki/` + +## Validation Scenarios + +1. Wiki availability: GitHub reports wiki support enabled for `OpenKotOR/ChangeEdit`, and the resolved wiki git remote accepts fetch/push operations. +2. Submodule integrity: cloning or updating submodules produces a populated `wiki/` checkout with a correct `.gitmodules` entry. +3. Documentation completeness: the wiki covers product purpose, core workflows, supported file formats, build/run instructions, limitations, and contributor entry points without contradicting repo-local docs. +4. Navigation quality: `Home.md` and `_Sidebar.md` provide working page discovery and no orphaned top-level pages remain. +5. Publication order: the wiki repository commit exists upstream before the superproject gitlink commit is pushed. + +## Risks and Mitigations + +- Wiki remote permissions may differ from normal repo push permissions. Mitigation: verify access before finalizing the submodule pointer. +- The wiki can drift from the root docs over time. Mitigation: keep the wiki curated and cross-link to root canonical docs for deep technical detail. +- There is no automated test suite for docs or app behavior. Mitigation: rely on repo evidence, careful link checks, and the repo's documented manual validation expectations. + +## Out of Scope + +- Changing Delphi source behavior, build tooling, or application UI. +- Rewriting existing root docs into a different documentation system. +- Adding generated site tooling, docs build pipelines, or non-wiki publishing infrastructure. diff --git a/docs/plans/2026-05-27-006-vscode-launch-defaults-lfg-plan.md b/docs/plans/2026-05-27-006-vscode-launch-defaults-lfg-plan.md new file mode 100644 index 0000000..9a9b0ac --- /dev/null +++ b/docs/plans/2026-05-27-006-vscode-launch-defaults-lfg-plan.md @@ -0,0 +1,121 @@ +--- +title: "LFG: ChangeEdit Launch Defaults Follow-up" +date: 2026-05-27 +status: completed +--- + +## Execution Outcome + +- Completed on 2026-05-27. +- Verified that the branch diff stayed limited to `.vscode/launch.json`, `.vscode/settings.json`, and `README.md` for the implementation slice. +- Confirmed there were no prompt-backed launch inputs remaining in the branch diff. +- Review/autofix produced no residual actionable work and no review-only edits to persist. +- Browser testing was not applicable because the PR diff does not touch browser-routable surfaces. +- PR #2 remains open, and no CI checks are configured on the branch. + +## Problem Frame + +The current branch `fix/changeedit-vscode-launch-defaults` is a follow-up to the already-merged ChangeEdit VS Code task cleanup. Its purpose is to finish the remaining workspace automation gap on this branch: remove interactive prompt-backed launch inputs, move those defaults into workspace settings, and keep the user-facing README aligned with the non-interactive build/run workflow. + +The implementation already exists on the branch, but this LFG pass still needs to complete the ordered pipeline against that slice: verify the branch state, review the diff against the requirements, persist any autofixes, confirm browser-test applicability, and close out the PR/CI steps. + +## Scope + +**In scope:** + +- Verify the current branch diff for `.vscode/launch.json`, `.vscode/settings.json`, and `README.md` +- Confirm the launch configuration no longer depends on interactive `inputs` +- Confirm workspace defaults exist for the ChangeEdit executable, Wine launcher, and Delphi compiler +- Confirm docs match the shipped Windows-native and Wine-backed workflow +- Run the remaining LFG review, validation, commit/push, PR, and CI-watch steps on this branch + +**Out of scope:** + +- Reworking the already-merged `.vscode/tasks.json` and `BUILD.md` changes unless review exposes a new defect in the current branch slice +- Modifying Delphi source files or runtime behavior +- Touching unrelated untracked plan artifacts already present in the repo + +## Requirements + +| # | Requirement | +| --- | --- | +| R1 | `.vscode/launch.json` resolves the executable and Wine launcher through workspace configuration rather than prompt-backed task inputs | +| R2 | `.vscode/settings.json` provides ChangeEdit defaults for compiler, executable, and Wine launcher values | +| R3 | `README.md` reflects the current Windows-native and Wine-backed VS Code workflow without stale task-input language | +| R4 | The current branch diff remains limited to the launch-default follow-up slice and does not sweep in unrelated files | +| R5 | The branch passes an LFG review/autofix pass with any resulting fixes durably committed before closeout | +| R6 | Browser testing is either run or explicitly recorded as not applicable based on the actual diff | +| R7 | The open PR for this branch is updated only through normal push flow unless residual findings or unresolved CI failures require PR-body changes | + +## Implementation Units + +### IU-1: Verify current branch slice + +**Files:** + +- `.vscode/launch.json` +- `.vscode/settings.json` +- `README.md` + +**Goals:** + +- Confirm the branch still matches its intended scope +- Confirm the implementation already present on the branch is the slice to review and ship + +**Verification:** + +- `git diff --stat origin/main...HEAD` shows the expected three-file follow-up scope + +### IU-2: Apply any small branch-local correction + +**Files:** + +- `.vscode/launch.json` +- `.vscode/settings.json` +- `README.md` + +**Goals:** + +- Fix any correctness or wording issue discovered while validating the current branch diff +- Keep changes minimal and branch-relevant + +**Verification:** + +- Touched files remain diagnostics-clean +- No interactive launch inputs remain in the branch diff + +### IU-3: Run review and closeout + +**Files:** + +- `.vscode/launch.json` +- `.vscode/settings.json` +- `README.md` +- `docs/plans/2026-05-27-006-vscode-launch-defaults-lfg-plan.md` + +**Goals:** + +- Complete the review/autofix step against the current branch diff +- Persist any review fixes separately if needed +- Finish PR and CI closeout for the open branch PR + +**Verification:** + +- Review findings are either fixed or explicitly recorded as none +- Remaining working tree changes are either committed for this branch or confirmed unrelated + +## Test Scenarios + +| Scenario | Pass condition | +| --- | --- | +| Branch scope check | Current diff against `origin/main` stays limited to launch/settings/README follow-up files | +| Launch-input removal | No prompt-backed `inputs` are required for the current launch configuration | +| Workspace defaults present | `changeEdit.delphiCompiler`, `changeEdit.executable`, and `changeEdit.wineCommand` exist in `.vscode/settings.json` | +| README parity | README language matches the ChangeEdit Windows-native and Wine-backed workflow | +| Browser-test applicability | The pipeline records a grounded N/A result if the diff does not touch browser-routable surfaces | + +## Dependencies + +- Current branch: `fix/changeedit-vscode-launch-defaults` +- Open PR: #2 against `main` +- Prior task-surface changes already merged to `main` diff --git a/docs/plans/2026-05-28-007-lfg-vscode-launch-defaults-continuation-plan.md b/docs/plans/2026-05-28-007-lfg-vscode-launch-defaults-continuation-plan.md new file mode 100644 index 0000000..6cc713c --- /dev/null +++ b/docs/plans/2026-05-28-007-lfg-vscode-launch-defaults-continuation-plan.md @@ -0,0 +1,31 @@ +# Plan: LFG Continuation for VS Code Launch Defaults + +Status: active +Date: 2026-05-28 +Scope: .vscode launch/task UX consistency and pipeline closeout on PR #2 + +## Goal + +Continue the branch work with at least one concrete improvement aligned to the non-interactive launch-defaults objective, then complete LFG review/push/check stages. + +## Implementation Units + +1. IU-1 Inspect current launch/task defaults + +- Verify whether interactive prompts still exist in launch/task surfaces. +- Identify a small, safe follow-up change to improve non-interactive behavior. + +1. IU-2 Implement one follow-up improvement + +- Apply minimal edits in .vscode files only. +- Keep behavior aligned with ChangeEdit and Delphi 7 constraints. + +1. IU-3 Review/autofix and persist + +- Run diagnostics/review pass. +- Commit review autofixes with required message if any are introduced. + +1. IU-4 Pipeline closeout + +- Push remaining changes. +- Run PR check-watch handling and complete the pass. diff --git a/docs/plans/2026-05-28-008-lfg-vscode-defaults-pass-plan.md b/docs/plans/2026-05-28-008-lfg-vscode-defaults-pass-plan.md new file mode 100644 index 0000000..92a42cb --- /dev/null +++ b/docs/plans/2026-05-28-008-lfg-vscode-defaults-pass-plan.md @@ -0,0 +1,27 @@ +# Plan: LFG Follow-up Pass for VS Code Defaults + +Status: active +Date: 2026-05-28 +Scope: clarify non-interactive VS Code configuration usage and complete LFG closeout + +## Goal + +Execute a full LFG follow-up pass with one concrete, low-risk improvement aligned to the launch-defaults objective. + +## Implementation Units + +1. IU-1 Confirm current defaults contract + +- Re-check launch/task/settings linkage for non-interactive behavior. + +1. IU-2 Apply one user-facing refinement + +- Add an explicit note in build documentation describing that launch and task flows read from changeEdit.* settings without prompting. + +1. IU-3 Review/autofix gate + +- Run diagnostics and apply any autofixes required by the review step. + +1. IU-4 Pipeline closeout + +- Commit/push remaining edits and run PR check-watch stage. diff --git a/inicon.bmp b/inicon.bmp new file mode 100644 index 0000000..7d917dd Binary files /dev/null and b/inicon.bmp differ diff --git a/inicon.gif b/inicon.gif new file mode 100644 index 0000000..e5e5904 Binary files /dev/null and b/inicon.gif differ diff --git a/wiki b/wiki new file mode 160000 index 0000000..0800fd5 --- /dev/null +++ b/wiki @@ -0,0 +1 @@ +Subproject commit 0800fd5c30aac8aa410090c7dff6a7b2f69817fa