// This source is from OpenEQ by Daeken et al. Modified a bit by Derision, some bug fixes etc. #include "wld.hpp" #include "file.hpp" #include "pfs.hpp" #include #include //#define DEBUGWLD static uchar encarr[] = {0x95, 0x3A, 0xC5, 0x2A, 0x95, 0x7A, 0x95, 0x6A}; inline void decode(uchar *str, int len) { int i; for(i = 0; i < len; ++i) str[i] ^= encarr[i % 8]; } FRAG_CONSTRUCTOR(Data03) { int i, count, fnlen; Texture *tex; count = uint32(buf); if(!count) count = 1; tex = new Texture; tex->filenames = new char *[count]; tex->frame_count = count; tex->current_frame = 0; tex->archive = nullptr; tex->flags = 0; for(i = 0; i < count; ++i) { fnlen = uint16(buf); tex->filenames[i] = new char[fnlen]; memcpy(tex->filenames[i], buf, fnlen); decode((uchar *) tex->filenames[i], fnlen); // Derision: Not sure why this check is here, but need to check fnlen is >=18 if(fnlen>=18) { if(tex->filenames[i][fnlen - 8] == '.') tex->filenames[i][fnlen - 17] = '\0'; } buf += fnlen; } this->frag = (void *) tex; } FRAG_CONSTRUCTOR(Data04) { int flags, count, i; Texture *tex, *texref; flags = uint32(buf); count = uint32(buf); if(flags & (1 << 2)) buf += 4; if(flags & (1 << 3)) buf += 4; if(count == 0 || count == 1) this->frag = wld->frags[uint32(buf) - 1]->frag; else { tex = new Texture; tex->frame_count = count; tex->current_frame = 0; tex->archive = nullptr; tex->filenames = new char *[count]; tex->flags = 0; for(i = 0; i < count; ++i) { texref = (Texture *) wld->frags[uint32(buf) - 1]->frag; tex->filenames[i] = texref->filenames[texref->frame_count - 1]; } this->frag = tex; } } FRAG_CONSTRUCTOR(Data05) { this->frag = wld->frags[uint32(buf) - 1]->frag; } FRAG_CONSTRUCTOR(Data15) { struct_Data15 *hdr = (struct_Data15 *) buf; Placeable *plac = new Placeable; Placeable **pl; plac->x = hdr->trans[0]; plac->y = hdr->trans[1]; plac->z = hdr->trans[2]; plac->rx = hdr->rot[2] / 512.f * 360.f; plac->ry = hdr->rot[1] / 512.f * 360.f; plac->rz = hdr->rot[0] / 512.f * 360.f; plac->scale[0] = hdr->scale[2]; plac->scale[1] = hdr->scale[1]; plac->scale[2] = hdr->scale[1]; plac->model = Strings::ToInt((const char*)&wld->sHash[-(int)hdr->ref]); pl = new Placeable *[wld->model_data.plac_count + 1]; memcpy(pl, wld->model_data.placeable, sizeof(Placeable *) * wld->model_data.plac_count); if(wld->model_data.plac_count) delete[] wld->model_data.placeable; pl[wld->model_data.plac_count] = plac; wld->model_data.placeable = pl; ++wld->model_data.plac_count; } FRAG_CONSTRUCTOR(Data1B) { struct_Data1B *data = (struct_Data1B *) buf; Light *light = new Light; this->frag = light; light->x = light->y = light->z = light->rad = 0; if(data->flags & (1 << 3)) { light->r = data->color[0]; light->g = data->color[1]; light->b = data->color[2]; } else light->r = light->g = light->b = 1.0f; } FRAG_CONSTRUCTOR(Data1C) { this->frag = wld->frags[uint32(buf) - 1]->frag; } FRAG_CONSTRUCTOR(Data21) { struct_Data21 *data; long count = *((long *) buf); long i; wld->tree = (BSP_Node *) malloc(count * sizeof(BSP_Node)); // Build the BSP Tree // for(i = 0; i < count; ++i) { wld->tree[i].node_number=i; data = (struct_Data21 *) (buf + (i * sizeof(struct_Data21)) + 4); wld->tree[i].normal[0] = data->normal[0]; wld->tree[i].normal[1] = data->normal[1]; wld->tree[i].normal[2] = data->normal[2]; wld->tree[i].splitdistance = data->splitdistance; wld->tree[i].region = data->region; wld->tree[i].left = data->node[0]; wld->tree[i].right = data->node[1]; } } FRAG_CONSTRUCTOR(Data22) { int pos; uchar *data6area ; struct_Data22 *data = (struct_Data22 *) buf; pos = sizeof(struct_Data22) + (12 * data->size1) + (8 * data->size2); data6area = buf + pos; if((data->size3!=0)||(data->size4!=0)) { printf("Size 3 and 4 not zero, we can't handle that yet\n"); exit(1); } data6area = data6area + ((data->size5) * 7 * 4); unsigned short d6size = *((unsigned short *) data6area); data6area = data6area + 2; // Move past d6 size data6area = data6area + d6size; // Move past RLE data ? Hopefully; float f1, f2, f3, f4; f1 = *((float *) data6area); f2 = *((float *) (data6area+4)); f3 = *((float *) (data6area+8)); f4 = *((float *) (data6area+12)); long Frag36Ref; if(data->flags==0x181) { Frag36Ref = *((long *) (data6area+20)); } } FRAG_CONSTRUCTOR(Data29) { long a,flags, numregions, lenstr; struct_Data29 *data29 = (struct_Data29 *) malloc(sizeof(struct_Data29)); data29->region_type = -1; // Start of by flagging type as unknown if(!strncmp((char *) &wld->sHash[-frag_name], "WT", 2)) data29->region_type = 1; // Water else if(!strncmp((char *) &wld->sHash[-frag_name], "LA", 2)) data29->region_type = 2; // Lava else if(!strncmp((char *) &wld->sHash[-frag_name], "DRNTP", 5)) data29->region_type = 3; // Zone Line else if(!strncmp((char *) &wld->sHash[-frag_name], "DRP_", 4)) data29->region_type = 4; // PVP else if(!strncmp((char *) &wld->sHash[-frag_name], "SL", 2)) data29->region_type = 5; // Slippery/Slime ? else if(!strncmp((char *) &wld->sHash[-frag_name], "DRN", 3)) data29->region_type = 6; // Ice/Ice Water else if(!strncmp((char *) &wld->sHash[-frag_name], "VWA", 3)) data29->region_type = 7; // VWater ? this->frag = (void *) data29; flags = *buf; numregions = *((long *)(buf+4)); data29->region_count = numregions ; data29->region_array = (long *)malloc(numregions * sizeof(long)); for(a=0;aregion_array[a] = *((long *)(buf+8+(a*4))); } lenstr = *((long *)(buf+8+(numregions*4))); data29->strlen=lenstr; if(lenstr==0) return; data29->str = (char *)malloc(lenstr); char *encstr; encstr = (char *)buf+8+(++a*4); decode((uchar *)encstr, lenstr); strcpy(data29->str, encstr); // printf(" data2 name is %s\n", data29->str); if(lenstr>=5) if(!strncmp(encstr,"WT",2)) data29->region_type = 1; // Water else if(!strncmp(encstr,"LA",2)) data29->region_type = 2; // Lava else if(!strncmp(encstr,"DRNTP",5)) data29->region_type = 3; // Zone Line ? else if(!strncmp(encstr,"DRP_",4)) data29->region_type = 4; //PVP else if(!strncmp(encstr,"SL",2)) data29->region_type = 5; // Slippery/Slime ? else if(!strncmp(encstr,"DRN",3)) data29->region_type = 6; // Ice/Ice Water ? else if(!strncmp(encstr,"VWN",3)) data29->region_type = 7; // Ice/Ice Water ? else data29->region_type = -2; // Flag this so that it doesn't get set to the default water or lava // later, because it is neither. // } FRAG_CONSTRUCTOR(Data28) { // struct_Data28 *data = (struct_Data28 *) buf; // obviously unused for now. } FRAG_CONSTRUCTOR(Data30) { struct_Data30 *data; uint32 ref; Texture *tex; data = (struct_Data30 *) buf; buf += sizeof(struct_Data30); if(!data->flags) buf += 8; ref = uint32(buf); if(!data->params1 || !ref) { tex = new Texture; tex->frame_count = 1; tex->current_frame = 0; tex->filenames = new char *[1]; tex->filenames[0] = "collide.dds"; tex->archive = nullptr; tex->flags = 1; this->frag = (void *) tex; return; } tex = (Texture *) wld->frags[ref - 1]->frag; if(data->params1 & (1 << 1) || data->params1 & (1 << 2) || data->params1 & (1 << 3) || data->params1 & (1 << 4)) tex->flags = 1; else tex->flags = 0; this->frag = (void *) tex; } FRAG_CONSTRUCTOR(Data31) { TexRef *tr; int i, count; buf += 4; count = uint32(buf); tr = new TexRef; tr->tex_count = count; tr->tex = new Texture *[count]; for(i = 0; i < count; ++i) tr->tex[i] = (Texture *) wld->frags[uint32(buf) - 1]->frag; this->frag = (void *) tr; } FRAG_CONSTRUCTOR(Data36) { struct_Data36 *header = (struct_Data36 *) buf; Poly *p; TexCoordsNew *tex_new; TexCoordsOld *tex_old; VertexNormal *vn; Vert *v; TexRef *tr; Model *model; Vertex *vert; Polygon *poly; float scale = 1.0f / (float) (1 << header->scale); int i, j, pc; TexMap *tm; float recip_255 = 1.0f / 256.0f, recip_127 = 1.0f / 127.0f; buf += sizeof(struct_Data36); model = new Model; model->vert_count = header->vertexCount; model->poly_count = header->PolygonsCount; model->verts = new Vertex *[model->vert_count]; model->polys = new Polygon *[model->poly_count]; tr = (TexRef *) wld->frags[header->fragment1 - 1]->frag; model->tex = tr->tex; // Reference to type 0x31 Texture List fragment model->tex_count = tr->tex_count; model->name = (char *) &wld->sHash[-frag_name]; #ifdef DEBUGWLD printf("Frag 0x36, %4d Vertices, %4d Polys, %4d TexCoords, %4d Normals, %3d Colors, %3d Polytex, %3d VertexTex, Name %s\n", header->vertexCount, header->PolygonsCount, header->texCoordsCount, header->normalsCount, header->colorCount, header->PolygonTexCount, header->vertexTexCount, model->name); #endif for(i = 0; i < header->vertexCount; ++i) { v = (Vert *) buf; vert = model->verts[i] = new Vertex; vert->x = header->centerX + v->x * scale; vert->y = header->centerY + v->y * scale; vert->z = header->centerZ + v->z * scale; #ifdef DEBUGWLD2 printf(" Vertex %4d: X=%4.2f, Y=%4.2f, Z=%4.2f\n", i, vert->x, vert->y, vert->z); #endif buf += sizeof(Vert); } if(wld->old) { for(i = 0; i < header->texCoordsCount; ++i) { tex_old = (TexCoordsOld *) buf; vert = model->verts[i]; vert->u = (float) tex_old->tx * recip_255; vert->v = (float) tex_old->tz * recip_255; #ifdef DEBUGWLD2 printf(" TexCoord %4d: U=%4.2f, V=%4.2f\n", i, vert->u, vert->v); #endif buf += sizeof(TexCoordsOld); } } else { for(i = 0; i < header->texCoordsCount; ++i) { tex_new = (TexCoordsNew *) buf; vert = model->verts[i]; vert->u = tex_new->tx; vert->v = tex_new->tz; #ifdef DEBUGWLD2 printf(" TexCoord %4d: U=%4.2f, V=%4.2f\n", i, vert->u, vert->v); #endif buf += sizeof(TexCoordsNew); } } for(i = 0; i < header->normalsCount; ++i) { if(ivertexCount) { vn = (VertexNormal *) buf; vert = model->verts[i]; vert->i = (float) vn->nx * recip_127; vert->j = (float) vn->ny * recip_127; vert->k = (float) vn->nz * recip_127; } buf += sizeof(VertexNormal); } buf += 4 * header->colorCount; for(i = 0; i < header->PolygonsCount; ++i) { p = (Poly *) buf; poly = model->polys[i] = new Polygon; poly->flags = p->flags; // Derision: 16/06/08 // Previously, this was assigning v1=v1, v2=v3, v3=v2. This is different to how it was done // in the WLD loader in the stock azone, and has the effect of screwing up the normals, // Changed to keep things consistent with prior versions. poly->v1 = p->v1; poly->v2 = p->v2; poly->v3 = p->v3; #ifdef DEBUGWLD2 printf("Frag36: Polygon: %5d Vertices: %5d, %5d, %5d\n", i, poly->v1, poly->v2, poly->v3); #endif buf += sizeof(Poly); } buf += 4 * header->size6; pc = 0; // For each polygontex entry ... for(i = 0; i < header->PolygonTexCount; ++i) { tm = (TexMap *) buf; for(j = 0; j < tm->polycount; ++j) { if(pc>=(model->poly_count)) { break; // Derision: Crashing here in acrylia because of this. TODO: Find root cause, for now, this lets zone load } // there are tm->polycount consecutive polys that use this tex model->polys[pc++]->tex = tm->tex; } buf += 4; } #ifdef DEBUGWLD fflush(stdout); #endif this->frag = (void *) model; } WLDLoader::WLDLoader() { } WLDLoader::~WLDLoader() { this->Close(); } int WLDLoader::Open(char *base_path, char *zone_name, Archive *archive) { uchar *buffer; int buf_len; int i, j, vc, pc, bc, mlen, *pmap; Zone_Model *zm; Model *m; Archive *obj_archive; char *filename, *model_name; struct_wld_header *header; struct_wld_basic_frag *frag; #ifdef DEBUGWLD printf("Zone: %s\n", zone_name); #endif this->model_data.zone_model = nullptr; this->model_data.plac_count = 0; this->model_data.placeable = 0; this->obj_loader = this->plac_loader = nullptr; this->clear_plac = 0; filename = new char[strlen(zone_name) + 5]; sprintf(filename, "%s.wld", zone_name); if(!GetFile(&buffer, &buf_len, nullptr, filename, archive)) { delete[] filename; return 0; } delete[] filename; header = (struct_wld_header *) buffer; if(header->magic != 0x54503d02) return 0; buffer += sizeof(struct_wld_header); this->sHash = buffer; decode(this->sHash, header->stringHashSize); buffer += header->stringHashSize; if(header->version == 0x00015500) this->old = 1; else this->old = 0; this->fragcount = header->fragmentCount; this->frags = new Fragment *[header->fragmentCount]; for(i = 0; i < this->fragcount; ++i) { frag = (struct_wld_basic_frag *) buffer; buffer += sizeof(struct_wld_basic_frag); switch(frag->id) { case 0x03: FRAGMENT(Data03); break; case 0x04: FRAGMENT(Data04); break; case 0x05: FRAGMENT(Data05); break; case 0x15: FRAGMENT(Data15); break; case 0x1B: FRAGMENT(Data1B); break; case 0x1C: FRAGMENT(Data1C); break; case 0x21: FRAGMENT(Data21); break; case 0x22: FRAGMENT(Data22); break; case 0x29: FRAGMENT(Data29); break; case 0x28: FRAGMENT(Data28); break; case 0x30: FRAGMENT(Data30); break; case 0x31: FRAGMENT(Data31); break; case 0x36: FRAGMENT(Data36); break; default: this->frags[i] = new Fragment; break; } this->frags[i]->type = frag->id; this->frags[i]->name = frag->nameRef; buffer += frag->size - 4; } if(!strcmp(&zone_name[strlen(zone_name) - 4], "_obj")) { this->model_data.plac_count = 0; this->model_data.model_count = 0; // for an obj_s3d file, find out how many placeabale objects there are (each 0x36 Frag is a placeable object in an obj_s3d file for(i = 0; i < this->fragcount; ++i) { if(this->frags[i]->type == 0x36) ++this->model_data.model_count; } // allocate space for this many models this->model_data.models = new Model *[this->model_data.model_count]; this->model_data.model_count = 0; for(i = 0; i < this->fragcount; ++i) { if(this->frags[i]->type == 0x36) this->model_data.models[this->model_data.model_count++] = (Model *) this->frags[i]->frag; } } else if(!strcmp(&zone_name[strlen(zone_name) - 4], "_chr")) { } else if(!strcmp(zone_name, "objects")) { this->clear_plac = 1; } else { // We are procesing the zone mesh S3D zm = this->model_data.zone_model = new Zone_Model; zm->vert_count = 0; zm->poly_count = 0; for(i = 0; i < this->fragcount; ++i) { if(this->frags[i]->type != 0x36) continue; m = (Model *) this->frags[i]->frag; zm->vert_count += m->vert_count; zm->poly_count += m->poly_count; zm->tex_count = m->tex_count; // The texcount and tex fields are the same for every 0x36 frag in the zone mesh S3D zm->tex = m->tex; } zm->verts = new Vertex *[zm->vert_count]; zm->polys = new Polygon *[zm->poly_count]; vc = pc = 0; for(i = 0; i < this->fragcount; ++i) { if(this->frags[i]->type != 0x36) continue; m = (Model *) this->frags[i]->frag; bc = vc; // Copy the vertexes from the 0x36 frag into our zone model for(j = 0; j < m->vert_count; ++j) zm->verts[vc++] = m->verts[j]; // Adjust the polygon vertices indices to match the indices in our zone model vertex array for(j = 0; j < m->poly_count; ++j) { m->polys[j]->v1 += bc; m->polys[j]->v2 += bc; m->polys[j]->v3 += bc; #ifdef DEBUGWLD printf("Polygon: %5d Vertices %5d, %5d, %5d\n", j, m->polys[j]->v1, m->polys[j]->v2, m->polys[j]->v3); #endif #ifdef DEBUGWLD printf("Zone Frag36 No. %5d: Poly %5d V3 = %4.3f, %4.3f, %4.3f\n", i, j, zm->verts[m->polys[j]->v3]->x, zm->verts[m->polys[j]->v3]->y, zm->verts[m->polys[j]->v3]->z); fflush(stdout); #endif zm->polys[pc++] = m->polys[j]; } } this->model_data.plac_count = 0; this->model_data.model_count = 0; filename = new char[strlen(zone_name) + 10]; sprintf(filename, "%s_obj.s3d",zone_name); obj_archive = new PFSLoader; if(!obj_archive->Open(fopen(filename, "rb"))) { // printf("_obj.s3d file not found.\n"); return 1; } delete[] filename; filename = new char[strlen(zone_name) + 5]; sprintf(filename, "%s_obj", zone_name); this->obj_loader = new WLDLoader(); this->obj_loader->Open(nullptr, filename, obj_archive); delete[] filename; this->model_data.model_count = this->obj_loader->model_data.model_count; this->model_data.models = this->obj_loader->model_data.models; this->plac_loader = new WLDLoader(); this->plac_loader->Open(nullptr, "objects", archive); if(this->model_data.plac_count) { delete this->model_data.placeable[0]; delete[] this->model_data.placeable; } this->model_data.plac_count = this->plac_loader->model_data.plac_count; this->model_data.placeable = this->plac_loader->model_data.placeable; for(i = 0; i < this->model_data.plac_count; ++i) { mlen = strlen((char *) this->model_data.placeable[i]->model) - 8; model_name = new char[mlen + 12]; memcpy(model_name, (char *) this->model_data.placeable[i]->model, mlen); model_name[mlen] = 0; sprintf(model_name, "%sDMSPRITEDEF", model_name); this->model_data.placeable[i]->model = -1; for(j = 0; j < this->model_data.model_count; ++j) { if(!strcmp(this->model_data.models[j]->name, model_name)) { this->model_data.placeable[i]->model = j; // printf("Placeable object %d name is %s\n", j, model_name); break; } } delete[] model_name; } for(i = 0; i < this->model_data.model_count; ++i) { for(j = 0; j < this->model_data.models[i]->tex_count; ++j) this->model_data.models[i]->tex[j]->archive = obj_archive; } for(i = 0; i < this->model_data.model_count; ++i) { pmap = new int[this->model_data.models[i]->poly_count]; memset(pmap, 0, sizeof(int) * this->model_data.models[i]->poly_count); for(j = 0; j < this->model_data.models[i]->tex_count; ++j) { filename = this->model_data.models[i]->tex[j]->filenames[0]; for(bc = 0; bc < this->model_data.zone_model->tex_count; ++bc) { if(!strcmp(filename, this->model_data.zone_model->tex[bc]->filenames[0])) { for(pc = 0; pc < this->model_data.models[i]->poly_count; ++pc) { if(!pmap[pc] && this->model_data.models[i]->polys[pc]->tex == j) { this->model_data.models[i]->polys[pc]->tex = bc; pmap[pc] = 1; } } break; } } } delete[] pmap; } } return 1; } int WLDLoader::Close() { int i, j; Zone_Model *zm = this->model_data.zone_model; Model *m; return -1; if(zm) { for(j = 0; j < zm->tex_count; ++j) delete zm->tex[j]; delete[] zm->tex; delete[] zm->verts; delete[] zm->polys; } for(i = 0; i < this->fragcount; ++i) { if(this->frags[i]->type == 0x36) { m = (Model *) this->frags[i]->frag; for(j = 0; j < m->vert_count; ++j) delete m->verts[j]; for(j = 0; j < m->poly_count; ++j) delete m->polys[j]; if(!zm) { for(j = 0; j < m->tex_count; ++j) delete m->tex[j]; delete[] m->tex; } delete[] m->verts; delete[] m->polys; delete m; } else delete this->frags[i]; } delete[] this->frags; if(this->clear_plac) { // for(i = 0; i < this->model_data.plac_count; ++i) // delete[] this->model_data.placeable[i]; } delete[] this->model_data.placeable; delete this->obj_loader; delete this->plac_loader; return 1; }