Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Libretro
melonDS
Commits
f8d1d08e
Commit
f8d1d08e
authored
Aug 15, 2020
by
Arisotura
Browse files
(finally) build the goddamn cheat interface
parent
4299ef5f
Changes
11
Hide whitespace changes
Inline
Side-by-side
src/ARCodeFile.cpp
View file @
f8d1d08e
...
...
@@ -35,8 +35,21 @@ ARCodeFile::ARCodeFile(const char* filename)
Categories
.
clear
();
if
(
!
Load
())
Error
=
true
;
}
ARCodeFile
::~
ARCodeFile
()
{
Categories
.
clear
();
}
bool
ARCodeFile
::
Load
()
{
FILE
*
f
=
Platform
::
OpenFile
(
Filename
,
"r"
);
if
(
!
f
)
return
;
if
(
!
f
)
return
true
;
Categories
.
clear
();
bool
isincat
=
false
;
ARCodeCat
curcat
;
...
...
@@ -60,15 +73,14 @@ ARCodeFile::ARCodeFile(const char* filename)
if
(
!
strncasecmp
(
start
,
"CAT"
,
3
))
{
char
catname
[
128
];
int
ret
=
sscanf
(
start
,
"CAT %127[^
\n
]"
,
catname
);
int
ret
=
sscanf
(
start
,
"CAT %127[^
\
r\
n
]"
,
catname
);
catname
[
127
]
=
'\0'
;
if
(
ret
<
1
)
{
printf
(
"AR: malformed CAT line: %s
\n
"
,
start
);
fclose
(
f
);
Error
=
true
;
return
;
return
false
;
}
if
(
isincode
)
curcat
.
Codes
.
push_back
(
curcode
);
...
...
@@ -84,23 +96,21 @@ ARCodeFile::ARCodeFile(const char* filename)
{
int
enable
;
char
codename
[
128
];
int
ret
=
sscanf
(
start
,
"CODE %d %127[^
\n
]"
,
&
enable
,
codename
);
int
ret
=
sscanf
(
start
,
"CODE %d %127[^
\
r\
n
]"
,
&
enable
,
codename
);
codename
[
127
]
=
'\0'
;
if
(
ret
<
2
)
{
printf
(
"AR: malformed CODE line: %s
\n
"
,
start
);
fclose
(
f
);
Error
=
true
;
return
;
return
false
;
}
if
(
!
isincat
)
{
printf
(
"AR: encountered CODE line with no category started
\n
"
);
fclose
(
f
);
Error
=
true
;
return
;
return
false
;
}
if
(
isincode
)
curcat
.
Codes
.
push_back
(
curcode
);
...
...
@@ -119,24 +129,21 @@ ARCodeFile::ARCodeFile(const char* filename)
{
printf
(
"AR: malformed data line: %s
\n
"
,
start
);
fclose
(
f
);
Error
=
true
;
return
;
return
false
;
}
if
(
!
isincode
)
{
printf
(
"AR: encountered data line with no code started
\n
"
);
fclose
(
f
);
Error
=
true
;
return
;
return
false
;
}
if
(
curcode
.
CodeLen
>=
2
*
64
)
{
printf
(
"AR: code too long!
\n
"
);
fclose
(
f
);
Error
=
true
;
return
;
return
false
;
}
u32
idx
=
curcode
.
CodeLen
;
...
...
@@ -150,27 +157,35 @@ ARCodeFile::ARCodeFile(const char* filename)
if
(
isincat
)
Categories
.
push_back
(
curcat
);
fclose
(
f
);
return
true
;
}
bool
ARCodeFile
::
Save
()
{
FILE
*
f
=
Platform
::
OpenFile
(
Filename
,
"w"
);
if
(
!
f
)
return
false
;
printf
(
"PARSED OUTPUT
\n
"
);
for
(
ARCodeCatList
::
iterator
it
=
Categories
.
begin
();
it
!=
Categories
.
end
();
it
++
)
{
ARCodeCat
&
cat
=
*
it
;
printf
(
"CAT %s
\n
"
,
cat
.
Name
);
if
(
it
!=
Categories
.
begin
())
fprintf
(
f
,
"
\n
"
);
fprintf
(
f
,
"CAT %s
\n\n
"
,
cat
.
Name
);
for
(
ARCodeList
::
iterator
jt
=
cat
.
Codes
.
begin
();
jt
!=
cat
.
Codes
.
end
();
jt
++
)
{
ARCode
&
code
=
*
jt
;
printf
(
"CODE %d %s
\n
"
,
code
.
Enabled
,
code
.
Name
);
f
printf
(
f
,
"CODE %d %s
\n
"
,
code
.
Enabled
,
code
.
Name
);
for
(
u32
i
=
0
;
i
<
code
.
CodeLen
;
i
+=
2
)
{
printf
(
"%08X %08X
\n
"
,
code
.
Code
[
i
],
code
.
Code
[
i
+
1
]);
f
printf
(
f
,
"%08X %08X
\n
"
,
code
.
Code
[
i
],
code
.
Code
[
i
+
1
]);
}
fprintf
(
f
,
"
\n
"
);
}
}
}
ARCodeFile
::~
ARCodeFile
()
{
//
fclose
(
f
);
return
true
;
}
src/ARCodeFile.h
View file @
f8d1d08e
...
...
@@ -19,7 +19,7 @@
#ifndef ARCODEFILE_H
#define ARCODEFILE_H
#include <
vector
>
#include <
list
>
#include "types.h"
...
...
@@ -32,7 +32,7 @@ typedef struct
}
ARCode
;
typedef
std
::
vector
<
ARCode
>
ARCodeList
;
typedef
std
::
list
<
ARCode
>
ARCodeList
;
typedef
struct
{
...
...
@@ -41,7 +41,7 @@ typedef struct
}
ARCodeCat
;
typedef
std
::
vector
<
ARCodeCat
>
ARCodeCatList
;
typedef
std
::
list
<
ARCodeCat
>
ARCodeCatList
;
class
ARCodeFile
...
...
@@ -52,6 +52,7 @@ public:
bool
Error
;
bool
Load
();
bool
Save
();
ARCodeCatList
Categories
;
...
...
src/Config.cpp
View file @
f8d1d08e
...
...
@@ -104,7 +104,7 @@ void Load()
while
(
!
feof
(
f
))
{
fgets
(
linebuf
,
1024
,
f
);
int
ret
=
sscanf
(
linebuf
,
"%31[A-Za-z_0-9]=%[^
\t\n
]"
,
entryname
,
entryval
);
int
ret
=
sscanf
(
linebuf
,
"%31[A-Za-z_0-9]=%[^
\t\
r\
n
]"
,
entryname
,
entryval
);
entryname
[
31
]
=
'\0'
;
if
(
ret
<
2
)
continue
;
...
...
src/frontend/FrontendUtil.h
View file @
f8d1d08e
...
...
@@ -67,6 +67,9 @@ extern bool SavestateLoaded;
// initialize the ROM handling utility
void
Init_ROM
();
// deinitialize the ROM handling utility
void
DeInit_ROM
();
// load the BIOS/firmware and boot from it
int
LoadBIOS
();
...
...
@@ -97,6 +100,9 @@ bool SaveState(const char* filename);
// undo the latest savestate load
void
UndoStateLoad
();
// enable or disable cheats
void
EnableCheats
(
bool
enable
);
// setup the display layout based on the provided display size and parameters
// * screenWidth/screenHeight: size of the host display
...
...
src/frontend/Util_ROM.cpp
View file @
f8d1d08e
...
...
@@ -27,6 +27,8 @@
#include "NDS.h"
#include "GBACart.h"
#include "AREngine.h"
namespace
Frontend
{
...
...
@@ -37,6 +39,9 @@ char PrevSRAMPath[ROMSlot_MAX][1024]; // for savestate 'undo load'
bool
SavestateLoaded
;
ARCodeFile
*
CheatFile
;
bool
CheatsOn
;
void
Init_ROM
()
{
...
...
@@ -48,6 +53,18 @@ void Init_ROM()
memset
(
SRAMPath
[
ROMSlot_GBA
],
0
,
1024
);
memset
(
PrevSRAMPath
[
ROMSlot_NDS
],
0
,
1024
);
memset
(
PrevSRAMPath
[
ROMSlot_GBA
],
0
,
1024
);
CheatFile
=
nullptr
;
CheatsOn
=
false
;
}
void
DeInit_ROM
()
{
if
(
CheatFile
)
{
delete
CheatFile
;
CheatFile
=
nullptr
;
}
}
// TODO: currently, when failing to load a ROM for whatever reason, we attempt
...
...
@@ -198,6 +215,25 @@ int VerifyDSiNAND()
return
Load_OK
;
}
void
LoadCheats
()
{
if
(
CheatFile
)
{
delete
CheatFile
;
CheatFile
=
nullptr
;
}
char
filename
[
1024
];
strncpy
(
filename
,
ROMPath
[
ROMSlot_NDS
],
1023
);
filename
[
1023
]
=
'\0'
;
strncpy
(
filename
+
strlen
(
ROMPath
[
ROMSlot_NDS
])
-
3
,
"mch"
,
3
);
// TODO: check for error (malformed cheat file, ...)
CheatFile
=
new
ARCodeFile
(
filename
);
AREngine
::
SetCodeFile
(
CheatsOn
?
CheatFile
:
nullptr
);
}
int
LoadBIOS
()
{
int
res
;
...
...
@@ -235,6 +271,8 @@ int LoadBIOS()
SavestateLoaded
=
false
;
LoadCheats
();
return
Load_OK
;
}
...
...
@@ -295,6 +333,8 @@ int LoadROM(const char* file, int slot)
{
SavestateLoaded
=
false
;
LoadCheats
();
// Reload the inserted GBA cartridge (if any)
// TODO: report failure there??
if
(
ROMPath
[
ROMSlot_GBA
][
0
]
!=
'\0'
)
NDS
::
LoadGBAROM
(
ROMPath
[
ROMSlot_GBA
],
SRAMPath
[
ROMSlot_GBA
]);
...
...
@@ -387,6 +427,8 @@ int Reset()
return
Load_ROMLoadError
;
}
LoadCheats
();
return
Load_OK
;
}
...
...
@@ -539,4 +581,11 @@ void UndoStateLoad()
}
}
void
EnableCheats
(
bool
enable
)
{
CheatsOn
=
enable
;
if
(
CheatFile
)
AREngine
::
SetCodeFile
(
CheatsOn
?
CheatFile
:
nullptr
);
}
}
src/frontend/qt_sdl/CheatsDialog.cpp
View file @
f8d1d08e
...
...
@@ -18,6 +18,7 @@
#include <stdio.h>
#include <QFileDialog>
#include <QMessageBox>
#include "types.h"
#include "Platform.h"
...
...
@@ -32,30 +33,380 @@ CheatsDialog* CheatsDialog::currentDlg = nullptr;
extern
char
*
EmuDirectory
;
namespace
Frontend
{
extern
ARCodeFile
*
CheatFile
;
}
CheatsDialog
::
CheatsDialog
(
QWidget
*
parent
)
:
QDialog
(
parent
),
ui
(
new
Ui
::
CheatsDialog
)
{
ui
->
setupUi
(
this
);
setAttribute
(
Qt
::
WA_DeleteOnClose
);
// setup UI here
codeFile
=
Frontend
::
CheatFile
;
QStandardItemModel
*
model
=
new
QStandardItemModel
();
ui
->
tvCodeList
->
setModel
(
model
);
connect
(
model
,
&
QStandardItemModel
::
itemChanged
,
this
,
&
CheatsDialog
::
onCheatEntryModified
);
connect
(
ui
->
tvCodeList
->
selectionModel
(),
&
QItemSelectionModel
::
selectionChanged
,
this
,
&
CheatsDialog
::
onCheatSelectionChanged
);
{
QStandardItem
*
root
=
model
->
invisibleRootItem
();
for
(
ARCodeCatList
::
iterator
i
=
codeFile
->
Categories
.
begin
();
i
!=
codeFile
->
Categories
.
end
();
i
++
)
{
ARCodeCat
&
cat
=
*
i
;
QStandardItem
*
catitem
=
new
QStandardItem
(
cat
.
Name
);
catitem
->
setEditable
(
true
);
catitem
->
setData
(
QVariant
::
fromValue
(
i
));
root
->
appendRow
(
catitem
);
for
(
ARCodeList
::
iterator
j
=
cat
.
Codes
.
begin
();
j
!=
cat
.
Codes
.
end
();
j
++
)
{
ARCode
&
code
=
*
j
;
QStandardItem
*
codeitem
=
new
QStandardItem
(
code
.
Name
);
codeitem
->
setEditable
(
true
);
codeitem
->
setCheckable
(
true
);
codeitem
->
setCheckState
(
code
.
Enabled
?
Qt
::
Checked
:
Qt
::
Unchecked
);
codeitem
->
setData
(
QVariant
::
fromValue
(
j
));
catitem
->
appendRow
(
codeitem
);
}
}
}
ui
->
txtCode
->
setPlaceholderText
(
""
);
codeChecker
=
new
ARCodeChecker
(
ui
->
txtCode
->
document
());
ui
->
btnNewARCode
->
setEnabled
(
false
);
ui
->
btnDeleteCode
->
setEnabled
(
false
);
ui
->
txtCode
->
setEnabled
(
false
);
}
CheatsDialog
::~
CheatsDialog
()
{
QAbstractItemModel
*
model
=
ui
->
tvCodeList
->
model
();
ui
->
tvCodeList
->
setModel
(
nullptr
);
delete
model
;
delete
codeChecker
;
delete
ui
;
}
void
CheatsDialog
::
on_CheatsDialog_accepted
()
{
// save shit here
codeFile
->
Save
();
closeDlg
();
}
void
CheatsDialog
::
on_CheatsDialog_rejected
()
{
// don't save shit here
codeFile
->
Load
();
closeDlg
();
}
void
CheatsDialog
::
on_btnNewCat_clicked
()
{
QStandardItem
*
root
=
((
QStandardItemModel
*
)
ui
->
tvCodeList
->
model
())
->
invisibleRootItem
();
ARCodeCat
cat
;
cat
.
Codes
.
clear
();
memset
(
cat
.
Name
,
0
,
128
);
strncpy
(
cat
.
Name
,
"(new category)"
,
127
);
codeFile
->
Categories
.
push_back
(
cat
);
ARCodeCatList
::
iterator
id
=
codeFile
->
Categories
.
end
();
id
--
;
QStandardItem
*
catitem
=
new
QStandardItem
(
cat
.
Name
);
catitem
->
setEditable
(
true
);
catitem
->
setData
(
QVariant
::
fromValue
(
id
));
root
->
appendRow
(
catitem
);
ui
->
tvCodeList
->
selectionModel
()
->
select
(
catitem
->
index
(),
QItemSelectionModel
::
ClearAndSelect
);
ui
->
tvCodeList
->
edit
(
catitem
->
index
());
}
void
CheatsDialog
::
on_btnNewARCode_clicked
()
{
QModelIndexList
indices
=
ui
->
tvCodeList
->
selectionModel
()
->
selectedIndexes
();
if
(
indices
.
isEmpty
())
{
// ????
return
;
}
QStandardItemModel
*
model
=
(
QStandardItemModel
*
)
ui
->
tvCodeList
->
model
();
QStandardItem
*
item
=
model
->
itemFromIndex
(
indices
.
first
());
QStandardItem
*
parentitem
;
QVariant
data
=
item
->
data
();
if
(
data
.
canConvert
<
ARCodeCatList
::
iterator
>
())
{
parentitem
=
item
;
}
else
if
(
data
.
canConvert
<
ARCodeList
::
iterator
>
())
{
parentitem
=
item
->
parent
();
}
else
{
printf
(
"what?? :(
\n
"
);
return
;
}
ARCodeCatList
::
iterator
it_cat
=
parentitem
->
data
().
value
<
ARCodeCatList
::
iterator
>
();
ARCodeCat
&
cat
=
*
it_cat
;
ARCode
code
;
memset
(
code
.
Name
,
0
,
128
);
strncpy
(
code
.
Name
,
"(new AR code)"
,
127
);
code
.
Enabled
=
true
;
code
.
CodeLen
=
0
;
memset
(
code
.
Code
,
0
,
sizeof
(
code
.
Code
));
cat
.
Codes
.
push_back
(
code
);
ARCodeList
::
iterator
id
=
cat
.
Codes
.
end
();
id
--
;
QStandardItem
*
codeitem
=
new
QStandardItem
(
code
.
Name
);
codeitem
->
setEditable
(
true
);
codeitem
->
setCheckable
(
true
);
codeitem
->
setCheckState
(
code
.
Enabled
?
Qt
::
Checked
:
Qt
::
Unchecked
);
codeitem
->
setData
(
QVariant
::
fromValue
(
id
));
parentitem
->
appendRow
(
codeitem
);
ui
->
tvCodeList
->
selectionModel
()
->
select
(
codeitem
->
index
(),
QItemSelectionModel
::
ClearAndSelect
);
ui
->
tvCodeList
->
edit
(
codeitem
->
index
());
}
void
CheatsDialog
::
on_btnDeleteCode_clicked
()
{
QModelIndexList
indices
=
ui
->
tvCodeList
->
selectionModel
()
->
selectedIndexes
();
if
(
indices
.
isEmpty
())
{
// ????
return
;
}
QMessageBox
::
StandardButton
res
=
QMessageBox
::
question
(
this
,
"Confirm deletion"
,
"Really delete the selected item?"
,
QMessageBox
::
Yes
|
QMessageBox
::
No
,
QMessageBox
::
No
);
if
(
res
!=
QMessageBox
::
Yes
)
return
;
QStandardItemModel
*
model
=
(
QStandardItemModel
*
)
ui
->
tvCodeList
->
model
();
QStandardItem
*
item
=
model
->
itemFromIndex
(
indices
.
first
());
QVariant
data
=
item
->
data
();
if
(
data
.
canConvert
<
ARCodeCatList
::
iterator
>
())
{
ARCodeCatList
::
iterator
it_cat
=
data
.
value
<
ARCodeCatList
::
iterator
>
();
(
*
it_cat
).
Codes
.
clear
();
codeFile
->
Categories
.
erase
(
it_cat
);
model
->
invisibleRootItem
()
->
removeRow
(
item
->
row
());
}
else
if
(
data
.
canConvert
<
ARCodeList
::
iterator
>
())
{
ARCodeList
::
iterator
it_code
=
data
.
value
<
ARCodeList
::
iterator
>
();
ARCodeCatList
::
iterator
it_cat
=
item
->
parent
()
->
data
().
value
<
ARCodeCatList
::
iterator
>
();
(
*
it_cat
).
Codes
.
erase
(
it_code
);
item
->
parent
()
->
removeRow
(
item
->
row
());
}
}
void
CheatsDialog
::
onCheatSelectionChanged
(
const
QItemSelection
&
sel
,
const
QItemSelection
&
desel
)
{
QModelIndexList
indices
=
sel
.
indexes
();
if
(
indices
.
isEmpty
())
{
ui
->
btnNewARCode
->
setEnabled
(
false
);
ui
->
btnDeleteCode
->
setEnabled
(
false
);
ui
->
txtCode
->
setEnabled
(
false
);
ui
->
txtCode
->
setPlaceholderText
(
""
);
ui
->
txtCode
->
clear
();
}
else
{
QStandardItem
*
item
=
((
QStandardItemModel
*
)
ui
->
tvCodeList
->
model
())
->
itemFromIndex
(
indices
.
first
());
QVariant
data
=
item
->
data
();
if
(
data
.
canConvert
<
ARCodeCatList
::
iterator
>
())
{
ui
->
btnDeleteCode
->
setEnabled
(
true
);
ui
->
txtCode
->
setEnabled
(
false
);
ui
->
txtCode
->
setPlaceholderText
(
""
);
ui
->
txtCode
->
clear
();
}
else
if
(
data
.
canConvert
<
ARCodeList
::
iterator
>
())
{
ARCode
&
code
=
*
(
data
.
value
<
ARCodeList
::
iterator
>
());
ui
->
btnDeleteCode
->
setEnabled
(
true
);
ui
->
txtCode
->
setEnabled
(
true
);
ui
->
txtCode
->
setPlaceholderText
(
"(enter AR code here)"
);
QString
codestr
=
""
;
for
(
u32
i
=
0
;
i
<
code
.
CodeLen
;
i
+=
2
)
{
u32
c0
=
code
.
Code
[
i
+
0
];
u32
c1
=
code
.
Code
[
i
+
1
];
//codestr += QString("%1 %2\n").arg(c0, 8, 16, '0').arg(c1, 8, 16, '0').toUpper();
codestr
+=
QString
::
asprintf
(
"%08X %08X
\n
"
,
c0
,
c1
);
}
ui
->
txtCode
->
setPlainText
(
codestr
);
}
ui
->
btnNewARCode
->
setEnabled
(
true
);
}
}
void
CheatsDialog
::
onCheatEntryModified
(
QStandardItem
*
item
)
{
QVariant
data
=
item
->
data
();
if
(
data
.
canConvert
<
ARCodeCatList
::
iterator
>
())
{
ARCodeCat
&
cat
=
*
(
data
.
value
<
ARCodeCatList
::
iterator
>
());
if
(
item
->
text
().
isEmpty
())
{
QString
oldname
=
QString
(
cat
.
Name
);
item
->
setText
(
oldname
.
isEmpty
()
?
"(blank category name??)"
:
oldname
);
}
else
{
strncpy
(
cat
.
Name
,
item
->
text
().
toStdString
().
c_str
(),
127
);
cat
.
Name
[
127
]
=
'\0'
;
}
}
else
if
(
data
.
canConvert
<
ARCodeList
::
iterator
>
())
{
ARCode
&
code
=
*
(
data
.
value
<
ARCodeList
::
iterator
>
());
if
(
item
->
text
().
isEmpty
())
{
QString
oldname
=
QString
(
code
.
Name
);
item
->
setText
(
oldname
.
isEmpty
()
?
"(blank code name??)"
:
oldname
);
}
else
{
strncpy
(
code
.
Name
,
item
->
text
().
toStdString
().
c_str
(),
127
);
code
.
Name
[
127
]
=
'\0'
;
}
code
.
Enabled
=
(
item
->
checkState
()
==
Qt
::
Checked
);
}
}
void
CheatsDialog
::
on_txtCode_textChanged
()
{
QModelIndexList
indices
=
ui
->
tvCodeList
->
selectionModel
()
->
selectedIndexes
();
if
(
indices
.
isEmpty
())
return
;
QStandardItem
*
item
=
((
QStandardItemModel
*
)
ui
->
tvCodeList
->
model
())
->
itemFromIndex
(
indices
.
first
());
QVariant
data
=
item
->
data
();
if
(
!
data
.
canConvert
<
ARCodeList
::
iterator
>
())
return
;
bool
error
=
false
;
u32
codeout
[
2
*
64
];
u32
codelen
=
0
;
QString
text
=
ui
->
txtCode
->
document
()
->
toPlainText
();
QStringList
lines
=
text
.
split
(
'\n'
,
QString
::
SkipEmptyParts
);
for
(
QStringList
::
iterator
it
=
lines
.
begin
();
it
!=
lines
.
end
();
it
++
)
{
QString
line
=
*
it
;
line
=
line
.
trimmed
();
if
(
line
.
isEmpty
())
continue
;
if
(
line
.
length
()
>
17
)
{
error
=
true
;
break
;
}
QStringList
numbers
=
line
.
split
(
' '
);
if
(
numbers
.
length
()
!=
2
)
{
error
=
true
;
break
;