ZenHAX

Free Game Research Forum | Official QuickBMS support | twitter @zenhax | SSL HTTPS://zenhax.com
It is currently Sat Sep 22, 2018 2:06 am

All times are UTC




Post new topic  Reply to topic  [ 31 posts ]  Go to page Previous 1 2
Author Message
 Post subject: Re: Quake Champions
PostPosted: Sun Jun 17, 2018 6:26 pm 

Joined: Sat Jun 16, 2018 10:53 pm
Posts: 8
Thanks for your help mate, highly appreciated. But even after successfully removing the steam encryption w/ steamless (nice hint btw, didn't know this exists), the mentioned constants are nowhere to find :( Well, except for a heckton of 0x15 and 0x23 matches that didn't really get me any further

If they changed all those values, I guess I would have to find the algorithm only by recognizing the corresponding assembly op codes. Which (besides sounding crazy) I doubt is even possible since they depend on the used compiler and its settings, if I am not mistaken?

aluigi wrote:
Worst case scenario dump the memory and do it the raw way :)

Will do this.
I think there could be an additional reverse engineering protection because the disassembled steamless output still has some "random byte" areas that shouldn't be there.

Can you recommend a decent memdump debugging tool? I didn't really like the few I "worked" with so far and the windows-implemented techniques are not that practical either

-----------------------------------------------------

EDIT: Windows task manager DMP file of the process didn't include the values either.... something strange is going on here


Top
   
 Post subject: Re: Quake Champions
PostPosted: Mon Jun 18, 2018 4:35 pm 

Joined: Sat Jun 16, 2018 10:53 pm
Posts: 8
Found it in the ram! Finally.

This is the actual nextUInt64() func:
(The comments are refering to the the code in http://aluigi.org/bms/quake_champions.bms)

Code:
mov r8,rcx                  
mov rcx,27BB2EE687B0B0FD { -2018463491 }   
mov rax,[r8]      // ulong U
imul rax,rcx         // U *= 2862933555777941757ULL   
mov rcx,61C8864680B583BF    
add rax,rcx         // U += 7046029254386353087ULL
mov [r8],rax      // save U                  
mov rdx,[r8+08]      // RDX = ulong V
shr rdx,11          // V >> 17
xor rdx,[r8+08]      // ^= V
mov rcx,rdx               
shl rcx,1F         // V << 31   
xor rcx,rdx         // ^= V
mov edx,FFFFDA61    // 4294957665U   
mov rax,rcx               
shr rax,08 { 8 }      // V >> 8
xor rax,rcx         // ^= V
mov [r8+08],rax      // save V
mov rcx,[r8+10]      // RCX = ulong W
mov eax,ecx
imul rdx,rax         // ???!!!!
shr rcx,20 { 32 }      // W >> 32   
add rdx,rcx         // W + ?!
mov [r8+10],rdx      // save W
mov rcx,[r8]      // RCX = ulong X (= U)                  
shl rcx,15 { 21 }      // U << 21               
xor rcx,[r8]         // ^ U = X
mov rax,rcx         // RAX = X               
shr rax,23 { 35 }      // X >> 35
xor rcx,rax         // ^= X
mov rax,rcx         // RAX = RCX   
shl rax,04 { 4 }      // X << 4
xor rax,rcx         // ^= X
add rax,[r8+08]      // (x + v)
xor rax,rdx         // ^ w
ret

I tried to analyze it as good as I possibly could. I am lacking a lot of practice tho, as you may see

If I am not mistaken, they changed the line
Code:
w = 4294957665U * (w & 0xffffffff) + (w >> 32);

to something like
Code:
v ^= v >> 17; 
ulong a = v;      // rdx?
v ^= v << 31;
v ^= v >> 8;
ulong b = v;       // rax?
w = (a * b) + (w >> 32);   // a * b -> imul rdx, rax??

But what is happing to the 4294957665U constant?

This is a bit over my head actually, can someone back me up here please?

EDIT: This is the NrRandom() function, which contains iterations of NextUInt64() since NrRandom() calls it a few times:
Code:
mov rax,38ECAC5FB3251641 { -1289415103 }
mov r8,rcx
mov [rcx+08],rax
mov qword ptr [rcx+10],00000001 { 1 }
mov rax,[QuakeChampions.NvOptimusEnablement+4A77E0] { [148F2BB98F0] }
test rax,rax
je QuakeChampions.apProcessExceptionDllCall+1D99A8
xor rdx,[rax]
mov rax,[rcx+08]
mov r11,27BB2EE687B0B0FD { -2018463491 }
xor rax,rdx
mov r10,61C8864680B583BF { -2135587905 }
imul rax,r11
mov r9d,FFFFDA61 { -9631 }
add rax,r10
mov [rcx],rax
mov rcx,[rcx+08]
shr rcx,11 { 17 }
xor rcx,[r8+08]
mov rdx,rcx
shl rdx,1F { 31 }
xor rdx,rcx
mov rax,rdx
shr rax,08 { 8 }
xor rax,rdx
mov [r8+08],rax
mov rcx,[r8+10]
mov eax,ecx
imul rax,r9
shr rcx,20 { 32 }
add rax,rcx
mov [r8+10],rax
mov rax,[r8]
mov [r8+08],rax
mov rax,[r8]
imul rax,r11
add rax,r10
mov [r8],rax
mov rcx,[r8+08]
shr rcx,11 { 17 }
xor rcx,[r8+08]
mov rdx,rcx
shl rdx,1F { 31 }
xor rdx,rcx
mov rax,rdx
shr rax,08 { 8 }
xor rax,rdx
mov [r8+08],rax
mov rcx,[r8+10]
mov eax,ecx
imul rax,r9
shr rcx,20 { 32 }
add rax,rcx
mov [r8+10],rax
mov rax,[r8+08]
mov [r8+10],rax
mov rax,[r8]
imul rax,r11
add rax,r10
mov [r8],rax
mov rcx,[r8+08]
shr rcx,11 { 17 }
xor rcx,[r8+08]
mov rdx,rcx
shl rdx,1F { 31 }
xor rdx,rcx
mov rax,rdx
shr rax,08 { 8 }
xor rax,rdx
mov [r8+08],rax
mov rcx,[r8+10]
mov eax,ecx
imul rax,r9
shr rcx,20 { 32 }
add rax,rcx
mov [r8+10],rax
ret

I've recently learned that r9 = r9d, so 4294957665U is in fact multiplied with W?

EDIT2: Here's everything I found in the memory (each function seperated, still messy): https://pastebin.com/5TMBEQWS


Top
   
 Post subject: Re: Quake Champions
PostPosted: Sun Jul 08, 2018 12:56 pm 

Joined: Sun Jul 08, 2018 12:24 pm
Posts: 1
Encryption pseudocode:
Code:
signed __int64 __fastcall sub_141562250(__int64 a1, __int64 a2)
{
  __int64 v2; // r8@1
  __int64 v3; // rcx@3
  signed __int64 result; // rax@3

  v2 = a1;
  *(_QWORD *)(a1 + 8) = 4101842887655102017i64;
  *(_QWORD *)(a1 + 16) = 1i64;
  if ( qword_142F99BE8 )
    a2 ^= *(_QWORD *)qword_142F99BE8;
  *(_QWORD *)a1 = 2862933555777941757i64 * (a2 ^ *(_QWORD *)(a1 + 8)) + 7046029254386353087i64;
  v3 = *(_QWORD *)(a1 + 8) ^ (*(_QWORD *)(a1 + 8) >> 17);
  *(_QWORD *)(v2 + 8) = v3 ^ (v3 << 31) ^ ((v3 ^ (unsigned __int64)(v3 << 31)) >> 8);
  *(_QWORD *)(v2 + 16) = (*(_QWORD *)(v2 + 16) >> 32) + 4294957665i64 * (unsigned int)*(_QWORD *)(v2 + 16);
  *(_QWORD *)(v2 + 8) = *(_QWORD *)v2;
  *(_QWORD *)v2 = 2862933555777941757i64 * *(_QWORD *)v2 + 7046029254386353087i64;
  *(_QWORD *)(v2 + 8) ^= (*(_QWORD *)(v2 + 8) >> 17) ^ ((*(_QWORD *)(v2 + 8) ^ (*(_QWORD *)(v2 + 8) >> 17)) << 31) ^ ((*(_QWORD *)(v2 + 8) ^ (*(_QWORD *)(v2 + 8) >> 17) ^ ((*(_QWORD *)(v2 + 8) ^ (*(_QWORD *)(v2 + 8) >> 17)) << 31)) >> 8);
  *(_QWORD *)(v2 + 16) = (*(_QWORD *)(v2 + 16) >> 32) + 4294957665i64 * (unsigned int)*(_QWORD *)(v2 + 16);
  *(_QWORD *)(v2 + 16) = *(_QWORD *)(v2 + 8);
  *(_QWORD *)v2 = 2862933555777941757i64 * *(_QWORD *)v2 + 7046029254386353087i64;
  *(_QWORD *)(v2 + 8) ^= (*(_QWORD *)(v2 + 8) >> 17) ^ ((*(_QWORD *)(v2 + 8) ^ (*(_QWORD *)(v2 + 8) >> 17)) << 31) ^ ((*(_QWORD *)(v2 + 8) ^ (*(_QWORD *)(v2 + 8) >> 17) ^ ((*(_QWORD *)(v2 + 8) ^ (*(_QWORD *)(v2 + 8) >> 17)) << 31)) >> 8);
  result = (*(_QWORD *)(v2 + 16) >> 32) + 4294957665i64 * (unsigned int)*(_QWORD *)(v2 + 16);
  *(_QWORD *)(v2 + 16) = result;
  return result;
}
int __fastcall sub_141561CB0(__int64 a1, signed __int64 a2)
{
  unsigned __int64 v2; // rdi@1
  __int64 v3; // rbx@1
  __int64 v4; // rax@2
  unsigned __int64 v5; // r10@2
  __int64 v6; // r8@2
  unsigned __int64 v7; // rdx@2
  signed __int64 v8; // r9@2
  unsigned __int64 v9; // rdx@3
  __int128 v10; // xmm1@6
  __int64 v11; // rdx@6
  __int64 v13; // [sp+0h] [bp-48h]@6
  __int64 v14; // [sp+20h] [bp-28h]@2
  unsigned __int64 v15; // [sp+28h] [bp-20h]@2
  unsigned __int64 v16; // [sp+30h] [bp-18h]@2
  __int64 v17; // [sp+38h] [bp-10h]@6

  v2 = 0i64;
  v3 = a1;
  *(_DWORD *)(a1 + 184) = 0;
  if ( a2 )
  {
    v6 = a1 + 112;
    *(_OWORD *)v6 = *(_OWORD *)a2;
    *(_OWORD *)(v6 + 16) = *(_OWORD *)(a2 + 16);
  }
  else
  {
    LODWORD(v4) = time64(0i64);
    sub_141562250((__int64)&v14, v4);
    v5 = v16;
    v6 = v3 + 112;
    v7 = v15;
    v8 = v14;
    do
    {
      v5 = 4294957665i64 * (unsigned int)v5 + (v5 >> 32);
      v9 = (((v7 >> 17) ^ v7) << 31) ^ (v7 >> 17) ^ v7;
      v7 = (v9 >> 8) ^ v9;
      v8 = 2862933555777941757i64 * v8 + 7046029254386353087i64;
      *(_BYTE *)(v6 + v2++) = v5 ^ (v7
                                  + (((v8 ^ (unsigned __int64)(v8 << 21)) >> 35) ^ v8 ^ 16
                                                                                      * (((v8 ^ (unsigned __int64)(v8 << 21)) >> 35) ^ v8)));
    }
    while ( v2 < 0x20 );
    a2 = v3 + 112;
  }
  *(_OWORD *)(v3 + 144) = *(_OWORD *)a2;
  v10 = *(_OWORD *)(a2 + 16);
  v11 = *(_QWORD *)v6;
  *(_QWORD *)(v3 + 176) = *(_QWORD *)v6;
  *(_OWORD *)(v3 + 160) = v10;
  sub_141562250(v3 + 192, v11);
  return sub_14211B3B0((unsigned __int64)&v13 ^ v17);
}


Tried cleaning it a little bit, still a long way to go, got bored, may be it will help someone:
Code:
signed __int64 __fastcall sub_141562250(__int64 a1, __int64 a2)
{
  __int64 u; // r8@1
  __int64 v3; // rcx@3
  signed __int64 result; // rax@3

  u = a1;
  v = 4101842887655102017i64;
  w = 1i64;
  if ( qword_142F99BE8 )
    a2 ^= qword_142F99BE8;
  u = 2862933555777941757i64 * (a2 ^ 4101842887655102017i64) + 7046029254386353087i64;
  v3 = 4101842887655102017i64 ^ (4101842887655102017i64 >> 17);
  v = v3 ^ (v3 << 31) ^ ((v3 ^ (v3 << 31)) >> 8);
  w = (w >> 32) + 4294957665i64 * (w & 0xffffffff);
  v = u;
  u = 2862933555777941757i64 * u + 7046029254386353087i64;
  v ^= (v >> 17) ^ ((v ^ (v >> 17)) << 31) ^ ((v ^ (v >> 17) ^ ((v ^ (v >> 17)) << 31)) >> 8);
  w = (w >> 32) + 4294957665i64 * (w & 0xffffffff);
  w = v;
  u = 2862933555777941757i64 * u + 7046029254386353087i64;
  v ^= (v >> 17) ^ ((v ^ (v >> 17)) << 31) ^ ((v ^ (v >> 17) ^ ((v ^ (v >> 17)) << 31)) >> 8);
  result = (w >> 32) + 4294957665i64 * (w & 0xffffffff);
  w = result;
  return result;
}
int __fastcall sub_141561CB0(__int64 a1, signed __int64 a2)
{
  unsigned __int64 v2; // rdi@1
  __int64 v3; // rbx@1
  __int64 now; // rax@2
  unsigned __int64 v5; // r10@2
  __int64 v6; // r8@2
  unsigned __int64 v7; // rdx@2
  signed __int64 v8; // r9@2
  unsigned __int64 v9; // rdx@3
  __int128 v10; // xmm1@6
  __int64 v11; // rdx@6
  __int64 v13; // [sp+0h] [bp-48h]@6
  __int64 v14; // [sp+20h] [bp-28h]@2
  unsigned __int64 v15; // [sp+28h] [bp-20h]@2
  unsigned __int64 v16; // [sp+30h] [bp-18h]@2
  __int64 v17; // [sp+38h] [bp-10h]@6

  v2 = 0i64;
  v3 = a1;
  *(_DWORD *)(a1 + 184) = 0;
  if ( a2 )
  {
    v6 = a1 + 112;
    *(_OWORD *)v6 = *(_OWORD *)a2;
    *(_OWORD *)(v6 + 16) = *(_OWORD *)(a2 + 16);
  }
  else
  {
    LODWORD(now) = time64(0i64);
    sub_141562250((__int64)&v14, now);
    w = v16;
    v6 = v3 + 112;
    v = v15;
    u = v14;
    do
    {
      w = 4294957665i64 * (w & 0xffffffff) + (w >> 32);
      v9 = (((v >> 17) ^ v) << 31) ^ (v >> 17) ^ v;
      v = (v9 >> 8) ^ v9;
      u = 2862933555777941757i64 * u + 7046029254386353087i64;
      *(_BYTE *)(v6 + v2++) = w ^ (v + (((u ^ (u << 21)) >> 35) ^ u ^ 16 * (((u ^ (u << 21)) >> 35) ^ u)));
    }
    while ( v2 < 0x20 );
    a2 = v3 + 112;
  }
  *(_OWORD *)(v3 + 144) = *(_OWORD *)a2;
  v10 = *(_OWORD *)(a2 + 16);
  v11 = *(_QWORD *)v6;
  *(_QWORD *)(v3 + 176) = *(_QWORD *)v6;
  *(_OWORD *)(v3 + 160) = v10;
  sub_141562250(v3 + 192, v11);
  return sub_14211B3B0((unsigned __int64)&v13 ^ v17);
}


Top
   
 Post subject: Re: Quake Champions
PostPosted: Fri Jul 13, 2018 2:39 pm 

Joined: Sat Jun 16, 2018 10:53 pm
Posts: 8
I already went through all of this. Regarding the constants and the u,v,w variables, everything is exactly the same, but they added a function call in the decryption code:
Quote:
if(++qc_seed_idx == 8) {
qc_seed = NextUInt64();
qc_seed_idx = 0;
}
//somewhere here they added....

E8 B699BB00 - call QuakeChampions.Scaleform::Render::Matrix4x4<double>::Transpose+3D4490 // this

and maybe some other Init Vector stuff (I guess) beforehand, including a time function and "security cookie" calls (which are not included in my pastebin since I found them later). The 4x4Matrix appears to be a shifting substitution box of some kind, obviously returning a double.

There are in fact several variations of this RNG where constant floats/doubles are multiplied to the NextUInt64() output, but I found none that includes a 4x4 matrix and thus a variable double value.

However it COULD be possible to reverse engineer the happenings in the box as well of course, but that's where my motivation, spare time and also capabilities reached its limits.

-> Probably easier to hook the running exe and go from there, reading out buffers or memcpy seeds, whatever. Maybe even call the matrix function from the outside. I am not that experienced in this field, but you get my point I guess

ah and those pointer dereferences to NvOptimusEnablement are also questionable. I dont even remember where it led to, as some weeks has passed
QuakeChampions.apProcessExceptionDllCall+1D9999 - 48 8B 05 5843A301 - mov rax,[QuakeChampions.NvOptimusEnablement+4A77E0] { [148F2BB98F0] }


//no guarantee of correctness


Top
   
 Post subject: Re: Quake Champions
PostPosted: Thu Aug 02, 2018 4:27 pm 

Joined: Thu Aug 02, 2018 3:59 pm
Posts: 1
Have anyone reached encrypted files?


Top
   
 Post subject: Re: Quake Champions
PostPosted: Fri Aug 31, 2018 12:17 am 

Joined: Thu Aug 30, 2018 3:13 pm
Posts: 2
it is decrypted with this code.
Code:
    void NrRandom(ulong seed)
    {
      v = 4101842887655102017ULL;
      w = 1;

      u = v ^ seed ^ 0x412e2206; NextUInt64();
      v = u;                     NextUInt64();
      w = v;                     NextUInt64();
    }


Top
   
 Post subject: Re: Quake Champions
PostPosted: Sun Sep 02, 2018 1:35 pm 

Joined: Sat Jun 16, 2018 10:53 pm
Posts: 8
z65536 wrote:
it is decrypted with this code.
Code:
    void NrRandom(ulong seed)
    {
      v = 4101842887655102017ULL;
      w = 1;

      u = v ^ seed ^ 0x412e2206; NextUInt64();
      v = u;                     NextUInt64();
      w = v;                     NextUInt64();
    }

Thanks a lot, works now.
Feel kinda stupid tho. How did you find it out?


Top
   
 Post subject: Re: Quake Champions
PostPosted: Mon Sep 03, 2018 3:14 am 

Joined: Thu Aug 30, 2018 3:13 pm
Posts: 2
buk0wski wrote:
z65536 wrote:
it is decrypted with this code.
Code:
    void NrRandom(ulong seed)
    {
      v = 4101842887655102017ULL;
      w = 1;

      u = v ^ seed ^ 0x412e2206; NextUInt64();
      v = u;                     NextUInt64();
      w = v;                     NextUInt64();
    }

Thanks a lot, works now.
Feel kinda stupid tho. How did you find it out?

I used a debugger.

these are function NrRandom and quake_decrypt
Code:
signed __int64 __fastcall sub_141666740(_QWORD *a1, __int64 a2)
{
  _QWORD *v2; // r8
  __int64 v3; // rcx
  signed __int64 result; // rax

  v2 = a1;
  a1[1] = 4101842887655102017i64;
  a1[2] = 1i64;
 
  if ( qword_1430FB778 )
    a2 ^= *(_QWORD *)qword_1430FB778;     //As this lines was added, I set a breakpoint here

  *a1 = 2862933555777941757i64 * (a2 ^ a1[1]) + 7046029254386353087i64;
  v3 = a1[1] ^ (a1[1] >> 17);
  v2[1] = v3 ^ (v3 << 31) ^ ((v3 ^ (unsigned __int64)(v3 << 31)) >> 8);
  v2[2] = (v2[2] >> 32) + 4294957665i64 * (unsigned int)v2[2];
  v2[1] = *v2;
  *v2 = 2862933555777941757i64 * *v2 + 7046029254386353087i64;
  v2[1] ^= (v2[1] >> 17) ^ ((v2[1] ^ (v2[1] >> 17)) << 31) ^ ((v2[1] ^ (v2[1] >> 17) ^ ((v2[1] ^ (v2[1] >> 17)) << 31)) >> 8);
  v2[2] = (v2[2] >> 32) + 4294957665i64 * (unsigned int)v2[2];
  v2[2] = v2[1];
  *v2 = 2862933555777941757i64 * *v2 + 7046029254386353087i64;
  v2[1] ^= (v2[1] >> 17) ^ ((v2[1] ^ (v2[1] >> 17)) << 31) ^ ((v2[1] ^ (v2[1] >> 17) ^ ((v2[1] ^ (v2[1] >> 17)) << 31)) >> 8);
  result = (v2[2] >> 32) + 4294957665i64 * (unsigned int)v2[2];
  v2[2] = result;
  return result;
}


Code:
__int16 __fastcall sub_141665F70(__int64 a1, _BYTE *a2, __int64 a3)
{
  __int64 v3; // r11
  _BYTE *v4; // r10
  __int64 i; // r9
  __int64 v6; // rcx
  char v7; // r8
  __int16 v8; // ax
  signed __int64 v9; // rax
  bool v10; // zf
  signed __int64 v11; // rdx

  v3 = a3;
  v4 = a2;
  for ( i = a1; v3; --v3 )
  {
    v6 = *(unsigned __int16 *)(i + 186);
    v7 = *(_BYTE *)(v6 + i + 144);
    *(_BYTE *)(v6 + i + 144) = *v4;
    *v4 ^= v7 ^ *(_BYTE *)(i + 176) & (unsigned __int64)(255i64 << 8 * (unsigned __int8)*(_WORD *)(i + 184));
    v8 = *(_WORD *)(i + 186);
    ++*(_WORD *)(i + 184);
    LOWORD(v9) = ((_BYTE)v8 + 1) & 0x1F;
    v10 = *(_WORD *)(i + 184) == 8;
    *(_WORD *)(i + 186) = v9;
    if ( v10 )
    {
      *(_QWORD *)(i + 192) = 2862933555777941757i64 * *(_QWORD *)(i + 192) + 7046029254386353087i64;
      *(_QWORD *)(i + 200) ^= (*(_QWORD *)(i + 200) >> 17) ^ ((*(_QWORD *)(i + 200) ^ (*(_QWORD *)(i + 200) >> 17)) << 31) ^ ((*(_QWORD *)(i + 200) ^ (*(_QWORD *)(i + 200) >> 17) ^ ((*(_QWORD *)(i + 200) ^ (*(_QWORD *)(i + 200) >> 17)) << 31)) >> 8);
      v11 = (*(_QWORD *)(i + 208) >> 32) + 4294957665i64 * *(unsigned int *)(i + 208);
      *(_QWORD *)(i + 208) = v11;
      v9 = v11 ^ (*(_QWORD *)(i + 200)
                + (((*(_QWORD *)(i + 192) ^ (*(_QWORD *)(i + 192) << 21)) >> 35) ^ *(_QWORD *)(i + 192) ^ (*(_QWORD *)(i + 192) << 21) ^ 16i64 * (((*(_QWORD *)(i + 192) ^ (*(_QWORD *)(i + 192) << 21)) >> 35) ^ *(_QWORD *)(i + 192) ^ (*(_QWORD *)(i + 192) << 21))));
      *(_WORD *)(i + 184) = 0;
      *(_QWORD *)(i + 176) = v9;
    }
    ++v4;
  }
  return v9;
}


Top
   
 Post subject: Re: Quake Champions
PostPosted: Fri Sep 07, 2018 4:45 pm 

Joined: Sat Jun 16, 2018 10:53 pm
Posts: 8
Thanks again, I got it.

They changed the constant in the latest patch. New, working code below.

Code:
void NrRandom(ulong seed)
    {
      v = 4101842887655102017ULL;
      w = 1;

      u = v ^ seed ^ 0x631A2028; NextUInt64();
      v = u;                     NextUInt64();
      w = v;                     NextUInt64();
    }


Top
   
 Post subject: Re: Quake Champions
PostPosted: Sat Sep 08, 2018 3:39 am 

Joined: Sat Sep 08, 2018 3:30 am
Posts: 1
Can someone extract bj's voice lines or share script to do it myself? please


Top
   
 Post subject: Re: Quake Champions
PostPosted: Thu Sep 13, 2018 9:49 pm 
Site Admin
User avatar

Joined: Wed Jul 30, 2014 9:32 pm
Posts: 8809
I have just updated the script in case someone wants to test it:
http://aluigi.org/bms/quake_champions.bms

The only sample file I have here uses an older constant so I didn't test it.


Top
   
Display posts from previous:  Sort by  
Post new topic  Reply to topic  [ 31 posts ]  Go to page Previous 1 2

All times are UTC


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Limited