Sonic and Sega Retro Message Board: The SX Engine - Sonic and Sega Retro Message Board

Jump to content

Hey there, Guest!  (Log In · Register) Help
  • 7 Pages +
  • 1
  • 2
  • 3
  • 4
  • Last ►
    Locked
    Locked Forum

The SX Engine Recreating the Sonic Xtreme executable (now open source!)

#16 User is offline Uhyve 

Posted 13 December 2010 - 11:59 AM

  • Master Procrastinator
  • Posts: 46
  • Joined: 08-November 10
  • Gender:Male
  • Location:Huddersfield, England
  • Project:SX Engine
Added a new video to main post.

#17 User is offline Andrew75 

Posted 13 December 2010 - 12:17 PM

  • Technical Artist
  • Posts: 1907
  • Joined: 12-December 09
  • Gender:Male
  • Project:Project AXSX(Sonic Xtreme) + Misc Projects
wow looking really good !
Wondering how you came about the block scales, by eying it or you bust the def wide open ?
This post has been edited by Andrew75: 13 December 2010 - 12:20 PM

#18 User is offline Vinchenz 

Posted 13 December 2010 - 02:16 PM

  • Yo! Hustle! Hustle!
  • Posts: 1397
  • Joined: 10-January 10
  • Gender:Male
  • Location:Canada
  • Project:Unity 5evr
That is very damn cool. DirectX9, huh? I guess I'll still check it out.

With Andrew working on his project and you working on yours, Sonic Xtreme is getting closer at being playable every day year. v.png

#19 User is offline Uhyve 

Posted 13 December 2010 - 02:19 PM

  • Master Procrastinator
  • Posts: 46
  • Joined: 08-November 10
  • Gender:Male
  • Location:Huddersfield, England
  • Project:SX Engine
QUOTE (Andrew75 @ Dec 13 2010, 05:17 PM)
Wondering how you came about the block scales, by eying it or you bust the def wide open ?

Yeah, I mainly just kept messing with values from the DEF files, trying to use them in a way that makes them work the same as the old viewer. Still not sure if the scales are completely right though, the floating creatures at the end of Jade Gully are kind of weirdly shaped...

BIG Edit: Okay, I now think I've figured out scales. There was some slightly weird stuff going on in those DEF files, maybe someone other than me will find this interesting.

Basically, for some reason, scales in the DEF files have a sign (+/-), which scales don't normally have (scales are usually multipliers, so unless you wanna turn something inside out, you don't do it). In this case, they seem to have started with a default scale (say 1.0) and added on the scale from the DEF, so if the scale is -0.5, you would have a scale of 0.5 (halving the size of the cube). On top of that, they weren't using floats (which means no decimal places), so they were using stuff like 128 to mean 1, which is actually pretty standard but I had no idea what 1 was so I kinda had to guess that. Once I figured all that out, I took at a look at the impossibly huge scales (for example, a y scaling value of 8388608, for those Beebot thingys). Turns out, there may have been some obfuscation going on, because I've never heard of anyone doing this for any reason, I had been assuming that the huge scales just meant high/low null, meaning the program defaults to max and minimum scale size. That wasn't it, it's actually kind of hard to explain what was going on, I'll copy and paste some code (read the comments):

CODE
// Set a max scale value
int maxScale = 128;

// Loading in yScale value from DEF file
usedCubes[currentCube].yScale = (float)atoi(num.c_str());

// If the imported value is above the maxScale, it's probably one of those impossible scales
if(abs(usedCubes[currentCube].yScale) > maxScale)
{
    int tempPower = 1;

    while (tempPower <= abs((int)usedCubes[currentCube].yScale))
    {
        // Find the highest power of two that'll fit into the imported scale (well, 1 multiply over)
        tempPower = tempPower * 2;
    }

    // Until we're within the scale range, keep subtracting the smallest power of two that'll fit.
    while(abs(usedCubes[currentCube].yScale) > maxScale)
    {
        // Divide first since we went over last time.
        tempPower = tempPower / 2;

        if (usedCubes[currentCube].yScale > 0) //Depending on whether we're dealing with a +/- scale...
            usedCubes[currentCube].yScale -= tempPower;
        else
            usedCubes[currentCube].yScale += tempPower;
    }
}


I'm actually certain at this point, that this is the correct way to find the scale. The only thing I can't figure out, is the reason for it. Apologies to those who don't find this interesting at all smile.png .

Edit: Heh, I probably should've checked for new posts before making such a large edit...
This post has been edited by Uhyve: 13 December 2010 - 09:52 PM

#20 User is offline Andrew75 

Posted 13 December 2010 - 06:20 PM

  • Technical Artist
  • Posts: 1907
  • Joined: 12-December 09
  • Gender:Male
  • Project:Project AXSX(Sonic Xtreme) + Misc Projects
QUOTE (Uhyve @ Dec 13 2010, 02:19 PM)
QUOTE (Andrew75 @ Dec 13 2010, 05:17 PM)
Wondering how you came about the block scales, by eying it or you bust the def wide open ?

Yeah, I mainly just kept messing with values from the DEF files, trying to use them in a way that makes them work the same as the old viewer. Still not sure if the scales are completely right though, the floating creatures at the end of Jade Gully are kind of weirdly shaped...


In Sanik's viewer the standard blocks appear squashed height wise. right?
I remember SaniK saying that the standard blocks were scaled at 1 x 1 x 1.54 ( 1.54 being depth)
than the fish eye formula and FOV would take care of the rest.( squashed look)

Would love to take some time and research deeper into how the def files define scales for blocks.
however there is no time to do this.
Man......I'd Pee myself If someone did the research into DEF files and documented whats going on in there.

The way I've been going about scales is by carefully looking at screen shots, videos and DEF viewer, and aligning the cubes heights to pixels.
than comparing it to an identical model and texture in the modeling program I use to scale a secondary block up to the point in the reference.
everything seems to be done by .25 .50 .75. and some other scales so far.
This post has been edited by Andrew75: 13 December 2010 - 06:38 PM

#21 User is offline Uhyve 

Posted 13 December 2010 - 08:50 PM

  • Master Procrastinator
  • Posts: 46
  • Joined: 08-November 10
  • Gender:Male
  • Location:Huddersfield, England
  • Project:SX Engine
Ah, I knew I read someone talking about having a standard block size. I actually had that working, but it broke at some point while I was making normal scales work. I'd make a small equation so you wouldn't have to do all the scaling by hand, but I'm not actually that great at proper maths equations... it's actually not too difficult once you understand that bit of code, since after that, you just add it onto 1 and that's your scale.

I actually do feel like I'm understanding more and more about how the DEF files work. I can really see myself getting stuck on Paths though. But then again, I can always release the program as open source (if I haven't already done that by that point), I'm sure someone on the internet would be able to figure it out if I can't.

Edit: Added a download link to the newest version, fresh off the compiler.
This post has been edited by Uhyve: 13 December 2010 - 09:42 PM

#22 User is offline True Dude 

Posted 13 December 2010 - 10:47 PM

  • Posts: 1307
  • Joined: 07-January 06
  • Gender:Male
  • Wiki edits:980
Weird, I tried testing this out, but when I tried clicking on the .exe it wouldn't load. Do I need to launch it in DosBox or something?

#23 User is offline Uhyve 

Posted 13 December 2010 - 10:54 PM

  • Master Procrastinator
  • Posts: 46
  • Joined: 08-November 10
  • Gender:Male
  • Location:Huddersfield, England
  • Project:SX Engine
Nah, it should just work. I should really put in some error messages. But I'd be willing to bet, that's a DirectX error, try installing the DirectX updater (it checks all of the individual DirectX components, for the odd one that is out of date):
DirectX Web Updater

If it's not that, I guess I must've used some DirectX function that you're graphics card can't do, because that's exactly what happens when a program fails to create a renderer.
This post has been edited by Uhyve: 13 December 2010 - 10:56 PM

#24 User is offline True Dude 

Posted 13 December 2010 - 11:01 PM

  • Posts: 1307
  • Joined: 07-January 06
  • Gender:Male
  • Wiki edits:980
QUOTE (Uhyve @ Dec 13 2010, 09:54 PM)
If it's not that, I guess I must've used some DirectX function that you're graphics card can't do, because that's exactly what happens when a program fails to create a renderer.

That explains it then, my graphics card I have right now is a piece of crap, and can't run anything worth a damn. I'll try it when I get a better graphics card, I guess.

#25 User is offline Uhyve 

Posted 13 December 2010 - 11:06 PM

  • Master Procrastinator
  • Posts: 46
  • Joined: 08-November 10
  • Gender:Male
  • Location:Huddersfield, England
  • Project:SX Engine
Yeah, if it's that, I'll try fixing it in the next build, I've got a pretty new computer so I didn't really do much to make sure it'd work on older machines.

Edit: Still not sure whether anyone cares (which is why I'm just editing an old post), but I figure it'll probably be a good idea to document how the DEF files work. Just been figuring out renderflags (I was just specifying specific renderflags originally, which means I was probably missing some stuff). Basically, these are binary flags, I'll give an example of one:

00000010 (2)

So if the 7th number means transparent, the object would be transparent. Say the 6th number is the mirror texture flag, and we wanted a transparent mirrored object, we'd set it like this:

00000110 (5)

The renderflags are basically the same but not binary formatted:

8448

Which means:

00100001 00000000 (8192 + 256)

You can work this out using annoyingly complicated powers of two logic but I won't bore you guys with that. Not totally done with renderflags yet though, as I still need to know what each flag is actually meant to do, I think that in the example above, 256 means mirror texture. I realized this a while back but now I've bothered implementing this, I just realized that the massive scales must be the offset flag, just like the comments hint at in the DEF files (no idea why that never occurred to me).

PS Just got fillcolors working, the DE levels are now starting to look more like levels.
This post has been edited by Uhyve: 15 December 2010 - 10:55 AM

#26 User is offline Uhyve 

Posted 25 December 2010 - 05:35 PM

  • Master Procrastinator
  • Posts: 46
  • Joined: 08-November 10
  • Gender:Male
  • Location:Huddersfield, England
  • Project:SX Engine
Figured I'd release the source as a Christmas present. There's no actual progress since last release and it's super messy since I wasn't actually planning on releasing anything, but at least if I get lazy and stop working on it now, people don't have to start again from scratch.

And yes, it's realllly messy.

http://code.google.com/p/sx-engine/source/detail?r=2
This post has been edited by Uhyve: 25 December 2010 - 05:37 PM

#27 User is offline Andrew75 

Posted 26 December 2010 - 08:33 AM

  • Technical Artist
  • Posts: 1907
  • Joined: 12-December 09
  • Gender:Male
  • Project:Project AXSX(Sonic Xtreme) + Misc Projects
Hey , don't get to lazy now, hope to see this project bare more fruit !
Anyways merry belated X-mas!.
Would have replied yesterday, had a really fulfilling day with family.
This post has been edited by Andrew75: 26 December 2010 - 08:35 AM

#28 User is offline SANiK 

Posted 10 January 2011 - 05:30 AM

  • Posts: 412
  • Joined: 21-September 04
  • Gender:Male
  • Wiki edits:6
QUOTE (Uhyve @ Dec 13 2010, 03:19 PM)
CODE
// Set a max scale value
int maxScale = 128;

// Loading in yScale value from DEF file
usedCubes[currentCube].yScale = (float)atoi(num.c_str());

// If the imported value is above the maxScale, it's probably one of those impossible scales
if(abs(usedCubes[currentCube].yScale) > maxScale)
{
    int tempPower = 1;

    while (tempPower <= abs((int)usedCubes[currentCube].yScale))
    {
        // Find the highest power of two that'll fit into the imported scale (well, 1 multiply over)
        tempPower = tempPower * 2;
    }

    // Until we're within the scale range, keep subtracting the smallest power of two that'll fit.
    while(abs(usedCubes[currentCube].yScale) > maxScale)
    {
        // Divide first since we went over last time.
        tempPower = tempPower / 2;

        if (usedCubes[currentCube].yScale > 0) //Depending on whether we're dealing with a +/- scale...
            usedCubes[currentCube].yScale -= tempPower;
        else
            usedCubes[currentCube].yScale += tempPower;
    }
}


That is alright guess work but it is actually simpler than that.
Try:
CODE
unsigned int combinedScaleOffset=atoi(num.c_str());

//Decompose combined data
usedCubes[currentCube].yScale = 1+(signed short)combinedScaleOffset/128.0f;
usedCubes[currentCube].yOffset = (signed short)(combinedScaleOffset >> 16)/256.0f;


The scale and offset are stored as a combined 32bit number.
The top 16 bits are the relative offset and the bottom 16 bits are the relative scale.

The scale is relative to 1 which you've figured out already and it uses 128.0f accuracy which you've also figured out.
The offset is 256.0f accuracy.


#29 User is offline SANiK 

Posted 10 January 2011 - 06:02 AM

  • Posts: 412
  • Joined: 21-September 04
  • Gender:Male
  • Wiki edits:6
Ofer made up as he went along. There are various functional differences in the DEF format, even though the DEF files show the same file version number.
You might also want to invest time in a nice database to manage the resources.

2) make hexdump actual hex data not strings
3) Palettes lack a Value or HexDump, they use FileName "..."
10) Implement UV orientation (flip/rotate), as well as implement it on the image data level
18) CRYSTAL2.DEF: Trunks of snowball trees aren't transparent

Bugs:
1) Double sided Crystal1 ramp going up... in video it doesn't disappear but it fades out... is this "weak double sided" or "double sided but blocking the camera..."
2) GALAXY1, Playme.def, DE_*, and RAMPS top-plane slope fiasco... they seem to use different settings
3) JG_07 Wood support bridge doesn't uses flat but has auto values... so I use auto... looks good but unsure if actual working of engine
5) Figure out how to use correct value of local shading. Also, is it added to the global shading or overridden?
6) Figure difference between X 90 and X 45 cube types instead of processing them the same...
8) Figure why the shadows seem to be inverted on the X thing... darker side is showing with lighter side behind it... wrong X setup, wrong shadoing, or discard shadows for X type?
9) Why is death egg so light
10) Check Fern_01 for unk2 and ... that fence might be transparent
11) Check out Diamon in Neo_01
12) Screen2 and scrreen1 water animating has blanks - transparent flag/auto alpha on first frame only?
13) TEST_05C.DEF = egypt level
14) Frame size might actually be 1 pixel thick?
15) (Blending) Transparency needs to be implemented for all sides. Not per texture blending but per renderflag blending.
16) Rotatation on renderflag level and texture level needs to be implemented
17) Texture scrolling needs to be implemented
18) Crystal frost, ramps have garbled textures on side
19) Shading algorithm is off
20) Fish eye algorithm is off
21) Some corners missing due to optomization algo I think in X shapes?

CODE
//Layout.c
#include "common.h"

//Defines
//TEXTURE:VALUE001=Flags
//#define LAYOUT_TXTR_GlobalMapping      1
//#define LAYOUT_TXTR_LocalMapping      2
//#define LAYOUT_TXTR_Animation          4
#define LAYOUT_TXTR_Bitmap            8    //Overrides LAYOUT_TXTR_FLAT_FILL & LAYOUT_TXTR_AUTO_FILL
#define LAYOUT_TXTR_FLAT_FILL          16 //Default, if 0
#define LAYOUT_TXTR_AUTO_FILL          32 //Overrides LAYOUT_TXTR_FLAT_FILL
#define LAYOUT_TXTR_FRAME1            64 //Draw X (what's the need for LAYOUT_TXTR_DRAW_X then...?)
#define LAYOUT_TXTR_FRAME2            128 //Draw []
//#define LAYOUT_TXTR_DRAW_X            256 //Undefined? //"Continues animation" is 256 //Might mean Draw_regardless...?

//TEXTURE:VALUE002=Render mode
#define LAYOUT_Opaque                  1
#define LAYOUT_Transparent            2
#define LAYOUT_Blend                  16
#define LAYOUT_Darken                  32    //Undoc
#define LAYOUT_Lighten                64    //Undoc
//#define LAYOUT_Disolve                128
#define LAYOUT_FlipH                  256
#define LAYOUT_FlipV                  512
//#define LAYOUT_Ditr                    1024
#define LAYOUT_AutoTrans              2048
//#define LAYOUT_RemapColors            4096
#define LAYOUT_BestQuality            8192
//#define LAYOUT_Rotate                  16384

//TEXTURE:VALUE003=Palette (Palette Name)
//TEXTURE:VALUE004=Unknown
//TEXTURE:VALUE005=Unknown
//TEXTURE:VALUE006=MaxFrames (New version: Uses stated animation count + Detection; Old version: Used detection to figure out animation count, e.g. if 0.pcx exists try 1.pcx etc.)
//TEXTURE:VALUE007=Unknown
//TEXTURE:VALUE008=Texture File Path
//TEXTURE:VALUE009=Unknown
//TEXTURE:VALUE010=Weight (Variable Name)
//TEXTURE:VALUE011=Elastic (Variable Name)
//TEXTURE:VALUE012=FrictionX (Variable Name)
//TEXTURE:VALUE013=FrictionY (Variable Name)
//TEXTURE:VALUE014=FrictionZ (Variable Name)

//TEXTURE:VALUE015=Physics flags
//#define LAYOUT_AlignPlayer            1
//#define LAYOUT_AlignWorld              2
//#define LAYOUT_RotWorldCW              4
//#define LAYOUT_RotWorldCCW            8
//#define LAYOUT_unknownNameButLetPlayerPass  16

//TEXTURE:VALUE016=Magnet (Variable Name)
//TEXTURE:VALUE017=Fill1 color (Flat fill color, autofill left) (color)
//TEXTURE:VALUE018=Frame1 color (\ color) (|- color) (color)
//TEXTURE:VALUE019=Frame2 color  (/ color) (_| color) (color)
//TEXTURE:VALUE020=Fill2 color (autofill right) (color)
//TEXTURE:VALUE021=Autofill color (color) (enables extra mode?) Color(r,g,b) r=gradient center shift, g=gradient format, b=gradient bar count ...
//TEXTURE:VALUE022=Scroll color (probably direction? X,Y, and speed?)

//CUBE:VALUE001=Flags
//#define  LAYOUT_CDF_EmptyCube          0    //Denotes empty space
#define LAYOUT_CDF_Active              1
//#define LAYOUT_CDF_Sloped              2
//#define LAYOUT_CDF_DSloped            4
#define LAYOUT_CDF_Chkrs              8      //CDF_Chkrs means depending on x,y,z position of tile, the sides of cube interchange
//#define LAYOUT_CDF_Sprite              16
//#define LAYOUT_CDF_Pass                32

//CUBE:VALUE002=Type (0=Cube, 1=Sprite Center, 2=Sprite Front, 3=Cross Y45, 4= Cross Y90, 5=Diamond, 6=Sprite & Shadow, 7=Pyramid)
//CUBE:VALUE003=Back (Texture Name)    //New Version: No texture, means no side
//CUBE:VALUE004=Top (Texture Name)     //New Version: No texture, means no side
//CUBE:VALUE005=Right (Texture Name)    //New Version: No texture, means no side
//CUBE:VALUE006=Bottom (Texture Name)    //New Version: No texture, means no side
//CUBE:VALUE007=Left (Texture Name)    //New Version: No texture, means no side
//CUBE:VALUE008=Front (Texture Name)    //New Version: No texture, means no side
//CUBE:VALUE009=Unknown
//CUBE:VALUE010=Unknown
//CUBE:VALUE011=Attack (0-31?)
//CUBE:VALUE012=Shield (0-31?)
//CUBE:VALUE013=Unknown
//CUBE:VALUE014=Unknown
//CUBE:VALUE015=Slope Direction
////
//CUBE:VALUE016=X Size/Offset
//CUBE:VALUE017=Y Size/Offset
//CUBE:VALUE018=Z Size/Offset
//CUBE:VALUE019=Unknown

//CUBE:VALUE020 (Double Sided),VALUE021 (Partial Face, Old Version Use Only: Faces not to Draw),VALUE022 (Not on grid, Old Version Use Only: For Mesh Optomization),VALUE023 (Auto determination, Old Version Use Only: Engine fills in blanks it seems?)
#define LAYOUT_BACK                    1
#define LAYOUT_TOP                    2
#define LAYOUT_RIGHT                  4
#define LAYOUT_BOTTOM                  8
#define LAYOUT_LEFT                    16
#define LAYOUT_FRONT                  32
//#define LAYOUT_HORIZONTAL              64
//#define LAYOUT_VERTICAL                128
//#define LAYOUT_SIDE                    256

//CUBE:VALUE024=Unknown
//CUBE:VALUE025=Unknown
//CUBE:VALUE026=Swap (Cube #) //Cube ID of new cube to spawn, but when is it triggered?
//CUBE:VALUE027=Unknown
//CUBE:VALUE028=Unknown
//CUBE:VALUE029=RenderFlags Back (Renderflags are the same as texture renderflags but with shading, where shading is upper 16bits); shading seems to be -15 to +15
//CUBE:VALUE030=RenderFlags Top
//CUBE:VALUE031=RenderFlags Right
//CUBE:VALUE032=RenderFlags Bottom
//CUBE:VALUE033=RenderFlags Left
//CUBE:VALUE034=RenderFlags Front

//QUBIX:VALUE001=Layout Texture FileName without FileFormat
//QUBIX:VALUE002=Background Texture FileName without FileFormat
//QUBIX:VALUE003=Unknown
//QUBIX:VALUE004=Unknown
//QUBIX:VALUE005=Specular Color (color)
//QUBIX:VALUE006=Fog Color (color)

//Variables
byte layout_drawPhase=0;
float slopeElevation[16][4]={
//top left, top right, front left, front right
{1.0f, 1.0f,
1.0f, 1.0f}, //0

{1.0f, 0.0f,
1.0f, 0.0f}, //1
{1.0f, 1.0f,
1.0f, 1.0f}, //2
{1.0f, 1.0f,
1.0f, 1.0f}, //3
{0.0f, 1.0f,
0.0f, 1.0f}, //4

{1.0f, 1.0f,
0.0f, 0.0f}, //5
{1.0f, 1.0f,
1.0f, 1.0f}, //6
{1.0f, 1.0f,
1.0f, 1.0f}, //7
{0.0f, 0.0f,
1.0f, 1.0f}, //8

{1.0f, 1.0f,
1.0f, 1.0f}, //9
{1.0f, 1.0f,
1.0f, 1.0f}, //10
{1.0f, 1.0f,
1.0f, 1.0f}, //11
{1.0f, 1.0f,
1.0f, 1.0f}, //12
};

//Functions
sdword layout_getIntValue(byte *a_stringData)
{
  if(a_stringData == NULL) return 0;
  
  //If it's already in numeric form, just return the number
  if((util_isDigit(a_stringData[0])) || (a_stringData[0]=='-')) return atoi(a_stringData);

  //If it's Color(#,#,#) then parse it into 0xRRGGBBFF
  if(strncmp(a_stringData, "Color(", sizeof("Color(")-1) == 0)
  {
    dword red,green,blue;
    sscanf(a_stringData, "Color(%d,%d,%d)", &red, &green, &blue);
    return (((byte)red)<<24) | (((byte)green)<<16) | (((byte)blue)<<8) | 0xFF;
  }
  
  //Else decode the value
  sdword intData=0;
  
  //TEXTURE:VALUE001=Flags
  if(strstr(a_stringData, "TXTR_Bitmap") != NULL) intData|=LAYOUT_TXTR_Bitmap;
  if(strstr(a_stringData, "TXTR_FLAT_FILL") != NULL) intData|=LAYOUT_TXTR_FLAT_FILL;
  if(strstr(a_stringData, "TXTR_AUTO_FILL") != NULL) intData|=LAYOUT_TXTR_AUTO_FILL;
  if(strstr(a_stringData, "TXTR_FRAME1") != NULL) intData|=LAYOUT_TXTR_FRAME1;
  if(strstr(a_stringData, "TXTR_FRAME2") != NULL) intData|=LAYOUT_TXTR_FRAME2;
  
  //CUBE:VALUE029 to inclusive VALUE034
  //TEXTURE:VALUE002=Render mode
  if(strstr(a_stringData, "Opaque") != NULL) intData|=LAYOUT_Opaque;
  if(strstr(a_stringData, "Transparent") != NULL) intData|=LAYOUT_Transparent;
  if(strstr(a_stringData, "FlipH") != NULL) intData|=LAYOUT_FlipH;
  if(strstr(a_stringData, "FlipV") != NULL) intData|=LAYOUT_FlipV;
  if(strstr(a_stringData, "AutoTrans") != NULL) intData|=LAYOUT_AutoTrans;
  if(strstr(a_stringData, "BestQuality") != NULL) intData|=LAYOUT_BestQuality;
  
  //CUBE:VALUE001=Flags
  if(strstr(a_stringData, "CDF_Active") != NULL) intData|=LAYOUT_CDF_Active;
  if(strstr(a_stringData, "CDF_Chkrs") != NULL) intData|=LAYOUT_CDF_Chkrs;
  
  //CUBE:VALUE020 to inclusive VALUE023
  if(strstr(a_stringData, "BACK") != NULL) intData|=LAYOUT_BACK;
  if(strstr(a_stringData, "TOP") != NULL) intData|=LAYOUT_TOP;
  if(strstr(a_stringData, "RIGHT") != NULL) intData|=LAYOUT_RIGHT;
  if(strstr(a_stringData, "BOTTOM") != NULL) intData|=LAYOUT_BOTTOM;
  if(strstr(a_stringData, "LEFT") != NULL) intData|=LAYOUT_LEFT;
  if(strstr(a_stringData, "FRONT") != NULL) intData|=LAYOUT_FRONT;
  
  return intData;
}

void layout_parsePath(byte *a_destString, byte *a_fileDirectory, byte *a_layoutStringData, byte *a_fileFormat)
{
  dword fileDirectoryLen=strlen(a_fileDirectory);
  
  //Prepare the full file path
  memcpy(a_destString, a_fileDirectory, fileDirectoryLen);
  dword stringDataLen=strlen(a_layoutStringData)-2;
  memcpy(&a_destString[fileDirectoryLen], &a_layoutStringData[1], stringDataLen);
  a_destString[fileDirectoryLen+stringDataLen]=0;
  
  //Convert : to backward slash
  byte *charPointer=&a_destString[fileDirectoryLen];
  while(*charPointer)
  {
    if(*charPointer == ':') *charPointer='\\';
    charPointer++;
  }
  
  //Append the file format
  strcat(a_destString, a_fileFormat);  
}

void layout_parseDef(Layout *a_layout, byte *a_fileName)
{
  //Get the size of the file
  dword fileSize=file_getSize(a_fileName);
  
  //Allocate and load the file into a buffer (and convert the buffer into a null terminated string)
  byte *fileBuffer=malloc(fileSize+1);
  file_read(a_fileName, fileBuffer, 0, fileSize);
  fileBuffer[fileSize]=0;
  
  //Remove comments, repetitive whitespace, and shift data down
  byte *fileDestPointer=fileBuffer;
  byte *fileSrcPointer=fileBuffer;
  byte stringMode=0;
  byte commentMode=0;
  byte whitespaceMode=0;
  while(*fileSrcPointer != 0)
  {
    //Is it a comment?
    if((!stringMode) && (!commentMode) && (*fileSrcPointer == '/') && (fileSrcPointer[1] == '/'))
    {
      commentMode=1;
      fileSrcPointer++;
    }
    //Is it a string?
    else if((!commentMode) && (*fileSrcPointer == '"'))
    {
      whitespaceMode=0;
      *fileDestPointer++=*fileSrcPointer++;
      stringMode=!stringMode;
    }
    //Is it inside a comment?
    else if(commentMode)
    {
      if(*fileSrcPointer == '\n')
      {
        *fileDestPointer++=*fileSrcPointer;
        commentMode=!commentMode;
      }
      fileSrcPointer++;
    }
    //Is it duplicate whitespace
    else if((!stringMode) && (whitespaceMode) && ((*fileSrcPointer == '\t') || (*fileSrcPointer == '\r') || (*fileSrcPointer == ' ')))
    {
      //Ignore it
      fileSrcPointer++;
    }
    //Is it all else
    else
    {
      whitespaceMode=((*fileSrcPointer == '\t') || (*fileSrcPointer == '\r') || (*fileSrcPointer == ' ') || (*fileSrcPointer == '\n'));
      *fileDestPointer++=*fileSrcPointer++;
    }
  }
  *fileDestPointer++=0; //Add terminating null
  
  //Calculate new fileSize
  fileSize=(dword)fileDestPointer-(dword)fileBuffer;
  
  //Parse the file
  dword fileOffset=0;
  while(1)
  {
    if(fileOffset >= fileSize) break;
    
    //Is it data?
    if(strncmp(&fileBuffer[fileOffset], "NEW_", sizeof("NEW_")-1) == 0)
    {
      fileOffset+=(sizeof("NEW_")-1);
      
      //Get type
      dword typeOffset=fileOffset;
      dword typeLen=0;
      while((util_isLetter(fileBuffer[fileOffset])) && (fileBuffer[fileOffset] != 0))
      {
        typeLen++;
        fileOffset++;
      }
      if(fileBuffer[fileOffset] == 0) break;
      
      //fwrite(&fileBuffer[typeOffset], typeLen, 1, stdout);printf(":");
      
      //Get name
      while((fileBuffer[fileOffset] != '"') && (fileBuffer[fileOffset] != 0)) fileOffset++;
      if(fileBuffer[fileOffset] == 0) break;
      
      dword nameOffset=fileOffset;
      dword nameLen=2; //Include the first " and the last "
      
      fileOffset++; //Skip the "
      while((fileBuffer[fileOffset] != '"') && (fileBuffer[fileOffset] != 0))
      {
        nameLen++;
        fileOffset++;
      }
      if(fileBuffer[fileOffset] == 0) break;
      
      //fwrite(&fileBuffer[nameOffset], nameLen, 1, stdout);printf("\n");
      
      //Get {
      while((fileBuffer[fileOffset] != '{') && (fileBuffer[fileOffset] != 0)) fileOffset++;
      if(fileBuffer[fileOffset] == 0) break;
      fileOffset++; //Skip the {
      
      //Figure which type it is
      List *typeListPointer=NULL;
      //VARIABLE
      if((typeLen == (sizeof("VARIABLE")-1)) && !strncmp(&fileBuffer[typeOffset], "VARIABLE", typeLen)){typeListPointer=&a_layout->variableList;}
      //PALETTE
      else if((typeLen == (sizeof("PALETTE")-1)) && !strncmp(&fileBuffer[typeOffset], "PALETTE", typeLen)){typeListPointer=&a_layout->paletteList;}
      //CAMERA
      else if((typeLen == (sizeof("CAMERA")-1)) && !strncmp(&fileBuffer[typeOffset], "CAMERA", typeLen)){typeListPointer=&a_layout->cameraList;}
      //TEXTURE
      else if((typeLen == (sizeof("TEXTURE")-1)) && !strncmp(&fileBuffer[typeOffset], "TEXTURE", typeLen)){typeListPointer=&a_layout->textureList;}
      //QUBIX
      else if((typeLen == (sizeof("QUBIX")-1)) && !strncmp(&fileBuffer[typeOffset], "QUBIX", typeLen)){typeListPointer=&a_layout->qubixList;}
      //ACTOR
      else if((typeLen == (sizeof("ACTOR")-1)) && !strncmp(&fileBuffer[typeOffset], "ACTOR", typeLen)){typeListPointer=&a_layout->actorList;}
      //PATH
      else if((typeLen == (sizeof("PATH")-1)) && !strncmp(&fileBuffer[typeOffset], "PATH", typeLen)){typeListPointer=&a_layout->pathList;}
      //CUBE
      else if((typeLen == (sizeof("CUBE")-1)) && !strncmp(&fileBuffer[typeOffset], "CUBE", typeLen)){typeListPointer=&a_layout->cubeList;}
      //UNKNOWN
      else
      {
        printf("Warning: Unknown Data Type: NEW_");
        fwrite(&fileBuffer[typeOffset], typeLen, 1, stdout);
        printf("\n");
      }
      
      //Add the proper list entry
      if(typeListPointer != NULL)
      {
        LayoutElement *layoutElementPointer=malloc(sizeof(LayoutElement));

        //Copy the element name to the element template, add term null to name
        layoutElementPointer->elementName=malloc(nameLen+1);
        memcpy(layoutElementPointer->elementName, &fileBuffer[nameOffset], nameLen);
        layoutElementPointer->elementName[nameLen]=0;
        
        //Initiate the two lists in the element
        list_init(&layoutElementPointer->valueList);
        list_init(&layoutElementPointer->hexDumpList);
        
        //Add the list element to the typeListPointer
        list_addEntry(typeListPointer, layoutElementPointer);
        
        //Scan for values or hexdumps, whilst getting }
        while((fileBuffer[fileOffset] != '}') && (fileBuffer[fileOffset] != 0))
        {
          if(fileOffset >= fileSize) break;

          //Is it a value?
          if(strncmp(&fileBuffer[fileOffset], "Value", sizeof("Value")-1) == 0)
          {
            fileOffset+=(sizeof("Value")-1);
            
            //Get id
            dword id=0;
            while((!util_isDigit(fileBuffer[fileOffset])) && (fileBuffer[fileOffset] != 0)) fileOffset++; //Approach the first digit
            if(fileBuffer[fileOffset] == 0) break;
            while((util_isDigit(fileBuffer[fileOffset])) && (fileBuffer[fileOffset] != 0)) //Process digit
            {
              id*=10;
              id+=fileBuffer[fileOffset]-='0';
              fileOffset++;
            }
            if(fileBuffer[fileOffset] == 0) break;
            
            //Get ,
            while((fileBuffer[fileOffset] != ',') && (fileBuffer[fileOffset] != 0)) fileOffset++;
            if(fileBuffer[fileOffset] == 0) break;
            fileOffset++; //Skip the ,
            
            //Get stringData
            dword dataOffset=fileOffset;
            dword dataLen=0;
            while((fileBuffer[fileOffset] != '\n') && (fileBuffer[fileOffset] != 0))
            {              
              dataLen++;
              fileOffset++;
            }
            if(fileBuffer[fileOffset] == 0) break;
            
            //Trim string data on Left
            while((dataLen > 0) && ((fileBuffer[dataOffset] == ' ') || (fileBuffer[dataOffset] == '\t') || (fileBuffer[dataOffset] == '\r')))
            {
              dataOffset++;
              dataLen--;
            }
            
            //Trim string data on Right
             while((dataLen > 0) && ((fileBuffer[dataOffset+dataLen-1] == ' ') || (fileBuffer[dataOffset+dataLen-1] == '\t') || (fileBuffer[dataOffset+dataLen-1] == '\r')))
            {
              dataLen--;
            }
            
            //Add Value to list
            {
              LayoutValue *layoutValuePointer=malloc(sizeof(LayoutValue));
              
              //Set the id
              layoutValuePointer->id=id;
              
              //Copy the value string data name to the element template, add term null to data
              layoutValuePointer->stringData=malloc(dataLen+1);
              memcpy(layoutValuePointer->stringData, &fileBuffer[dataOffset], dataLen);
              layoutValuePointer->stringData[dataLen]=0;
              
              //Generate int value
              layoutValuePointer->intData=layout_getIntValue(layoutValuePointer->stringData);
              
              //Add to listElement's value list
              list_addEntry(&layoutElementPointer->valueList, layoutValuePointer);
            }
          }
           //Is it a hexdump?
          else if(strncmp(&fileBuffer[fileOffset], "HexDump", sizeof("HexDump")-1) == 0)
          {
            fileOffset+=(sizeof("HexDump")-1);
            
            //Get hexDumpData
            while((fileBuffer[fileOffset] != '"') && (fileBuffer[fileOffset] != 0)) fileOffset++;
            if(fileBuffer[fileOffset] == 0) break;
            
            dword hexOffset=fileOffset;
            dword hexLen=2; //Include the first " and the last "
            
            fileOffset++; //Skip the "
            while((fileBuffer[fileOffset] != '"') && (fileBuffer[fileOffset] != 0))
            {
              hexLen++;
              fileOffset++;
            }
            if(fileBuffer[fileOffset] == 0) break;
            
            //Add hexDump to list
            {
              byte *hexDumpData=malloc(hexLen+1);
              memcpy(hexDumpData, &fileBuffer[hexOffset], hexLen);
              hexDumpData[hexLen]=0;
              
              //Add to listElement's hexdump list
              list_addEntry(&layoutElementPointer->hexDumpList, hexDumpData);
            }
          }
          //All else
          else
          {
            fileOffset++;
          }
        }
        if(fileBuffer[fileOffset] == 0) break;
        fileOffset++; //Skip the {
      }
    }
    //All else
    else
    {
      fileOffset++;
    }
  }
  
  //Free the file from memory
  free(fileBuffer);
}

LayoutValue *layout_getLayoutValue(LayoutElement *a_layoutElement, dword a_id)
{
  dword entryIndex=0;
  dword entryTotal=list_getEntryCount(&a_layoutElement->valueList);
  while(entryIndex < entryTotal)
  {
    LayoutValue *layoutValuePointer=list_getEntry(&a_layoutElement->valueList, entryIndex);
    
    //Found a matching id
    if(layoutValuePointer->id == a_id)
    {
      return layoutValuePointer;
    }
    
    //Goto the next entry
    entryIndex++;
  }
  
  //No such id found, return NULL
  return NULL;
}

void layout_parsePcx(Layout *a_layout, byte *a_fileName)
{
  if(a_layout == NULL) return;
  
  //Load the .pcx (ignoring the palette)
  FILE *file=fopen(a_fileName, "rb");
  if(!file) return;
  
  //Read the header
  if(fgetc(file)!=0xA) return;
  if(fgetc(file)!=0x5) return;
  if(fgetc(file)!=1) return;
  if(fgetc(file)!=8) return;
  
  //Get the image size
  word tempWidth;
  word tempHeight;
  word imageWidth;
  word imageHeight;
  fread(&tempWidth, 2, 1, file);
  fread(&tempHeight, 2, 1, file);
  fread(&imageWidth, 2, 1, file);
  fread(&imageHeight, 2, 1, file);
  imageWidth-=tempWidth;
  imageHeight-=tempHeight;
  imageWidth++;
  imageHeight++;
  if(!imageWidth || !imageHeight) return;
  
  //Expect 1 color plane
  fseek(file, 53, SEEK_CUR);
  if(fgetc(file)!=1) return;
  
  //Get the number of bytes per line
  word pcxBytesPerLine;
  fread(&pcxBytesPerLine, 2, 1, file);
  
  //Skip to the image data
  fseek(file, 128, SEEK_SET);
  
  //Calculate some constants
  dword dataSize=imageWidth * imageHeight;

  //Allocate the buffer where the image is to be copied
  byte *pcxBitmap=malloc(dataSize);

  //Decode the image
  {
    dword y=0;
    while(y < imageHeight)
    {
      //Setup the address at which the bitmap's written to
      byte *bitmap_rw=&pcxBitmap[y*imageWidth];
      
      //Loop till a break's requested
      dword subPixelCount=0;
      while(1)
      {
        //Get the count and read values
        byte rleData=fgetc(file);  
        byte rleValue=rleData;
        byte rleCount=1;
        if(0xC0 == (rleData & 0xC0))
        {
          rleCount=rleData & 0x3F;
          rleValue=fgetc(file);
        }
      
        //Decompress the RLE
        while(rleCount)
        {
          subPixelCount++;
          
          //8-bit transfer
          *bitmap_rw++=rleValue;
          
          //Check if the scanline's done
          if(subPixelCount == imageWidth)
          {
            subPixelCount=0;
            goto reset;
          }
          
          //Decrement the counter
          rleCount--;
        }
      }
      reset:
      y++;
    }
  }

  //Find the true map dimensions
  dword sectionWidth=0;
  dword sectionHeight=0;
  {
    dword count=0;
    byte *bitmap_rw=NULL;

    //Find the width (5th palette color = separator)
    bitmap_rw=pcxBitmap+1;
     for(count=0; *bitmap_rw == 5; count++)
      bitmap_rw++;
    a_layout->layoutWidth=count;
    
    //Find the height (5th palette color = separator)
    bitmap_rw=pcxBitmap+imageWidth;
    for(count=0; *bitmap_rw == 5; count++)
      bitmap_rw+=imageWidth;
    a_layout->layoutDepth=count; //Depth
    
    //Calculate the Height
    sectionWidth=imageWidth/(a_layout->layoutWidth+2);
    sectionHeight=imageHeight/(a_layout->layoutDepth+2);
    a_layout->layoutHeight=sectionWidth * sectionHeight; //Height
  }
  
  //Get the layout map buffer ready
  a_layout->layoutMap=malloc(a_layout->layoutWidth * a_layout->layoutHeight * a_layout->layoutDepth);
  
  //Copy the tiles in the correct order
  {
    sdword x,y,z,w;
    byte *bitmap_rw=pcxBitmap;
    for(y=0; y<sectionHeight; y++)
    {
      bitmap_rw+=imageWidth;
      for(z=a_layout->layoutDepth-1; z>=0; z--)
      {
        for(x=0; x<sectionWidth; x++)
        {
          bitmap_rw++; //Skip separator
          byte *sync=&a_layout->layoutMap[(y*sectionWidth*a_layout->layoutWidth*a_layout->layoutDepth)+(x*a_layout->layoutWidth*a_layout->layoutDepth)+(z*a_layout->layoutWidth)];
          for(w=0; w<a_layout->layoutWidth; w++)
          {
            *sync++=~(*bitmap_rw++);
          }
          bitmap_rw++; //Skip separator
        }
      }
      bitmap_rw+=imageWidth;
    }
  }
  
  //Close the file
  fclose(file);
}

dword layout_doesNeighborHaveNormalSide(Layout *a_layout, byte a_tileId, word a_x, word a_y, word a_z, word a_side)
{ //True = side is existant and non-scaled
  
  byte *sync;
  
  //Draw the map
  if(a_z >= a_layout->layoutDepth) return 0;
  if(a_x >= a_layout->layoutWidth) return 0;
  if(a_y >= a_layout->layoutHeight) return 0;
  
  //Does cube exist?
  sync=&a_layout->layoutMap[(a_y*a_layout->layoutWidth*a_layout->layoutDepth)+(a_z*a_layout->layoutWidth)+a_x];
  if(*sync)
  {
    //If the cubes are the same return true
    //Since this function will only be called if the original is unmodified
    if(a_tileId == *sync) return -1;
    
    //Read cube type
    LayoutElement *layoutElementPointer=NULL;
    LayoutValue *layoutValuePointer=NULL;
    
    //Get cube element id
    layoutElementPointer=list_getEntry(&a_layout->cubeList, (*sync)-1);
    if(layoutElementPointer == NULL) return 0;
    
    //Read cubeType
    dword cubeType=0;
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 2);
    if(layoutValuePointer != NULL) cubeType=layoutValuePointer->intData;
    
    //Type is not cube
    if(cubeType != 0) return 0;
    
    //Check if cube has any scaling or offsetting
    ///Nonexistant value's means values should be 0 by default
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 16);
    if(layoutValuePointer != NULL)
    {
      if(layoutValuePointer->intData != 0) return 0;
    }
    
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 17);
    if(layoutValuePointer != NULL)
    {
      if(layoutValuePointer->intData != 0) return 0;
    }
    
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 18);
    if(layoutValuePointer != NULL)
    {
      if(layoutValuePointer->intData != 0) return 0;
    }
    
    //Check if side is to be drawn by checking if there's a texture
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 3+a_side);
    if(layoutValuePointer == NULL) return 0;
    
    //Check that the cube is not a slope
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 15);
    if(layoutValuePointer != NULL)
    {
      //Side qube is a slope
      if(layoutValuePointer->intData != 0) return 0;  
    }
    
    //Side exists and is not scaled
    return -1;
  }

  //No qube exists
  return 0;
}

float layout_getDropDistance(Layout *a_layout, byte *a_slopeType, word a_x, word a_y, word a_z)
{
  byte *sync;
  sdword y=0;
  float height=0.0f;
  *a_slopeType=0;
  
  //Skip the current piece
  a_y--;
  
  //Draw the map
  if(a_z >= a_layout->layoutDepth) return -1;
  if(a_x >= a_layout->layoutWidth) return -1;
  if(a_y >= a_layout->layoutHeight) return -1;
  
  for(y=a_y; y >= 0; y--) //Down to up
  {
    sync=&a_layout->layoutMap[(y*a_layout->layoutWidth*a_layout->layoutDepth)+(a_z*a_layout->layoutWidth)+a_x];
    if(*sync)
    {
      //Read cube type
      LayoutElement *layoutElementPointer=NULL;
      LayoutValue *layoutValuePointer=NULL;
      
      //Get cube element id
      layoutElementPointer=list_getEntry(&a_layout->cubeList, (*sync)-1);
      if(layoutElementPointer == NULL) return -1;
      
      //Read cubeType
      dword cubeType=0;
      layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 2);
      if(layoutValuePointer != NULL) cubeType=layoutValuePointer->intData;
      
      if(cubeType == 0)
      {
        //Return slope type
        layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 15);
        if(layoutValuePointer != NULL) //Only apply the slope transformation if the cubeType is "Cube"
        {
          *a_slopeType=layoutValuePointer->intData;
        }
        return height;
      }
    }
    height+=1.0f;
  }
  
  return -1;
}


CODE
void layout_drawCube(Layout *a_layout, float *a_matrix, byte a_tileId, word a_x, word a_y, word a_z)
{
  LayoutElement *layoutElementPointer=NULL;
  LayoutValue *layoutValuePointer=NULL;
  
  //Do not draw if far away
  {
    float tempX=a_x;
    float tempY=a_y;
    float tempZ=a_z*1.54f;
    
    //Apply the matrices to the point
    vector_matrix_mul(tempX, tempY, tempZ, a_matrix);
    
    float distanceFromCamera=tempZ;//sqrt((tempX*tempX)+(tempY*tempY)+(tempZ*tempZ));
    //if(distanceFromCamera > 24.0f) return; //Do not draw far away cubes
    //if(distanceFromCamera >= 30.0f) { printf("%f %f %f\n", tempX, tempY, tempZ); return; } //Do not draw far away cubes
    if(distanceFromCamera < 0.0f) return;
    else if(distanceFromCamera >= 30.0f) return;
  }
  
  //Get cube element id
  layoutElementPointer=list_getEntry(&a_layout->cubeList, a_tileId-1);
  if(layoutElementPointer == NULL) return;
  
  //Read cubeFlags
  dword cubeFlags=0;
  layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 1);
  if(layoutValuePointer != NULL) cubeFlags=layoutValuePointer->intData;
  
  //Is the cube active? If not don't draw it
  if(!(cubeFlags & LAYOUT_CDF_Active))
    return;
  
  //Read cubeType
  dword cubeType=0;
  layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 2);
  if(layoutValuePointer != NULL) cubeType=layoutValuePointer->intData;
  
  //Generate cube texture lookup id based on side (for use with Value 003+[sideId])
  byte textureToSide[6]={0,1,2,3,4,5};
  
  //Is the cube in checkers mode? If so, remix the sides based on position of tile
  if(cubeFlags & LAYOUT_CDF_Chkrs)
  {
    if(((a_x+a_y+a_z)&1))
    {
      textureToSide[0]=5;
      textureToSide[1]=3;
      textureToSide[2]=4;
      textureToSide[3]=1;
      textureToSide[4]=2;
      textureToSide[5]=0;
    }
  }
  
  //Fetch cube scale
  dword unmodifiedShape=-1;
  float xScale=1.0f;
  float yScale=1.0f;
  float zScale=1.0f;
  float xOffset=0.0f;
  float yOffset=0.0f;
  float zOffset=0.0f;
  {
    dword value=0;
  
    //Get xso
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 16);
    if(layoutValuePointer != NULL)
    {
      value=layoutValuePointer->intData;
      if(value != 0) unmodifiedShape=0;
      xScale=1+(sword)(value>>0)/128.0f;
      xOffset=(sword)(value>>16)/256.0f;
    }
    
    //Get yso
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 17);
    if(layoutValuePointer != NULL)
    {
      value=layoutValuePointer->intData;
      if(value != 0) unmodifiedShape=0;
      yScale=1+(sword)(value>>0)/128.0f;
      yOffset=(sword)(value>>16)/256.0f;
    }
    
    //Get zso
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 18);
    if(layoutValuePointer != NULL)
    {
      value=layoutValuePointer->intData;
      if(value != 0) unmodifiedShape=0;
      zScale=1+(sword)(value>>0)/128.0f;
      zOffset=(sword)(value>>16)/256.0f;
    }
  }
  
  //Initiate the cube mesh
  Vertex cubeVertex[9]=
  {
    {xOffset+a_x-0.5f*xScale, yOffset+a_y+0.5f*yScale, (zOffset+a_z)*1.54f-0.5f*1.54f*zScale}, //Front, top-left
    {xOffset+a_x+0.5f*xScale, yOffset+a_y+0.5f*yScale, (zOffset+a_z)*1.54f-0.5f*1.54f*zScale}, //Front, top-right
    {xOffset+a_x-0.5f*xScale, yOffset+a_y-0.5f*yScale, (zOffset+a_z)*1.54f-0.5f*1.54f*zScale}, //Front, bottom-left
    {xOffset+a_x+0.5f*xScale, yOffset+a_y-0.5f*yScale, (zOffset+a_z)*1.54f-0.5f*1.54f*zScale}, //Front, bottom-right
    
    {xOffset+a_x-0.5f*xScale, yOffset+a_y+0.5f*yScale, (zOffset+a_z)*1.54f+0.5f*1.54f*zScale}, //Back, top-left
    {xOffset+a_x+0.5f*xScale, yOffset+a_y+0.5f*yScale, (zOffset+a_z)*1.54f+0.5f*1.54f*zScale}, //Back, top-right
    {xOffset+a_x-0.5f*xScale, yOffset+a_y-0.5f*yScale, (zOffset+a_z)*1.54f+0.5f*1.54f*zScale}, //Back, bottom-left
    {xOffset+a_x+0.5f*xScale, yOffset+a_y-0.5f*yScale, (zOffset+a_z)*1.54f+0.5f*1.54f*zScale}, //Back, bottom-right
    
    {xOffset+a_x, yOffset+a_y, (zOffset+a_z)*1.54f}, //Center
  };
  
  //Deform cube mesh if there's a slope
  layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 15);
  if((layoutValuePointer != NULL) && (cubeType == 0)) //Only apply the slope transformation if the cubeType is "Cube"
  {
    dword value=layoutValuePointer->intData;
    if(value != 0) unmodifiedShape=0;
  
    //Use the appropriate transformation
    switch(value)
    {
      case 0: break; //None
      
      case 5: //Left plane: back down to front
        memcpy(&cubeVertex[0], &cubeVertex[2], sizeof(Vertex));
        memcpy(&cubeVertex[1], &cubeVertex[3], sizeof(Vertex));
        break;
        
      case 8: //Left plane: front down to back
        memcpy(&cubeVertex[5], &cubeVertex[1], sizeof(Vertex));
        memcpy(&cubeVertex[4], &cubeVertex[0], sizeof(Vertex));
        break;
        
      case 6: //Left plane: back up to front
        memcpy(&cubeVertex[2], &cubeVertex[0], sizeof(Vertex));
        memcpy(&cubeVertex[3], &cubeVertex[1], sizeof(Vertex));
        break;
        
      case 7: //Left plane: front up to back
        memcpy(&cubeVertex[6], &cubeVertex[4], sizeof(Vertex));
        memcpy(&cubeVertex[7], &cubeVertex[5], sizeof(Vertex));
        break;
        
      case 1: //Front plane: left down to right
        memcpy(&cubeVertex[1], &cubeVertex[3], sizeof(Vertex));
        memcpy(&cubeVertex[5], &cubeVertex[7], sizeof(Vertex));
        break;
        
      case 4: //Front plane: right down to left
        memcpy(&cubeVertex[0], &cubeVertex[1], sizeof(Vertex));
        memcpy(&cubeVertex[4], &cubeVertex[5], sizeof(Vertex));
        break;
        
      case 2: //Front plane: left up to right
        memcpy(&cubeVertex[3], &cubeVertex[2], sizeof(Vertex));
        memcpy(&cubeVertex[7], &cubeVertex[6], sizeof(Vertex));
        break;
        
      case 3: //Front plane: right up to left
        memcpy(&cubeVertex[2], &cubeVertex[0], sizeof(Vertex));
        memcpy(&cubeVertex[6], &cubeVertex[4], sizeof(Vertex));
        break;
      
      //Questionable setup *fix*
      case 12: //Top plane: back down to front
        memcpy(&cubeVertex[1], &cubeVertex[0], sizeof(Vertex));
        memcpy(&cubeVertex[3], &cubeVertex[2], sizeof(Vertex));
        break;
      
      case 9: //Top plane: back up to front //10-12
        memcpy(&cubeVertex[0], &cubeVertex[4], sizeof(Vertex));
        memcpy(&cubeVertex[2], &cubeVertex[6], sizeof(Vertex));
        break;
      
      case 11: //Top plane: front down to back //12-10
        //memcpy(&cubeVertex[5], &cubeVertex[1], sizeof(Vertex));
        //memcpy(&cubeVertex[7], &cubeVertex[3], sizeof(Vertex));
        memcpy(&cubeVertex[5], &cubeVertex[4], sizeof(Vertex));
        memcpy(&cubeVertex[7], &cubeVertex[6], sizeof(Vertex));
        break;
        
      case 10: //Top plane: front up to back
        memcpy(&cubeVertex[4], &cubeVertex[5], sizeof(Vertex));
        memcpy(&cubeVertex[6], &cubeVertex[7], sizeof(Vertex));
        break;
        
      /*case 9: //Top plane: back down to front
        memcpy(&cubeVertex[1], &cubeVertex[0], sizeof(Vertex));
        memcpy(&cubeVertex[3], &cubeVertex[2], sizeof(Vertex));
        break;
        
      case 12: //Top plane: front down to back
        memcpy(&cubeVertex[5], &cubeVertex[1], sizeof(Vertex));
        memcpy(&cubeVertex[7], &cubeVertex[3], sizeof(Vertex));
        memcpy(&cubeVertex[1], &cubeVertex[0], sizeof(Vertex));
        memcpy(&cubeVertex[3], &cubeVertex[2], sizeof(Vertex));
        memcpy(&cubeVertex[0], &cubeVertex[4], sizeof(Vertex));
        memcpy(&cubeVertex[2], &cubeVertex[6], sizeof(Vertex));
        break;
        
      case 10: //Top plane: back up to front
        memcpy(&cubeVertex[0], &cubeVertex[4], sizeof(Vertex));
        memcpy(&cubeVertex[2], &cubeVertex[6], sizeof(Vertex));
        memcpy(&cubeVertex[4], &cubeVertex[5], sizeof(Vertex));
        memcpy(&cubeVertex[6], &cubeVertex[7], sizeof(Vertex));
        memcpy(&cubeVertex[5], &cubeVertex[1], sizeof(Vertex));
        memcpy(&cubeVertex[7], &cubeVertex[3], sizeof(Vertex));
        break;
        
      case 11: //Top plane: front up to back
        memcpy(&cubeVertex[4], &cubeVertex[5], sizeof(Vertex));
        memcpy(&cubeVertex[6], &cubeVertex[7], sizeof(Vertex));
        break;*/
    }
  }
  
  //Transform the mesh based on the cubeType
  if(cubeType == 0) //Cube
  {
    //return;
  }
  else if(cubeType == 1) //Sprite Center
  {
    //return;
  }
  else if(cubeType == 2) //Sprite Front (unk)
  {
    //printf("unk2 found\n");
    //system("pause");
    //return;
    //Found in Fern_01
  }
  else if(cubeType == 3) //45 deg X
  {
    //return;
  }
  else if(cubeType == 4) //90 deg X
  {
    //return;
  }
  else if(cubeType == 5) //Diamond (unk)
  {
    //printf("unk5 found\n");
    //system("pause");
    //return;
    //Found in Neo_01
  }
  else if(cubeType == 6) //Sprite with Shadow
  {
    //return;
  }
  else if(cubeType == 7) //Pyramid (unk)
  {
    //printf("unk7 found\n");
    //system("pause");
    return;
  }
  
  //Figure which sides are double sided
  dword doubleSidedFlag=0;
  layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 20);
  if(layoutValuePointer != NULL) doubleSidedFlag=layoutValuePointer->intData;
  
  //Setup draw settings
  texture_enable(true);
  texture_color(255, 255, 255);
  texture_smooth(false);
  
  //Front Face
  do
  {
    //Choose texture, if there's no texture do not draw
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 3+textureToSide[5]);
    if(layoutValuePointer == NULL) break;
    List *listPointer=&a_layout->layoutTexture[layoutValuePointer->intData].frameList;
    texture_set(list_getEntry(listPointer, (a_layout->clock)%list_getEntryCount(listPointer)));
    
    //Can this face be optimized?
    //Can only be applied to shapes that are not modified via scaling or offsetting
    if(unmodifiedShape)
    {
      //Do not draw if the in the front shape's back face is visible
      if(layout_doesNeighborHaveNormalSide(a_layout, a_tileId, a_x, a_y, a_z-1, 0)) break;
    }
    
    //Is it blended? Don't draw it on the first draw stage
    if(layout_drawPhase == 0)
    {
      LayoutElement *textureElement=a_layout->layoutTexture[layoutValuePointer->intData].textureElement;
      if(textureElement != NULL)
      {
        layoutValuePointer=layout_getLayoutValue(textureElement, 2);
        if(layoutValuePointer != NULL)
        {
          if(layoutValuePointer->intData & LAYOUT_Blend) break;
        }
      }
    }
    
    //Is it double sided?
    texture_noclip(doubleSidedFlag & LAYOUT_FRONT);

    //Apply global shading
    sword shadeDelta=255+a_layout->layoutShading[5];
    shadeDelta=(byte)shadeDelta;
    
    //Apply local shading
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 29+5);
    if(layoutValuePointer != NULL)
    {
      sbyte localShadeDelta=layoutValuePointer->intData >> 16;
      shadeDelta+=localShadeDelta*16;
      
      //Is it blended
      if(layoutValuePointer->intData & LAYOUT_Blend) texture_alpha(128);
    }
    if(shadeDelta < 0) shadeDelta=0; else if(shadeDelta > 255) shadeDelta=255;
    if(cubeType == 0) texture_color(shadeDelta, shadeDelta, shadeDelta);
    else texture_color(255, 255, 255);
    
    //Setup uvs
    float uvAx=0.0f, uvAy=0.0f;
    float uvBx=0.0f, uvBy=1.0f;
    float uvCx=1.0f, uvCy=1.0f;
    float uvDx=1.0f, uvDy=0.0f;
    
    //Manage UVs
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 29+textureToSide[5]);
    if(layoutValuePointer != NULL)
    {
      if(layoutValuePointer->intData & LAYOUT_FlipH)
      {
        float temp;
        
        temp=uvAx;uvAx=uvDx;uvDx=temp;
        temp=uvAy;uvAy=uvDy;uvDy=temp;
        temp=uvBx;uvBx=uvCx;uvCx=temp;
        temp=uvBy;uvBy=uvCy;uvCy=temp;
      }
      if(layoutValuePointer->intData & LAYOUT_FlipV)
      {
        float temp;
        
        temp=uvAx;uvAx=uvBx;uvBx=temp;
        temp=uvAy;uvAy=uvBy;uvBy=temp;
        temp=uvDx;uvDx=uvCx;uvCx=temp;
        temp=uvDy;uvDy=uvCy;uvCy=temp;
      }
    }
    
    //Draw
    if((cubeType == 4) || (cubeType == 3))
    { //Back-right leg
      glBegin(GL_QUADS);
        glTexCoord2f(uvDx, uvDy); reflex(a_matrix, cubeVertex[8].x, cubeVertex[0].y, cubeVertex[8].z);
        glTexCoord2f(uvCx, uvCy); reflex(a_matrix, cubeVertex[8].x, cubeVertex[2].y, cubeVertex[8].z);
        glTexCoord2f(uvBx, uvBy); reflex(a_matrix, cubeVertex[7].x, cubeVertex[7].y, cubeVertex[7].z);
        glTexCoord2f(uvAx, uvAy); reflex(a_matrix, cubeVertex[5].x, cubeVertex[5].y, cubeVertex[5].z);
      glEnd();
    }
    else
    {
      glBegin(GL_QUADS);
        glTexCoord2f(uvAx, uvAy); reflex(a_matrix, cubeVertex[0].x, cubeVertex[0].y, cubeVertex[0].z);
        glTexCoord2f(uvBx, uvBy); reflex(a_matrix, cubeVertex[2].x, cubeVertex[2].y, cubeVertex[2].z);
        glTexCoord2f(uvCx, uvCy); reflex(a_matrix, cubeVertex[3].x, cubeVertex[3].y, cubeVertex[3].z);
        glTexCoord2f(uvDx, uvDy); reflex(a_matrix, cubeVertex[1].x, cubeVertex[1].y, cubeVertex[1].z);
      glEnd();
    }
  } while(0);
  
  //Top Face
  do
  {
    texture_alpha(255);
    
    //Choose texture, if there's no texture do not draw
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 3+textureToSide[1]);
    if(layoutValuePointer == NULL) break;
    List *listPointer=&a_layout->layoutTexture[layoutValuePointer->intData].frameList;
    texture_set(list_getEntry(listPointer, (a_layout->clock)%list_getEntryCount(listPointer)));
    
    //Can this face be optimized?
    //Can only be applied to shapes that are not modified via scaling or offsetting
    if(unmodifiedShape)
    {
      //Do not draw if the above shape's bottom face is visible
      if(layout_doesNeighborHaveNormalSide(a_layout, a_tileId, a_x, a_y+1, a_z, 3)) break;
    }
    
    //Is it blended? Don't draw it on the first draw stage
    if(layout_drawPhase == 0)
    {
      LayoutElement *textureElement=a_layout->layoutTexture[layoutValuePointer->intData].textureElement;
      if(textureElement != NULL)
      {
        layoutValuePointer=layout_getLayoutValue(textureElement, 2);
        if(layoutValuePointer != NULL)
        {
          if(layoutValuePointer->intData & LAYOUT_Blend) break;
        }
      }
    }
    
    //Is it double sided?
    texture_noclip(doubleSidedFlag & LAYOUT_TOP);
    
    //Apply global shading
    sword shadeDelta=255+a_layout->layoutShading[1];
    shadeDelta=(byte)shadeDelta;
    
    //Apply local shading
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 29+1);
    if(layoutValuePointer != NULL)
    {
      sbyte localShadeDelta=layoutValuePointer->intData >> 16;
      shadeDelta+=localShadeDelta*16;
    }
    if(shadeDelta < 0) shadeDelta=0; else if(shadeDelta > 255) shadeDelta=255;
    if(cubeType == 0) texture_color(shadeDelta, shadeDelta, shadeDelta);
    else texture_color(255, 255, 255);
    
    //Setup uvs
    float uvAx=1.0f, uvAy=1.0f;
    float uvBx=1.0f, uvBy=0.0f;
    float uvCx=0.0f, uvCy=0.0f;
    float uvDx=0.0f, uvDy=1.0f;
    
    //Manage UVs
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 29+textureToSide[1]);
    if(layoutValuePointer != NULL)
    {
      if(layoutValuePointer->intData & LAYOUT_FlipH)
      {
        float temp;
        temp=uvAx;uvAx=uvDx;uvDx=temp;
        temp=uvAy;uvAy=uvDy;uvDy=temp;
        temp=uvBx;uvBx=uvCx;uvCx=temp;
        temp=uvBy;uvBy=uvCy;uvCy=temp;
      }
      if(layoutValuePointer->intData & LAYOUT_FlipV)
      {
        float temp;
        
        temp=uvAx;uvAx=uvBx;uvBx=temp;
        temp=uvAy;uvAy=uvBy;uvBy=temp;
        temp=uvDx;uvDx=uvCx;uvCx=temp;
        temp=uvDy;uvDy=uvCy;uvCy=temp;
      }
    }
    
    //Draw
    glBegin(GL_QUADS);
      glTexCoord2f(uvAx, uvAy); reflex(a_matrix, cubeVertex[1].x, cubeVertex[1].y, cubeVertex[1].z);
      glTexCoord2f(uvBx, uvBy); reflex(a_matrix, cubeVertex[5].x, cubeVertex[5].y, cubeVertex[5].z);
      glTexCoord2f(uvCx, uvCy); reflex(a_matrix, cubeVertex[4].x, cubeVertex[4].y, cubeVertex[4].z);
      glTexCoord2f(uvDx, uvDy); reflex(a_matrix, cubeVertex[0].x, cubeVertex[0].y, cubeVertex[0].z);
    glEnd();
  } while(0);
  
  //Back Face
  do
  {
    texture_alpha(255);
    
    //Choose texture, if there's no texture do not draw
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 3+textureToSide[0]);
    if(layoutValuePointer == NULL) break;
    List *listPointer=&a_layout->layoutTexture[layoutValuePointer->intData].frameList;
    texture_set(list_getEntry(listPointer, (a_layout->clock)%list_getEntryCount(listPointer)));
    
    //Can this face be optimized?
    //Can only be applied to shapes that are not modified via scaling or offsetting
    if(unmodifiedShape)
    {
      //Do not draw if the in the back shape's front face is visible
      if(layout_doesNeighborHaveNormalSide(a_layout, a_tileId, a_x, a_y, a_z+1, 5)) break;
    }
    
    //Is it blended? Don't draw it on the first draw stage
    if(layout_drawPhase == 0)
    {
      LayoutElement *textureElement=a_layout->layoutTexture[layoutValuePointer->intData].textureElement;
      if(textureElement != NULL)
      {
        layoutValuePointer=layout_getLayoutValue(textureElement, 2);
        if(layoutValuePointer != NULL)
        {
          if(layoutValuePointer->intData & LAYOUT_Blend) break;
        }
      }
    }
    
    //Is it double sided?
    texture_noclip(doubleSidedFlag & LAYOUT_BACK);
    
    //Apply global shading
    sword shadeDelta=255+a_layout->layoutShading[0];
    shadeDelta=(byte)shadeDelta;
    
    //Apply local shading
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 29+0);
    if(layoutValuePointer != NULL)
    {
      sbyte localShadeDelta=layoutValuePointer->intData >> 16;
      shadeDelta+=localShadeDelta*16;
    }
    if(shadeDelta < 0) shadeDelta=0; else if(shadeDelta > 255) shadeDelta=255;
    if(cubeType == 0) texture_color(shadeDelta, shadeDelta, shadeDelta);
    else texture_color(255, 255, 255);
    
    //Setup uvs
    float uvAx=0.0f, uvAy=0.0f;
    float uvBx=0.0f, uvBy=1.0f;
    float uvCx=1.0f, uvCy=1.0f;
    float uvDx=1.0f, uvDy=0.0f;
    
    //Manage UVs
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 29+textureToSide[0]);
    if(layoutValuePointer != NULL)
    {
      if(layoutValuePointer->intData & LAYOUT_FlipH)
      {
        float temp;
        temp=uvAx;uvAx=uvDx;uvDx=temp;
        temp=uvAy;uvAy=uvDy;uvDy=temp;
        temp=uvBx;uvBx=uvCx;uvCx=temp;
        temp=uvBy;uvBy=uvCy;uvCy=temp;
      }
      if(layoutValuePointer->intData & LAYOUT_FlipV)
      {
        float temp;
        
        temp=uvAx;uvAx=uvBx;uvBx=temp;
        temp=uvAy;uvAy=uvBy;uvBy=temp;
        temp=uvDx;uvDx=uvCx;uvCx=temp;
        temp=uvDy;uvDy=uvCy;uvCy=temp;
      }
    }
    
    //Draw
    if((cubeType == 4) || (cubeType == 3))
    { //Front-left leg
      glBegin(GL_QUADS);
        glTexCoord2f(uvAx, uvAy); reflex(a_matrix, cubeVertex[0].x, cubeVertex[0].y, cubeVertex[0].z);
        glTexCoord2f(uvBx, uvBy); reflex(a_matrix, cubeVertex[2].x, cubeVertex[2].y, cubeVertex[2].z);
        glTexCoord2f(uvCx, uvCy); reflex(a_matrix, cubeVertex[8].x, cubeVertex[2].y, cubeVertex[8].z);
        glTexCoord2f(uvDx, uvDy); reflex(a_matrix, cubeVertex[8].x, cubeVertex[0].y, cubeVertex[8].z);
      glEnd();
    }
    else
    {
      glBegin(GL_QUADS);
        glTexCoord2f(uvAx, uvAy); reflex(a_matrix, cubeVertex[5].x, cubeVertex[5].y, cubeVertex[5].z);
        glTexCoord2f(uvBx, uvBy); reflex(a_matrix, cubeVertex[7].x, cubeVertex[7].y, cubeVertex[7].z);
        glTexCoord2f(uvCx, uvCy); reflex(a_matrix, cubeVertex[6].x, cubeVertex[6].y, cubeVertex[6].z);
        glTexCoord2f(uvDx, uvDy); reflex(a_matrix, cubeVertex[4].x, cubeVertex[4].y, cubeVertex[4].z);
      glEnd();
    }
  } while(0);
  
  //Bottom Face
  do
  {
    texture_alpha(255);
    
    //Choose texture, if there's no texture do not draw
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 3+textureToSide[3]);
    if(layoutValuePointer == NULL) break;
    List *listPointer=&a_layout->layoutTexture[layoutValuePointer->intData].frameList;
    texture_set(list_getEntry(listPointer, (a_layout->clock)%list_getEntryCount(listPointer)));
    
    //Can this face be optimized?
    //Can only be applied to shapes that are not modified via scaling or offsetting
    if(unmodifiedShape)
    {
      //Do not draw if the bottom shape's top face is visible
      if(layout_doesNeighborHaveNormalSide(a_layout, a_tileId, a_x, a_y-1, a_z, 1)) break;
    }
    
    //Is it blended? Don't draw it on the first draw stage
    if(layout_drawPhase == 0)
    {
      LayoutElement *textureElement=a_layout->layoutTexture[layoutValuePointer->intData].textureElement;
      if(textureElement != NULL)
      {
        layoutValuePointer=layout_getLayoutValue(textureElement, 2);
        if(layoutValuePointer != NULL)
        {
          if(layoutValuePointer->intData & LAYOUT_Blend) break;
        }
      }
    }
    
    //Is it double sided?
    texture_noclip(doubleSidedFlag & LAYOUT_BOTTOM);
    
    //Apply global shading
    sword shadeDelta=255+a_layout->layoutShading[3];
    shadeDelta=(byte)shadeDelta;
    
    //Apply local shading
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 29+3);
    if(layoutValuePointer != NULL)
    {
      sbyte localShadeDelta=layoutValuePointer->intData >> 16;
      shadeDelta+=localShadeDelta*16;
    }
    if(shadeDelta < 0) shadeDelta=0; else if(shadeDelta > 255) shadeDelta=255;
    if(cubeType == 0) texture_color(shadeDelta, shadeDelta, shadeDelta);
    else texture_color(255, 255, 255);
    
    //Darken?
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 29+3);
    if(layoutValuePointer != NULL)
    {
      if(layoutValuePointer->intData & LAYOUT_Darken) texture_color(0, 0, 0);
    }
    
    //Setup uvs
    float uvAx=0.0f, uvAy=0.0f;
    float uvBx=1.0f, uvBy=0.0f;
    float uvCx=1.0f, uvCy=1.0f;
    float uvDx=0.0f, uvDy=1.0f;
    
    //Manage UVs
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 29+textureToSide[3]);
    if(layoutValuePointer != NULL)
    {
      if(layoutValuePointer->intData & LAYOUT_FlipH)
      {
        float temp;
        temp=uvAx;uvAx=uvDx;uvDx=temp;
        temp=uvAy;uvAy=uvDy;uvDy=temp;
        temp=uvBx;uvBx=uvCx;uvCx=temp;
        temp=uvBy;uvBy=uvCy;uvCy=temp;
      }
      if(layoutValuePointer->intData & LAYOUT_FlipV)
      {
        float temp;
        
        temp=uvAx;uvAx=uvBx;uvBx=temp;
        temp=uvAy;uvAy=uvBy;uvBy=temp;
        temp=uvDx;uvDx=uvCx;uvCx=temp;
        temp=uvDy;uvDy=uvCy;uvCy=temp;
      }
    }
    
    //Draw
    if((cubeType == 6) || (cubeType == 1) || (cubeType == 2))
    { //Draw a Shadow? (Special case for a sprite)
      
      //Set texture color to black
      texture_color(0, 0, 0);
      texture_alpha(100);
      
      //Find how far to drop the shadow
      byte slopeOfProjectPlane=0;
      float shadowDropDistance=layout_getDropDistance(a_layout, &slopeOfProjectPlane, a_x, a_y, a_z);
      
      //Do not draw if shadowDropDistance is negative
      if(shadowDropDistance < 0) break;
      
      //Draw a bottom face facing up
      glBegin(GL_QUADS);
        glTexCoord2f(uvAx, uvAy); reflex(a_matrix, cubeVertex[6].x, a_y-0.45f-shadowDropDistance-(1.0f-slopeElevation[slopeOfProjectPlane][0])*0.75f, cubeVertex[6].z);
        glTexCoord2f(uvDx, uvDy); reflex(a_matrix, cubeVertex[2].x, a_y-0.45f-shadowDropDistance-(1.0f-slopeElevation[slopeOfProjectPlane][2])*0.75f, cubeVertex[2].z);
        glTexCoord2f(uvCx, uvCy); reflex(a_matrix, cubeVertex[3].x, a_y-0.45f-shadowDropDistance-(1.0f-slopeElevation[slopeOfProjectPlane][3])*0.75f, cubeVertex[3].z);
        glTexCoord2f(uvBx, uvBy); reflex(a_matrix, cubeVertex[7].x, a_y-0.45f-shadowDropDistance-(1.0f-slopeElevation[slopeOfProjectPlane][1])*0.75f, cubeVertex[7].z);
      glEnd();
    }
    else
    {
      glBegin(GL_QUADS);
        glTexCoord2f(uvAx, uvAy); reflex(a_matrix, cubeVertex[6].x, cubeVertex[6].y, cubeVertex[6].z);
        glTexCoord2f(uvBx, uvBy); reflex(a_matrix, cubeVertex[7].x, cubeVertex[7].y, cubeVertex[7].z);
        glTexCoord2f(uvCx, uvCy); reflex(a_matrix, cubeVertex[3].x, cubeVertex[3].y, cubeVertex[3].z);
        glTexCoord2f(uvDx, uvDy); reflex(a_matrix, cubeVertex[2].x, cubeVertex[2].y, cubeVertex[2].z);
      glEnd();
    }
  } while(0);
  
  //Right face
  do
  {
    texture_alpha(255);
    
    //Choose texture, if there's no texture do not draw
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 3+textureToSide[2]);
    if(layoutValuePointer == NULL) break;
    List *listPointer=&a_layout->layoutTexture[layoutValuePointer->intData].frameList;
    texture_set(list_getEntry(listPointer, (a_layout->clock)%list_getEntryCount(listPointer)));
    
    //Can this face be optimized?
    //Can only be applied to shapes that are not modified via scaling or offsetting
    if(unmodifiedShape)
    {
      //Do not draw if the right shape's left face is visible
      if(layout_doesNeighborHaveNormalSide(a_layout, a_tileId, a_x+1, a_y, a_z, 4)) break;
    }
    
    //Is it blended? Don't draw it on the first draw stage
    if(layout_drawPhase == 0)
    {
      LayoutElement *textureElement=a_layout->layoutTexture[layoutValuePointer->intData].textureElement;
      if(textureElement != NULL)
      {
        layoutValuePointer=layout_getLayoutValue(textureElement, 2);
        if(layoutValuePointer != NULL)
        {
          if(layoutValuePointer->intData & LAYOUT_Blend) break;
        }
      }
    }
    
    //Is it double sided?
    texture_noclip(doubleSidedFlag & LAYOUT_RIGHT);
    
    //Apply global shading
    sword shadeDelta=255+a_layout->layoutShading[2];
    shadeDelta=(byte)shadeDelta;
    
    //Apply local shading
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 29+2);
    if(layoutValuePointer != NULL)
    {
      sbyte localShadeDelta=layoutValuePointer->intData >> 16;
      shadeDelta+=localShadeDelta*16;
    }
    if(shadeDelta < 0) shadeDelta=0; else if(shadeDelta > 255) shadeDelta=255;
    if(cubeType == 0) texture_color(shadeDelta, shadeDelta, shadeDelta);
    else texture_color(255, 255, 255);
    
    //Setup uvs
    float uvAx=1.0f, uvAy=1.0f;
    float uvBx=1.0f, uvBy=0.0f;
    float uvCx=0.0f, uvCy=0.0f;
    float uvDx=0.0f, uvDy=1.0f;
    
    //Manage UVs
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 29+textureToSide[2]);
    if(layoutValuePointer != NULL)
    {
      if(layoutValuePointer->intData & LAYOUT_FlipH)
      {
        float temp;
        temp=uvAx;uvAx=uvDx;uvDx=temp;
        temp=uvAy;uvAy=uvDy;uvDy=temp;
        temp=uvBx;uvBx=uvCx;uvCx=temp;
        temp=uvBy;uvBy=uvCy;uvCy=temp;
      }
      if(layoutValuePointer->intData & LAYOUT_FlipV)
      {
        float temp;
        
        temp=uvAx;uvAx=uvBx;uvBx=temp;
        temp=uvAy;uvAy=uvBy;uvBy=temp;
        temp=uvDx;uvDx=uvCx;uvCx=temp;
        temp=uvDy;uvDy=uvCy;uvCy=temp;
      }
    }
    
    //Draw
    if((cubeType == 4) || (cubeType == 3))
    { //Front-right leg
      glBegin(GL_QUADS);
        glTexCoord2f(uvDx, uvDy); reflex(a_matrix, cubeVertex[3].x, cubeVertex[3].y, cubeVertex[3].z);
        glTexCoord2f(uvCx, uvCy); reflex(a_matrix, cubeVertex[1].x, cubeVertex[1].y, cubeVertex[1].z);
        glTexCoord2f(uvBx, uvBy); reflex(a_matrix, cubeVertex[8].x, cubeVertex[0].y, cubeVertex[8].z);
        glTexCoord2f(uvAx, uvAy); reflex(a_matrix, cubeVertex[8].x, cubeVertex[2].y, cubeVertex[8].z);
      glEnd();
    }
    else
    {
      glBegin(GL_QUADS);
        glTexCoord2f(uvAx, uvAy); reflex(a_matrix, cubeVertex[7].x, cubeVertex[7].y, cubeVertex[7].z);
        glTexCoord2f(uvBx, uvBy); reflex(a_matrix, cubeVertex[5].x, cubeVertex[5].y, cubeVertex[5].z);
        glTexCoord2f(uvCx, uvCy); reflex(a_matrix, cubeVertex[1].x, cubeVertex[1].y, cubeVertex[1].z);
        glTexCoord2f(uvDx, uvDy); reflex(a_matrix, cubeVertex[3].x, cubeVertex[3].y, cubeVertex[3].z);
      glEnd();
    }
  } while(0);
  
  //Left Face
  do
  {
    texture_alpha(255);
    
    //Choose texture, if there's no texture do not draw
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 3+textureToSide[4]);
    if(layoutValuePointer == NULL) break;
    List *listPointer=&a_layout->layoutTexture[layoutValuePointer->intData].frameList;
    texture_set(list_getEntry(listPointer, (a_layout->clock)%list_getEntryCount(listPointer)));
    
    //Can this face be optimized?
    //Can only be applied to shapes that are not modified via scaling or offsetting
    if(unmodifiedShape)
    {
      //Do not draw if the left shape's right face is visible
      if(layout_doesNeighborHaveNormalSide(a_layout, a_tileId, a_x-1, a_y, a_z, 2)) break;
    }
    
    //Is it blended? Don't draw it on the first draw stage
    if(layout_drawPhase == 0)
    {
      LayoutElement *textureElement=a_layout->layoutTexture[layoutValuePointer->intData].textureElement;
      if(textureElement != NULL)
      {
        layoutValuePointer=layout_getLayoutValue(textureElement, 2);
        if(layoutValuePointer != NULL)
        {
          if(layoutValuePointer->intData & LAYOUT_Blend) break;
        }
      }
    }
    
    //Is it double sided?
    texture_noclip(doubleSidedFlag & LAYOUT_LEFT);
    
    //Apply global shading
    sword shadeDelta=255+a_layout->layoutShading[4];
    shadeDelta=(byte)shadeDelta;
    
    //Apply local shading
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 29+4);
    if(layoutValuePointer != NULL)
    {
      sbyte localShadeDelta=layoutValuePointer->intData >> 16;
      shadeDelta+=localShadeDelta*16;
    }
    if(shadeDelta < 0) shadeDelta=0; else if(shadeDelta > 255) shadeDelta=255;
    if(cubeType == 0) texture_color(shadeDelta, shadeDelta, shadeDelta);
    else texture_color(255, 255, 255);
    
    //Setup uvs
    float uvAx=1.0f, uvAy=0.0f;
    float uvBx=0.0f, uvBy=0.0f;
    float uvCx=0.0f, uvCy=1.0f;
    float uvDx=1.0f, uvDy=1.0f;
    
    //Manage UVs
    layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 29+textureToSide[4]);
    if(layoutValuePointer != NULL)
    {
      if(layoutValuePointer->intData & LAYOUT_FlipH)
      {
        float temp;
        
        temp=uvAx;uvAx=uvBx;uvBx=temp;
        temp=uvAy;uvAy=uvBy;uvBy=temp;
        temp=uvDx;uvDx=uvCx;uvCx=temp;
        temp=uvDy;uvDy=uvCy;uvCy=temp;
      }
      if(layoutValuePointer->intData & LAYOUT_FlipV)
      {
        float temp;
        
        temp=uvAx;uvAx=uvDx;uvDx=temp;
        temp=uvAy;uvAy=uvDy;uvDy=temp;
        temp=uvBx;uvBx=uvCx;uvCx=temp;
        temp=uvBy;uvBy=uvCy;uvCy=temp;
      }
    }
    
    //Draw
    if((cubeType == 4) || (cubeType == 3))
    {
      glBegin(GL_QUADS);
        glTexCoord2f(uvAx, uvAy); reflex(a_matrix, cubeVertex[8].x, cubeVertex[0].y, cubeVertex[8].z);
        glTexCoord2f(uvBx, uvBy); reflex(a_matrix, cubeVertex[4].x, cubeVertex[4].y, cubeVertex[4].z);
        glTexCoord2f(uvCx, uvCy); reflex(a_matrix, cubeVertex[6].x, cubeVertex[6].y, cubeVertex[6].z);
        glTexCoord2f(uvDx, uvDy); reflex(a_matrix, cubeVertex[8].x, cubeVertex[2].y, cubeVertex[8].z);
      glEnd();
    }
    else
    {
      glBegin(GL_QUADS);
        glTexCoord2f(uvAx, uvAy); reflex(a_matrix, cubeVertex[0].x, cubeVertex[0].y, cubeVertex[0].z);
        glTexCoord2f(uvBx, uvBy); reflex(a_matrix, cubeVertex[4].x, cubeVertex[4].y, cubeVertex[4].z);
        glTexCoord2f(uvCx, uvCy); reflex(a_matrix, cubeVertex[6].x, cubeVertex[6].y, cubeVertex[6].z);
        glTexCoord2f(uvDx, uvDy); reflex(a_matrix, cubeVertex[2].x, cubeVertex[2].y, cubeVertex[2].z);
      glEnd();
    }
  } while(0);
  
  //Reset draw settings
  texture_noclip(false);
  texture_alpha(255);
  texture_color(255, 255, 255);
}

void layout_draw(Layout *a_layout, float *a_matrix)
{
  byte *sync;
  sdword x=0;
  sdword y=0;
  sdword z=0;
  
  //Draw the bg
  /*if((layout_drawPhase == 0) && (a_layout->layoutBackgroundTexture))
  {
    camera_2D();
    texture_color(255, 255, 255);
    texture_smooth(true);
    glDisable(GL_LIGHTING);
    texture_set(a_layout->layoutBackgroundTexture);
    //else texture_set(&tile_dummy);
    glBegin(GL_QUADS);
      glTexCoord2f(0, 0); glVertex2f(0, 0);
      glTexCoord2f(0, 1); glVertex2f(0, PH);
      glTexCoord2f(1, 1); glVertex2f(PW, PH);
      glTexCoord2f(1, 0); glVertex2f(PW, 0);
    glEnd();
    glEnable(GL_LIGHTING);
    glDisable(GL_LIGHTING);
    camera_3D(45);
  }*/

  //Draw the map
  for(z=a_layout->layoutDepth-1; z >= 0; z--) //Back to front
  {
    for(y=0; y < a_layout->layoutHeight; y++) //Down to up
    {
      sync=&a_layout->layoutMap[(y*a_layout->layoutWidth*a_layout->layoutDepth)+(z*a_layout->layoutWidth)];
      for(x=0; x < a_layout->layoutWidth; x++)
      {
        if(*sync)
        {
          layout_drawCube(a_layout, a_matrix, *sync++, x, y, z);
        } else sync++;
      }
    }
  }
  
  layout_drawPhase=!layout_drawPhase;
}

void layout_init(Layout *a_layout)
{
  //Initiate the lists
  list_init(&a_layout->variableList);
  list_init(&a_layout->paletteList);
  list_init(&a_layout->cameraList);
  list_init(&a_layout->textureList);
  list_init(&a_layout->qubixList);
  list_init(&a_layout->actorList);
  list_init(&a_layout->pathList);
  list_init(&a_layout->cubeList);
  
  //Set the proper NULLs
  a_layout->clock=0;
  a_layout->layoutTexture=NULL;
  a_layout->layoutWidth=0;
  a_layout->layoutHeight=0;
  a_layout->layoutDepth=0;
  a_layout->layoutMap=NULL;
  a_layout->layoutBackgroundTexture=NULL;
  memset(a_layout->layoutShading, 0, sizeof(a_layout->layoutShading));
}

void layout_loadDef(Layout *a_layout, byte *a_defFileName)
{
  //Parse the DEF file
  layout_parseDef(a_layout, a_defFileName);
}

void layout_build(Layout *a_layout, byte *a_pcxBaseDirPath)
{
  byte stringBuffer[512]={0};
  
  //Texture:Process and load all the textures
  do
  {
    dword textureTotal=list_getEntryCount(&a_layout->textureList);
    if(!textureTotal) break;
    
    //Create the memory for the textures
    a_layout->layoutTexture=malloc(sizeof(LayoutTexture) * textureTotal);
    
    //Process each
    {
      dword textureCounter=0;
      while(textureCounter < textureTotal)
      {
        LayoutElement *layoutElementPointer=list_getEntry(&a_layout->textureList, textureCounter);
        LayoutValue *layoutValuePointer=NULL;
        Image image;
        
        //Initiate the LayoutTexture
        list_init(&a_layout->layoutTexture[textureCounter].frameList);
        a_layout->layoutTexture[textureCounter].textureElement=layoutElementPointer;
        
        //Read texture type
        dword textureType=0;
        layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 1);
        if(layoutValuePointer != NULL) textureType=layoutValuePointer->intData;
        
        //Read texture render mode information
        dword textureRenderMode=0;
        layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 2);
        if(layoutValuePointer != NULL) textureRenderMode=layoutValuePointer->intData;
        
        //Read shade colors
        dword shadeLeftColor=0xFF;
        dword shadeRightColor=0xFF;
        dword frameLeftColor=0xFF;
        dword frameRightColor=0xFF;
        
        //Fetch shade left color
        layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 17);
        if(layoutValuePointer != NULL) shadeLeftColor=layoutValuePointer->intData;
        
        //Fetch shade right color
        layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 20);
        if(layoutValuePointer != NULL) shadeRightColor=layoutValuePointer->intData;
        
        //Fetch frame left color
        layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 18);
        if(layoutValuePointer != NULL) frameLeftColor=layoutValuePointer->intData;
        
        //Fetch frame right color
        layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 19);
        if(layoutValuePointer != NULL) frameRightColor=layoutValuePointer->intData;
        
        //Get ready to load all frames
        dword frameCounter=0;
        bool firstFrameHasAlpha=1;
        while(1)
        {
          bool lastFrameSuccess=0;
          
          //Do we load a bitmap or use a fresh texture?
          if(textureType & LAYOUT_TXTR_Bitmap)
          {
            //Read texture file name
            layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 8);

            //A file path is stated
            if(layoutValuePointer != NULL)
            {
              //Prepare the full file path
              if(frameCounter == 0)
                layout_parsePath(stringBuffer, a_pcxBaseDirPath, layoutValuePointer->stringData, ".pcx");
              else
              {
                //Mutate file path
                dword pathLen=strlen(stringBuffer)-4;
                
                if(util_isDigit(stringBuffer[pathLen-1]))
                {
                  if(stringBuffer[pathLen-1] == '9')
                  {
                    stringBuffer[pathLen-1]='0';
                    if(util_isDigit(stringBuffer[pathLen-2]))
                    {
                      if(stringBuffer[pathLen-2] == '9')
                      {
                        stringBuffer[pathLen-2]='0';
                        if(util_isDigit(stringBuffer[pathLen-3]))
                        {
                          if(stringBuffer[pathLen-3] == '9')
                            break;
                          else stringBuffer[pathLen-3]++;
                        }
                      }
                      else stringBuffer[pathLen-2]++;
                    }
                    else break;
                  }
                  else stringBuffer[pathLen-1]++;
                }
                else break; //Can't mutate
              }
              
              //Attempt to load frame
              if(image_loadPcx(&image, stringBuffer, 1))
              {
                //Do we use transparency? //AutoTrans always
                if(/*(textureRenderMode & LAYOUT_AutoTrans) &&*/ firstFrameHasAlpha)
                {
                  if(frameCounter==0)
                    firstFrameHasAlpha=image_autoAlpha(&image); //Always apply autotrans
                  else
                    image_autoAlpha(&image);
                }
                
                //Increment the frame count
                frameCounter++;
                  
                //Success
                lastFrameSuccess=1;
              }
            }
            
            //Load a dummy frame if frameCounter is 0
            if(frameCounter == 0)
            {
              image_create(&image, 32, 32, 1);
              image_fillFlat(&image, 0x8080A0FF);
            }
            else if(lastFrameSuccess == 0)
              break;  //If last frame was not a success and we already loaded 1 frame, break
          }
          else if(textureType & LAYOUT_TXTR_AUTO_FILL)
          {
            //Auto fill
            image_create(&image, 32, 32, 1);
            image_fillAuto(&image, shadeLeftColor, shadeRightColor);
          }
          else //LAYOUT_TXTR_FLAT_FILL is default
          {
            //Flat fill
            image_create(&image, 32, 32, 1);
            //image_fillFlat(&image, shadeLeftColor);
            image_fillAuto(&image, shadeLeftColor, shadeRightColor); //MAKE JG07 WOOD look pretty since it's flat but not auto...
          }
          
          //Does the image have a box frame?
          if(textureType & LAYOUT_TXTR_FRAME2)
          {
            image_drawFrame(&image, frameLeftColor, frameRightColor);
          }
          
          //Does the image have a X frame?
          if(textureType & LAYOUT_TXTR_FRAME1)
          {
            image_drawCross(&image, frameLeftColor, frameRightColor);
          }
          
          //Do we blend?
          if(textureRenderMode & LAYOUT_Blend)
          {
            image_blendAlpha(&image);
          }
          
          //Process the image and make it a texture
          Texture *texture=malloc(sizeof(Texture));
          texture_fromImage(texture, &image);
          image_destroy(&image);
          
          list_addEntry(&a_layout->layoutTexture[textureCounter].frameList, texture);
        
          //If last frame was not a success, break
          if(lastFrameSuccess == 0) break;
        }
        
        //Goto next texture
        textureCounter++;
      }
    }
  } while(0);
  
  //Qubix:Load the layout & background image
  do
  {
    LayoutElement *layoutElementPointer=NULL;
    LayoutValue *layoutValuePointer=NULL;
    
    //Load the layout
    {
      //Get first qubix element
      layoutElementPointer=list_getEntry(&a_layout->qubixList, 0);
      if(layoutElementPointer == NULL) break;
      
      //Get layout texture file name
      layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 1);
      if(layoutValuePointer == NULL) break;
      
      //Prepare the full file path
      layout_parsePath(stringBuffer, a_pcxBaseDirPath, layoutValuePointer->stringData, ".pcx");
      
      //Parse
      layout_parsePcx(a_layout, stringBuffer);
    }
    
    //Load background image
    {
      layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 2);
      if(layoutValuePointer == NULL) break;
      
      //Prepare the full file path
      layout_parsePath(stringBuffer, a_pcxBaseDirPath, layoutValuePointer->stringData, ".pcx");
      
      //Load image
      Image image;
      if(!image_loadPcx(&image, stringBuffer, 0)) break;
      
      a_layout->layoutBackgroundTexture=malloc(sizeof(Texture));
      texture_fromImage(a_layout->layoutBackgroundTexture, &image);
      
      image_destroy(&image);
    }
  } while(0);
  
  //Cube:Link the textures to the cubes by storing the engine texture's index in the intData of the cube's texture reference
  do
  {
    dword cubeTotal=list_getEntryCount(&a_layout->cubeList);
    dword textureTotal=list_getEntryCount(&a_layout->textureList);
    if(!cubeTotal) break;
    if(!textureTotal) break;
    
    //Loop through each cube
    dword cubeCounter=0;
    while(cubeCounter < cubeTotal)
    {
      LayoutElement *layoutElementPointer=list_getEntry(&a_layout->cubeList, cubeCounter);
      LayoutValue *layoutValuePointer=NULL;
        
      //Loop through each side
      dword sideCounter=0;
      while(sideCounter < 6) //6 sides in a cube
      {
        //See if there's a texture set for the said side
        layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 3+sideCounter);
        
         if(layoutValuePointer != NULL)
        {
          //A texture is set, now we must find the texture by matching names
          dword textureCounter=0;
          while(textureCounter < textureTotal)
          {
            LayoutElement *layoutSubElementPointer=list_getEntry(&a_layout->textureList, textureCounter);
            
            if(strcmp(layoutValuePointer->stringData, layoutSubElementPointer->elementName) == 0)
            {
              //We have found a match, link textures
              layoutValuePointer->intData=textureCounter;
              break;
            }
            
            //Goto next texture
            textureCounter++;
          }
          
          //Has a match failed to be found? A cube with a texture given that's not in the actual texture list at all
          //Just let it be
        }
        
        //Goto next side
        sideCounter++;
      }
      
      //Goto next cube
      cubeCounter++;
    }
  } while(0);
  
  //Variable:Find some important variables and transfer their values over
  do
  {
    dword variableTotal=list_getEntryCount(&a_layout->variableList);
    if(!variableTotal) break;
    
    //Loop through each variable
    dword variableCounter=0;
    while(variableCounter < variableTotal)
    {
      LayoutElement *layoutElementPointer=list_getEntry(&a_layout->variableList, variableCounter);
      LayoutValue *layoutValuePointer=NULL;

      //Scan to see if it's a key variable
      if(strcmp(layoutElementPointer->elementName, "\"SYS_BackShading\"") == 0)
      {
        //Get the value
        layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 2);
        if(layoutValuePointer != NULL) a_layout->layoutShading[0]=layoutValuePointer->intData;
      }
      else if(strcmp(layoutElementPointer->elementName, "\"SYS_TopShading\"") == 0)
      {
        //Get the value
        layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 2);
        if(layoutValuePointer != NULL) a_layout->layoutShading[1]=layoutValuePointer->intData;
      }
      else if(strcmp(layoutElementPointer->elementName, "\"SYS_RightShading\"") == 0)
      {
        //Get the value
        layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 2);
        if(layoutValuePointer != NULL) a_layout->layoutShading[2]=layoutValuePointer->intData;
      }
      else if(strcmp(layoutElementPointer->elementName, "\"SYS_BottomShading\"") == 0)
      {
        //Get the value
        layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 2);
        if(layoutValuePointer != NULL) a_layout->layoutShading[3]=layoutValuePointer->intData;
      }
      else if(strcmp(layoutElementPointer->elementName, "\"SYS_LeftShading\"") == 0)
      {
        //Get the value
        layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 2);
        if(layoutValuePointer != NULL) a_layout->layoutShading[4]=layoutValuePointer->intData;
      }
      else if(strcmp(layoutElementPointer->elementName, "\"SYS_FrontShading\"") == 0)
      {
        //Get the value
        layoutValuePointer=layout_getLayoutValue(layoutElementPointer, 2);
        if(layoutValuePointer != NULL) a_layout->layoutShading[5]=layoutValuePointer->intData;
      }
      
      //Goto next variable
      variableCounter++;
    }
  } while(0);
}

void layout_debug(Layout *a_layout)
{
  //VARIABLE
  {
    dword entryIndex=0;
    dword entryTotal=list_getEntryCount(&a_layout->variableList);
    while(entryIndex < entryTotal)
    {
      LayoutElement *layoutElementPointer=list_getEntry(&a_layout->variableList, entryIndex);
      
      //Print element name
      printf("[VARIABLE:%s]\n", layoutElementPointer->elementName);
      
      //Loop through element value list
      {
        dword entryIndex=0;
        dword entryTotal=list_getEntryCount(&layoutElementPointer->valueList);
        while(entryIndex < entryTotal)
        {
          LayoutValue *layoutValuePointer=list_getEntry(&layoutElementPointer->valueList, entryIndex);
          
          //Print value id & data
          printf("%d=%s\n", layoutValuePointer->id, layoutValuePointer->stringData);
          
          //Goto the next entry
          entryIndex++;
        }
      }
      
      //Loop through element hexdump list
      {
        dword entryIndex=0;
        dword entryTotal=list_getEntryCount(&layoutElementPointer->hexDumpList);
        while(entryIndex < entryTotal)
        {
          byte *hexDump=list_getEntry(&layoutElementPointer->hexDumpList, entryIndex);
          
          //Print value id & data
          printf("HexDump=%s\n", hexDump);
          
          //Goto the next entry
          entryIndex++;
        }
      }
      
      //Goto the next entry
      entryIndex++;
    }
  }
  //list_init(&a_layout->paletteList);
  //list_init(&a_layout->cameraList);
  //TEXTURE
  {
    dword entryIndex=0;
    dword entryTotal=list_getEntryCount(&a_layout->textureList);
    while(entryIndex < entryTotal)
    {
      LayoutElement *layoutElementPointer=list_getEntry(&a_layout->textureList, entryIndex);
      
      //Print element name
      printf("[TEXTURE:%s]\n", layoutElementPointer->elementName);
      
      //Loop through element value list
      {
        dword entryIndex=0;
        dword entryTotal=list_getEntryCount(&layoutElementPointer->valueList);
        while(entryIndex < entryTotal)
        {
          LayoutValue *layoutValuePointer=list_getEntry(&layoutElementPointer->valueList, entryIndex);
          
          //Print value id & data
          printf("%d=%s\n", layoutValuePointer->id, layoutValuePointer->stringData);
          
          //Goto the next entry
          entryIndex++;
        }
      }
      
      //Loop through element hexdump list
      {
        dword entryIndex=0;
        dword entryTotal=list_getEntryCount(&layoutElementPointer->hexDumpList);
        while(entryIndex < entryTotal)
        {
          byte *hexDump=list_getEntry(&layoutElementPointer->hexDumpList, entryIndex);
          
          //Print value id & data
          printf("HexDump=%s\n", hexDump);
          
          //Goto the next entry
          entryIndex++;
        }
      }
      
      //Goto the next entry
      entryIndex++;
    }
  }
  //list_init(&a_layout->qubixList);
  //list_init(&a_layout->actorList);
  //list_init(&a_layout->pathList);
  //list_init(&a_layout->cubeList);
}


#30 User is offline Uhyve 

Posted 10 January 2011 - 01:07 PM

  • Master Procrastinator
  • Posts: 46
  • Joined: 08-November 10
  • Gender:Male
  • Location:Huddersfield, England
  • Project:SX Engine
QUOTE (SANiK @ Jan 10 2011, 10:30 AM)
The scale and offset are stored as a combined 32bit number.
The top 16 bits are the relative offset and the bottom 16 bits are the relative scale.

The scale is relative to 1 which you've figured out already and it uses 128.0f accuracy which you've also figured out.
The offset is 256.0f accuracy.

Ah man, that is alot simpler, feel like I should've thought of that.

QUOTE (SANiK @ Jan 10 2011, 11:02 AM)
Ofer made up as he went along. There are various functional differences in the DEF format, even though the DEF files show the same file version number.
You might also want to invest time in a nice database to manage the resources.

Yeah, I noticed some stuff in the JG_0* levels that suggested something like that happening, some sort of database probably would be the best idea. My code feels really hacky at this point...

QUOTE (SANiK @ Jan 10 2011, 11:02 AM)
Code

... Especially after seeing that. That is really clean code, I'm kind of embarrassed to have released my source now. Gonna have to spend some time looking through all that, I'm not too proud to admit that I'm alittle giddy right now. My code has so many glitches, I've constantly been wondering how you managed to get some of the stuff working.
This post has been edited by Uhyve: 10 January 2011 - 01:59 PM

  • 7 Pages +
  • 1
  • 2
  • 3
  • 4
  • Last ►
    Locked
    Locked Forum

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users