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;
224 _pico_printf( PICO_NORMAL, "Loading \"%s\"", fileName );
226 bb = (picoByte_t*) buffer;
229 fm.fm_header_hdr = (fm_chunk_header_t *) bb;
230 fm_file_pos = sizeof(fm_chunk_header_t) + fm.fm_header_hdr->size;
231 if( (strcmp(fm.fm_header_hdr->ident, FM_HEADERCHUNKNAME)) )
233 _pico_printf( PICO_WARNING, "FM Header Ident incorrect\n");
237 if( _pico_little_long( fm.fm_header_hdr->version ) != FM_HEADERCHUNKVER )
239 _pico_printf( PICO_WARNING, "FM Header Version incorrect\n");
244 fm.fm_skin_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);
245 fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_skin_hdr->size;
246 if( (strcmp(fm.fm_skin_hdr->ident, FM_SKINCHUNKNAME)) )
248 _pico_printf( PICO_WARNING, "FM Skin Ident incorrect\n");
252 if( _pico_little_long( fm.fm_skin_hdr->version ) != FM_SKINCHUNKVER )
254 _pico_printf( PICO_WARNING, "FM Skin Version incorrect\n");
259 fm.fm_st_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);
260 fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_st_hdr->size;
261 if( (strcmp(fm.fm_st_hdr->ident, FM_STCOORDCHUNKNAME)) )
263 _pico_printf( PICO_WARNING, "FM ST Ident incorrect\n");
267 if( _pico_little_long( fm.fm_st_hdr->version ) != FM_STCOORDCHUNKVER )
269 _pico_printf( PICO_WARNING, "FM ST Version incorrect\n");
274 fm.fm_tri_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);
275 fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_tri_hdr->size;
276 if( (strcmp(fm.fm_tri_hdr->ident, FM_TRISCHUNKNAME)) )
278 _pico_printf( PICO_WARNING, "FM Tri Ident incorrect\n");
282 if( _pico_little_long( fm.fm_tri_hdr->version ) != FM_TRISCHUNKVER )
284 _pico_printf( PICO_WARNING, "FM Tri Version incorrect\n");
289 fm.fm_frame_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);
290 fm_file_pos += sizeof(fm_chunk_header_t);
291 if( (strcmp(fm.fm_frame_hdr->ident, FM_FRAMESCHUNKNAME)) )
293 _pico_printf( PICO_WARNING, "FM Frame Ident incorrect\n");
297 if( _pico_little_long( fm.fm_frame_hdr->version ) != FM_FRAMESCHUNKVER )
299 _pico_printf( PICO_WARNING, "FM Frame Version incorrect\n");
304 fm_file_pos = sizeof(fm_chunk_header_t);
305 fm_head = fm.fm_header = (fm_header_t *) (bb + fm_file_pos);
306 fm_file_pos += fm.fm_header_hdr->size;
309 fm_file_pos += sizeof(fm_chunk_header_t);
310 fm.fm_skin = (fm_skinpath_t *) (bb + fm_file_pos);
311 fm_file_pos += fm.fm_skin_hdr->size;
314 fm_file_pos += sizeof(fm_chunk_header_t);
315 texCoord = fm.fm_st = (fm_st_t *) (bb + fm_file_pos);
316 fm_file_pos += fm.fm_st_hdr->size;
319 fm_file_pos += sizeof(fm_chunk_header_t);
320 tri_verts = fm.fm_tri = (fm_xyz_st_t *) (bb + fm_file_pos);
321 fm_file_pos += fm.fm_tri_hdr->size;
324 fm_file_pos += sizeof(fm_chunk_header_t);
325 frame = fm.fm_frame = (fm_frame_t *) (bb + fm_file_pos);
328 if( fm_head->numFrames < 1 )
330 _pico_printf( PICO_ERROR, "%s has 0 frames!", fileName );
334 if( frameNum < 0 || frameNum >= fm_head->numFrames )
336 _pico_printf( PICO_ERROR, "Invalid or out-of-range FM frame specified" );
341 fm_head->skinWidth = _pico_little_long( fm_head->skinWidth );
342 fm_head->skinHeight = _pico_little_long( fm_head->skinHeight );
343 fm_head->frameSize = _pico_little_long( fm_head->frameSize );
345 fm_head->numSkins = _pico_little_long( fm_head->numSkins );
346 fm_head->numXYZ = _pico_little_long( fm_head->numXYZ );
347 fm_head->numST = _pico_little_long( fm_head->numST );
348 fm_head->numTris = _pico_little_long( fm_head->numTris );
349 fm_head->numGLCmds = _pico_little_long( fm_head->numGLCmds );
350 fm_head->numFrames = _pico_little_long( fm_head->numFrames );
352 // swap frame scale and translation
353 for( i = 0; i < 3; i++ )
355 frame->header.scale[ i ] = _pico_little_float( frame->header.scale[ i ] );
356 frame->header.translate[ i ] = _pico_little_float( frame->header.translate[ i ] );
360 triangle = tri_verts;
361 for( i = 0; i < fm_head->numTris; i++, triangle++ )
363 for( j = 0; j < 3; j++ )
365 triangle->index_xyz[ j ] = _pico_little_short( triangle->index_xyz[ j ] );
366 triangle->index_st[ j ] = _pico_little_short( triangle->index_st[ j ] );
371 for( i = 0; i < fm_head->numST; i++ )
373 texCoord->s = _pico_little_short( texCoord[i].s );
374 texCoord->t = _pico_little_short( texCoord[i].t );
377 strncpy(skinname, (unsigned char *) fm.fm_skin, FM_SKINPATHSIZE );
379 #ifdef FM_VERBOSE_DBG
380 // Print out md2 values
381 _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 );
385 _pico_setfext( skinname, "" );
386 _pico_unixify( skinname );
388 /* create new pico model */
389 picoModel = PicoNewModel();
390 if( picoModel == NULL )
392 _pico_printf( PICO_ERROR, "Unable to allocate a new model" );
397 PicoSetModelFrameNum( picoModel, frameNum );
398 PicoSetModelNumFrames( picoModel, fm_head->numFrames ); /* sea */
399 PicoSetModelName( picoModel, fileName );
400 PicoSetModelFileName( picoModel, fileName );
402 // allocate new pico surface
403 picoSurface = PicoNewSurface( picoModel );
404 if( picoSurface == NULL )
406 _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );
407 PicoFreeModel( picoModel );
412 PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );
413 PicoSetSurfaceName( picoSurface, frame->header.name );
414 picoShader = PicoNewShader( picoModel );
415 if( picoShader == NULL )
417 _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );
418 PicoFreeModel( picoModel );
422 PicoSetShaderName( picoShader, skinname );
424 // associate current surface with newly created shader
425 PicoSetSurfaceShader( picoSurface, picoShader );
427 // Init LUT for Verts
428 p_index_LUT = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t) * fm_head->numXYZ);
429 for(i=0; i<fm_head->numXYZ; i++)
431 p_index_LUT[i].Vert = -1;
432 p_index_LUT[i].ST = -1;
433 p_index_LUT[i].next = NULL;
436 // Fill in Look Up Table, and allocate/fill Linked List from vert array as needed for dup STs per Vert.
437 tot_numVerts = fm_head->numXYZ;
439 triangle = tri_verts;
441 for(i=0; i<fm_head->numTris; i++)
445 if (p_index_LUT[triangle->index_xyz[j]].ST == -1) // No Main Entry
446 p_index_LUT[triangle->index_xyz[j]].ST = triangle->index_st[j];
448 else if (triangle->index_st[j] == p_index_LUT[triangle->index_xyz[j]].ST ) // Equal to Main Entry
450 #ifdef FM_VERBOSE_DBG
451 _pico_printf( PICO_NORMAL, "-> Tri #%d, Vert %d:\t XYZ:%d ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]);
455 else if ( (p_index_LUT[triangle->index_xyz[j]].next == NULL) ) // Not equal to Main entry, and no LL entry
456 { // Add first entry of LL from Main
457 p_index_LUT2 = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t));
458 if (p_index_LUT2 == NULL)
459 _pico_printf( PICO_NORMAL, " Couldn't allocate memory!\n");
460 p_index_LUT[triangle->index_xyz[j]].next = (index_LUT_t *)p_index_LUT2;
461 p_index_LUT2->Vert = dups;
462 p_index_LUT2->ST = triangle->index_st[j];
463 p_index_LUT2->next = NULL;
464 #ifdef FM_VERBOSE_DBG
465 _pico_printf( PICO_NORMAL, " ADDING first LL XYZ:%d DUP:%d ST:%d\n", triangle->index_xyz[j], dups, triangle->index_st[j]);
467 triangle->index_xyz[j] = dups + fm_head->numXYZ; // Make change in Tri hunk
470 else // Try to find in LL from Main Entry
472 p_index_LUT3 = p_index_LUT2 = p_index_LUT[triangle->index_xyz[j]].next;
473 while ( (p_index_LUT2 != NULL) && (triangle->index_xyz[j] != p_index_LUT2->Vert) ) // Walk down LL
475 p_index_LUT3 = p_index_LUT2;
476 p_index_LUT2 = p_index_LUT2->next;
478 p_index_LUT2 = p_index_LUT3;
480 if ( triangle->index_st[j] == p_index_LUT2->ST ) // Found it
482 triangle->index_xyz[j] = p_index_LUT2->Vert + fm_head->numXYZ; // Make change in Tri hunk
483 #ifdef FM_VERBOSE_DBG
484 _pico_printf( PICO_NORMAL, "--> Tri #%d, Vert %d:\t XYZ:%d ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]);
489 if ( p_index_LUT2->next == NULL) // Didn't find it. Add entry to LL.
492 p_index_LUT3 = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t));
493 if (p_index_LUT3 == NULL)
494 _pico_printf( PICO_NORMAL, " Couldn't allocate memory!\n");
495 p_index_LUT2->next = (index_LUT_t *)p_index_LUT3;
496 p_index_LUT3->Vert = dups;
497 p_index_LUT3->ST = triangle->index_st[j];
498 p_index_LUT3->next = NULL;
499 #ifdef FM_VERBOSE_DBG
500 _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]);
502 triangle->index_xyz[j] = dups + fm_head->numXYZ; // Make change in Tri hunk
506 #ifdef FM_VERBOSE_DBG
507 _pico_printf( PICO_NORMAL, "---> Tri #%d, Vert %d:\t XYZ:%d ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]);
513 // malloc and build array for Dup STs
514 p_index_LUT_DUPS = (index_DUP_LUT_t *)_pico_alloc(sizeof(index_DUP_LUT_t) * dups);
515 if (p_index_LUT_DUPS == NULL)
516 _pico_printf( PICO_NORMAL, " Couldn't allocate memory!\n");
519 for(i=0; i<fm_head->numXYZ; i++)
521 p_index_LUT2 = p_index_LUT[i].next;
522 while (p_index_LUT2 != NULL)
524 p_index_LUT_DUPS[p_index_LUT2->Vert].OldVert = i;
525 p_index_LUT_DUPS[p_index_LUT2->Vert].ST = p_index_LUT2->ST;
527 p_index_LUT2 = p_index_LUT2->next;
530 #ifdef FM_VERBOSE_DBG
531 _pico_printf( PICO_NORMAL, " Dups = %d\n", dups);
532 _pico_printf( PICO_NORMAL, " Dup Index = %d\n", dup_index);
534 for(i=0; i<fm_head->numXYZ; i++)
536 #ifdef FM_VERBOSE_DBG
537 _pico_printf( PICO_NORMAL, "Vert: %4d\t%4d",i, p_index_LUT[i].ST);
539 if (p_index_LUT[i].next != NULL)
542 p_index_LUT2 = p_index_LUT[i].next;
544 #ifdef FM_VERBOSE_DBG
545 _pico_printf( PICO_NORMAL, " %4d %4d", p_index_LUT2->Vert, p_index_LUT2->ST);
547 p_index_LUT2 = p_index_LUT2->next;
548 } while ( p_index_LUT2 != NULL);
551 #ifdef FM_VERBOSE_DBG
552 _pico_printf( PICO_NORMAL, "\n");
557 #ifdef FM_VERBOSE_DBG
558 for(i=0; i<dup_index; i++)
559 _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);
561 triangle = tri_verts;
562 for(i=0; i<fm_head->numTris; i++)
565 _pico_printf( PICO_NORMAL, "Tri #%d, Vert %d:\t XYZ:%d ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]);
566 _pico_printf( PICO_NORMAL, "\n");
571 triangle = tri_verts;
572 for( j = 0; j < fm_head->numTris; j++, triangle++ )
574 PicoSetSurfaceIndex( picoSurface, j*3 , triangle->index_xyz[0] );
575 PicoSetSurfaceIndex( picoSurface, j*3+1 , triangle->index_xyz[1] );
576 PicoSetSurfaceIndex( picoSurface, j*3+2 , triangle->index_xyz[2] );
579 vert = (fm_vert_normal_t*) ((picoByte_t*) (frame->verts) );
580 for(i=0; i< fm_head->numXYZ; i++, vert++)
582 /* set vertex origin */
583 xyz[ 0 ] = vert->v[0] * frame->header.scale[0] + frame->header.translate[0];
584 xyz[ 1 ] = vert->v[1] * frame->header.scale[1] + frame->header.translate[1];
585 xyz[ 2 ] = vert->v[2] * frame->header.scale[2] + frame->header.translate[2];
586 PicoSetSurfaceXYZ( picoSurface, i , xyz );
589 normal[ 0 ] = fm_normals[vert->lightnormalindex][0];
590 normal[ 1 ] = fm_normals[vert->lightnormalindex][1];
591 normal[ 2 ] = fm_normals[vert->lightnormalindex][2];
592 PicoSetSurfaceNormal( picoSurface, i , normal );
595 st[ 0 ] = ((texCoord[p_index_LUT[i].ST].s) / ((float)fm_head->skinWidth));
596 st[ 1 ] = (texCoord[p_index_LUT[i].ST].t / ((float)fm_head->skinHeight));
597 PicoSetSurfaceST( picoSurface, 0, i , st );
602 for(i=0; i<dups; i++)
604 j = p_index_LUT_DUPS[i].OldVert;
605 /* set vertex origin */
606 xyz[ 0 ] = frame->verts[j].v[0] * frame->header.scale[0] + frame->header.translate[0];
607 xyz[ 1 ] = frame->verts[j].v[1] * frame->header.scale[1] + frame->header.translate[1];
608 xyz[ 2 ] = frame->verts[j].v[2] * frame->header.scale[2] + frame->header.translate[2];
609 PicoSetSurfaceXYZ( picoSurface, i + fm_head->numXYZ , xyz );
612 normal[ 0 ] = fm_normals[frame->verts[j].lightnormalindex][0];
613 normal[ 1 ] = fm_normals[frame->verts[j].lightnormalindex][1];
614 normal[ 2 ] = fm_normals[frame->verts[j].lightnormalindex][2];
615 PicoSetSurfaceNormal( picoSurface, i + fm_head->numXYZ , normal );
618 st[ 0 ] = ((texCoord[p_index_LUT_DUPS[i].ST].s) / ((float)fm_head->skinWidth));
619 st[ 1 ] = (texCoord[p_index_LUT_DUPS[i].ST].t / ((float)fm_head->skinHeight));
620 PicoSetSurfaceST( picoSurface, 0, i + fm_head->numXYZ , st );
625 PicoSetSurfaceColor( picoSurface, 0, 0, color );
627 // Free up malloc'ed LL entries
628 for(i=0; i<fm_head->numXYZ; i++)
630 if(p_index_LUT[i].next != NULL)
632 p_index_LUT2 = p_index_LUT[i].next;
634 p_index_LUT3 = p_index_LUT2->next;
635 _pico_free(p_index_LUT2);
636 p_index_LUT2 = p_index_LUT3;
638 } while (p_index_LUT2 != NULL);
643 _pico_printf(PICO_WARNING, " Not all LL mallocs freed\n");
645 // Free malloc'ed LUTs
646 _pico_free(p_index_LUT);
647 _pico_free(p_index_LUT_DUPS);
649 /* return the new pico model */
656 /* pico file format module definition */
657 const picoModule_t picoModuleFM =
659 "0.85", /* module version string */
660 "Heretic 2 FM", /* module display name */
661 "Nurail", /* author's name */
662 "2003 Nurail", /* module copyright */
664 "fm", NULL, NULL, NULL /* default extensions to use */
666 _fm_canload, /* validation routine */
667 _fm_load, /* load routine */
668 NULL, /* save validation routine */
669 NULL /* save routine */