rydoginator / ACNL-NTR-Cheats

A combination of cheats that I've been creating with NTR
93 stars 13 forks source link

Realtime Offset Address #13

Closed edwardmuel closed 7 years ago

edwardmuel commented 8 years ago

TODO: Create more codes (planning on finding weather and time offsets, but it's a slow process :sleep:)

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) is 0x5C7A0. (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.

rydoginator commented 8 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

rydoginator commented 8 years ago

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!

edwardmuel commented 8 years ago

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.)

edwardmuel commented 8 years ago

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.

20160824_225642

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);
}
rydoginator commented 8 years ago

Awesome! And the macro should be added to cheats.h I'll port them tomorrow once I have the chance

rydoginator commented 8 years ago

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!

rydoginator commented 8 years ago

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?

edwardmuel commented 8 years ago

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)

rydoginator commented 8 years ago

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

edwardmuel commented 8 years ago

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.

edwardmuel commented 8 years ago

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);
    }
}
rydoginator commented 8 years ago

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.

edwardmuel commented 8 years ago

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)

ghost commented 8 years ago

Wow! Amazing work!!

rydoginator commented 7 years ago

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.