Pre-Alpha hype thread

Discussion in 'General Discussion' started by RHY3756547, Mar 27, 2014.

  1. Fatbag

    Fatbag Member

    Since I was not able to locate the code that updates +0xfc, I wrote the code myself. I've verified that the simulator runs at mostly the correct speed. It simulates 100 game minutes in 600 real world seconds, which is close enough to the target of 120 (1 game minute per 5 real world seconds). This is probably because the game defaults sim_speed to 0x15 (21), and if you multiply that by 120/100, you get 25.2, so sim_speed is probably *supposed* to be 25 (which is a "rounder" number than 21), but the developers probably temporarily changed it to 21 for defaulthouse.dat. I'll come up with a patch to share soon.

    Next on the list is getting the action queue to work. It turns out that if you spawn NPCs on the lot, you will control the first NPC that you put on the lot; if you click around, you'll get that NPC's head in the pie menu, and if you click on the NPC itself, you get "This is the active character". (Clicking on other NPCs shows you the regular pie menu actions for those NPCs.) Clicking pie menu commands does not do anything yet.

    If you pass "-playerid 0" to Play Test, you'll get the default sim named "Guy" on the lot, with a green plumb-bob above him. (I'm not sure if you can control Guy if you apply the simulator hack to Play Test.) You'll notice that Play Test comes with "netuser00000.iff" in the userdata/characters folder, but Pre-Alpha doesn't come with it. Copying and pasting this file into Pre-Alpha's characters folder and running with -playerid 0 doesn't cause Guy or any other sim to show up, so it's possible that the format of netuser00000.iff changed.

    What's interesting is that, though I can't seem to get the AddPlayer cheat to work (you just get the error "You cannot create a cheat avatar copied from a previous clone. Use the original."), if you keep netuser00000.iff in your characters folder, and you replace the conditional jump just above the reference to the error message string with an unconditional jump, and then run "AddPlayer" and skip over the access violations that result, then the game will put a plumb-bob over the active character and also create a new file, netuser00001.iff. If you rename this new file to netuser00000.iff, and you run KillPlayer, then the game doesn't crash the next time you run the AddPlayer cheat.

    If you restart the game though, and your lot doesn't load and you have to delete nonetworkhouse.dat, then when the game creates a new nonetworkhouse.dat, it also deletes all files that exist with the name "netuser%d.iff", even if you pass -avatarid 0, and it still does not load any characters.

    The main goal though is just getting the action queue to work.
    Blayer98 and Afr0 like this.
  2. Fatbag

    Fatbag Member

    Well getting the action queue to work is a tough subject, so for now, here is the patch to enable the simulator. :D

    Apply to Pre-Alpha's TSOSimulatorClientD.dll using beat (hosted here, and I'm also hosting my own copy here).

    Then run Pre-Alpha's TSOClient.exe with the -NoNetwork argument (-debug_objects is also recommended). Click OK to the "Some objects in the game could not be loaded" error. We probably get this error simply because nonetworkhouse.dat does not exist by default and needs to be created. Run the "fullopt" cheat (using ctrl+shift+C), and run the cheat "sim_speed 25" to play at normal speed. Run the "edith" cheat to open Edith, and use Window -> New Object Browser to add things to the lot. Use Sims -> Show Module Inspector to view running objects on the lot.

    If the game starts crashing whenever you try to run it, delete nonetworkhouse.dat in the UserData/Houses folder. Note also that UserData/tso.syslog can grow to a large size, so you can also delete this from time to time.

    Also, the simulator did stop working for me after fooling around for a while with Edith. If that ever happens to you, setting dword [cTSOSimulator+0xfc] to dword [cTSOSimulator+0xcc] fixes the issue. (cTSOSimulator is allocated dynamically, but you can catch its value by putting a breakpoint on cTSOSimulator::ClientIdle() at base+0x1ecfc in TSOSimulatorClientD.dll and looking at ecx.)

    I'll describe each modification that I made.

    I expanded the size of cTSOSimulator by 0x18 bytes, by changing the instruction at base+0x6a0a from "push 0x130" to "push 0x148". The 0x18 bytes hold three qwords: something I call time_frequency (which is set during program initialization to the result of QueryPerformanceFrequency(), which Microsoft says is guaranteed not to change until the computer restarts), time_counter (which is set to the result of QueryPerformanceCounter() during each call to ClientIdle()), and fractional_time (which holds the remaining "fraction of a tick" that needs to be added to time_counter in the next call of ClientIdle()).

    I hooked the cTSOSimulator constructor to initialize these three dwords; time_frequency is initialized by QueryPerformanceFrequency(), and time_counter and fractional_time are set to 0. At base+0x1cd02, "call base+0x96623" was changed to "call base+0xbc331". I overwrote the 00s at base+0xbc331 with this function:

    lea edi, [esi+0x130]
    push edi
    call dword [eax+0xa032d] ; base+0x1ccef+0xa032d = base+0xbd01c -> QueryPerformanceFrequency IAT entry
    xor ebx, ebx
    mov dword [edi+0x08], ebx
    mov dword [edi+0x0c], ebx
    mov dword [edi+0x10], ebx
    mov dword [edi+0x14], ebx
    mov ecx, esi
    jmp base+0x96623
    Also, in the function where the house gets loaded and the value of the tick counter (cTSOSimulator+0xcc) gets read from the house file (defaulthouse.dat or nonetworkhouse.dat), I placed a hook to also store this value in the "simulate until" value (cTSOSimulator+0xfc). This was done by changing the instruction at base+0x1d95b from "call base+0xa0055" to "call base+0xbc353". I overwrote the 00s at base+0xbc353 with this function:

    mov dword [edi+0xbc], ebx
    jmp base+0xa0055
    Lastly, I patched ClientIdle() to call my new "AdvanceSimulator()" routine (as I'm calling it). The instruction at base+0x1ed0e was changed from "mov eax, dword [esi+0xcc]" to "call base+0xbc35e; nop". (AdvanceSimulator() returns dword [esi+0xcc] in eax, but is not stdcall compliant.) I overwrote the bytes at base+0xbc35e with this (quite nicely optimized) function:

      sub esp, 0x08
      add esi, 0x130
      push esp
      call dword [eax+0x9e31a] ; base+0x1ed06+0x9e31a = base+0xbd020 -> QueryPerformanceCounter IAT entry
      pop edi
      pop eax
      ; read time_counter and then store current_time into time_counter
      mov edx, dword [esi+0x08] ; time_counter
      mov ecx, dword [esi+0x0c] ; time_counter+4
      mov dword [esi+0x08], edi ; time_counter
      mov dword [esi+0x0c], eax ; time_counter+4
      ; perform the 64-bit subtraction current_time - (previous value of time_counter, before writing the new one)
      sub edi, edx
      sbb eax, ecx
      ; if the time difference from the last simulation is greater than time_frequency (1 second), or if the is_paused byte is set, do not count this as simulation time
      mov edx, eax
      xor ecx, ecx
      movsx ebx, byte [esi-0x3c] ; +0xf4: cTSOSimulator::is_paused byte
      cmp edi, dword [esi] ; time_frequency
      sbb edx, dword [esi+0x04] ; time_frequency+4
      sbb ebx, ecx
      mov ebx, dword [esi-0x64] ; cTSOSimulator+0xcc
      jns return
      ; multiply the time difference by cTSOSimulator::speed (25 decimal for normal speed)
      mov ecx, dword [esi-0xc8] ; cTSOSimulator+0x68: speed
      mul ecx
      xchg eax, edi
      mul ecx
      add edx, edi
      ; add the fractional tick left over from the last simulation
      mov ecx, dword [esi-0x34] ; cTSOSimulator+0xfc
      add eax, dword [esi+0x10] ; fractional_time
      adc edx, dword [esi+0x14] ; fractional_time+4
      ; back up the existing x87 FPU control word, and then change it to set the rounding mode to "truncate" and disable floating point exceptions
      push ecx
      fstcw word [esp]
      push 0x07ff
      fldcw word [esp]
      ; perform the calculation:
      ; add_ticks = speed_multiplied_time_counter_difference_plus_fractional_time / time_frequency
      ; fractional_time = time_frequency - a*time_frequency
      push edx
      push eax
      fild qword [esi] ; time_frequency
      fild qword [esp]
      fld st0
      fdiv st0, st2
      fist dword [esp]
      fmul st0, st2
      fsubp st1, st0
      fistp qword [esi+0x10] ; fractional_time
      fstp st0 ; align the FPU stack
      ; restore the original control word
      fldcw word [esp+0x0c]
      ; the rest of the code uses sbb tricks to avoid branch instructions
      pop eax
      add ecx, eax ; Take cTSOSimulator+0xfc, and add only the low 4 bytes of add_ticks
      sbb eax, eax ; set eax to all FFs if this would overflow (cTSOSimulator+0xfc would go past 0xFFFFFFFF), all 00s otherwise
      lea edx, [ecx+1] ; temp = add_ticks + 1
      add esp, 0x0c ; deallocate everything that remains on the stack
      and eax, edx ; set eax to add_ticks+1 if cTSOSimulator+0xfc would overflow, 0 otherwise
      sub ecx, eax ; subtract cTSOSimulator+0xfc by this amount (set it to 0 on overflow, do nothing otherwise)
      sub ebx, eax ; subtract cTSOSimulator+0xcc by this amount
      sbb eax, eax ; see if *this* would overflow
      not eax ; set eax to all 00s on overflow, all FFs otherwise
      and ebx, eax ; set cTSOSimulator+0xcc to all 00s if it would overflow
      mov eax, ecx ; temp = cTSOSimulator+0xfc
      add eax, 0x3c ; ClientIdle() always advances this field by 0x3c, so, see if temp+0x3c would overflow
      sbb eax, eax ; set eax to all FFs on overflow, all 00s otherwise
      and eax, ebx ; set eax to cTSOSimulator+0xcc on overflow, 0 otherwise
      sub ebx, eax ; subtract cTSOSimulator+0xcc by this amount (set it to 0)
      sub ecx, eax ; subtract cTSOSimulator+0xfc by this amount
      sbb eax, eax ; see if *this* would overflow
      not eax ; set eax to all 00s on overflow, all FFs otherwise
      and ecx, eax ; and cTSOSimulator+0xfc by this amount (set it to 0 on overflow, do nothing otherwise)
      mov dword [esi-0x34], ecx ; store cTSOSimulator+0xfc
      mov dword [esi-0x64], ebx ; store cTSOSimulator+0xcc
      mov eax, ebx ; return cTSOSimulator+0xcc
      sub esi, 0x130 ; readjust esi
    Note that I'm using x87 FPU instead of SSE since there's no efficient way to convert a 64-bit integer to a floating point value with SSE on x86-32, and I'm using floating point rather than fixed point for the division step because there is no "divide 64-bit integer by 64-bit integer" instruction anywhere on x86-32; you have to perform an algorithm in software, and this is at probably least as slow as converting to floating point and back in hardware.
    Last edited: Apr 13, 2014
    yoyo, cnnoi33, Blayer98 and 3 others like this.
  3. RHY3756547

    RHY3756547 FreeSO Developer Staff Member Moderator

    Incredibly impressive, amazing work! I was surprised when attempting some call trees how fragile maxis's implementation of simantics is - It's incredibly easy to cause an exception if you call trees meant for avatars using a null caller (the module inspector). Our vm is as fragile (if not more, since it crashes on exceptions so i can debug them...) but I thought that was because we haven't implemented many error checks.
    Blayer98 and aidancheddar like this.
  4. Nahuel3d

    Nahuel3d Active Member

    Yep, i was surprised :p
  5. Fatbag

    Fatbag Member

    At the request of Rhys, I gave a shot at enabling the simulator in Play Test. I got the simulator working in Play Test at blazing fast speed now, so now I just need to port my modifications over. The default character on the lot "Guy" can't be controlled even with the simulator working, but he animates.

    It's interesting how unlike Pre-Alpha, even though Play Test comes with defaulthouse.dat, house.dat, and house10.iff, it does not even touch any of those files. The default house appears to be hardcoded into the game. default_house.xml was not introduced until the first boxed version of TSO.
    Last edited: Apr 23, 2014
    Blayer98 likes this.
  6. RHY3756547

    RHY3756547 FreeSO Developer Staff Member Moderator

    Have you tried running the Play Test version of Person A Possess in place of an idle anim to see for sure if they broke the animation instead of changed the format?
  7. Fatbag

    Fatbag Member

    I've made the patch for Play Test:

    You will have to also change the byte in TSOServiceClientD.dll at offset 0xbf25 from "15" to "19" to play at normal speed. I didn't bother making a bps patch for that.

    The changes from Pre-Alpha to Play Test are summarized as follows:
    cTSOSimulator constructor: TSOSimulatorClientD_base+0x1cce5 -> TSOSimulatorClientD_base+0x54df2
    cTSOSimulator::ClientIdle() function: cTSOSimulatorClient_base+0x1ecfc -> TSOSimulatorClientD_base+0x5699d
    Game speed: esi+0x68 -> esi+0x58 (decreased by 0x10)
    Current tick value: esi+0xcc -> esi+0xbc (decreased by 0x10)
    Is paused byte: esi+0xf4 -> esi+0xe8 (decreased by 0x0c)
    "Simulate until tick" value: esi+0xfc -> esi+0xf0 (decreased by 0x0c)

    The results of the a2o-soc-death-possessed-sima.anim animation are:
    Play Test animation played in Play Test: Glitched legs, with vox_death_possessor sound
    Pre-Alpha animation played in Play Test: Normal legs, no sound
    Play Test animation played in Pre-Alpha: Glitched legs, no sound
    Pre-Alpha animation played in Pre-Alpha: Normal legs, no sound

    Interestingly, in Play Test, the animation also shows up in the avatar panel.
    RHY3756547 and aidancheddar like this.
  8. RHY3756547

    RHY3756547 FreeSO Developer Staff Member Moderator

    I think the Sim is rendered playing the idle-breathe animation in the avatar panel in Play Test (so they don't look like a dead stuffed body). So that animation is definitely broken, not great... It's weird that the playtest animation in the pre-alpha played no sound, since other animations in the pre-alpha (like a2o-bull-hi2-ccw.anim) have the same sound events as in playtest and the final game, implying sound events were meant to be in the game at that point.
  9. RHY3756547

    RHY3756547 FreeSO Developer Staff Member Moderator

    By the way, with the pre-alpha you can run any interaction on a sim using a simple custom global tree, that I just worked out brainstorming with franco. Here's the tree:

    You need to manually change the interaction number each time and save, but apart from that you can then easily call your new global tree from the object in question through the object inspector (Object.../Send Message/) to call the specified interaction. I might make a more general version that you can patch Global.iff with.
    Blayer98 and Fatbag like this.
  10. francot514

    francot514 Well-Known Member

    I can confirm that actions are not working because there is not selected person, as like this cheat message "interests" show:

    Attached Files:

  11. Fatbag

    Fatbag Member

    I was helping Afr0 search for any pictures of property purchasing in The Sims Online (here is the thread); I was unsuccessful, but I found a "Pre-Alpha" picture:

    The UI button placement is slightly different in that picture compared to the version of Pre-Alpha that we have. (Ours looks the same as the later versions of TSO.) In fact, this is likely to be a concept image, and not an actual ingame screenshot, due to the lighting visible on the terrain and the use of drop-shadows on the UI bars, neither of which actually appear in Pre-Alpha through EA-Land. There is also another picture similar to this one but which has "AM/PM xx:xx" rather than "xx:xx AM/PM" and where the "xx:xx" appears higher than the "AM/PM", indicating that the text was probably photoshopped (

    On the other hand, here is a picture from Gamespot that has the same UI as our version of Pre-Alpha (note the old bookmark icon):

    There are plenty more of these pictures in this Flickr photostream, starting on page 140 (look for the old bookmark icon)
    Last edited: Mar 27, 2015
  12. francot514

    francot514 Well-Known Member

    The big research about defaulthouse.dat start using tso pre alpha, the floor arrays seems to be controlled by one byte!!!

    In the image i changed the street floor by this red..

    Attached Files:

    Blayer98 likes this.

Share This Page