-- batch_labels.lua - Batch label and base address editing tool (selected items only) -- Save to CE installation directory/autorun/ folder for automatic loading -- Define form variables local batchLabelForm = nil -- Helper function to get selected address items local function getSelectedAddressItems() local al = getAddressList() local selectedItems = {} local selectedCount = 0 -- Traverse all address items, collect selected ones for n = 0, al.Count - 1 do local item = al[n] if item.selected then selectedCount = selectedCount + 1 selectedItems[selectedCount] = item end end return selectedItems, selectedCount end -- Function to create batch label editing form function createBatchLabelForm() -- Check if form already exists, if so show and bring to front if batchLabelForm and not batchLabelForm.isDestroyed then batchLabelForm.show() batchLabelForm.BringToFront() return end -- If form was destroyed, reset variable if batchLabelForm and batchLabelForm.isDestroyed then batchLabelForm = nil end -- Create main form batchLabelForm = createForm(false) batchLabelForm.Position = poDesktopCenter batchLabelForm.Width = 666 batchLabelForm.Height = 780 batchLabelForm.Caption = 'Batch Edit Labels - Selected Items Only v1.1' -- Create group box local gb1 = createGroupBox(batchLabelForm) gb1.Left = 13 gb1.Top = 13 gb1.Width = 637 gb1.Height = 130 gb1.Caption = 'Operations' -- Create buttons local b1 = createButton(gb1) b1.Left = 26 b1.Top = 26 b1.Width = 130 b1.Height = 39 b1.caption = 'Read Selected (F5)' b1.ShowHint = true b1.Hint = 'Read labels from selected address list items' local b2 = createButton(gb1) b2.Left = 182 b2.Top = 26 b2.Width = 130 b2.Height = 39 b2.caption = 'Apply to Selected (F6)' b2.ShowHint = true b2.Hint = 'Apply changes to selected address list items' local b3 = createButton(gb1) b3.Left = 338 b3.Top = 26 b3.Width = 130 b3.Height = 39 b3.caption = 'Clear (F7)' b3.ShowHint = true b3.Hint = 'Clear edit box content' local b4 = createButton(gb1) b4.Left = 494 b4.Top = 26 b4.Width = 130 b4.Height = 39 b4.caption = 'Save to File' b4.ShowHint = true b4.Hint = 'Save current content to text file' local b5 = createButton(gb1) b5.Left = 182 b5.Top = 78 b5.Width = 130 b5.Height = 39 b5.caption = 'Clear Selected Labels' b5.ShowHint = true b5.Hint = 'Clear labels of all selected items' local b6 = createButton(gb1) b6.Left = 338 b6.Top = 78 b6.Width = 130 b6.Height = 39 b6.caption = 'Invert Selection' b6.ShowHint = true b6.Hint = 'Invert selection status of all address items' -- Create status label local lblStats = createLabel(batchLabelForm) lblStats.Left = 20 lblStats.Top = 156 lblStats.Caption = 'Status: Ready' lblStats.Font.Size = 13 -- Create Memo control local gb2 = createGroupBox(batchLabelForm) gb2.Left = 13 gb2.Top = 182 gb2.Width = 637 gb2.Height = 585 gb2.Caption = 'Label Edit Area' local m = createMemo(gb2) m.Height = 546 m.Left = 13 m.Top = 26 m.Width = 611 m.WordWrap = false m.ScrollBars = "ssAutoBoth" m.Font.Name = "Consolas" m.Font.Size = 13 -- Status update function local function updateStatus(message) lblStats.Caption = 'Status: ' .. message end -- Show confirmation dialog local function showConfirm(message) return messageDialog(message, mtConfirmation, mbYesNo) == mrYes end -- Read button function (read selected items only) b1.OnClick = function() local selectedItems, selectedCount = getSelectedAddressItems() if selectedCount == 0 then updateStatus("No address items selected") messageDialog("Please select address items to operate on first", mtWarning, mbOK) return end m.clear() local lines = {} local hasDescription = 0 for i = 1, selectedCount do local item = selectedItems[i] local description = item.description or "" if description ~= "" then hasDescription = hasDescription + 1 end -- Show only label content, not addresses lines[i] = description end m.Lines.Text = table.concat(lines, "\r\n") updateStatus("Read complete: " .. tostring(selectedCount) .. " items selected, " .. tostring(hasDescription) .. " have labels") end -- Apply button function (apply to selected items only) b2.OnClick = function() local selectedItems, selectedCount = getSelectedAddressItems() if selectedCount == 0 then updateStatus("No address items selected") messageDialog("Please select address items to operate on first", mtWarning, mbOK) return end local memoText = m.Lines.Text if memoText == "" then updateStatus("Edit box is empty") return end local lines = m.Lines local lineCount = lines.Count local modifiedCount = 0 -- Calculate number of lines to process (take the smaller value) local processCount if lineCount < selectedCount then processCount = lineCount updateStatus("Warning: Edit box lines (" .. tostring(lineCount) .. ") less than selected addresses (" .. tostring(selectedCount) .. ")") else processCount = selectedCount end for i = 0, processCount - 1 do local line = lines[i] -- Use entire line as label selectedItems[i + 1].description = line modifiedCount = modifiedCount + 1 end if lineCount > selectedCount then local ignoredCount = lineCount - selectedCount updateStatus("Modification complete: " .. tostring(modifiedCount) .. "/" .. tostring(selectedCount) .. " selected labels updated (" .. tostring(ignoredCount) .. " extra lines ignored)") else updateStatus("Modification complete: " .. tostring(modifiedCount) .. "/" .. tostring(selectedCount) .. " selected labels updated") end end -- Clear button function b3.OnClick = function() if m.Lines.Text ~= "" then if showConfirm("Are you sure you want to clear the edit box content?") then m.clear() updateStatus("Edit box cleared") end else updateStatus("Edit box is already empty") end end -- Clear selected items labels function b5.OnClick = function() local selectedItems, selectedCount = getSelectedAddressItems() if selectedCount == 0 then updateStatus("No address items selected") messageDialog("Please select address items to operate on first", mtWarning, mbOK) return end if showConfirm("Are you sure you want to clear labels of all selected address items? (" .. tostring(selectedCount) .. " items)") then for i = 1, selectedCount do selectedItems[i].description = "" end updateStatus("Cleared labels of " .. tostring(selectedCount) .. " selected address items") end end -- Invert selection function b6.OnClick = function() local al = getAddressList() local totalCount = al.Count local selectedCount = 0 for n = 0, totalCount - 1 do local item = al[n] item.selected = not item.selected if item.selected then selectedCount = selectedCount + 1 end end updateStatus("Selection inverted, currently " .. tostring(selectedCount) .. "/" .. tostring(totalCount) .. " addresses selected") end -- Save to file function b4.OnClick = function() local memoText = m.Lines.Text if memoText == "" then updateStatus("No content to save") messageDialog("Edit box is empty, cannot save", mtWarning, mbOK) return end local sd = createSaveDialog(batchLabelForm) sd.Filter = 'Text files (*.txt)|*.txt|All files (*.*)|*.*' sd.Title = 'Save labels to file' sd.FileName = 'cheat_engine_labels_' .. os.date("%Y%m%d_%H%M%S") .. '.txt' if sd.execute() then local filename = sd.FileName local file = io.open(filename, "w") if file then file:write(memoText) file:close() updateStatus("Saved to: " .. filename) else updateStatus("Save failed") messageDialog("Cannot save file: " .. filename, mtError, mbOK) end end end -- Add keyboard shortcuts batchLabelForm.OnKeyDown = function(sender, key) if key == VK_F5 then b1.OnClick() elseif key == VK_F6 then b2.OnClick() elseif key == VK_F7 then b3.OnClick() elseif key == VK_ESCAPE then batchLabelForm.close() end end -- Add form close confirmation batchLabelForm.OnClose = function() local memoText = m.Lines.Text if memoText ~= "" then if not showConfirm("Edit box has unsaved content, are you sure you want to close?") then return caNone end end -- After form closes, reset variable reference to allow recreation batchLabelForm = nil return caFree end -- Show form batchLabelForm.show() updateStatus("Ready - Press F5 to read selected labels, F6 to apply to selected, F7 to clear, ESC to close") -- Auto-adjust form position batchLabelForm.BringToFront() end -- Batch edit pointer base address function function batchEditPointerBaseAddress() -- Create main form local f = createForm() if not f then showMessage("Cannot create form") return end f.Position = poScreenCenter f.Width = 650 f.Height = 520 f.Caption = 'Batch Edit Pointer Base Address - Correct Format Version' f.BorderIcons = '[biSystemMenu,biMinimize]' -- Create panel local panel = createPanel(f) panel.Align = alClient panel.BevelOuter = bvLowered -- Create title local titleLabel = createLabel(panel) titleLabel.Left = 15 titleLabel.Top = 15 titleLabel.Caption = 'Batch Edit Pointer Base Address' titleLabel.Font.Size = 16 titleLabel.Font.Style = '[fsBold]' -- Create info label local infoLabel = createLabel(panel) infoLabel.Left = 15 infoLabel.Top = 50 infoLabel.Width = 610 infoLabel.Caption = 'Function: Batch modify pointer base addresses using correct format.' infoLabel.Font.Size = 10 -- New base address input local lblNewBase = createLabel(panel) lblNewBase.Left = 15 lblNewBase.Top = 100 lblNewBase.Caption = 'New Base Address:' lblNewBase.Font.Size = 10 local edtNewBase = createEdit(panel) edtNewBase.Left = 130 edtNewBase.Top = 98 edtNewBase.Width = 200 edtNewBase.Height = 24 edtNewBase.Text = '' -- Create example label local lblExample = createLabel(panel) lblExample.Left = 340 lblExample.Top = 102 lblExample.Caption = 'Example: game.exe+100' lblExample.Font.Color = 0x808080 lblExample.Font.Size = 10 -- Offset handling mode local lblOffsetMode = createLabel(panel) lblOffsetMode.Left = 15 lblOffsetMode.Top = 140 lblOffsetMode.Caption = 'Offset Handling:' lblOffsetMode.Font.Size = 10 local chkKeepOffsets = createCheckBox(panel) chkKeepOffsets.Left = 130 chkKeepOffsets.Top = 138 chkKeepOffsets.Width = 180 chkKeepOffsets.Height = 24 chkKeepOffsets.Caption = 'Keep original offsets' chkKeepOffsets.Checked = true chkKeepOffsets.Font.Size = 10 -- Processing range local lblRange = createLabel(panel) lblRange.Left = 15 lblRange.Top = 180 lblRange.Caption = 'Processing Range:' lblRange.Font.Size = 10 local chkSelectedOnly = createCheckBox(panel) chkSelectedOnly.Left = 130 chkSelectedOnly.Top = 178 chkSelectedOnly.Width = 180 chkSelectedOnly.Height = 24 chkSelectedOnly.Caption = 'Selected items only' chkSelectedOnly.Checked = true chkSelectedOnly.Font.Size = 10 -- Log display area local logMemo = createMemo(panel) logMemo.Left = 15 logMemo.Top = 230 logMemo.Width = 620 logMemo.Height = 180 logMemo.ReadOnly = true logMemo.ScrollBars = ssVertical logMemo.Lines.Text = 'Waiting for operation...\n' logMemo.Font.Name = 'Courier New' logMemo.Font.Size = 10 -- Button panel local buttonPanel = createPanel(panel) buttonPanel.Left = 15 buttonPanel.Top = 420 buttonPanel.Width = 620 buttonPanel.Height = 50 buttonPanel.BevelOuter = bvNone -- Create buttons local btnAnalyze = createButton(buttonPanel) btnAnalyze.Left = 15 btnAnalyze.Top = 10 btnAnalyze.Width = 100 btnAnalyze.Height = 35 btnAnalyze.Caption = 'Analyze' btnAnalyze.Font.Size = 10 local btnModify = createButton(buttonPanel) btnModify.Left = 130 btnModify.Top = 10 btnModify.Width = 100 btnModify.Height = 35 btnModify.Caption = 'Modify' btnModify.Enabled = false btnModify.Font.Size = 10 local btnClearLog = createButton(buttonPanel) btnClearLog.Left = 245 btnClearLog.Top = 10 btnClearLog.Width = 100 btnClearLog.Height = 35 btnClearLog.Caption = 'Clear' btnClearLog.Font.Size = 10 local btnClose = createButton(buttonPanel) btnClose.Left = 360 btnClose.Top = 10 btnClose.Width = 100 btnClose.Height = 35 btnClose.Caption = 'Close' btnClose.Font.Size = 10 -- Progress bar local progressBar = createProgressBar(panel) progressBar.Left = 15 progressBar.Top = 480 progressBar.Width = 620 progressBar.Height = 15 progressBar.Visible = false -- Function: Add log function addLog(message) logMemo.Lines.Text = logMemo.Lines.Text .. message .. '\n' logMemo.SelStart = string.len(logMemo.Lines.Text) end -- Function: Set address using correct method function setAddressWithOffsets(mr, baseAddress, offsets) -- Use correct CE method to set address local success, err = pcall(function() -- Set base address mr.setAddress(baseAddress) -- Set offset count local offsetCount = #offsets if offsetCount > 0 then mr.setOffsetCount(offsetCount) -- Set offsets (Note: In CE, Offset[0] is outermost offset, Offset[OffsetCount-1] is first offset) for i = 0, offsetCount - 1 do mr.setOffset(i, offsets[i + 1]) -- Lua table starts from 1, CE offsets from 0 end else mr.setOffsetCount(0) -- No offsets end end) return success, err end -- Function: Analyze selected items function analyzeSelectedItems() logMemo.Lines.Text = 'Analysis Results:\n' local addressList = getAddressList() if not addressList then addLog("Error: Cannot get address list") return 0 end local totalCount = addressList.Count local selectedCount = 0 addLog(string.format("Total address list items: %d", totalCount)) -- Check if there are selected items local hasSelected = false if chkSelectedOnly.Checked then addLog("Analyzing selected items...") for i = 0, totalCount - 1 do local mr = addressList.getMemoryRecord(i) if mr and mr.Selected then selectedCount = selectedCount + 1 addLog(string.format("Item %d: %s", i + 1, mr.Description or "No description")) addLog(string.format(" Address: %s", mr.Address)) addLog(string.format(" Pointer: %s", mr.IsPointer and "Yes" or "No")) addLog(string.format(" Offset count: %d", mr.OffsetCount)) if mr.OffsetCount > 0 then addLog(" Offsets (outermost to innermost, Offset[0] is outermost):") for j = 0, mr.OffsetCount - 1 do local offset = mr.Offset[j] if offset then addLog(string.format(" Offset[%d] = %X", j, offset)) end end -- Show actual pointer chain (from base address to outermost offset) local chainStr = mr.Address addLog(" Full pointer chain: " .. chainStr) end addLog("") hasSelected = true end end if not hasSelected then addLog("Warning: No selected items") end else addLog("Analyzing all items...") for i = 0, totalCount - 1 do local mr = addressList.getMemoryRecord(i) if mr then selectedCount = selectedCount + 1 -- Only show first 5 as examples if i < 5 then addLog(string.format("Example %d: %s", i + 1, mr.Description or "No description")) addLog(string.format(" Address: %s", mr.Address)) end end end addLog(string.format("Found %d items", selectedCount)) if totalCount >= 5 then addLog("... (more items not shown)") end end addLog("Analysis complete") btnModify.Enabled = (selectedCount > 0) and (trim(edtNewBase.Text) ~= "") return selectedCount end -- Function: Batch modify base addresses function modifyBaseAddresses() local newBase = trim(edtNewBase.Text) if newBase == "" then showMessage("Please enter new base address") return end addLog("") addLog("Starting batch modification of base addresses...") addLog(string.format("New base address: %s", newBase)) local addressList = getAddressList() if not addressList then addLog("Error: Cannot get address list") return end -- Show progress bar progressBar.Visible = true progressBar.Position = 0 progressBar.Max = addressList.Count local modifiedCount = 0 local failedCount = 0 -- Process selected items or all items for i = 0, addressList.Count - 1 do local mr = addressList.getMemoryRecord(i) -- Check if this item should be processed local shouldProcess = false if chkSelectedOnly.Checked then shouldProcess = mr and mr.Selected else shouldProcess = mr ~= nil end if shouldProcess then -- Update progress progressBar.Position = i + 1 -- Get original offsets local offsets = {} local offsetCount = mr.OffsetCount if chkKeepOffsets.Checked then -- Keep original offsets for j = 0, offsetCount - 1 do offsets[j + 1] = mr.Offset[j] -- Lua table starts from 1 end end -- Record pre-modification info local oldAddress = mr.Address local description = mr.Description or "" -- Use correct method to set new address local success, err = setAddressWithOffsets(mr, newBase, offsets) -- Record log if success then addLog(string.format("Successfully modified item %d: %s", i + 1, description)) addLog(string.format(" Old address: %s", oldAddress)) addLog(string.format(" New address: %s", mr.Address)) modifiedCount = modifiedCount + 1 -- Verify offsets correctly retained if chkKeepOffsets.Checked and offsetCount > 0 then addLog(" Offset verification:") local allMatch = true for j = 0, offsetCount - 1 do local originalOffset = offsets[j + 1] local newOffset = mr.Offset[j] if originalOffset == newOffset then addLog(string.format(" Offset[%d]: %X ✓", j, originalOffset)) else addLog(string.format(" Offset[%d]: old%X -> new%X ✗", j, originalOffset, newOffset or 0)) allMatch = false end end if not allMatch then addLog(" Warning: Some offsets do not match") end end else addLog(string.format("Failed to modify item %d: %s", i + 1, description)) addLog(string.format(" Error: %s", err)) failedCount = failedCount + 1 end end end -- Hide progress bar progressBar.Visible = false -- Show results addLog(string.format("Batch modification complete!")) addLog(string.format("Success: %d items", modifiedCount)) addLog(string.format("Failed: %d items", failedCount)) showMessage(string.format("Batch modification complete!\nSuccess: %d items\nFailed: %d items", modifiedCount, failedCount)) end -- Function: Clear log function clearLog() logMemo.Lines.Text = 'Log cleared\n' logMemo.SelStart = string.len(logMemo.Lines.Text) end -- Function: String trim function trim(s) if s == nil then return "" end return s:match("^%s*(.-)%s*$") end -- Set button events btnAnalyze.onClick = analyzeSelectedItems btnModify.onClick = modifyBaseAddresses btnClearLog.onClick = clearLog btnClose.onClick = function() f.close() end -- Base address edit box event: enable modify button when content exists edtNewBase.OnChange = function() btnModify.Enabled = trim(edtNewBase.Text) ~= "" end -- Checkbox change events chkSelectedOnly.OnChange = function() analyzeSelectedItems() end chkKeepOffsets.OnChange = function() analyzeSelectedItems() end -- Form close event f.onClose = function() return caFree end -- Add usage instructions local helpLabel = createLabel(panel) helpLabel.Left = 15 helpLabel.Top = 380 helpLabel.Width = 610 helpLabel.Caption = 'Tip: Use correct setAddress, setOffsetCount, and setOffset methods to set addresses.' helpLabel.Font.Color = 0x008000 helpLabel.Font.Size = 10 -- Show form f.show() addLog("Batch edit pointer base address script loaded") addLog("Using correct method to set addresses:") addLog("1. setAddress('base address')") addLog("2. setOffsetCount(offset count)") addLog("3. setOffset(index, offset value)") addLog("Note: Offset[0] is outermost offset") addLog("Usage steps:") addLog("1. Select items to modify in CE address list") addLog("2. Enter new base address") addLog("3. Click 'Analyze' button") addLog("4. Click 'Modify' button") end -- Function to create tool menu items local function createToolsMenuItem() -- Check if menu items already exist local exists1 = false local exists2 = false if MainForm and MainForm.Menu then for i = 0, MainForm.Menu.Items.Count-1 do if MainForm.Menu.Items[i].Caption == 'Batch Edit Labels (Selected)' then exists1 = true elseif MainForm.Menu.Items[i].Caption == 'Batch Edit Pointer Base Address' then exists2 = true end end if not exists1 then -- Create batch edit labels menu item local mi1 = createMenuItem(MainForm.Menu) mi1.Caption = 'Batch Edit Labels (Selected)' mi1.OnClick = function() -- Call function to create main form createBatchLabelForm() end MainForm.Menu.Items.add(mi1) end if not exists2 then -- Create batch edit pointer base address menu item local mi2 = createMenuItem(MainForm.Menu) mi2.Caption = 'Batch Edit Pointer Base Address' mi2.OnClick = function() batchEditPointerBaseAddress() end MainForm.Menu.Items.add(mi2) end return true end return false end -- Tool menu add function local function addToToolsMenu() -- Check if menu items already added if not MainForm or not MainForm.Menu then -- If no main menu, delay adding local timer = createTimer(nil) timer.Interval = 1000 timer.OnTimer = function(timer) timer.destroy() if MainForm and MainForm.Menu then createToolsMenuItem() end end else createToolsMenuItem() end end -- Delayed execution to ensure CE fully starts local startupTimer = createTimer(nil, false) startupTimer.Interval = 1000 startupTimer.OnTimer = function(timer) timer.destroy() addToToolsMenu() end startupTimer.Enabled = true