// ------------------------------------------------------------------------------- // Load up all the imds into an array static bool multiLoadMiscImds(void) { UDWORD i = 0; bool bMoreToProcess = true; char name[15]; // hopefully! /* Go thru' the list */ while (bMoreToProcess) { snprintf(name, sizeof(name), "%s.pie", miscImds[i].pName); /* see if the resource loader can find it */ miscImds[i].pImd = modelGet(name); /* If it didn't get it then... */ if (!miscImds[i].pImd) { /* Say which one and return false */ debug(LOG_ERROR, "Can't find misselaneous PIE file : %s", miscImds[i].pName); ASSERT(false, "NULL PIE"); return (false); } /* If the next one's the end one, then get out now. This is cos strcmp will return 0 only at end of list */ bMoreToProcess = strcmp(miscImds[++i].pName, "END_OF_IMD_LIST"); } return true; }
/** * Create animation for a model. Called from animation script. */ bool anim_Create3D(char szPieFileName[], UWORD uwStates, UWORD uwFrameRate, UWORD uwObj, ANIM_MODE ubType, UWORD uwID) { ANIM3D *psAnim3D; iIMDShape *psFrames; UWORD uwFrames, i; /* allocate anim */ if ((psAnim3D = (ANIM3D *)malloc(sizeof(ANIM3D))) == NULL) { return false; } /* get local pointer to shape */ psAnim3D->psFrames = modelGet(szPieFileName); /* count frames in imd */ psFrames = psAnim3D->psFrames; uwFrames = 0; while (psFrames != NULL) { uwFrames++; psFrames = psFrames->next; } /* check frame count matches script */ if (ubType == ANIM_3D_TRANS && uwObj != uwFrames) { ASSERT(false, "Frames in pie %s != script objects %i", szPieFileName, uwObj); free(psAnim3D); return false; } /* get pointers to individual frames */ psAnim3D->apFrame = (iIMDShape **)malloc(uwFrames * sizeof(iIMDShape *)); psFrames = psAnim3D->psFrames; for (i = 0; i < uwFrames; i++) { psAnim3D->apFrame[i] = psFrames; psFrames = psFrames->next; } /* init members */ psAnim3D->animType = ubType; anim_InitBaseMembers((BASEANIM *)psAnim3D, uwStates, uwFrameRate, uwObj, ubType, uwID); g_animGlobals.psAnimList.push_front(psAnim3D); /* update globals */ g_animGlobals.uwCurObj = 0; return true; }
static bool initMiscImd(unsigned i, unsigned n, const char *nameFormat, unsigned flagType) { char pieName[100]; snprintf(pieName, sizeof(pieName), nameFormat, n); pAssemblyPointIMDs[flagType][i] = modelGet(pieName); if (!pAssemblyPointIMDs[flagType][i]) { debug(LOG_ERROR, "Can't find assembly point graphic %s for factory", pieName); return false; } return true; }
/* Load the feature stats */ bool loadFeatureStats(const char *pFileName) { WzConfig ini(pFileName, WzConfig::ReadOnlyAndRequired); QStringList list = ini.childGroups(); asFeatureStats = new FEATURE_STATS[list.size()]; numFeatureStats = list.size(); for (int i = 0; i < list.size(); ++i) { ini.beginGroup(list[i]); asFeatureStats[i] = FEATURE_STATS(REF_FEATURE_START + i); FEATURE_STATS *p = &asFeatureStats[i]; p->name = ini.value("name").toString(); p->id = list[i]; QString subType = ini.value("type").toString(); if (subType == "TANK WRECK") p->subType = FEAT_TANK; else if (subType == "GENERIC ARTEFACT") p->subType = FEAT_GEN_ARTE; else if (subType == "OIL RESOURCE") p->subType = FEAT_OIL_RESOURCE; else if (subType == "BOULDER") p->subType = FEAT_BOULDER; else if (subType == "VEHICLE") p->subType = FEAT_VEHICLE; else if (subType == "BUILDING") p->subType = FEAT_BUILDING; else if (subType == "OIL DRUM") p->subType = FEAT_OIL_DRUM; else if (subType == "TREE") p->subType = FEAT_TREE; else if (subType == "SKYSCRAPER") p->subType = FEAT_SKYSCRAPER; else ASSERT(false, "Unknown feature type: %s", subType.toUtf8().constData()); p->psImd = modelGet(ini.value("model").toString()); p->baseWidth = ini.value("width", 1).toInt(); p->baseBreadth = ini.value("breadth", 1).toInt(); p->tileDraw = ini.value("tileDraw", 1).toInt(); p->allowLOS = ini.value("lineOfSight", 1).toInt(); p->visibleAtStart = ini.value("startVisible", 1).toInt(); p->damageable = ini.value("damageable", 1).toInt(); p->body = ini.value("hitpoints", 1).toInt(); p->armourValue = ini.value("armour", 1).toInt(); //and the oil resource - assumes only one! if (asFeatureStats[i].subType == FEAT_OIL_RESOURCE) { oilResFeature = &asFeatureStats[i]; } ini.endGroup(); } return true; }
// ppFileData is incremented to the end of the file on exit! static iIMDShape *iV_ProcessIMD(const QString &filename, const char **ppFileData, const char *FileDataEnd) { const char *pFileData = *ppFileData; char buffer[PATH_MAX], texfile[PATH_MAX], normalfile[PATH_MAX], specfile[PATH_MAX]; int cnt, nlevels; iIMDShape *shape; UDWORD level; int32_t imd_version; uint32_t imd_flags; bool bTextured = false; iIMDShape *objanimpie[ANIM_EVENT_COUNT]; memset(normalfile, 0, sizeof(normalfile)); memset(specfile, 0, sizeof(specfile)); if (sscanf(pFileData, "%255s %d%n", buffer, &imd_version, &cnt) != 2) { debug(LOG_ERROR, "%s: bad PIE version: (%s)", filename.toUtf8().constData(), buffer); assert(false); return nullptr; } pFileData += cnt; if (strcmp(PIE_NAME, buffer) != 0) { debug(LOG_ERROR, "%s: Not an IMD file (%s %d)", filename.toUtf8().constData(), buffer, imd_version); return nullptr; } //Now supporting version PIE_VER and PIE_FLOAT_VER files if (imd_version != PIE_VER && imd_version != PIE_FLOAT_VER) { debug(LOG_ERROR, "%s: Version %d not supported", filename.toUtf8().constData(), imd_version); return nullptr; } // Read flag if (sscanf(pFileData, "%255s %x%n", buffer, &imd_flags, &cnt) != 2) { debug(LOG_ERROR, "%s: bad flags: %s", filename.toUtf8().constData(), buffer); return nullptr; } pFileData += cnt; /* This can be either texture or levels */ if (sscanf(pFileData, "%255s %d%n", buffer, &nlevels, &cnt) != 2) { debug(LOG_ERROR, "%s: Expecting TEXTURE or LEVELS: %s", filename.toUtf8().constData(), buffer); return nullptr; } pFileData += cnt; // get texture page if specified if (strncmp(buffer, "TEXTURE", 7) == 0) { int i, pwidth, pheight; char ch, texType[PATH_MAX]; /* the first parameter for textures is always ignored; which is why we ignore * nlevels read in above */ ch = *pFileData++; // Run up to the dot or till the buffer is filled. Leave room for the extension. for (i = 0; i < PATH_MAX - 5 && (ch = *pFileData++) != '\0' && ch != '.'; ++i) { texfile[i] = ch; } texfile[i] = '\0'; if (sscanf(pFileData, "%255s%n", texType, &cnt) != 1) { debug(LOG_ERROR, "%s: Texture info corrupt: %s", filename.toUtf8().constData(), buffer); return nullptr; } pFileData += cnt; if (strcmp(texType, "png") != 0) { debug(LOG_ERROR, "%s: Only png textures supported", filename.toUtf8().constData()); return nullptr; } sstrcat(texfile, ".png"); if (sscanf(pFileData, "%d %d%n", &pwidth, &pheight, &cnt) != 2) { debug(LOG_ERROR, "%s: Bad texture size: %s", filename.toUtf8().constData(), buffer); return nullptr; } pFileData += cnt; /* Now read in LEVELS directive */ if (sscanf(pFileData, "%255s %d%n", buffer, &nlevels, &cnt) != 2) { debug(LOG_ERROR, "%s: Bad levels info: %s", filename.toUtf8().constData(), buffer); return nullptr; } pFileData += cnt; bTextured = true; } if (strncmp(buffer, "NORMALMAP", 9) == 0) { char ch, texType[PATH_MAX]; int i; /* the first parameter for textures is always ignored; which is why we ignore * nlevels read in above */ ch = *pFileData++; // Run up to the dot or till the buffer is filled. Leave room for the extension. for (i = 0; i < PATH_MAX - 5 && (ch = *pFileData++) != '\0' && ch != '.'; ++i) { normalfile[i] = ch; } normalfile[i] = '\0'; if (sscanf(pFileData, "%255s%n", texType, &cnt) != 1) { debug(LOG_ERROR, "%s: Normal map info corrupt: %s", filename.toUtf8().constData(), buffer); return nullptr; } pFileData += cnt; if (strcmp(texType, "png") != 0) { debug(LOG_ERROR, "%s: Only png normal maps supported", filename.toUtf8().constData()); return nullptr; } sstrcat(normalfile, ".png"); /* Now read in LEVELS directive */ if (sscanf(pFileData, "%255s %d%n", buffer, &nlevels, &cnt) != 2) { debug(LOG_ERROR, "%s: Bad levels info: %s", filename.toUtf8().constData(), buffer); return nullptr; } pFileData += cnt; } if (strncmp(buffer, "SPECULARMAP", 11) == 0) { char ch, texType[PATH_MAX]; int i; /* the first parameter for textures is always ignored; which is why we ignore nlevels read in above */ ch = *pFileData++; // Run up to the dot or till the buffer is filled. Leave room for the extension. for (i = 0; i < PATH_MAX - 5 && (ch = *pFileData++) != '\0' && ch != '.'; ++i) { specfile[i] = ch; } specfile[i] = '\0'; if (sscanf(pFileData, "%255s%n", texType, &cnt) != 1) { debug(LOG_ERROR, "%s specular map info corrupt: %s", filename.toUtf8().constData(), buffer); return nullptr; } pFileData += cnt; if (strcmp(texType, "png") != 0) { debug(LOG_ERROR, "%s: only png specular maps supported", filename.toUtf8().constData()); return nullptr; } sstrcat(specfile, ".png"); /* Try -again- to read in LEVELS directive */ if (sscanf(pFileData, "%255s %d%n", buffer, &nlevels, &cnt) != 2) { debug(LOG_ERROR, "%s: Bad levels info: %s", filename.toUtf8().constData(), buffer); return nullptr; } pFileData += cnt; } for (int i = 0; i < ANIM_EVENT_COUNT; i++) { objanimpie[i] = nullptr; } while (strncmp(buffer, "EVENT", 5) == 0) { char animpie[PATH_MAX]; ASSERT(nlevels < ANIM_EVENT_COUNT && nlevels >= 0, "Invalid event type %d", nlevels); pFileData++; if (sscanf(pFileData, "%255s%n", animpie, &cnt) != 1) { debug(LOG_ERROR, "%s animation model corrupt: %s", filename.toUtf8().constData(), buffer); return nullptr; } pFileData += cnt; objanimpie[nlevels] = modelGet(animpie); /* Try -yet again- to read in LEVELS directive */ if (sscanf(pFileData, "%255s %d%n", buffer, &nlevels, &cnt) != 2) { debug(LOG_ERROR, "%s: Bad levels info: %s", filename.toUtf8().constData(), buffer); return nullptr; } pFileData += cnt; } if (strncmp(buffer, "LEVELS", 6) != 0) { debug(LOG_ERROR, "%s: Expecting 'LEVELS' directive (%s)", filename.toUtf8().constData(), buffer); return nullptr; } /* Read first LEVEL directive */ if (sscanf(pFileData, "%255s %d%n", buffer, &level, &cnt) != 2) { debug(LOG_ERROR, "(_load_level) file corrupt -J"); return nullptr; } pFileData += cnt; if (strncmp(buffer, "LEVEL", 5) != 0) { debug(LOG_ERROR, "%s: Expecting 'LEVEL' directive (%s)", filename.toUtf8().constData(), buffer); return nullptr; } shape = _imd_load_level(filename, &pFileData, FileDataEnd, nlevels, imd_version, level); if (shape == nullptr) { debug(LOG_ERROR, "%s: Unsuccessful", filename.toUtf8().constData()); return nullptr; } // load texture page if specified if (bTextured) { int texpage = iV_GetTexture(texfile); int normalpage = iV_TEX_INVALID; int specpage = iV_TEX_INVALID; ASSERT_OR_RETURN(nullptr, texpage >= 0, "%s could not load tex page %s", filename.toUtf8().constData(), texfile); if (normalfile[0] != '\0') { debug(LOG_TEXTURE, "Loading normal map %s for %s", normalfile, filename.toUtf8().constData()); normalpage = iV_GetTexture(normalfile, false); ASSERT_OR_RETURN(nullptr, normalpage >= 0, "%s could not load tex page %s", filename.toUtf8().constData(), normalfile); } if (specfile[0] != '\0') { debug(LOG_TEXTURE, "Loading specular map %s for %s", specfile, filename.toUtf8().constData()); specpage = iV_GetTexture(specfile, false); ASSERT_OR_RETURN(nullptr, specpage >= 0, "%s could not load tex page %s", filename.toUtf8().constData(), specfile); } // assign tex pages and flags to all levels for (iIMDShape *psShape = shape; psShape != nullptr; psShape = psShape->next) { psShape->texpage = texpage; psShape->normalpage = normalpage; psShape->specularpage = specpage; psShape->flags = imd_flags; } // check if model should use team colour mask if (imd_flags & iV_IMD_TCMASK) { int texpage_mask; pie_MakeTexPageTCMaskName(texfile); sstrcat(texfile, ".png"); texpage_mask = iV_GetTexture(texfile); ASSERT_OR_RETURN(shape, texpage_mask >= 0, "%s could not load tcmask %s", filename.toUtf8().constData(), texfile); // Propagate settings through levels for (iIMDShape *psShape = shape; psShape != nullptr; psShape = psShape->next) { psShape->tcmaskpage = texpage_mask; } } } // copy over model-wide animation information, stored only in the first level for (int i = 0; i < ANIM_EVENT_COUNT; i++) { shape->objanimpie[i] = objanimpie[i]; } *ppFileData = pFileData; return shape; }
/** Load the research stats */ bool loadResearch(WzConfig &ini) { ASSERT(ini.isAtDocumentRoot(), "WzConfig instance is in the middle of traversal"); std::vector<WzString> list = ini.childGroups(); PLAYER_RESEARCH dummy; memset(&dummy, 0, sizeof(dummy)); std::vector< std::vector<WzString> > preResearch; preResearch.resize(list.size()); for (size_t inc = 0; inc < list.size(); ++inc) { // HACK FIXME: the code assumes we have empty PLAYER_RESEARCH entries to throw around for (auto &j : asPlayerResList) { j.push_back(dummy); } ini.beginGroup(list[inc]); RESEARCH research; research.index = inc; research.name = ini.string("name"); research.id = list[inc]; //check the name hasn't been used already ASSERT_OR_RETURN(false, checkResearchName(&research, inc), "Research name '%s' used already", getName(&research)); research.ref = REF_RESEARCH_START + inc; research.results = ini.json("results", nlohmann::json::array()); //set subGroup icon WzString subGroup = ini.value("subgroupIconID", "").toWzString(); if (subGroup.compare("") != 0) { research.subGroup = setIconID(subGroup.toUtf8().c_str(), getName(&research)); } else { research.subGroup = NO_RESEARCH_ICON; } //set key topic unsigned int keyTopic = ini.value("keyTopic", 0).toUInt(); ASSERT(keyTopic <= 1, "Invalid keyTopic for research topic - '%s' ", getName(&research)); if (keyTopic <= 1) { research.keyTopic = ini.value("keyTopic", 0).toUInt(); } else { research.keyTopic = 0; } //set tech code UBYTE techCode = ini.value("techCode", 0).toUInt(); ASSERT(techCode <= 1, "Invalid tech code for research topic - '%s' ", getName(&research)); if (techCode == 0) { research.techCode = TC_MAJOR; } else { research.techCode = TC_MINOR; } //set the iconID WzString iconID = ini.value("iconID", "").toWzString(); if (iconID.compare("") != 0) { research.iconID = setIconID(iconID.toUtf8().c_str(), getName(&research)); } else { research.iconID = NO_RESEARCH_ICON; } //get the IMDs used in the interface WzString statID = ini.value("statID", "").toWzString(); research.psStat = nullptr; if (statID.compare("") != 0) { //try find the structure stat with given name research.psStat = getCompStatsFromName(statID); ASSERT_OR_RETURN(false, research.psStat, "Could not find stats for %s research %s", statID.toUtf8().c_str(), getName(&research)); } WzString imdName = ini.value("imdName", "").toWzString(); if (imdName.compare("") != 0) { research.pIMD = modelGet(imdName); ASSERT(research.pIMD != nullptr, "Cannot find the research PIE '%s' for record '%s'", imdName.toUtf8().data(), getName(&research)); } WzString imdName2 = ini.value("imdName2", "").toWzString(); if (imdName2.compare("") != 0) { research.pIMD2 = modelGet(imdName2); ASSERT(research.pIMD2 != nullptr, "Cannot find the 2nd research '%s' PIE for record '%s'", imdName2.toUtf8().data(), getName(&research)); } WzString msgName = ini.value("msgName", "").toWzString(); if (msgName.compare("") != 0) { //check its a major tech code ASSERT(research.techCode == TC_MAJOR, "This research should not have a message associated with it, '%s' the message will be ignored!", getName(&research)); if (research.techCode == TC_MAJOR) { research.pViewData = getViewData(msgName); } } //set the researchPoints unsigned int resPoints = ini.value("researchPoints", 0).toUInt(); ASSERT_OR_RETURN(false, resPoints <= UWORD_MAX, "Research Points too high for research topic - '%s' ", getName(&research)); research.researchPoints = resPoints; //set the research power unsigned int resPower = ini.value("researchPower", 0).toUInt(); ASSERT_OR_RETURN(false, resPower <= UWORD_MAX, "Research Power too high for research topic - '%s' ", getName(&research)); research.researchPower = resPower; //remember research pre-requisites for futher checking preResearch[inc] = ini.value("requiredResearch").toWzStringList(); //set components results std::vector<WzString> compResults = ini.value("resultComponents").toWzStringList(); for (size_t j = 0; j < compResults.size(); j++) { WzString compID = compResults[j].trimmed(); COMPONENT_STATS *pComp = getCompStatsFromName(compID); if (pComp != nullptr) { research.componentResults.push_back(pComp); } else { ASSERT(false, "Invalid item '%s' in list of result components of research '%s' ", compID.toUtf8().c_str(), getName(&research)); } } //set replaced components std::vector<WzString> replacedComp = ini.value("replacedComponents").toWzStringList(); for (size_t j = 0; j < replacedComp.size(); j++) { //read pair of components oldComponent:newComponent std::vector<WzString> pair = replacedComp[j].split(":"); ASSERT(pair.size() == 2, "Invalid item '%s' in list of replaced components of research '%s'. Required format: 'oldItem:newItem, item1:item2'", replacedComp[j].toUtf8().c_str(), getName(&research)); if (pair.size() != 2) { continue; //skip invalid entries } WzString oldCompID = pair[0].trimmed(); WzString newCompID = pair[1].trimmed(); COMPONENT_STATS *oldComp = getCompStatsFromName(oldCompID); if (oldComp == nullptr) { ASSERT(false, "Invalid item '%s' in list of replaced components of research '%s'. Wrong component code.", oldCompID.toUtf8().c_str(), getName(&research)); continue; } COMPONENT_STATS *newComp = getCompStatsFromName(newCompID); if (newComp == nullptr) { ASSERT(false, "Invalid item '%s' in list of replaced components of research '%s'. Wrong component code.", newCompID.toUtf8().c_str(), getName(&research)); continue; } RES_COMP_REPLACEMENT replItem; replItem.pOldComponent = oldComp; replItem.pNewComponent = newComp; research.componentReplacement.push_back(replItem); } //set redundant components std::vector<WzString> redComp = ini.value("redComponents").toWzStringList(); for (size_t j = 0; j < redComp.size(); j++) { WzString compID = redComp[j].trimmed(); COMPONENT_STATS *pComp = getCompStatsFromName(compID); if (pComp == nullptr) { ASSERT(false, "Invalid item '%s' in list of redundant components of research '%s' ", compID.toUtf8().c_str(), getName(&research)); } else { research.pRedArtefacts.push_back(pComp); } } //set result structures std::vector<WzString> resStruct = ini.value("resultStructures").toWzStringList(); for (size_t j = 0; j < resStruct.size(); j++) { WzString strucID = resStruct[j].trimmed(); int structIndex = getStructStatFromName(strucID); ASSERT(structIndex >= 0, "Invalid item '%s' in list of result structures of research '%s' ", strucID.toUtf8().c_str(), getName(&research)); if (structIndex >= 0) { research.pStructureResults.push_back(structIndex); } } //set required structures std::vector<WzString> reqStruct = ini.value("requiredStructures").toWzStringList(); for (size_t j = 0; j < reqStruct.size(); j++) { WzString strucID = reqStruct[j].trimmed(); int structIndex = getStructStatFromName(strucID.toUtf8().c_str()); ASSERT(structIndex >= 0, "Invalid item '%s' in list of required structures of research '%s' ", strucID.toUtf8().c_str(), getName(&research)); if (structIndex >= 0) { research.pStructList.push_back(structIndex); } } //set redundant structures std::vector<WzString> redStruct = ini.value("redStructures").toWzStringList(); for (size_t j = 0; j < redStruct.size(); j++) { WzString strucID = redStruct[j].trimmed(); int structIndex = getStructStatFromName(strucID.toUtf8().c_str()); ASSERT(structIndex >= 0, "Invalid item '%s' in list of redundant structures of research '%s' ", strucID.toUtf8().c_str(), getName(&research)); if (structIndex >= 0) { research.pRedStructs.push_back(structIndex); } } asResearch.push_back(research); ini.endGroup(); } //Load and check research pre-requisites (need do it AFTER loading research items) for (size_t inc = 0; inc < asResearch.size(); inc++) { std::vector<WzString> &preRes = preResearch[inc]; for (size_t j = 0; j < preRes.size(); j++) { WzString resID = preRes[j].trimmed(); RESEARCH *preResItem = getResearch(resID.toUtf8().c_str()); ASSERT(preResItem != nullptr, "Invalid item '%s' in list of pre-requisites of research '%s' ", resID.toUtf8().c_str(), getName(&asResearch[inc])); if (preResItem != nullptr) { asResearch[inc].pPRList.push_back(preResItem->index); } } } return true; }