nfsu2-re

https://github.com/yugecin/nfsu2-re

Index #

Index

Display settings #

Display settings get saved in the windows registry. See Registry.

TODO. I was writing this when I got distracted by the language stuff.

Symbols:

Index

Registry #

Many values (mainly display settings) are loaded/saved in procs LoadRegistrySettings and SaveRegistrySettings.

They are saved in key Software\EA Games\Need for Speed Underground 2 in HKEY_CURRENT_USER. On my machine this translates to: HKEY_CURRENT_USER\Software\Classes\VirtualStore\MACHINE\SOFTWARE\Wow6432Node\EA Games\Need for Speed Underground 2. I'm guessing the extra path in between is because of WOW64.

Variables loaded from there:

VariableKey
_optVERSIONUNUSED VERSION
_optSIZEUNUSED SIZE
_optCarReflectionUpdateRate g_CarEnvironmentMapEnable
_optCarReflectionDetail g_CarEnvironmentMapUpdateData
_optCarShadowNeon g_CarShadowEnable
_optCarHeadlight g_CarHeadlightEnable
_optCarLightingEnableUNUSED g_CarLightingEnable
_optCarDamageEnableUNUSED g_CarDamageEnable
_optCrowds g_CrowdEnable
_optWorldReflectionDetail g_RoadReflectionEnable
_optFog g_FogEnable
_optMotionBlur g_MotionBlurEnable
_optLightTrails g_LightStreaksEnable
_optLightGlow g_LightGlowEnable
_optAnimatedTextureEnable g_AnimatedTextureEnable
_optParticleSystem g_ParticleSystemEnable
_optDepthOfField g_DepthOfFieldEnable
_optWorldDetail g_WorldLodLevel
_optCarGeometryDetail g_CarLodLevel
_optOverBright g_OverBrightEnable
_optEnchancedContrast g_BleachByPassEnable
_optTinting g_TintingEnable
_optFSAALevel g_FSAALevel
_optHorizonFog g_HorizonFogEnable
_optRainSplatter g_RainEnable
_optTextureFiltering g_TextureFiltering
_optRacingResolutionIdx g_RacingResolution
_optLevelOfDetail g_PerformanceLevel
_optVsync g_VSyncOn

LoadOtherRegistrySettings also loads registry settings, but only once at boot. It seems to load a special key that I haven't seen before: Software\Electronic Arts\EA Games\Need for Speed Underground 2\er. This key is not present on my system, so this seems interesting.

HKEY key;
DWORD type, lenData;
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\E...2\\er", 0, KEY_READ, &key)) {
	type = REG_SZ; // a null-terminated string
	lenData = 21;
	if (RegQueryValueExA(key, 0, 0, &type, &someRegValue_er, &lenData)) {
		someRegValue_er[20] = 0;
	} else {
		someRegValue_er[0] = 0;
	}
}
RegCloseKey(&key);

Then it loads more stuff from the normal key:

Install Dir is read. If set, passed to (winapi) GetLongPathNameA. and if that succeeds, the value is copied to buffer at installDirPath.

CD Drive is read. If set, passed to (winapi) GetLongPathNameA. passed to (winapi) GetLastError and if that succeeds, the value is copied to CDDrivePath.

NotFirstTime is read. If set, notFirstTime gets set to 1, otherwise 0. It doesn't check the value in the register key. Then 1 is written to the register key.

CacheSize is read. If set, unnamed_75D65B is called with the value. TODO

SwapSize is read. If set, proc at unnamed_75D65B is called with the value. TODO

Language is read. Then it does something with looping through a language. This took some deciphering but I found out that there is some struct with language information and associated data. See Language metadata.

StreamingInstall is read. Then something with SomeElaborateStrcmp??. TODO

Symbols:

Index

Game region #

List by GetCountryCode:

List by OpenPatchesWebsite:

Then some network region stuff at networkRegionStuff showed a 14th entry:

Symbols:

Index

Bin data #

Lots of data is in .bin files. These files can contain multiple struct BinSection entries.

These are all the sections that are in the LANGUAGES/English.bin file:

offset 00000: section 00039000 size 3D230
offset 3D238: section 00000000 size 40
offset 3D280: section 00030201 size FF0
offset 3E278: section 00000000 size 0
offset 3E280: section 00030201 size 1270
offset 3F4F8: section 00000000 size 0
offset 3F500: section 00030201 size 370
offset 3F878: section 00000000 size 0
offset 3F880: section 00030201 size 1070
offset 408F8: section 00000000 size 0
offset 40900: section 00030201 size A70
offset 41378: section 00000000 size 0
offset 41380: section 00030201 size CF0

This is output by an early version of nfsu2-re-binfiles/main.c.

Note that the offset also increments by 8, being sizeof struct BinSectionHeader.

A section with magic 0 seems to be just for padding/alignment uses.

Sections that have magic starting with 8 seem to be sections that have more sections inside, for example the data of section with magic 80034147 contains more sections, with magic 34148, 34149, 3414A, 3414C, 3414D.

Symbols:

Index

Language #

There seems to be no way to change the language, except for changing the value in the registry? See Registry. Perhaps it is only set when the game is installed?

TODO:

.rdata:007983D0 aLanguageselect db 'LanguageSelectScreen',0 ; TODO

Language

Language metadata #

Thanks to code where it reads the Registry, I found following data:

struct LanguageData languageData[] = {
        { "English US",            0,        0 },
        { "English UK",            0,        5 },
        { "French",                1,        6 },
        { "German",                2,        7 },
        { "Italian",               3,        8 },
        { "Spanish",               4,        9 },
        { "Swedish",               6,        5 },
        { "Danish",                7,        5 },
        { "Dutch",                 5,        5 },
        { "Korean",                8,        2 },
        { "Chinese (Traditional)", 9,        4 },
        { "English China",         9,        11 },
        { "Japanese",              10,       3 },
        { "Thai",                  11,       12 },
        { 0,                       0,        0 },
};

The value stored in the registry for Language is checked against all language strings in the above data. The matching language's field_4 gets stored in languageIndex, or 0 when the name stored in the registry didn't match any names.

After chasing usages of a region related proc, I found more language data which is being used in LoadLanguageSomething. They seem to fit into three more structs.

/*from hashes, prefixed with HASH_*/
#define HASH_CONDUITMDITC_TT21I 0x5B9D88B9
#define HASH_font_impact36 0x0920075C
#define HASH_CONDUITMDITC_TT14I 0x5B9D84DB
#define HASH_FONT_CONDUITMDITCTT38BI 0x9583AA1A
#define HASH_arial 0xA87927BE
#define HASH_arial12 0xAB6215C1
#define HASH_lcd_let48 0xBBBA71C2
#define HASH_conduitmditc_tt14i_korean 0xDCA5485A
#define HASH_conduitmditc_tt21i_korean 0x833A8678
#define HASH_conduitmditc_tt14i_chinese 0xF88A75F9
#define HASH_conduitmditc_tt21i_chinese 0x71C777D7
#define HASH_conduitmditc_tt14i_japanese 0x743BF1C1
#define HASH_conduitmditc_tt21i_japanese 0x15192F5F
#define HASH_conduitmditc_tt14i_thailand 0xA73823FF
#define HASH_conduitmditc_tt21i_thailand 0x4815619D

struct LanguageStruct7F6DE8 languageStuff7F6DE8[] = {
        { HASH_CONDUITMDITC_TT21I,          0x0 },
        { HASH_font_impact36,               0x40000 },
        { HASH_CONDUITMDITC_TT14I,          0x10000 },
        { HASH_FONT_CONDUITMDITCTT38BI,     0x100000 },
        { HASH_arial,                       0x10000 },
        { HASH_arial12,                     0x10000 },
        { HASH_lcd_let48,                   0x4000 },
        { HASH_conduitmditc_tt14i_korean,   0x40000 },
        { HASH_conduitmditc_tt21i_korean,   0x80000 },
        { HASH_conduitmditc_tt14i_chinese,  0x40000 },
        { HASH_conduitmditc_tt21i_chinese,  0x80000 },
        { HASH_conduitmditc_tt14i_japanese, 0x40000 },
        { HASH_conduitmditc_tt21i_japanese, 0x80000 },
        { HASH_conduitmditc_tt14i_thailand, 0x40000 },
        { HASH_conduitmditc_tt21i_thailand, 0x80000 },
};

struct LanguageFontData languageStuff7F6E60;
languageStuff7F6E60[0][0] = HASH_CONDUITMDITC_TT21I;
languageStuff7F6E60[0][1] = HASH_arial;
languageStuff7F6E60[0][2] = HASH_CONDUITMDITC_TT14I;
languageStuff7F6E60[0][3] = HASH_lcd_let48;
languageStuff7F6E60[0][8] = HASH_FONT_CONDUITMDITCTT38BI;
languageStuff7F6E60[0][16] = HASH_arial12;
languageStuff7F6E60[0][17] = HASH_font_impact36;
languageStuff7F6E60[1][0] = HASH_conduitmditc_tt21i_korean;
languageStuff7F6E60[1][1] = HASH_arial;
languageStuff7F6E60[1][2] = HASH_conduitmditc_tt14i_korean;
languageStuff7F6E60[1][3] = HASH_lcd_let48;
languageStuff7F6E60[1][16] = HASH_arial12;
languageStuff7F6E60[1][17] = HASH_font_impact36;
languageStuff7F6E60[2][0] = HASH_conduitmditc_tt21i_japanese;
languageStuff7F6E60[2][1] = HASH_arial;
languageStuff7F6E60[2][2] = HASH_conduitmditc_tt14i_japanese;
languageStuff7F6E60[2][3] = HASH_lcd_let48;
languageStuff7F6E60[2][8] = HASH_FONT_CONDUITMDITCTT38BI;
languageStuff7F6E60[2][16] = HASH_arial12;
languageStuff7F6E60[2][17] = HASH_font_impact36;
languageStuff7F6E60[3][0] = HASH_conduitmditc_tt21i_chinese;
languageStuff7F6E60[3][1] = HASH_arial;
languageStuff7F6E60[3][2] = HASH_conduitmditc_tt14i_chinese;
languageStuff7F6E60[3][3] = HASH_lcd_let48;
languageStuff7F6E60[3][16] = HASH_arial12;
languageStuff7F6E60[3][17] = HASH_font_impact36;

struct LanguageFileData languageFileData[] = {
	{  0, "ENGLISH", "LANGUAGES\\ENGLISH.BIN", languageStuff7F6E60 + 0 },
	{  1, "FRENCH", "LANGUAGES\\FRENCH.BIN", languageStuff7F6E60 + 0 },
	{  2, "GERMAN", "LANGUAGES\\GERMAN.BIN", languageStuff7F6E60 + 0 },
	{  3, "ITALIAN", "LANGUAGES\\ITALIAN.BIN", languageStuff7F6E60 + 0 },
	{  4, "SPANISH", "LANGUAGES\\SPANISH.BIN", languageStuff7F6E60 + 0 },
	{  5, "DUTCH", "LANGUAGES\\DUTCH.BIN", languageStuff7F6E60 + 0 },
	{  6, "SWEDISH", "LANGUAGES\\SWEDISH.BIN", languageStuff7F6E60 + 0 },
	{  7, "DANISH", "LANGUAGES\\DANISH.BIN", languageStuff7F6E60 + 0 },
	{  8, "KOREAN", "LANGUAGES\\KOREAN.BIN", languageStuff7F6E60 + 1 },
	{  9, "CHINESE", "LANGUAGES\\CHINESE.BIN", languageStuff7F6E60 + 3 },
	{ 10, "JAPANESE", "LANGUAGES\\JAPANESE.BIN", languageStuff7F6E60 + 2 },
	/*there is no Thai entry here?*/
};

In LoadLanguageSomething, languageFileData is searched. If GetGameRegion is 2, the entry with id 8 is searched, otherwise id 0 is searched. When it's not found the game will segfault because of the code below:

.text:004FF6A2                 jl      short loc_4FF693
.text:004FF6A4 loc_4FF6A4:
.text:004FF6A4                 xor     edx, edx
.text:004FF6A6 loc_4FF6A6:
.text:004FF6A6                 mov     ecx, [edx+0Ch]

Then struct LanguageFileData.fontdata is read. Its field_0[0] value is read and then a matching entry in languageStuff7F6DE8 is searched. More stuff is read in a loop and reset and I lost track. Eventually something is called with string LanguageMemoryPool.

.rdata:007983D0 aLanguageselect db 'LanguageSelectScreen',0 ; TODO

Symbols:

Index

Language

Language files #

The language strings are in a bin section with magic 0x39000, and is structured like this:

struct BinDataLanguage39000 header;
char unknown[header.tableOffset - sizeof(header)];
struct LanguageTableEntry tableEntries[header.numStrings];
char *strings; /*all zero-terminated strings after each other*/

The tableEntries are sorted (low hash to high hash), required because a binary search is used at lookup.

.text:00497766                 push    04B5DE3E9h ; hash (SMS_SUBJECT_HEADER)
.text:0049776B                 lea     eax, [esp+104h+buffer]
.text:00497772                 push    80h             ; destlen
.text:00497777                 push    eax             ; dest
.text:00497778                 call    GetLanguageStringIntoBuf

Loading is done in LoadLanguageBinSection39000. See language_english.txt (339KB) for all LANGUAGES/English.bin strings by their hash. (This was generated by an early version of nfsu2-re-binfiles/main.c)

The hashes seem to be made with the case insensitive hash function cshash. The original labels are also stored as a language file in LANGUAGES/Labels.bin

TODO: language files also contain bin sections with magic 0x30201

Symbols:

Index

Pools #

I hooked AllocateAndInitPool and collected some output: createpoollog.txt (10KB) This will probably help a lot when trying to figure out other things. Note that the first time the AUD: NFS3DMixCtl Pool is made, it has an element amount of zero. The dump is from just starting the game and starting a circuit race.

Pools can be enlarged by Pool::Extend, but only if its flags has the POOL_FLAG_EXTENDABLE flag. When extending, the newly created pool gets added as last in the main pool's nextLinkedPool. The main pool's firstAvailableElement will point to the first allocated element of the next pool, and the last allocated element of the new pool will point to the first element of the main pool.

Symbols:

Index

Speedy boot #

I want a fast startup because I'm booting the game so many times (not to mention the numerious crashes)

During startup, with the debug string output enabled, there are a few entries showing loading and unloading states, such as LS_THXMovie.fng. I already sped up boot by replacing the MOVIES/THX_logo.vp6 with MOVIES/blank.vp6. While that skips the unskippable intro logos, it still takes some time because it switches to a state where it tries loading and it takes slightly under a second to switch to the next state.

So by following the debug prints, I found following section:

.data:007F65E8 off_7F65E8  dd offset aDiscerrorpc_fn
.data:007F65EC             dd offset byte_783FE5
.data:007F65F0             dd offset aMc_bootup_fng ; "MC_Bootup.fng"
.data:007F65F4             dd offset byte_783FE5
.data:007F65F8             dd offset byte_783FE5
.data:007F65FC             dd offset aLs_blankmovie_ ; "LS_BlankMovie.fng"
.data:007F6600             dd offset aLs_ealogo_fng ; "LS_EAlogo.fng"
.data:007F6604             dd offset byte_783FE5
.data:007F6608             dd offset aLs_thxmovie_fn ; "LS_THXMovie.fng"
.data:007F660C             dd offset aLs_psamovie_fn ; "LS_PSAMovie.fng"
.data:007F6610             dd offset aUg_ls_introfmv ; "UG_LS_IntroFMV.fng"
.data:007F6614             dd offset aUg_ls_splash_f ; "UG_LS_Splash.fng"
.data:007F6618             dd offset aMc_background_ ; "MC_Background.fng"
.data:007F661C             dd offset aUi_main_fng  ; "UI_Main.fng"

This very much looks like the boot states the game goes through. byte_783FE5 is basically an empty string, so maybe there used to be more states, or some are for different game versions?

Putting all of them to a reference to an empty string makes the boot pretty speedy but then it doesn't do much. After initial boot, there's a black screen for a short moment, then the basic loading progressbar screen with the cars and Rachel shows, then it goes to a black screen and you can move the cursor. So the game is running but nothing of UI is running so you can't do much.

Only leaving UG_LS_Splash.fng shows the splash screen with Rachel and the cars, the famous music starts playing and when the beat drops the EA TRAX message shows and "Press enter" shows. When pressing enter, we're back to the black screen with a moveable cursor.

Only leaving MC_BACKGROUND.fng shows the splash screen with Rachel and the cars and the prompt if I want to create a new profile. When pressing 'no', we're back to the black screen. When pressing 'yes' I get to enter a name but when it then asks to save the profile, it says 'Unable to save profile' so I have to choose 'no'. Then black screen again.

Only leaving UI_MAIN.fng directly jumps to the main menu where you see Rachel's car after a short moment after the loading screen showed. It didn't ask for a profile to load and when I try to load a profile it says that no profiles were found. Scary. I can create a new profile but it says 'Unable to save'. There is no music in the menu. Music does start when starting a race but going back to the menus stops the music again. Going to EA TRAX in the option does suddenly start the music in the menus.

When only leaving LS_EALogo.fng, the ea logo movie sound starts to play while the 'loading' screen is still showing, then there's one frame of the logo movie when it's about halfway the sound of the movie and then it goes to black screen with cursor. Leaving LS_BlankMovie.fng in as well fixes that problem.

Leaving just MC_Bootup.fng doesn't seem to change much, not even timing wise. It does sound important, so I tried this in combination with UI_MAIN.fng. It jumps into the main menu, but when I go to the profile manager now, I can load my profiles. Nice. The main menu music still doesn't load without going to EA TRAX, but I'm okay with that. One weird thing is that the Q button doesn't show the 'quit game' dialog. After searching a bit, it seems like LS_PSAMovie.fng is the one that makes the Q to quit keybind happen. That's quite interesting, definitely something to research when/if I get more into the workings of the UI.

(Later I found the PSA screen(s) put canUseQToExit on 1 after they're done, I'm guessing for some reason they don't want us to exit while the PSA is playing, or before the intro movies end.)

So my final setup is to only leave in MC_Bootup (because it makes profiles work), MC_Background (so I can load a profile at start) and UI_Main (obviously). Then also write 1 to canUseQToExit. Code in nfsu2-re-hooks/speedyboot.c.

Index

Console leftovers #

Note: console as in somewhere you type in, not a videogame console

In the hooks project, I "enabled" this console in the functions initConsolePOC in the file nfsu2-re-hooks/faux-enable-console.c. As it doesn't seem to be used anywhere, the only effect you can see is that whatever you type is written into the console_real_text buffer (use CheatEngine or something you like to inspect it).

Console leftovers

ConsoleConsumeKey #

When looking at the MainWndProc (which is not to hard too find because it's passed in a struct to RegisterClassExA), I saw a call to a function that is called on every WM_CHAR event. The function does nothing when the value in consoleEnabledFlag is zero, and there are no write references to it, so it seems like it will always be zero.

First it seems like the input is dropped if it's a CR (carriage return) and when the value at consoleIgnoreNextCR is non-zero. Again, this is always zero so CR's are never dropped.

Then it has four different handlers. The first one handles a backspace character, second one does escape, third one does tab and CR, and the last one does everything else. Basically one character is deleted when pressing backspace and one character is added in all other cases. I named following variables:

Basically, on backspace it checks if consoleTextCaretPosition is bigger than zero, copies everything in consoleTextString starting from consoleTextCaretPosition to a zero byte into a newly allocated buffer, decrements consoleTextCaretPosition and consoleTextStringLength, and copies everything from the allocated buffer back into consoleTextString at position consoleTextCaretPosition.

When inserting, it checks if consoleTextStringLength is less than consoleTextStringMaxLength, copies everything in consoleTextString starting from consoleTextCaretPosition to a zero byte into a newly allocated buffer, inserts the character in consoleTextString at position consoleTextCaretPosition, increments consoleTextCaretPosition and consoleTextStringLength by one, and copies everything from the allocated buffer back into consoleTextString at position consoleTextCaretPosition.

  • Backspace: delete character before caret
  • Escape: insert zero byte
  • Tab and CR: insert LF
  • Other: insert char if it's a-z or A-Z or 0-9 or, if consoleDisallowSpecialChars is zero, _ or @ or . or -. If none of these pass, do a check for more keys as described below

Console leftovers > ConsoleConsumeKey

A hardcoded key check #

At the end of looking if a character is valid, when it hasn't passed any of the criteria above, it checks one last function if consoleFilterSpecialChars is 1. The function seems to check if the key is one of 55 configured values and if so, the char is accepted.

Index

Console leftovers

ConsoleMoveCaret #

This function is also called from MainWndProc, this time when a WM_KEYDOWN message is received. One parameter is passed based on the keycode of the event:

This is the only other function that checks if consoleEnabledFlag is non-zero.

Index

Console leftovers (continuation)

Symbols:

Index

Hash functions #

While scrolling and looking at references to some strings, I found some parts that look like following excerpt:

.text:005121AE     push    offset aGenericdialo_2 ; "GenericDialog_Animate_SMALL.fng"
.text:005121B3     call    cihash
.text:005121B8     add     esp, 4
.text:005121BB     cmp     esi, eax
.text:005121BD     jz      short loc_512218
.text:005121BF     push    offset aGenericdialog_ ; "GenericDialog_SMALL.fng"
.text:005121C4     call    cihash
.text:005121C9     add     esp, 4
.text:005121CC     cmp     esi, eax
.text:005121CE     jz      short loc_512218
.text:005121D0     push    offset aGenericdialo_3 ; "GenericDialog_Animate_MED.fng"
.text:005121D5     call    cihash
.text:005121DA     add     esp, 4
.text:005121DD     cmp     esi, eax
.text:005121DF     jz      short loc_512218
.text:005121E1     push    offset aGenericdialo_4 ; "GenericDialog_MED.fng"
.text:005121E6     call    cihash
.text:005121EB     add     esp, 4
.text:005121EE     cmp     esi, eax
.text:005121F0     jz      short loc_512218
.text:005121F2     push    offset aGenericdialo_5 ; "GenericDialog_Animate_LARGE.fng"
.text:005121F7     call    cihash
.text:005121FC     add     esp, 4
.text:005121FF     cmp     esi, eax
.text:00512201     jz      short loc_512218
.text:00512203     push    offset aGenericdialo_0 ; "GenericDialog_LARGE.fng"
.text:00512208     call    cihash
.text:0051220D     add     esp, 4
.text:00512210     cmp     esi, eax
.text:00512212     jz      short loc_512218

The proc cihash doesn't look very complicated; it has one argument and doesn't perform any calls. It dereferences the given argument and increment its position, until a zero has been found. While it's doing that, eax is modified based on the read value. So I assumed it is some hashing function and hooked it to see what kind of things are passed through it.

As it turns out, it gets called many, many times.

time        input                       result
53.42743683 GenericDialog_SMALL.fng     6962C0CD
53.42753220 UI_PC_Help_Bar.fng          33AC1CB4
53.42756271 OL_ICON_GROUP               2BAC0CEE
53.42758179 UI_PC_Help_Bar.fng          33AC1CB4
53.42760086 Hide                        0016A259
53.42761993 UI_Main.fng                 C343126A
53.42766190 GarageMain.fng              4CDD8B14
53.42769241 GarageMain.fng              4CDD8B14
53.42771149 GarageMain.fng              4CDD8B14
53.42773056 GenericDialog.fng           F68A7675
53.42774963 UI_GenericParts_Browser.fng AF09F84F
53.42776871 GenericDialog.fng           F68A7675
53.42779160 GenericDialog_SMALL.fng     6962C0CD
53.42781067 UI_PC_Help_Bar.fng          33AC1CB4
53.42782974 UI_Main.fng                 C343126A
53.42784882 GarageMain.fng              4CDD8B14
53.42790222 UI_MagazineBack.fng         FA8CC482
53.42796707 GenericDialog_SMALL.fng     6962C0CD
53.42798615 UI_PC_Help_Bar.fng          33AC1CB4
53.42800522 UI_Main.fng                 C343126A
53.42802429 GarageMain.fng              4CDD8B14
53.42922211 GenericDialog_SMALL.fng     6962C0CD
53.42924881 UI_PC_Help_Bar.fng          33AC1CB4
53.42926788 UI_Main.fng                 C343126A
53.42928696 GarageMain.fng              4CDD8B14

The excerpt above is probably from one cycle in the update loop, and is repeated a lot.

Some time later I found a very similar looking proc: cshash, so I copied the previous hook for this function. This one gets called many times seemingly at startup and when car parts or maps get loaded.

When writing this I took a better look at the procedures and saw that the only difference really is that the first one is case insensitive while the second one is case sensitive. In the cihash one, it checks for every character if it is in the range 'a'-'z' and subtracts 0x20 if so, making the character uppercase before updating the hash.

See hashes.txt (258KB) for list of hashes, each line is formatted as hash\tinput\tresult\tproc\n. This file is updated at random times. 8 minutes of playing gives about a 277MB file, all output was piped to sort | uniq.

How I collected these can be seen in the nfsu2-re-hooks/hook-*-hash-*.c files.

Hash functions

Messing with hash results #

I had an impulse to try to return different results for certain hashes. For example, return the result of FIRETRUCK when the input is 240SX. The result is .. difficult to describe. Here's some short points:

  • Browsing to the stock 240sx to customize shows an empty spot, no car
  • Selecting the stock car to customize will suddenly swap to the firetruck model
  • I can modify the firetruck, I can't select any bumpers but I can choose spoilers, although it does nothing
  • Changing wheels/spinners/hydraulics does show
  • Then going out of the customization menu crashes the game (this includes going to performance tuning)

jumping firetruck
Hop hop skippity hop

  • Browsing a previously customized 240sx only shows the wheels of the car
  • If the wheels were not customized, it shows black rectangles as wheels and the game crashes when I try to customize the wheels/spinners
  • Selecting the customized car to customize doesn't change anything special, still only wheels
  • Only the firetruck customization options can be selected
  • Choosing a spoiler shows the spoiler in the center of the car
  • When going out of a category (like Body parts), the car switches to the same car but the wheels at the position where they should be for the firetruck
  • When going outside the customize menu now, the car's brand is a * symbol
  • When going to a different car and back, the firetruck model shows
  • Entering customization again hides the firetruck, only showing the wheels (the spoiler is gone?)
  • After saving and undoing the swap and restarting the game, the saved car stays a firetruck (but the behavior is still the same as described above)
  • Using the car to race makes the framerate go down significantly, except when in nose camera (you just see headlights driving without a vehicle body)
  • It doesn't lag at all in the performance tuning

dyno chart
Here's a dyno chart of the firetruck

While the firetruck is unused in the game, I also tried to do this with the taxi, but it gave the same results. Then I tried to replace it with a "normal" car, the Mustang.

  • Browsing a stock 240sx only shows black rectangles for wheels
  • When selecting the car, it gets replaced with a mustang, and now I'm customizing a mustang
  • The only change I can see is that there are no headlights or taillights
  • Going ingame now only shows the front bumper of the car, which is the thing I customized
  • Going back to the menu now shows the car with stock headlights and taillights
  • Going ingame again now shows the car fully as it should
  • Selecting a customized 240sx now shows the doors of a mustang

driving invisible mustang
Car?

The behavior isn't always the same, just give it a try and see for yourself. Replacing the taxi with the ambulance just lags out the game whenever a taxi comes in sight.

Check the function SomeHashCS43DB50Print in the file nfsu2-re-hooks/hook-43DB50-hash-cs.c to see how I did this, the code to replace cars is in comments.

Index

Hash functions (continuation)

Symbols:

Index

Lists #

Many structs have a doubly linked list of data with a sentinel node (thanks Wikipedia for teaching me that name) being in the struct itself. It kinda looks like this:

struct SomeStruct {			struct Child {
	void *vtable;				void *vtable;
	// yada yada				struct Child *next;
	struct Child *next;			struct Child *prev;
	struct Child *prev;			// yada yada
	// yada yada			}
}

So then one can loop over all nodes by going to next until next is the sentinel node:

void *sentinel = &someStruct->next;
struct Child *next = someStruct->next;
while (next != sentinel) {
	doSomething(next);
	next = next->next;
}

ObjectLink #

Though almost always, the lists look something like this:

struct SomeStruct {			struct Child {
	void *vtable;				void *vtable;
	// yada yada				struct ObjectLink link;
	struct ObjectLink link;		// yada yada
	// yada yada			}
}

So to loop over all nodes, one would have to do this:

struct ObjectLink *sentinel = &someStruct->link;
struct ObjectLink *next = someStruct->next;
struct Child *actual;
while (next != sentinel) {
	actual = (struct Child*)((int) next - 4);
	doSomething(actual);
	next = next->next;
}

Or I'm missing something. Either way I'm wondering what the source code looks like for these things.

Index

Lists

0xABADCAFE #

0xABADCAFE is a constant that's very often used, sometimes also used as bogus pointers on list nodes that are being removed:

struct Child *someNode = ...;

someNode->prev->next = someNode->next;
someNode->next->prev = someNode->prev;
someNode->prev = (void*) 0xABADCAFE;
someNode->next = (void*) 0xABADCAFE;
delete someNode;

This happens for example in UIData_field8::DirectPopFNG.

A quick search shows some known usages for this value:

"A bad cafe", Used to initialize all unallocated memory (Wikipedia: Magic number # debug values)
0xABADCAFE: A startup to this value to initialize all free memory to catch errant pointers (C/C++ programming language notes, yurichev.com)

Though the usage observed here is often also before destruction of objects instead of only at initialization.

Index

Debug print #

While scrolling through the data segment, I found these interesting strings:

.rdata:0079B160 aDeletingPackag db 'Deleting package [%s]',0Ah,0
.rdata:0079B177                 align 4
.rdata:0079B178 aUnActivateXX   db 'Un-Activate!!! %x %x',0Ah,0
.rdata:0079B18E                 db 2 dup(0)
.rdata:0079B190 aWillBeUnloaded db 'Will be unloaded [%s]',0Ah,0

Even more interesting is that these strings are using by pushing them on to the stack, followed by a call to a specific proc that looks like this:

.text:0050D510 sub_50D510  proc near
.text:0050D510                 xor     eax, eax
.text:0050D512                 retn
.text:0050D512 sub_50D510  endp

It appears as if this used to be some kind of debug printf function, so I named it DebugPrint50D510. I hooked the function and made it output to a log file, see nfsu2-re-hooks/replace-50D510-DebugPrint.c.

It also seems to pass data that is not a pointer to a string. I'm guessing since the implementation of this function was removed (and now just returns zero), and there were probably other removed functions, so some calls that are supposed to be done to different functions may now all point to this one function that also happened to be used for the debug print stuff. My simple solution is to check if the first passed argument points to memory in the data section, and print if so.

I did a short session browsing customization options and performance tuning with this enabled, the results can be seen in the log file debugstring50D510.txt (149KB). Each line is formatted as debugstr\t50D510\tcallee\tstring\n. Here is a 'small' excerpt:

DIALOG :: --------- ShowDialog -----------
DIALOG :: |_ Do you want to convert your trunk to carbon fiber?
DIALOG :: Oh shit - the control mask is zero.  This is bad.  Try to use Top Package's control mask.
DIALOG ::  |_ TopPackage = UI_GenericParts_Browser.fng, mask = 0.
DIALOG ::    |_ Crap - mask is zero, forcing to 0xff.
DIALOG :: Success : Control mask = 255, Handle = 2
DIALOG :: --------- ShowDialog Finished -----------
Init Package GenericDialog_SMALL.fng
DIALOG :: Constructor
Joy Event: FEPad_Accept[0]
Joy Event: FEPad_Accept[0]
Joy Event: FEPad_Back[1]
Joy Event: FEPad_Back[1]
DIALOG :: Tick, ReturnWithMessage Set, Dismissing: [GenericDialog_SMALL.fng] [b4623f67]
DIALOG :: DismissDialog.  Handle(2).  Current handle(2)
DIALOG ::  |_ Found, popping.
Queue popping GenericDialog_SMALL.fng
Will unload -----------GenericDialog_SMALL.fng------------------
Will be unloaded [GenericDialog_SMALL.fng]
DIALOG :: Closing, sending message (b4623f67) to (UI_GenericParts_Browser.fng).
Send message[b4623f67] to package [UI_GenericParts_Browser.fng]
Message was queued
Un-Activate!!! 37a52f0 37e69c0
Deleting package [GenericDialog_SMALL.fng]
package[GenericDialog_SMALL.fng] will unload

Most of the messages seem to be about dialogs, joy (keyboard) events, FEng/fng packages (I'm guessing those are ui resources/designs?).

Debug print

Audio debug print #

And later I found another one: DebugPrint4691C0. This one only formats the given string and parameters into audioDebugString. It doesn't print it. This one's implementation seem to be fully present, but it is disabled in the way that the first argument passed is always 0 which causes the function to return immediately. Just changing it to anything else will make it actually do the formatting. Sometimes (instead of hardcoded 0), doPrintAudioDebugLog is passed, but its value is also always 0.

With a few little hooks (nfsu2-re-hooks/hook-4691C0-DebugPrint.c), we get following output: debugstring4691C0.txt (52KB).

It's a lot of the same, so here are the few unique strings that comes out of it (after removing the duplicates, ignoring variable parameter values):

3dLookupID = 90082800  SFXOBJOUT ID 82800
COnnect SubChannel ID = d0000000
COnnect SubChannel ID = d0000000 FINISHED
COnnect SubChannel State = 0 Copy = 0 Num = 0 of 2 , Ptr= c45c330
ChannelID c0020002, State = 0, chid = 8006002,
Cleaningup Backup Stream after 15 seconds
Connect Mix Channel  d00c0105
Deleting Stream ID 7 m_queuedObject due to PurgeStreams
Ext EVTMixCtl ID b0003006
Ext EVTMixCtl ID b0003006 Connects OutPtr c458010, Out Val 0
Ext MixCtl ID 9006008 Connects OutPtr c4dcbdc, Out Val 0
FE MASK = 3f, IG Mask = 7ffffc0, NUMFE SOngs = 6, Num IG Songs = 21, PBMODE = 1
Local EVTMixCtl ID b2013016 Connects OutPtr c477fb8, Out Val 0
Local MixCtl ID 9016008 Connects OutPtr c4dcd68, Out Val 0
Local SUB ID d0020104 Connects OutPtr c479b60, Out Val 0
MASTER CH ID = c0090004, OutID = 40000020, dBUP = 200
Master CHannel c0020002, MIXCHIN ID 20000002, Num Fixed Inputs = 2
Master CHannel c0020002, Num 3DConnections = 0
NIS Audio Is REady to Play
NIS Is Ready to Play
Queue Button THrough
SUB CH ID = d0000501, dBUP = 522, dbDown = -522
Set c0080107 InputMapPtr to Input Start 30010002
Sub Channel Array Ptr c4dbe8c, Current Block Offset = cc
Sub Channel d0000000, Num 3DConnections = 0
Sub Channel d00b0001, Num Fixed Inputs = 21
Sub Channel d00b0001, Num Fixed Inputs = 3
Track Handle 11000001, Track Overhead 4192, BufferOverhead = 77981

Index

UI #

Names with .FNG seem to be occuring a lot, it seems like that are UI resources or some kind of UI widget descriptors.

In text strings, ^ is a line feed (but \n might work as well, TODO).

UI things seem to work on a 640x480 canvas. Most things (especially when mouse is involved) seem to be positioned ([-320,+320],[-240,+240]).

A UI element is described by struct UIElement. Its type field tells which kind of element it is. Currently I have:

(later I found UIData_field8_4134::ctor which kinda seems like it has names for some types of elements? I put those names in the list below between square brackets)

All UI elements are addressable by the hash (see Hash functions) of its name (stored in its field hash). So then one can find UI elements by using FindUILabelByName or FindUILabelByHash and the likes.

See also blogpost: Exploring UI

UI

Dialogs #

Thanks to the Debug print discovery, my attention got grabbed by something that looked like dialog code. These leftover debug strings helped so much.

Someone left a trace here:

.text:005541FC                 call    DialogInfo::ctor
.text:00554201                 push    offset aDialogConstruc ; "DIALOG :: Constructor\n"
.text:00554206                 call    DebugPrint50D510

One of the first thing ShowDialog does is calling GetFNGforDialog, which looks like initializes the dialog maybe? One argument is passed, seemingly the dialog name. But then it checks something at the passed dialog name +324h. Conclusion: it's passing a struct with dialog info, of which the first member is the name.

UI > Dialogs

GetFNGForDialog #

This function is only used once, by the ShowDialog function. It checks the value at +324h, which seems to be a pointer to a string, looks like maybe a type of dialog? It's checked if that value is either NULL or empty string or equal to animating or 3button. I decided to reimplement that function. Fun fact: while doing this I managed to write an infinite loop which caused a BSOD.

At the end I decided upon the name GetFNGforDialog. It returns a pointer to a string that says what FNG to display for the dialog that was passed.

The dialog info may already have the FNG set, this function will return that unless it's not set or it's not either animating or 3button.

  • HelpDialog_SMALL.fng when isHelpDialog and text has less than 5 linebreaks
  • HelpDialog_MED.fng when isHelpDialog and text has more than 4 linebreaks
  • GenericDialog_ThreeButton.fng when myFNGName is 3button
  • GenericDialog_SMALL.fng when text width is < 2561.0f
  • GenericDialog_Animate_SMALL.fng same as above, but when type is animating
  • GenericDialog_MED.fng when text width is < 5122.0f
  • GenericDialog_Animate_MED.fng same as above, but when type is animating
  • GenericDialog_LARGE.fng if the other measurements didn't pass
  • GenericDialog_Animate_LARGE.fng same as above, but when type is animating

See the nfsu2-re-hooks/replace-526C40-GetFNGForDialog.c function for the complete reimplementation.

Here's me messing with the returned value:

dialog
HelpDialog_SMALL.fng

dialog
GenericDialog_MED.fng

dialog
GenericDialog_ThreeButton.fng

dialog
GenericDialog_Animate_SMALL.fng

I can't remember seeing this animate style dialog... But when adding some debug prints, it seems like the online/LAN play uses this: dialog 'Retrieving updated games list^from the server...' has type animating but that dialog disappears so fast I probably never noticed it.

Then I tried replacing it with something totally different, UI_GenericParts_Browser.fng. I half expected the game to crash, so I was surprised when it showed me this:

main menu ui on splash screen
:)

Seems like this is a nice place to test loading other screens. Some do crash, but here's UI_OLEAMessenger.fng:

online messenger menu with placeholder text
No idea why that text is .. Spanish?

Printing the dialog info struct address sadly showed that it wasn't stored in the executable itself so it's probably somewhere deep in a bin file.

Index

UI > Dialogs (continuation)

struct DialogInfo was made by looking at all of the above and more. (Especially DialogInfo::ctor helped here.)

Symbols:

Index

UI

FNG things #

fngdata
struct FNGData fngdata[] = {
/*7F7DC8*/{ "UI_Main.fng", // 0
            0x4ED6B0,
            HELP_MAIN_MENU,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE,
            0x0, 0x100, 0x0 },

/*7F7DE4*/{ "UI_OptionsMain.fng", // 1
            0x4EFDE0,
            HELP_OPTIONS_MAIN,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F7E00*/{ "UI_Options.fng", // 2
            0x4E3710,
            HELP_OPTIONS,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_BACK,
            0x0, 0x1CA, 0x0 },

/*7F7E1C*/{ "UI_Wheel_Options.fng", // 3
            0x4E3770,
            HELP_WHEEL,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_BACK,
            0x0, 0x1CA, 0x0 },

/*7F7E38*/{ "UI_PC_Customize_Options.fng", // 4
            0x4E36B0,
            0,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_BACK,
            0x0, 0x1CA, 0x0 },

/*7F7E54*/{ "UI_Options_PC_Controller.fng", // 5
            0x4F92E0,
            0,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_BACK | BUTTON_DYNO_RESET_OR_PC_NAV_RESET_KEYS,
            0x0, 0x1CA, 0x0 },

/*7F7E70*/{ "UI_PC_LAN_ServerSelect.fng", // 6
            0x4FD4B0,
            0,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK | BUTTON_OL_HOST_LAN_SERVER,
            0x0, 0x1CA, 0x0 },

/*7F7E8C*/{ "UI_PC_LAN.fng", // 7
            0x4D6C90,
            0,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x1CA, 0x0 },

/*7F7EA8*/{ "UI_PC_Help_Bar.fng", // 8
            0x552280,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F7EC4*/{ "UI_Trailers.fng", // 9
            0x4CFB20,
            HELP_OPTIONS_TRAILERS,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F7EE0*/{ "Credits.fng", // 10
            0x4B8720,
            HELP_OPTIONS_CREDITS,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F7EFC*/{ "ScreenPrintf.fng", // 11
            0x0,
            0,
            0,
            0x0, 0x100, 0x1 },

/*7F7F18*/{ "loading_boot.fng", // 12
            0x0,
            0,
            0,
            0x0, 0x80, 0x2 },

/*7F7F34*/{ "UI_CareerCrib.fng", // 13
            0x4ED780,
            HELP_CRIB,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0xC0, 0x0 },

/*7F7F50*/{ "UI_CribRewardOptionsMain.fng", // 14
            0x4ED7E0,
            HELP_CRIB_REWARD_OPTIONS,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0xC0, 0x0 },

/*7F7F6C*/{ "UI_CareerCarSelect.fng", // 15
            0x4FC110,
            HELP_CRIB_CAR_SELECT,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0xC0, 0x0 },

/*7F7F88*/{ "UI_StartCareer.fng", // 16
            0x4ED710,
            HELP_CAREER_OPTIONS,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0xC0, 0x0 },

/*7F7FA4*/{ "UI_MagazineBack.fng", // 17
            0x554E60,
            0,
            0,
            0x0, 0xC0, 0x0 },

/*7F7FC0*/{ "UI_MagazineReward.fng", // 18
            0x56B140,
            0,
            0,
            0x0, 0xC0, 0x0 },

/*7F7FDC*/{ "UI_MagazineSelect.fng", // 19
            0x55B520,
            HELP_CRIB_MAGAZINE,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0xC0, 0x0 },

/*7F7FF8*/{ "UI_MagazineView.fng", // 20
            0x554EC0,
            0,
            0,
            0x0, 0xC0, 0x0 },

/*7F8014*/{ "UI_RewardsSponsor.fng", // 21
            0x4ED840,
            HELP_CRIB_REWARD_SPONSORS,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_BACK,
            0x0, 0xC0, 0x0 },

/*7F8030*/{ "UI_CareerWorldMap.fng", // 22
            0x4F9D30,
            HELP_CRIB_WORLD_MAP,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_BACK | LAYOUT_WORLDMAP_POSITION,
            0x0, 0x80, 0x0 },

/*7F804C*/{ "UI_Status_Master.fng", // 23
            0x4E1B80,
            0,
            BUTTON_PC_NAV_BACK | LAYOUT_WORLDMAP_POSITION | LAYOUT_ONE_LINE_NO_BG,
            0x0, 0x80, 0x0 },

/*7F8068*/{ "UI_Status_Career.fng", // 24
            0x4EEB10,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8084*/{ "UI_Status_Region.fng", // 25
            0x4B1C90,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F80A0*/{ "UI_Status_DVD.fng", // 26
            0x4B1D90,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F80BC*/{ "EA_Trax_Jukebox.fng", // 27
            0x554F20,
            HELP_OPTIONS_EATRAX,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F80D8*/{ "UI_Menu_Asset_Reputation.fng", // 28
            0x553B00,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F80F4*/{ "UI_CareerCarLot.fng", // 29
            0x4FC0B0,
            HELP_CAREER_CARLOT,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0xC0, 0x0 },

/*7F8110*/{ "UI_EngageEventDialog.fng", // 30
            0x4CE650,
            0,
            0,
            0x0, 0xC0, 0x0 },

/*7F812C*/{ "UI_EngageRaceDialog.fng", // 31
            0x4E34A0,
            0,
            0,
            0x0, 0xC0, 0x0 },

/*7F8148*/{ "UI_EngageShopDialog.fng", // 32
            0x4CE6B0,
            0,
            0,
            0x0, 0xC0, 0x0 },

/*7F8164*/{ "UI_Showcase_Preview.fng", // 33
            0x4E3500,
            0,
            0,
            0x0, 0xC0, 0x0 },

/*7F8180*/{ "UI_Showcase_DPAD.fng", // 34
            0x4CF5C0,
            0,
            0,
            0x0, 0xC0, 0x0 },

/*7F819C*/{ "IG_PlayMovie.fng", // 35
            0x554E00,
            0,
            0,
            0x0, 0xC0, 0x0 },

/*7F81B8*/{ "UI_Pause.fng", // 36
            0x4F4790,
            0,
            BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK | LAYOUT_ONE_LINE_NO_BG | LAYOUT_PAUSEMENU_POSITION,
            0x0, 0xC0, 0x0 },

/*7F81D4*/{ "UI_PauseOptionsMain.fng", // 37
            0x4F47F0,
            0,
            BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK | LAYOUT_ONE_LINE_NO_BG | LAYOUT_PAUSEMENU_POSITION,
            0x0, 0x80, 0x0 },

/*7F81F0*/{ "UI_PauseOptions.fng", // 38
            0x4EB650,
            0,
            BUTTON_PC_NAV_BACK | LAYOUT_ONE_LINE_NO_BG,
            0x0, 0x440, 0x0 },

/*7F820C*/{ "UI_ReplayControl.fng", // 39
            0x4D7AD0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8228*/{ "HUD_SingleRace.fng", // 40
            0x0,
            0,
            0,
            0x0, 0x168, 0x1 },

/*7F8244*/{ "HUD_Drift.fng", // 41
            0x0,
            0,
            0,
            0x0, 0x80, 0x1 },

/*7F8260*/{ "HUD_Drag.fng", // 42
            0x0,
            0,
            0,
            0x0, 0x80, 0x1 },

/*7F827C*/{ "UI_InGame_WorldMap.fng", // 43
            0x4F9C10,
            0,
            BUTTON_PC_NAV_BACK | LAYOUT_WORLDMAP_POSITION | LAYOUT_ONE_LINE_NO_BG,
            0x0, 0x80, 0x0 },

/*7F8298*/{ "UI_EngageMessageDialog.fng", // 44
            0x4B2240,
            0,
            BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK | LAYOUT_WORLDMAP_POSITION | LAYOUT_ONE_LINE_NO_BG,
            0x0, 0x80, 0x0 },

/*7F82B4*/{ "UI_SMS_Mailbox.fng", // 45
            0x4E2050,
            0,
            BUTTON_PC_NAV_READ_MESSAGE | BUTTON_PC_NAV_BACK | BUTTON_PC_NAV_DELETE | LAYOUT_WORLDMAP_POSITION | LAYOUT_ONE_LINE_NO_BG,
            0x0, 0x80, 0x0 },

/*7F82D0*/{ "GarageMain.fng", // 46
            0x4EB130,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F82EC*/{ "DiscError.fng", // 47
            0x5522D0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8308*/{ "GenericDialog.fng", // 48
            0x0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8324*/{ "GenericDialog_LARGE.fng", // 49
            0x0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8340*/{ "GenericDialog_MED.fng", // 50
            0x0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F835C*/{ "GenericDialog_SMALL.fng", // 51
            0x0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8378*/{ "GenericDialog_Animate_LARGE.fng", // 52
            0x0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8394*/{ "GenericDialog_Animate_MED.fng", // 53
            0x0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F83B0*/{ "GenericDialog_Animate_SMALL.fng", // 54
            0x0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F83CC*/{ "GenericDialog_ThreeButton.fng", // 55
            0x0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F83E8*/{ "IG_GenericDialog_LARGE.fng", // 56
            0x0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8404*/{ "IG_GenericDialog_MED.fng", // 57
            0x0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8420*/{ "IG_GenericDialog_SMALL.fng", // 58
            0x0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F843C*/{ "HelpDialog_LARGE.fng", // 59
            0x0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8458*/{ "HelpDialog_MED.fng", // 60
            0x0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8474*/{ "HelpDialog_SMALL.fng", // 61
            0x0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8490*/{ "GenericerDialog.fng", // 62
            0x0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F84AC*/{ "MU_QRTransmissionSelect.fng", // 63
            0x0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F84C8*/{ "MU_QuickRaceCarSelect.fng", // 64
            0x4EB1A0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F84E4*/{ "Chyron_FE.fng", // 65
            0x4CB180,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8500*/{ "Chyron_IG.fng", // 66
            0x4CB180,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F851C*/{ "UI_DebugTest.fng", // 67
            0x4B89B0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8538*/{ "UI_InGameDialog.fng", // 68
            0x0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8554*/{ "UI_VirtualKeyboard.fng", // 69
            0x4EFD40,
            HELP_VIRTUAL_KEYBOARD,
            0,
            0x0, 0x258, 0x0 },

/*7F8570*/{ "UI_QRModeSelect.fng", // 70
            0x4EF280,
            HELP_QR_MODE_SELECT,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F858C*/{ "UI_QRModeOptions.fng", // 71
            0x4FA550,
            HELP_QR_MODE_OPTIONS,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x200, 0x0 },

/*7F85A8*/{ "UI_QRTrackSelect.fng", // 72
            0x4EF9B0,
            HELP_TRACK_SELECT,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F85C4*/{ "UI_QRCarSelect.fng", // 73
            0x4FC190,
            HELP_QR_CAR_SELECT,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK | BUTTON_PC_NAV_CUSTOMIZE,
            0x0, 0x100, 0x0 },

/*7F85E0*/{ "UI_OLCarSelect.fng", // 74
            0x4FC190,
            HELP_OL_CAR_SELECT,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK | BUTTON_PC_NAV_CUSTOMIZE,
            0x0, 0x100, 0x0 },

/*7F85FC*/{ "2P_PressStart.fng", // 75
            0x4CCED0,
            0,
            0,
            0x0, 0x100, 0x0 },

/*7F8618*/{ "UI_DebugCarCustomize.fng", // 76
            0x554A00,
            0,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F8634*/{ "UI_ChooseCustomizeCategory.fng", // 77
            0x5591E0,
            HELP_SHOP_SELECT,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F8650*/{ "UI_ChoosePerformanceCategory.fng", // 78
            0x559C30,
            HELP_PERFORMANCE_SHOP_MAIN,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F866C*/{ "UI_GenericParts_Browser.fng", // 79
            0x566430,
            HELP_CRIB_CHANGEPARTS_MAIN,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0xFFFFFFFF, 0x80, 0x0 },

/*7F8688*/{ "UI_ChoosePaintCategory.fng", // 80
            0x55A400,
            HELP_GRAPHICS_SHOP_MAIN,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_CANCEL_CHANGES | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F86A4*/{ "UI_ChoosePerformancePackage.fng", // 81
            0x55C8D0,
            HELP_PERFORMANCE_SHOP_MAIN,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_INSTALL_PACKAGE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F86C0*/{ "UI_BuyPerformanceParts.fng", // 82
            0x56C150,
            HELP_PERFORMANCE_SHOP_PART_SELECT,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_INSTALL_PART | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F86DC*/{ "UI_PerformanceBrandSelect.fng", // 83
            0x554B80,
            0,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE,
            0x0, 0x80, 0x0 },

/*7F86F8*/{ "UI_Paint.fng", // 84
            0x56C1B0,
            HELP_GRAPHICS_SHOP_PAINT_SELECT,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F8714*/{ "UI_ChooseVinylLayer.fng", // 85
            0x555440,
            HELP_GRAPHICS_SHOP_VINYL_LAYER_SELECT,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F8730*/{ "UI_ChooseUniquePart.fng", // 86
            0x554B20,
            HELP_UNIQUES_BROWSER,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_INSTALL_PART | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F874C*/{ "UI_DecalMain.fng", // 87
            0x554AC0,
            HELP_GRAPHICS_SHOP_DECAL_SELECT_CATEGORY,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F8768*/{ "UI_DecalsOverlay.fng", // 88
            0x56B900,
            0,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F8784*/{ "UI_DecalsOverlayInvis.fng", // 89
            0x56B900,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F87A0*/{ "UI_ChooseCustomHUD.fng", // 90
            0x554DA0,
            HELP_CHANGE_HUD_COLOR,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F87BC*/{ "UI_ChooseRimBrand.fng", // 91
            0x554A60,
            HELP_BODY_SHOP_MAIN,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F87D8*/{ "UI_Rims_Browser.fng", // 92
            0x56B350,
            HELP_BODY_SHOP_MAIN,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F87F4*/{ "UI_ChooseSpinner.fng", // 93
            0x56C0F0,
            HELP_BODY_SHOP_MAIN,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F8810*/{ "UI_PerformanceDyno_MAIN.fng", // 94
            0x55D7F0,
            0,
            BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x100, 0x0 },

/*7F882C*/{ "UI_PerformanceTuning_Master.fng", // 95
            0x55D8B0,
            0,
            BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F8848*/{ "UI_PerformanceTuning_Graph.fng", // 96
            0x55DB70,
            0,
            BUTTON_PC_NAV_BACK | BUTTON_DYNO_RESET_OR_PC_NAV_RESET_KEYS | BUTTON_DYNO_TIP,
            0x0, 0x100, 0x0 },

/*7F8864*/{ "UI_PerformanceTuning_NOS.fng", // 97
            0x55DB70,
            0,
            BUTTON_PC_NAV_BACK | BUTTON_DYNO_RESET_OR_PC_NAV_RESET_KEYS | BUTTON_DYNO_TIP,
            0x0, 0x100, 0x0 },

/*7F8880*/{ "UI_PerformanceTuning_Sliders.fng", // 98
            0x55DB10,
            0,
            BUTTON_PC_NAV_BACK | BUTTON_DYNO_RESET_OR_PC_NAV_RESET_KEYS,
            0x0, 0x100, 0x0 },

/*7F889C*/{ "UI_PerformanceTuning_Drivetrain.fng", // 99
            0x55DB10,
            0,
            BUTTON_PC_NAV_BACK | BUTTON_DYNO_RESET_OR_PC_NAV_RESET_KEYS | BUTTON_DYNO_TIP,
            0x0, 0x100, 0x0 },

/*7F88B8*/{ "UI_PerformanceTuning_Setting.fng", // 100
            0x55D850,
            0,
            BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x100, 0x0 },

/*7F88D4*/{ "UI_PerformanceDyno_Chart.fng", // 101
            0x55AF20,
            0,
            BUTTON_PC_NAV_CONTINUE,
            0x0, 0x100, 0x0 },

/*7F88F0*/{ "UI_PerformanceDyno_Results.fng", // 102
            0x55AF80,
            0,
            BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x100, 0x0 },

/*7F890C*/{ "UI_ICEMAIN.fng", // 103
            0x560550,
            HELP_CARSPECIALTIES_SHOP_MAIN,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x100, 0x0 },

/*7F8928*/{ "UI_IcePartsOverlay.fng", // 104
            0x56C3C0,
            0,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_INSTALL_PART | BUTTON_PC_NAV_BACK,
            0x0, 0x100, 0x0 },

/*7F8944*/{ "UI_CustomNeonMain.fng", // 105
            0x554C40,
            0,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x100, 0x0 },

/*7F8960*/{ "UI_NeonPartsOverlay.fng", // 106
            0x554BE0,
            0,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_INSTALL_PART | BUTTON_PC_NAV_BACK,
            0x0, 0x100, 0x0 },

/*7F897C*/{ "UI_CustomHUDOverlay.fng", // 107
            0x56C420,
            HELP_HUD_SELECT,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_INSTALL_PART | BUTTON_PC_NAV_BACK,
            0x0, 0x100, 0x0 },

/*7F8998*/{ "UI_PostRaceResults.fng", // 108
            0x4FBB60,
            0,
            BUTTON_PC_NAV_CONTINUE | LAYOUT_ONE_LINE_NO_BG | LAYOUT_RIGHT_POSITION,
            0x0, 0x80, 0x0 },

/*7F89B4*/{ "UI_PostRaceReward.fng", // 109
            0x4D72F0,
            0,
            BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F89D0*/{ "UI_SponsorPopup.fng", // 110
            0x4C1280,
            0,
            BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F89EC*/{ "UI_Sponsorship_new.fng", // 111
            0x4F4730,
            0,
            BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F8A08*/{ "UI_PostRace_TournResults.fng", // 112
            0x4FBF00,
            0,
            BUTTON_PC_NAV_CONTINUE | LAYOUT_ONE_LINE_NO_BG | LAYOUT_RIGHT_POSITION,
            0x0, 0x80, 0x0 },

/*7F8A24*/{ "UI_PostRace_TournStandings.fng", // 113
            0x4FBF60,
            0,
            BUTTON_PC_NAV_CONTINUE,
            0x0, 0x80, 0x0 },

/*7F8A40*/{ "UI_PostRaceStats.fng", // 114
            0x4FD6C0,
            0,
            BUTTON_PC_NAV_BACK | LAYOUT_ONE_LINE_NO_BG,
            0x0, 0x80, 0x0 },

/*7F8A5C*/{ "UI_PostRace.fng", // 115
            0x4F3940,
            0,
            BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK | LAYOUT_ONE_LINE_NO_BG | LAYOUT_PAUSEMENU_POSITION,
            0x0, 0x80, 0x0 },

/*7F8A78*/{ "MU_PostRaceConfirm.fng", // 116
            0x0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8A94*/{ "LS_BlankMovie.fng", // 117
            0x4A8AD0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8AB0*/{ "LS_EALogo.fng", // 118
            0x4A89D0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8ACC*/{ "MW_LS_IntroFMV.fng", // 119
            0x4C52D0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8AE8*/{ "MW_LS_Splash.fng", // 120
            0x4C5500,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8B04*/{ "UG_LS_IntroFMV.fng", // 121
            0x4C52D0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8B20*/{ "UG_LS_Splash.fng", // 122
            0x4C5500,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8B3C*/{ "LS_THXMovie.fng", // 123
            0x4A8D90,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8B58*/{ "LS_PSAMovie.fng", // 124
            0x4A8C60,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8B74*/{ "LS_Demo_Legal.fng", // 125
            0x4C50C0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8B90*/{ "LS_Demo_PSA.fng", // 126
            0x4A8EC0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8BAC*/{ "LS_Demo_ESRB.fng", // 127
            0x4A8F50,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8BC8*/{ "LS_Demo_Warning.fng", // 128
            0x4A9080,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8BE4*/{ "UI_EngageMessageDialog.fng", // 129
            0x4B2240,
            0,
            BUTTON_PC_NAV_BACK | BUTTON_PC_NAV_DELETE | LAYOUT_ONE_LINE_NO_BG,
            0x0, 0x80, 0x0 },

/*7F8C00*/{ "", // 130
            0x4D9480,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8C1C*/{ "LS_LangSelect.fng", // 131
            0x4F48A0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8C38*/{ "LS_Chinese_Health.fng", // 132
            0x4A87E0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8C54*/{ "UI_OL_Disconnect.fng", // 133
            0x4D0390,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8C70*/{ "UI_OL_Disconnect_BG.fng", // 134
            0x4D03F0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8C8C*/{ "UI_OL_WebOffer.fng", // 135
            0x49D550,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8CA8*/{ "UI_OL_WebOffer2.fng", // 136
            0x4B9E80,
            0,
            0,
            0x0, 0x0, 0x0 },

/*7F8CC4*/{ "UI_OL_News.fng", // 137
            0x49D4F0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8CE0*/{ "UI_OLLobbyRoom.fng", // 138
            0x4FA990,
            HELP_OL_LOBBY,
            0,
            0x0, 0x200, 0x0 },

/*7F8CFC*/{ "UI_OLGameRoom.fng", // 139
            0x4F1820,
            HELP_OL_GAMEROOM,
            0,
            0x0, 0x200, 0x0 },

/*7F8D18*/{ "UI_OLGameRoom_host.fng", // 140
            0x4F1880,
            0,
            0,
            0x0, 0x200, 0x0 },

/*7F8D34*/{ "UI_OLGameRoom_client.fng", // 141
            0x4F18E0,
            0,
            0,
            0x0, 0x200, 0x0 },

/*7F8D50*/{ "UI_OLPreRaceStart.fng", // 142
            0x4D3430,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8D6C*/{ "UI_OL_ViewCar.fng", // 143
            0x4FB260,
            HELP_OL_VIEWCAR,
            BUTTON_PC_NAV_BACK,
            0x0, 0x100, 0x0 },

/*7F8D88*/{ "UI_OL_ViewTrack.fng", // 144
            0x4FB2C0,
            HELP_OL_VIEWTRACK,
            BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F8DA4*/{ "UI_OLCarLot.fng", // 145
            0x4FD450,
            HELP_OL__TRADECAR_MAIN,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F8DC0*/{ "UI_OLMAIN.fng", // 146
            0x4F1940,
            HELP_OL_MAIN_MENU,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK | BUTTON_PC_NAV_EA_MESSENGER,
            0x0, 0x80, 0x0 },

/*7F8DDC*/{ "UI_OLFilters.fng", // 147
            0x4E6530,
            0,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK | BUTTON_PC_NAV_EA_MESSENGER,
            0x0, 0x80, 0x0 },

/*7F8DF8*/{ "PC_OL_Lobby.fng", // 148
            0x4FA990,
            0,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_BACK | BUTTON_PC_NAV_EA_MESSENGER,
            0x0, 0x80, 0x0 },

/*7F8E14*/{ "PC_OL_GameRoom.fng", // 149
            0x4F1820,
            HELP_OL_GAME_ROOM,
            BUTTON_PC_NAV_BACK | BUTTON_PC_NAV_EA_MESSENGER,
            0x0, 0x200, 0x0 },

/*7F8E30*/{ "UI_OLPassword.fng", // 150
            0x4E42C0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8E4C*/{ "UI_OLRankings_Personal.fng", // 151
            0x4F1BC0,
            HELP_OL_PERSONAL_RANK,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_BACK | BUTTON_PC_NAV_EA_MESSENGER,
            0x0, 0x80, 0x0 },

/*7F8E68*/{ "UI_OLRankings_Overall.fng", // 152
            0x4FB1A0,
            HELP_OL_OVERALL_RANK,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK | BUTTON_PC_NAV_EA_MESSENGER,
            0x0, 0x80, 0x0 },

/*7F8E84*/{ "UI_OLRankings_Monthly.fng", // 153
            0x4E64D0,
            HELP_OL_MONTHLY_RANK,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_BACK | BUTTON_PC_NAV_EA_MESSENGER,
            0x0, 0x80, 0x0 },

/*7F8EA0*/{ "UI_OLEAMessenger.fng", // 154
            0x4F22F0,
            HELP_OL_MESSENGER,
            0,
            0x0, 0x80, 0x0 },

/*7F8EBC*/{ "PC_OL_SEARCH.fng", // 155
            0x4D5070,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8ED8*/{ "UI_OL_FriendDialogue.fng", // 156
            0x4FB200,
            0,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F8EF4*/{ "UI_OLX_Message.fng", // 157
            0x4F24E0,
            0,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F8F10*/{ "UI_OLRankings.fng", // 158
            0x4F2540,
            HELP_OL_RANKINGS_MENU,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK | BUTTON_PC_NAV_EA_MESSENGER,
            0x0, 0x80, 0x0 },

/*7F8F2C*/{ "UI_OLX_FindResults.fng", // 159
            0x4F25A0,
            0,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F8F48*/{ "UI_OL_Feedback.fng", // 160
            0x4F27E0,
            HELP_OL_MESSENGER_FEEDBACK,
            0,
            0x0, 0x80, 0x0 },

/*7F8F64*/{ "UI_OL_Challenge.fng", // 161
            0x4D6940,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F8F80*/{ "UI_OLViewCareer.fng", // 162
            0x4E88C0,
            0,
            BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F8F9C*/{ "UI_OLISPConnect.fng", // 163
            0x4B9750,
            0,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x0, 0x0 },

/*7F8FB8*/{ "UI_OLSelectPersona.fng", // 164
            0x4E3CD0,
            HELP_OL_PS2_PERSONA_SELECT,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK | BUTTON_PC_NAV_DELETE,
            0x0, 0x100, 0x0 },

/*7F8FD4*/{ "UI_OLCreateUser.fng", // 165
            0x4E3D50,
            HELP_OL_PS2_CREATEACCOUNT,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_BACK,
            0x0, 0x0, 0x0 },

/*7F8FF0*/{ "UI_OLCreateUser_2.fng", // 166
            0x4BAA60,
            0,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_BACK,
            0x0, 0x0, 0x0 },

/*7F900C*/{ "UI_OLAgeVerif.fng", // 167
            0x4E3DD0,
            0,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x0, 0x0 },

/*7F9028*/{ "UI_OLAgeTooYoung.fng", // 168
            0x49E780,
            0,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x0, 0x0 },

/*7F9044*/{ "UI_OLUseExisting.fng", // 169
            0x4E3E30,
            HELP_OL_PS2_USEEXISTING,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x100, 0x0 },

/*7F9060*/{ "UI_OLForgotAccountName.fng", // 170
            0x4E3BD0,
            0,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x100, 0x0 },

/*7F907C*/{ "UI_OLEALogin.fng", // 171
            0x4E3C50,
            HELP_OL_PS2_EALOGIN,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x100, 0x0 },

/*7F9098*/{ "UI_DateEntry.fng", // 172
            0x552720,
            HELP_OL_PS2_DATEWIDGET,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x0, 0x0 },

/*7F90B4*/{ "DiscErrorPC.fng", // 173
            0x4D97A0,
            0,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F90D0*/{ "UI_ProfileManager.fng", // 174
            0x4F4900,
            HELP_OPTIONS_MAIN,
            BUTTON_PC_NAV_QUIT | BUTTON_PC_NAV_CONTINUE | BUTTON_PC_NAV_BACK,
            0x0, 0x80, 0x0 },

/*7F90EC*/{ "UI_Deleteprofile.fng", // 175
            0x4F4960,
            HELP_OPTIONS_MAIN,
            0,
            0x0, 0x80, 0x0 },

/*7F9108*/{ "MC_Bootup.fng", // 176
            0x4F32B0,
            0,
            0,
            0x0, 0x80, 0x0 },

/*7F9124*/{ "MC_List.fng", // 177
            0x4D7070,
            HELP_LOAD_PROFILE_BOOT,
            BUTTON_PC_NAV_QUIT,
            0x0, 0x80, 0x0 },

/*7F9140*/{ "MC_Main.fng", // 178
            0x4F3320,
            0,
            BUTTON_PC_NAV_QUIT,
            0x0, 0x80, 0x0 },

/*7F915C*/{ "MC_Background.fng", // 179
            0x4A8740,
            0,
            0,
            0x0, 0x80, 0x0 },
};
Short excerpt from hooking CreateFNGObject

arg2 for the dialogs are a pointer to the text string

ShowFNG(AB447B38 (""), 037864A0, 00000000) = 00000000
ShowFNG(33AC1CB4 ("UI_PC_Help_Bar.fng"), 0378BD30, 00000000) = 03D7EB60
ShowFNG(E503D390 ("MC_Bootup.fng"), 0379ADE0, 00000000) = 03786140
ShowFNG(23F4270A ("LS_PSAMovie.fng"), 03786120, 00000000) = 03787AD0
ShowFNG(F774ED37 ("MC_Background.fng"), 03786120, 00000000) = 03786290
ShowFNG(801E019C ("MC_Main.fng"), 03787F40, 00000000) = 0379F4B0
ShowFNG(136E3C13 ("MC_List.fng"), 0379C8C0, 00000000) = 037A86F0
ShowFNG(4CDD8B14 ("GarageMain.fng"), 037AE470, 00000000) = 037AFD70
ShowFNG(C343126A ("UI_Main.fng"), 037A8EC0, 00000000) = 037AE880
ShowFNG(8E2B101E ("UI_QRCarSelect.fng"), 037879D0, 00000000) = 0379D3B0
ShowFNG(887BBEA7 ("UI_ChooseCustomizeCategory.fng"), 0379BF40, 00000000) = 03787570
ShowFNG(DF3D7763 ("UI_Menu_Asset_Reputation.fng"), 03789730, 00000000) = 0378A940
ShowFNG(AF09F84F ("UI_GenericParts_Browser.fng"), 0378A860, 00000000) = 037BBBB0
ShowFNG(887BBEA7 ("UI_ChooseCustomizeCategory.fng"), 037B9F40, 00000000) = 037BE620
ShowFNG(8E2B101E ("UI_QRCarSelect.fng"), 037BE4A0, 00000000) = 03787660
ShowFNG(C343126A ("UI_Main.fng"), 0378A570, 00000000) = 0379F260
ShowFNG(F6C99F4D ("UI_QRModeSelect.fng"), 0379F0F0, 00000000) = 03788380
ShowFNG(8E2B101E ("UI_QRCarSelect.fng"), 0378BF40, 00000000) = 0379B7C0
ShowFNG(6962C0CD ("GenericDialog_SMALL.fng"), 0379B620, 00838640) = 037A80E0
ShowFNG(6962C0CD ("GenericDialog_SMALL.fng"), 037AE720, 00838640) = 037ABA50
ShowFNG(6962C0CD ("GenericDialog_SMALL.fng"), 037AE720, 00838640) = 037ABA50
ShowFNG(AB447B38 (""), 03767010, 00000000) = 00000000
ShowFNG(0EB1F7E5 ("HUD_SingleRace.fng"), 037A45B0, 00000000) = 00000000
ShowFNG(26691D4A ("Chyron_IG.fng"), 03793AE0, 00000000) = 03D7EBF0
ShowFNG(33AC1CB4 ("UI_PC_Help_Bar.fng"), 037B8870, 00000000) = 03D7EB60
ShowFNG(32124083 ("UI_Pause.fng"), 03796280, 00000000) = 037B8B90
ShowFNG(33AC1CB4 ("UI_PC_Help_Bar.fng"), 037C7D30, 00000000) = 03D7EB60
ShowFNG(73F51C74 ("UI_PauseOptionsMain.fng"), 037C3EF0, 00000000) = 037C6080
ShowFNG(AEA9078F ("UI_PauseOptions.fng"), 037BAE10, 00000000) = 037D8430
ShowFNG(73F51C74 ("UI_PauseOptionsMain.fng"), 037D8F40, 00000000) = 037DE0C0
ShowFNG(33AC1CB4 ("UI_PC_Help_Bar.fng"), 037DFF40, 00000000) = 03D7EB60
ShowFNG(32124083 ("UI_Pause.fng"), 037DDF40, 00000000) = 037DE2B0
ShowFNG(AA469286 ("UI_InGameDialog.fng"), 037C1120, 00838640) = 037C2AB0
TODO:
.rdata:0079C118 aIconscroller_5 db 'IconScrollerMenu : Switch/Pop/Direct Pop called on me (0x%x).',0Ah
.rdata:0079C118                                         ; DATA XREF: sub_543D40+3E9?o
.rdata:0079C118                 db 0
.rdata:0079C157                 align 4
.rdata:0079C158 aIconscroller_4 db 'IconScrollerMenu : EXIT/INIT_COMPLETE called on me (0x%x).',0Ah,0
.rdata:0079C158                                         ; DATA XREF: sub_543D40+38B?o
.rdata:0079C194 aIconscroller_2 db 'IconScrollerMenu : UNDIM_COMPLETE.',0Ah,0

Symbols:

Index

UI

Listing all FNG screens #

Listed by nfsu2-re-hooks/debug-custom-iterate-somethingui.c

Listing all that are loaded when I run my game. I might've missed some but I ran that code for each place in the game that has a separate bin/bun file (like drag, drift, performance tuning, ...).

~200 lines
0027F0C1: fngdata UI_Paint.fng
013932DF: fngdata UI_OLEALogin.fng
01905520: fngdata UI_ChoosePaintCategory.fng
01E17DF6: fngdata UI_OLISPConnect.fng
028DFD06: fngdata UI_PostRace.fng
0352B6D3: fngdata UI_OLCreateUser.fng
04B053EB: fngdata GenericDialog_Animate_SMALL.fng
04EDFC8D: fngdata IG_PlayMovie.fng
066EAC16: fngdata UI_ChoosePerformanceCategory.fng
0682FBB1: fngdata UI_MagazineSelect.fng
07F516C3: fngdata UI_SMS_Mailbox.fng
0800A835: fngdata GenericDialog.fng
09AB5829: fngdata UI_OLAgeVerif.fng
0A857B6D: fngdata UI_OLViewCareer.fng
0B18389B: fngdata ScreenPrintf.fng
0CEAC20E: HASH
0F35F92F: fngdata UI_PauseOptions.fng
12A62B7C: fngdata UI_CareerCarLot.fng
13CA35C3: fngdata UI_Menu_Asset_Reputation.fng
152631D1: fngdata UI_RewardsSponsor.fng
15964F7E: fngdata UI_PerformanceBrandSelect.fng
15BE6570: HASH
173F7696: fngdata UI_MagazineReward.fng
175B0026: fngdata UI_Options_PC_Controller.fng
17B0719C: HASH
1821F0A1: fngdata UI_OL_WebOffer2.fng
19F14079: fngdata IG_GenericDialog_MED.fng
1A4525D8: fngdata UI_PostRaceResults.fng
1B2211AC: HASH
1BB72367: HASH
1C79317C: fngdata UI_DecalsOverlayInvis.fng
1D3BEC2F: fngdata LS_Demo_Legal.fng
1D9A9084: fngdata 2P_PressStart.fng
1EA89655: fngdata UI_PerformanceDyno_MAIN.fng
20626E14: fngdata UI_ChooseUniquePart.fng
207415ED: HASH
2373CEF6: fngdata UI_EngageShopDialog.fng
250CFFA8: HASH
25977A8D: fngdata GenericDialog_SMALL.fng
272D4D66: fngdata UI_Status_DVD.fng
2988130C: fngdata UI_DebugTest.fng
298C7043: fngdata EA_Trax_Jukebox.fng
2A008F69: fngdata UI_PerformanceTuning_Setting.fng
2E8E7B34: fngdata UI_Status_Master.fng
2EA28EA6: NAME    Loading.fng
2ED34899: fngdata HelpDialog_SMALL.fng
308FA458: NAME    PC_Loading.fng
30AC2FA1: fngdata UI_OL_FriendDialogue.fng
312C2437: fngdata UI_PerformanceTuning_Master.fng
32A39AB7: fngdata MC_Background.fng
334565B5: fngdata UI_ChooseVinylLayer.fng
34157582: fngdata UI_IcePartsOverlay.fng
364151B1: fngdata UI_OL_Disconnect_BG.fng
368B832C: NAME    HUD_Short_Track.fng
375CD530: fngdata UI_OL_ViewCar.fng
39700EB6: fngdata UI_OptionsMain.fng
3D0BB9B4: fngdata UI_PC_Help_Bar.fng
3DFBA96A: fngdata GenericDialog_MED.fng
3DFCB975: fngdata UI_CustomNeonMain.fng
447DECC9: fngdata UI_OL_Disconnect.fng
48897FC3: fngdata UI_Rims_Browser.fng
48DEA295: fngdata UI_OLLobbyRoom.fng
4B6AEF82: fngdata UI_ChooseCustomHUD.fng
4B744508: fngdata DiscErrorPC.fng
4BA5E3C5: fngdata UI_PC_Customize_Options.fng
4DC4524F: fngdata UI_OL_ViewTrack.fng
4E8F3E91: HASH
4EE26A74: fngdata UI_PauseOptionsMain.fng
51C5629B: fngdata UI_CribRewardOptionsMain.fng
53EBF5F5: fngdata DiscError.fng
559BF91D: fngdata UI_OLRankings.fng
571933C1: fngdata UI_PerformanceTuning_Sliders.fng
57608F53: HASH
58A34EAF: fngdata UI_GenericParts_Browser.fng
5A350141: fngdata UI_NeonPartsOverlay.fng
5BD84D87: fngdata UI_ChooseCustomizeCategory.fng
5C26695E: fngdata UI_QRCarSelect.fng
5CFDCCDB: fngdata UI_InGame_WorldMap.fng
5F511518: HASH
60EFC10C: fngdata UI_MagazineView.fng
62EDCD4B: fngdata UI_Trailers.fng
6796BB48: HASH
6A4D7065: HASH
6B49C096: fngdata Credits.fng
6BC30104: fngdata UI_ChoosePerformancePackage.fng
6C4E4A5C: fngdata MC_Main.fng
6DE29245: fngdata UI_ChooseSpinner.fng
70B41B0E: fngdata LS_Demo_PSA.fng
7595818A: fngdata PC_OL_SEARCH.fng
75D5F4F4: HASH
75E59577: fngdata UI_CareerCrib.fng
75EF8A72: fngdata UI_PC_LAN.fng
75F15DCE: fngdata LS_BlankMovie.fng
7620B9D6: fngdata UI_Sponsorship_new.fng
7A0532F0: fngdata UI_OLPreRaceStart.fng
7E4A82FC: fngdata UI_OLX_Message.fng
7E67096A: HASH
7E75C222: fngdata UI_MagazineBack.fng
7E794C7E: fngdata UI_EngageEventDialog.fng
7E7FB245: fngdata UI_DebugCarCustomize.fng
7F0E48FE: HASH
82633365: fngdata Chyron_FE.fng
837735E5: fngdata UI_OLCarLot.fng
8549BCA3: fngdata UI_Showcase_Preview.fng
882AFF54: fngdata UI_OLGameRoom_host.fng
89561DF6: fngdata LS_Demo_ESRB.fng
8986DE4A: fngdata Chyron_IG.fng
8C07642F: fngdata UI_PostRace_TournResults.fng
8C6DC7F5: fngdata UI_DateEntry.fng
8D46762C: fngdata PC_OL_Lobby.fng
8DD82E2F: fngdata UI_OLEAMessenger.fng
8EB04A1A: fngdata UI_Showcase_DPAD.fng
90C68FFB: fngdata UI_PerformanceTuning_NOS.fng
93953D3D: fngdata UI_QRTrackSelect.fng
93CDA4FA: fngdata LS_Chinese_Health.fng
94B8EEC5: fngdata UI_StartCareer.fng
94F5DE45: fngdata UI_Wheel_Options.fng
966756A8: fngdata UI_PostRace_TournStandings.fng
97396BBA: fngdata UI_Status_Career.fng
97B8D7B1: fngdata UI_OLAgeTooYoung.fng
985F0BEA: fngdata LS_PSAMovie.fng
9917E7DD: fngdata UI_CareerWorldMap.fng
991E7B6D: HASH
9BAF3723: fngdata UI_CustomHUDOverlay.fng
9C70ADBB: fngdata UI_ICEMAIN.fng
9F0D0D93: HASH
9F2A5298: fngdata MU_PostRaceConfirm.fng
A07EE45F: fngdata UI_OLForgotAccountName.fng
A12FD64D: fngdata UI_CareerCarSelect.fng
A17E8D57: fngdata UI_OLGameRoom.fng
A34791EE: fngdata IG_GenericDialog_LARGE.fng
A419E049: HASH
A448D5B6: HASH
A6268CF5: fngdata UI_OLGameRoom_client.fng
A67D2563: fngdata UI_Pause.fng
A78253CC: HASH
AA7FA5B8: fngdata UI_OLSelectPersona.fng
AB4A06F1: fngdata UI_BuyPerformanceParts.fng
AD1275DE: fngdata MW_LS_IntroFMV.fng
AD574C17: fngdata MU_QuickRaceCarSelect.fng
AF2B8985: NAME    Loading_Tips.fng
AF735B2A: fngdata UI_Main.fng
AF7CBBCE: HASH
AFEF0EB4: fngdata MW_LS_Splash.fng
B0426798: fngdata UI_OLUseExisting.fng
B0A628AD: fngdata UI_QRModeSelect.fng
B1801731: fngdata UI_ProfileManager.fng
B18CA345: fngdata UI_OLMAIN.fng
B2163416: fngdata UI_OLCarSelect.fng
B3268EAF: fngdata UI_OL_WebOffer.fng
B5B46040: fngdata LS_Demo_Warning.fng
B6362877: fngdata UI_EngageRaceDialog.fng
B80E4053: fngdata UI_DecalsOverlay.fng
BAC65E4D: fngdata UI_SponsorPopup.fng
C1A60945: fngdata HUD_SingleRace.fng
C2C0D48B: fngdata UI_PostRaceReward.fng
C3D52B59: fngdata UI_QRModeOptions.fng
C5040108: HASH
C6BE90AD: HASH
C9D7D6AA: fngdata UI_OLX_FindResults.fng
C9E86D9D: fngdata UI_PerformanceTuning_Graph.fng
C9E9B24C: fngdata UI_Status_Region.fng
CAC91380: fngdata UI_OLRankings_Personal.fng
CD10D406: fngdata UI_InGameDialog.fng
D0784C79: fngdata UI_OLFilters.fng
D08AF008: fngdata LS_LangSelect.fng
D2A8D47D: fngdata GenericDialog_Animate_LARGE.fng
D3B29348: fngdata GenericDialog_Animate_MED.fng
D4CC1CE2: fngdata UI_OL_Challenge.fng
D4D60373: fngdata UI_ReplayControl.fng
D54F115C: fngdata IG_GenericDialog_SMALL.fng
D5FB7455: fngdata UI_ChooseRimBrand.fng
D63ED3A4: fngdata UI_OL_Feedback.fng
D7F96E7D: fngdata LS_EALogo.fng
D8D4A69D: fngdata UI_VirtualKeyboard.fng
D9430075: fngdata UI_PostRaceStats.fng
DA67CC71: fngdata UI_Options.fng
DC554682: fngdata UI_PerformanceDyno_Results.fng
DD2161AC: fngdata UG_LS_Splash.fng
E0A971A7: fngdata UI_OLRankings_Monthly.fng
E2FCB91C: fngdata UI_OL_News.fng
E309B843: fngdata UI_DecalMain.fng
E3AD2FC9: NAME    Loading_Controller.fng
E4DABD68: fngdata GenericDialog_ThreeButton.fng
E648DFA3: fngdata UI_PerformanceTuning_Drivetrain.fng
E8FCD890: fngdata MC_Bootup.fng
ED044659: fngdata loading_boot.fng
ED560791: HASH
ED58A8C8: fngdata UI_PC_LAN_ServerSelect.fng
F02566D6: fngdata UG_LS_IntroFMV.fng
F0A3D2D5: HASH
F1646EA1: fngdata UI_EngageMessageDialog.fng
F38FFB1F: fngdata GenericDialog_LARGE.fng
F4127481: fngdata HUD_Drift.fng
F529AB42: fngdata UI_PerformanceDyno_Chart.fng
FA10C7BB: HASH
FB064971: fngdata UI_OLRankings_Overall.fng
FC1ADA49: fngdata UI_Deleteprofile.fng
FC6AB906: fngdata HUD_Drag.fng
FCBA7C6B: fngdata PC_OL_GameRoom.fng
FCCBC92B: fngdata HelpDialog_LARGE.fng
FCFACC73: fngdata UI_OLPassword.fng
FD948476: fngdata HelpDialog_MED.fng
FE23C19A: fngdata LS_THXMovie.fng
FF9E84D3: fngdata MC_List.fng
FFD19C74: fngdata GarageMain.fng

Lines with fngdata means they have a matching entry in fngdata. Lines with NAME don't, but are from binsection 30203 so they show their struct BinSection30203Data.fngName. Lines with HASH don't, and are from binsection 30210 so they show their struct BinSection30210.fngNameCSHash.

One of those that don't have a name but do show a name from their binsection data is Loading_Controller.fng. Since this one isn't in fngdata nor is the string present in the executable, this is the first time I've seen it.

All entries that have no name but only a hash known:

0CEAC20E 15BE6570 17B0719C 1B2211AC 1BB72367 250CFFA8 4E8F3E91 57608F53 5F511518 6796BB48 6A4D7065
75D5F4F4 7E67096A 7F0E48FE 991E7B6D 9F0D0D93 A419E049 A448D5B6 A78253CC AF7CBBCE C5040108 C6BE90AD
ED560791 F0A3D2D5 FA10C7BB 207415ED

Only of these are in hashes.txt (258KB) at this moment (207415ED: HUD_World_Loading.fng), so I'm curious what the other screens are... camera editor? I've seen strings and controls for it...):

Index

UI

Some FNG screens #

This was done by putting pointers to their strings into the boot flow entries (see Speedy boot).

2P_PressStart.fng

2player press start

loading_boot.fng

alternative loading screen with nfsu2 logo

HUD_World_Loading.fng

black screen with loader

Loading_Controller.fng

loading screen but with a list of control names, but the controller image itself is missing

UI > Some FNG screens

Replacing the loading screen #

I found loading_boot.fng pretty cool, so I wondered if that could be used instead for the loading screens (on boot, when loading race, going into freeroam, ..).

I've seen lots of places refer to a string with content PC_Loading.fng, so I thought maybe replacing that with loading_boot.fng will just work. And it does.

strcpy((char*) 0x790734, "loading_boot.fng");

Since the string is longer, it will override this string with an empty string though:

.rdata:00790744 aLoadingTipsFng db 'Loading_Tips.fng',0

It seems unused, so I guess it's fine. That made me wonder if that one could be used as loading screen, maybe it shows tips? But when trying it just shows a black screen. Maybe it's a cut feature?

Using Loading_Controller.fng and HUG_World_Loading.fng also works.

Index

UI > Some FNG screens (continuation)

UI_DebugTest.fng

pulsing text and different colored circles

UI_OLAgeTooYoung.fng

age too young

UI_OLMAIN.fng

online main menu

UI_OL_Feedback.fng

online player feedback menu

UI_OL_FriendDialogue.fng

online friend dialogue

UI_OL_Voice_Chat.fng

online voice chat

Index

UI

ScreenPrintf.fng #

So this is an interesting name, but doesn't really seem used (it makes sense, it shouldn't be used in a release build of the game). Though now with the few functions I documented maybe I can make it show?

I tried following code:

static
void hook_on_msg_WM_CHAR(int wparam)
{
	if (wparam == 121) { // y
		AddFngToUIObject_1("ScreenPrintf.fng", 0);
	}
}

It didn't crash, so that was a win already. The second parameter for that func is what I suspect some data to initialize the fng screen. It fortunately seems like either the ScreenPrintf one doesn't initialize anything or it has a null check for the passed data. It doesn't seem to show anything though. Then I thought of the UI elements and their visitor methods (like FNGInfo::VisitUIRecursively). So I tried to print all the UI elements that are on screen (See nfsu2-re-hooks/debug-custom-uielementvisitor.c, and here's the output when in the options menu in the pause menu while in career freeroam:

Output
fng UI_PauseOptionsMain.fng:
    element type 5 hash 8D7E0C53 flags 40000000:
        element type 2 hash 42ADB44C flags 40000000: Pause Options Main
        element type 1 hash B62648B1 flags 40000000:
        element type 1 hash 7CA40D0F flags 40000000:
        element type 5 hash 371F1C93 flags 40000000:
            element type 1 hash 0A570002 flags 40000000:
            element type 1 hash A67C3FE2 flags 40000000:
            element type 1 hash A67C3FE2 flags 40000000:
            element type 1 hash A67C3FE2 flags 40000000:
            element type 1 hash 5E11FBFB flags 40000000:
        element type 5 hash DE2F4E52 flags 40000000:
            element type 1 hash A67C3FE2 flags 40000000:
            element type 1 hash C8C5F8A6 flags 40000000:
            element type 1 hash C8C5F8A6 flags 40000000:
            element type 1 hash A67C3FE2 flags 40000000:
            element type 1 hash A67C3FE2 flags 40000000:
        element type 2 hash 5E7B09C9 flags 40000000: Option title
        element type 1 hash 687FDA31 flags 40000000:
        element type 1 hash DF422345 flags 43800000:
        element type 5 hash 02DDF58C flags 40000041:
            element type 5 hash 02DDF58C flags 40000001:
            element type 5 hash 812A09D4 flags 40000001:
                element type 2 hash 1C7FCF8B flags 40000001: Back
                element type 2 hash B02B46B7 flags 40000001: $JOY_EVENT_FENG_CANCEL$
            element type 5 hash 6A218478 flags 40000001:
                element type 2 hash 93B5083A flags 40000001: Select
                element type 2 hash 9D752086 flags 40000001: $JOY_EVENT_FENG_SELECT$
        element type 5 hash 0DE769B5 flags 40000000:
            element type 1 hash 5E1208DF flags 40000000:
            element type 1 hash 0894389E flags 40000000:
            element type 1 hash A67C3FE2 flags 40000000:
            element type 1 hash A67C3FE2 flags 40000000:
            element type 1 hash 5E11FBFB flags 40000000:
            element type 1 hash 0A570002 flags 40000000:
            element type 1 hash 0A570002 flags 40000000:
        element type 5 hash 52BDEE1D flags 40000000:
            element type 1 hash 39CC2F66 flags 40000000:
            element type 1 hash DDE4A5E5 flags 40000000:
            element type 1 hash DDE4A5E8 flags 40000000:
            element type 1 hash BE9AB445 flags 40000000:
            element type 1 hash 39CC2F45 flags 40000000:
            element type 1 hash DDE4A5E7 flags 40000000:
            element type 1 hash 39CC2F24 flags 40000000:
            element type 1 hash DDE4A5E6 flags 40000000:
    element type 1 hash B51549B8 flags 50080000:
    element type 1 hash 4B4E3890 flags 50080000:
    element type 1 hash 4B4E388F flags 50080000:
    element type 1 hash 4B4E388E flags 50080000:
    element type 1 hash 4B4E388D flags 50080000:
    element type 1 hash 4B4E388C flags 50080000:
    element type 1 hash 4B4E388B flags 57C80001:
    element type 1 hash 4B4E388A flags 57C80001:
    element type 1 hash 4B4E3889 flags 57C80000:
    element type 2 hash 47FF4E7C flags 40000000: EVENT HANDLER

    element type 1 hash 444969FE flags 40000000:
    element type 1 hash 444969FD flags 40000001:
    element type 1 hash 3FBB7A4E flags 40000000:
    element type 1 hash 688A1551 flags 40000000:
    element type 5 hash BE77206B flags 40000000:
        element type 5 hash 550E571C flags 48000000:
            element type 1 hash 1F3B164E flags 40000000:
    element type 1 hash B04669E3 flags        1:
    element type 1 hash 4B4E3888 flags 53C80000:
fng UI_PC_Help_Bar.fng:
    element type 2 hash 47FF4E7C flags 40000000: Event Handler
    element type 2 hash B8A7C6CF flags 50080001: WWWWWWWWWWWWW
    element type 1 hash 62376D1B flags 40000001:
    element type 2 hash B8A7C6CE flags 10080001: WWWWWWWWWWWWW
    element type 1 hash 62376D1A flags        1:
    element type 1 hash 62376D19 flags        0:
    element type 2 hash B8A7C6CD flags 10080000: WWWWWWWWWWWWW
    element type 2 hash B8A7C6CC flags 10080000: WWWWWWWWWWWWW
    element type 1 hash 62376D18 flags        0:
    element type 2 hash B8A7C6D0 flags 50080001: WWWWWWWWWWWWW
    element type 1 hash 62376D1C flags 40000001:
    element type 1 hash 62376D1D flags 40000001:
    element type 2 hash B8A7C6D1 flags 50080001: WWWWWWWWWWWWW
    element type 1 hash A444D171 flags 40000000:
    element type 1 hash A444D172 flags 40000000:
    element type 1 hash A444D170 flags 40000000:
    element type 1 hash A444D16F flags        0:
    element type 1 hash A444D16E flags 40000000:
    element type 1 hash E0AE8220 flags        0:
    element type 5 hash A22ED3B7 flags 40000000:
        element type 5 hash 2BAC0CEE flags 40000000:
            element type 1 hash E8E1E298 flags 40000001:
            element type 1 hash E8E1E29A flags 40000001:
            element type 1 hash E8E1E297 flags 40000001:
            element type 5 hash 12DF3004 flags 40000000:
                element type 1 hash 5145A2BF flags 40000000:
                element type 1 hash 5145A2BD flags 40000000:
                element type 1 hash 5145A2BC flags 40000000:
        element type 5 hash 3B949CD0 flags 40000001:
            element type 5 hash 84EB1500 flags 40000001:
                element type 2 hash EB406FEC flags 42800003: AA
                element type 2 hash EB3A688A flags 43800003: 0
                element type 5 hash 2253BC50 flags 40000001:
                    element type 1 hash A67C3FE2 flags 40000001:
                    element type 1 hash A67C3FE2 flags 40000001:
                element type 5 hash 6D2A1AE9 flags 40000001:
                    element type 1 hash D253B8F3 flags 40000001:
                    element type 1 hash 0894449B flags 40000001:
                element type 5 hash 22534096 flags 40000001:
                    element type 1 hash D6B3DA2D flags 40000001:
                    element type 1 hash A67C3FE2 flags 40000001:
fng HUD_SingleRace.fng:
    element type 1 hash 79498F08 flags 40000001:
    element type 1 hash 79498F07 flags 40000001:
    element type 1 hash 79498F06 flags 40000001:
    element type 5 hash 697D2BCA flags 40000001:
        element type 1 hash 4F649313 flags 43800001:
        element type 1 hash 4F649314 flags 43800001:
    element type 2 hash 77CE0364 flags 40000001: Disconnect in 15s
    element type 2 hash 192F96D9 flags        1: Bank Cash
    element type 2 hash 49106CE2 flags        1: Cash Reward: 333
    element type 2 hash 2A37A685 flags 40000001: DEFAULT STRING
    element type 2 hash 2A37A684 flags 40000001: DEFAULT STRING
    element type 5 hash 29F0A455 flags 40000001:
    element type 2 hash 2A37A683 flags 40000001: DEFAULT STRING
    element type 2 hash 2A37A682 flags 40000001: DEFAULT STRING
    element type 2 hash 2A37A681 flags 40000001: DEFAULT STRING
    element type 2 hash 2A37A680 flags 40000001: DEFAULT STRING
    element type 1 hash EB24E169 flags 40000001:
    element type 1 hash A71E35FB flags 40000001:
    element type 1 hash 62451575 flags 40000001:
    element type 1 hash B65634BC flags 40000001:
    element type 1 hash 84C55E14 flags 40000001:
    element type 1 hash C11804C6 flags 40000001:
    element type 1 hash 409C6EF0 flags 40000001:
    element type 1 hash 9E59FCF0 flags 40000001:
    element type 1 hash 79498F05 flags 40000001:
    element type 1 hash F7EC30B2 flags 40000001:
    element type 1 hash E24C838B flags 40000001:
    element type 1 hash 19EF4E89 flags 40000001:
    element type 1 hash 6E8333B0 flags 40000001:
    element type 1 hash 2C225885 flags 40000001:
    element type 1 hash 9E59FCEF flags 40000001:
    element type 1 hash 79498F04 flags 40000001:
    element type 1 hash F7EC30B1 flags 40000001:
    element type 1 hash E24C838A flags 40000001:
    element type 1 hash 19EF4E88 flags 40000001:
    element type 1 hash 6E8333AF flags 40000001:
    element type 1 hash 2C225884 flags 40000001:
    element type 2 hash 668C9CD6 flags        1: x3
    element type 1 hash 9E59FCEE flags 40000001:
    element type 1 hash 9E59FCED flags 40000001:
    element type 1 hash 9E59FCEC flags 40000001:
    element type 1 hash 9E59FCEB flags 40000001:
    element type 1 hash 79498F03 flags 40000001:
    element type 1 hash 79498F02 flags 40000001:
    element type 1 hash 79498F01 flags 40000001:
    element type 1 hash 79498F00 flags 40000001:
    element type 1 hash F7EC30B0 flags 40000001:
    element type 1 hash F7EC30AF flags 40000001:
    element type 1 hash F7EC30AE flags 40000001:
    element type 1 hash F7EC30AD flags 42400001:
    element type 1 hash E24C8389 flags 40000001:
    element type 1 hash 19EF4E87 flags 40000001:
    element type 1 hash 6E8333AE flags 40000001:
    element type 1 hash 2C225883 flags 40000001:
    element type 2 hash 8EB730A7 flags        3: Fort Union
    element type 1 hash 18DDF914 flags 40000001:
    element type 1 hash FE2467E3 flags 40000001:
    element type 2 hash 407919AF flags        1:
    element type 1 hash A71E35FA flags 40000001:
    element type 1 hash A71E35F9 flags 40000001:
    element type 1 hash A71E35F8 flags 40000001:
    element type 1 hash A71E35F7 flags 40000001:
    element type 1 hash A71E35F6 flags 40000001:
    element type 1 hash 1149C7DA flags 40000001:
    element type 1 hash 62451574 flags 40000001:
    element type 1 hash B65634BB flags 40000001:
    element type 1 hash 84C55E13 flags 40000001:
    element type 1 hash C11804C5 flags 40000001:
    element type 1 hash 409C6EEF flags 40000001:
    element type 1 hash 62451573 flags 40000001:
    element type 1 hash B65634BA flags 40000001:
    element type 1 hash 84C55E12 flags 40000001:
    element type 1 hash C11804C4 flags 40000001:
    element type 1 hash 409C6EEE flags 40000001:
    element type 1 hash 62451572 flags 40000001:
    element type 1 hash B65634B9 flags 40000001:
    element type 1 hash 84C55E11 flags 40000001:
    element type 1 hash C11804C3 flags 40000001:
    element type 1 hash 409C6EED flags 40000001:
    element type 1 hash 62451571 flags 40000001:
    element type 1 hash B65634B8 flags 40000001:
    element type 1 hash 84C55E10 flags 40000001:
    element type 1 hash C11804C2 flags 40000001:
    element type 1 hash 409C6EEC flags 40000001:
    element type 1 hash 62451570 flags 40000001:
    element type 1 hash B65634B7 flags 40000001:
    element type 1 hash 84C55E0F flags 40000001:
    element type 1 hash C11804C1 flags 40000001:
    element type 2 hash 565F28AE flags 40000001: TIME
    element type 2 hash 22465E2B flags 40000001: LAPY TIMER
    element type 2 hash 27754C7F flags 40000001: HANGY TIMEY!
    element type 2 hash 15D0E247 flags 40000001: + 25
    element type 1 hash 409C6EEB flags 42400001:
    element type 1 hash F2A54430 flags 42400001:
    element type 1 hash 7AF1CA63 flags 40000001:
    element type 1 hash E24C8388 flags 40000001:
    element type 1 hash 7AF1CA62 flags 40000001:
    element type 1 hash E24C8387 flags 40000001:
    element type 1 hash 19EF4E86 flags 40000001:
    element type 1 hash 19EF4E85 flags 40000001:
    element type 1 hash 6E8333AD flags 40000001:
    element type 1 hash 6E8333AC flags 40000001:
    element type 1 hash 2C225882 flags 40000001:
    element type 1 hash 2C225881 flags 40000001:
    element type 1 hash E24C8386 flags 40000001:
    element type 1 hash 7AF1CA61 flags 40000001:
    element type 1 hash 19EF4E84 flags 42400001:
    element type 1 hash 6E8333AB flags 42400001:
    element type 1 hash 2C225880 flags 42400001:
    element type 1 hash 024C680E flags 40000001:
    element type 1 hash 024C680B flags 40000001:
    element type 1 hash 024C680C flags 40000001:
    element type 1 hash 024C680D flags 40000001:
    element type 1 hash 024C680A flags 40000001:
    element type 1 hash 024C6809 flags 40000001:
    element type 1 hash 024C6808 flags 40000001:
    element type 1 hash EB24E168 flags 40000001:
    element type 1 hash EB24E165 flags 40000001:
    element type 1 hash EB24E166 flags 40000001:
    element type 1 hash EB24E167 flags 40000001:
    element type 1 hash EB24E164 flags 40000001:
    element type 1 hash EB24E163 flags 40000001:
    element type 1 hash EB24E162 flags 40000001:
    element type 1 hash 1B259972 flags 40000001:
    element type 1 hash C1347E2F flags 42400001:
    element type 1 hash 40CDC515 flags 42400001:
    element type 5 hash 382D2FC9 flags 40000001:
        element type 1 hash 142EDB6C flags 40000001:
        element type 1 hash 99EBA9C7 flags 40000001:
        element type 1 hash 142EDA64 flags 40000001:
        element type 1 hash 92A75587 flags 40000001:
    element type 1 hash 90FE80F3 flags 42400001:
    element type 2 hash 2DA220AA flags 40000001: +9999
    element type 2 hash 29AF04FD flags 40000001: 9.99
    element type 2 hash C18C12FE flags 40000001: 9.99
    element type 2 hash C18C12FD flags 40000001: WWWWWWWWWWWW DISCONNECTED
    element type 1 hash 411301E3 flags 40000001:
    element type 2 hash F59D909F flags 40000001: xxx:xxx:xxx
    element type 1 hash C82FA200 flags 40000001:
    element type 5 hash E8434EB4 flags 40000001:
        element type 2 hash 9C183BF8 flags 40000001: 8
        element type 2 hash 44F24239 flags 40000001: nd
        element type 2 hash 3BBD6268 flags 40000001: /8
    element type 5 hash 035B0E22 flags 40000001:
    element type 5 hash AFFD0354 flags 40000001:
    element type 5 hash 02DDF58C flags 40000001:
    element type 5 hash 5164D4EA flags 40000001:
        element type 1 hash 05D19F25 flags 40000001:
        element type 1 hash 61D30442 flags 40000001:
        element type 2 hash C1D5FF50 flags 40000003:   0
        element type 2 hash C3383B63 flags 40000003: KM/H
        element type 1 hash F0250DAC flags 40000001:
        element type 2 hash B84589BE flags 40000003: 1
        element type 5 hash C5D551B7 flags 40000001:
            element type 1 hash 6D5FE871 flags 40000001:
            element type 1 hash 19C99185 flags 40000001:
            element type 1 hash 1A33CBB5 flags 40000001:
            element type 1 hash 1A33CBB6 flags 40000001:
            element type 1 hash 6D5ECE44 flags 40000001:
            element type 1 hash 00B78D9E flags 40000001:
            element type 1 hash 19C97422 flags 40000001:
        element type 1 hash 25CF2F74 flags 42800001:
        element type 1 hash 1D9051AA flags 40000001:
        element type 1 hash 11495CD6 flags 40000001:
        element type 1 hash 11461255 flags 40000001:
        element type 1 hash 39E180C4 flags 40000001:
    element type 5 hash 84CCAB08 flags 40000001:
    element type 2 hash A7806DDB flags 40000001: Race Over Message
Goes Here
    element type 1 hash 4E4566E5 flags 40000001:
    element type 5 hash 3345911D flags 40000001:
        element type 1 hash A206A0B4 flags 43800001:
        element type 1 hash 0A729B1B flags 43800001:
        element type 1 hash 99F665F4 flags 40000001:
        element type 1 hash B996A84C flags 40000001:
        element type 1 hash 3B1B624A flags 43800001:
        element type 1 hash 50910822 flags 43800001:
        element type 5 hash ED16ECE6 flags 40000001:
    element type 5 hash 54BE67BC flags 40000001:
        element type 2 hash 05092833 flags 40000001: Time:
        element type 2 hash 30D9802E flags 40000001: 99999999
        element type 1 hash A86C3C9C flags 40000001:
    element type 5 hash 5ED609A1 flags 40000001:
        element type 1 hash FA3D7B71 flags 40000001:
        element type 1 hash FA3D7B72 flags 40000001:
        element type 1 hash FA3D7B73 flags 40000001:
        element type 1 hash FA3D7B74 flags 40000001:
        element type 1 hash 4FDDD64C flags 40000001:
        element type 1 hash 4FDDD64D flags 40000001:
        element type 1 hash 4FDDD64E flags 40000001:
        element type 1 hash 4FDDD64F flags 40000001:
        element type 2 hash 4183E0A1 flags 40000001: 1
        element type 2 hash 4183E0A2 flags 40000001: 2
        element type 2 hash 4183E0A3 flags 40000001: 3
        element type 2 hash 4183E0A4 flags 40000001: 4
        element type 2 hash 2AF89FD7 flags 40000001: wwwwwwwwwww
        element type 2 hash 2AF89FD8 flags 40000001: wwwwwwwwwww
        element type 2 hash 2AF89FD9 flags 40000001: wwwwwwwwwww
        element type 2 hash 2AF89FDA flags 40000001: wwwwwwwwwww
    element type 5 hash 70F90BD4 flags 40000001:
        element type 2 hash 8B7E6EA1 flags 40000001: Laps
        element type 1 hash A86C3C9B flags 40000001:
        element type 2 hash F2685238 flags 40000001: /80
        element type 2 hash 57F0E3A5 flags 40000001: 10
    element type 5 hash 8CA76279 flags C0000001:
        element type 1 hash 238F4B29 flags 40000001:
        element type 1 hash C81A81F5 flags 40000001:
        element type 1 hash ACBAF9EF flags 40000001:
        element type 2 hash 92CDC512 flags 40000001: + 500
        element type 2 hash BCEA10CE flags 40000001: Meters
    element type 5 hash 30D59B88 flags C0000001:
        element type 1 hash 9A4E84A9 flags 40000001:
        element type 2 hash CD3A501F flags 40000001: 00:00:00
        element type 2 hash F7CFC1FC flags 40000001: Forhanosgranska
        element type 2 hash 6BF40D64 flags 40000001: Time:
    element type 1 hash 4B9B8F5A flags 42800001:
    element type 5 hash D292009B flags C0000001:
        element type 1 hash F11AFFD1 flags 40000001:
        element type 1 hash 9C355B23 flags 40000001:
        element type 1 hash F11AFFD2 flags 40000001:
        element type 1 hash 9C355B24 flags 40000001:
        element type 1 hash F11AFFD3 flags 40000001:
        element type 1 hash 9C355B25 flags 40000001:
        element type 1 hash B2D2E745 flags 40000001:
        element type 2 hash 7121F270 flags 40000001: Circuit Stats
        element type 2 hash C8D50791 flags 40000001: Best Lap:
        element type 2 hash C8D50792 flags 40000001: Current G's:
        element type 2 hash C8D50793 flags 40000001: Maximum G's:
        element type 2 hash F3828848 flags 40000001: 00000
        element type 2 hash F3828849 flags 40000001: 00000
        element type 2 hash F382884A flags 40000001: 00000
        element type 5 hash 29F0A455 flags 40000001:
            element type 2 hash 4635CA2C flags 40000001: Time:
            element type 2 hash E1FA0DA7 flags 40000001: 99999999
            element type 1 hash 9C355B26 flags 40000001:
        element type 1 hash B2751131 flags 40000001:
        element type 5 hash 0B470B80 flags 40000001:
            element type 1 hash 968971D0 flags 40000001:
            element type 1 hash 968971D3 flags 40000001:
            element type 1 hash 718D020B flags 40000001:
            element type 1 hash 718D0209 flags 40000001:
            element type 1 hash 718D020A flags 40000001:
            element type 1 hash 968971D2 flags 40000001:
            element type 1 hash 718D020C flags 40000001:
            element type 1 hash 968971D1 flags 40000001:
    element type 5 hash D80C97E1 flags 40000001:
        element type 1 hash 053A5193 flags 40000001:
        element type 1 hash BF9CC87C flags 40000001:
        element type 2 hash 0D94B769 flags 40000001: Filter
    element type 5 hash 06254D8A flags 40000001:
        element type 1 hash EED72BCE flags 40000001:
        element type 2 hash 0FBA78C2 flags 40000001: Searching
Connection
        element type 2 hash 06012887 flags 43800001:
        element type 5 hash E2848973 flags 40000001:
            element type 1 hash 4F649313 flags 43800001:
            element type 1 hash 4F649314 flags 43800001:
    element type 5 hash 3F8644CA flags C0000001:
        element type 2 hash 1930B057 flags 40000003: 0
        element type 2 hash 31B0D166 flags 40000001: Bank:
fng ScreenPrintf.fng:
    element type 2 hash B9D457DE flags 42000000: DEFAULT STRING
    element type 2 hash B9D457DF flags 42000000: DEFAULT STRING
    element type 2 hash B9D457F7 flags 42000000: DEFAULT STRING
    element type 2 hash B9D457F8 flags 42000000: DEFAULT STRING
    element type 2 hash B9D457F9 flags 42000000: DEFAULT STRING
    element type 2 hash B9D457FA flags 42000000: DEFAULT STRING
    element type 2 hash B9D457FB flags 42000000: DEFAULT STRING
    element type 2 hash B9D457FC flags 42000000: DEFAULT STRING
    element type 2 hash B9D457FD flags 42000000: DEFAULT STRING
    element type 2 hash B9D457FE flags 42000000: DEFAULT STRING
    element type 2 hash B9D457FF flags 42000000: DEFAULT STRING
    element type 2 hash B9D45800 flags 42000000: DEFAULT STRING
    element type 2 hash B9D45818 flags 42000000: DEFAULT STRING
    element type 2 hash B9D45819 flags 42000000: DEFAULT STRING
    element type 2 hash B9D4581A flags 42000000: DEFAULT STRING
    element type 2 hash B9D4581B flags 42000000: DEFAULT STRING
    element type 2 hash B9D457B7 flags 42000000: DEFAULT STRING
    element type 2 hash B9D457B8 flags 42000000: DEFAULT STRING
    element type 2 hash B9D457B9 flags 42000000: DEFAULT STRING
    element type 2 hash B9D457BA flags 42000000: DEFAULT STRING
    element type 2 hash B9D457BB flags 42000000: DEFAULT STRING
    element type 2 hash B9D457BC flags 42000000: DEFAULT STRING
    element type 2 hash B9D457BD flags 42000000: DEFAULT STRING
    element type 2 hash B9D457BE flags 42000000: DEFAULT STRING
    element type 2 hash B9D457D6 flags 42000000: DEFAULT STRING
    element type 2 hash B9D457D7 flags 42000000: DEFAULT STRING
    element type 2 hash B9D457D8 flags 42000000: DEFAULT STRING
    element type 2 hash B9D457D9 flags 42000000: DEFAULT STRING
    element type 2 hash B9D457DA flags 42000000: DEFAULT STRING
    element type 2 hash B9D457DB flags 42000000: DEFAULT STRING
    element type 2 hash B9D457DC flags 42000000: DEFAULT STRING
    element type 2 hash B9D457DD flags 42000000: DEFAULT STRING
    element type 2 hash 5334FAE2 flags 42000000: DEFAULT STRING
    element type 2 hash 5334FAE3 flags 42000000: DEFAULT STRING
    element type 2 hash 5334FAE4 flags 42000000: DEFAULT STRING
    element type 2 hash 5334FAE5 flags 42000000: DEFAULT STRING
    element type 2 hash 5334FAE6 flags 42000000: DEFAULT STRING
    element type 2 hash 5334FAE7 flags 42000000: DEFAULT STRING
    element type 2 hash 5334FAE8 flags 42000000: DEFAULT STRING
    element type 2 hash 5334FAE9 flags 42000000: DEFAULT STRING
    element type 2 hash 5334FAEA flags 42000000: DEFAULT STRING
    element type 2 hash 5334FAEB flags 42000000: DEFAULT STRING
    element type 2 hash B9D45773 flags 42000000: DEFAULT STRING
    element type 2 hash B9D45774 flags 42000000: DEFAULT STRING
    element type 2 hash B9D45775 flags 42000000: DEFAULT STRING
    element type 2 hash B9D45776 flags 42000000: DEFAULT STRING
    element type 2 hash B9D45777 flags 42000000: DEFAULT STRING
    element type 2 hash B9D45778 flags 42000000: DEFAULT STRING
    element type 2 hash B9D45779 flags 42000000: DEFAULT STRING
    element type 2 hash B9D4577A flags 42000000: DEFAULT STRING
    element type 2 hash B9D4577B flags 42000000: DEFAULT STRING
    element type 2 hash B9D4577C flags 42000000: DEFAULT STRING
    element type 2 hash B9D45794 flags 42000000: DEFAULT STRING
    element type 2 hash B9D45795 flags 42000000: DEFAULT STRING
    element type 2 hash B9D45796 flags 42000000: DEFAULT STRING
    element type 2 hash B9D45797 flags 42000000: DEFAULT STRING
    element type 2 hash B9D45798 flags 42000000: DEFAULT STRING
    element type 2 hash B9D45799 flags 42000000: DEFAULT STRING
    element type 2 hash B9D4579A flags 42000000: DEFAULT STRING
    element type 2 hash B9D4579B flags 42000000: DEFAULT STRING
    element type 2 hash B9D4579C flags 42000000: DEFAULT STRING
    element type 2 hash B9D4579D flags 42000000: DEFAULT STRING
    element type 2 hash B9D457B5 flags 42000000: DEFAULT STRING
    element type 2 hash B9D457B6 flags 42000000: DEFAULT STRING

At the end we can see all the elements of the ScreenPrintf.fng package. Seems like it just has a bunch of labels. But this is a nice confirmation that my previous code to load it, did actually work. From the other elements, I guessed that flag 2 means it should be visible, so I tried adding that.

default string
Cool.

Then the text can be changed easily by one of the many funcs, for example:

SetUILabelByHashFormattedString("ScreenPrintf.fng", 0xB9D457DE, "0xB9D457DE", "");

custom strings
Nice.

A quick search of all the label hashes did not give any result, so it seems like there is no code left in the program where it would set any of the labels' text.

Index

UI

PC help bar #

This is the button bar at the bottom of the screen, with the player name and money text. It also has some elements that are only shown when you're connected online (TODO).

Buttons are changed by using PCHelpBarFNGObject::SyncByMask. Buttons are predetermined, you can only specify which buttons you want and not set an arbitrary text label. The positions are shown below. See PCHelpBarFNGObject::SyncByMask and enum PCHELPBARFLAGS for the button masks. The lowest matching mask gets placed on button1, and so on.

two line button bar with 6 buttons
Normal bar

wide button bar with 3 buttons
LAYOUT_ONE_LINE_NO_BG

wide button bar with 3 buttons, slighty up
LAYOUT_ONE_LINE_NO_BG | LAYOUT_WORLDMAP_POSITION

wide button bar with 3 buttons, up
LAYOUT_ONE_LINE_NO_BG | LAYOUT_PAUSEMENU_POSITION

right aligned button bar with 2 buttons
LAYOUT_ONE_LINE_NO_BG | LAYOUT_RIGHT_POSITION

Symbols:

Index

UI (continuation)

UI keys seem to be hashed with the case insensitive hash.

Symbols:

Index

Input #

Input

Mouse input #

While the canvas is 640x480, mouse position seems to be handled mostly as ([-320,+320],[-240,+240]).

Symbols:

Index

Input

Key (Joy) input #

List of joy ids in enum JOYID comes from the very helpful strings in joyKeyData.

Patching in the B5 JOY_EVENT_AUTO_PILOT id (by for example replacing the world-map key in career to this key), makes it so the player's car starts driving like an AI car. Example in nfsu2-re-hooks/switch-joyids-in-career.c.

Symbols:

Index

Cheats #

Cheats can only be inserted at the 'press enter key' screen at boot. (I remember this from looking up cheats in my childhood), but toggling sponsor car cheats when ingame has immediate effect.

The career money cheat is mildly interesting, as it seems to work in two different ways. It adds the amount to a variable that is used when a new career is started and it adds the amount to the current career data if the loaded profile didn't start career yet.

/*from CheatScreenData::DoCareerCheat*/
/*ordermebaby ($1000 career start bonus)*/
cheatExtraCareerMoney1000 = 1000;
// ^ used when starting a new career from a profile that
// was loaded from the profile menu
if (!profileData.career.isCareerStarted)  {
	if (CareerBank::GetMoney() == 0)  {
		CareerBank::AddMoney(1000);
		// ^ This only has an effect when the profile was
		// loaded from the loading screen
	}
}

Cheats

Cheat configuration data #

Only 4 of the 20 cheats have their data already filled in, for some reason. It looks like this at boot:

struct CheatScreenData cheatScreenData; // partially initialized by CheatScreenData::InitCheats?
struct CheatData cheatData[20] = {
	{ "needperformance1",	CHEAT_TYPE_PERFORMANCE, 1, 0, 0, 0, 1, 1, 0 }, // 0
	{ "needperformance2",	CHEAT_TYPE_PERFORMANCE, 2, 0, 0, 0, 1, 1, 0 }, // 1
	{ "gimmevisual1",	CHEAT_TYPE_VISUALS, 2, 0, 0, 0, 1, 1, 0 }, // 2
	{ "gimmevisual2"	CHEAT_TYPE_VISUALS, 2, 0, 0, 0, 1, 1, 0 }, // 3
	{ "gimmechingy"		CHEAT_TYPE_SPONSOR_CAR, 0, 0, 0, 0, 0, 0, 0 }, // 4
	{ ""			0, 0, 0, 0, 0, 0, 0, 0 }, // 5
	{ ""			0, 0, 0, 0, 0, 0, 0, 0 }, // 6
	{ ""			0, 0, 0, 0, 0, 0, 0, 0 }, // 7
	{ ""			0, 0, 0, 0, 0, 0, 0, 0 }, // 8
	{ ""			0, 0, 0, 0, 0, 0, 0, 0 }, // 9
	{ ""			0, 0, 0, 0, 0, 0, 0, 0 }, // 10
	{ ""			0, 0, 0, 0, 0, 0, 0, 0 }, // 11
	{ ""			0, 0, 0, 0, 0, 0, 0, 0 }, // 12
	{ ""			0, 0, 0, 0, 0, 0, 0, 0 }, // 13
	{ ""			0, 0, 0, 0, 0, 0, 0, 0 }, // 14
	{ ""			0, 0, 0, 0, 0, 0, 0, 0 }, // 15
	{ ""			0, 0, 0, 0, 0, 0, 0, 0 }, // 16
	{ ""			0, 0, 0, 0, 0, 0, 0, 0 }, // 17
	{ ""			0, 0, 0, 0, 0, 0, 0, 0 }, // 18
	{ ""			0, 0, 0, 0, 0, 0, 0, 0 }, // 19
};

Then during boot, CheatScreenData::InitCheats? gets called, which fills in all the rest. Hashes are done with case sensitive hash

struct CheatData cheatData[20] = {
	{ "needperformance1",	CHEAT_TYPE_PERFORMANCE, 0x00000001, 0, 0, 0, 1, 1, 0 }, //
	{ "needperformance2",	CHEAT_TYPE_PERFORMANCE, 0x00000002, 0, 0, 0, 1, 1, 0 }, //
	{ "gimmevisual1",	CHEAT_TYPE_VISUALS, 0x00000002, 0, 0, 0, 1, 1, 0 }, //
	{ "gimmevisual2"	CHEAT_TYPE_VISUALS, 0x00000002, 0, 0, 0, 1, 1, 0 }, //
	{ "gimmechingy"		CHEAT_TYPE_SPONSOR_CAR, 0x3EE7D094, 0, 0, 0, 1, 1, 0 }, // "SPONSOR_CHINGY"
	{ "wannacapone"		CHEAT_TYPE_SPONSOR_CAR, 0x3E6D00C8, 0, 0, 0, 1, 1, 0 }, // "SPONSOR_CAPONE"
	{ "wantmyd3"		CHEAT_TYPE_SPONSOR_CAR, 0x54E32D29, 0, 0, 0, 1, 1, 0 }, // "SPONSOR_D3"
	{ "shinestreetbright"	CHEAT_TYPE_SPONSOR_CAR, 0xE57B6400, 0, 0, 0, 1, 1, 0 }, // "SPONSOR_SHINESTREET"
	{ "davidchoeart"	CHEAT_TYPE_SPONSOR_CAR, 0x7760C479, 0, 0, 0, 1, 1, 0 }, // "SPONSOR_DAVIDCHOE"
	{ "tunejapantuning"	CHEAT_TYPE_SPONSOR_CAR, 0x002F98F1, 0, 0, 0, 1, 1, 0 }, // "SPONSOR_JAPANTUNING"
	{ "yodogg"		CHEAT_TYPE_SPONSOR_CAR, 0x007090A1, 0, 0, 0, 1, 1, 0 }, // "SPONSOR_SNOOP_DOGG"
	{ "opendoors"		CHEAT_TYPE_SPONSOR_CAR, 0x4BAECE79, 0, 0, 0, 1, 1, 0 }, // "SPONSOR_THE_DOORS"
	{ "needmybestbuy"	CHEAT_TYPE_SPONSOR_VINYL, 0x9E52EAC1, 0, 0, 0, 1, 1, 0 }, // "AD_BESTBUY"
	{ "goforoldspice"	CHEAT_TYPE_SPONSOR_VINYL, 0x5FD7B956, 0, 0, 0, 1, 1, 0 }, // "AD_OLDSPICE"
	{ "gottaedge"		CHEAT_TYPE_SPONSOR_VINYL, 0x44C47B98, 0, 0, 0, 1, 1, 0 }, // "AD_EDGE"
	{ "gottahavebk"		CHEAT_TYPE_SPONSOR_VINYL, 0xCF5F6873, 0, 0, 0, 1, 1, 0 }, // "AD_BURGERKING"
	{ "gotmycingular"	CHEAT_TYPE_SPONSOR_VINYL, 0x7C504338, 0, 0, 0, 1, 1, 0 }, // "AD_CINGULAR"
	{ "regmybank"		CHEAT_TYPE_CAREER, 0x00000002, 0, 0, 0, 1, 1, 0 }, //
	{ "regmebaby"		CHEAT_TYPE_CAREER, 0x00000003, 0, 0, 0, 1, 1, 0 }, //
	{ "ordermebaby"		CHEAT_TYPE_CAREER, 0x00000004, 0, 0, 0, 1, 1, 0 }, //
};

And part of that init function is disabling the AD_BESTBUY cheat (which is a sponsor vinyl) if the game region is not US:

void __thiscall CheatScreenData::InitCheats?(struct CheatScreenData *this)
{
	int i;

	// much more init code here

	// TODO reimplement this instead of putting this code in docs...
	this->vtable = (void*) unnamed_7A0604; // TODO!!
	this->doCheatCheck = 0;
	this->cheats = cheatData;
	this->numCheats = 20;
	this->typedCheatLength = 0;
	this->field_38 = 0;

	for (i = 0; i < 20; i++) {
		if (this->cheats[i].cheatType == CHEAT_TYPE_VINYL &&
			this->cheats[i].cheatData == hashCS43DB50("AD_BESTBUY") &&
			_gameRegion != 0 /*REGION_US*/)
		{
			this->cheats[i].cheatEnabled = 0;
		}
	}
}

Index

Cheats

Unused cheats or dev cheats #

These are things that look like cheat functionality, but is not present in the game's cheats and don't seem accessible. Possibly dev functionality?

Cheats > Unused cheats or dev cheats

Set career bank to 1 million #

Putting setCareerBankToOneMillion to non-zero will set your career bank to $1M. It will happen on the next game loop (render?) and the variable will autoreset back to 0.

Index

Cheats > Unused cheats or dev cheats

Reveal undiscovered hidden shops on the map #

Putting showAllHiddenShops to non-zero will show all shops on the map, even undiscovered hidden shops. Putting it back to zero will reset this behavior and all undiscovered hidden shops will not be shown anymore.

Index

Cheats (continuation)

Symbols:

Index

Car model data #

When jumping around XREFs I stumbled upon a function that I named GetLogoHashForCarModel. It uses a lot of strings that are manufacturers and stuff like CARSELECT_MANUFACTURER_%s. There are two parameters, the first seems to be the index of the car and the second is if the caller wants the manufacturer logo or the brand logo. The function gave some nice information:

After hooking and printing addresses, some more obvious data could be seen:

This became struct Car.

Looping through all the data and printing their names:

car #: name0 name1 manufacturerName
car 0: PEUGOT PEUGOT PEUGEOT
car 1: FOCUS FOCUS FORD
car 2: COROLLA COROLLA TOYOTA
car 3: 240SX 240SX NISSAN
car 4: MIATA MIATA MAZDA
car 5: CIVIC CIVIC HONDA
car 6: PEUGOT106 PEUGOT106 PEUGEOT
car 7: CORSA CORSA VAUXHALL
car 8: HUMMER HUMMER HUMMER
car 9: NAVIGATOR NAVIGATOR LINCOLN
car 10: ESCALADE ESCALADE CADILLAC
car 11: TIBURON TIBURON HYUNDAI
car 12: SENTRA SENTRA NISSAN
car 13: CELICA CELICA TOYOTA
car 14: IS300 IS300 LEXUS
car 15: SUPRA SUPRA TOYOTA
car 16: GOLF GOLF VOLKSWAGEN
car 17: A3 A3 AUDI
car 18: RSX RSX ACURA
car 19: ECLIPSE ECLIPSE MITSUBISHI
car 20: TT TT AUDI
car 21: RX8 RX8 MAZDA
car 22: 350Z 350Z NISSAN
car 23: G35 G35 INFINITI
car 24: 3000GT 3000GT MITSUBISHI
car 25: GTO GTO PONTIAC
car 26: MUSTANGGT MUSTANGGT FORD
car 27: SKYLINE SKYLINE NISSAN
car 28: LANCEREVO8 LANCEREVO8 MITSUBISHI
car 29: RX7 RX7 MAZDA
car 30: IMPREZAWRX IMPREZAWRX SUBARU
car 31: TAXI TAXI GENERIC
car 32: 4DR_SEDAN02 4DR_SEDAN02 GENERIC
car 33: AMBULANCE AMBULANCE GENERIC
car 34: TAXI02 TAXI02 GENERIC
car 35: PANELVAN PANELVAN GENERIC
car 36: COUPE COUPE GENERIC
car 37: FIRETRUCK FIRETRUCK GENERIC
car 38: PARCELVAN PARCELVAN GROLSON
car 39: PICKUP PICKUP Chevrolet
car 40: 4DR_SEDAN 4DR_SEDAN Chevrolet
car 41: BUS BUS Silver Eagle
car 42: SUV SUV GENERIC
car 43: MINIVAN MINIVAN GENERIC
car 44: HATCHBACK HATCHBACK GENERIC
car 45: HATCHBACK02 HATCHBACK02 GENERIC

Symbols:

Index

Sound #

Symbols:

Index

Career #

loadIntoCareerImmediately? is a weird variable. It changes the boot process so when UI_Main.fng is loaded, the game immediately goes in career freeroam after loading a profile. Except, you're in Rachel's car. When entering a shop, you're back in your own car, but when exiting the GPS is set to the crib. When arriving at the crib though, you can't enter it. However, you can enter the crib if that first shop you entered was the car lot.

Doing this on a finished career profile will put you in Rachel's car at the airport parking, just like when starting a new career. The end of game sms will be open. When driving, the phonecalls from the start of the game come in (friends telling Rachel where the races are going on), but they don't appear on the map.

My best guess is that this 'load into career' option is a cut/unfinished feature.

Career

Unlocks #

Cars are unlocked by finishing specific races. For some cars this is different for US or non-US game versions.

The data that list the required race per car is in bin section 34A1F, which is in bin section 80034A10, which is in the file GLOBAL/GLOBALB.BUN. Entries in there are of type struct CarUnlockEntry.

Data of 34A1F in my game files:

carNameHashunlockingRaceIfGameRegionIsUSunlockingRaceIfGameRegionIsNotUs
2D7C9B5D (=3000GT) AB7A688BAB7A688B
000AC6D1 (=350Z) AB7A6889AB7A6889
00000453 (=A3) 5E802BCD5E802BCD
545AEC60 (=CELICA) 1185EF085E802BCA
7502DA84 (=ECLIPSE) 5E802BCE5E802BCE
CF704771 (=ESCALADE) 1185EF061185EF06
0000A86E (=G35) AB7A688AAB7A688A
00163087 (=GOLF) 5E802BCC5E802BCD
0000ACC9 (=GTO) AB7A688CAB7A688C
6126DD4D (=HUMMER) 1185EF071185EF07
CF4458F8 (=IMPREZAWRX)F874A54AF874A54A
030236EE (=IS300) 5E802BCA5E802BCB
09D2EFF6 (=LANCEREVO8)AB7A688EAB7A688E
03455D6B (=MIATA) 000000001185EF07
35165819 (=MUSTANGGT) AB7A688DAB7A688D
0219C94A (=NAVIGATOR) 1185EF061185EF06
0000DB7C (=RSX) 5E802BCD00000000
0000DC00 (=RX7) AB7A6889AB7A6889
0000DC01 (=RX8) AB7A6888AB7A6888
79AEBFEC (=SENTRA) 1185EF081185EF08
BE48375E (=SKYLINE) F874A549F874A549
03B8C48A (=SUPRA) 5E802BCB5E802BCC
04FD0522 (=TIBURON) 1185EF071185EF08
000006E7 (=TT) 5E802BCE5E802BCE

Interesting that they are sorted alphabetically based on the underlying value that was hashed.

Symbols:

Index

Career

Markers #

The money pickup/info pickup(engage sms messages)/race/shop/garage/neighbourhood/and more? markers. They are in bin section 0x3414A, which is in bin section 0x80034147, in TRACKS\ROUTESL4R%c\Paths%d.bin. Bin section 3414A is loaded as part of PathsData::ReadFromBinData and inited in PathsData::InitAfterReadFromBinData.

After reading, they are in pathsData.allMarkers, which has pathsData.numMarkers entries. They are also in pathsData.markers, which is an array. Each entry points to an array of markers with their type matching the index of the array, except the last entry. The last entry at index 21 points to where the end of the array of index 20 is, it does not point to an array of markers itself.

This data is only loaded while in career/quickrace/freeroam.

Not loading bin section 0x3414A results in no career markers at all, and no traffic (but other racers do still spawn).

See markerdump.txt (39KB) for a data dump as made by nfsu2-re-hooks/debug-custom-dump-markers.c.

Symbols:

Index

Career

SMS stuff #

Dump of all sms message data: smsdump.txt (63KB). This data is in bin section 0x34A17 (which is inside bin section 0x80034A10, which is in file GLOBAL/GLOBALB.BUN).

Symbols:

Index

Career (continuation)

Symbols:

Index

LAN/OL #

Symbols:

Index

D3D9 Stuff #

Symbols:

Index

Other symbols #

.rdata:0079B9F0 aDDecodercaptur db 'd:\decodercapture%04d.fss',0

.rdata:007A1344 a__IndepSrcOnli db '..\indep\src\online/LobbyCore.cpp',0

.rdata:007A5BF8 aHotpositionSHo db '//',0Ah
.rdata:007A5BF8                 db '// HotPosition - %s',0Ah
.rdata:007A5BF8                 db '//',0Ah
.rdata:007A5BF8                 db 'HOTPOSITION: %8.2f,%8.2f,%8.2f  Angle=0x%04x',0Ah,0

.rdata:00787568 aSeeulator      db 'Seeulator',0
.rdata:00787574 aJr2server      db 'JR2Server',0

Symbols:

Index