Skip to content

Commit fbdad3e

Browse files
committed
Show update message if game is outdated
1 parent 7319361 commit fbdad3e

File tree

10 files changed

+150
-94
lines changed

10 files changed

+150
-94
lines changed

3gx/includes/memmem.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#include <3ds.h>
2+
3+
void *memmem (const void *haystack, size_t haystack_len, const void *needle, size_t needle_len);

3gx/includes/title_info.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
#include <3ds.h>
44

55
u64 get_title_id();
6+
u64 get_remaster_version();

3gx/sources/main.c

Lines changed: 19 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,7 @@
99
#include "pokereader.h"
1010
#include "title_info.h"
1111
#include "hid.h"
12-
13-
typedef enum SupportedTitle
14-
{
15-
GAME_X = 0x0004000000055D00,
16-
GAME_Y = 0x0004000000055E00,
17-
GAME_OR = 0x000400000011C400,
18-
GAME_AS = 0x000400000011C500,
19-
GAME_S = 0x0004000000164800,
20-
GAME_M = 0x0004000000175E00,
21-
GAME_US = 0x00040000001B5000,
22-
GAME_UM = 0x00040000001B5100,
23-
GAME_TRANSPORTER = 0x00040000000C9C00,
24-
GAME_CRYSTAL_EN = 0x0004000000172800,
25-
GAME_CRYSTAL_DE = 0x0004000000172B00,
26-
GAME_CRYSTAL_FR = 0x0004000000172E00,
27-
GAME_CRYSTAL_ES = 0x0004000000173100,
28-
GAME_CRYSTAL_IT = 0x0004000000173400,
29-
} SupportedTitle;
12+
#include "memmem.h"
3013

3114
static Handle thread;
3215
static Handle memLayoutChanged;
@@ -142,6 +125,15 @@ u8 HID_INPUT_MAP_PATCH[0x8] = {
142125
0x00, 0xf0, 0x1f, 0xe5, // ldr pc,[pc + 0x8]
143126
};
144127

128+
u8 PRESENT_FRAMEBUFFER_BYTES[0X10] = {
129+
0x28, 0x00, 0x8d, 0xe2, 0x00, 0x80, 0xa0, 0xe3, 0x01, 0x70, 0xa0, 0xe1, 0x00, 0x0e, 0x90, 0xe8,
130+
};
131+
132+
u8 MAP_INPUT_BLOCK[] = {
133+
0x01, 0x20, 0xa0, 0x13, 0x03, 0x20, 0xa0, 0x03, 0x01, 0x32, 0xa0, 0xe3, 0x1f, 0x00,
134+
0x00, 0xef, 0xa0, 0x1f, 0xb0, 0xe1, 0x01, 0x10, 0xa0, 0x03, 0x18, 0x10, 0xc4, 0x05
135+
};
136+
145137
extern char *fake_heap_start;
146138
extern char *fake_heap_end;
147139
extern u32 __ctru_heap;
@@ -181,76 +173,20 @@ void main(void)
181173
// Get memory layout changed event
182174
svcControlProcess(CUR_PROCESS_HANDLE, PROCESSOP_GET_ON_MEMORY_CHANGE_EVENT, (u32)&memLayoutChanged, 0);
183175

184-
u32 present_buffer_ptr = 0;
185-
u32 get_screen_jump_inst = 0;
186-
u32 map_input_memory_block = 0;
187-
188-
switch (get_title_id())
189-
{
190-
case GAME_X:
191-
present_buffer_ptr = 0x149354;
192-
get_screen_jump_inst = 0xeb006cca;
193-
map_input_memory_block = 0x133dfc;
194-
break;
195-
case GAME_Y:
196-
present_buffer_ptr = 0x149354;
197-
get_screen_jump_inst = 0xeb006cca;
198-
map_input_memory_block = 0x133dfc;
199-
break;
200-
case GAME_OR:
201-
present_buffer_ptr = 0x148758;
202-
get_screen_jump_inst = 0xeb0071d4;
203-
map_input_memory_block = 0x1331e8;
204-
break;
205-
case GAME_AS:
206-
present_buffer_ptr = 0x148758;
207-
get_screen_jump_inst = 0xeb0071d4;
208-
map_input_memory_block = 0x1331e8;
209-
break;
210-
case GAME_S:
211-
present_buffer_ptr = 0x278540;
212-
get_screen_jump_inst = 0xeb0003d3;
213-
map_input_memory_block = 0x170eac;
214-
break;
215-
case GAME_M:
216-
present_buffer_ptr = 0x278540;
217-
get_screen_jump_inst = 0xeb0003d3;
218-
map_input_memory_block = 0x170eac;
219-
break;
220-
case GAME_US:
221-
present_buffer_ptr = 0x279bb4;
222-
get_screen_jump_inst = 0xeb0003d3;
223-
map_input_memory_block = 0x17234c;
224-
break;
225-
case GAME_UM:
226-
present_buffer_ptr = 0x279bb4;
227-
get_screen_jump_inst = 0xeb0003d3;
228-
map_input_memory_block = 0x17234c;
229-
break;
230-
case GAME_TRANSPORTER:
231-
present_buffer_ptr = 0x12b7ec;
232-
get_screen_jump_inst = 0xeb02cbd4;
233-
map_input_memory_block = 0x11f63c;
234-
break;
235-
case GAME_CRYSTAL_EN:
236-
case GAME_CRYSTAL_DE:
237-
case GAME_CRYSTAL_FR:
238-
case GAME_CRYSTAL_ES:
239-
case GAME_CRYSTAL_IT:
240-
present_buffer_ptr = 0x14aa24;
241-
get_screen_jump_inst = 0xeb00b512;
242-
map_input_memory_block = 0x146a28;
243-
break;
244-
default:
245-
return;
246-
}
176+
MemInfo info;
177+
PageInfo out;
178+
svcQueryMemory(&info, &out, 0x100000);
179+
180+
u32 present_buffer_ptr = (u32)memmem((u8*)info.base_addr, info.size, PRESENT_FRAMEBUFFER_BYTES, sizeof(PRESENT_FRAMEBUFFER_BYTES)) - 8;
181+
u32 map_input_memory_block = (u32)memmem((u8*)info.base_addr, info.size, MAP_INPUT_BLOCK, sizeof(MAP_INPUT_BLOCK));
247182

183+
u32 get_screen_branch = *(u32 *)(present_buffer_ptr + 0x20) - 4;
248184
u32 *present_buffer_pa = (u32 *)PA_FROM_VA_PTR(present_buffer_ptr);
249185
memcpy(present_buffer_pa, DRAW_PATCH, 0x94);
250186
present_buffer_pa[0x4] = (u32)run_hook;
251187
// 7 instructions * 4 bytes per instruction
252-
present_buffer_pa[0x5] = (u32)present_buffer_ptr + (0x7 * 0x4); // set return address
253-
present_buffer_pa[0xc] = get_screen_jump_inst; // fix get_screen branch instruction
188+
present_buffer_pa[0x5] = present_buffer_ptr + (0x7 * 0x4); // set return address
189+
present_buffer_pa[0xc] = get_screen_branch; // fix get_screen branch instruction
254190

255191
u32 *map_input_memory_block_pa = (u32 *)PA_FROM_VA_PTR(map_input_memory_block);
256192
memcpy(map_input_memory_block_pa, HID_INPUT_MAP_PATCH, 0x8);

3gx/sources/memmem.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/* Copyright (C) 1991-2025 Free Software Foundation, Inc.
2+
This file is part of the GNU C Library.
3+
4+
This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation; either version 2, or (at your option)
7+
any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License along
15+
with this program; if not, write to the Free Software Foundation,
16+
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
17+
18+
/*
19+
20+
@deftypefn Supplemental void* memmem (const void *@var{haystack}, @
21+
size_t @var{haystack_len} const void *@var{needle}, size_t @var{needle_len})
22+
23+
Returns a pointer to the first occurrence of @var{needle} (length
24+
@var{needle_len}) in @var{haystack} (length @var{haystack_len}).
25+
Returns @code{NULL} if not found.
26+
27+
@end deftypefn
28+
29+
*/
30+
31+
#include <stddef.h>
32+
#include <string.h>
33+
34+
/* Return the first occurrence of NEEDLE in HAYSTACK. */
35+
void *
36+
memmem (const void *haystack, size_t haystack_len, const void *needle,
37+
size_t needle_len)
38+
{
39+
const char *begin;
40+
const char *const last_possible
41+
= (const char *) haystack + haystack_len - needle_len;
42+
43+
if (needle_len == 0)
44+
/* The first occurrence of the empty string is deemed to occur at
45+
the beginning of the string. */
46+
return (void *) haystack;
47+
48+
/* Sanity check, otherwise the loop might search through the whole
49+
memory. */
50+
if (__builtin_expect (haystack_len < needle_len, 0))
51+
return NULL;
52+
53+
for (begin = (const char *) haystack; begin <= last_possible; ++begin)
54+
if (begin[0] == ((const char *) needle)[0] &&
55+
!memcmp ((const void *) &begin[1],
56+
(const void *) ((const char *) needle + 1),
57+
needle_len - 1))
58+
return (void *) begin;
59+
60+
return NULL;
61+
}

3gx/sources/title_info.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ u64 g_program_id = 0;
55

66
u64 get_title_id()
77
{
8-
98
if (g_program_id == 0)
109
{
1110
fsInit();
@@ -18,4 +17,20 @@ u64 get_title_id()
1817
}
1918

2019
return g_program_id;
21-
}
20+
}
21+
22+
u64 g_remaster_version = 0;
23+
24+
u64 get_remaster_version() {
25+
if (g_remaster_version == 0) {
26+
fsInit();
27+
u32 processId = 0;
28+
svcGetProcessId(&processId, CUR_PROCESS_HANDLE);
29+
FS_ProductInfo info;
30+
FSUSER_GetProductInfo(&info, processId);
31+
fsExit();
32+
g_remaster_version = info.remasterVersion;
33+
}
34+
35+
return g_remaster_version;
36+
}

reader_core/src/gen6/game_lib.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
use crate::title::title_id;
2-
use crate::title::SupportedTitle;
1+
use crate::title::{supported_title, SupportedTitle};
32
use crate::utils::game_fn;
43

54
game_fn!(xy_get_seed_hash() -> u64 = 0x10cad8);
65
game_fn!(oras_get_seed_hash() -> u64 = 0x10ca94);
76

87
pub fn get_seed_hash() -> u64 {
9-
match title_id() {
8+
match supported_title() {
109
SupportedTitle::X | SupportedTitle::Y => xy_get_seed_hash(),
1110
SupportedTitle::Or | SupportedTitle::As => oras_get_seed_hash(),
1211
_ => 0,

reader_core/src/lib.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ mod title;
1616
mod transporter;
1717
mod utils;
1818

19-
use title::{title_id, SupportedTitle};
19+
use title::{supported_title, SupportedTitle};
2020

2121
#[cfg(target_os = "horizon")]
2222
#[panic_handler]
@@ -45,7 +45,7 @@ fn my_panic(info: &core::panic::PanicInfo) -> ! {
4545
#[cfg(target_os = "horizon")]
4646
#[no_mangle]
4747
pub extern "C" fn initialize() {
48-
match title_id() {
48+
match supported_title() {
4949
SupportedTitle::S | SupportedTitle::M => gen7::init_sm(),
5050
SupportedTitle::Us => gen7::init_us(),
5151
SupportedTitle::Um => gen7::init_um(),
@@ -63,7 +63,7 @@ pub extern "C" fn initialize() {
6363

6464
#[no_mangle]
6565
pub extern "C" fn run_frame() {
66-
match title_id() {
66+
match supported_title() {
6767
SupportedTitle::S | SupportedTitle::M => gen7::run_sm_frame(),
6868
SupportedTitle::Us | SupportedTitle::Um => gen7::run_usum_frame(),
6969
SupportedTitle::Or | SupportedTitle::As => gen6::run_oras_frame(),
@@ -74,6 +74,11 @@ pub extern "C" fn run_frame() {
7474
| SupportedTitle::CrystalFr
7575
| SupportedTitle::CrystalEs
7676
| SupportedTitle::CrystalIt => crystal::run_frame(),
77-
SupportedTitle::Invalid => {}
77+
SupportedTitle::Invalid => {
78+
pnp::println!("Unsupported game update!");
79+
pnp::println!("");
80+
pnp::println!("Please update your game");
81+
pnp::println!("for PokeReader to run");
82+
}
7883
}
7984
}

reader_core/src/pnp/bindings.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ extern "C" {
77
pub fn host_get_game_title_id() -> u64;
88
pub fn host_game_start_ms() -> u64;
99
pub fn get_current_keys() -> u32;
10+
pub fn get_remaster_version() -> u64;
1011
pub fn osGetTime() -> u64;
1112
}
1213

@@ -40,4 +41,8 @@ pub mod test_stubs {
4041
pub extern "C" fn osGetTime() -> u64 {
4142
0
4243
}
44+
#[no_mangle]
45+
pub extern "C" fn get_remaster_version() -> u64 {
46+
0
47+
}
4348
}

reader_core/src/pnp/utils.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,7 @@ use super::bindings;
44
pub fn title_id() -> u64 {
55
unsafe { bindings::host_get_game_title_id() }
66
}
7+
8+
pub fn update_version() -> u64 {
9+
unsafe { bindings::get_remaster_version() }
10+
}

reader_core/src/title.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,33 @@ pub enum SupportedTitle {
2222
CrystalIt = 0x0004000000173400,
2323
}
2424

25-
pub fn title_id() -> SupportedTitle {
26-
pnp::title_id().into()
25+
static mut LOADED_TITLE: SupportedTitle = SupportedTitle::Invalid;
26+
27+
pub fn supported_title() -> SupportedTitle {
28+
// Reader is single-threaded, so this is safe.
29+
// Even then, title and update version will also always be the same values.
30+
unsafe {
31+
if LOADED_TITLE == SupportedTitle::Invalid {
32+
let title = pnp::title_id().into();
33+
LOADED_TITLE = match (title, pnp::update_version()) {
34+
(SupportedTitle::S, 2)
35+
| (SupportedTitle::M, 2)
36+
| (SupportedTitle::Us, 2)
37+
| (SupportedTitle::Um, 2)
38+
| (SupportedTitle::Or, 7)
39+
| (SupportedTitle::As, 7)
40+
| (SupportedTitle::X, 5)
41+
| (SupportedTitle::Y, 5)
42+
| (SupportedTitle::Transporter, 5)
43+
| (SupportedTitle::CrystalEn, 0)
44+
| (SupportedTitle::CrystalDe, 0)
45+
| (SupportedTitle::CrystalFr, 0)
46+
| (SupportedTitle::CrystalEs, 0)
47+
| (SupportedTitle::CrystalIt, 0) => title,
48+
(_, _) => SupportedTitle::Invalid,
49+
};
50+
}
51+
52+
LOADED_TITLE
53+
}
2754
}

0 commit comments

Comments
 (0)