![]() |
![]() |
#801 | |
Hobbyist
Join Date: Sep 2018
Location: South Africa
Posts: 61
|
![]() Quote:
I've looked on this site regularly I'm not sure if the classic Lara that Cristina has made is the same one though, I remember downloading the mod from Zac Medley's site when I was a kid but I never knew how to work the skin loader so I just left it. I no longer have the PC that I downloaded the mod to. All the old links are dead, I believe I saw the mod in a YouTube video but I could be mistaken. https://youtu.be/FpKyWVpsiV8
__________________
"The entrance is underwater. No problem." Last edited by Kami; 06-09-18 at 15:52. |
|
![]() |
![]() |
![]() |
#802 |
Hobbyist
Join Date: May 2018
Posts: 26
|
![]()
Well i just tried to use SLGC to install an south pacific outfit i got from http://www.*****************/community...moddings2.html ,but even after following all instructions,when i try to inmport the bumpmap,it gives me a 'Runtime error 9' and i cant figure out what the issue is.
|
![]() |
![]() |
![]() |
#803 |
Explorer
Join Date: Feb 2015
Location: Italy
Posts: 873
|
![]()
Hi! I don't know if you have ever noticed that gas on PS2 has a rotating effect while PC gas is static. I managed to restore this effect and, since I find this mod quite interesting from a modding perspective I'd like to know if someone is interested in learning how it works. I could write a nice post like what Arsunt did with the waving PS1 inventory a while ago. But before doing this I want to make sure that there is someone who is going to read it because I don't want to waste my time
![]() Take note that there will be some mathematical equations and a lot of assembly stuff. |
![]() |
![]() |
![]() |
#804 |
Historian
Join Date: Mar 2015
Location: United Kingdom
Posts: 401
|
![]()
Whilst I personally wouldn't understand the more complicated parts, I would be interested in reading about how you achieved this.
![]()
__________________
"D'you know, it's going to be real pleasure to shut you up." |
![]() |
![]() |
![]() |
#805 |
Explorer
Join Date: Feb 2015
Location: Italy
Posts: 873
|
![]()
Hello there!
![]() ![]() I've recently fixed another PS2 missing effect in the PC version of The Angel of Darkness and this time I've decided to share with you the entire process that led to this result. I hope you can learn something from it. The missing effect we're going to explore together is the rotating gas; it can be found in many levels: Derelict Apartment Block, Galleries Under Siege, Bio-Research Facility, Maximum Containment Area, to name a few. In PC version the gas does not rotate and this greatly reduces the immersivity. But, without wasting any more time, let's start! Introduction To begin with, let's compare PS2 and PC versions. https://www.youtube.com/watch?v=Dn7A6OKLOVI Enabling wireframe mode in PC version (F11) and then hiding room and object meshes (SHIFT+F3, SHIFT+F4) allows us to see better our gas sprites. All these black squares are static whilst they should rotate. You can notice that each square has a diagonal; keep this mind, we'll return to it in a moment. ![]() Finding the bug Ok, to fix this bug we first have to find it, right? Let's move to IDA and have a look to game functions. Using filter function of "Function name" tab I've filtered all game functions containing "gas" in their name. ![]() The first function we're going to explore is fxInsertGas. We can guess from its name that this function is designed to add gas sprites to the environment. Here's the function's body: ![]() For simplicity I've already added three variables names here (GasCloud_Rotation, GasCloud_Diagonal and GasCloud_Colour). It wasn't like that the first time I opened that function. Anyway, every time this function is called, a new GASCLOUD struct is created. This struct contains all the information the game needs to proper render a single gas sprite like its position, its colour, its size and... its rotation. This is how this struct should have looked in the original source code: Code:
struct GASCLOUD { float X; // Center of the sprite float Y; // Center of the sprite float Z; // Center of the sprite float W; // Always 1 float diagonal; // Diagonal lenght float rot_angle; // Rotation angle unsigned int ARGB; // Gas colour unsigned short Unknown1; unsigned short Unknown2; }; ![]() Hummmm, all those red spots ranging from 0 to 360 look much like rotation degrees to me. So it means that this function is fine because it writes the rotation angle without a hitch. We can conclude that the rotation angle is written for nothing because there's no other function that reads it. Time to go back to IDA... ![]() Now, let's move to fxParticleAdd_GasCloud function. This function has the following four parameters:
Code:
struct VF_VCT { float v1_X; float v1_Y; float v1_Z; unsigned int v1_colorBGRA; float v1_U; float v1_V; float v2_X; float v2_Y; float v2_Z; unsigned int v2_colorBGRA; float v2_U; float v2_V; float v3_X; float v3_Y; float v3_Z; unsigned int v3_colorBGRA; float v3_U; float v3_V; float v4_X; float v4_Y; float v4_Z; unsigned int v4_colorBGRA; float v4_U; float v4_V; };
This is how vertices look inside the vertex buffer: ![]() How to read the picture:
Finding a fix Those who have studied trigonometry at school may know where this is going: we have 4 vertices and we need to rotate them around the center of the square and put them back inside the vertex buffer. That sounds easy when you say it, but it's not. I'll sum up here all the steps required to achieve this goal:
Creating the fix It drives me crazy thinking that writing this fix in C++ would have taken no more than 10 minutes for a Core Design programmer. Unfortunately for us, there's no way to escape assembly so we have to do this in the hard way. ![]() First we need a codecave, a region of memory where we can write our instructions. We will tell the game to jump to that codecave, run our instructions and then jump back to the ending of fxParticleAdd_GasCloud function. Since relying on codecaves is questionable and it's not easy to find large ones, I've implemented in my TRAOD Multipatcher an assembly injection engine that automatically allocates new space at the end of the file. In this guide we'll assume that we already have our allocated space for the code. We will also allocate 16 extra bytes (4 double words) for temporarily storing some data during calculations; we'll call them DATA1, DATA2, DATA3 and DATA4. 1) Read the rotation angle A and convert it to radiants Code:
mov edx, [ebx+8] // Reads the pointer to GASCLOUD struct fld dword ptr [edx+14] // Pushes the rotation angle onto the top of the FPU (floating point unit) stack fldpi // Pushes Pi onto the top of the FPU stack fmulp st(1),st(0) // Multiplies the rotation angle by Pi and pops Pi fdiv dword ptr [009511C0] // Divides the result of the multiplication by 180. The rotation angle in radiants is now at the top of the stack, st(0) Code:
fsincos // After this instruction we have the sine in st(1) and the cosine in st(0) Code:
(1) movlps xmm0, [eax] // Reads X and Y of vertex 1 (2) movlps xmm1, [eax+48] // Reads X and Y of vertex 4 (3) addps xmm0, xmm1 // Sums X1+X4 and Y1+Y4 (4) movlps xmm1, [00717940] // Loads 2 - 2 (5) divps xmm0, xmm1 // Divide (X1+X4) and (Y1+Y4) by 2 movlps [DATA1], xmm0 // Save the result into memory. This step is required to load the result to the upper part of xmm0 register (6) movhps xmm0, [DATA1] // After this instruction xmm0 contains xy coordinates of the center of the sprite in both lower and upper part of the register ![]() 4) Translate the 4 vertices so that the center of the square is at the origin of the Cartesian plane Code:
movlps xmm1, [eax] // Loads X1/Y1 movhps xmm1, [eax+18] // Loads X2/Y2 movlps xmm2, [eax+30] // Loads X3/Y3 (7) movhps xmm2, [eax+48] // Loads X4/Y4 subps xmm1, xmm0 // Subtracts the center of the sprite from v1 and v2 (8) subps xmm2, xmm0 // Subtracts the center of the sprite from v3 and v4 ![]() 5) Rotate the 4 vertices Code:
movaps xmm3, xmm1 // Copies X1/Y1/X2/Y2 from xmm1 to xmm3 register (9) movaps xmm4, xmm2 // Copies X3/Y3/X4/Y4 from xmm2 to xmm4 register fst [DATA1] // Stores cosine in DATA1 fstp [DATA4] // Stores and pops cosine in DATA4 fst [DATA2] // Stores sine in DATA2 (10) fstp [DATA3] // Stores and pops sine in DATA3 movlps xmm7, [DATA1] // Loads cos-sin into lower part of xmm7 (11) movhps xmm7, [DATA1] // Loads cos-sin into upper part of xmm7 mulps xmm1, xmm7 (12) mulps xmm2, xmm7 movlps xmm7, [DATA3] // Loads sin-cos into lower part of xmm7 (13) movhps xmm7, [DATA3] // Loads sin-cos into upper part of xmm7 mulps xmm3, xmm7 (14) mulps xmm4, xmm7 (15) movaps xmm5, xmm1 (16) psrldq xmm5, 4 // Shifts xmm5 register by 4 bytes to the lower part. In this way X and Y are aligned so we can subtract them (17) subps xmm1, xmm5 // In the picture below we're going to use only a and c values movaps xmm5, xmm2 psrldq xmm5, 4 subps xmm2, xmm5 movaps xmm5, xmm3 // We now have X1new, X2new, X3new, X4new psrldq xmm5, 4 // Same as (16) but this time we need to add them addps xmm3, xmm5 movaps xmm5, xmm4 psrldq xmm5, 4 (18) addps xmm4, xmm5 // We now have Y1new, Y2new, Y3new, Y4new movaps [DATA1], xmm3 // The purpose of the following instructions is to rearrange x1/x2 and y1/y2 values position inside xmm1 register mov ecx, [DATA1] mov ebp, [DATA3] movaps [DATA1], xmm1 mov [DATA2], ecx mov [DATA4], ebp movaps xmm1, [DATA1] // xmm1 is now ready movaps [DATA1], xmm4 // The purpose of the following instructions is to rearrange x3/x4 and y3/y4 values position inside xmm2 register mov ecx, [DATA1] mov ebp, [DATA3] movaps [DATA1], xmm2 mov [DATA2], ecx mov [DATA4], ebp (19) movaps xmm2, [DATA1] // xmm2 is now ready ![]() ![]() 6) Translate the 4 vertices back to the original position Code:
addps xmm1, xmm0 addps xmm2, xmm0 7) Overwrite old vertices coordinates inside the vertex buffer with the new ones Code:
movlps [eax], xmm1 // Copies X1new and Y1new in the vertex buffer movhps [eax+18], xmm1 // Copies X2new and Y2new in the vertex buffer movlps [eax+30], xmm2 // Copies X3new and Y3new in the vertex buffer movhps [eax+48], xmm2 // Copies X4new and Y4new in the vertex buffer https://www.youtube.com/watch?v=1IqPP4QZpzs Last edited by nakamichi680; 17-11-19 at 11:06. |
![]() |
![]() |
![]() |
#806 |
Professor
Join Date: Mar 2012
Location: France
Posts: 3,686
|
![]()
This sure was an interesting read!
![]() I can't read assembler, because I never learned and couldn't figure out how it was working with so few instructions and seemingly cryptic language, what were registers and what they were for, etc, but with your explanations I think I understood some things about it (you work with bytes, and to make calculations between variables you store them in a stacka stack and then apply an operation on that stack, something along those lines? It's still very blurry but I have an idea now). It's very low-level but it seems it's even lower than I originally thought. And gas definitely looks better with rotation! I always found it weird how it was so static, but I never knew on PS2 there was rotation. ![]() Last edited by Joey79100; 14-09-18 at 18:39. |
![]() |
![]() |
![]() |
#807 |
Explorer
Join Date: Feb 2015
Location: Italy
Posts: 873
|
![]()
The code above makes use of ecx, edx and ebp (which are three 32 bits general purpose registers of x86 CPUs, the others are eax, ebx, esi, edi, esp and eip), FPU (which has 8 registers organized into a stack) and SSE registers (from xmm0 to xmm7, each one 128 bits large). This code is for p3 and p4 versions of the executable since both Pentium 3 and Pentium 4 support SSE instructions.
TRAOD.EXE is for CPUs that doesn't support SSE so I had to rewrite the code above to use only the FPU. This is the non-SSE code (tested and fully working): Code:
mov ecx, [ebp+8] fld dword ptr [ecx+14] fldpi fmulp st(1),st(0) fdiv dword ptr [0062C170] fsincos fstp [DATA1] fstp [DATA2] fld dword ptr [edi-48] fadd dword ptr [edi] fdiv dword ptr [00634F20] fstp [DATA3] fld dword ptr [edi-44] fadd dword ptr [edi+4] fdiv dword ptr [00634F20] fstp [DATA4] fld dword ptr [edi-48] fsub dword ptr [DATA3] fmul dword ptr [DATA1] fld dword ptr [edi-44] fsub dword ptr [DATA4] fmul dword ptr [DATA2] fsubp st(1), st(0) fadd dword ptr [DATA3] fld dword ptr [edi-48] fsub dword ptr [DATA3] fmul dword ptr [DATA2] fld dword ptr [edi-44] fsub dword ptr [DATA4] fmul dword ptr [DATA1] faddp st(1), st(0) fadd dword ptr [DATA4] fstp [edi-44] fstp [edi-48] fld dword ptr [edi-30] fsub dword ptr [DATA3] fmul dword ptr [DATA1] fld dword ptr [edi-2C] fsub dword ptr [DATA4] fmul dword ptr [DATA2] fsubp st(1), st(0) fadd dword ptr [DATA3] fld dword ptr [edi-30] fsub dword ptr [DATA3] fmul dword ptr [DATA2] fld dword ptr [edi-2C] fsub dword ptr [DATA4] fmul dword ptr [DATA1] faddp st(1), st(0) fadd dword ptr [DATA4] fstp [edi-2C] fstp [edi-30] fld dword ptr [edi-18] fsub dword ptr [DATA3] fmul dword ptr [DATA1] fld dword ptr [edi-14] fsub dword ptr [DATA4] fmul dword ptr [DATA2] fsubp st(1), st(0) fadd dword ptr [DATA3] fld dword ptr [edi-18] fsub dword ptr [DATA3] fmul dword ptr [DATA2] fld dword ptr [edi-14] fsub dword ptr [DATA4] fmul dword ptr [DATA1] faddp st(1), st(0) fadd dword ptr [DATA4] fstp [edi-14] fstp [edi-18] fld dword ptr [edi] fsub dword ptr [DATA3] fmul dword ptr [DATA1] fld dword ptr [edi+4] fsub dword ptr [DATA4] fmul dword ptr [DATA2] fsubp st(1), st(0) fadd dword ptr [DATA3] fld dword ptr [edi] fsub dword ptr [DATA3] fmul dword ptr [DATA2] fld dword ptr [edi+4] fsub dword ptr [DATA4] fmul dword ptr [DATA1] faddp st(1), st(0) fadd dword ptr [DATA4] fstp [edi+4] fstp [edi] add edi, 18 mov [edx], edi jmp 0046D28F Last edited by nakamichi680; 18-09-18 at 21:08. |
![]() |
![]() |
![]() |
#808 | |
Archaeologist
Join Date: Jun 2006
Location: Jordan
Posts: 2,259
|
![]() Quote:
Though this is going to take many cups of coffee to understand, even if you knew (some) assembly. I admire your dedication ![]()
__________________
Right now, I'm losing patience |
|
![]() |
![]() |
![]() |
#809 |
Archaeologist
Join Date: Sep 2017
Location: Spain
Posts: 1,043
|
![]()
Literally ME trying to understand all
those formulas to fixing the gas effect: I'm in love with this little things. When PC version finally got all its issues fixed, the game will be so enjoyable to play... Nice work as always, Nakamichi. Last edited by tlr online; 14-09-18 at 21:29. Reason: Image removed. See FAQ on posting images. |
![]() |
![]() |
![]() |
#810 |
Archaeologist
Join Date: Aug 2009
Location: Ireland
Posts: 2,106
|
![]()
A very educational read
![]() |
![]() |
![]() |
![]() |
Thread Tools | |
|
|