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 )
69 unsigned char *bb, *bb0;
72 bb0 = bb = (picoByte_t*) _pico_alloc(bufSize);
73 memcpy(bb, buffer, bufSize);
76 fm.fm_header_hdr = (fm_chunk_header_t *) bb;
77 fm_file_pos = sizeof(fm_chunk_header_t) + fm.fm_header_hdr->size;
79 _pico_printf( PICO_VERBOSE, "IDENT: %s\n", (unsigned char *) fm.fm_header_hdr->ident );
81 if( (strcmp(fm.fm_header_hdr->ident, FM_HEADERCHUNKNAME)) )
84 _pico_printf( PICO_WARNING, "FM Header Ident incorrect\n");
87 return PICO_PMV_ERROR_IDENT;
91 if( _pico_little_long( fm.fm_header_hdr->version ) != FM_HEADERCHUNKVER )
94 _pico_printf( PICO_WARNING, "FM Header Version incorrect\n");
97 return PICO_PMV_ERROR_VERSION;
101 fm.fm_skin_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);
102 fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_skin_hdr->size;
103 #ifdef FM_VERBOSE_DBG
104 _pico_printf( PICO_VERBOSE, "SKIN: %s\n", (unsigned char *) fm.fm_skin_hdr->ident );
106 if( (strcmp(fm.fm_skin_hdr->ident, FM_SKINCHUNKNAME)) )
109 _pico_printf( PICO_WARNING, "FM Skin Ident incorrect\n");
112 return PICO_PMV_ERROR_IDENT;
116 if( _pico_little_long( fm.fm_skin_hdr->version ) != FM_SKINCHUNKVER )
119 _pico_printf( PICO_WARNING, "FM Skin Version incorrect\n");
122 return PICO_PMV_ERROR_VERSION;
126 fm.fm_st_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);
127 fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_st_hdr->size;
128 #ifdef FM_VERBOSE_DBG
129 _pico_printf( PICO_VERBOSE, "ST: %s\n", (unsigned char *) fm.fm_st_hdr->ident );
131 if( (strcmp(fm.fm_st_hdr->ident, FM_STCOORDCHUNKNAME)) )
134 _pico_printf( PICO_WARNING, "FM ST Ident incorrect\n");
137 return PICO_PMV_ERROR_IDENT;
141 if( _pico_little_long( fm.fm_st_hdr->version ) != FM_STCOORDCHUNKVER )
144 _pico_printf( PICO_WARNING, "FM ST Version incorrect\n");
147 return PICO_PMV_ERROR_VERSION;
151 fm.fm_tri_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);
152 fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_tri_hdr->size;
153 #ifdef FM_VERBOSE_DBG
154 _pico_printf( PICO_VERBOSE, "TRI: %s\n", (unsigned char *) fm.fm_tri_hdr->ident );
156 if( (strcmp(fm.fm_tri_hdr->ident, FM_TRISCHUNKNAME)) )
159 _pico_printf( PICO_WARNING, "FM Tri Ident incorrect\n");
162 return PICO_PMV_ERROR_IDENT;
166 if( _pico_little_long( fm.fm_tri_hdr->version ) != FM_TRISCHUNKVER )
169 _pico_printf( PICO_WARNING, "FM Tri Version incorrect\n");
172 return PICO_PMV_ERROR_VERSION;
176 fm.fm_frame_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);
177 fm_file_pos += sizeof(fm_chunk_header_t);
178 #ifdef FM_VERBOSE_DBG
179 _pico_printf( PICO_VERBOSE, "FRAME: %s\n", (unsigned char *) fm.fm_frame_hdr->ident );
181 if( (strcmp(fm.fm_frame_hdr->ident, FM_FRAMESCHUNKNAME)) )
184 _pico_printf( PICO_WARNING, "FM Frame Ident incorrect\n");
187 return PICO_PMV_ERROR_IDENT;
191 if( _pico_little_long( fm.fm_frame_hdr->version ) != FM_FRAMESCHUNKVER )
194 _pico_printf( PICO_WARNING, "FM Frame Version incorrect\n");
197 return PICO_PMV_ERROR_VERSION;
200 // file seems to be a valid fm
206 // _fm_load() loads a Heretic 2 model file.
207 static picoModel_t *_fm_load( PM_PARAMS_LOAD )
209 int i, j, dups, dup_index;
211 index_LUT_t *p_index_LUT, *p_index_LUT2, *p_index_LUT3;
212 index_DUP_LUT_t *p_index_LUT_DUPS;
214 fm_vert_normal_t *vert;
216 char skinname[FM_SKINPATHSIZE];
218 fm_header_t *fm_head;
220 fm_xyz_st_t *tri_verts;
221 fm_xyz_st_t *triangle;
224 picoByte_t *bb, *bb0;
225 picoModel_t *picoModel;
226 picoSurface_t *picoSurface;
227 picoShader_t *picoShader;
228 picoVec3_t xyz, normal;
233 bb0 = bb = (picoByte_t*) _pico_alloc(bufSize);
234 memcpy(bb, buffer, bufSize);
237 fm.fm_header_hdr = (fm_chunk_header_t *) bb;
238 fm_file_pos = sizeof(fm_chunk_header_t) + fm.fm_header_hdr->size;
239 if( (strcmp(fm.fm_header_hdr->ident, FM_HEADERCHUNKNAME)) )
241 _pico_printf( PICO_WARNING, "FM Header Ident incorrect\n");
246 if( _pico_little_long( fm.fm_header_hdr->version ) != FM_HEADERCHUNKVER )
248 _pico_printf( PICO_WARNING, "FM Header Version incorrect\n");
254 fm.fm_skin_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);
255 fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_skin_hdr->size;
256 if( (strcmp(fm.fm_skin_hdr->ident, FM_SKINCHUNKNAME)) )
258 _pico_printf( PICO_WARNING, "FM Skin Ident incorrect\n");
263 if( _pico_little_long( fm.fm_skin_hdr->version ) != FM_SKINCHUNKVER )
265 _pico_printf( PICO_WARNING, "FM Skin Version incorrect\n");
271 fm.fm_st_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);
272 fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_st_hdr->size;
273 if( (strcmp(fm.fm_st_hdr->ident, FM_STCOORDCHUNKNAME)) )
275 _pico_printf( PICO_WARNING, "FM ST Ident incorrect\n");
280 if( _pico_little_long( fm.fm_st_hdr->version ) != FM_STCOORDCHUNKVER )
282 _pico_printf( PICO_WARNING, "FM ST Version incorrect\n");
288 fm.fm_tri_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);
289 fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_tri_hdr->size;
290 if( (strcmp(fm.fm_tri_hdr->ident, FM_TRISCHUNKNAME)) )
292 _pico_printf( PICO_WARNING, "FM Tri Ident incorrect\n");
297 if( _pico_little_long( fm.fm_tri_hdr->version ) != FM_TRISCHUNKVER )
299 _pico_printf( PICO_WARNING, "FM Tri Version incorrect\n");
305 fm.fm_frame_hdr = (fm_chunk_header_t *) (bb + fm_file_pos);
306 fm_file_pos += sizeof(fm_chunk_header_t);
307 if( (strcmp(fm.fm_frame_hdr->ident, FM_FRAMESCHUNKNAME)) )
309 _pico_printf( PICO_WARNING, "FM Frame Ident incorrect\n");
314 if( _pico_little_long( fm.fm_frame_hdr->version ) != FM_FRAMESCHUNKVER )
316 _pico_printf( PICO_WARNING, "FM Frame Version incorrect\n");
322 fm_file_pos = sizeof(fm_chunk_header_t);
323 fm_head = fm.fm_header = (fm_header_t *) (bb + fm_file_pos);
324 fm_file_pos += fm.fm_header_hdr->size;
327 fm_file_pos += sizeof(fm_chunk_header_t);
328 fm.fm_skin = (fm_skinpath_t *) (bb + fm_file_pos);
329 fm_file_pos += fm.fm_skin_hdr->size;
332 fm_file_pos += sizeof(fm_chunk_header_t);
333 texCoord = fm.fm_st = (fm_st_t *) (bb + fm_file_pos);
334 fm_file_pos += fm.fm_st_hdr->size;
337 fm_file_pos += sizeof(fm_chunk_header_t);
338 tri_verts = fm.fm_tri = (fm_xyz_st_t *) (bb + fm_file_pos);
339 fm_file_pos += fm.fm_tri_hdr->size;
342 fm_file_pos += sizeof(fm_chunk_header_t);
343 frame = fm.fm_frame = (fm_frame_t *) (bb + fm_file_pos);
346 if( fm_head->numFrames < 1 )
348 _pico_printf( PICO_ERROR, "%s has 0 frames!", fileName );
353 if( frameNum < 0 || frameNum >= fm_head->numFrames )
355 _pico_printf( PICO_ERROR, "Invalid or out-of-range FM frame specified" );
361 fm_head->skinWidth = _pico_little_long( fm_head->skinWidth );
362 fm_head->skinHeight = _pico_little_long( fm_head->skinHeight );
363 fm_head->frameSize = _pico_little_long( fm_head->frameSize );
365 fm_head->numSkins = _pico_little_long( fm_head->numSkins );
366 fm_head->numXYZ = _pico_little_long( fm_head->numXYZ );
367 fm_head->numST = _pico_little_long( fm_head->numST );
368 fm_head->numTris = _pico_little_long( fm_head->numTris );
369 fm_head->numGLCmds = _pico_little_long( fm_head->numGLCmds );
370 fm_head->numFrames = _pico_little_long( fm_head->numFrames );
372 // swap frame scale and translation
373 for( i = 0; i < 3; i++ )
375 frame->header.scale[ i ] = _pico_little_float( frame->header.scale[ i ] );
376 frame->header.translate[ i ] = _pico_little_float( frame->header.translate[ i ] );
380 triangle = tri_verts;
381 for( i = 0; i < fm_head->numTris; i++, triangle++ )
383 for( j = 0; j < 3; j++ )
385 triangle->index_xyz[ j ] = _pico_little_short( triangle->index_xyz[ j ] );
386 triangle->index_st[ j ] = _pico_little_short( triangle->index_st[ j ] );
391 for( i = 0; i < fm_head->numST; i++ )
393 texCoord->s = _pico_little_short( texCoord[i].s );
394 texCoord->t = _pico_little_short( texCoord[i].t );
397 strncpy(skinname, (const char *) fm.fm_skin, FM_SKINPATHSIZE );
399 #ifdef FM_VERBOSE_DBG
400 // Print out md2 values
401 _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 );
405 _pico_setfext( skinname, "" );
406 _pico_unixify( skinname );
408 /* create new pico model */
409 picoModel = PicoNewModel();
410 if( picoModel == NULL )
412 _pico_printf( PICO_ERROR, "Unable to allocate a new model" );
418 PicoSetModelFrameNum( picoModel, frameNum );
419 PicoSetModelNumFrames( picoModel, fm_head->numFrames ); /* sea */
420 PicoSetModelName( picoModel, fileName );
421 PicoSetModelFileName( picoModel, fileName );
423 // allocate new pico surface
424 picoSurface = PicoNewSurface( picoModel );
425 if( picoSurface == NULL )
427 _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );
428 PicoFreeModel( picoModel );
434 PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );
435 PicoSetSurfaceName( picoSurface, frame->header.name );
436 picoShader = PicoNewShader( picoModel );
437 if( picoShader == NULL )
439 _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );
440 PicoFreeModel( picoModel );
445 PicoSetShaderName( picoShader, skinname );
447 // associate current surface with newly created shader
448 PicoSetSurfaceShader( picoSurface, picoShader );
450 // Init LUT for Verts
451 p_index_LUT = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t) * fm_head->numXYZ);
452 for(i=0; i<fm_head->numXYZ; i++)
454 p_index_LUT[i].Vert = -1;
455 p_index_LUT[i].ST = -1;
456 p_index_LUT[i].next = NULL;
459 // Fill in Look Up Table, and allocate/fill Linked List from vert array as needed for dup STs per Vert.
461 triangle = tri_verts;
463 for(i=0; i<fm_head->numTris; i++)
467 if (p_index_LUT[triangle->index_xyz[j]].ST == -1) // No Main Entry
468 p_index_LUT[triangle->index_xyz[j]].ST = triangle->index_st[j];
470 else if (triangle->index_st[j] == p_index_LUT[triangle->index_xyz[j]].ST ) // Equal to Main Entry
472 #ifdef FM_VERBOSE_DBG
473 _pico_printf( PICO_NORMAL, "-> Tri #%d, Vert %d:\t XYZ:%d ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]);
477 else if ( (p_index_LUT[triangle->index_xyz[j]].next == NULL) ) // Not equal to Main entry, and no LL entry
478 { // Add first entry of LL from Main
479 p_index_LUT2 = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t));
480 if (p_index_LUT2 == NULL)
481 _pico_printf( PICO_NORMAL, " Couldn't allocate memory!\n");
482 p_index_LUT[triangle->index_xyz[j]].next = (index_LUT_t *)p_index_LUT2;
483 p_index_LUT2->Vert = dups;
484 p_index_LUT2->ST = triangle->index_st[j];
485 p_index_LUT2->next = NULL;
486 #ifdef FM_VERBOSE_DBG
487 _pico_printf( PICO_NORMAL, " ADDING first LL XYZ:%d DUP:%d ST:%d\n", triangle->index_xyz[j], dups, triangle->index_st[j]);
489 triangle->index_xyz[j] = dups + fm_head->numXYZ; // Make change in Tri hunk
492 else // Try to find in LL from Main Entry
494 p_index_LUT3 = p_index_LUT2 = p_index_LUT[triangle->index_xyz[j]].next;
495 while ( (p_index_LUT2 != NULL) && (triangle->index_xyz[j] != p_index_LUT2->Vert) ) // Walk down LL
497 p_index_LUT3 = p_index_LUT2;
498 p_index_LUT2 = p_index_LUT2->next;
500 p_index_LUT2 = p_index_LUT3;
502 if ( triangle->index_st[j] == p_index_LUT2->ST ) // Found it
504 triangle->index_xyz[j] = p_index_LUT2->Vert + fm_head->numXYZ; // Make change in Tri hunk
505 #ifdef FM_VERBOSE_DBG
506 _pico_printf( PICO_NORMAL, "--> Tri #%d, Vert %d:\t XYZ:%d ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]);
511 if ( p_index_LUT2->next == NULL) // Didn't find it. Add entry to LL.
514 p_index_LUT3 = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t));
515 if (p_index_LUT3 == NULL)
516 _pico_printf( PICO_NORMAL, " Couldn't allocate memory!\n");
517 p_index_LUT2->next = (index_LUT_t *)p_index_LUT3;
518 p_index_LUT3->Vert = dups;
519 p_index_LUT3->ST = triangle->index_st[j];
520 p_index_LUT3->next = NULL;
521 #ifdef FM_VERBOSE_DBG
522 _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]);
524 triangle->index_xyz[j] = dups + fm_head->numXYZ; // Make change in Tri hunk
528 #ifdef FM_VERBOSE_DBG
529 _pico_printf( PICO_NORMAL, "---> Tri #%d, Vert %d:\t XYZ:%d ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]);
535 // malloc and build array for Dup STs
536 p_index_LUT_DUPS = (index_DUP_LUT_t *)_pico_alloc(sizeof(index_DUP_LUT_t) * dups);
537 if (p_index_LUT_DUPS == NULL)
538 _pico_printf( PICO_NORMAL, " Couldn't allocate memory!\n");
541 for(i=0; i<fm_head->numXYZ; i++)
543 p_index_LUT2 = p_index_LUT[i].next;
544 while (p_index_LUT2 != NULL)
546 p_index_LUT_DUPS[p_index_LUT2->Vert].OldVert = i;
547 p_index_LUT_DUPS[p_index_LUT2->Vert].ST = p_index_LUT2->ST;
549 p_index_LUT2 = p_index_LUT2->next;
552 #ifdef FM_VERBOSE_DBG
553 _pico_printf( PICO_NORMAL, " Dups = %d\n", dups);
554 _pico_printf( PICO_NORMAL, " Dup Index = %d\n", dup_index);
556 for(i=0; i<fm_head->numXYZ; i++)
558 #ifdef FM_VERBOSE_DBG
559 _pico_printf( PICO_NORMAL, "Vert: %4d\t%4d",i, p_index_LUT[i].ST);
561 if (p_index_LUT[i].next != NULL)
564 p_index_LUT2 = p_index_LUT[i].next;
566 #ifdef FM_VERBOSE_DBG
567 _pico_printf( PICO_NORMAL, " %4d %4d", p_index_LUT2->Vert, p_index_LUT2->ST);
569 p_index_LUT2 = p_index_LUT2->next;
570 } while ( p_index_LUT2 != NULL);
573 #ifdef FM_VERBOSE_DBG
574 _pico_printf( PICO_NORMAL, "\n");
579 #ifdef FM_VERBOSE_DBG
580 for(i=0; i<dup_index; i++)
581 _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);
583 triangle = tri_verts;
584 for(i=0; i<fm_head->numTris; i++)
587 _pico_printf( PICO_NORMAL, "Tri #%d, Vert %d:\t XYZ:%d ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]);
588 _pico_printf( PICO_NORMAL, "\n");
593 triangle = tri_verts;
594 for( j = 0; j < fm_head->numTris; j++, triangle++ )
596 PicoSetSurfaceIndex( picoSurface, j*3 , triangle->index_xyz[0] );
597 PicoSetSurfaceIndex( picoSurface, j*3+1 , triangle->index_xyz[1] );
598 PicoSetSurfaceIndex( picoSurface, j*3+2 , triangle->index_xyz[2] );
601 vert = (fm_vert_normal_t*) ((picoByte_t*) (frame->verts) );
602 for(i=0; i< fm_head->numXYZ; i++, vert++)
604 /* set vertex origin */
605 xyz[ 0 ] = vert->v[0] * frame->header.scale[0] + frame->header.translate[0];
606 xyz[ 1 ] = vert->v[1] * frame->header.scale[1] + frame->header.translate[1];
607 xyz[ 2 ] = vert->v[2] * frame->header.scale[2] + frame->header.translate[2];
608 PicoSetSurfaceXYZ( picoSurface, i , xyz );
611 normal[ 0 ] = fm_normals[vert->lightnormalindex][0];
612 normal[ 1 ] = fm_normals[vert->lightnormalindex][1];
613 normal[ 2 ] = fm_normals[vert->lightnormalindex][2];
614 PicoSetSurfaceNormal( picoSurface, i , normal );
617 st[ 0 ] = ((texCoord[p_index_LUT[i].ST].s) / ((float)fm_head->skinWidth));
618 st[ 1 ] = (texCoord[p_index_LUT[i].ST].t / ((float)fm_head->skinHeight));
619 PicoSetSurfaceST( picoSurface, 0, i , st );
624 for(i=0; i<dups; i++)
626 j = p_index_LUT_DUPS[i].OldVert;
627 /* set vertex origin */
628 xyz[ 0 ] = frame->verts[j].v[0] * frame->header.scale[0] + frame->header.translate[0];
629 xyz[ 1 ] = frame->verts[j].v[1] * frame->header.scale[1] + frame->header.translate[1];
630 xyz[ 2 ] = frame->verts[j].v[2] * frame->header.scale[2] + frame->header.translate[2];
631 PicoSetSurfaceXYZ( picoSurface, i + fm_head->numXYZ , xyz );
634 normal[ 0 ] = fm_normals[frame->verts[j].lightnormalindex][0];
635 normal[ 1 ] = fm_normals[frame->verts[j].lightnormalindex][1];
636 normal[ 2 ] = fm_normals[frame->verts[j].lightnormalindex][2];
637 PicoSetSurfaceNormal( picoSurface, i + fm_head->numXYZ , normal );
640 st[ 0 ] = ((texCoord[p_index_LUT_DUPS[i].ST].s) / ((float)fm_head->skinWidth));
641 st[ 1 ] = (texCoord[p_index_LUT_DUPS[i].ST].t / ((float)fm_head->skinHeight));
642 PicoSetSurfaceST( picoSurface, 0, i + fm_head->numXYZ , st );
647 PicoSetSurfaceColor( picoSurface, 0, 0, color );
649 // Free up malloc'ed LL entries
650 for(i=0; i<fm_head->numXYZ; i++)
652 if(p_index_LUT[i].next != NULL)
654 p_index_LUT2 = p_index_LUT[i].next;
656 p_index_LUT3 = p_index_LUT2->next;
657 _pico_free(p_index_LUT2);
658 p_index_LUT2 = p_index_LUT3;
660 } while (p_index_LUT2 != NULL);
665 _pico_printf(PICO_WARNING, " Not all LL mallocs freed\n");
667 // Free malloc'ed LUTs
668 _pico_free(p_index_LUT);
669 _pico_free(p_index_LUT_DUPS);
671 /* return the new pico model */
679 /* pico file format module definition */
680 const picoModule_t picoModuleFM =
682 "0.85", /* module version string */
683 "Heretic 2 FM", /* module display name */
684 "Nurail", /* author's name */
685 "2003 Nurail", /* module copyright */
687 "fm", NULL, NULL, NULL /* default extensions to use */
689 _fm_canload, /* validation routine */
690 _fm_load, /* load routine */
691 NULL, /* save validation routine */
692 NULL /* save routine */