code:
#include "common.h"
#include <rwcore.h>
#include <rpworld.h>

#include "Lights.h"
#include "Timer.h"
#include "Timecycle.h"
#include "Coronas.h"
#include "Weather.h"
#include "ZoneCull.h"
#include "Frontend.h"

RpLight *pAmbient;
RpLight *pDirect;
RpLight *pExtraDirectionals[4] = { nil };
int LightStrengths[4];
int NumExtraDirLightsInWorld;

RwRGBAReal AmbientLightColourForFrame;
RwRGBAReal AmbientLightColourForFrame_PedsCarsAndObjects;
RwRGBAReal DirectionalLightColourForFrame;

RwRGBAReal AmbientLightColour;
RwRGBAReal DirectionalLightColour;

void
SetLightsWithTimeOfDayColour(RpWorld *)
{
	CVector vec1, vec2, vecsun;
	RwMatrix mat;

	if(pAmbient){
		AmbientLightColourForFrame.red = CTimeCycle::GetAmbientRed() * CCoronas::LightsMult;
		AmbientLightColourForFrame.green = CTimeCycle::GetAmbientGreen() * CCoronas::LightsMult;
		AmbientLightColourForFrame.blue = CTimeCycle::GetAmbientBlue() * CCoronas::LightsMult;
		if(CWeather::LightningFlash && !CCullZones::CamNoRain()){
			AmbientLightColourForFrame.red = 1.0f;
			AmbientLightColourForFrame.green = 1.0f;
			AmbientLightColourForFrame.blue = 1.0f;
		}
		AmbientLightColourForFrame_PedsCarsAndObjects.red = Min(1.0f, AmbientLightColourForFrame.red*1.3f);
		AmbientLightColourForFrame_PedsCarsAndObjects.green = Min(1.0f, AmbientLightColourForFrame.green*1.3f);
		AmbientLightColourForFrame_PedsCarsAndObjects.blue = Min(1.0f, AmbientLightColourForFrame.blue*1.3f);
		RpLightSetColor(pAmbient, &AmbientLightColourForFrame);
	}

	if(pDirect){
		DirectionalLightColourForFrame.red = CTimeCycle::GetDirectionalRed() * CCoronas::LightsMult;
		DirectionalLightColourForFrame.green = CTimeCycle::GetDirectionalGreen() * CCoronas::LightsMult;
		DirectionalLightColourForFrame.blue = CTimeCycle::GetDirectionalBlue() * CCoronas::LightsMult;
		RpLightSetColor(pDirect, &DirectionalLightColourForFrame);

		vecsun = CTimeCycle::m_VectorToSun[CTimeCycle::m_CurrentStoredValue];
		vec1 = CVector(0.0f, 0.0f, 1.0f);
		vec2 = CrossProduct(vec1, vecsun);
		vec2.Normalise();
		vec1 = CrossProduct(vec2, vecsun);
		mat.at.x = -vecsun.x;
		mat.at.y = -vecsun.y;
		mat.at.z = -vecsun.z;
		mat.right.x = vec1.x;
		mat.right.y = vec1.y;
		mat.right.z = vec1.z;
		mat.up.x = vec2.x;
		mat.up.y = vec2.y;
		mat.up.z = vec2.z;
		RwFrameTransform(RpLightGetFrame(pDirect), &mat, rwCOMBINEREPLACE);
	}

	if(CMenuManager::m_PrefsBrightness > 256){
		float f1 = 2.0f * (CMenuManager::m_PrefsBrightness/256.0f - 1.0f) * 0.6f + 1.0f;
		float f2 = 3.0f * (CMenuManager::m_PrefsBrightness/256.0f - 1.0f) * 0.6f + 1.0f;

		AmbientLightColourForFrame.red = Min(1.0f, AmbientLightColourForFrame.red * f2);
		AmbientLightColourForFrame.green = Min(1.0f, AmbientLightColourForFrame.green * f2);
		AmbientLightColourForFrame.blue = Min(1.0f, AmbientLightColourForFrame.blue * f2);
		AmbientLightColourForFrame_PedsCarsAndObjects.red = Min(1.0f, AmbientLightColourForFrame_PedsCarsAndObjects.red * f1);
		AmbientLightColourForFrame_PedsCarsAndObjects.green = Min(1.0f, AmbientLightColourForFrame_PedsCarsAndObjects.green * f1);
		AmbientLightColourForFrame_PedsCarsAndObjects.blue = Min(1.0f, AmbientLightColourForFrame_PedsCarsAndObjects.blue * f1);
#ifdef FIX_BUGS
		DirectionalLightColourForFrame.red = Min(1.0f, DirectionalLightColourForFrame.red * f1);
		DirectionalLightColourForFrame.green = Min(1.0f, DirectionalLightColourForFrame.green * f1);
		DirectionalLightColourForFrame.blue = Min(1.0f, DirectionalLightColourForFrame.blue * f1);
#else
		DirectionalLightColourForFrame.red = Min(1.0f, AmbientLightColourForFrame.red * f1);
		DirectionalLightColourForFrame.green = Min(1.0f, AmbientLightColourForFrame.green * f1);
		DirectionalLightColourForFrame.blue = Min(1.0f, AmbientLightColourForFrame.blue * f1);
#endif
	}
}

RpWorld*
LightsCreate(RpWorld *world)
{
	int i;
	RwRGBAReal color;
	RwFrame *frame;

	if(world == nil)
		return nil;

	pAmbient = RpLightCreate(rpLIGHTAMBIENT);
	RpLightSetFlags(pAmbient, rpLIGHTLIGHTATOMICS);
	color.red = 0.25f;
	color.green = 0.25f;
	color.blue = 0.2f;
	RpLightSetColor(pAmbient, &color);

	pDirect = RpLightCreate(rpLIGHTDIRECTIONAL);
	RpLightSetFlags(pDirect, rpLIGHTLIGHTATOMICS);
	color.red = 1.0f;
	color.green = 0.85f;
	color.blue = 0.45f;
	RpLightSetColor(pDirect, &color);
	RpLightSetRadius(pDirect, 2.0f);
	frame = RwFrameCreate();
	RpLightSetFrame(pDirect, frame);
	RwV3d axis = { 1.0f, 1.0f, 0.0f };
	RwFrameRotate(frame, &axis, 160.0f, rwCOMBINEPRECONCAT);

	RpWorldAddLight(world, pAmbient);
	RpWorldAddLight(world, pDirect);

	for(i = 0; i < NUMEXTRADIRECTIONALS; i++){
		pExtraDirectionals[i] = RpLightCreate(rpLIGHTDIRECTIONAL);
		RpLightSetFlags(pExtraDirectionals[i], 0);
		color.red = 1.0f;
		color.green = 0.5f;
		color.blue = 0.0f;
		RpLightSetColor(pExtraDirectionals[i], &color);
		RpLightSetRadius(pExtraDirectionals[i], 2.0f);
		frame = RwFrameCreate();
		RpLightSetFrame(pExtraDirectionals[i], frame);
		RpWorldAddLight(world, pExtraDirectionals[i]);
	}

	return world;
}

void
LightsDestroy(RpWorld *world)
{
	int i;

	if(world == nil)
		return;

	if(pAmbient){
		RpWorldRemoveLight(world, pAmbient);
		RpLightDestroy(pAmbient);
		pAmbient = nil;
	}

	if(pDirect){
		RpWorldRemoveLight(world, pDirect);
		RwFrameDestroy(RpLightGetFrame(pDirect));
		RpLightDestroy(pDirect);
		pDirect = nil;
	}

	for(i = 0; i < NUMEXTRADIRECTIONALS; i++)
		if(pExtraDirectionals[i]){
			RpWorldRemoveLight(world, pExtraDirectionals[i]);
			RwFrameDestroy(RpLightGetFrame(pExtraDirectionals[i]));
			RpLightDestroy(pExtraDirectionals[i]);
			pExtraDirectionals[i] = nil;
		}
}

void
WorldReplaceNormalLightsWithScorched(RpWorld *world, float l)
{
	RwRGBAReal color;
	color.red = l;
	color.green = l;
	color.blue = l;
	RpLightSetColor(pAmbient, &color);
	RpLightSetFlags(pDirect, 0);
}

void
WorldReplaceScorchedLightsWithNormal(RpWorld *world)
{
	RpLightSetColor(pAmbient, &AmbientLightColourForFrame);
	RpLightSetFlags(pDirect, rpLIGHTLIGHTATOMICS);
}

void
AddAnExtraDirectionalLight(RpWorld *world, float dirx, float diry, float dirz, float red, float green, float blue)
{
	float strength;
	int weakest;
	int i, n;
	RwRGBAReal color;
	RwV3d *dir;

	strength = Max(Max(red, green), blue);
	n = -1;
	if(NumExtraDirLightsInWorld < NUMEXTRADIRECTIONALS)
		n = NumExtraDirLightsInWorld;
	else{
		weakest = strength;
		for(i = 0; i < NUMEXTRADIRECTIONALS; i++)
			if(LightStrengths[i] < weakest){
				weakest = LightStrengths[i];
				n = i;
			}
	}

	if(n < 0)
		return;

	color.red = red;
	color.green = green;
	color.blue = blue;
	RpLightSetColor(pExtraDirectionals[n], &color);
	dir = RwMatrixGetAt(RwFrameGetMatrix(RpLightGetFrame(pExtraDirectionals[n])));
	dir->x = -dirx;
	dir->y = -diry;
	dir->z = -dirz;
	RwMatrixUpdate(RwFrameGetMatrix(RpLightGetFrame(pExtraDirectionals[n])));
	RwFrameUpdateObjects(RpLightGetFrame(pExtraDirectionals[n]));
	RpLightSetFlags(pExtraDirectionals[n], rpLIGHTLIGHTATOMICS);
	LightStrengths[n] = strength;
	NumExtraDirLightsInWorld = Min(NumExtraDirLightsInWorld+1, NUMEXTRADIRECTIONALS);
}

void
RemoveExtraDirectionalLights(RpWorld *world)
{
	int i;
	for(i = 0; i < NumExtraDirLightsInWorld; i++)
		RpLightSetFlags(pExtraDirectionals[i], 0);
	NumExtraDirLightsInWorld = 0;
}

void
SetAmbientAndDirectionalColours(float f)
{
	AmbientLightColour.red = AmbientLightColourForFrame.red * f;
	AmbientLightColour.green = AmbientLightColourForFrame.green * f;
	AmbientLightColour.blue = AmbientLightColourForFrame.blue * f;

	DirectionalLightColour.red = DirectionalLightColourForFrame.red * f;
	DirectionalLightColour.green = DirectionalLightColourForFrame.green * f;
	DirectionalLightColour.blue = DirectionalLightColourForFrame.blue * f;

	RpLightSetColor(pAmbient, &AmbientLightColour);
	RpLightSetColor(pDirect, &DirectionalLightColour);
}

// unused
void
SetFlashyColours(float f)
{
	if(CTimer::GetTimeInMilliseconds() & 0x100){
		AmbientLightColour.red = 1.0f;
		AmbientLightColour.green = 1.0f;
		AmbientLightColour.blue = 1.0f;

		DirectionalLightColour.red = DirectionalLightColourForFrame.red;
		DirectionalLightColour.green = DirectionalLightColourForFrame.green;
		DirectionalLightColour.blue = DirectionalLightColourForFrame.blue;

		RpLightSetColor(pAmbient, &AmbientLightColour);
		RpLightSetColor(pDirect, &DirectionalLightColour);
	}else{
		SetAmbientAndDirectionalColours(f * 0.75f);
	}
}

// unused
void
SetFlashyColours_Mild(float f)
{
	if(CTimer::GetTimeInMilliseconds() & 0x100){
		AmbientLightColour.red = 0.65f;
		AmbientLightColour.green = 0.65f;
		AmbientLightColour.blue = 0.65f;

		DirectionalLightColour.red = DirectionalLightColourForFrame.red;
		DirectionalLightColour.green = DirectionalLightColourForFrame.green;
		DirectionalLightColour.blue = DirectionalLightColourForFrame.blue;

		RpLightSetColor(pAmbient, &AmbientLightColour);
		RpLightSetColor(pDirect, &DirectionalLightColour);
	}else{
		SetAmbientAndDirectionalColours(f * 0.9f);
	}
}

void
SetBrightMarkerColours(float f)
{
	AmbientLightColour.red = 0.6f;
	AmbientLightColour.green = 0.6f;
	AmbientLightColour.blue = 0.6f;

	DirectionalLightColour.red = (1.0f - DirectionalLightColourForFrame.red) * 0.4f + DirectionalLightColourForFrame.red;
	DirectionalLightColour.green = (1.0f - DirectionalLightColourForFrame.green) * 0.4f + DirectionalLightColourForFrame.green;
	DirectionalLightColour.blue = (1.0f - DirectionalLightColourForFrame.blue) * 0.4f + DirectionalLightColourForFrame.blue;

	RpLightSetColor(pAmbient, &AmbientLightColour);
	RpLightSetColor(pDirect, &DirectionalLightColour);
}

void
ReSetAmbientAndDirectionalColours(void)
{
	RpLightSetColor(pAmbient, &AmbientLightColourForFrame);
	RpLightSetColor(pDirect, &DirectionalLightColourForFrame);
}

void
DeActivateDirectional(void)
{
	RpLightSetFlags(pDirect, 0);
}

void
ActivateDirectional(void)
{
	RpLightSetFlags(pDirect, rpLIGHTLIGHTATOMICS);
}

void
SetAmbientColours(void)
{
	RpLightSetColor(pAmbient, &AmbientLightColourForFrame);
}

void
SetAmbientColoursForPedsCarsAndObjects(void)
{
	RpLightSetColor(pAmbient, &AmbientLightColourForFrame_PedsCarsAndObjects);
}

uint8 IndicateR[] = { 0, 255, 0, 0, 255, 255, 0 };
uint8 IndicateG[] = { 0, 0, 255, 0, 255, 0, 255 };
uint8 IndicateB[] = { 0, 0, 0, 255, 0, 255, 255 };

void
SetAmbientColoursToIndicateRoadGroup(int i)
{
	AmbientLightColour.red   = IndicateR[i%7]/255.0f;
	AmbientLightColour.green = IndicateG[i%7]/255.0f;
	AmbientLightColour.blue  = IndicateB[i%7]/255.0f;
	RpLightSetColor(pAmbient, &AmbientLightColour);
}

void
SetAmbientColours(RwRGBAReal *color)
{
	RpLightSetColor(pAmbient, color);
}


code:
#pragma once

extern RpLight *pAmbient;
extern RpLight *pDirect;
extern RpLight *pExtraDirectionals[4];
extern int LightStrengths[4];
extern int NumExtraDirLightsInWorld;

void SetLightsWithTimeOfDayColour(RpWorld *);
RpWorld *LightsCreate(RpWorld *world);
void LightsDestroy(RpWorld *world);
void WorldReplaceNormalLightsWithScorched(RpWorld *world, float l);
void WorldReplaceScorchedLightsWithNormal(RpWorld *world);
void AddAnExtraDirectionalLight(RpWorld *world, float dirx, float diry, float dirz, float red, float green, float blue);
void RemoveExtraDirectionalLights(RpWorld *world);
void SetAmbientAndDirectionalColours(float f);
void SetFlashyColours(float f);
void SetFlashyColours_Mild(float f);
void SetBrightMarkerColours(float f);
void ReSetAmbientAndDirectionalColours(void);
void DeActivateDirectional(void);
void ActivateDirectional(void);
void SetAmbientColours(void);
void SetAmbientColoursForPedsCarsAndObjects(void);
void SetAmbientColoursToIndicateRoadGroup(int i);
void SetAmbientColours(RwRGBAReal *color);

license: mit


code:
#include "common.h"
#include "main.h"
#include "FileMgr.h"
#include "Timer.h"
#include "ModelInfo.h"
#include "Streaming.h"
#include "FileLoader.h"
#include "MemoryHeap.h"

#ifdef USE_CUSTOM_ALLOCATOR

//#define MEMORYHEAP_ASSERT(cond) { if (!(cond)) { printf("ASSERT File:%s Line:%d\n", __FILE__, __LINE__); exit(1); } }
//#define MEMORYHEAP_ASSERT_MESSAGE(cond, message) { if (!(cond)) { printf("ASSERT File:%s Line:%d:\n\t%s\n", __FILE__, __LINE__, message); exit(1); } }

#define MEMORYHEAP_ASSERT(cond) assert(cond)
#define MEMORYHEAP_ASSERT_MESSAGE(cond, message) assert(cond)

// registered pointers that we keep track of
void **gPtrList[4000];
int32 numPtrs;
int32 gPosnInList;
// indices into the ptr list in here are free
CStack<int32, 4000> m_ptrListIndexStack;
// how much memory we've moved
uint32 memMoved;

CMemoryHeap gMainHeap;

void
CMemoryHeap::Init(uint32 total)
{
	MEMORYHEAP_ASSERT((total != 0xF) != 0);

	m_totalMemUsed = 0;
	m_memUsed = nil;
	m_currentMemID = MEMID_FREE;
	m_blocksUsed = nil;
	m_totalBlocksUsed = 0;
	m_unkMemId = -1;

	uint8 *mem = (uint8*)malloc(total);
	assert(((uintptr)mem & 0xF) == 0);
	m_start = (HeapBlockDesc*)mem;
	m_end = (HeapBlockDesc*)(mem + total - sizeof(HeapBlockDesc));
	m_start->m_memId = MEMID_FREE;
	m_start->m_size = total - 2*sizeof(HeapBlockDesc);
	m_end->m_memId = MEMID_GAME;
	m_end->m_size = 0;

	m_freeList.m_last.m_size = INT_MAX;
	m_freeList.Init();
	m_freeList.Insert(m_start);

	// TODO: figure out what these are and use sizeof
	m_fixedSize[0].Init(0x10);
	m_fixedSize[1].Init(0x20);
	m_fixedSize[2].Init(0xE0);
	m_fixedSize[3].Init(0x60);
	m_fixedSize[4].Init(0x1C0);
	m_fixedSize[5].Init(0x50);

	m_currentMemID = MEMID_FREE;	// disable registration
	m_memUsed = (uint32*)Malloc(NUM_MEMIDS * sizeof(uint32));
	m_blocksUsed = (uint32*)Malloc(NUM_MEMIDS * sizeof(uint32));
	RegisterMalloc(GetDescFromHeapPointer(m_memUsed));
	RegisterMalloc(GetDescFromHeapPointer(m_blocksUsed));

	m_currentMemID = MEMID_GAME;
	for(int i = 0; i < NUM_MEMIDS; i++){
		m_memUsed[i] = 0;
		m_blocksUsed[i] = 0;
	}
}

void
CMemoryHeap::RegisterMalloc(HeapBlockDesc *block)
{
	block->m_memId = m_currentMemID;
	if(m_currentMemID == MEMID_FREE)
		return;
	m_totalMemUsed += block->m_size + sizeof(HeapBlockDesc);
	m_memUsed[m_currentMemID] += block->m_size + sizeof(HeapBlockDesc);
	m_blocksUsed[m_currentMemID]++;
	m_totalBlocksUsed++;
}

void
CMemoryHeap::RegisterFree(HeapBlockDesc *block)
{
	if(block->m_memId == MEMID_FREE)
		return;
	m_totalMemUsed -= block->m_size + sizeof(HeapBlockDesc);
	m_memUsed[block->m_memId] -= block->m_size + sizeof(HeapBlockDesc);
	m_blocksUsed[block->m_memId]--;
	m_totalBlocksUsed--;
}

void*
CMemoryHeap::Malloc(uint32 size)
{
	static int recursion = 0;

	// weird way to round up
	if((size & 0xF) != 0)
		size = (size&~0xF) + 0x10;

	recursion++;

	// See if we can allocate from one of the fixed-size lists
	for(int i = 0; i < NUM_FIXED_MEMBLOCKS; i++){
		CommonSize *list = &m_fixedSize[i];
		if(m_fixedSize[i].m_size == size){
			HeapBlockDesc *block = list->Malloc();
			if(block){
				RegisterMalloc(block);
				recursion--;
				return block->GetDataPointer();
			}
			break;
		}
	}

	// now try the normal free list
	HeapBlockDesc *next;
	for(HeapBlockDesc *block = m_freeList.m_first.m_next;
	    block != &m_freeList.m_last;
	    block = next){
		MEMORYHEAP_ASSERT(block->m_memId == MEMID_FREE);
		MEMORYHEAP_ASSERT_MESSAGE(block >= m_start && block <= m_end, "Block outside of memory");

		// make sure block has maximum size
		uint32 initialsize = block->m_size;
		uint32 blocksize = CombineFreeBlocks(block);
#ifdef FIX_BUGS
		// has to be done here because block can be moved
		next = block->m_next;
#endif
		if(initialsize != blocksize){
			block->RemoveHeapFreeBlock();
			HeapBlockDesc *pos = block->m_prev->FindSmallestFreeBlock(block->m_size);
			block->InsertHeapFreeBlock(pos->m_prev);
		}
		if(block->m_size >= size){
			// got space to allocate from!
			block->RemoveHeapFreeBlock();
			FillInBlockData(block, block->GetNextConsecutive(), size);
			recursion--;
			return block->GetDataPointer();
		}
#ifndef FIX_BUGS
		next = block->m_next;
#endif
	}

	// oh no, we're losing, try to free some stuff
	static bool removeCollision = false;
	static bool removeIslands = false;
	static bool removeBigBuildings = false;
	size_t initialMemoryUsed = CStreaming::ms_memoryUsed;
	CStreaming::MakeSpaceFor(0xCFE800 - CStreaming::ms_memoryUsed);
	if (recursion > 10)
		CGame::TidyUpMemory(true, false);
	else if (recursion > 6)
		CGame::TidyUpMemory(false, true);
	if (initialMemoryUsed == CStreaming::ms_memoryUsed && recursion > 11) {
		if (!removeCollision && !CGame::playingIntro) {
			CModelInfo::RemoveColModelsFromOtherLevels(LEVEL_GENERIC);
			removeCollision = true;
		}
		else if (!removeIslands && !CGame::playingIntro) {
			CStreaming::RemoveIslandsNotUsed(LEVEL_INDUSTRIAL);
			CStreaming::RemoveIslandsNotUsed(LEVEL_COMMERCIAL);
			CStreaming::RemoveIslandsNotUsed(LEVEL_SUBURBAN);
			removeIslands = true;
		}
		else if (!removeBigBuildings) {
			CStreaming::RemoveBigBuildings(LEVEL_INDUSTRIAL);
			CStreaming::RemoveBigBuildings(LEVEL_COMMERCIAL);
			CStreaming::RemoveBigBuildings(LEVEL_SUBURBAN);
		}
		else {
			LoadingScreen("NO MORE MEMORY", nil, nil);
			LoadingScreen("NO MORE MEMORY", nil, nil);
		}
		CGame::TidyUpMemory(true, false);
	}
	void *mem = Malloc(size);
	if (removeCollision) {
		CTimer::Stop();
		// TODO: different on PS2
		CFileLoader::LoadCollisionFromDatFile(CCollision::ms_collisionInMemory);
		removeCollision = false;
		CTimer::Update();
	}
	if (removeBigBuildings || removeIslands) {
		CTimer::Stop();
		if (!CGame::playingIntro)
			CStreaming::RequestBigBuildings(CGame::currLevel);
		CStreaming::LoadAllRequestedModels(true);
		removeBigBuildings = false;
		removeIslands = false;
		CTimer::Update();
	}
	recursion--;
	return mem;
}

void*
CMemoryHeap::Realloc(void *ptr, uint32 size)
{
	if(ptr == nil)
		return Malloc(size);

	// weird way to round up
	if((size & 0xF) != 0)
		size = (size&~0xF) + 0x10;

	HeapBlockDesc *block = GetDescFromHeapPointer(ptr);

#ifdef FIX_BUGS
	// better handling of size < block->m_size
	if(size == 0){
		Free(ptr);
		return nil;
	}
	if(block->m_size >= size){
		// shrink allocated block
		RegisterFree(block);
		PushMemId(block->m_memId);
		FillInBlockData(block, block->GetNextConsecutive(), size);
		PopMemId();
		return ptr;
	}
#else
	// not growing. just returning here is a bit cheap though
	if(block->m_size >= size)
		return ptr;
#endif

	// have to grow allocated block
	HeapBlockDesc *next = block->GetNextConsecutive();
	MEMORYHEAP_ASSERT_MESSAGE(next >= m_start && next <= m_end, "Block outside of memory");
	if(next->m_memId == MEMID_FREE){
		// try to grow the current block
		// make sure the next free block has maximum size
		uint32 freespace = CombineFreeBlocks(next);
		HeapBlockDesc *end = next->GetNextConsecutive();
		MEMORYHEAP_ASSERT_MESSAGE(end >= m_start && end <= m_end, "Block outside of memory");
		// why the sizeof here?
		if(block->m_size + next->m_size + sizeof(HeapBlockDesc) >= size){
			// enough space to grow
			next->RemoveHeapFreeBlock();
			RegisterFree(block);
			PushMemId(block->m_memId);
			FillInBlockData(block, next->GetNextConsecutive(), size);
			PopMemId();
			return ptr;
		}
	}

	// can't grow the existing block, have to get a new one and copy
	PushMemId(block->m_memId);
	void *dst = Malloc(size);
	PopMemId();
	memcpy(dst, ptr, block->m_size);
	Free(ptr);
	return dst;
}

void
CMemoryHeap::Free(void *ptr)
{
	HeapBlockDesc *block = GetDescFromHeapPointer(ptr);
	MEMORYHEAP_ASSERT_MESSAGE(block->m_memId != MEMID_FREE, "MemoryHeap corrupt");
	MEMORYHEAP_ASSERT(m_unkMemId == -1 || m_unkMemId == block->m_memId);

	RegisterFree(block);
	block->m_memId = MEMID_FREE;
	CombineFreeBlocks(block);
	FreeBlock(block);
	if(block->m_ptrListIndex != -1){
		int32 idx = block->m_ptrListIndex;
		gPtrList[idx] = nil;
		m_ptrListIndexStack.push(idx);
	}
	block->m_ptrListIndex = -1;
}

// allocate 'size' bytes from 'block'
void
CMemoryHeap::FillInBlockData(HeapBlockDesc *block, HeapBlockDesc *end, uint32 size)
{
	block->m_size = size;
	block->m_ptrListIndex = -1;
	HeapBlockDesc *remainder = block->GetNextConsecutive();
	MEMORYHEAP_ASSERT(remainder <= end);

	if(remainder < end-1){
		RegisterMalloc(block);

		// can fit another block in the remaining space
		remainder->m_size = GetSizeBetweenBlocks(remainder, end);
		remainder->m_memId = MEMID_FREE;
		MEMORYHEAP_ASSERT(remainder->m_size != 0);
		FreeBlock(remainder);
	}else{
		// fully allocate this one
		if(remainder < end)
			// no gaps allowed
			block->m_size = GetSizeBetweenBlocks(block, end);
		RegisterMalloc(block);
	}
}

// Make sure free block has no other free blocks after it
uint32
CMemoryHeap::CombineFreeBlocks(HeapBlockDesc *block)
{
	HeapBlockDesc *next = block->GetNextConsecutive();
	if(next->m_memId != MEMID_FREE)
		return block->m_size;
	// get rid of free blocks after this one and adjust size
	for(; next->m_memId == MEMID_FREE; next = next->GetNextConsecutive())
		next->RemoveHeapFreeBlock();
	block->m_size = GetSizeBetweenBlocks(block, next);
	return block->m_size;
}

// Try to move all registered memory blocks into more optimal location
void
CMemoryHeap::TidyHeap(void)
{
	for(int i = 0; i < numPtrs; i++){
		if(gPtrList[i] == nil || *gPtrList[i] == nil)
			continue;
		HeapBlockDesc *newblock = WhereShouldMemoryMove(*gPtrList[i]);
		if(newblock)
			*gPtrList[i] = MoveHeapBlock(newblock, GetDescFromHeapPointer(*gPtrList[i]));
	}
}

//
void
CMemoryHeap::RegisterMemPointer(void *ptr)
{
	HeapBlockDesc *block = GetDescFromHeapPointer(*(void**)ptr);

	if(block->m_ptrListIndex != -1)
		return;		// already registered

	int index;
	if(m_ptrListIndexStack.sp > 0){
		// re-use a previously free'd index
		index = m_ptrListIndexStack.pop();
	}else{
		// have to find a new index
		index = gPosnInList;

		void **pp = gPtrList[index];
		// we're replacing an old pointer here??
		if(pp && *pp && *pp != (void*)0xDDDDDDDD)
			GetDescFromHeapPointer(*pp)->m_ptrListIndex = -1;

		gPosnInList++;
		if(gPosnInList == 4000)
			gPosnInList = 0;
		if(numPtrs < 4000)
			numPtrs++;
	}
	gPtrList[index] = (void**)ptr;
	block->m_ptrListIndex = index;
}

void*
CMemoryHeap::MoveMemory(void *ptr)
{
	HeapBlockDesc *newblock = WhereShouldMemoryMove(ptr);
	if(newblock)
		return MoveHeapBlock(newblock, GetDescFromHeapPointer(ptr));
	else
		return ptr;
}

HeapBlockDesc*
CMemoryHeap::WhereShouldMemoryMove(void *ptr)
{
	HeapBlockDesc *block = GetDescFromHeapPointer(ptr);
	MEMORYHEAP_ASSERT(block->m_memId != MEMID_FREE);

	HeapBlockDesc *next = block->GetNextConsecutive();
	if(next->m_memId != MEMID_FREE)
		return nil;

	// we want to move the block into another block
	// such that the free space between this and the next block can be minimized
	HeapBlockDesc *newblock = m_freeList.m_first.FindSmallestFreeBlock(block->m_size);
	// size of free space wouldn't decrease, so return
	if(newblock->m_size >= block->m_size + next->m_size)
		return nil;
	// size of free space wouldn't decrease enough
	if(newblock->m_size >= 16 + 1.125f*block->m_size)	// what are 16 and 1.125 here? sizeof(HeapBlockDesc)?
		return nil;
	return newblock;
}

void*
CMemoryHeap::MoveHeapBlock(HeapBlockDesc *dst, HeapBlockDesc *src)
{
	PushMemId(src->m_memId);
	dst->RemoveHeapFreeBlock();
	FillInBlockData(dst, dst->GetNextConsecutive(), src->m_size);
	PopMemId();
	memcpy(dst->GetDataPointer(), src->GetDataPointer(), src->m_size);
	memMoved += src->m_size;
	dst->m_ptrListIndex = src->m_ptrListIndex;
	src->m_ptrListIndex = -1;
	Free(src->GetDataPointer());
	return dst->GetDataPointer();
}

uint32
CMemoryHeap::GetMemoryUsed(int32 id)
{
	return m_memUsed[id];
}

uint32
CMemoryHeap::GetBlocksUsed(int32 id)
{
	return m_blocksUsed[id];
}

void
CMemoryHeap::PopMemId(void)
{
	assert(m_idStack.sp > 0);
	m_currentMemID = m_idStack.pop();
	assert(m_currentMemID != MEMID_FREE);
}

void
CMemoryHeap::PushMemId(int32 id)
{
	MEMORYHEAP_ASSERT(id != MEMID_FREE);
	assert(m_idStack.sp < 16);
	m_idStack.push(m_currentMemID);
	m_currentMemID = id;
}

void
CMemoryHeap::ParseHeap(void)
{
	char tmp[16];
	int fd = CFileMgr::OpenFileForWriting("heap.txt");
	CTimer::Stop();

	// CMemoryHeap::IntegrityCheck();

	uint32 addrQW = 0;
	for(HeapBlockDesc *block = m_start; block < m_end; block = block->GetNextConsecutive()){
		char chr = '*';	// free
		if(block->m_memId != MEMID_FREE)
			chr = block->m_memId-1 + 'A';
		int numQW = block->m_size>>4;

		if((addrQW & 0x3F) == 0){
			sprintf(tmp, "\n%5dK:", addrQW>>6);
			CFileMgr::Write(fd, tmp, 8);
		}
		CFileMgr::Write(fd, "#", 1);	// the descriptor, has to be 16 bytes!!!!
		addrQW++;

		while(numQW--){
			if((addrQW & 0x3F) == 0){
				sprintf(tmp, "\n%5dK:", addrQW>>6);
				CFileMgr::Write(fd, tmp, 8);
			}
			CFileMgr::Write(fd, &chr, 1);
			addrQW++;
		}
	}

	CTimer::Update();
	CFileMgr::CloseFile(fd);
}

void
CommonSize::Init(uint32 size)
{
	m_freeList.Init();
	m_size = size;
	m_failed = 0;
	m_remaining = 0;
}

#endif


code:
#pragma once

// some windows shit
#ifdef MoveMemory
#undef MoveMemory
#endif

#ifdef USE_CUSTOM_ALLOCATOR
#define PUSH_MEMID(id) gMainHeap.PushMemId(id)
#define POP_MEMID() gMainHeap.PopMemId()
#define REGISTER_MEMPTR(ptr) gMainHeap.RegisterMemPointer(ptr)
#else
#define PUSH_MEMID(id)
#define POP_MEMID()
#define REGISTER_MEMPTR(ptr)
#endif

enum {
	MEMID_FREE,
	MEMID_GAME = 1,	// "Game"
	MEMID_WORLD = 2,	// "World"
	MEMID_ANIMATION = 3,	// "Animation"
	MEMID_POOLS = 4,	// "Pools"
	MEMID_DEF_MODELS = 5,	// "Default Models"
	MEMID_STREAM = 6,	// "Streaming"
	MEMID_STREAM_MODELS = 7,	// "Streamed Models" (instance)
	MEMID_STREAM_TEXUTRES = 8,	// "Streamed Textures"
	MEMID_TEXTURES = 9,	// "Textures"
	MEMID_COLLISION = 10,	// "Collision"
	MEMID_RENDERLIST = 11,	// ?
	MEMID_GAME_PROCESS = 12,	// "Game Process"
	MEMID_SCRIPT = 13,	// "Script"
	MEMID_CARS = 14,	// "Cars"
	MEMID_RENDER = 15,	// "Render"
	MEMID_FRONTEND = 17,	// ?

	NUM_MEMIDS,

	NUM_FIXED_MEMBLOCKS = 6
};

template<typename T, uint32 N>
class CStack
{
public:
	T values[N];
	uint32 sp;

	CStack() : sp(0) {}
	void push(const T& val) { values[sp++] = val; }
	T& pop() { return values[--sp]; }
};

struct HeapBlockDesc
{
	uint32 m_size;
	int16 m_memId;
	int16 m_ptrListIndex;
	HeapBlockDesc *m_next;
	HeapBlockDesc *m_prev;

	HeapBlockDesc *GetNextConsecutive(void)
	{
		return (HeapBlockDesc*)((uintptr)this + sizeof(HeapBlockDesc) + m_size);
	}

	void *GetDataPointer(void)
	{
		return (void*)((uintptr)this + sizeof(HeapBlockDesc));
	}

	void RemoveHeapFreeBlock(void)
	{
		m_next->m_prev = m_prev;
		m_prev->m_next = m_next;
	}

	// after node
	void InsertHeapFreeBlock(HeapBlockDesc *node)
	{
		m_next = node->m_next;
		node->m_next->m_prev = this;
		m_prev = node;
		node->m_next = this;
	}

	HeapBlockDesc *FindSmallestFreeBlock(uint32 size)
	{
		HeapBlockDesc *b;
		for(b = m_next; b->m_size < size; b = b->m_next);
		return b;
	}
};

#ifdef USE_CUSTOM_ALLOCATOR
// TODO: figure something out for 64 bit pointers
static_assert(sizeof(HeapBlockDesc) == 0x10, "HeapBlockDesc must have 0x10 size otherwise most of assumptions don't make sense");
#endif

struct HeapBlockList
{
	HeapBlockDesc m_first;
	HeapBlockDesc m_last;

	void Init(void)
	{
		m_first.m_next = &m_last;
		m_last.m_prev = &m_first;
	}

	void Insert(HeapBlockDesc *node)
	{
		node->InsertHeapFreeBlock(&m_first);
	}
};

struct CommonSize
{
	HeapBlockList m_freeList;
	uint32 m_size;
	uint32 m_failed;
	uint32 m_remaining;

	void Init(uint32 size);
	void Free(HeapBlockDesc *node)
	{
		m_freeList.Insert(node);
		m_remaining++;
	}
	HeapBlockDesc *Malloc(void)
	{
		if(m_freeList.m_first.m_next == &m_freeList.m_last){
			m_failed++;
			return nil;
		}
		HeapBlockDesc *block = m_freeList.m_first.m_next;
		m_remaining--;
		block->RemoveHeapFreeBlock();
		block->m_ptrListIndex = -1;
		return block;
	}
};

class CMemoryHeap
{
public:
	HeapBlockDesc *m_start;
	HeapBlockDesc *m_end;
	HeapBlockList m_freeList;
	CommonSize m_fixedSize[NUM_FIXED_MEMBLOCKS];
	uint32 m_totalMemUsed;
	CStack<int32, 16> m_idStack;
	uint32 m_currentMemID;
	uint32 *m_memUsed;
	uint32 m_totalBlocksUsed;
	uint32 *m_blocksUsed;
	uint32 m_unkMemId;

	CMemoryHeap(void) : m_start(nil) {}
	void Init(uint32 total);
	void RegisterMalloc(HeapBlockDesc *block);
	void RegisterFree(HeapBlockDesc *block);
	void *Malloc(uint32 size);
	void *Realloc(void *ptr, uint32 size);
	void Free(void *ptr);
	void FillInBlockData(HeapBlockDesc *block, HeapBlockDesc *end, uint32 size);
	uint32 CombineFreeBlocks(HeapBlockDesc *block);
	void *MoveMemory(void *ptr);
	HeapBlockDesc *WhereShouldMemoryMove(void *ptr);
	void *MoveHeapBlock(HeapBlockDesc *dst, HeapBlockDesc *src);
	void PopMemId(void);
	void PushMemId(int32 id);
	void RegisterMemPointer(void *ptr);
	void TidyHeap(void);
	uint32 GetMemoryUsed(int32 id);
	uint32 GetBlocksUsed(int32 id);
	int32 GetLargestFreeBlock(void) { return m_freeList.m_last.m_prev->m_size; }

	void ParseHeap(void);

	HeapBlockDesc *GetDescFromHeapPointer(void *block)
	{
		return (HeapBlockDesc*)((uintptr)block - sizeof(HeapBlockDesc));
	}
	uint32 GetSizeBetweenBlocks(HeapBlockDesc *first, HeapBlockDesc *second)
	{
		return (uintptr)second - (uintptr)first - sizeof(HeapBlockDesc);
	}
	void FreeBlock(HeapBlockDesc *block){
		for(int i = 0; i < NUM_FIXED_MEMBLOCKS; i++){
			if(m_fixedSize[i].m_size == block->m_size){
				m_fixedSize[i].Free(block);
				return;
			}
		}
		HeapBlockDesc *b = m_freeList.m_first.FindSmallestFreeBlock(block->m_size);
		block->InsertHeapFreeBlock(b->m_prev);
	}
};

extern CMemoryHeap gMainHeap;


code:
#include "common.h"
#include "MemoryHeap.h"
#include "MemoryMgr.h"

uint8 *pMemoryTop;

void
InitMemoryMgr(void)
{
#ifdef USE_CUSTOM_ALLOCATOR
#ifdef GTA_PS2
#error "finish this"
#else
	// randomly allocate 128mb
	gMainHeap.Init(128*1024*1024);
#endif
#endif
}

RwMemoryFunctions memFuncs = {
	MemoryMgrMalloc,
	MemoryMgrFree,
	MemoryMgrRealloc,
	MemoryMgrCalloc
};

#ifdef USE_CUSTOM_ALLOCATOR
// game seems to be using heap directly here, but this is nicer
void *operator new(size_t sz) throw() { return MemoryMgrMalloc(sz); }
void *operator new[](size_t sz) throw() { return MemoryMgrMalloc(sz); }
void operator delete(void *ptr) throw() { MemoryMgrFree(ptr); }
void operator delete[](void *ptr) throw() { MemoryMgrFree(ptr); }
#endif

void*
MemoryMgrMalloc(size_t size)
{
#ifdef USE_CUSTOM_ALLOCATOR
	void *mem = gMainHeap.Malloc(size);
#else
	void *mem = malloc(size);
#endif
	if((uint8*)mem + size > pMemoryTop)
		pMemoryTop = (uint8*)mem + size ;
	return mem;
}

void*
MemoryMgrRealloc(void *ptr, size_t size)
{
#ifdef USE_CUSTOM_ALLOCATOR
	void *mem = gMainHeap.Realloc(ptr, size);
#else
	void *mem = realloc(ptr, size);
#endif
	if((uint8*)mem + size  > pMemoryTop)
		pMemoryTop = (uint8*)mem + size ;
	return mem;
}

void*
MemoryMgrCalloc(size_t num, size_t size)
{
#ifdef USE_CUSTOM_ALLOCATOR
	void *mem = gMainHeap.Malloc(num*size);
#else
	void *mem = calloc(num, size);
#endif
	if((uint8*)mem + size  > pMemoryTop)
		pMemoryTop = (uint8*)mem + size ;
#ifdef FIX_BUGS
	memset(mem, 0, num*size);
#endif
	return mem;
}

void
MemoryMgrFree(void *ptr)
{
#ifdef USE_CUSTOM_ALLOCATOR
#ifdef FIX_BUGS
	// i don't suppose this is handled by RW?
	if(ptr == nil) return;
#endif
	gMainHeap.Free(ptr);
#else
	free(ptr);
#endif
}

void *
RwMallocAlign(RwUInt32 size, RwUInt32 align)
{
#if defined (FIX_BUGS) || defined(FIX_BUGS_64)
	uintptr ptralign = align-1;
	void *mem = (void *)MemoryMgrMalloc(size + sizeof(uintptr) + ptralign);

	ASSERT(mem != nil);

	void *addr = (void *)((((uintptr)mem) + sizeof(uintptr) + ptralign) & ~ptralign);

	ASSERT(addr != nil);
#else
	void *mem = (void *)MemoryMgrMalloc(size + align);

	ASSERT(mem != nil);

	void *addr = (void *)((((uintptr)mem) + align) & ~(align - 1));

	ASSERT(addr != nil);
#endif

	*(((void **)addr) - 1) = mem;

	return addr;
}

void
RwFreeAlign(void *mem)
{
	ASSERT(mem != nil);

	void *addr = *(((void **)mem) - 1);

	ASSERT(addr != nil);

	MemoryMgrFree(addr);
}


code:
#pragma once

extern RwMemoryFunctions memFuncs;
void InitMemoryMgr(void);

void *MemoryMgrMalloc(size_t size);
void *MemoryMgrRealloc(void *ptr, size_t size);
void *MemoryMgrCalloc(size_t num, size_t size);
void MemoryMgrFree(void *ptr);

void *RwMallocAlign(RwUInt32 size, RwUInt32 align);
void RwFreeAlign(void *mem);


code:
#include "common.h"

#include "NodeName.h"

static int32 gPluginOffset;

enum
{
	ID_NODENAME = MAKECHUNKID(rwVENDORID_ROCKSTAR, 0xFE),
};

#define NODENAMEEXT(o) (RWPLUGINOFFSET(char, o, gPluginOffset))

void*
NodeNameConstructor(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject)
{
	if(gPluginOffset > 0)
		NODENAMEEXT(object)[0] = '\0';
	return object;
}

void*
NodeNameDestructor(void *object, RwInt32 offsetInObject, RwInt32 sizeInObject)
{
	return object;
}

void*
NodeNameCopy(void *dstObject, const void *srcObject, RwInt32 offsetInObject, RwInt32 sizeInObject)
{
	strncpy(NODENAMEEXT(dstObject), NODENAMEEXT(srcObject), 23);
	return nil;
}

RwStream*
NodeNameStreamRead(RwStream *stream, RwInt32 binaryLength, void *object, RwInt32 offsetInObject, RwInt32 sizeInObject)
{
	RwStreamRead(stream, NODENAMEEXT(object), binaryLength);
	NODENAMEEXT(object)[binaryLength] = '\0';
	return stream;
}

RwStream*
NodeNameStreamWrite(RwStream *stream, RwInt32 binaryLength, const void *object, RwInt32 offsetInObject, RwInt32 sizeInObject)
{
	RwStreamWrite(stream, NODENAMEEXT(object), binaryLength);
	return stream;
}

RwInt32
NodeNameStreamGetSize(const void *object, RwInt32 offsetInObject, RwInt32 sizeInObject)
{
	char *name = NODENAMEEXT(object);	// can't be nil
	return name ? (RwInt32)rwstrlen(name) : 0;
}

bool
NodeNamePluginAttach(void)
{
	gPluginOffset = RwFrameRegisterPlugin(24, ID_NODENAME,
                                NodeNameConstructor,
                                NodeNameDestructor,
                                NodeNameCopy);
	RwFrameRegisterPluginStream(ID_NODENAME,
		NodeNameStreamRead,
		NodeNameStreamWrite,
		NodeNameStreamGetSize);
	return gPluginOffset != -1;
}

char*
GetFrameNodeName(RwFrame *frame)
{
	if(gPluginOffset < 0)
		return nil;
	return NODENAMEEXT(frame);
}


code:
#pragma once

bool NodeNamePluginAttach(void);
char *GetFrameNodeName(RwFrame *frame);


code:
#define WITHD3D
#include "common.h"
#include <rpskin.h>

#include "RwHelper.h"
#include "Timecycle.h"
#include "skeleton.h"
#include "Debug.h"
#include "MBlur.h"
#if !defined(FINAL) || defined(DEBUGMENU)
#include "rtcharse.h"
#endif
#ifndef FINAL
RtCharset *debugCharset;
bool bDebugRenderGroups;
#endif

#ifdef PS2_ALPHA_TEST
bool gPS2alphaTest = true;
#else
bool gPS2alphaTest = false;
#endif
bool gBackfaceCulling = true;

#if !defined(FINAL) || defined(DEBUGMENU)
static bool charsetOpen;
void OpenCharsetSafe()
{
	if(!charsetOpen)
		RtCharsetOpen();
	charsetOpen = true;
}
#endif

void CreateDebugFont()
{
#ifndef FINAL
	RwRGBA color = { 255, 255, 128, 255 };
	RwRGBA colorbg = { 0, 0, 0, 0 };
	OpenCharsetSafe();
	debugCharset = RtCharsetCreate(&color, &colorbg);
#endif
}

void DestroyDebugFont()
{
#ifndef FINAL
	RtCharsetDestroy(debugCharset);
	RtCharsetClose();
	charsetOpen = false;
#endif
}

void ObrsPrintfString(const char *str, short x, short y)
{
#ifndef FINAL
	RtCharsetPrintBuffered(debugCharset, str, x*8, y*16, true);
#endif
}

void FlushObrsPrintfs()
{
#ifndef FINAL
	RtCharsetBufferFlush();
#endif
}

void
DefinedState(void)
{
	RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSWRAP);
	RwRenderStateSet(rwRENDERSTATETEXTUREPERSPECTIVE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATEZTESTENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void*)TRUE);
	RwRenderStateSet(rwRENDERSTATESHADEMODE, (void*)rwSHADEMODEGOURAUD);
	RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR);
	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
	RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
	RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
	RwRenderStateSet(rwRENDERSTATEALPHAPRIMITIVEBUFFER, (void*)FALSE);
	RwRenderStateSet(rwRENDERSTATEBORDERCOLOR, (void*)RWRGBALONG(0, 0, 0, 255));
	RwRenderStateSet(rwRENDERSTATEFOGENABLE, (void*)FALSE);
	RwRenderStateSet(rwRENDERSTATEFOGCOLOR,
		(void*)RWRGBALONG(CTimeCycle::GetFogRed(), CTimeCycle::GetFogGreen(), CTimeCycle::GetFogBlue(), 255));
	RwRenderStateSet(rwRENDERSTATEFOGTYPE, (void*)rwFOGTYPELINEAR);
	RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE);

#ifdef LIBRW
	rw::SetRenderState(rw::ALPHATESTFUNC, rw::ALPHAGREATEREQUAL);

	rw::SetRenderState(rw::GSALPHATEST, gPS2alphaTest);
#else
	// D3D stuff
	RwD3D8SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER);
#endif
	SetAlphaRef(2);
}

void
SetAlphaRef(int ref)
{
#ifdef LIBRW
	rw::SetRenderState(rw::ALPHATESTREF, ref+1);
#else
	RwD3D8SetRenderState(D3DRS_ALPHAREF, ref);
#endif
}

void
SetCullMode(uint32 mode)
{
	if(gBackfaceCulling)
		RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)mode);
	else
		RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE);
}

#ifndef FINAL
void
PushRendergroup(const char *name)
{
	if(!bDebugRenderGroups)
		return;
#if defined(RW_OPENGL)
	if(GLAD_GL_KHR_debug)
		glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, name);
#elif defined(RW_D3D9)
	static WCHAR tmp[256];
	MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, name, -1, tmp, sizeof(tmp));
	D3DPERF_BeginEvent(0xFFFFFFFF, tmp);
#endif
}

void
PopRendergroup(void)
{
	if(!bDebugRenderGroups)
		return;
#if defined(RW_OPENGL)
	if(GLAD_GL_KHR_debug)
		glPopDebugGroup();
#elif defined(RW_D3D9)
	D3DPERF_EndEvent();
#endif
}
#endif

RwFrame*
GetFirstFrameCallback(RwFrame *child, void *data)
{
	*(RwFrame**)data = child;
	return nil;
}

RwFrame*
GetFirstChild(RwFrame *frame)
{
	RwFrame *child;

	child = nil;
	RwFrameForAllChildren(frame, GetFirstFrameCallback, &child);
	return child;
}

RwObject*
GetFirstObjectCallback(RwObject *object, void *data)
{
	*(RwObject**)data = object;
	return nil;
}

RwObject*
GetFirstObject(RwFrame *frame)
{
	RwObject *obj;

	obj = nil;
	RwFrameForAllObjects(frame, GetFirstObjectCallback, &obj);
	return obj;
}

RpAtomic*
GetFirstAtomicCallback(RpAtomic *atm, void *data)
{
	*(RpAtomic**)data = atm;
	return nil;
}

RpAtomic*
GetFirstAtomic(RpClump *clump)
{
	RpAtomic *atm;

	atm = nil;
	RpClumpForAllAtomics(clump, GetFirstAtomicCallback, &atm);
	return atm;
}

RwTexture*
GetFirstTextureCallback(RwTexture *tex, void *data)
{
	*(RwTexture**)data = tex;
	return nil;
}

RwTexture*
GetFirstTexture(RwTexDictionary *txd)
{
	RwTexture *tex;

	tex = nil;
	RwTexDictionaryForAllTextures(txd, GetFirstTextureCallback, &tex);
	return tex;
}

#ifdef PED_SKIN
static RpAtomic*
isSkinnedCb(RpAtomic *atomic, void *data)
{
	RpAtomic **pAtomic = (RpAtomic**)data;
	if(*pAtomic)
		return nil;	// already found one
	if(RpSkinGeometryGetSkin(RpAtomicGetGeometry(atomic)))
		*pAtomic = atomic;	// we could just return nil here directly...
	return atomic;
}

RpAtomic*
IsClumpSkinned(RpClump *clump)
{
	RpAtomic *atomic = nil;
	RpClumpForAllAtomics(clump, isSkinnedCb, &atomic);
	return atomic;
}

static RpAtomic*
GetAnimHierarchyCallback(RpAtomic *atomic, void *data)
{
	*(RpHAnimHierarchy**)data = RpSkinAtomicGetHAnimHierarchy(atomic);
	return nil;
}

RpHAnimHierarchy*
GetAnimHierarchyFromSkinClump(RpClump *clump)
{
	RpHAnimHierarchy *hier = nil;
	RpClumpForAllAtomics(clump, GetAnimHierarchyCallback, &hier);
	return hier;
}

static RwFrame*
GetAnimHierarchyFromClumpCB(RwFrame *frame, void *data)
{
	RpHAnimHierarchy *hier = RpHAnimFrameGetHierarchy(frame);
	if(hier){
		*(RpHAnimHierarchy**)data = hier;
		return nil;
	}
	RwFrameForAllChildren(frame, GetAnimHierarchyFromClumpCB, data);
	return frame;
}

RpHAnimHierarchy*
GetAnimHierarchyFromClump(RpClump *clump)
{
	RpHAnimHierarchy *hier = nil;
	RwFrameForAllChildren(RpClumpGetFrame(clump), GetAnimHierarchyFromClumpCB, &hier);
	return hier;
}

RwFrame*
GetHierarchyFromChildNodesCB(RwFrame *frame, void *data)
{
	RpHAnimHierarchy **pHier = (RpHAnimHierarchy**)data;
	RpHAnimHierarchy *hier = RpHAnimFrameGetHierarchy(frame);
	if(hier == nil)
		RwFrameForAllChildren(frame, GetHierarchyFromChildNodesCB, &hier);
	*pHier = hier;
	return nil;
}

void
SkinGetBonePositionsToTable(RpClump *clump, RwV3d *boneTable)
{
	int i, parent;
	RpAtomic *atomic;
	RpSkin *skin;
	RpHAnimHierarchy *hier;
	int numBones;
	RwMatrix m, invmat;
	int stack[32];
	int sp;

	if(boneTable == nil)
		return;

//	atomic = GetFirstAtomic(clump);		// mobile, also VC
	atomic = IsClumpSkinned(clump);		// xbox, seems safer
	assert(atomic);
	skin = RpSkinGeometryGetSkin(RpAtomicGetGeometry(atomic));
	assert(skin);
	hier = GetAnimHierarchyFromSkinClump(clump);
	assert(hier);
	boneTable[0].x = 0.0f;
	boneTable[0].y = 0.0f;
	boneTable[0].z = 0.0f;
	numBones = RpSkinGetNumBones(skin);
	parent = 0;
	sp = 0;
#ifdef FIX_BUGS
	stack[0] = 0;	// i think this is ok
#endif
	for(i = 1; i < numBones; i++){
		RwMatrixCopy(&m, &RpSkinGetSkinToBoneMatrices(skin)[i]);
		RwMatrixInvert(&invmat, &m);
		const RwMatrix *x = RpSkinGetSkinToBoneMatrices(skin);
		RwV3dTransformPoints(&boneTable[i], &invmat.pos, 1, &x[parent]);
		if(HIERNODEINFO(hier)[i].flags & rpHANIMPUSHPARENTMATRIX)
			stack[++sp] = parent;
		if(HIERNODEINFO(hier)[i].flags & rpHANIMPOPPARENTMATRIX)
			parent = stack[sp--];
		else
			parent = i;

		//assert(parent >= 0 && parent < numBones);
	}
}

RpHAnimAnimation*
HAnimAnimationCreateForHierarchy(RpHAnimHierarchy *hier)
{
	int i;
#if defined FIX_BUGS || defined LIBRW
	int numNodes = hier->numNodes*2;	// you're supposed to have at least two KFs per node
#else
	int numNodes = hier->numNodes;
#endif
	RpHAnimAnimation *anim = RpHAnimAnimationCreate(rpHANIMSTDKEYFRAMETYPEID, numNodes, 0, 0.0f);
	if(anim == nil)
		return nil;
	RpHAnimStdKeyFrame *frame;
	for(i = 0; i < numNodes; i++){
		frame = (RpHAnimStdKeyFrame*)HANIMFRAME(anim, i);	// games uses struct size here, not safe
		frame->q.real = 1.0f;
		frame->q.imag.x = frame->q.imag.y = frame->q.imag.z = 0.0f;
		frame->t.x = frame->t.y = frame->t.z = 0.0f;
#if defined FIX_BUGS || defined LIBRW
		// times are subtracted and divided giving NaNs
		// so they can't both be 0
		frame->time = i/hier->numNodes;
#else
		frame->time = 0.0f;
#endif
		frame->prevFrame = nil;
	}
	return anim;
}

void
RenderSkeleton(RpHAnimHierarchy *hier)
{
	int i;
	int sp;
	int stack[32];
	int par;
	CVector p1, p2;
	int numNodes = hier->numNodes;
	RwMatrix *mats = RpHAnimHierarchyGetMatrixArray(hier);
	p1 = mats[0].pos;

	par = 0;
	sp = 0;
	stack[sp++] = par;
	for(i = 1; i < numNodes; i++){
		p1 = mats[par].pos;
		p2 = mats[i].pos;
		CDebug::AddLine(p1, p2, 0xFFFFFFFF, 0xFFFFFFFF);
		if(HIERNODEINFO(hier)[i].flags & rpHANIMPUSHPARENTMATRIX)
			stack[sp++] = par;
		par = i;
		if(HIERNODEINFO(hier)[i].flags & rpHANIMPOPPARENTMATRIX)
			par = stack[--sp];
	}
}
#endif

void
CameraSize(RwCamera * camera, RwRect * rect,
		   RwReal viewWindow, RwReal aspectRatio)
{
	if (camera)
	{
		RwVideoMode         videoMode;
		RwRect              r;
		RwRect              origSize = { 0, 0, 0, 0 };	// FIX just to make the compier happy
		RwV2d               vw;

		RwEngineGetVideoModeInfo(&videoMode,
								 RwEngineGetCurrentVideoMode());

		origSize.w  = RwRasterGetWidth(RwCameraGetRaster(camera));
		origSize.h = RwRasterGetHeight(RwCameraGetRaster(camera));

		if (!rect)
		{
			if (videoMode.flags & rwVIDEOMODEEXCLUSIVE)
			{
				/* For full screen applications, resizing the camera just doesn't
				 * make sense, use the video mode size.
				 */

				r.x = r.y = 0;
				r.w = videoMode.width;
				r.h = videoMode.height;
				rect = &r;
			}
			else
			{
				/*
				rect not specified - reuse current values
				*/
				r.w = RwRasterGetWidth(RwCameraGetRaster(camera));
				r.h = RwRasterGetHeight(RwCameraGetRaster(camera));
				r.x = r.y = 0;
				rect = &r;
			}
		}

		if (( origSize.w != rect->w ) || ( origSize.h != rect->h ))
		{
			RwRaster           *raster;
			RwRaster           *zRaster;

			// BUG: game just changes camera raster's sizes, but this is a hack
#if defined FIX_BUGS || defined LIBRW
			/*
			 * Destroy rasters...
			 */

			raster = RwCameraGetRaster(camera);
			if( raster )
			{
				RwRasterDestroy(raster);
				camera->frameBuffer = nil;
			}

			zRaster = RwCameraGetZRaster(camera);
			if( zRaster )
			{
				RwRasterDestroy(zRaster);
				camera->zBuffer = nil;
			}

			/*
			 * Create new rasters...
			 */

			raster = RwRasterCreate(rect->w, rect->h, 0, rwRASTERTYPECAMERA);
			zRaster = RwRasterCreate(rect->w, rect->h, 0, rwRASTERTYPEZBUFFER);

			if( raster && zRaster )
			{
				RwCameraSetRaster(camera, raster);
				RwCameraSetZRaster(camera, zRaster);
			}
			else
			{
				if( raster )
				{
					RwRasterDestroy(raster);
				}

				if( zRaster )
				{
					RwRasterDestroy(zRaster);
				}

				rect->x = origSize.x;
				rect->y = origSize.y;
				rect->w = origSize.w;
				rect->h = origSize.h;

				/*
				 * Use default values...
				 */
				raster =
					RwRasterCreate(rect->w, rect->h, 0, rwRASTERTYPECAMERA);

				zRaster =
					RwRasterCreate(rect->w, rect->h, 0, rwRASTERTYPEZBUFFER);

				RwCameraSetRaster(camera, raster);
				RwCameraSetZRaster(camera, zRaster);
			}
#else
			raster = RwCameraGetRaster(camera);
			zRaster = RwCameraGetZRaster(camera);

			raster->width = zRaster->width = rect->w;
			raster->height = zRaster->height = rect->h;
#endif
#ifdef FIX_BUGS
			if(CMBlur::BlurOn){
				CMBlur::MotionBlurClose();
				CMBlur::MotionBlurOpen(camera);
			}
#endif
		}

		/* Figure out the view window */
		if (videoMode.flags & rwVIDEOMODEEXCLUSIVE)
		{
			/* derive ratio from aspect ratio */
			vw.x = viewWindow;
			vw.y = viewWindow / aspectRatio;
		}
		else
		{
			/* derive from pixel ratios */
			if (rect->w > rect->h)
			{
				vw.x = viewWindow;
				vw.y = (rect->h * viewWindow) / rect->w;
			}
			else
			{
				vw.x = (rect->w * viewWindow) / rect->h;
				vw.y = viewWindow;
			}
		}

		RwCameraSetViewWindow(camera, &vw);

		RsGlobal.width  = rect->w;
		RsGlobal.height = rect->h;
	}

	return;
}

void
CameraDestroy(RwCamera *camera)
{
	RwRaster    *raster, *tmpRaster;
	RwFrame     *frame;

	if (camera)
	{
		frame = RwCameraGetFrame(camera);
		if (frame)
		{
			RwFrameDestroy(frame);
		}

		raster = RwCameraGetRaster(camera);
		if (raster)
		{
			tmpRaster = RwRasterGetParent(raster);

			RwRasterDestroy(raster);

			if ((tmpRaster != nil) && (tmpRaster != raster))
			{
				RwRasterDestroy(tmpRaster);
			}
		}

		raster = RwCameraGetZRaster(camera);
		if (raster)
		{
			tmpRaster = RwRasterGetParent(raster);

			RwRasterDestroy(raster);

			if ((tmpRaster != nil) && (tmpRaster != raster))
			{
				RwRasterDestroy(tmpRaster);
			}
		}

		RwCameraDestroy(camera);
	}

	return;
}

RwCamera           *
CameraCreate(RwInt32 width, RwInt32 height, RwBool zBuffer)
{
	RwCamera           *camera;

	camera = RwCameraCreate();

	if (camera)
	{
		RwCameraSetFrame(camera, RwFrameCreate());
		RwCameraSetRaster(camera,
						  RwRasterCreate(0, 0, 0, rwRASTERTYPECAMERA));

		if (zBuffer)
		{
			RwCameraSetZRaster(camera,
							   RwRasterCreate(0, 0, 0,
											  rwRASTERTYPEZBUFFER));
		}

		/* now check that everything is valid */
		if (RwCameraGetFrame(camera) &&
			RwCameraGetRaster(camera) &&
			RwRasterGetParent(RwCameraGetRaster(camera)) &&
			(!zBuffer || (RwCameraGetZRaster(camera) &&
						  RwRasterGetParent(RwCameraGetZRaster
											(camera)))))
		{
			/* everything OK */
			return (camera);
		}
	}

	/* if we're here then an error must have occurred so clean up */

	CameraDestroy(camera);
	return (nil);
}

#ifdef LIBRW
#include <rpmatfx.h>
#include "VehicleModelInfo.h"

int32
findPlatform(rw::Atomic *a)
{
	rw::Geometry *g = a->geometry;
	if(g->instData)
		return g->instData->platform;
	return 0;
}

// in CVehicleModelInfo in VC
static RpMaterial*
GetMatFXEffectMaterialCB(RpMaterial *material, void *data)
{
	if(RpMatFXMaterialGetEffects(material) == rpMATFXEFFECTNULL)
		return material;
	*(int*)data = RpMatFXMaterialGetEffects(material);
	return nil;
}

// Game doesn't read atomic extensions so we never get any other than the default pipe,
// but we need it for uninstancing
void
attachPipe(rw::Atomic *atomic)
{
	if(RpSkinGeometryGetSkin(RpAtomicGetGeometry(atomic)))
		atomic->pipeline = rw::skinGlobals.pipelines[rw::platform];
	else{
		int fx = rpMATFXEFFECTNULL;
		RpGeometryForAllMaterials(RpAtomicGetGeometry(atomic), GetMatFXEffectMaterialCB, &fx);
		if(fx != rpMATFXEFFECTNULL)
			RpMatFXAtomicEnableEffects(atomic);
	}
}

// Attach pipes for the platform we have native data for so we can uninstance
void
switchPipes(rw::Atomic *a, int32 platform)
{
	if(a->pipeline && a->pipeline->platform != platform){
		uint32 plgid = a->pipeline->pluginID;
		switch(plgid){
		// assume default pipe won't be attached explicitly
		case rw::ID_SKIN:
			a->pipeline = rw::skinGlobals.pipelines[platform];
			break;
		case rw::ID_MATFX:
			a->pipeline = rw::matFXGlobals.pipelines[platform];
			break;
		}
	}
}

RpAtomic*
ConvertPlatformAtomic(RpAtomic *atomic, void *data)
{
	int32 driver = rw::platform;
	int32 platform = findPlatform(atomic);
	if(platform != 0 && platform != driver){
		attachPipe(atomic);	// kludge
		rw::ObjPipeline *origPipe = atomic->pipeline;
		rw::platform = platform;
		switchPipes(atomic, rw::platform);
		if(atomic->geometry->flags & rw::Geometry::NATIVE)
			atomic->uninstance();
		// no ADC in this game
		//rw::ps2::unconvertADC(atomic->geometry);
		rw::platform = driver;
		atomic->pipeline = origPipe;
	}
	return atomic;
}
#endif

#if defined(FIX_BUGS) && defined(GTA_PC)
RwUInt32 saved_alphafunc, saved_alpharef;

void
SetAlphaTest(RwUInt32 alpharef)
{
#ifdef LIBRW
	saved_alphafunc = rw::GetRenderState(rw::ALPHATESTFUNC);
	saved_alpharef = rw::GetRenderState(rw::ALPHATESTREF);

	rw::SetRenderState(rw::ALPHATESTFUNC, rw::ALPHAGREATEREQUAL);
	rw::SetRenderState(rw::ALPHATESTREF, 0);
#else
	RwD3D8GetRenderState(D3DRS_ALPHAFUNC, &saved_alphafunc);
	RwD3D8GetRenderState(D3DRS_ALPHAREF, &saved_alpharef);

	RwD3D8SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);
	RwD3D8SetRenderState(D3DRS_ALPHAREF, alpharef);
#endif
}

void
RestoreAlphaTest()
{
#ifdef LIBRW
	rw::SetRenderState(rw::ALPHATESTFUNC, saved_alphafunc);
	rw::SetRenderState(rw::ALPHATESTREF, saved_alpharef);
#else
	RwD3D8SetRenderState(D3DRS_ALPHAFUNC, saved_alphafunc);
	RwD3D8SetRenderState(D3DRS_ALPHAREF, saved_alpharef);
#endif
}
#endif


code:
#pragma once

extern bool bDebugRenderGroups;
extern bool gPS2alphaTest;

void OpenCharsetSafe();
void CreateDebugFont();
void DestroyDebugFont();
void ObrsPrintfString(const char *str, short x, short y);
void FlushObrsPrintfs();
void DefinedState(void);
void SetAlphaRef(int ref);
void SetCullMode(uint32 mode);
RwFrame *GetFirstChild(RwFrame *frame);
RwObject *GetFirstObject(RwFrame *frame);
RpAtomic *GetFirstAtomic(RpClump *clump);
RwTexture *GetFirstTexture(RwTexDictionary *txd);

#ifdef PED_SKIN
RpAtomic *IsClumpSkinned(RpClump *clump);
RpHAnimHierarchy *GetAnimHierarchyFromSkinClump(RpClump *clump);	// get from atomic
RpHAnimHierarchy *GetAnimHierarchyFromClump(RpClump *clump);	// get from frame
RwFrame *GetHierarchyFromChildNodesCB(RwFrame *frame, void *data);
void SkinGetBonePositionsToTable(RpClump *clump, RwV3d *boneTable);
RpHAnimAnimation *HAnimAnimationCreateForHierarchy(RpHAnimHierarchy *hier);
RpAtomic *AtomicRemoveAnimFromSkinCB(RpAtomic *atomic, void *data);
void RenderSkeleton(RpHAnimHierarchy *hier);
#endif

RwTexDictionary *RwTexDictionaryGtaStreamRead(RwStream *stream);
RwTexDictionary *RwTexDictionaryGtaStreamRead1(RwStream *stream);
RwTexDictionary *RwTexDictionaryGtaStreamRead2(RwStream *stream, RwTexDictionary *texDict);
void ReadVideoCardCapsFile(uint32&, uint32&, uint32&, uint32&);
bool CheckVideoCardCaps(void);
void WriteVideoCardCapsFile(void);
void ConvertingTexturesScreen(uint32, uint32, const char*);
void DealWithTxdWriteError(uint32, uint32, const char*);
bool CreateTxdImageForVideoCard();

bool RpClumpGtaStreamRead1(RwStream *stream);
RpClump *RpClumpGtaStreamRead2(RwStream *stream);
void RpClumpGtaCancelStream(void);

void CameraSize(RwCamera *camera,
                RwRect *rect,
                RwReal viewWindow,
                RwReal aspectRatio);
void CameraDestroy(RwCamera *camera);
RwCamera *CameraCreate(RwInt32 width,
                       RwInt32 height,
                       RwBool zBuffer);

RpAtomic *ConvertPlatformAtomic(RpAtomic *atomic, void *data);

#if defined(FIX_BUGS) && defined (GTA_PC)
void SetAlphaTest(RwUInt32 alpharef);
void RestoreAlphaTest();
#else
#define SetAlphaTest(a) (0)
#define RestoreAlphaTest() (0)
#endif


code:
#ifndef LIBRW

#define WITHD3D
#include "common.h"
#include "rpmatfx.h"

struct MatFXNothing { int pad[5]; int effect; };

struct MatFXBump
{
	RwFrame *bumpFrame;
	RwTexture *bumpedTex;
	RwTexture *bumpTex;
	float negBumpCoefficient;
	int pad;
	int effect;
};

struct MatFXEnv
{
	RwFrame *envFrame;
	RwTexture *envTex;
	float envCoeff;
	int envFBalpha;
	int pad;
	int effect;
};

struct MatFXDual
{
	RwTexture *dualTex;
	RwInt32 srcBlend;
	RwInt32 dstBlend;
};

struct MatFX
{
	union {
		MatFXNothing n;
		MatFXBump b;
		MatFXEnv e;
		MatFXDual d;
	} fx[2];
	int effects;
};

extern "C" {
	extern int MatFXMaterialDataOffset;
	extern int MatFXAtomicDataOffset;

	void _rpMatFXD3D8AtomicMatFXEnvRender(RxD3D8InstanceData* inst, int flags, int sel, RwTexture* texture, RwTexture* envMap);
	void _rpMatFXD3D8AtomicMatFXRenderBlack(RxD3D8InstanceData *inst);
	void _rpMatFXD3D8AtomicMatFXBumpMapRender(RxD3D8InstanceData *inst, int flags, RwTexture *texture, RwTexture *bumpMap, RwTexture *envMap);
	void _rpMatFXD3D8AtomicMatFXDualPassRender(RxD3D8InstanceData *inst, int flags, RwTexture *texture, RwTexture *dualTexture);
}

#ifdef PS2_MATFX

void
_rpMatFXD3D8AtomicMatFXDefaultRender(RxD3D8InstanceData *inst, int flags, RwTexture *texture)
{
	if(flags & (rpGEOMETRYTEXTURED|rpGEOMETRYTEXTURED2) && texture)
		RwD3D8SetTexture(texture, 0);
	else
		RwD3D8SetTexture(nil, 0);
	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)(inst->vertexAlpha || inst->material->color.alpha != 0xFF));
	RwD3D8SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, inst->vertexAlpha != 0);
	RwD3D8SetPixelShader(0);
	RwD3D8SetVertexShader(inst->vertexShader);
	RwD3D8SetStreamSource(0, inst->vertexBuffer, inst->stride);

	if(inst->indexBuffer){
		RwD3D8SetIndices(inst->indexBuffer, inst->baseIndex);
		RwD3D8DrawIndexedPrimitive(inst->primType, 0, inst->numVertices, 0, inst->numIndices);
	}else
		RwD3D8DrawPrimitive(inst->primType, inst->baseIndex, inst->numVertices);
}

// map [-1; -1] -> [0; 1], flip V
static RwMatrix scalenormal = {
	{ 0.5f, 0.0f, 0.0f }, 0,
	{ 0.0f, -0.5f, 0.0f }, 0,
	{ 0.0f, 0.0f, 1.0f }, 0,
	{ 0.5f, 0.5f, 0.0f }, 0,

};

// flipped U for PS2
static RwMatrix scalenormal_flipU = {
	{ -0.5f, 0.0f, 0.0f }, 0,
	{ 0.0f, -0.5f, 0.0f }, 0,
	{ 0.0f, 0.0f, 1.0f }, 0,
	{ 0.5f, 0.5f, 0.0f }, 0,

};

void
ApplyEnvMapTextureMatrix(RwTexture *tex, int n, RwFrame *frame)
{
	RwD3D8SetTexture(tex, n);
	RwD3D8SetTextureStageState(n, D3DRS_ALPHAREF, 2);
	RwD3D8SetTextureStageState(n, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACENORMAL);
	if(frame){
		RwMatrix *envframemat = RwMatrixCreate();
		RwMatrix *tmpmat = RwMatrixCreate();
		RwMatrix *envmat = RwMatrixCreate();

		RwMatrixInvert(envframemat, RwFrameGetLTM(frame));
		// PS2
		// can this be simplified?
		*tmpmat = *RwFrameGetLTM(RwCameraGetFrame((RwCamera*)RWSRCGLOBAL(curCamera)));
		RwV3dNegate(&tmpmat->right, &tmpmat->right);
		tmpmat->flags = 0;
		tmpmat->pos.x = 0.0f;
		tmpmat->pos.y = 0.0f;
		tmpmat->pos.z = 0.0f;
		RwMatrixMultiply(envmat, tmpmat, envframemat);
		*tmpmat = *envmat;
		// important because envframemat can have a translation that we don't like
		tmpmat->pos.x = 0.0f;
		tmpmat->pos.y = 0.0f;
		tmpmat->pos.z = 0.0f;
		// for some reason we flip in U as well
		RwMatrixMultiply(envmat, tmpmat, &scalenormal_flipU);

		RwD3D8SetTransform(D3DTS_TEXTURE0+n, envmat);

		RwMatrixDestroy(envmat);
		RwMatrixDestroy(tmpmat);
		RwMatrixDestroy(envframemat);
	}else
		RwD3D8SetTransform(D3DTS_TEXTURE0+n, &scalenormal);
}

void
_rpMatFXD3D8AtomicMatFXEnvRender_ps2(RxD3D8InstanceData *inst, int flags, int sel, RwTexture *texture, RwTexture *envMap)
{
	MatFX *matfx = *RWPLUGINOFFSET(MatFX*, inst->material, MatFXMaterialDataOffset);
	MatFXEnv *env = &matfx->fx[sel].e;

	uint8 intens = (uint8)(env->envCoeff*255.0f);

	if(intens == 0 || envMap == nil){
		if(sel == 0)
			_rpMatFXD3D8AtomicMatFXDefaultRender(inst, flags, texture);
		return;
	}

	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)(inst->vertexAlpha || inst->material->color.alpha != 0xFF));
	if(flags & (rpGEOMETRYTEXTURED|rpGEOMETRYTEXTURED2) && texture)
		RwD3D8SetTexture(texture, 0);
	else
		RwD3D8SetTexture(nil, 0);
	RwD3D8SetPixelShader(0);
	RwD3D8SetVertexShader(inst->vertexShader);
	RwD3D8SetStreamSource(0, inst->vertexBuffer, inst->stride);
	RwD3D8SetIndices(inst->indexBuffer, inst->baseIndex);
	if(inst->indexBuffer)
		RwD3D8DrawIndexedPrimitive(inst->primType, 0, inst->numVertices, 0, inst->numIndices);
	else
		RwD3D8DrawPrimitive(inst->primType, inst->baseIndex, inst->numVertices);

	// Effect pass

	ApplyEnvMapTextureMatrix(envMap, 0, env->envFrame);
	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
	RwUInt32 src, dst, lighting, zwrite, fog, fogcol;
	RwRenderStateGet(rwRENDERSTATESRCBLEND, &src);
	RwRenderStateGet(rwRENDERSTATEDESTBLEND, &dst);

	// This is of course not using framebuffer alpha,
	// but if the diffuse texture had no alpha, the result should actually be rather the same
	if(env->envFBalpha)
		RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
	else
		RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDONE);
	RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDONE);
	RwD3D8GetRenderState(D3DRS_LIGHTING, &lighting);
	RwD3D8GetRenderState(D3DRS_ZWRITEENABLE, &zwrite);
	RwD3D8GetRenderState(D3DRS_FOGENABLE, &fog);
	RwD3D8SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
	if(fog){
		RwD3D8GetRenderState(D3DRS_FOGCOLOR, &fogcol);
		RwD3D8SetRenderState(D3DRS_FOGCOLOR, 0);
	}

	D3DCOLOR texfactor = D3DCOLOR_RGBA(intens, intens, intens, intens);
	RwD3D8SetRenderState(D3DRS_TEXTUREFACTOR, texfactor);
	RwD3D8SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE);
	RwD3D8SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
	RwD3D8SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_TFACTOR);
	// alpha unused
	//RwD3D8SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
	//RwD3D8SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
	//RwD3D8SetTextureStageState(1, D3DTSS_ALPHAARG2, D3DTA_TFACTOR);

	if(inst->indexBuffer)
		RwD3D8DrawIndexedPrimitive(inst->primType, 0, inst->numVertices, 0, inst->numIndices);
	else
		RwD3D8DrawPrimitive(inst->primType, inst->baseIndex, inst->numVertices);

	// Reset states

	RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)FALSE);
	RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)src);
	RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)dst);
	RwD3D8SetRenderState(D3DRS_LIGHTING, lighting);
	RwD3D8SetRenderState(D3DRS_ZWRITEENABLE, zwrite);
	if(fog)
		RwD3D8SetRenderState(D3DRS_FOGCOLOR, fogcol);
	RwD3D8SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
	RwD3D8SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
	RwD3D8SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, 0);
	RwD3D8SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
}

void
_rwD3D8EnableClippingIfNeeded(void *object, RwUInt8 type)
{
	int clip;
	if (type == rpATOMIC)
		clip = !RwD3D8CameraIsSphereFullyInsideFrustum(RwCameraGetCurrentCameraMacro(), RpAtomicGetWorldBoundingSphere((RpAtomic *)object));
	else
		clip = !RwD3D8CameraIsBBoxFullyInsideFrustum(RwCameraGetCurrentCameraMacro(), &((RpWorldSector *)object)->tightBoundingBox);
	RwD3D8SetRenderState(D3DRS_CLIPPING, clip);
}

void
_rwD3D8AtomicMatFXRenderCallback(RwResEntry *repEntry, void *object, RwUInt8 type, RwUInt32 flags)
{
	RwBool lighting;
	RwBool forceBlack;
	RxD3D8ResEntryHeader *header;
	RxD3D8InstanceData *inst;
	RwInt32 i;

	if (flags & rpGEOMETRYPRELIT) {
		RwD3D8SetRenderState(D3DRS_COLORVERTEX, 1);
		RwD3D8SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_COLOR1);
	} else {
		RwD3D8SetRenderState(D3DRS_COLORVERTEX, 0);
		RwD3D8SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL);
	}

	_rwD3D8EnableClippingIfNeeded(object, type);

	RwD3D8GetRenderState(D3DRS_LIGHTING, &lighting);
	if (lighting || flags & rpGEOMETRYPRELIT) {
		forceBlack = FALSE;
	} else {
		forceBlack = TRUE;
		RwD3D8SetTexture(nil, 0);
		RwD3D8SetRenderState(D3DRS_TEXTUREFACTOR, D3DCOLOR_RGBA(0, 0, 0, 255));
		RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2);
		RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR);
	}

	header = (RxD3D8ResEntryHeader *)(repEntry + 1);
	inst = (RxD3D8InstanceData *)(header + 1);
	for (i = 0; i < header->numMeshes; i++) {
		if (forceBlack)
			_rpMatFXD3D8AtomicMatFXRenderBlack(inst);
		else {
			if (lighting)
				RwD3D8SetSurfaceProperties(&inst->material->color, &inst->material->surfaceProps, flags & rpGEOMETRYMODULATEMATERIALCOLOR);
			MatFX *matfx = *RWPLUGINOFFSET(MatFX *, inst->material, MatFXMaterialDataOffset);
			int effect = matfx ? matfx->effects : rpMATFXEFFECTNULL;
			switch (effect) {
			case rpMATFXEFFECTNULL:
			default:
				_rpMatFXD3D8AtomicMatFXDefaultRender(inst, flags, inst->material->texture);
				break;
			case rpMATFXEFFECTBUMPMAP:
				_rpMatFXD3D8AtomicMatFXBumpMapRender(inst, flags, inst->material->texture, matfx->fx[0].b.bumpedTex, nil);
				break;
			case rpMATFXEFFECTENVMAP:
			{
				// TODO: matfx switch in the settings
				//_rpMatFXD3D8AtomicMatFXEnvRender(inst, flags, 0, inst->material->texture, matfx->fx[0].e.envTex);
				_rpMatFXD3D8AtomicMatFXEnvRender_ps2(inst, flags, 0, inst->material->texture, matfx->fx[0].e.envTex);
				break;
			}
			case rpMATFXEFFECTBUMPENVMAP:
				_rpMatFXD3D8AtomicMatFXBumpMapRender(inst, flags, inst->material->texture, matfx->fx[0].b.bumpedTex, matfx->fx[1].e.envTex);
				break;
			case rpMATFXEFFECTDUAL:
				_rpMatFXD3D8AtomicMatFXDualPassRender(inst, flags, inst->material->texture, matfx->fx[0].d.dualTex);
				break;
			}
		}
		inst++;
	}

	if (forceBlack) {
		RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2);
		RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
	}
}

void
ReplaceMatFxCallback()
{
	RxD3D8AllInOneSetRenderCallBack(
	    RxPipelineFindNodeByName(RpMatFXGetD3D8Pipeline(rpMATFXD3D8ATOMICPIPELINE), RxNodeDefinitionGetD3D8AtomicAllInOne()->name, nil, nil),
	    _rwD3D8AtomicMatFXRenderCallback);

}
#endif // PS2_MATFX

#endif // !LIBRW


code:
#ifndef LIBRW

#define WITHD3D
#include "common.h"
#ifdef PS2_ALPHA_TEST
#include "rwcore.h"

extern "C" {
RwBool _rwD3D8RenderStateIsVertexAlphaEnable(void);
RwBool _rwD3D8RenderStateVertexAlphaEnable(RwBool enable);
RwRaster *_rwD3D8RWGetRasterStage(RwUInt32 stage);
}

extern bool gPS2alphaTest;

void
_rxD3D8DualPassRenderCallback(RwResEntry *repEntry, void *object, RwUInt8 type, RwUInt32 flags)
{
	RxD3D8ResEntryHeader *resEntryHeader;
	RxD3D8InstanceData *instancedData;
	RwInt32 numMeshes;
	RwBool lighting;
	RwBool vertexAlphaBlend;
	RwBool forceBlack;
	RwUInt32 ditherEnable;
	RwUInt32 shadeMode;
	void *lastVertexBuffer;

	/* Get lighting state */
	RwD3D8GetRenderState(D3DRS_LIGHTING, &lighting);

	forceBlack = FALSE;

	if (lighting) {
		if (flags & rxGEOMETRY_PRELIT) {
			/* Emmisive color from the vertex colors */
			RwD3D8SetRenderState(D3DRS_COLORVERTEX, TRUE);
			RwD3D8SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_COLOR1);
		} else {
			/* Emmisive color from material, set to black in the submit node */
			RwD3D8SetRenderState(D3DRS_COLORVERTEX, FALSE);
			RwD3D8SetRenderState(D3DRS_EMISSIVEMATERIALSOURCE, D3DMCS_MATERIAL);
		}
	} else {
		if ((flags & rxGEOMETRY_PRELIT) == 0) {
			forceBlack = TRUE;

			RwD3D8GetRenderState(D3DRS_DITHERENABLE, &ditherEnable);
			RwD3D8GetRenderState(D3DRS_SHADEMODE, &shadeMode);

			RwD3D8SetRenderState(D3DRS_TEXTUREFACTOR, 0xff000000);
			RwD3D8SetRenderState(D3DRS_DITHERENABLE, FALSE);
			RwD3D8SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
		}
	}

	/* Enable clipping */
	if (type == rpATOMIC) {
		RpAtomic *atomic;
		RwCamera *cam;

		atomic = (RpAtomic *)object;

		cam = RwCameraGetCurrentCamera();
		// RWASSERT(cam);

		if (RwD3D8CameraIsSphereFullyInsideFrustum(cam, RpAtomicGetWorldBoundingSphere(atomic))) {
			RwD3D8SetRenderState(D3DRS_CLIPPING, FALSE);
		} else {
			RwD3D8SetRenderState(D3DRS_CLIPPING, TRUE);
		}
	} else {
		RpWorldSector *worldSector;
		RwCamera *cam;

		worldSector = (RpWorldSector *)object;

		cam = RwCameraGetCurrentCamera();
		// RWASSERT(cam);

		if (RwD3D8CameraIsBBoxFullyInsideFrustum(cam, RpWorldSectorGetTightBBox(worldSector))) {
			RwD3D8SetRenderState(D3DRS_CLIPPING, FALSE);
		} else {
			RwD3D8SetRenderState(D3DRS_CLIPPING, TRUE);
		}
	}

	/* Set texture to NULL if hasn't any texture flags */
	if ((flags & (rxGEOMETRY_TEXTURED | rpGEOMETRYTEXTURED2)) == 0) {
		RwD3D8SetTexture(NULL, 0);

		if (forceBlack) {
			RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2);
			RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR);

			RwD3D8SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
		}
	}

	/* Get vertex alpha Blend state */
	vertexAlphaBlend = _rwD3D8RenderStateIsVertexAlphaEnable();

	/* Set Last vertex buffer to force the call */
	lastVertexBuffer = (void *)0xffffffff;

	/* Get the instanced data */
	resEntryHeader = (RxD3D8ResEntryHeader *)(repEntry + 1);
	instancedData = (RxD3D8InstanceData *)(resEntryHeader + 1);

	/*
	 * Data shared between meshes
	 */

	/*
	 * Set the Default Pixel shader
	 */
	RwD3D8SetPixelShader(0);

	/*
	 * Vertex shader
	 */
	RwD3D8SetVertexShader(instancedData->vertexShader);

	/* Get the number of meshes */
	numMeshes = resEntryHeader->numMeshes;
	while (numMeshes--) {
		// RWASSERT(instancedData->material != NULL);

		if ((flags & (rxGEOMETRY_TEXTURED | rpGEOMETRYTEXTURED2))) {
			RwD3D8SetTexture(instancedData->material->texture, 0);

			if (forceBlack) {
				/* Only change the colorop, we need to use the texture alpha channel */
				RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2);
				RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR);
			}
		}

		if (instancedData->vertexAlpha || (0xFF != instancedData->material->color.alpha)) {
			if (!vertexAlphaBlend) {
				vertexAlphaBlend = TRUE;

				_rwD3D8RenderStateVertexAlphaEnable(TRUE);
			}
		} else {
			if (vertexAlphaBlend) {
				vertexAlphaBlend = FALSE;

				_rwD3D8RenderStateVertexAlphaEnable(FALSE);
			}
		}

		if (lighting) {
			if (instancedData->vertexAlpha) {
				RwD3D8SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);
			} else {
				RwD3D8SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
			}

			RwD3D8SetSurfaceProperties(&instancedData->material->color, &instancedData->material->surfaceProps, (flags & rxGEOMETRY_MODULATE));
		}

		/*
		 * Render
		 */

		/* Set the stream source */
		if (lastVertexBuffer != instancedData->vertexBuffer) {
			RwD3D8SetStreamSource(0, instancedData->vertexBuffer, instancedData->stride);

			lastVertexBuffer = instancedData->vertexBuffer;
		}
		if (!gPS2alphaTest) {
			/* Set the Index buffer */
			if (instancedData->indexBuffer != NULL) {
				RwD3D8SetIndices(instancedData->indexBuffer, instancedData->baseIndex);

				/* Draw the indexed primitive */
				RwD3D8DrawIndexedPrimitive((D3DPRIMITIVETYPE)instancedData->primType, 0, instancedData->numVertices, 0, instancedData->numIndices);
			} else {
				RwD3D8DrawPrimitive((D3DPRIMITIVETYPE)instancedData->primType, instancedData->baseIndex, instancedData->numVertices);
			}
		} else {
			RwD3D8SetIndices(instancedData->indexBuffer, instancedData->baseIndex);

			int hasAlpha, alphafunc, alpharef, zwrite;
			RwD3D8GetRenderState(D3DRS_ALPHABLENDENABLE, &hasAlpha);
			RwD3D8GetRenderState(D3DRS_ZWRITEENABLE, &zwrite);
			if (hasAlpha && zwrite) {
				RwD3D8GetRenderState(D3DRS_ALPHAFUNC, &alphafunc);
				RwD3D8GetRenderState(D3DRS_ALPHAREF, &alpharef);

				RwD3D8SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);
				RwD3D8SetRenderState(D3DRS_ALPHAREF, 128);

				if (instancedData->indexBuffer)
					RwD3D8DrawIndexedPrimitive(instancedData->primType, 0, instancedData->numVertices, 0, instancedData->numIndices);
				else
					RwD3D8DrawPrimitive(instancedData->primType, instancedData->baseIndex, instancedData->numVertices);

				RwD3D8SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_LESS);
				RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, FALSE);

				if (instancedData->indexBuffer)
					RwD3D8DrawIndexedPrimitive(instancedData->primType, 0, instancedData->numVertices, 0, instancedData->numIndices);
				else
					RwD3D8DrawPrimitive(instancedData->primType, instancedData->baseIndex, instancedData->numVertices);

				RwD3D8SetRenderState(D3DRS_ALPHAFUNC, alphafunc);
				RwD3D8SetRenderState(D3DRS_ALPHAREF, alpharef);
				RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, (void *)TRUE);
			} else {
				if (instancedData->indexBuffer)
					RwD3D8DrawIndexedPrimitive(instancedData->primType, 0, instancedData->numVertices, 0, instancedData->numIndices);
				else
					RwD3D8DrawPrimitive(instancedData->primType, instancedData->baseIndex, instancedData->numVertices);
			}
		}

		/* Move onto the next instancedData */
		instancedData++;
	}

	if (forceBlack) {
		RwD3D8SetRenderState(D3DRS_DITHERENABLE, ditherEnable);
		RwD3D8SetRenderState(D3DRS_SHADEMODE, shadeMode);

		if (_rwD3D8RWGetRasterStage(0)) {
			RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
			RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
		} else {
			RwD3D8SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2);
			RwD3D8SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
		}
	}
}

void
ReplaceAtomicPipeCallback()
{
	RxD3D8AllInOneSetRenderCallBack(RxPipelineFindNodeByName(RXPIPELINEGLOBAL(platformAtomicPipeline), RxNodeDefinitionGetD3D8AtomicAllInOne()->name, nil, nil),
	                                _rxD3D8DualPassRenderCallback);
}

#endif // PS2_ALPHA_TEST

#endif // !LIBRW


code:
#pragma warning( push )
#pragma warning( disable : 4005)
#pragma warning( pop )
#define FORCE_PC_SCALING
#include "common.h"
#ifdef ANISOTROPIC_FILTERING
#include "rpanisot.h"
#endif
#include "crossplatform.h"
#include "platform.h"

#include "Timer.h"
#ifdef GTA_PC
#include "FileMgr.h"
#include "Pad.h"
#include "main.h"
#include "Directory.h"
#include "Streaming.h"
#include "TxdStore.h"
#include "CdStream.h"
#include "Font.h"
#include "Sprite2d.h"
#include "Text.h"
#include "RwHelper.h"
#include "Frontend.h"
#endif //GTA_PC

float texLoadTime;
int32 texNumLoaded;

#ifdef LIBRW
#define READNATIVE(stream, tex, size) rwNativeTextureHackRead(stream, tex, size)
#else
#define READNATIVE(stream, tex, size) RWSRCGLOBAL(stdFunc[rwSTANDARDNATIVETEXTUREREAD](stream, tex, size))
#endif

RwTexture*
RwTextureGtaStreamRead(RwStream *stream)
{
	RwUInt32 size, version;
	RwTexture *tex;

	if(!RwStreamFindChunk(stream, rwID_TEXTURENATIVE, &size, &version))
		return nil;

	float preloadTime = (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerMillisecond();

	if(!READNATIVE(stream, &tex, size))
		return nil;

	if (gGameState == GS_INIT_PLAYING_GAME) {
		texLoadTime = (texNumLoaded * texLoadTime + (float)CTimer::GetCurrentTimeInCycles() / (float)CTimer::GetCyclesPerMillisecond() - preloadTime) / (float)(texNumLoaded+1);
		texNumLoaded++;
	}

#ifdef ANISOTROPIC_FILTERING
	if(tex && RpAnisotGetMaxSupportedMaxAnisotropy() > 1)	// BUG? this was RpAnisotTextureGetMaxAnisotropy, but that doesn't make much sense
		RpAnisotTextureSetMaxAnisotropy(tex, RpAnisotGetMaxSupportedMaxAnisotropy());
#endif

	return tex;
}

RwTexture*
destroyTexture(RwTexture *texture, void *data)
{
	RwTextureDestroy(texture);
	return texture;
}

RwTexDictionary*
RwTexDictionaryGtaStreamRead(RwStream *stream)
{
	RwUInt32 size, version;
	RwInt32 numTextures;
	RwTexDictionary *texDict;
	RwTexture *tex;

	if(!RwStreamFindChunk(stream, rwID_STRUCT, &size, &version))
		return nil;
	if(RwStreamRead(stream, &numTextures, size) != size)
		return nil;

	texDict = RwTexDictionaryCreate();
	if(texDict == nil)
		return nil;

	while(numTextures--){
		tex = RwTextureGtaStreamRead(stream);
		if(tex == nil){
			RwTexDictionaryForAllTextures(texDict, destroyTexture, nil);
			RwTexDictionaryDestroy(texDict);
			return nil;
		}
		RwTexDictionaryAddTexture(texDict, tex);
	}

	return texDict;
}

static int32 numberTextures = -1;
static int32 streamPosition;

RwTexDictionary*
RwTexDictionaryGtaStreamRead1(RwStream *stream)
{
	RwUInt32 size, version;
	RwInt32 numTextures;
	RwTexDictionary *texDict;
	RwTexture *tex;

	numberTextures = 0;
	if(!RwStreamFindChunk(stream, rwID_STRUCT, &size, &version))
		return nil;
	assert(size == 4);
	if(RwStreamRead(stream, &numTextures, size) != size)
		return nil;

	texDict = RwTexDictionaryCreate();
	if(texDict == nil)
		return nil;

	numberTextures = numTextures/2;

	while(numTextures > numberTextures){
		numTextures--;

		tex = RwTextureGtaStreamRead(stream);
		if(tex == nil){
			RwTexDictionaryForAllTextures(texDict, destroyTexture, nil);
			RwTexDictionaryDestroy(texDict);
			return nil;
		}
		RwTexDictionaryAddTexture(texDict, tex);
	}

	numberTextures = numTextures;
	streamPosition = STREAMPOS(stream);

	return texDict;
}

RwTexDictionary*
RwTexDictionaryGtaStreamRead2(RwStream *stream, RwTexDictionary *texDict)
{
	RwTexture *tex;

	RwStreamSkip(stream, streamPosition - STREAMPOS(stream));

	while(numberTextures--){
		tex = RwTextureGtaStreamRead(stream);
		if(tex == nil){
			RwTexDictionaryForAllTextures(texDict, destroyTexture, nil);
			RwTexDictionaryDestroy(texDict);
			return nil;
		}
		RwTexDictionaryAddTexture(texDict, tex);
	}

	return texDict;
}

#ifdef GTA_PC

#ifdef LIBRW

#define CAPSVERSION 0

struct GPUcaps
{
	uint32 version;	// so we can force regeneration easily
	uint32 platform;
	uint32 subplatform;
	uint32 dxtSupport;
};

static void
GetGPUcaps(GPUcaps *caps)
{
	caps->version = CAPSVERSION;
	caps->platform = rw::platform;
	caps->subplatform = 0;
	caps->dxtSupport = 0;
	// TODO: more later
#ifdef RW_GL3
	caps->subplatform = rw::gl3::gl3Caps.gles;
	caps->dxtSupport = rw::gl3::gl3Caps.dxtSupported;
#endif
#ifdef RW_D3D9
	caps->dxtSupport = 1;	// TODO, probably
#endif
}

void
ReadVideoCardCapsFile(GPUcaps *caps)
{
	memset(caps, 0, sizeof(GPUcaps));

	int32 file = CFileMgr::OpenFile("DATA\\CAPS.DAT", "rb");
	if (file != 0) {
		CFileMgr::Read(file, (char*)&caps->version, 4);
		CFileMgr::Read(file, (char*)&caps->platform, 4);
		CFileMgr::Read(file, (char*)&caps->subplatform, 4);
		CFileMgr::Read(file, (char*)&caps->dxtSupport, 4);
		CFileMgr::CloseFile(file);
	}
}

bool
CheckVideoCardCaps(void)
{
	GPUcaps caps, fcaps;
	GetGPUcaps(&caps);
	ReadVideoCardCapsFile(&fcaps);
	return caps.version != fcaps.version ||
		caps.platform != fcaps.platform ||
		caps.subplatform != fcaps.subplatform ||
		caps.dxtSupport != fcaps.dxtSupport;
}

void
WriteVideoCardCapsFile(void)
{
	GPUcaps caps;
	GetGPUcaps(&caps);
	int32 file = CFileMgr::OpenFile("DATA\\CAPS.DAT", "wb");
	if (file != 0) {
		CFileMgr::Write(file, (char*)&caps.version, 4);
		CFileMgr::Write(file, (char*)&caps.platform, 4);
		CFileMgr::Write(file, (char*)&caps.subplatform, 4);
		CFileMgr::Write(file, (char*)&caps.dxtSupport, 4);
		CFileMgr::CloseFile(file);
	}
}

#else
extern "C" RwInt32 _rwD3D8FindCorrectRasterFormat(RwRasterType type, RwInt32 flags);
void
ReadVideoCardCapsFile(uint32 &cap32, uint32 &cap24, uint32 &cap16, uint32 &cap8)
{
	cap32 = UINT32_MAX;
	cap24 = UINT32_MAX;
	cap16 = UINT32_MAX;
	cap8 = UINT32_MAX;

	int32 file = CFileMgr::OpenFile("DATA\\CAPS.DAT", "rb");
	if (file != 0) {
		CFileMgr::Read(file, (char*)&cap32, 4);
		CFileMgr::Read(file, (char*)&cap24, 4);
		CFileMgr::Read(file, (char*)&cap16, 4);
		CFileMgr::Read(file, (char*)&cap8, 4);
		CFileMgr::CloseFile(file);
	}
}

bool
CheckVideoCardCaps(void)
{
	uint32 cap32 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT8888);
	uint32 cap24 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT888);
	uint32 cap16 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT1555);
	uint32 cap8 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMATPAL8 | rwRASTERFORMAT8888);
	uint32 fcap32, fcap24, fcap16, fcap8;
	ReadVideoCardCapsFile(fcap32, fcap24, fcap16, fcap8);
	return cap32 != fcap32 || cap24 != fcap24 || cap16 != fcap16 || cap8 != fcap8;
}

void
WriteVideoCardCapsFile(void)
{
	uint32 cap32 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT8888);
	uint32 cap24 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT888);
	uint32 cap16 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMAT1555);
	uint32 cap8 = _rwD3D8FindCorrectRasterFormat(rwRASTERTYPETEXTURE, rwRASTERFORMATPAL8 | rwRASTERFORMAT8888);
	int32 file = CFileMgr::OpenFile("DATA\\CAPS.DAT", "wb");
	if (file != 0) {
		CFileMgr::Write(file, (char*)&cap32, 4);
		CFileMgr::Write(file, (char*)&cap24, 4);
		CFileMgr::Write(file, (char*)&cap16, 4);
		CFileMgr::Write(file, (char*)&cap8, 4);
		CFileMgr::CloseFile(file);
	}
}
#endif

void
ConvertingTexturesScreen(uint32 num, uint32 count, const char *text)
{
	HandleExit();

	CSprite2d *splash = LoadSplash(nil);
	if (!DoRWStuffStartOfFrame(0, 0, 0, 0, 0, 0, 255))
		return;

	CSprite2d::SetRecipNearClip();
	CSprite2d::InitPerFrame();
	CFont::InitPerFrame();
	DefinedState();

	RwRenderStateSet(rwRENDERSTATETEXTUREADDRESS, (void*)rwTEXTUREADDRESSCLAMP);
	splash->Draw(CRect(0.0f, 0.0f, SCREEN_WIDTH, SCREEN_HEIGHT), CRGBA(255, 255, 255, 255));

	CSprite2d::DrawRect(CRect(SCREEN_SCALE_X(200.0f), SCREEN_SCALE_Y(240.0f), SCREEN_SCALE_FROM_RIGHT(200.0f), SCREEN_SCALE_Y(248.0f)), CRGBA(64, 64, 64, 255));
	CSprite2d::DrawRect(CRect(SCREEN_SCALE_X(200.0f), SCREEN_SCALE_Y(240.0f), (SCREEN_SCALE_FROM_RIGHT(200.0f) - SCREEN_SCALE_X(200.0f)) * ((float)num / (float)count) + SCREEN_SCALE_X(200.0f), SCREEN_SCALE_Y(248.0f)), CRGBA(255, 217, 106, 255));
	CSprite2d::DrawRect(CRect(SCREEN_SCALE_X(120.0f), SCREEN_SCALE_Y(150.0f), SCREEN_SCALE_FROM_RIGHT(120.0f), SCREEN_HEIGHT - SCREEN_SCALE_Y(220.0f)), CRGBA(50, 50, 50, 210));

	CFont::SetBackgroundOff();
	CFont::SetPropOn();
	CFont::SetScale(SCREEN_SCALE_X(0.45f), SCREEN_SCALE_Y(0.7f));
	CFont::SetCentreOff();
	CFont::SetWrapx(SCREEN_SCALE_FROM_RIGHT(170.0f));
	CFont::SetJustifyOff();
	CFont::SetColor(CRGBA(255, 217, 106, 255));
	CFont::SetBackGroundOnlyTextOff();
	CFont::SetFontStyle(FONT_BANK);
	CFont::PrintString(SCREEN_SCALE_X(170.0f), SCREEN_SCALE_Y(160.0f), TheText.Get(text));
	CFont::DrawFonts();
	DoRWStuffEndOfFrame();
}

void
DealWithTxdWriteError(uint32 num, uint32 count, const char *text)
{
	while (!RsGlobal.quit) {
		ConvertingTexturesScreen(num, count, text);
		CPad::UpdatePads();
		if (CPad::GetPad(0)->GetEscapeJustDown())
			break;
	}
	RsGlobal.quit = false;
	LoadingScreen(nil, nil, nil);
	RsGlobal.quit = true;
}

#ifdef LIBRW
#define STREAMTELL(str)	str->tell()
#else
#define STREAMTELL(str) filesys->rwftell((str)->Type.file.fpFile)
#endif

bool
CreateTxdImageForVideoCard()
{
	uint8 *buf = new uint8[CDSTREAM_SECTOR_SIZE];
	CDirectory *pDir = new CDirectory(TXDSTORESIZE);
	CDirectory::DirectoryInfo dirInfo;

	CStreaming::FlushRequestList();

#ifndef LIBRW
	RwFileFunctions *filesys = RwOsGetFileInterface();
#endif

	RwStream *img = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMWRITE, "models\\txd.img");
	if (img == nil) {
		// original code does otherwise and it leaks
		delete []buf;
		delete pDir;

		if (_dwOperatingSystemVersion == OS_WINNT || _dwOperatingSystemVersion == OS_WIN2000 || _dwOperatingSystemVersion == OS_WINXP)
			DealWithTxdWriteError(0, TXDSTORESIZE, "CVT_CRT");

		return false;
	}

#ifdef RW_GL3
	// so we can read back DXT with GLES
	// only works for textures that are not yet loaded
	// so let's hope that is the case for all
	rw::gl3::needToReadBackTextures = true;
#endif

#ifdef DISABLE_VSYNC_ON_TEXTURE_CONVERSION
	// let's disable vsync and frame limiter to speed up texture conversion
	// (actually we probably don't need to disable frame limiter in here, but let's do it just in case =P)
	int8 vsyncState = CMenuManager::m_PrefsVsync;
	int8 frameLimiterState = CMenuManager::m_PrefsFrameLimiter;
	CMenuManager::m_PrefsVsync = 0;
	CMenuManager::m_PrefsFrameLimiter = 0;
#endif

	int32 i;
	for (i = 0; i < TXDSTORESIZE; i++) {
		ConvertingTexturesScreen(i, TXDSTORESIZE, "CVT_MSG");

		if (CTxdStore::GetSlot(i) != nil && CStreaming::IsObjectInCdImage(i + STREAM_OFFSET_TXD)) {
#ifdef FIX_BUGS
			if(strcmp(CTxdStore::GetTxdName(i), "generic") == 0)
				continue;
#endif

			CStreaming::RequestTxd(i, STREAMFLAGS_KEEP_IN_MEMORY);
			CStreaming::RequestModelStream(0);
			CStreaming::FlushChannels();

			char filename[64];
			sprintf(filename, "%s.txd", CTxdStore::GetTxdName(i));

			if (CTxdStore::GetSlot(i)->texDict) {

				int32 pos = STREAMTELL(img);

				if (RwTexDictionaryStreamWrite(CTxdStore::GetSlot(i)->texDict, img) == nil) {
					DealWithTxdWriteError(i, TXDSTORESIZE, "CVT_ERR");
					RwStreamClose(img, nil);
					delete []buf;
					delete pDir;
					CStreaming::RemoveTxd(i);
#ifdef RW_GL3
					rw::gl3::needToReadBackTextures = false;
#endif
					return false;
				}

				int32 size = STREAMTELL(img) - pos;
				int32 num = size % CDSTREAM_SECTOR_SIZE;

				size /= CDSTREAM_SECTOR_SIZE;
				if (num != 0) {
					size++;
					num = CDSTREAM_SECTOR_SIZE - num;
					RwStreamWrite(img, buf, num);
				}

				dirInfo.offset = pos / CDSTREAM_SECTOR_SIZE;
				dirInfo.size = size;
				strncpy(dirInfo.name, filename, sizeof(dirInfo.name));
				pDir->AddItem(dirInfo);
				CStreaming::RemoveTxd(i);
			}
			CStreaming::FlushRequestList();
		}
	}

#ifdef DISABLE_VSYNC_ON_TEXTURE_CONVERSION
	// restore vsync and frame limiter states
	CMenuManager::m_PrefsVsync = vsyncState;
	CMenuManager::m_PrefsFrameLimiter = frameLimiterState;
#endif

	RwStreamClose(img, nil);
	delete []buf;

#ifdef RW_GL3
	rw::gl3::needToReadBackTextures = false;
#endif

	if (!pDir->WriteDirFile("models\\txd.dir")) {
		DealWithTxdWriteError(i, TXDSTORESIZE, "CVT_ERR");
		delete pDir;
		return false;
	}

	delete pDir;

	WriteVideoCardCapsFile();
	return true;
}
#endif // GTA_PC


code:
#ifndef LIBRW

#include <d3d8.h>
#define WITHD3D
#include "common.h"
#include "TexturePools.h"

// TODO: this needs to be integrated into RW

extern "C" LPDIRECT3DDEVICE8 _RwD3DDevice;

CTexturePool aTexturePools[12];
CPaletteList PaletteList;
int numTexturePools;
int MaxPaletteIndex;
bool bUsePaletteIndex = true;

void
CTexturePool::Create(D3DFORMAT _Format, int _size, uint32 mipmapLevels, int32 numTextures)
{
	Format = _Format;
	size = _size;
	levels = mipmapLevels;
	pTextures = new IDirect3DTexture8 *[numTextures];
	texturesMax = numTextures;
	texturesNum = 0;
	texturesUsed = 0;
}

void
CTexturePool::Release()
{
	int i = 0;
	while (i < texturesNum) {
		pTextures[i]->Release();
		i++;
	}

	delete[] pTextures;

	pTextures = nil;
	texturesNum = 0;
	texturesUsed = 0;
}

IDirect3DTexture8 *
CTexturePool::FindTexture()
{
	if (texturesNum == 0)
		return nil;
	texturesUsed--;
	return pTextures[--texturesNum];
}

bool
CTexturePool::AddTexture(IDirect3DTexture8 *texture)
{
	++texturesUsed;
	if (texturesNum >= texturesMax)
		return false;
	pTextures[texturesNum] = texture;
	++texturesNum;
	return true;
}

void
CTexturePool::Resize(int numTextures)
{
	if (numTextures == texturesMax)
		return;

	IDirect3DTexture8 **newTextures = new IDirect3DTexture8 *[numTextures];

	for (int i = 0; i < texturesNum && i < numTextures; i++)
		newTextures[i] = pTextures[i];

	if (numTextures < texturesNum) {
		for (int i = numTextures; i < texturesNum; i++)
			pTextures[i]->Release();
	}
	delete[] pTextures;
	pTextures = newTextures;
	texturesMax = numTextures;
}

void
CPaletteList::Alloc(int max)
{
	Data = new int[max];
	Max = max;
	Num = 0;
}

void
CPaletteList::Free()
{
	delete[] Data;
	Data = nil;
	Num = 0;
}

int
CPaletteList::Find()
{
	if (Num == 0)
		return -1;
	return Data[--Num];
}

void
CPaletteList::Add(int item)
{
	if (Num < Max)
		Data[Num++] = item;
	else {
		Resize(2 * Max);
		Add(item);
	}
}

void
CPaletteList::Resize(int max)
{
	if (max == Max)
		return;

	int *newData = new int[4 * max];
	for (int i = 0; i < Num && i < max; i++)
		newData[i] = Data[i];
	delete[] Data;
	Data = newData;
	Max = max;
}

HRESULT
CreateTexture(int width, int height, int levels, D3DFORMAT Format, IDirect3DTexture8 **texture)
{
	if (width == height) {
		for (int i = 0; i < numTexturePools; i++) {
			if (width != aTexturePools[i].GetSize() && levels == aTexturePools[i].levels && Format == aTexturePools[i].Format)
				*texture = aTexturePools[i].FindTexture();
		}
	}
	if (*texture)
		return D3D_OK;
	else
		return _RwD3DDevice->CreateTexture(width, height, levels, 0, Format, D3DPOOL_MANAGED, texture);
}

void
ReleaseTexture(IDirect3DTexture8 *texture)
{
	int levels = 1;
	if (texture->GetLevelCount() > 1)
		levels = 0;

	D3DSURFACE_DESC SURFACE_DESC;

	texture->GetLevelDesc(0, &SURFACE_DESC);

	if (SURFACE_DESC.Width == SURFACE_DESC.Height) {
		for (int i = 0; i < numTexturePools; i++) {
			if (SURFACE_DESC.Width == aTexturePools[i].GetSize() && SURFACE_DESC.Format == aTexturePools[i].Format && levels == aTexturePools[i].levels) {
				if (!aTexturePools[i].AddTexture(texture)) {
					if (aTexturePools[i].texturesUsed > 3 * aTexturePools[i].texturesMax / 2) {
						aTexturePools[i].Resize(2 * aTexturePools[i].texturesMax);
						aTexturePools[i].texturesUsed--;
						aTexturePools[i].AddTexture(texture);
					} else {
						texture->Release();
					}
				}
				return;
			}
		}
	}
	if (numTexturePools < 12 && bUsePaletteIndex && levels != 0 && SURFACE_DESC.Width == SURFACE_DESC.Height &&
	    (SURFACE_DESC.Width == 64 || SURFACE_DESC.Width == 128 || SURFACE_DESC.Width == 256)) {
		aTexturePools[numTexturePools].Create(SURFACE_DESC.Format, SURFACE_DESC.Width, 1, 16);
		aTexturePools[numTexturePools].AddTexture(texture);
		numTexturePools++;
	} else
		texture->Release();
}

int
FindAvailablePaletteIndex()
{
	int index = PaletteList.Find();
	if (index == -1)
		index = MaxPaletteIndex++;
	return index;
}

void
AddAvailablePaletteIndex(int index)
{
	if (bUsePaletteIndex)
		PaletteList.Add(index);
}

void
_TexturePoolsInitialise()
{
	PaletteList.Alloc(100);
	MaxPaletteIndex = 0;
}

void
_TexturePoolsShutdown()
{
	for (int i = 0; i < numTexturePools; i++)
		aTexturePools[i].Release();

	numTexturePools = 0;
	bUsePaletteIndex = false;
	PaletteList.Free();
}

#endif // !LIBRW


code:
#pragma once

class CTexturePool
{
public:
	D3DFORMAT Format;
	int size;
	uint32 levels;
	int32 texturesMax;
	int32 texturesUsed;
	int32 texturesNum;
	IDirect3DTexture8 **pTextures;

public:
	CTexturePool() {}
	void Create(D3DFORMAT _Format, int size, uint32 mipmapLevels, int32 numTextures);
	void Release();
	IDirect3DTexture8 *FindTexture();
	bool AddTexture(IDirect3DTexture8 *texture);
	void Resize(int numTextures);
#ifdef FIX_BUGS
	int GetSize() { return size; }
#else
	float GetSize() { return size; }
#endif
};

class CPaletteList
{
	int Max;
	int Num;
	int *Data;
public:
	void Alloc(int max);
	void Free();
	int Find();
	void Add(int item);
	void Resize(int max);
};

void _TexturePoolsInitialise();
void _TexturePoolsShutdown();


code:
#include "common.h"

#include "templates.h"
#include "General.h"
#include "Streaming.h"
#include "RwHelper.h"
#include "TxdStore.h"

CPool<TxdDef,TxdDef> *CTxdStore::ms_pTxdPool;
RwTexDictionary *CTxdStore::ms_pStoredTxd;

void
CTxdStore::Initialise(void)
{
	if(ms_pTxdPool == nil)
		ms_pTxdPool = new CPool<TxdDef,TxdDef>(TXDSTORESIZE);
}

void
CTxdStore::Shutdown(void)
{
	if(ms_pTxdPool)
		delete ms_pTxdPool;
}

void
CTxdStore::GameShutdown(void)
{
	int i;

	for(i = 0; i < TXDSTORESIZE; i++){
		TxdDef *def = GetSlot(i);
		if(def && GetNumRefs(i) == 0)
			RemoveTxdSlot(i);
	}
}

int
CTxdStore::AddTxdSlot(const char *name)
{
	TxdDef *def = ms_pTxdPool->New();
	assert(def);
	def->texDict = nil;
	def->refCount = 0;
	strcpy(def->name, name);
	return ms_pTxdPool->GetJustIndex(def);
}

void
CTxdStore::RemoveTxdSlot(int slot)
{
	TxdDef *def = GetSlot(slot);
	if(def->texDict)
		RwTexDictionaryDestroy(def->texDict);
	ms_pTxdPool->Delete(def);
}

int
CTxdStore::FindTxdSlot(const char *name)
{
	int size = ms_pTxdPool->GetSize();
	for(int i = 0; i < size; i++){
		TxdDef *def = GetSlot(i);
		if(def && !CGeneral::faststricmp(def->name, name))
			return i;
	}
	return -1;
}

char*
CTxdStore::GetTxdName(int slot)
{
	return GetSlot(slot)->name;
}

void
CTxdStore::PushCurrentTxd(void)
{
	ms_pStoredTxd = RwTexDictionaryGetCurrent();
}

void
CTxdStore::PopCurrentTxd(void)
{
	RwTexDictionarySetCurrent(ms_pStoredTxd);
	ms_pStoredTxd = nil;
}

void
CTxdStore::SetCurrentTxd(int slot)
{
	RwTexDictionarySetCurrent(GetSlot(slot)->texDict);
}

void
CTxdStore::Create(int slot)
{
	GetSlot(slot)->texDict = RwTexDictionaryCreate();
}

int
CTxdStore::GetNumRefs(int slot)
{
	return GetSlot(slot)->refCount;
}

void
CTxdStore::AddRef(int slot)
{
	GetSlot(slot)->refCount++;
}

void
CTxdStore::RemoveRef(int slot)
{
	if(--GetSlot(slot)->refCount <= 0)
		CStreaming::RemoveTxd(slot);
}

void
CTxdStore::RemoveRefWithoutDelete(int slot)
{
	GetSlot(slot)->refCount--;
}

bool
CTxdStore::LoadTxd(int slot, RwStream *stream)
{
	TxdDef *def = GetSlot(slot);

	if(RwStreamFindChunk(stream, rwID_TEXDICTIONARY, nil, nil)){
		def->texDict = RwTexDictionaryGtaStreamRead(stream);
		return def->texDict != nil;
	}
	printf("Failed to load TXD\n");
	return false;
}

bool
CTxdStore::LoadTxd(int slot, const char *filename)
{
	RwStream *stream;
	bool ret;

	ret = false;
	_rwD3D8TexDictionaryEnableRasterFormatConversion(true);
	do
		stream = RwStreamOpen(rwSTREAMFILENAME, rwSTREAMREAD, filename);
	while(stream == nil);
	ret = LoadTxd(slot, stream);
	RwStreamClose(stream, nil);
	return ret;
}

bool
CTxdStore::StartLoadTxd(int slot, RwStream *stream)
{
	TxdDef *def = GetSlot(slot);
	if(RwStreamFindChunk(stream, rwID_TEXDICTIONARY, nil, nil)){
		def->texDict = RwTexDictionaryGtaStreamRead1(stream);
		return def->texDict != nil;
	}else{
		printf("Failed to load TXD\n");
		return false;
	}
}

bool
CTxdStore::FinishLoadTxd(int slot, RwStream *stream)
{
	TxdDef *def = GetSlot(slot);
	def->texDict = RwTexDictionaryGtaStreamRead2(stream, def->texDict);
	return def->texDict != nil;
}

void
CTxdStore::RemoveTxd(int slot)
{
	TxdDef *def = GetSlot(slot);
	if(def->texDict)
		RwTexDictionaryDestroy(def->texDict);
	def->texDict = nil;
}


code:
#pragma once

#include "templates.h"

struct TxdDef {
	RwTexDictionary *texDict;
	int refCount;
	char name[20];
};

class CTxdStore
{
	static CPool<TxdDef,TxdDef> *ms_pTxdPool;
	static RwTexDictionary *ms_pStoredTxd;
public:
	static void Initialise(void);
	static void Shutdown(void);
	static void GameShutdown(void);
	static int AddTxdSlot(const char *name);
	static void RemoveTxdSlot(int slot);
	static int FindTxdSlot(const char *name);
	static char *GetTxdName(int slot);
	static void PushCurrentTxd(void);
	static void PopCurrentTxd(void);
	static void SetCurrentTxd(int slot);
	static void Create(int slot);
	static int GetNumRefs(int slot);
	static void AddRef(int slot);
	static void RemoveRef(int slot);
	static void RemoveRefWithoutDelete(int slot);
	static bool LoadTxd(int slot, RwStream *stream);
	static bool LoadTxd(int slot, const char *filename);
	static bool StartLoadTxd(int slot, RwStream *stream);
	static bool FinishLoadTxd(int slot, RwStream *stream);
	static void RemoveTxd(int slot);

	static TxdDef *GetSlot(int slot) {
		assert(slot >= 0);
		assert(ms_pTxdPool);
		assert(slot < ms_pTxdPool->GetSize());
		return ms_pTxdPool->GetSlot(slot);
	}
	static bool isTxdLoaded(int slot);
};