cheat finder enhancements
This commit is contained in:
parent
04f65c9b1b
commit
de8edd03f1
|
@ -11,8 +11,13 @@ class CheatFinderManager {
|
|||
private(set) var matchedMemoryAddresses = [Int: UInt8]()
|
||||
var comparisonMemory = [UInt8]()
|
||||
|
||||
private let dispatchQueue = DispatchQueue(label: "CheatFinderManager", qos: .background)
|
||||
var timer: DispatchSourceTimer?
|
||||
|
||||
var savedMatches = [Int: (value: UInt8, enabled: Bool)]()
|
||||
|
||||
enum UIState {
|
||||
case initial, startedNewSearch, isSearching, didSearch
|
||||
case initial, startedNewSearch, isSearching, didSearch, showSaved
|
||||
}
|
||||
|
||||
enum SearchMode {
|
||||
|
@ -22,8 +27,30 @@ class CheatFinderManager {
|
|||
|
||||
var uiState = UIState.initial
|
||||
|
||||
func start() {
|
||||
if timer == nil {
|
||||
timer = DispatchSource.makeTimerSource(queue: dispatchQueue)
|
||||
timer?.setEventHandler(handler: {
|
||||
self.updateMemoryWithCheats()
|
||||
})
|
||||
timer?.schedule(deadline: .now(), repeating: .seconds(1))
|
||||
timer?.resume()
|
||||
}
|
||||
}
|
||||
|
||||
func updateMemoryWithCheats() {
|
||||
print("CheatFinderManager updating memory...")
|
||||
let memory = EmuWrapper.memory()
|
||||
for (address, memoryValue) in savedMatches {
|
||||
if let memory = memory, memoryValue.enabled {
|
||||
print("updating memory at \(String(format: "%05X",address)) to \(memoryValue.value)")
|
||||
memory[address] = memoryValue.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func update(with memory: UnsafeMutablePointer<UInt8>) {
|
||||
for address in 0..<0x95ff {
|
||||
for address in 0..<EmuMemoryModel.maxMemorySize {
|
||||
matchedMemoryAddresses[address] = memory[address]
|
||||
}
|
||||
}
|
||||
|
@ -46,13 +73,12 @@ class CheatFinderManager {
|
|||
if matched {
|
||||
newMatches[address] = newValue
|
||||
}
|
||||
// // avoid having too many matches
|
||||
// if newMatches.count > 10000 {
|
||||
// break
|
||||
// }
|
||||
}
|
||||
matchedMemoryAddresses = newMatches
|
||||
}
|
||||
|
||||
// start new search:
|
||||
// put all memory into matched
|
||||
//
|
||||
// set less than
|
||||
// run findNewMatches
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import Foundation
|
|||
|
||||
class EmuMemoryModel {
|
||||
let numToDisplayPerCell = 8
|
||||
let maxMemorySize = 128 * 1024
|
||||
static let maxMemorySize = 256 * 1024
|
||||
|
||||
private(set) var memory = EmuWrapper.memory()
|
||||
private(set) var referencedAddresses = [UInt16: [AddressedInstruction]]()
|
||||
|
@ -47,8 +47,8 @@ class EmuMemoryModel {
|
|||
}
|
||||
|
||||
func getMemoryHexString(at address: Int) -> String {
|
||||
guard address > 0 && address < maxMemorySize else {
|
||||
print("Cannot get memory: address out of range \(address) > \(maxMemorySize)")
|
||||
guard address > 0 && address < Self.maxMemorySize else {
|
||||
print("Cannot get memory: address out of range \(address) > \(Self.maxMemorySize)")
|
||||
return ""
|
||||
}
|
||||
guard let memory = memory else {
|
||||
|
@ -63,8 +63,8 @@ class EmuMemoryModel {
|
|||
}
|
||||
|
||||
func setMemory(at address:Int, value: UInt8) {
|
||||
guard address > 0 && address < maxMemorySize else {
|
||||
print("Cannot set memory: address out of range \(address) > \(maxMemorySize)")
|
||||
guard address > 0 && address < Self.maxMemorySize else {
|
||||
print("Cannot set memory: address out of range \(address) > \(Self.maxMemorySize)")
|
||||
return
|
||||
}
|
||||
guard let memory = memory else {
|
||||
|
@ -76,8 +76,8 @@ class EmuMemoryModel {
|
|||
}
|
||||
|
||||
func getMemory(at address:Int) -> UInt8 {
|
||||
guard address > 0 && address < maxMemorySize else {
|
||||
print("Cannot get memory: address out of range \(address) > \(maxMemorySize)")
|
||||
guard address > 0 && address < Self.maxMemorySize else {
|
||||
print("Cannot get memory: address out of range \(address) > \(Self.maxMemorySize)")
|
||||
return 0
|
||||
}
|
||||
guard let memory = memory else {
|
||||
|
|
|
@ -23,7 +23,7 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
}
|
||||
|
||||
var mode: Mode = .jumpToAddress
|
||||
var cheatFinder = CheatFinderManager()
|
||||
let cheatFinder: CheatFinderManager
|
||||
|
||||
var matchedInstructions = [AddressedInstruction]()
|
||||
|
||||
|
@ -73,8 +73,18 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
return button
|
||||
}()
|
||||
|
||||
let addToCheatsButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.titleLabel?.font = UIFont(name: "Print Char 21", size: 12)
|
||||
button.setTitle("Add to Cheats", for: .normal)
|
||||
button.layer.borderWidth = 1
|
||||
button.layer.borderColor = UIColor.orange.cgColor
|
||||
button.addTarget(self, action: #selector(addToCheatButtonPressed(_:)), for: .touchUpInside )
|
||||
return button
|
||||
}()
|
||||
|
||||
lazy var editFieldsStackView: UIStackView = {
|
||||
let stackView = UIStackView(arrangedSubviews: [memoryField, updateMemoryButton, resetMemoryButton])
|
||||
let stackView = UIStackView(arrangedSubviews: [memoryField, updateMemoryButton, resetMemoryButton, addToCheatsButton])
|
||||
stackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
stackView.axis = .horizontal
|
||||
stackView.spacing = 4
|
||||
|
@ -151,7 +161,8 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
}()
|
||||
|
||||
lazy var cheatFinderInitialActionsStackView: UIStackView = {
|
||||
let stackView = UIStackView(arrangedSubviews: [cheatFinderNewSearchButton, cheatFinderContinueSearchButton, cheatFinderSearchLessButton, cheatFinderSearchGreaterButton, cheatFinderSearchEqualButton])
|
||||
let stackView = UIStackView(arrangedSubviews: [cheatFinderNewSearchButton, cheatFinderContinueSearchButton, cheatFinderSearchLessButton, cheatFinderSearchGreaterButton, cheatFinderSearchEqualButton,
|
||||
cheatFinderShowSavedButton])
|
||||
stackView.translatesAutoresizingMaskIntoConstraints = false
|
||||
stackView.axis = .horizontal
|
||||
stackView.spacing = 4
|
||||
|
@ -197,6 +208,26 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
return button
|
||||
}()
|
||||
|
||||
lazy var cheatFinderShowSavedButton: ToggleButton = {
|
||||
let button = ToggleButton()
|
||||
button.titleLabel?.font = UIFont(name: "Print Char 21", size: 12)
|
||||
button.setTitle("Matched", for: .normal)
|
||||
button.setTitle("Saved", for: .selected)
|
||||
button.layer.borderWidth = 1
|
||||
button.layer.borderColor = UIColor.purple.cgColor
|
||||
button.onTapped = { [weak self] wasSelected in
|
||||
if wasSelected {
|
||||
self?.cheatFinder.uiState = .showSaved
|
||||
} else {
|
||||
self?.cheatFinder.uiState = .didSearch
|
||||
}
|
||||
self?.cheatFinderUpdateUI()
|
||||
}
|
||||
button.tag = 3
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
return button
|
||||
}()
|
||||
|
||||
let findCodeButton: DebugMemoryButton = {
|
||||
let button = DebugMemoryButton()
|
||||
button.titleLabel?.font = UIFont(name: "Print Char 21", size: 9)
|
||||
|
@ -237,6 +268,25 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
return view
|
||||
}()
|
||||
|
||||
lazy var cheatFinderSavedTableView: UITableView = {
|
||||
let view = UITableView(frame: .zero)
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.backgroundColor = .clear
|
||||
view.dataSource = self
|
||||
view.delegate = self
|
||||
view.register(UITableViewCell.self, forCellReuseIdentifier: "CheatFinderMatchCell")
|
||||
return view
|
||||
}()
|
||||
|
||||
init(cheatFinderManager: CheatFinderManager) {
|
||||
self.cheatFinder = cheatFinderManager
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
lazy var emulatorScreenView: UIImageView = {
|
||||
let screenView = UIImageView(frame: .zero)
|
||||
screenView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
@ -261,6 +311,7 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
// view.addSubview(cheatFinderSearchStackView)
|
||||
view.addSubview(cheatFinderPromptLabel)
|
||||
view.addSubview(cheatFinderMatchesTableView)
|
||||
view.addSubview(cheatFinderSavedTableView)
|
||||
cheatFinderInitialActionsStackView.topAnchor.constraint(equalTo: segmentedControl.bottomAnchor, constant: 8).isActive = true
|
||||
cheatFinderInitialActionsStackView.centerXAnchor.constraint(equalTo: segmentedControl.centerXAnchor).isActive = true
|
||||
// cheatFinderInitialActionsStackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true
|
||||
|
@ -275,11 +326,15 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
cheatFinderMatchesTableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 24).isActive = true
|
||||
cheatFinderMatchesTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -24).isActive = true
|
||||
cheatFinderMatchesTableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 8).isActive = true
|
||||
[cheatFinderInitialActionsStackView, cheatFinderPromptLabel, cheatFinderMatchesTableView].forEach{ $0.isHidden = true }
|
||||
cheatFinderSavedTableView.topAnchor.constraint(equalTo: cheatFinderMatchesTableView.topAnchor).isActive = true
|
||||
cheatFinderSavedTableView.leadingAnchor.constraint(equalTo: cheatFinderMatchesTableView.leadingAnchor).isActive = true
|
||||
cheatFinderSavedTableView.trailingAnchor.constraint(equalTo: cheatFinderMatchesTableView.trailingAnchor).isActive = true
|
||||
cheatFinderSavedTableView.bottomAnchor.constraint(equalTo: cheatFinderMatchesTableView.bottomAnchor).isActive = true
|
||||
[cheatFinderInitialActionsStackView, cheatFinderPromptLabel, cheatFinderMatchesTableView, cheatFinderSavedTableView].forEach{ $0.isHidden = true }
|
||||
}
|
||||
|
||||
func cheatFinderHide() {
|
||||
[cheatFinderInitialActionsStackView, cheatFinderPromptLabel, cheatFinderMatchesTableView, cheatFinderSearchLessButton, cheatFinderSearchGreaterButton, cheatFinderNewSearchButton, cheatFinderContinueSearchButton, cheatFinderSearchEqualButton].forEach{ $0.isHidden = true }
|
||||
[cheatFinderInitialActionsStackView, cheatFinderPromptLabel, cheatFinderMatchesTableView, cheatFinderSearchLessButton, cheatFinderSearchGreaterButton, cheatFinderNewSearchButton, cheatFinderContinueSearchButton, cheatFinderSearchEqualButton, cheatFinderShowSavedButton, cheatFinderSavedTableView].forEach{ $0.isHidden = true }
|
||||
}
|
||||
|
||||
func cheatFinderUpdateUI() {
|
||||
|
@ -292,19 +347,25 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
[cheatFinderInitialActionsStackView, cheatFinderNewSearchButton,
|
||||
cheatFinderSearchLessButton, cheatFinderSearchGreaterButton,
|
||||
cheatFinderSearchEqualButton,
|
||||
cheatFinderPromptLabel].forEach{ $0.isHidden = false }
|
||||
cheatFinderPromptLabel, cheatFinderShowSavedButton].forEach{ $0.isHidden = false }
|
||||
cheatFinderPromptLabel.text = "New search started! Search to find matches..."
|
||||
case .isSearching:
|
||||
[cheatFinderInitialActionsStackView, cheatFinderNewSearchButton, cheatFinderSearchLessButton, cheatFinderSearchGreaterButton,
|
||||
cheatFinderSearchEqualButton, cheatFinderPromptLabel].forEach{ $0.isHidden = false }
|
||||
cheatFinderSearchEqualButton, cheatFinderPromptLabel, cheatFinderShowSavedButton].forEach{ $0.isHidden = false }
|
||||
cheatFinderPromptLabel.text = "Search for values..."
|
||||
case .didSearch:
|
||||
[cheatFinderInitialActionsStackView, cheatFinderNewSearchButton,
|
||||
cheatFinderSearchLessButton, cheatFinderSearchGreaterButton,
|
||||
cheatFinderSearchEqualButton,
|
||||
cheatFinderPromptLabel, cheatFinderMatchesTableView].forEach{ $0.isHidden = false }
|
||||
cheatFinderPromptLabel, cheatFinderMatchesTableView, cheatFinderShowSavedButton].forEach{ $0.isHidden = false }
|
||||
cheatFinderPromptLabel.text = "Number of matches: \(cheatFinder.matchedMemoryAddresses.count)"
|
||||
cheatFinderMatchesTableView.reloadData()
|
||||
case .showSaved:
|
||||
[cheatFinderInitialActionsStackView, cheatFinderNewSearchButton,
|
||||
cheatFinderSearchLessButton, cheatFinderSearchGreaterButton,
|
||||
cheatFinderSearchEqualButton,
|
||||
cheatFinderPromptLabel, cheatFinderShowSavedButton, cheatFinderSavedTableView].forEach{ $0.isHidden = false }
|
||||
cheatFinderSavedTableView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -314,7 +375,7 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
return
|
||||
}
|
||||
cheatFinder.comparisonMemory = [UInt8]()
|
||||
for address in 0..<0x95ff {
|
||||
for address in 0..<EmuMemoryModel.maxMemorySize {
|
||||
cheatFinder.comparisonMemory.append(memory[address])
|
||||
}
|
||||
switch sender.tag {
|
||||
|
@ -442,7 +503,8 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
private func updateMatchedInstructions() {
|
||||
if let delegate = delegate,
|
||||
let text = memoryField.text,
|
||||
let address = getAddressFromText(text) {
|
||||
let address = getAddressFromText(text),
|
||||
address < 0x10000 {
|
||||
matchedInstructions = delegate.referencedMemoryAddresses[UInt16(address)] ?? [AddressedInstruction]()
|
||||
findInCodeResultsTableView.reloadData()
|
||||
}
|
||||
|
@ -476,7 +538,7 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
}
|
||||
let scanner = Scanner(string: text)
|
||||
var address: UInt64 = 0
|
||||
if scanner.scanHexInt64(&address) && address < 0x95ff {
|
||||
if scanner.scanHexInt64(&address) && address < EmuMemoryModel.maxMemorySize {
|
||||
return address
|
||||
}
|
||||
return nil
|
||||
|
@ -491,11 +553,11 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
let charLimit: Int = {
|
||||
switch mode {
|
||||
case .jumpToAddress:
|
||||
return 4
|
||||
return 5
|
||||
case .changeMemory:
|
||||
return 2
|
||||
default:
|
||||
return 4
|
||||
return 5
|
||||
}
|
||||
}()
|
||||
if text.count > charLimit {
|
||||
|
@ -581,6 +643,41 @@ class DebugMemoryActionViewController: UIViewController {
|
|||
}
|
||||
delegate?.updateMemory(at: selectedAddress, with: memory)
|
||||
}
|
||||
|
||||
@objc func addToCheatButtonPressed(_ sender: UIButton) {
|
||||
guard let selectedAddress = delegate?.selectedAddress,
|
||||
let enteredText = memoryField.text,
|
||||
let memory = UInt8(enteredText, radix: 16) else {
|
||||
print("Could not get memory to update!")
|
||||
return
|
||||
}
|
||||
cheatFinder.savedMatches[selectedAddress] = (value: memory, enabled: true)
|
||||
cheatFinderSavedTableView.reloadData()
|
||||
}
|
||||
|
||||
@objc func cheatTableCellActionButtonPressed(_ sender: UIButton) {
|
||||
let address = sender.tag
|
||||
guard let matched = cheatFinder.matchedMemoryAddresses[address] else {
|
||||
print("Could not find matched address: \(address)")
|
||||
return
|
||||
}
|
||||
if cheatFinder.savedMatches[address] != nil {
|
||||
cheatFinder.savedMatches.removeValue(forKey: address)
|
||||
} else {
|
||||
cheatFinder.savedMatches[address] = (value: matched, enabled: true)
|
||||
}
|
||||
cheatFinderSavedTableView.reloadData()
|
||||
}
|
||||
|
||||
@objc func cheatSavedTableSwitchPressed(_ sender: UISwitch) {
|
||||
let address = sender.tag
|
||||
guard let matched = cheatFinder.savedMatches[address] else {
|
||||
print("could not find saved entry!")
|
||||
return
|
||||
}
|
||||
cheatFinder.savedMatches[address] = (value: matched.value, enabled: sender.isOn)
|
||||
cheatFinderSavedTableView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
extension DebugMemoryActionViewController: EmulatorKeyboardKeyPressedDelegate {
|
||||
|
@ -616,6 +713,8 @@ extension DebugMemoryActionViewController: UITableViewDataSource {
|
|||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
if tableView == findInCodeResultsTableView {
|
||||
return matchedInstructions.count
|
||||
} else if tableView == cheatFinderSavedTableView {
|
||||
return cheatFinder.savedMatches.keys.count
|
||||
} else {
|
||||
return cheatFinder.matchedMemoryAddresses.keys.count
|
||||
}
|
||||
|
@ -630,15 +729,39 @@ extension DebugMemoryActionViewController: UITableViewDataSource {
|
|||
cell.textLabel?.textColor = .yellow
|
||||
cell.textLabel?.textAlignment = .center
|
||||
return cell
|
||||
} else if tableView == cheatFinderSavedTableView {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "CheatFinderMatchCell", for: indexPath)
|
||||
let addresses = cheatFinder.savedMatches.keys.sorted()
|
||||
let index = addresses.index(addresses.startIndex, offsetBy: indexPath.row)
|
||||
let address = addresses[index]
|
||||
let saved = cheatFinder.savedMatches[address]!
|
||||
cell.textLabel?.text = String(format: "%04X: %02X",address,saved.value)
|
||||
cell.textLabel?.font = UIFont(name: "Print Char 21", size: 14)
|
||||
cell.textLabel?.textColor = .red
|
||||
cell.textLabel?.textAlignment = .left
|
||||
let enableSwitch = UISwitch(frame: .zero)
|
||||
enableSwitch.isOn = saved.enabled
|
||||
enableSwitch.addTarget(self, action: #selector(cheatSavedTableSwitchPressed(_:)), for: .valueChanged)
|
||||
enableSwitch.tag = address
|
||||
cell.accessoryView = enableSwitch
|
||||
return cell
|
||||
} else {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "CheatFinderMatchCell", for: indexPath)
|
||||
let addresses = cheatFinder.matchedMemoryAddresses.keys
|
||||
let addresses = cheatFinder.matchedMemoryAddresses.keys.sorted()
|
||||
let index = addresses.index(addresses.startIndex, offsetBy: indexPath.row)
|
||||
let address = addresses[index]
|
||||
cell.textLabel?.text = String(format: "%04X: %02X",address,cheatFinder.matchedMemoryAddresses[address]!)
|
||||
cell.textLabel?.font = UIFont(name: "Print Char 21", size: 14)
|
||||
cell.textLabel?.textColor = .red
|
||||
cell.textLabel?.textAlignment = .center
|
||||
let isSaved = cheatFinder.savedMatches[address] != nil
|
||||
let actionButton = isSaved ? UIButton(type: .custom) : UIButton(type: .contactAdd)
|
||||
actionButton.tag = address
|
||||
if isSaved {
|
||||
actionButton.setTitle("Remove", for: .normal)
|
||||
}
|
||||
actionButton.addTarget(self, action: #selector(cheatTableCellActionButtonPressed(_:)), for: .touchUpInside)
|
||||
cell.accessoryView = actionButton
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
@ -651,7 +774,7 @@ extension DebugMemoryActionViewController: UITableViewDelegate {
|
|||
let address = instruction.address
|
||||
delegate?.jump(to: Int(address))
|
||||
} else {
|
||||
let addresses = cheatFinder.matchedMemoryAddresses.keys
|
||||
let addresses = tableView == cheatFinderSavedTableView ? cheatFinder.savedMatches.keys.sorted() : cheatFinder.matchedMemoryAddresses.keys.sorted()
|
||||
let index = addresses.index(addresses.startIndex, offsetBy: indexPath.row)
|
||||
let address = addresses[index]
|
||||
delegate?.jump(to: address)
|
||||
|
|
|
@ -20,6 +20,9 @@ enum EmuMemoryMapSection: Int {
|
|||
case highResGraphicsPage1
|
||||
case highResGraphicsPage2
|
||||
case applesoftStringData
|
||||
case ioArea
|
||||
case bankSwitched
|
||||
case auxBanks
|
||||
|
||||
var range:Range<Int> {
|
||||
switch self {
|
||||
|
@ -33,7 +36,10 @@ enum EmuMemoryMapSection: Int {
|
|||
case .freespace2: return 0xc00..<0x2000
|
||||
case .highResGraphicsPage1: return 0x2000..<0x4000
|
||||
case .highResGraphicsPage2: return 0x4000..<0x6000
|
||||
case .applesoftStringData: return 0x6000..<0x95ff
|
||||
case .applesoftStringData: return 0x6000..<0xc000
|
||||
case .ioArea: return 0xc000..<0xd000
|
||||
case .bankSwitched: return 0xd000..<0x10000
|
||||
case .auxBanks: return 0x10000..<0x30000
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,6 +60,9 @@ enum EmuMemoryMapSection: Int {
|
|||
case .highResGraphicsPage1: return "High Resolution Graphics Page 1"
|
||||
case .highResGraphicsPage2: return "High Resolution Graphics Page 2"
|
||||
case .applesoftStringData: return "Applesoft String Data"
|
||||
case .ioArea: return "IO Area"
|
||||
case .bankSwitched: return "Bank Switched"
|
||||
case .auxBanks: return "Auxilliary Memory? 😅"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,8 +87,16 @@ enum EmuMemoryMapSection: Int {
|
|||
return .highResGraphicsPage1
|
||||
} else if EmuMemoryMapSection.highResGraphicsPage2.range.contains(address) {
|
||||
return .highResGraphicsPage2
|
||||
} else {
|
||||
} else if EmuMemoryMapSection.applesoftStringData.range.contains(address) {
|
||||
return .applesoftStringData
|
||||
} else if EmuMemoryMapSection.ioArea.range.contains(address) {
|
||||
return .ioArea
|
||||
} else if EmuMemoryMapSection.bankSwitched.range.contains(address) {
|
||||
return .bankSwitched
|
||||
} else if EmuMemoryMapSection.auxBanks.range.contains(address) {
|
||||
return .auxBanks
|
||||
} else {
|
||||
return .auxBanks
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -112,6 +129,8 @@ protocol DebugMemoryViewControllerDelegate: class {
|
|||
var actionControllerAnimatorProgress = 0.0
|
||||
var animator: UIViewPropertyAnimator?
|
||||
|
||||
var cheatFinder = CheatFinderManager()
|
||||
|
||||
private var displayLink: CADisplayLink?
|
||||
private var framesSince = 0
|
||||
private var framesSinceUpdateScreen = 0
|
||||
|
@ -211,7 +230,7 @@ protocol DebugMemoryViewControllerDelegate: class {
|
|||
}
|
||||
|
||||
func setupActionController() {
|
||||
let actionController = DebugMemoryActionViewController()
|
||||
let actionController = DebugMemoryActionViewController(cheatFinderManager: cheatFinder)
|
||||
delegate = actionController
|
||||
actionController.delegate = self
|
||||
addChild(actionController)
|
||||
|
@ -335,6 +354,7 @@ protocol DebugMemoryViewControllerDelegate: class {
|
|||
super.viewDidAppear(animated)
|
||||
dataTableView.reloadData()
|
||||
delegate?.refreshActionController()
|
||||
cheatFinder.start()
|
||||
}
|
||||
|
||||
@objc func handlePan(_ recognizer: UIPanGestureRecognizer) {
|
||||
|
@ -414,7 +434,7 @@ extension DebugMemoryViewController: UITableViewDataSource {
|
|||
if tableView == codeTableView {
|
||||
return 1
|
||||
} else {
|
||||
return EmuMemoryMapSection.applesoftStringData.rawValue + 1
|
||||
return EmuMemoryMapSection.auxBanks.rawValue + 1
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,14 +7,8 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
class DebugMemoryButton: UIButton {
|
||||
class ToggleButton: UIButton {
|
||||
var onTapped: ((Bool) -> Void)?
|
||||
|
||||
override open var isSelected: Bool {
|
||||
didSet {
|
||||
backgroundColor = isSelected ? .white : .clear
|
||||
}
|
||||
}
|
||||
|
||||
convenience init() {
|
||||
self.init(type: .custom)
|
||||
|
@ -27,7 +21,15 @@ class DebugMemoryButton: UIButton {
|
|||
}
|
||||
}
|
||||
|
||||
class DebugPauseResumeButton: DebugMemoryButton {
|
||||
class DebugMemoryButton: ToggleButton {
|
||||
override open var isSelected: Bool {
|
||||
didSet {
|
||||
backgroundColor = isSelected ? .white : .clear
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DebugPauseResumeButton: ToggleButton {
|
||||
override open var isSelected: Bool {
|
||||
didSet {
|
||||
backgroundColor = isSelected ? .red : .clear
|
||||
|
|
|
@ -61,7 +61,7 @@ class DebugMemoryCell: UITableViewCell {
|
|||
self.delegate = delegate
|
||||
self.offset = offset
|
||||
stackView.arrangedSubviews.forEach{ $0.removeFromSuperview() }
|
||||
addressLabel.text = String(format: "%04X:", offset)
|
||||
addressLabel.text = String(format: "%05X:", offset)
|
||||
stackView.addArrangedSubview(addressLabel)
|
||||
stackView.setCustomSpacing(3, after: addressLabel)
|
||||
for (index, hexValue) in hexMemoryValues.enumerated() {
|
||||
|
|
|
@ -104,14 +104,23 @@ class EmulatorKeyboardView: UIView {
|
|||
return stackView
|
||||
}()
|
||||
|
||||
let dragMeView: UILabel = {
|
||||
let label = UILabel(frame: .zero)
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
label.text = "DRAG ME!"
|
||||
label.textColor = UIColor.systemRed
|
||||
return label
|
||||
let dragMeView: UIView = {
|
||||
let view = UIView(frame: .zero)
|
||||
view.backgroundColor = .white
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.widthAnchor.constraint(equalToConstant: 80).isActive = true
|
||||
view.heightAnchor.constraint(equalToConstant: 2).isActive = true
|
||||
let outerView = UIView(frame: .zero)
|
||||
outerView.backgroundColor = .clear
|
||||
outerView.translatesAutoresizingMaskIntoConstraints = false
|
||||
outerView.addSubview(view)
|
||||
view.centerXAnchor.constraint(equalTo: outerView.centerXAnchor).isActive = true
|
||||
view.centerYAnchor.constraint(equalTo: outerView.centerYAnchor).isActive = true
|
||||
outerView.heightAnchor.constraint(equalToConstant: 20).isActive = true
|
||||
outerView.widthAnchor.constraint(equalToConstant: 100).isActive = true
|
||||
return outerView
|
||||
}()
|
||||
|
||||
|
||||
private var pressedKeyLabels = [String: UILabel]()
|
||||
|
||||
convenience init() {
|
||||
|
@ -145,7 +154,7 @@ class EmulatorKeyboardView: UIView {
|
|||
alternateKeyRowsStackView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor, constant: -4.0).isActive = true
|
||||
addSubview(dragMeView)
|
||||
dragMeView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
|
||||
dragMeView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8.0).isActive = true
|
||||
dragMeView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
|
||||
}
|
||||
|
||||
|
||||
|
@ -172,6 +181,14 @@ class EmulatorKeyboardView: UIView {
|
|||
viewModel.keyPressed(sender.key)
|
||||
}
|
||||
|
||||
@objc private func keyCancelled(_ sender: KeyboardButton) {
|
||||
let title = sender.titleLabel?.text ?? "😭"
|
||||
if let label = pressedKeyLabels[title] {
|
||||
label.removeFromSuperview()
|
||||
pressedKeyLabels.removeValue(forKey: title)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func keyReleased(_ sender: KeyboardButton) {
|
||||
if sender.key.keyCode == AppleKeyboardKey.KEY_SPECIAL_TOGGLE.rawValue {
|
||||
delegate?.toggleAlternateKeys()
|
||||
|
@ -243,6 +260,7 @@ class EmulatorKeyboardView: UIView {
|
|||
key.addTarget(self, action: #selector(keyPressed(_:)), for: .touchDown)
|
||||
key.addTarget(self, action: #selector(keyReleased(_:)), for: .touchUpInside)
|
||||
key.addTarget(self, action: #selector(keyReleased(_:)), for: .touchUpOutside)
|
||||
key.addTarget(self, action: #selector(keyCancelled(_:)), for: .touchCancel)
|
||||
if keyCoded.isModifier {
|
||||
modifierButtons.update(with: key)
|
||||
}
|
||||
|
@ -560,7 +578,7 @@ struct KeyPosition {
|
|||
AppleIIKey(label: "SPACE", code: AppleKeyboardKey.KEY_SPACE.rawValue, keySize: .wide),
|
||||
AppleIIKey(label: "SHIFT", code: AppleKeyboardKey.KEY_SHIFT.rawValue,
|
||||
keySize: .standard, isModifier: true, imageName: "shift", imageNameHighlighted: "shift.fill"),
|
||||
AppleIIKey(label: "DELETE", code: AppleKeyboardKey.KEY_DELETE.rawValue, imageName: "delete.left", imageNameHighlighted: "delete.left.fill")
|
||||
AppleIIKey(label: "DELETE", code: AppleKeyboardKey.KEY_DELETE.rawValue, imageName: "delete.left", imageNameHighlighted: "delete.left.fill")
|
||||
],
|
||||
[
|
||||
AppleIIKey(label: "\(UnicodeScalar(0xe080)!)", code: AppleKeyboardKey.KEY_OPTION.rawValue,
|
||||
|
@ -774,8 +792,8 @@ struct KeyPosition {
|
|||
keyboardConstraints.removeAll()
|
||||
leftKeyboardView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(leftKeyboardView)
|
||||
leftKeyboardView.heightAnchor.constraint(equalToConstant: 250).isActive = true
|
||||
leftKeyboardView.widthAnchor.constraint(equalToConstant: 175).isActive = true
|
||||
leftKeyboardView.heightAnchor.constraint(equalToConstant: 270).isActive = true
|
||||
leftKeyboardView.widthAnchor.constraint(equalToConstant: 180).isActive = true
|
||||
keyboardConstraints.append(contentsOf: [
|
||||
leftKeyboardView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
|
||||
leftKeyboardView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
|
||||
|
@ -786,8 +804,8 @@ struct KeyPosition {
|
|||
rightKeyboardView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
|
||||
rightKeyboardView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
|
||||
])
|
||||
rightKeyboardView.heightAnchor.constraint(equalToConstant: 250).isActive = true
|
||||
rightKeyboardView.widthAnchor.constraint(equalToConstant: 175).isActive = true
|
||||
rightKeyboardView.heightAnchor.constraint(equalToConstant: 270).isActive = true
|
||||
rightKeyboardView.widthAnchor.constraint(equalToConstant: 180).isActive = true
|
||||
NSLayoutConstraint.activate(keyboardConstraints)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue