ALERROR CWeaponFireDesc::InitFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc, const CString &sUNID, bool bDamageOnly)

//	InitFromXML
//
//	Loads shot data from an element

	{
	ALERROR error;
	int i;

	m_pExtension = Ctx.pExtension;
	m_fVariableInitialSpeed = false;
	m_bFragment = false;

	//	Load basic attributes

	m_sUNID = sUNID;
	m_Lifetime.LoadFromXML(pDesc->GetAttribute(LIFETIME_ATTRIB));
	int iMaxLifetime = m_Lifetime.GetMaxValue();
	m_bCanDamageSource = pDesc->GetAttributeBool(CAN_HIT_SOURCE_ATTRIB);
	m_bAutoTarget = pDesc->GetAttributeBool(AUTO_TARGET_ATTRIB);
	m_bNoFriendlyFire = pDesc->GetAttributeBool(NO_FRIENDLY_FIRE_ATTRIB);
	m_InitialDelay.LoadFromXML(pDesc->GetAttribute(INITIAL_DELAY_ATTRIB));

	//	Load missile speed

	CString sData;
	if (pDesc->FindAttribute(MISSILE_SPEED_ATTRIB, &sData))
		{
		if (error = m_MissileSpeed.LoadFromXML(sData))
			{
			Ctx.sError = CONSTLIT("Invalid missile speed attribute");
			return ERR_FAIL;
			}

		m_fVariableInitialSpeed = !m_MissileSpeed.IsConstant();
		m_rMissileSpeed = (double)m_MissileSpeed.GetAveValue() * LIGHT_SPEED / 100;
		}
	else
		{
		m_fVariableInitialSpeed = false;
		m_rMissileSpeed = LIGHT_SPEED;
		}

	//	Load the effect to use

	if (error = m_pEffect.LoadEffect(Ctx, 
			strPatternSubst("%s:e", sUNID),
			pDesc->GetContentElementByTag(EFFECT_TAG),
			pDesc->GetAttribute(EFFECT_ATTRIB)))
		return error;

	//	Load stealth

	m_iStealth = pDesc->GetAttributeInteger(STEALTH_ATTRIB);
	if (m_iStealth == 0)
		m_iStealth = CSpaceObject::stealthNormal;

	//	Other properties

	m_iSplashChance = pDesc->GetAttributeIntegerBounded(PARTICLE_SPLASH_CHANCE_ATTRIB, 0, 100, 0);
	m_iMissChance = pDesc->GetAttributeIntegerBounded(PARTICLE_MISS_CHANCE_ATTRIB, 0, 100, 0);

	//	Load specific properties

	CString sValue = pDesc->GetAttribute(FIRE_TYPE_ATTRIB);
	if (strEquals(sValue, FIRE_TYPE_MISSILE) || strEquals(sValue, FIRE_TYPE_BEAM))
		{
		m_iFireType = (strEquals(sValue, FIRE_TYPE_BEAM) ? ftBeam : ftMissile);

		//	For backwards compatibility, if we don't have an effect, assume
		//	a beam effect.

		if (m_iFireType == ftBeam && m_pEffect == NULL)
			{
			if (error = m_pEffect.CreateBeamEffect(Ctx, pDesc, strPatternSubst("%s:e", sUNID)))
				return error;

			//	Backwards compatibility in case a CBeam object is loaded from
			//	an old save file.

			m_iBeamType = beamLaser;
			m_wPrimaryColor = CG16bitImage::RGBValue(0xf1, 0x5f, 0x2a);
			m_wSecondaryColor = CG16bitImage::RGBValue(0xff, 0x00, 0x00);
			m_iIntensity = 1;
			}

		//	Load the image for the missile

		CXMLElement *pImage = pDesc->GetContentElementByTag(IMAGE_TAG);
		if (pImage)
			if (error = m_Image.InitFromXML(Ctx, pImage))
				return error;

		m_bDirectional = pDesc->GetAttributeBool(DIRECTIONAL_ATTRIB);
		if (m_bDirectional && m_pEffect)
			m_pEffect->SetVariants(g_RotationRange);

		m_iAccelerationFactor = pDesc->GetAttributeInteger(ACCELERATION_FACTOR_ATTRIB);
		int iMaxSpeed = pDesc->GetAttributeInteger(MAX_MISSILE_SPEED_ATTRIB);
		if (iMaxSpeed == 0)
			m_rMaxMissileSpeed = m_rMissileSpeed;
		else
			m_rMaxMissileSpeed = (Metric)iMaxSpeed * LIGHT_SPEED / 100.0;

		//	Hit points and interaction

		m_iHitPoints = pDesc->GetAttributeInteger(HIT_POINTS_ATTRIB);
		CString sInteraction;
		if (pDesc->FindAttribute(INTERACTION_ATTRIB, &sInteraction))
			m_iInteraction = strToInt(sInteraction, 100);
		else
			m_iInteraction = (m_iFireType == ftBeam ? 0 : 100);

		//	Load exhaust data

		CXMLElement *pExhaust = pDesc->GetContentElementByTag(MISSILE_EXHAUST_TAG);
		if (pExhaust)
			{
			m_iExhaustRate = pExhaust->GetAttributeInteger(EXHAUST_RATE_ATTRIB);
			m_iExhaustLifetime = pExhaust->GetAttributeInteger(EXHAUST_LIFETIME_ATTRIB);
			m_rExhaustDrag = pExhaust->GetAttributeInteger(EXHAUST_DRAG_ATTRIB) / 100.0;

			CXMLElement *pImage = pExhaust->GetContentElementByTag(IMAGE_TAG);
			if (error = m_ExhaustImage.InitFromXML(Ctx, pImage))
				return error;
			}
		else
			{
			m_iExhaustRate = 0;
			m_iExhaustLifetime = 0;
			m_rExhaustDrag = 0.0;
			}
		}
	else if (strEquals(sValue, FIRE_TYPE_AREA))
		{
		m_iFireType = ftArea;

		m_rMaxMissileSpeed = m_rMissileSpeed;

		//	Load expansion speed

		if (pDesc->FindAttribute(EXPANSION_SPEED_ATTRIB, &sData))
			{
			if (error = m_ExpansionSpeed.LoadFromXML(sData))
				{
				Ctx.sError = CONSTLIT("Invalid expansionSpeed attribute");
				return ERR_FAIL;
				}
			}
		else
			m_ExpansionSpeed.SetConstant(20);

		//	Area damage density

		if (pDesc->FindAttribute(AREA_DAMAGE_DENSITY_ATTRIB, &sData))
			{
			if (error = m_AreaDamageDensity.LoadFromXML(sData))
				{
				Ctx.sError = CONSTLIT("Invalid areaDamageDensity attribute");
				return ERR_FAIL;
				}
			}
		else
			m_AreaDamageDensity.SetConstant(32);

		//	Must have effect

		if (m_pEffect == NULL)
			{
			Ctx.sError = CONSTLIT("Must have <Effect> for area damage.");
			return ERR_FAIL;
			}
		}
	else if (strEquals(sValue, FIRE_TYPE_PARTICLES))
		{
		m_iFireType = ftParticles;

		m_rMaxMissileSpeed = m_rMissileSpeed;

		if (error = m_ParticleCount.LoadFromXML(pDesc->GetAttribute(PARTICLE_COUNT_ATTRIB)))
			{
			Ctx.sError = CONSTLIT("Invalid particle count.");
			return error;
			}

		if (error = m_ParticleEmitTime.LoadFromXML(pDesc->GetAttribute(PARTICLE_EMIT_TIME_ATTRIB)))
			{
			Ctx.sError = CONSTLIT("Invalid particle emit time.");
			return error;
			}

		m_iParticleSpread = pDesc->GetAttributeInteger(PARTICLE_SPREAD_ANGLE_ATTRIB);
		m_iParticleSpreadWidth = pDesc->GetAttributeInteger(PARTICLE_SPREAD_WIDTH_ATTRIB);
		}
	else if (strEquals(sValue, FIRE_TYPE_RADIUS))
		{
		m_iFireType = ftRadius;

		m_rMaxMissileSpeed = m_rMissileSpeed;

		m_rMinRadius = LIGHT_SECOND * (Metric)pDesc->GetAttributeInteger(MIN_RADIUS_ATTRIB);
		m_rMaxRadius = LIGHT_SECOND * (Metric)pDesc->GetAttributeInteger(MAX_RADIUS_ATTRIB);

		//	For radius, lifetime attribute is not required. We always set the lifetime
		//	to the effect lifetime.

		if (m_pEffect && iMaxLifetime == 0)
			{
			int iEffectLifetime = m_pEffect->GetLifetime();

			//	If the effect lifetime is infinite then change it
			//	to something more finite (this is technically an error condition)

			if (iEffectLifetime == -1)
				iEffectLifetime = 666;

			m_Lifetime.SetConstant(iEffectLifetime);
			iMaxLifetime = iEffectLifetime;
			}
		}
	else if (!bDamageOnly)
		{
		Ctx.sError = CONSTLIT("Invalid weapon fire type");
		return ERR_FAIL;
		}

	//	The effect should have the same lifetime as the shot
	//	Note: For radius damage it is the other way around (we set iMaxLifetime based on
	//	the effect--see above)

	if (m_pEffect)
		m_pEffect->SetLifetime(iMaxLifetime);

	//	We initialize this with the UNID, and later resolve the reference
	//	during OnDesignLoadComplete

	m_pAmmoType.LoadUNID(Ctx, pDesc->GetAttribute(AMMO_ID_ATTRIB));

	//	Maneuverability

	m_iManeuverability = pDesc->GetAttributeInteger(MANEUVERABILITY_ATTRIB);
	m_iManeuverRate = pDesc->GetAttributeIntegerBounded(MANEUVER_RATE_ATTRIB, 1, 180, -1);
	if (m_iManeuverRate == -1 && m_iManeuverability > 0)
		m_iManeuverRate = g_RotationAngle;
	else if (m_iManeuverability == 0 && m_iManeuverRate > 0)
		m_iManeuverability = 1;

	//	Load continuous and passthrough

	m_iContinuous = pDesc->GetAttributeInteger(BEAM_CONTINUOUS_ATTRIB);
	if (pDesc->FindAttributeInteger(PASSTHROUGH_ATTRIB, &m_iPassthrough))
		{
		//	In previous versions passthrough was a boolean value, so for backwards
		//	compatibility we treat 0 as 50%.
		//
		//	Note: We don't do this for ftArea because we need a way to specify
		//	passthrough=0 (since ftArea defaults to non-zero passthrough). Also,
		//	ftArea has no backwards compatibility issues (passthrough is only
		//	supported for 1.1 and above).

		if (m_iPassthrough == 0 && m_iFireType != ftArea)
			m_iPassthrough = 50;
		}
	else
		{
		//	If this is an area weapon, we set passthrough to a default value
		//	(for backwards compatibility)

		if (m_iFireType == ftArea)
			m_iPassthrough = 80;
		else
			m_iPassthrough = 0;
		}

	//	Load damage

	if (error = m_Damage.LoadFromXML(Ctx, pDesc->GetAttribute(DAMAGE_ATTRIB)))
		{
		Ctx.sError = strPatternSubst(CONSTLIT("Invalid damage specification: %s"), pDesc->GetAttribute(DAMAGE_ATTRIB));
		return error;
		}

	//	Fragments

	m_pFirstFragment = NULL;
	SFragmentDesc *pLastFragment = NULL;
	int iFragCount = 0;
	for (i = 0; i < pDesc->GetContentElementCount(); i++)
		{
		CXMLElement *pFragDesc = pDesc->GetContentElement(i);
		if (!strEquals(FRAGMENT_TAG, pFragDesc->GetTag()))
			continue;

		//	Create a new fragmentation descriptor

		SFragmentDesc *pNewDesc = new SFragmentDesc;
		pNewDesc->pNext = NULL;
		if (pLastFragment)
			pLastFragment->pNext = pNewDesc;
		else
			m_pFirstFragment = pNewDesc;

		pLastFragment = pNewDesc;

		//	Load fragment data

		pNewDesc->pDesc = new CWeaponFireDesc;
		CString sFragUNID = strPatternSubst("%s/f%d", sUNID, iFragCount++);
		if (error = pNewDesc->pDesc->InitFromXML(Ctx, pFragDesc, sFragUNID))
			return error;

		pNewDesc->pDesc->m_bFragment = true;

		//	Set the fragment count

		CString sCount = pFragDesc->GetAttribute(COUNT_ATTRIB);
		if (sCount.IsBlank())
			sCount = pDesc->GetAttribute(FRAGMENT_COUNT_ATTRIB);
		pNewDesc->Count.LoadFromXML(sCount);

		//	Set MIRV flag

		pNewDesc->bMIRV = (pFragDesc->GetAttributeBool(MULTI_TARGET_ATTRIB) 
				|| pDesc->GetAttributeBool(FRAGMENT_TARGET_ATTRIB));
		}

	//	If we have fragments, then set proximity appropriately
	//	NOTE: We set the fail safe value even if we don't set the proximity
	//	blast because we might set m_bProximityBlast later if there
	//	is an OnFragment event.

	m_bProximityBlast = (iFragCount != 0);
	m_iProximityFailsafe = pDesc->GetAttributeInteger(FAILSAFE_ATTRIB);

	//	Compute max effective range

	if (m_iFireType == ftArea)
		m_rMaxEffectiveRange = (m_ExpansionSpeed.GetAveValue() * LIGHT_SECOND / 100.0) * Ticks2Seconds(iMaxLifetime) * 0.75;
	else
		{
		Metric rEffectiveLifetime;
		if (m_iManeuverability > 0)
			rEffectiveLifetime = Ticks2Seconds(iMaxLifetime) * 0.75;
		else
			rEffectiveLifetime = Min(Ticks2Seconds(iMaxLifetime), 100.0);

		Metric rSpeed = (m_rMissileSpeed + m_rMaxMissileSpeed) / 2;
		m_rMaxEffectiveRange = rSpeed * rEffectiveLifetime;

		//	If we have fragments, add to the effective range

		if (m_pFirstFragment)
			m_rMaxEffectiveRange += m_pFirstFragment->pDesc->m_rMaxEffectiveRange;
		}

	//	Effects

	if (error = m_pHitEffect.LoadEffect(Ctx,
			strPatternSubst("%s:h", sUNID),
			pDesc->GetContentElementByTag(HIT_EFFECT_TAG),
			pDesc->GetAttribute(HIT_EFFECT_ATTRIB)))
		return error;

	if (error = m_pFireEffect.LoadEffect(Ctx,
			strPatternSubst("%s:f", sUNID),
			pDesc->GetContentElementByTag(FIRE_EFFECT_TAG),
			pDesc->GetAttribute(FIRE_EFFECT_ATTRIB)))
		return error;

	//	Vapor trail

	if (!pDesc->FindAttributeInteger(VAPOR_TRAIL_WIDTH_ATTRIB, &m_iVaporTrailWidth))
		m_iVaporTrailWidth = 100 * pDesc->GetAttributeInteger(VAPOR_TRAIL_ATTRIB);

	if (m_iVaporTrailWidth)
		{
		m_wVaporTrailColor = LoadRGBColor(pDesc->GetAttribute(VAPOR_TRAIL_COLOR_ATTRIB));
		m_iVaporTrailLength = pDesc->GetAttributeInteger(VAPOR_TRAIL_LENGTH_ATTRIB);
		if (m_iVaporTrailLength <= 0)
			m_iVaporTrailLength = 64;

		if (!pDesc->FindAttributeInteger(VAPOR_TRAIL_WIDTH_INC_ATTRIB, &m_iVaporTrailWidthInc))
			m_iVaporTrailWidthInc = 25;
		}
	else
		m_iVaporTrailLength = 0;

	//	Sound

	DWORD dwSoundID = LoadUNID(Ctx, pDesc->GetAttribute(SOUND_ATTRIB));
	if (dwSoundID)
		m_iFireSound = g_pUniverse->FindSound(dwSoundID);
	else
		m_iFireSound = -1;

	//	Events

	CXMLElement *pEventsDesc = pDesc->GetContentElementByTag(EVENTS_TAG);
	if (pEventsDesc)
		{
		if (error = m_Events.InitFromXML(Ctx, pEventsDesc))
			return error;
		}

	//	Check to see if this element has an enhanced sub-element. If so, then we
	//	recurse.

	CXMLElement *pEnhanced = pDesc->GetContentElementByTag(ENHANCED_TAG);
	if (pEnhanced)
		{
		m_pEnhanced = new CWeaponFireDesc(*this);
		if (error = m_pEnhanced->OverrideDesc(Ctx, pEnhanced))
			return error;
		}
	else
		m_pEnhanced = NULL;

	return NOERROR;
	}
Beispiel #2
0
void GenerateWordList (const CString &sDataFile, CXMLElement *pCmdLine)

//	GenerateWordList
//
//	Generate a list of unique words used in the game

{
    ALERROR error;
    int i;
    CString sError;

    //	Open the XML file

    CResourceDb Resources(sDataFile);
    if (error = Resources.Open())
    {
        printf("Unable to initialize data file.\n");
        return;
    }

    CXMLElement *pGameFile;
    if (error = Resources.LoadGameFile(&pGameFile, NULL, &sError))
    {
        printf("%s\n", sError.GetASCIIZPointer());
        return;
    }

    //	Create the context

    CSymbolTable WordList(FALSE, TRUE);
    TraverseCtx Ctx;
    Ctx.pWordList = &WordList;

    //	Recursive descent

    ParseWordList(Ctx, pGameFile);

    //	Parse all modules too

    CXMLElement *pModules = pGameFile->GetContentElementByTag(MODULES_TAG);
    if (pModules)
    {
        for (i = 0; i < pModules->GetContentElementCount(); i++)
        {
            CXMLElement *pModule = pModules->GetContentElement(i);
            CXMLElement *pModuleXML;
            if (error = Resources.LoadModule(NULL_STR, pModule->GetAttribute(FILENAME_ATTRIB), &pModuleXML, &sError))
            {
                printf("%s\n", sError.GetASCIIZPointer());
                return;
            }

            ParseWordList(Ctx, pModuleXML);
        }
    }

    //	Print out the word list

    for (i = 0; i < WordList.GetCount(); i++)
        printf("%s\n", WordList.GetKey(i).GetASCIIZPointer());
}
bool IDockScreenDisplay::GetDisplayOptions (SInitCtx &Ctx, SDisplayOptions *retOptions, CString *retsError)

//	GetDisplayOptions
//
//	Initializes the display options structure, which is used by list and 
//	selector displays.

	{
	DEBUG_TRY

	//	Initialize background image options

	CString sBackgroundID;
	if (Ctx.pDesc->FindAttribute(BACKGROUND_ID_ATTRIB, &sBackgroundID))
		{
		//	If the attribute exists, but is empty (or equals "none") then
		//	we don't have a background

		if (sBackgroundID.IsBlank() || strEquals(sBackgroundID, CONSTLIT("none")))
			retOptions->BackgroundDesc.iType = backgroundNone;

		else if (strEquals(sBackgroundID, TYPE_HERO))
			{
			retOptions->BackgroundDesc.iType = backgroundObjHeroImage;
			retOptions->BackgroundDesc.pObj = Ctx.pLocation;
			}

		//	If the ID is "object" then we should ask the object

		else if (strEquals(sBackgroundID, TYPE_OBJECT))
			{
			retOptions->BackgroundDesc.pObj = Ctx.pLocation;
            if (Ctx.pLocation->IsPlayer())
			    retOptions->BackgroundDesc.iType = backgroundObjSchematicImage;
            else
			    retOptions->BackgroundDesc.iType = backgroundObjHeroImage;
			}

		else if (strEquals(sBackgroundID, TYPE_SCHEMATIC))
			{
			retOptions->BackgroundDesc.iType = backgroundObjSchematicImage;
			retOptions->BackgroundDesc.pObj = Ctx.pLocation;
			}

		//	If the ID is "player" then we should ask the player ship

		else if (strEquals(sBackgroundID, DATA_FROM_PLAYER))
			{
			CSpaceObject *pPlayer = g_pUniverse->GetPlayerShip();
			if (pPlayer)
				{
				retOptions->BackgroundDesc.iType = backgroundObjSchematicImage;
				retOptions->BackgroundDesc.pObj = pPlayer;
				}
			}

		//	Otherwise, we expect an integer

		else
			{
			retOptions->BackgroundDesc.iType = backgroundImage;
			retOptions->BackgroundDesc.dwImageID = strToInt(sBackgroundID, 0);
			}
		}

	//	Initialize control rect. If we have a background, then initialize to
	//	backwards compatible position. Otherwise, we take up the full range.

	if (retOptions->BackgroundDesc.iType != backgroundDefault)
		{
		retOptions->rcControl.left = 4;
		retOptions->rcControl.top = 12;
		retOptions->rcControl.right = 548;
		retOptions->rcControl.bottom = 396;
		}
	else
		{
		retOptions->rcControl.left = 0;
		retOptions->rcControl.top = 23;
		retOptions->rcControl.right = 600;
		retOptions->rcControl.bottom = 502;
		}

	//	Get the type

	if (Ctx.pDisplayDesc
			&& Ctx.pDisplayDesc->FindAttribute(TYPE_ATTRIB, &retOptions->sType))
		NULL;
	else
		retOptions->sType = Ctx.pDesc->GetAttribute(TYPE_ATTRIB);

	//	There are a couple of different ways to get options (for backwards
	//	compatibility).

	CXMLElement *pOptions;
	if ((pOptions = Ctx.pDesc->GetContentElementByTag(LIST_OPTIONS_TAG)) == NULL
			&& (pOptions = Ctx.pDesc->GetContentElementByTag(LIST_TAG)) == NULL
			&& (pOptions = Ctx.pDisplayDesc) == NULL)
		return true;

	retOptions->pOptions = pOptions;

	//	Read from the element

	retOptions->sDataFrom = pOptions->GetAttribute(DATA_FROM_ATTRIB);
	if (!pOptions->FindAttribute(CRITERIA_ATTRIB, &retOptions->sItemCriteria))
		retOptions->sItemCriteria = pOptions->GetAttribute(LIST_ATTRIB);
	retOptions->sInitialItemCode = pOptions->GetAttribute(INITIAL_ITEM_ATTRIB);
	retOptions->sRowHeightCode = pOptions->GetAttribute(ROW_HEIGHT_ATTRIB);

	//	Init code

	CXMLElement *pInitCode = pOptions->GetContentElementByTag(ON_DISPLAY_INIT_TAG);
	if (pInitCode == NULL)
		pInitCode = pOptions;

	retOptions->sCode = pInitCode->GetContentText(0);

	//	List options

	retOptions->bNoArmorSpeedDisplay = pOptions->GetAttributeBool(NO_ARMOR_SPEED_DISPLAY_ATTRIB);
	retOptions->bActualItems = pOptions->GetAttributeBool(DISPLAY_ACTUAL_ATTRIB);
	retOptions->cxIcon = pOptions->GetAttributeIntegerBounded(ICON_WIDTH_ATTRIB, 0, -1, ICON_WIDTH);
	retOptions->cyIcon = pOptions->GetAttributeIntegerBounded(ICON_HEIGHT_ATTRIB, 0, -1, ICON_HEIGHT);
	retOptions->rIconScale = pOptions->GetAttributeDoubleBounded(ICON_SCALE_ATTRIB, 0.0, -1.0, 1.0);

    //  Selector options

    retOptions->bNoEmptySlots = pOptions->GetAttributeBool(NO_EMPTY_SLOTS_ATTRIB);
    retOptions->sSlotNameCode = pOptions->GetAttribute(SLOT_NAME_ATTRIB);

	//	See if we have control position

	if (pOptions->FindAttributeInteger(POS_X_ATTRIB, (int *)&retOptions->rcControl.left))
		{
		retOptions->rcControl.top = pOptions->GetAttributeIntegerBounded(POS_Y_ATTRIB, 0, -1);
		retOptions->rcControl.right = retOptions->rcControl.left + pOptions->GetAttributeIntegerBounded(WIDTH_ATTRIB, 0, -1);
		retOptions->rcControl.bottom = retOptions->rcControl.top + pOptions->GetAttributeIntegerBounded(HEIGHT_ATTRIB, 0, -1);
		}

	return true;

	DEBUG_CATCH
	}
Beispiel #4
0
void GenerateEffectImage (CUniverse &Universe, CXMLElement *pCmdLine)
	{
	CString sError;
	int i, j;

	//	Input file

	CString sInput = pCmdLine->GetAttribute(CONSTLIT("input"));
	if (sInput.IsBlank())
		{
		printf("Input file required.\n");
		return;
		}

	//	Output file (optional)

	CString sFilespec = pCmdLine->GetAttribute(CONSTLIT("output"));
	if (!sFilespec.IsBlank())
		sFilespec = pathAddExtensionIfNecessary(sFilespec, CONSTLIT(".bmp"));

	//	Load a resource file so that we can create a design load context

	CResourceDb Resources(CONSTLIT("Transcendence"));
	if (Resources.Open(DFOPEN_FLAG_READ_ONLY, &sError) != NOERROR)
		{
		printf("%s\n", (LPSTR)sError);
		return;
		}

	CExternalEntityTable *pEntities;
	if (Resources.LoadEntities(&sError, &pEntities) != NOERROR)
		{
		printf("%s\n", sError.GetASCIIZPointer());
		return;
		}

	//	Generate a list of effect render structures

	TArray<SEffectRender> Effects;

	//	Load the input file

	CFileReadBlock InputFile(sInput);
	if (InputFile.Open() != NOERROR)
		{
		printf("Unable to open input file: %s\n", sInput.GetASCIIZPointer());
		return;
		}

	//	Parse the file

	CXMLElement *pRenderFile;
	if (CXMLElement::ParseXML(&InputFile, pEntities, &pRenderFile, &sError) != NOERROR)
		{
		printf("%s\n", sError.GetASCIIZPointer());
		return;
		}

	//	Keep track of the max cell size and frame count

	int cxCellWidth = 0;
	int cyCellHeight = 0;
	int iCellsPerEffect = 1;

	//	Generate structures

	SDesignLoadCtx LoadCtx;
	LoadCtx.sResDb = Resources.GetFilespec();
	LoadCtx.pResDb = &Resources;
	LoadCtx.bNoVersionCheck = true;

	for (i = 0; i < pRenderFile->GetContentElementCount(); i++)
		{
		CXMLElement *pRender = pRenderFile->GetContentElement(i);
		CXMLElement *pEffectDesc = pRender->GetContentElementByTag(CONSTLIT("Effect"));
		if (pEffectDesc == NULL)
			{
			printf("<Effect> tag required.\n");
			return;
			}

		SEffectRender *pEffect = Effects.Insert();

		//	Parse the effect

		if (pEffect->pEffectCreator.LoadEffect(LoadCtx, CONSTLIT("none"), pEffectDesc, NULL_STR) != NOERROR)
			{
			printf("%s\n", LoadCtx.sError.GetASCIIZPointer());
			return;
			}

		//	Bind

		if (pEffect->pEffectCreator.Bind(LoadCtx) != NOERROR)
			{
			printf("%s\n", LoadCtx.sError.GetASCIIZPointer());
			return;
			}

		//	Create a painter

		pEffect->pPainter = pEffect->pEffectCreator->CreatePainter();
		if (pEffect->pPainter == NULL)
			{
			printf("Unable to create painter.\n");
			return;
			}

		//	Render specs

		int cyHeight = pRender->GetAttributeIntegerBounded(CONSTLIT("height"), 0, -1, 128);
		int cxWidth = pRender->GetAttributeIntegerBounded(CONSTLIT("width"), 0, -1, 128);

		cxCellWidth = Max(cxCellWidth, cxWidth);
		cyCellHeight = Max(cyCellHeight, cyHeight);

		//	Figure out how many animation cells

		pEffect->iLifetime = Max(1, pEffect->pEffectCreator->GetLifetime());
		iCellsPerEffect = Max(iCellsPerEffect, pEffect->iLifetime);
		}

	//	Create the resulting image

	CImageGrid Output;
	Output.Create(iCellsPerEffect * Effects.GetCount(), cxCellWidth, cyCellHeight);

	//	Paint

	for (i = 0; i < Effects.GetCount(); i++)
		{
		SEffectRender *pEffect = &Effects[i];

		SViewportPaintCtx Ctx;

		for (j = 0; j < pEffect->iLifetime; j++)
			{
			int x, y;
			Output.GetCellCenter(i * iCellsPerEffect + j, &x, &y);

			//	Create a context

			Ctx.iTick = j;

			//	Paint the effect

			pEffect->pPainter->Paint(Output.GetImage(), x, y, Ctx);

			//	Update

			pEffect->pPainter->OnUpdate();
			}
		}

	//	Output

	OutputImage(Output.GetImage(), sFilespec);
	}
Beispiel #5
0
ALERROR CEffectCreator::OnCreateFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc)

//	OnCreateFromXML
//
//	Load from XML. This is only called if we go through the EffectType path
//	(as opposed to plain Effect types).

	{
	ALERROR error;

	//	Basic info

	m_sUNID = strFromInt(GetUNID(), false);

	if (error = InitBasicsFromXML(Ctx, pDesc))
		return error;

	//	Allow our subclass to initialize based on the effect
	//	(We know we have one because we couldn't have gotten this far
	//	without one. See CreateTypeFromXML.)

	CXMLElement *pEffect = pDesc->GetContentElementByTag(EFFECT_TAG);
	ASSERT(pEffect);

	//	Continue

	if (pEffect->GetContentElementCount() == 1)
		{
		CXMLElement *pEffectDesc = pEffect->GetContentElement(0);

		//	Load events for this effect, in case they're here.

		CXMLElement *pEventsDesc = pEffectDesc->GetContentElementByTag(EVENTS_TAG);
		if (pEventsDesc)
			{
			if (error = m_Events.InitFromXML(Ctx, pEventsDesc))
				return error;
			}

		//	Load the single effect

		error = OnEffectCreateFromXML(Ctx, pEffectDesc, m_sUNID);
		}
	else
		error = OnEffectCreateFromXML(Ctx, pEffect, m_sUNID);

	if (error)
		return error;

	//	Load damage descriptors

	CXMLElement *pDamageDesc = pDesc->GetContentElementByTag(DAMAGE_TAG);
	if (pDamageDesc)
		{
		m_pDamage = new CWeaponFireDesc;

		CString sUNID = strPatternSubst(CONSTLIT("%d/d"), GetUNID());
		if (error = m_pDamage->InitFromXML(Ctx, pDamageDesc, sUNID, true))
			return error;
		}
	
	return NOERROR;
	}
ALERROR CPlayerSettings::InitFromXML (SDesignLoadCtx &Ctx, CShipClass *pClass, CXMLElement *pDesc)

//	InitFromXML
//
//	Initialize from an XML element

	{
	ALERROR error;
	int i;

	m_sDesc = pDesc->GetAttribute(DESC_ATTRIB);
	m_dwLargeImage = LoadUNID(Ctx, pDesc->GetAttribute(LARGE_IMAGE_ATTRIB));
	m_fDebug = pDesc->GetAttributeBool(DEBUG_ONLY_ATTRIB);
	m_fInitialClass = pDesc->GetAttributeBool(INITIAL_CLASS_ATTRIB);

	m_fHasArmorDesc = false;
	m_fHasReactorDesc = false;
	m_fHasShieldDesc = false;

	//	Some ship capabilities

	bool bValue;
	if (pDesc->FindAttributeBool(AUTOPILOT_ATTRIB, &bValue))
		m_fAutopilot = bValue;
	else
		m_fAutopilot = true;

	//	Load some miscellaneous data

	CString sAttrib;
	if (!pDesc->FindAttribute(STARTING_CREDITS_ATTRIB, &sAttrib))
		sAttrib = CONSTLIT("5d20+200");

	if (error = m_StartingCredits.InitFromXML(Ctx, sAttrib))
		return error;

	m_sStartNode = pDesc->GetAttribute(STARTING_SYSTEM_ATTRIB);
	m_sStartPos = pDesc->GetAttribute(STARTING_POS_ATTRIB);
	if (m_sStartPos.IsBlank())
		m_sStartPos = CONSTLIT("Start");

	//	Load the ship screen

	CString sShipScreenUNID = pDesc->GetAttribute(SHIP_SCREEN_ATTRIB);
	if (sShipScreenUNID.IsBlank())
		sShipScreenUNID = strFromInt(DEFAULT_SHIP_SCREEN_UNID, FALSE);
	m_pShipScreen.LoadUNID(Ctx, sShipScreenUNID);

	//	Load the armor display data

	CXMLElement *pArmorDisplay = pDesc->GetContentElementByTag(ARMOR_DISPLAY_TAG);
	if (pArmorDisplay && pArmorDisplay->GetContentElementCount() > 0)
		{
		m_iArmorDescCount = pArmorDisplay->GetContentElementCount();
		m_pArmorDesc = new SArmorImageDesc [m_iArmorDescCount];
		for (i = 0; i < m_iArmorDescCount; i++)
			{
			SArmorImageDesc &ArmorDesc = m_pArmorDesc[i];
			CXMLElement *pSegment = pArmorDisplay->GetContentElement(i);

			if (error = ArmorDesc.Image.InitFromXML(Ctx, pSegment))
				return ComposeLoadError(Ctx, ERR_ARMOR_DISPLAY_NEEDED);

			ArmorDesc.sName = pSegment->GetAttribute(NAME_ATTRIB);
			ArmorDesc.xDest = pSegment->GetAttributeInteger(DEST_X_ATTRIB);
			ArmorDesc.yDest = pSegment->GetAttributeInteger(DEST_Y_ATTRIB);
			ArmorDesc.xHP = pSegment->GetAttributeInteger(HP_X_ATTRIB);
			ArmorDesc.yHP = pSegment->GetAttributeInteger(HP_Y_ATTRIB);
			ArmorDesc.yName = pSegment->GetAttributeInteger(NAME_Y_ATTRIB);
			ArmorDesc.cxNameBreak = pSegment->GetAttributeInteger(NAME_BREAK_WIDTH);
			ArmorDesc.xNameDestOffset = pSegment->GetAttributeInteger(NAME_DEST_X_ATTRIB);
			ArmorDesc.yNameDestOffset = pSegment->GetAttributeInteger(NAME_DEST_Y_ATTRIB);
			}

		m_fHasArmorDesc = true;
		}
	else
		{
		m_fHasArmorDesc = false;
		m_iArmorDescCount = 0;
		m_pArmorDesc = NULL;
		}

	//	Load shield display data

	CXMLElement *pShieldDisplay = pDesc->GetContentElementByTag(SHIELD_DISPLAY_TAG);
	if (pShieldDisplay)
		{
		if (error = m_ShieldDesc.Image.InitFromXML(Ctx, 
				pShieldDisplay->GetContentElementByTag(IMAGE_TAG)))
			return ComposeLoadError(Ctx, ERR_SHIELD_DISPLAY_NEEDED);

		m_fHasShieldDesc = true;
		}

	//	Load reactor display data

	CXMLElement *pReactorDisplay = pDesc->GetContentElementByTag(REACTOR_DISPLAY_TAG);
	if (pReactorDisplay)
		{
		if (error = m_ReactorDesc.ReactorImage.InitFromXML(Ctx,
				pReactorDisplay->GetContentElementByTag(IMAGE_TAG)))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		CXMLElement *pImage = pReactorDisplay->GetContentElementByTag(POWER_LEVEL_IMAGE_TAG);
		if (pImage == NULL || (error = m_ReactorDesc.PowerLevelImage.InitFromXML(Ctx, pImage)))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		m_ReactorDesc.xPowerLevelImage = pImage->GetAttributeInteger(DEST_X_ATTRIB);
		m_ReactorDesc.yPowerLevelImage = pImage->GetAttributeInteger(DEST_Y_ATTRIB);

		pImage = pReactorDisplay->GetContentElementByTag(FUEL_LEVEL_IMAGE_TAG);
		if (pImage == NULL || (error = m_ReactorDesc.FuelLevelImage.InitFromXML(Ctx, pImage)))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		m_ReactorDesc.xFuelLevelImage = pImage->GetAttributeInteger(DEST_X_ATTRIB);
		m_ReactorDesc.yFuelLevelImage = pImage->GetAttributeInteger(DEST_Y_ATTRIB);

		pImage = pReactorDisplay->GetContentElementByTag(FUEL_LOW_LEVEL_IMAGE_TAG);
		if (pImage == NULL || (error = m_ReactorDesc.FuelLowLevelImage.InitFromXML(Ctx, pImage)))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		if (error = InitRectFromElement(pReactorDisplay->GetContentElementByTag(REACTOR_TEXT_TAG),
				&m_ReactorDesc.rcReactorText))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		if (error = InitRectFromElement(pReactorDisplay->GetContentElementByTag(POWER_LEVEL_TEXT_TAG),
				&m_ReactorDesc.rcPowerLevelText))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		if (error = InitRectFromElement(pReactorDisplay->GetContentElementByTag(FUEL_LEVEL_TEXT_TAG),
				&m_ReactorDesc.rcFuelLevelText))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		m_fHasReactorDesc = true;
		}

	//	Done

	return NOERROR;
	}
ALERROR CConquerNodesProc::OnInitFromXML (SDesignLoadCtx &Ctx, CXMLElement *pDesc, const CString &sUNID)

//	OnInitFromXML
//
//	Initialize from XML element

	{
	ALERROR error;
	int i;

	//	Initialize criteria

	CTopologyNode::ParseCriteria(NULL, &m_Criteria);

	//	Loop over all elements

	for (i = 0; i < pDesc->GetContentElementCount(); i++)
		{
		CXMLElement *pItem = pDesc->GetContentElement(i);

		//	If we have a criteria, parse it and remember it
		//	(Note: If multiple criteria are found, we take the latest one).

		if (strEquals(pItem->GetTag(), CRITERIA_TAG))
			{
			//	Parse the filter

			if (error = CTopologyNode::ParseCriteria(pItem, &m_Criteria, &Ctx.sError))
				return error;
			}

		//	Otherwise, treat it as a conqueror definition and insert it in the list

		else
			{
			CString sNewUNID = strPatternSubst(CONSTLIT("%s/%d"), sUNID, m_Conquerors.GetCount());
			SConqueror *pConqueror = m_Conquerors.Insert();

			//	Get some properties

			pConqueror->iSeedChance = pItem->GetAttributeIntegerBounded(SEED_CHANCE_ATTRIB, 1, 100, 1);
			pConqueror->iMaxSeeds = pItem->GetAttributeIntegerBounded(MAX_SEEDS_ATTRIB, 1, -1, 1);
			pConqueror->iExpandChance = pItem->GetAttributeIntegerBounded(EXPAND_CHANCE_ATTRIB, 1, 100, 1);
			pConqueror->iMaxNodes = pItem->GetAttributeIntegerBounded(MAX_NODES_ATTRIB, 1, -1, 1);

			//	Get the seed and expand probabilities

			if (error = LoadNodeWeightTable(Ctx, pItem->GetContentElementByTag(SEED_CHANCE_TAG), &pConqueror->Seed))
				return error;

			if (error = LoadNodeWeightTable(Ctx, pItem->GetContentElementByTag(EXPAND_CHANCE_TAG), &pConqueror->Expand))
				return error;

			//	Get the processor itself

			CXMLElement *pProcXML = pItem->GetContentElementByTag(PROCESSOR_TAG);
			if (pProcXML)
				{

				if (error = ITopologyProcessor::CreateFromXMLAsGroup(Ctx, pProcXML, sNewUNID, &pConqueror->pProc))
					return error;
				}
			else
				{
				Ctx.sError = CONSTLIT("<Processor> element not found in <ConquerNodes>");
				return ERR_FAIL;
				}
			}
		}

	return NOERROR;
	}
ALERROR CPlayerSettings::InitFromXML (SDesignLoadCtx &Ctx, CShipClass *pClass, CXMLElement *pDesc)

//	InitFromXML
//
//	Initialize from an XML element

	{
	ALERROR error;
	int i;

	m_sDesc = pDesc->GetAttribute(DESC_ATTRIB);
	if (error = LoadUNID(Ctx, pDesc->GetAttribute(LARGE_IMAGE_ATTRIB), &m_dwLargeImage))
		return error;

	m_fDebug = pDesc->GetAttributeBool(DEBUG_ONLY_ATTRIB);
	CString sInitialClass = pDesc->GetAttribute(INITIAL_CLASS_ATTRIB);
	if (strEquals(sInitialClass, CONSTLIT("always")))
		{
		m_fInitialClass = true;
		m_fIncludeInAllAdventures = true;
		}
	else
		{
		m_fInitialClass = CXMLElement::IsBoolTrueValue(sInitialClass);
		m_fIncludeInAllAdventures = false;
		}

	m_fHasArmorDesc = false;
	m_fHasReactorDesc = false;
	m_fHasShieldDesc = false;

	//	Some ship capabilities

	bool bValue;
	if (pDesc->FindAttributeBool(AUTOPILOT_ATTRIB, &bValue))
		m_fAutopilot = bValue;
	else
		m_fAutopilot = true;

	//	Load some miscellaneous data

	CString sAttrib;
	if (!pDesc->FindAttribute(STARTING_CREDITS_ATTRIB, &sAttrib))
		sAttrib = CONSTLIT("5d20+200");

	if (error = m_StartingCredits.InitFromXML(Ctx, sAttrib))
		return error;

	m_sStartNode = pDesc->GetAttribute(STARTING_SYSTEM_ATTRIB);
	m_sStartPos = pDesc->GetAttribute(STARTING_POS_ATTRIB);
	if (m_sStartPos.IsBlank())
		m_sStartPos = CONSTLIT("Start");

	//	Load the ship screen

	CString sShipScreenUNID = pDesc->GetAttribute(SHIP_SCREEN_ATTRIB);
	if (sShipScreenUNID.IsBlank())
		sShipScreenUNID = strFromInt(DEFAULT_SHIP_SCREEN_UNID, false);
	m_pShipScreen.LoadUNID(Ctx, sShipScreenUNID);

	//	Load the armor display data

	CXMLElement *pArmorDisplay = pDesc->GetContentElementByTag(ARMOR_DISPLAY_TAG);
	if (pArmorDisplay && pArmorDisplay->GetContentElementCount() > 0)
		{
		//	Loop over all sub elements

		for (i = 0; i < pArmorDisplay->GetContentElementCount(); i++)
			{
			CXMLElement *pSub = pArmorDisplay->GetContentElement(i);

			if (strEquals(pSub->GetTag(), ARMOR_SECTION_TAG))
				{
				SArmorSegmentImageDesc &ArmorDesc = *m_ArmorDesc.Segments.Insert();

				if (error = ArmorDesc.Image.InitFromXML(Ctx, pSub))
					return ComposeLoadError(Ctx, ERR_ARMOR_DISPLAY_NEEDED);

				ArmorDesc.sName = pSub->GetAttribute(NAME_ATTRIB);
				ArmorDesc.xDest = pSub->GetAttributeInteger(DEST_X_ATTRIB);
				ArmorDesc.yDest = pSub->GetAttributeInteger(DEST_Y_ATTRIB);
				ArmorDesc.xHP = pSub->GetAttributeInteger(HP_X_ATTRIB);
				ArmorDesc.yHP = pSub->GetAttributeInteger(HP_Y_ATTRIB);
				ArmorDesc.yName = pSub->GetAttributeInteger(NAME_Y_ATTRIB);
				ArmorDesc.cxNameBreak = pSub->GetAttributeInteger(NAME_BREAK_WIDTH);
				ArmorDesc.xNameDestOffset = pSub->GetAttributeInteger(NAME_DEST_X_ATTRIB);
				ArmorDesc.yNameDestOffset = pSub->GetAttributeInteger(NAME_DEST_Y_ATTRIB);
				}
			else if (strEquals(pSub->GetTag(), SHIP_IMAGE_TAG))
				{
				if (error = m_ArmorDesc.ShipImage.InitFromXML(Ctx, pSub))
					return ComposeLoadError(Ctx, ERR_SHIP_IMAGE_NEEDED);
				}
			else
				return ComposeLoadError(Ctx, strPatternSubst(CONSTLIT("Unknown ArmorDisplay element: "), pSub->GetTag()));
			}

		m_fHasArmorDesc = true;
		}
	else
		m_fHasArmorDesc = false;

	//	Load shield display data

	CXMLElement *pShieldDisplay = pDesc->GetContentElementByTag(SHIELD_DISPLAY_TAG);
	if (pShieldDisplay)
		{
		//	Load the new shield effect

		if (error = m_ShieldDesc.pShieldEffect.LoadEffect(Ctx,
				strPatternSubst(CONSTLIT("%d:p:s"), pClass->GetUNID()),
				pShieldDisplay->GetContentElementByTag(SHIELD_EFFECT_TAG),
				pShieldDisplay->GetAttribute(SHIELD_EFFECT_ATTRIB)))
			return error;

		//	If we don't have the new effect, load the backwards compatibility
		//	image.

		if (m_ShieldDesc.pShieldEffect.IsEmpty())
			{
			if (error = m_ShieldDesc.Image.InitFromXML(Ctx, 
					pShieldDisplay->GetContentElementByTag(IMAGE_TAG)))
				return ComposeLoadError(Ctx, ERR_SHIELD_DISPLAY_NEEDED);
			}

		m_fHasShieldDesc = true;
		}

	//	If we have a shield effect then we must have an armor image

	if (!m_ShieldDesc.pShieldEffect.IsEmpty() && m_ArmorDesc.ShipImage.GetBitmapUNID() == 0)
		return ComposeLoadError(Ctx, ERR_MUST_HAVE_SHIP_IMAGE);

	//	Load reactor display data

	CXMLElement *pReactorDisplay = pDesc->GetContentElementByTag(REACTOR_DISPLAY_TAG);
	if (pReactorDisplay)
		{
		if (error = m_ReactorDesc.ReactorImage.InitFromXML(Ctx,
				pReactorDisplay->GetContentElementByTag(IMAGE_TAG)))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		CXMLElement *pImage = pReactorDisplay->GetContentElementByTag(POWER_LEVEL_IMAGE_TAG);
		if (pImage == NULL || (error = m_ReactorDesc.PowerLevelImage.InitFromXML(Ctx, pImage)))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		m_ReactorDesc.xPowerLevelImage = pImage->GetAttributeInteger(DEST_X_ATTRIB);
		m_ReactorDesc.yPowerLevelImage = pImage->GetAttributeInteger(DEST_Y_ATTRIB);

		pImage = pReactorDisplay->GetContentElementByTag(FUEL_LEVEL_IMAGE_TAG);
		if (pImage == NULL || (error = m_ReactorDesc.FuelLevelImage.InitFromXML(Ctx, pImage)))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		m_ReactorDesc.xFuelLevelImage = pImage->GetAttributeInteger(DEST_X_ATTRIB);
		m_ReactorDesc.yFuelLevelImage = pImage->GetAttributeInteger(DEST_Y_ATTRIB);

		pImage = pReactorDisplay->GetContentElementByTag(FUEL_LOW_LEVEL_IMAGE_TAG);
		if (pImage == NULL || (error = m_ReactorDesc.FuelLowLevelImage.InitFromXML(Ctx, pImage)))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		if (error = InitRectFromElement(pReactorDisplay->GetContentElementByTag(REACTOR_TEXT_TAG),
				&m_ReactorDesc.rcReactorText))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		if (error = InitRectFromElement(pReactorDisplay->GetContentElementByTag(POWER_LEVEL_TEXT_TAG),
				&m_ReactorDesc.rcPowerLevelText))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		if (error = InitRectFromElement(pReactorDisplay->GetContentElementByTag(FUEL_LEVEL_TEXT_TAG),
				&m_ReactorDesc.rcFuelLevelText))
			return ComposeLoadError(Ctx, ERR_REACTOR_DISPLAY_NEEDED);

		m_fHasReactorDesc = true;
		}

	//	Done

	return NOERROR;
	}
Beispiel #9
0
ALERROR WriteModule (const CString &sFilename, 
					 const CString &sFolder, 
					 CExternalEntityTable *pEntityTable,
					 CSymbolTable &Resources, 
					 CDataFile &Out, 
					 int *retiModuleEntry)
	{
	ALERROR error;
	int i;

	//	Parse the file

	CXMLElement *pModule;
	CExternalEntityTable EntityTable;
	if (pEntityTable)
		{
		CFileReadBlock DataFile(sFilename);
		CString sError;

		printf("Parsing %s...", sFilename.GetASCIIZPointer());
		if (error = CXMLElement::ParseXML(&DataFile, pEntityTable, &pModule, &sError))
			{
			printf("\n%s\n", sError.GetASCIIZPointer());
			return error;
			}

		printf("done.\n");
		}
	else
		{
		CFileReadBlock DataFile(sFilename);
		CString sError;

		printf("Parsing %s...", sFilename.GetASCIIZPointer());
		if (error = CXMLElement::ParseXML(&DataFile, &pModule, &sError, &EntityTable))
			{
			printf("\n%s\n", sError.GetASCIIZPointer());
			return error;
			}

		pEntityTable = &EntityTable;
		printf("done.\n");
		}

	//	Write the module itself

	int iEntry;
	if (error = WriteGameFile(sFilename, Out, &iEntry))
		return error;

	//	If the caller doesn't want the module entry, then it means that this is
	//	a module (instead of the main file). If so, add it to the resources table

	if (retiModuleEntry == NULL)
		Resources.AddEntry(sFilename, (CObject *)iEntry);

	//	Store all the image resources

	if (error = WriteModuleImages(pModule, sFolder, Resources, Out))
		return error;

	//	Store all the sound resources

	if (error = WriteModuleSounds(pModule, sFolder, Resources, Out))
		return error;

	//	Store all modules

	CXMLElement *pModules = pModule->GetContentElementByTag(TAG_MODULES);
	if (pModules)
		{
		for (i = 0; i < pModules->GetContentElementCount(); i++)
			{
			CXMLElement *pItem = pModules->GetContentElement(i);

			CString sFilename = pItem->GetAttribute(ATTRIB_FILENAME);
			if (error = WriteModule(sFilename, sFolder, pEntityTable, Resources, Out, NULL))
				continue;
			}
		}

	//	Done

	if (retiModuleEntry)
		*retiModuleEntry = iEntry;

	return NOERROR;
	}