Display settings get saved in the windows registry. See Registry.
TODO. I was writing this when I got distracted by the language stuff.
Symbols:
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:
Variable | Key |
---|---|
_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:
List by GetCountryCode:
List by OpenPatchesWebsite:
Then some network region stuff at networkRegionStuff showed a 14th entry:
Symbols:
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:
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
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:
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:
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:
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
.
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).
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.
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
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.
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:
VK_LEFT
: -1
VK_RIGHT
: 1
VK_HOME
: -consoleTextStringMaxLength
VK_END
: consoleTextStringMaxLength
This is the only other function that checks if consoleEnabledFlag is non-zero.
Console leftovers (continuation)
Symbols:
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.
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:
Hop hop skippity hop
*
symbol
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.
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.
Hash functions (continuation)
Symbols:
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; }
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.
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.
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?).
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
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
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.
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:
HelpDialog_SMALL.fng
GenericDialog_MED.fng
GenericDialog_ThreeButton.fng
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:
:)
Seems like this is a nice place to test loading other screens. Some do crash, but
here's UI_OLEAMessenger.fng
:
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.
struct DialogInfo was made by looking at all of the above and more. (Especially DialogInfo::ctor helped here.)
Symbols:
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 }, };
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:
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...):
This was done by putting pointers to their strings into the boot flow entries (see Speedy boot).
2P_PressStart.fng
loading_boot.fng
HUD_World_Loading.fng
Loading_Controller.fng
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.
UI > Some FNG screens (continuation)
UI_DebugTest.fng
UI_OLAgeTooYoung.fng
UI_OLMAIN.fng
UI_OL_Feedback.fng
UI_OL_FriendDialogue.fng
UI_OL_Voice_Chat.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.
Cool.
Then the text can be changed easily by one of the many funcs, for example:
SetUILabelByHashFormattedString("ScreenPrintf.fng", 0xB9D457DE, "0xB9D457DE", "");
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.
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.
Normal bar
LAYOUT_ONE_LINE_NO_BG
LAYOUT_ONE_LINE_NO_BG | LAYOUT_WORLDMAP_POSITION
LAYOUT_ONE_LINE_NO_BG | LAYOUT_PAUSEMENU_POSITION
LAYOUT_ONE_LINE_NO_BG | LAYOUT_RIGHT_POSITION
Symbols:
UI (continuation)
UI keys seem to be hashed with the case insensitive hash.
Symbols:
While the canvas is 640x480
, mouse position seems to be handled mostly
as ([-320,+320],[-240,+240])
.
Symbols:
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:
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 } }
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; } } }
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
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.
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.
Cheats (continuation)
Symbols:
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:
0x890
+0x0
, there is a brand name
+0xC0
, there is a manufacturer name
After hooking and printing addresses, some more obvious data could be seen:
+0x20
is a the brand name again
+0x40
is a geometry path (example CARS\A3\GEOMETRY.BIN
)
+0x60
is a geometry path (example CARS\A3\GEOMETRY.LZC
)
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:
Symbols:
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.
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:
carNameHash | unlockingRaceIfGameRegionIsUS | unlockingRaceIfGameRegionIsNotUs |
---|---|---|
2D7C9B5D (=3000GT) | AB7A688B | AB7A688B |
000AC6D1 (=350Z) | AB7A6889 | AB7A6889 |
00000453 (=A3) | 5E802BCD | 5E802BCD |
545AEC60 (=CELICA) | 1185EF08 | 5E802BCA |
7502DA84 (=ECLIPSE) | 5E802BCE | 5E802BCE |
CF704771 (=ESCALADE) | 1185EF06 | 1185EF06 |
0000A86E (=G35) | AB7A688A | AB7A688A |
00163087 (=GOLF) | 5E802BCC | 5E802BCD |
0000ACC9 (=GTO) | AB7A688C | AB7A688C |
6126DD4D (=HUMMER) | 1185EF07 | 1185EF07 |
CF4458F8 (=IMPREZAWRX) | F874A54A | F874A54A |
030236EE (=IS300) | 5E802BCA | 5E802BCB |
09D2EFF6 (=LANCEREVO8) | AB7A688E | AB7A688E |
03455D6B (=MIATA) | 00000000 | 1185EF07 |
35165819 (=MUSTANGGT) | AB7A688D | AB7A688D |
0219C94A (=NAVIGATOR) | 1185EF06 | 1185EF06 |
0000DB7C (=RSX) | 5E802BCD | 00000000 |
0000DC00 (=RX7) | AB7A6889 | AB7A6889 |
0000DC01 (=RX8) | AB7A6888 | AB7A6888 |
79AEBFEC (=SENTRA) | 1185EF08 | 1185EF08 |
BE48375E (=SKYLINE) | F874A549 | F874A549 |
03B8C48A (=SUPRA) | 5E802BCB | 5E802BCC |
04FD0522 (=TIBURON) | 1185EF07 | 1185EF08 |
000006E7 (=TT) | 5E802BCE | 5E802BCE |
Interesting that they are sorted alphabetically based on the underlying value that was hashed.
Symbols:
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:
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:
Career (continuation)
Symbols:
Symbols:
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: