Closed edwardmuel closed 7 years ago
I've tried to poke at the realtime address and it keeps reverting back. (EUR copy) What are you doing to time travel? Setting the towntime first then the realtime? And I have a macro to add and subtract values, so this can be easily put into a cheat in my plugin once we figure out how to change it :P
Wait, nvm, I started at 0x9E7AAA and time started moving. Either I can create a function where you type 8 AM and time goes to that or just press B + D pad right/left to go forward and backwards in time like in the City Folk Gecko version. Great find!
I set towntime first and then the realtime. Towntime is needed for the time travel to persist across saving. (If you add enough time to realtime to trigger the game's next-day sequence, the time will be reverted afterwards if you didn't set towntime.)
I threw together some quick code and it time travel doesn't work fully when visitors are in the town. The time changes on the host's system but stays the same on any clients. Nookling Junction is open on the host's system but closed on the client's system.
Feel free to use/improve this text-to-time snippet: (input format is YYMMDDHHmm-
(Years, "months", days, hours, minutes, - is optionally added for setting the time offset to be negative)
Example: 0100000000- will set the game's time offset to be 1 year in the past relative to the 3DS's system time.
Edit: It would probably be better to use weeks instead of months if doing it this way since it would be more accurate than just using 28 days = 1 month
Added to configs.h
:
#ifndef WRITES64
# define WRITES64(addr, data) *(vs64*)(addr) = data
#endif
Main code:
void text2item_eur(void)
{
u16 *id = (u16 *)0x16F4C460;
char yy_str[3] = { 0 };
char mm_str[3] = { 0 };
char dd_str[3] = { 0 };
char hh_str[3] = { 0 };
char mz_str[3] = { 0 };
char pm_str[2] = { 0 };
s64 res_year = 0;
s64 res_month = 0;
s64 res_day = 0;
s64 res_hour = 0;
s64 res_min = 0;
s64 res_nansec = 0;
s64 res_plmn = 1;
if (!is_pressed(BUTTON_X + BUTTON_DR))
return;
yy_str[0] = (char)READU8(id + 0);
yy_str[1] = (char)READU8(id + 1);
mm_str[0] = (char)READU8(id + 2);
mm_str[1] = (char)READU8(id + 3);
dd_str[0] = (char)READU8(id + 4);
dd_str[1] = (char)READU8(id + 5);
hh_str[0] = (char)READU8(id + 6);
hh_str[1] = (char)READU8(id + 7);
mz_str[0] = (char)READU8(id + 8);
mz_str[1] = (char)READU8(id + 9);
if ((char)READU8(id + 10) == 45) //minus
res_plmn = -1;
res_year = (s64)strtoul(yy_str, NULL, 16);
res_month = (s64)strtoul(mm_str, NULL, 16);
res_day = (s64)strtoul(dd_str, NULL, 16);
res_hour = (s64)strtoul(hh_str, NULL, 16);
res_min = (s64)strtoul(mz_str, NULL, 16);
res_year = res_year * (365 * 24 * 60 * 60) * (1000000000); //(year to seconds) * (seconds to nanoseconds)
res_month = res_month * (28 * 24 * 60 * 60) * (1000000000); //("month" to seconds) * (seconds to nanoseconds)
res_day = res_day * (24 * 60 * 60) * (1000000000); //(day to seconds) * (seconds to nanoseconds)
res_hour = res_hour * (60 * 60) * (1000000000); //(hour to seconds) * (seconds to nanoseconds)
res_min = res_min * (60) * (1000000000); //(minute to seconds) * (seconds to nanoseconds)
res_nansec = (res_year + res_month + res_day + res_hour + res_min) * res_plmn;
WRITES64(0x16014920, res_nansec);
WRITES64(0x9E7AA8, res_nansec);
wait_keys_released(X + DR);
}
Awesome! And the macro should be added to cheats.h I'll port them tomorrow once I have the chance
Your way works great, but my dream was to port this code from City Folk `Press B + Left D-Pad to Rewind One Hour, B + Right D-Pad to Fast Forward One Hour, or B + Down D-Pad to Reset Time to Normal [brkirch]
C23BF754 00000011 901F0004 9421FF80 BC410008 48000005 7CE802A6 80670068 8087006C 38A00032 3CC0EB89 60C62B00 811F0000 5508056B 41820034 2C000002 4082000C 7C862014 7C651914 2C000001 4082000C 7C862010 7C651910 2C000004 4082000C 38800000 38600000 90670068 9087006C B8410008 38210080 4800000C 00000000 00000000 60000000 00000000 C23855E8 00000009 9421FF80 BCA10008 7D6802A6 48000005 7CC802A6 3D208038 556A001E 7C095000 41820014 80E6FFD8 8106FFD4 7C882014 7C671914 7D6803A6 B8A10008 38210080 4E800020 00000000 041443A0 4800000C
` So I did it by adding this macro to cheats.h
#define ADD64(addr, value) *(vs64*)addr += value
#define SUB64(addr, value) *(vs64*)addr -= value
and then writing this function (in cheats_eur.c)
void time(void)
{
if(is_pressed(BUTTON_B + BUTTON_DR))
{
ADD64(0x16014920, 0x34630B8A000);
ADD64(0x9E7AA8, 0x34630B8A000);
wait_keys_released(B + DR);
}
if(is_pressed(BUTTON_B + BUTTON_DL))
{
SUB64(0x16014920, 0x34630B8A000);
SUB64(0x9E7AA8, 0x34630B8A000);
wait_keys_released(B + DL);
}
if(is_pressed(BUTTON_B + BUTTON_DD))
{
WRITES64(0x16014920, 0x0000000000000000);
WRITES64(0x9E7AA8, 0x0000000000000000);
}
}
so you press B + D pad right to fast forward an hour, B + D pad left to rewind a hour and B + D pad down to go back to regular time. Thanks for your findings!
Use of this online breaks the ingame chat (at least on the island) Either you can't hear the other person or the other person can't hear you. Try it on your 3DS's and confirm that, because I don't know if it's only related to the island... Also, can you get into closed shops with that when you're the visitor?
It does seem to break ingame chat. The person who is in the future can't hear messages from the person in the past. The person who is in the past can hear messages from the person who is in the future. (or something like that. It seems like whenever the players don't have the same time offset, messaging is at least partially broken)
You can get into closed shops when you're the visitor. I just tested it.
I don't know if the host/visitor can change which seasonal insects/fish appear.
My other 3DS only has the USA copy of the game and my code isn't working. Are both of the memory addresses the same for the USA version?
Edit 1: The visitor time travelling catches the host's seasonal insects/fish Edit 2: The visitor catches the time travelling host's seasonal insects/fish (this is what I was originally hoping for, I would rather not directly edit the memory of my main save game)
It's different addresses, I think that in the 0xYYYYYY memory region there is a +1000 difference from EUR to USA and in the 0x14000000+ memory region there is a -600 difference so the offsets should be 0x16014320 0x9E8AA8 not tested but I believe this is true Also if you use my ADD64 and SUB64 macros, you can create a 'text to time machine' code, where instead of writing a 64 bit value, you add and/or subtract a 64 bit value so you can type '0000010000' in chat to fast forward or rewind a day :P
I had to search through the memory. The addresses for USA are 0x16014620
and 0x9E8AB0
Also, adding/subtracting is a much better idea than what I came up with. It takes less guesswork to get to a specific month/day. It also helps because I assume that players would be able to keep the same time offset as each other. (I'm guessing that clients set their own time offsets to match the host's adjusted time when connecting)
Edit: Is there any value in memory that indicates you're visiting another town? The towntime offset probably shouldn't be set when visiting. I'm not entirely sure, but setting towntime while visiting might affect the time in the visitor's town whenever they save+quit.
I've done some fiddling around, and I think this is the least clunky way to implement time travel:
There is actually no need to detect if you're visiting a town when time travelling. I set towntime and realtime to 0 then returned to my town, and my town's local time was not affected in any way.
I'm using the following hotkeys:
In my code, I'm using plain digits for time values rather than hexadecimal for readability.
I only wait for dpad_*
to be released so that time can be changed more quickly by not having to release B/A
Added to cheats.h
:
#ifndef SET64
#define ADD64(addr, value) *(vs64*)addr += value
#define SUB64(addr, value) *(vs64*)addr -= value
#define SET64(addr, value) *(vs64*)addr = value
#endif
Added to cheats_eur.h
:
void time_eur(void)
{
// b+R+d: reset
// b+l/r: minute
//a+b+l/r: hour
// b+u/d: day
//a+b+u/d: week
u16 *realtime = (u16*)0x9E7AA8; //USA is 0x9E8AB0
u16 *towntime = (u16*)0x16014920; //USA is 0x16014620
s64 nanosec_min = (s64)(60) * (s64)(1000000000); //(minute in seconds) * (seconds to nanoseconds)
s64 nanosec_hour = (s64)(3600) * (s64)(1000000000); //(hour in seconds) * (seconds to nanoseconds)
s64 nanosec_day1 = (s64)(24 * 3600) * (s64)(1000000000); //(day to seconds) * (seconds to nanoseconds)
s64 nanosec_day7 = (s64)(7 * 24 * 3600) * (s64)(1000000000); //(week to seconds) * (seconds to nanoseconds)
//reset needs to be before b+dd with how wait_keys_released is being used here
if(is_pressed(BUTTON_B + BUTTON_R + BUTTON_DD))
{
SET64(towntime, (s64)0);
SET64(realtime, (s64)0);
wait_keys_released(DD);
}
if(is_pressed(BUTTON_B + BUTTON_DR))
{
if(is_pressed(BUTTON_A)) {
ADD64(towntime, nanosec_hour);
ADD64(realtime, nanosec_hour);
} else {
ADD64(towntime, nanosec_min);
ADD64(realtime, nanosec_min);
}
wait_keys_released(DR);
}
if(is_pressed(BUTTON_B + BUTTON_DL))
{
if(is_pressed(BUTTON_A)) {
SUB64(towntime, nanosec_hour);
SUB64(realtime, nanosec_hour);
} else {
SUB64(towntime, nanosec_min);
SUB64(realtime, nanosec_min);
}
wait_keys_released(DL);
}
if(is_pressed(BUTTON_B + BUTTON_DU))
{
if(is_pressed(BUTTON_A)) {
ADD64(towntime, nanosec_day7);
ADD64(realtime, nanosec_day7);
} else {
ADD64(towntime, nanosec_day1);
ADD64(realtime, nanosec_day1);
}
wait_keys_released(DU);
}
if(is_pressed(BUTTON_B + BUTTON_DD))
{
if(is_pressed(BUTTON_A)) {
SUB64(towntime, nanosec_day7);
SUB64(realtime, nanosec_day7);
} else {
SUB64(towntime, nanosec_day1);
SUB64(realtime, nanosec_day1);
}
wait_keys_released(DD);
}
}
That makes the code a lot more readable and less intimidating, thanks! For my plugin, I'm going to include your text to time travel code included with the time forward and back and a function to fastforward/rewind minutes without the wait_keys_released restriction, and it looks really cool... You should try it! But feel free to make a pull and upload your own copy if you want. What else do you plan on searching? I've got 2 people with Gateways that help find offsets, like collision checks lighting and other random codes that I need to throw into the plugin, but you can check out progress and stable releases here http://gbatemp.net/threads/release-animal-crossing-new-leaf-multi-cheat-ntr-plugin.428522/ and we should continue this conversation on there to discuss offsets/different variations of code.
I don't really plan on searching for anything else; all I was really interested in doing myself is time travel. I don't have a Gateway or anything, so searching through memory can be extremely clunky or slow. The only real options I have are https://github.com/valarnin/NTRRemoteDebugger (extremely slow when trying to filter down results) or https://github.com/imthe666st/NTRClient (extremely clunky, have to dump memory to a file and feed it through a hex editor)
Wow! Amazing work!!
Closing this since everything has been found for the Amiibo update. Will use this info to find it in any future updates for ACNL or any future Animal Crossing games to find.
This seems to be correct for the EUR version of the game. Untested on USA; I'm not going to do any experimenting on USA until I have a chance to go get my physical cartridge and re-inject my save.
0x009E7AA8
- realtime offset (signed int64; value in nanoseconds)0x16014920
- towntime offest (also signed int64; value in nanoseconds)Setting towntime offset first and then realtime offset to
1261440000000000000 nanoseconds
will advance the game's time by approximately 40 years relative to the 3DS's current time.Setting towntime offset alone does nothing until you save+quit the game and re-enter. (haven't actually tested this, but this is most likely what happens because the game doesn't copy realtime offset to towntime offset when saving from jumping forward a day) Setting realtime offset to a value that would trigger the game to save (such as +1 day) without setting the towntime offset will cause the game to revert back to towntime offset after the game saved.
If these two memory addresses aren't correct for USA/JPN, the towntime offset in
garden.dat
(for EUR, anyway) is0x5C7A0
. (it should be just before one of the instances of your town's name) I found the towntime offset in the save file by changing the game's time and checking what bytes in the save file were changed. Then I was able to search the game's memory for real-time changes.In this screenshot of my
garden.dat
, I have the offset set so that my town is in the year 2050.I'm unsure about weather because I'm only interested in time travel.
I'm not sure if/how time travel works when you're the host of a multiplayer session; I found this plugin while seeing if it would be easy to create a plugin for AC. Perhaps it might work with a text-to-time cheat like text-to-item? I'm going to try it myself and see if I have any luck.