1 /* -----------------------------------------------------------------------------
5 Copyright (c) 2002, Randy Reddig & seaw0lf
8 Redistribution and use in source and binary forms, with or without modification,
9 are permitted provided that the following conditions are met:
11 Redistributions of source code must retain the above copyright notice, this list
12 of conditions and the following disclaimer.
14 Redistributions in binary form must reproduce the above copyright notice, this
15 list of conditions and the following disclaimer in the documentation and/or
16 other materials provided with the distribution.
18 Neither the names of the copyright holders nor the names of its contributors may
19 be used to endorse or promote products derived from this software without
20 specific prior written permission.
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
23 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
26 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 ----------------------------------------------------------------------------- */
36 Nurail: Used pm_md3.c (Randy Reddig) as a template.
45 //#define FM_VERBOSE_DBG 0
49 typedef struct index_LUT_s
53 struct index_LUT_s *next;
57 typedef struct index_DUP_LUT_s
66 static int _fm_canload( PM_PARAMS_CANLOAD )
72 bb = (unsigned char *) buffer;
75 fm.fm_header_hdr = (fm_chunk_header_t *) bb;
76 fm_file_pos = sizeof(fm_chunk_header_t) + fm.fm_header_hdr->size;
78 _pico_printf( PICO_VERBOSE, "IDENT: %s\n", (unsigned char *) fm.fm_header_hdr->ident );
80 if( (strcmp(fm.fm_header_hdr->ident, FM_HEADERCHUNKNAME)) )
83 _pico_printf( PICO_WARNING, "FM Header Ident incorrect\n");
85 return PICO_PMV_ERROR_IDENT;
89 if( _pico_little_long( fm.fm_header_hdr->version ) != FM_HEADERCHUNKVER )
92 _pico_printf( PICO_WARNING, "FM Header Version incorrect\n");
94 return PICO_PMV_ERROR_VERSION;
98 fm.fm_skin_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);
99 fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_skin_hdr->size;
100 #ifdef FM_VERBOSE_DBG
101 _pico_printf( PICO_VERBOSE, "SKIN: %s\n", (unsigned char *) fm.fm_skin_hdr->ident );
103 if( (strcmp(fm.fm_skin_hdr->ident, FM_SKINCHUNKNAME)) )
106 _pico_printf( PICO_WARNING, "FM Skin Ident incorrect\n");
108 return PICO_PMV_ERROR_IDENT;
112 if( _pico_little_long( fm.fm_skin_hdr->version ) != FM_SKINCHUNKVER )
115 _pico_printf( PICO_WARNING, "FM Skin Version incorrect\n");
117 return PICO_PMV_ERROR_VERSION;
121 fm.fm_st_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);
122 fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_st_hdr->size;
123 #ifdef FM_VERBOSE_DBG
124 _pico_printf( PICO_VERBOSE, "ST: %s\n", (unsigned char *) fm.fm_st_hdr->ident );
126 if( (strcmp(fm.fm_st_hdr->ident, FM_STCOORDCHUNKNAME)) )
129 _pico_printf( PICO_WARNING, "FM ST Ident incorrect\n");
131 return PICO_PMV_ERROR_IDENT;
135 if( _pico_little_long( fm.fm_st_hdr->version ) != FM_STCOORDCHUNKVER )
138 _pico_printf( PICO_WARNING, "FM ST Version incorrect\n");
140 return PICO_PMV_ERROR_VERSION;
144 fm.fm_tri_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);
145 fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_tri_hdr->size;
146 #ifdef FM_VERBOSE_DBG
147 _pico_printf( PICO_VERBOSE, "TRI: %s\n", (unsigned char *) fm.fm_tri_hdr->ident );
149 if( (strcmp(fm.fm_tri_hdr->ident, FM_TRISCHUNKNAME)) )
152 _pico_printf( PICO_WARNING, "FM Tri Ident incorrect\n");
154 return PICO_PMV_ERROR_IDENT;
158 if( _pico_little_long( fm.fm_tri_hdr->version ) != FM_TRISCHUNKVER )
161 _pico_printf( PICO_WARNING, "FM Tri Version incorrect\n");
163 return PICO_PMV_ERROR_VERSION;
167 fm.fm_frame_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);
168 fm_file_pos += sizeof(fm_chunk_header_t);
169 #ifdef FM_VERBOSE_DBG
170 _pico_printf( PICO_VERBOSE, "FRAME: %s\n", (unsigned char *) fm.fm_frame_hdr->ident );
172 if( (strcmp(fm.fm_frame_hdr->ident, FM_FRAMESCHUNKNAME)) )
175 _pico_printf( PICO_WARNING, "FM Frame Ident incorrect\n");
177 return PICO_PMV_ERROR_IDENT;
181 if( _pico_little_long( fm.fm_frame_hdr->version ) != FM_FRAMESCHUNKVER )
184 _pico_printf( PICO_WARNING, "FM Frame Version incorrect\n");
186 return PICO_PMV_ERROR_VERSION;
189 // file seems to be a valid fm
195 // _fm_load() loads a Heretic 2 model file.
196 static picoModel_t *_fm_load( PM_PARAMS_LOAD )
198 int i, j, dups, dup_index;
201 index_LUT_t *p_index_LUT, *p_index_LUT2, *p_index_LUT3;
202 index_DUP_LUT_t *p_index_LUT_DUPS;
204 fm_vert_normal_t *vert;
206 char skinname[FM_SKINPATHSIZE];
208 fm_header_t *fm_head;
210 fm_xyz_st_t *tri_verts;
211 fm_xyz_st_t *triangle;
215 picoModel_t *picoModel;
216 picoSurface_t *picoSurface;
217 picoShader_t *picoShader;
218 picoVec3_t xyz, normal;
223 bb = (picoByte_t*) buffer;
226 fm.fm_header_hdr = (fm_chunk_header_t *) bb;
227 fm_file_pos = sizeof(fm_chunk_header_t) + fm.fm_header_hdr->size;
228 if( (strcmp(fm.fm_header_hdr->ident, FM_HEADERCHUNKNAME)) )
230 _pico_printf( PICO_WARNING, "FM Header Ident incorrect\n");
234 if( _pico_little_long( fm.fm_header_hdr->version ) != FM_HEADERCHUNKVER )
236 _pico_printf( PICO_WARNING, "FM Header Version incorrect\n");
241 fm.fm_skin_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);
242 fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_skin_hdr->size;
243 if( (strcmp(fm.fm_skin_hdr->ident, FM_SKINCHUNKNAME)) )
245 _pico_printf( PICO_WARNING, "FM Skin Ident incorrect\n");
249 if( _pico_little_long( fm.fm_skin_hdr->version ) != FM_SKINCHUNKVER )
251 _pico_printf( PICO_WARNING, "FM Skin Version incorrect\n");
256 fm.fm_st_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);
257 fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_st_hdr->size;
258 if( (strcmp(fm.fm_st_hdr->ident, FM_STCOORDCHUNKNAME)) )
260 _pico_printf( PICO_WARNING, "FM ST Ident incorrect\n");
264 if( _pico_little_long( fm.fm_st_hdr->version ) != FM_STCOORDCHUNKVER )
266 _pico_printf( PICO_WARNING, "FM ST Version incorrect\n");
271 fm.fm_tri_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);
272 fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_tri_hdr->size;
273 if( (strcmp(fm.fm_tri_hdr->ident, FM_TRISCHUNKNAME)) )
275 _pico_printf( PICO_WARNING, "FM Tri Ident incorrect\n");
279 if( _pico_little_long( fm.fm_tri_hdr->version ) != FM_TRISCHUNKVER )
281 _pico_printf( PICO_WARNING, "FM Tri Version incorrect\n");
286 fm.fm_frame_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);
287 fm_file_pos += sizeof(fm_chunk_header_t);
288 if( (strcmp(fm.fm_frame_hdr->ident, FM_FRAMESCHUNKNAME)) )
290 _pico_printf( PICO_WARNING, "FM Frame Ident incorrect\n");
294 if( _pico_little_long( fm.fm_frame_hdr->version ) != FM_FRAMESCHUNKVER )
296 _pico_printf( PICO_WARNING, "FM Frame Version incorrect\n");
301 fm_file_pos = sizeof(fm_chunk_header_t);
302 fm_head = fm.fm_header = (fm_header_t *) (bb + fm_file_pos);
303 fm_file_pos += fm.fm_header_hdr->size;
306 fm_file_pos += sizeof(fm_chunk_header_t);
307 fm.fm_skin = (fm_skinpath_t *) (bb + fm_file_pos);
308 fm_file_pos += fm.fm_skin_hdr->size;
311 fm_file_pos += sizeof(fm_chunk_header_t);
312 texCoord = fm.fm_st = (fm_st_t *) (bb + fm_file_pos);
313 fm_file_pos += fm.fm_st_hdr->size;
316 fm_file_pos += sizeof(fm_chunk_header_t);
317 tri_verts = fm.fm_tri = (fm_xyz_st_t *) (bb + fm_file_pos);
318 fm_file_pos += fm.fm_tri_hdr->size;
321 fm_file_pos += sizeof(fm_chunk_header_t);
322 frame = fm.fm_frame = (fm_frame_t *) (bb + fm_file_pos);
325 if( fm_head->numFrames < 1 )
327 _pico_printf( PICO_ERROR, "%s has 0 frames!", fileName );
331 if( frameNum < 0 || frameNum >= fm_head->numFrames )
333 _pico_printf( PICO_ERROR, "Invalid or out-of-range FM frame specified" );
338 fm_head->skinWidth = _pico_little_long( fm_head->skinWidth );
339 fm_head->skinHeight = _pico_little_long( fm_head->skinHeight );
340 fm_head->frameSize = _pico_little_long( fm_head->frameSize );
342 fm_head->numSkins = _pico_little_long( fm_head->numSkins );
343 fm_head->numXYZ = _pico_little_long( fm_head->numXYZ );
344 fm_head->numST = _pico_little_long( fm_head->numST );
345 fm_head->numTris = _pico_little_long( fm_head->numTris );
346 fm_head->numGLCmds = _pico_little_long( fm_head->numGLCmds );
347 fm_head->numFrames = _pico_little_long( fm_head->numFrames );
349 // swap frame scale and translation
350 for( i = 0; i < 3; i++ )
352 frame->header.scale[ i ] = _pico_little_float( frame->header.scale[ i ] );
353 frame->header.translate[ i ] = _pico_little_float( frame->header.translate[ i ] );
357 triangle = tri_verts;
358 for( i = 0; i < fm_head->numTris; i++, triangle++ )
360 for( j = 0; j < 3; j++ )
362 triangle->index_xyz[ j ] = _pico_little_short( triangle->index_xyz[ j ] );
363 triangle->index_st[ j ] = _pico_little_short( triangle->index_st[ j ] );
368 for( i = 0; i < fm_head->numST; i++ )
370 texCoord->s = _pico_little_short( texCoord[i].s );
371 texCoord->t = _pico_little_short( texCoord[i].t );
374 strncpy(skinname, (unsigned char *) fm.fm_skin, FM_SKINPATHSIZE );
376 #ifdef FM_VERBOSE_DBG
377 // Print out md2 values
378 _pico_printf(PICO_VERBOSE,"numSkins->%d numXYZ->%d numST->%d numTris->%d numFrames->%d\nSkin Name \"%s\"\n", fm_head->numSkins, fm_head->numXYZ, fm_head->numST, fm_head->numTris, fm_head->numFrames, &skinname );
382 _pico_setfext( skinname, "" );
383 _pico_unixify( skinname );
385 /* create new pico model */
386 picoModel = PicoNewModel();
387 if( picoModel == NULL )
389 _pico_printf( PICO_ERROR, "Unable to allocate a new model" );
394 PicoSetModelFrameNum( picoModel, frameNum );
395 PicoSetModelNumFrames( picoModel, fm_head->numFrames ); /* sea */
396 PicoSetModelName( picoModel, fileName );
397 PicoSetModelFileName( picoModel, fileName );
399 // allocate new pico surface
400 picoSurface = PicoNewSurface( picoModel );
401 if( picoSurface == NULL )
403 _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );
404 PicoFreeModel( picoModel );
409 PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );
410 PicoSetSurfaceName( picoSurface, frame->header.name );
411 picoShader = PicoNewShader( picoModel );
412 if( picoShader == NULL )
414 _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );
415 PicoFreeModel( picoModel );
419 PicoSetShaderName( picoShader, skinname );
421 // associate current surface with newly created shader
422 PicoSetSurfaceShader( picoSurface, picoShader );
424 // Init LUT for Verts
425 p_index_LUT = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t) * fm_head->numXYZ);
426 for(i=0; i<fm_head->numXYZ; i++)
428 p_index_LUT[i].Vert = -1;
429 p_index_LUT[i].ST = -1;
430 p_index_LUT[i].next = NULL;
433 // Fill in Look Up Table, and allocate/fill Linked List from vert array as needed for dup STs per Vert.
434 tot_numVerts = fm_head->numXYZ;
436 triangle = tri_verts;
438 for(i=0; i<fm_head->numTris; i++)
442 if (p_index_LUT[triangle->index_xyz[j]].ST == -1) // No Main Entry
443 p_index_LUT[triangle->index_xyz[j]].ST = triangle->index_st[j];
445 else if (triangle->index_st[j] == p_index_LUT[triangle->index_xyz[j]].ST ) // Equal to Main Entry
447 #ifdef FM_VERBOSE_DBG
448 _pico_printf( PICO_NORMAL, "-> Tri #%d, Vert %d:\t XYZ:%d ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]);
452 else if ( (p_index_LUT[triangle->index_xyz[j]].next == NULL) ) // Not equal to Main entry, and no LL entry
453 { // Add first entry of LL from Main
454 p_index_LUT2 = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t));
455 if (p_index_LUT2 == NULL)
456 _pico_printf( PICO_NORMAL, " Couldn't allocate memory!\n");
457 p_index_LUT[triangle->index_xyz[j]].next = (index_LUT_t *)p_index_LUT2;
458 p_index_LUT2->Vert = dups;
459 p_index_LUT2->ST = triangle->index_st[j];
460 p_index_LUT2->next = NULL;
461 #ifdef FM_VERBOSE_DBG
462 _pico_printf( PICO_NORMAL, " ADDING first LL XYZ:%d DUP:%d ST:%d\n", triangle->index_xyz[j], dups, triangle->index_st[j]);
464 triangle->index_xyz[j] = dups + fm_head->numXYZ; // Make change in Tri hunk
467 else // Try to find in LL from Main Entry
469 p_index_LUT3 = p_index_LUT2 = p_index_LUT[triangle->index_xyz[j]].next;
470 while ( (p_index_LUT2 != NULL) && (triangle->index_xyz[j] != p_index_LUT2->Vert) ) // Walk down LL
472 p_index_LUT3 = p_index_LUT2;
473 p_index_LUT2 = p_index_LUT2->next;
475 p_index_LUT2 = p_index_LUT3;
477 if ( triangle->index_st[j] == p_index_LUT2->ST ) // Found it
479 triangle->index_xyz[j] = p_index_LUT2->Vert + fm_head->numXYZ; // Make change in Tri hunk
480 #ifdef FM_VERBOSE_DBG
481 _pico_printf( PICO_NORMAL, "--> Tri #%d, Vert %d:\t XYZ:%d ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]);
486 if ( p_index_LUT2->next == NULL) // Didn't find it. Add entry to LL.
489 p_index_LUT3 = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t));
490 if (p_index_LUT3 == NULL)
491 _pico_printf( PICO_NORMAL, " Couldn't allocate memory!\n");
492 p_index_LUT2->next = (index_LUT_t *)p_index_LUT3;
493 p_index_LUT3->Vert = dups;
494 p_index_LUT3->ST = triangle->index_st[j];
495 p_index_LUT3->next = NULL;
496 #ifdef FM_VERBOSE_DBG
497 _pico_printf( PICO_NORMAL, " ADDING additional LL XYZ:%d DUP:%d NewXYZ:%d ST:%d\n", triangle->index_xyz[j], dups, dups + (fm_head->numXYZ), triangle->index_st[j]);
499 triangle->index_xyz[j] = dups + fm_head->numXYZ; // Make change in Tri hunk
503 #ifdef FM_VERBOSE_DBG
504 _pico_printf( PICO_NORMAL, "---> Tri #%d, Vert %d:\t XYZ:%d ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]);
510 // malloc and build array for Dup STs
511 p_index_LUT_DUPS = (index_DUP_LUT_t *)_pico_alloc(sizeof(index_DUP_LUT_t) * dups);
512 if (p_index_LUT_DUPS == NULL)
513 _pico_printf( PICO_NORMAL, " Couldn't allocate memory!\n");
516 for(i=0; i<fm_head->numXYZ; i++)
518 p_index_LUT2 = p_index_LUT[i].next;
519 while (p_index_LUT2 != NULL)
521 p_index_LUT_DUPS[p_index_LUT2->Vert].OldVert = i;
522 p_index_LUT_DUPS[p_index_LUT2->Vert].ST = p_index_LUT2->ST;
524 p_index_LUT2 = p_index_LUT2->next;
527 #ifdef FM_VERBOSE_DBG
528 _pico_printf( PICO_NORMAL, " Dups = %d\n", dups);
529 _pico_printf( PICO_NORMAL, " Dup Index = %d\n", dup_index);
531 for(i=0; i<fm_head->numXYZ; i++)
533 #ifdef FM_VERBOSE_DBG
534 _pico_printf( PICO_NORMAL, "Vert: %4d\t%4d",i, p_index_LUT[i].ST);
536 if (p_index_LUT[i].next != NULL)
539 p_index_LUT2 = p_index_LUT[i].next;
541 #ifdef FM_VERBOSE_DBG
542 _pico_printf( PICO_NORMAL, " %4d %4d", p_index_LUT2->Vert, p_index_LUT2->ST);
544 p_index_LUT2 = p_index_LUT2->next;
545 } while ( p_index_LUT2 != NULL);
548 #ifdef FM_VERBOSE_DBG
549 _pico_printf( PICO_NORMAL, "\n");
554 #ifdef FM_VERBOSE_DBG
555 for(i=0; i<dup_index; i++)
556 _pico_printf( PICO_NORMAL, " Dup Index #%d OldVert: %d ST: %d\n", i, p_index_LUT_DUPS[i].OldVert, p_index_LUT_DUPS[i].ST);
558 triangle = tri_verts;
559 for(i=0; i<fm_head->numTris; i++)
562 _pico_printf( PICO_NORMAL, "Tri #%d, Vert %d:\t XYZ:%d ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]);
563 _pico_printf( PICO_NORMAL, "\n");
568 triangle = tri_verts;
569 for( j = 0; j < fm_head->numTris; j++, triangle++ )
571 PicoSetSurfaceIndex( picoSurface, j*3 , triangle->index_xyz[0] );
572 PicoSetSurfaceIndex( picoSurface, j*3+1 , triangle->index_xyz[1] );
573 PicoSetSurfaceIndex( picoSurface, j*3+2 , triangle->index_xyz[2] );
576 vert = (fm_vert_normal_t*) ((picoByte_t*) (frame->verts) );
577 for(i=0; i< fm_head->numXYZ; i++, vert++)
579 /* set vertex origin */
580 xyz[ 0 ] = vert->v[0] * frame->header.scale[0] + frame->header.translate[0];
581 xyz[ 1 ] = vert->v[1] * frame->header.scale[1] + frame->header.translate[1];
582 xyz[ 2 ] = vert->v[2] * frame->header.scale[2] + frame->header.translate[2];
583 PicoSetSurfaceXYZ( picoSurface, i , xyz );
586 normal[ 0 ] = fm_normals[vert->lightnormalindex][0];
587 normal[ 1 ] = fm_normals[vert->lightnormalindex][1];
588 normal[ 2 ] = fm_normals[vert->lightnormalindex][2];
589 PicoSetSurfaceNormal( picoSurface, i , normal );
592 st[ 0 ] = ((texCoord[p_index_LUT[i].ST].s) / ((float)fm_head->skinWidth));
593 st[ 1 ] = (texCoord[p_index_LUT[i].ST].t / ((float)fm_head->skinHeight));
594 PicoSetSurfaceST( picoSurface, 0, i , st );
599 for(i=0; i<dups; i++)
601 j = p_index_LUT_DUPS[i].OldVert;
602 /* set vertex origin */
603 xyz[ 0 ] = frame->verts[j].v[0] * frame->header.scale[0] + frame->header.translate[0];
604 xyz[ 1 ] = frame->verts[j].v[1] * frame->header.scale[1] + frame->header.translate[1];
605 xyz[ 2 ] = frame->verts[j].v[2] * frame->header.scale[2] + frame->header.translate[2];
606 PicoSetSurfaceXYZ( picoSurface, i + fm_head->numXYZ , xyz );
609 normal[ 0 ] = fm_normals[frame->verts[j].lightnormalindex][0];
610 normal[ 1 ] = fm_normals[frame->verts[j].lightnormalindex][1];
611 normal[ 2 ] = fm_normals[frame->verts[j].lightnormalindex][2];
612 PicoSetSurfaceNormal( picoSurface, i + fm_head->numXYZ , normal );
615 st[ 0 ] = ((texCoord[p_index_LUT_DUPS[i].ST].s) / ((float)fm_head->skinWidth));
616 st[ 1 ] = (texCoord[p_index_LUT_DUPS[i].ST].t / ((float)fm_head->skinHeight));
617 PicoSetSurfaceST( picoSurface, 0, i + fm_head->numXYZ , st );
622 PicoSetSurfaceColor( picoSurface, 0, 0, color );
624 // Free up malloc'ed LL entries
625 for(i=0; i<fm_head->numXYZ; i++)
627 if(p_index_LUT[i].next != NULL)
629 p_index_LUT2 = p_index_LUT[i].next;
631 p_index_LUT3 = p_index_LUT2->next;
632 _pico_free(p_index_LUT2);
633 p_index_LUT2 = p_index_LUT3;
635 } while (p_index_LUT2 != NULL);
640 _pico_printf(PICO_WARNING, " Not all LL mallocs freed\n");
642 // Free malloc'ed LUTs
643 _pico_free(p_index_LUT);
644 _pico_free(p_index_LUT_DUPS);
646 /* return the new pico model */
653 /* pico file format module definition */
654 const picoModule_t picoModuleFM =
656 "0.85", /* module version string */
657 "Heretic 2 FM", /* module display name */
658 "Nurail", /* author's name */
659 "2003 Nurail", /* module copyright */
661 "fm", NULL, NULL, NULL /* default extensions to use */
663 _fm_canload, /* validation routine */
664 _fm_load, /* load routine */
665 NULL, /* save validation routine */
666 NULL /* save routine */