1 //---------------------------------------------------------------------------------------------
2 //---------------------------------------------------------------------------------------------
3 //
4 // MNGView Sample Application for VC6:
5 // Loads all MNG/JNG/PNG Files LibMNG can do
6 // Can save a single Frame to PNG Format.
7 //
8 // This code is public domain.
9 // Created by Nikolaus Brennig, November 14th, 2000.
10 // virtualnik@nol.at
11 // http://cust.nol.at/ppee
12 //
13 // Tab: 4
14 //
15 //---------------------------------------------------------------------------------------------
16 //---------------------------------------------------------------------------------------------
17 #define WIN32_LEAN_AND_MEAN
18 #include "Main.h"
19 #include <libmng.h>
20 
21 
22 //---------------------------------------------------------------------------------------------
23 // VARS:
24 //---------------------------------------------------------------------------------------------
25 typedef struct
26 {
27 	FILE			*file;
28 	LPSTR			filename;
29 	mng_uint32		delay;
30 } mngstuff;
31 
32 int					lineWidth;
33 BYTE				*mngdestbuffer;
34 mngstuff			*mymngstuff;
35 mng_handle			Curmng;
36 
37 
38 
39 ///////////////////////////////////////////////////////////////////////////////////////////////
40 ///////////////////////////////////////////////////////////////////////////////////////////////
41 //---------------------------------------------------------------------------------------------
42 // callbacks for the mng decoder:
43 //---------------------------------------------------------------------------------------------
44 ///////////////////////////////////////////////////////////////////////////////////////////////
45 ///////////////////////////////////////////////////////////////////////////////////////////////
46 
47 //---------------------------------------------------------------------------------------------
48 // memory allocation; data must be zeroed
49 //---------------------------------------------------------------------------------------------
mymngalloc(mng_uint32 size)50 mng_ptr mymngalloc( mng_uint32 size )
51 {
52 	return (mng_ptr)calloc(1, size);
53 }
54 
55 
56 //---------------------------------------------------------------------------------------------
57 // memory deallocation
58 //---------------------------------------------------------------------------------------------
mymngfree(mng_ptr p,mng_uint32 size)59 void mymngfree(mng_ptr p, mng_uint32 size)
60 {
61 	free(p);
62 }
63 
64 
65 //---------------------------------------------------------------------------------------------
66 // Stream open:
67 //---------------------------------------------------------------------------------------------
mymngopenstream(mng_handle mng)68 mng_bool mymngopenstream(mng_handle mng)
69 {
70 	mngstuff	*mymng;
71 
72 	// look up our stream struct
73     mymng = (mngstuff*)mng_get_userdata(mng);
74 
75 	// open the file
76 	mymng->file = fopen( mymng->filename, "rb" );
77 	if( mymng->file == NULL )
78 	{
79 		char temp[100];
80 		sprintf( temp, "Unable to open File: %s", mymng->filename );
81 		Warning( temp );
82 		return MNG_FALSE;
83 	}
84 
85 	return MNG_TRUE;
86 }
87 
88 
89 //---------------------------------------------------------------------------------------------
90 // Stream open for Writing:
91 //---------------------------------------------------------------------------------------------
mymngopenstreamwrite(mng_handle mng)92 mng_bool mymngopenstreamwrite(mng_handle mng)
93 {
94 	mngstuff	*mymng;
95 
96 	// look up our stream struct
97     mymng = (mngstuff*)mng_get_userdata(mng);
98 
99 	// open the file
100 	mymng->file = fopen( mymng->filename, "wb" );
101 	if( mymng->file == NULL )
102 	{
103 		Warning( "unable to open file!" );
104 		return MNG_FALSE;
105 	}
106 
107 	return MNG_TRUE;
108 }
109 
110 
111 //---------------------------------------------------------------------------------------------
112 // Stream close:
113 //---------------------------------------------------------------------------------------------
mymngclosestream(mng_handle mng)114 mng_bool mymngclosestream(mng_handle mng)
115 {
116 	return MNG_TRUE; // We close the file ourself, mng_cleanup doesnt seem to do it...
117 }
118 
119 
120 //---------------------------------------------------------------------------------------------
121 // feed data to the decoder
122 //---------------------------------------------------------------------------------------------
mymngreadstream(mng_handle mng,mng_ptr buffer,mng_uint32 size,mng_uint32 * bytesread)123 mng_bool mymngreadstream( mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32 *bytesread )
124 {
125 	mngstuff *mymng;
126 
127 	// look up our stream struct
128 	mymng = (mngstuff*)mng_get_userdata(mng);
129 
130 	// read the requested amount of data from the file
131 	*bytesread = fread( buffer, sizeof(BYTE), size, mymng->file );
132 
133 	return MNG_TRUE;
134 }
135 
136 
137 //---------------------------------------------------------------------------------------------
138 // the header's been read. set up the display stuff
139 //---------------------------------------------------------------------------------------------
mymngprocessheader(mng_handle mng,mng_uint32 width,mng_uint32 height)140 mng_bool mymngprocessheader( mng_handle mng, mng_uint32 width, mng_uint32 height )
141 {
142 	// Store values:
143 	W = width; H = height;
144 	Bits = 24;
145 	lineWidth = ((((W * Bits) + 31) >> 5) << 2);
146 
147 	// Create decoderbuffer:
148 	mngdestbuffer = new BYTE[lineWidth*H];
149 	if( mngdestbuffer == 0 ) Warning( "Out of Memory!" );
150 
151 	// Create the MemoryBitmap now, we store there the image...
152 	if( DefaultMemImage ) SelectObject( MemDC, DefaultMemImage );
153 	if( MemDC ) DeleteDC( MemDC );
154 	if( MemImage ) DeleteObject( MemImage );
155 	hdc = GetDC( hPicWin );
156     MemDC = CreateCompatibleDC( 0 );
157 	MemImage = CreateCompatibleBitmap( hdc, W, H );
158 	DefaultMemImage = (HBITMAP)SelectObject( MemDC, MemImage );
159 	ReleaseDC( hPicWin, hdc );
160 
161 	// Set output style:
162 	mng_set_canvasstyle( mng, MNG_CANVAS_BGR8 );
163 
164 	return MNG_TRUE;
165 }
166 
167 
168 //---------------------------------------------------------------------------------------------
169 // return a row pointer for the decoder to fill
170 //---------------------------------------------------------------------------------------------
mymnggetcanvasline(mng_handle mng,mng_uint32 line)171 mng_ptr mymnggetcanvasline( mng_handle mng, mng_uint32 line )
172 {
173 	return (mng_ptr)(mngdestbuffer + (lineWidth*(H-1-line)));
174 }
175 
176 
177 //---------------------------------------------------------------------------------------------
178 // timer
179 //---------------------------------------------------------------------------------------------
mymnggetticks(mng_handle mng)180 mng_uint32 mymnggetticks(mng_handle mng)
181 {
182 	return (mng_uint32)GetTickCount();
183 }
184 
185 
186 //---------------------------------------------------------------------------------------------
187 // Refresh:
188 //---------------------------------------------------------------------------------------------
mymngrefresh(mng_handle mng,mng_uint32 x,mng_uint32 y,mng_uint32 w,mng_uint32 h)189 mng_bool mymngrefresh( mng_handle mng, mng_uint32 x, mng_uint32 y, mng_uint32 w, mng_uint32 h )
190 {
191     PBITMAPINFO bmpi = (PBITMAPINFO)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER));
192 	bmpi->bmiHeader.biSize			= sizeof(BITMAPINFOHEADER);
193 	bmpi->bmiHeader.biWidth			= W;
194 	bmpi->bmiHeader.biHeight		= H;
195 	bmpi->bmiHeader.biPlanes		= 1;
196 	bmpi->bmiHeader.biCompression   = BI_RGB;
197 	bmpi->bmiHeader.biBitCount		= Bits;
198 	bmpi->bmiHeader.biSizeImage		= 0;
199 	bmpi->bmiHeader.biClrUsed		= 0;
200 	bmpi->bmiHeader.biClrImportant	= 0;
201 
202 	// Now blt the Image onto our MemDC...
203 	StretchDIBits( MemDC, 0, 0, W, H, 0, 0, W, H, mngdestbuffer, bmpi, 0, SRCCOPY );
204 	LocalFree((PBITMAPINFO)bmpi);
205 
206 	return MNG_TRUE;
207 }
208 
209 
210 //---------------------------------------------------------------------------------------------
211 // interframe delay callback
212 //---------------------------------------------------------------------------------------------
mymngsettimer(mng_handle mng,mng_uint32 msecs)213 mng_bool mymngsettimer(mng_handle mng, mng_uint32 msecs)
214 {
215 	mngstuff	*mymng;
216 
217 	// look up our stream struct
218     mymng = (mngstuff*)mng_get_userdata(mng);
219 
220 	// set the timer for when the decoder wants to be woken
221 	mymng->delay = msecs;
222 
223 	return MNG_TRUE;
224 }
225 
226 
227 //---------------------------------------------------------------------------------------------
228 // Error Callback;
229 //---------------------------------------------------------------------------------------------
mymngerror(mng_handle mng,mng_int32 code,mng_int8 severity,mng_chunkid chunktype,mng_uint32 chunkseq,mng_int32 extra1,mng_int32 extra2,mng_pchar text)230 mng_bool mymngerror(
231 	mng_handle mng, mng_int32 code, mng_int8 severity,
232 	mng_chunkid chunktype, mng_uint32 chunkseq,
233 	mng_int32 extra1, mng_int32 extra2, mng_pchar text
234 	)
235 {
236 	char	chunk[5];
237 
238 	// pull out the chuck type as a string
239 	// FIXME: does this assume unsigned char?
240 	chunk[0] = (char)((chunktype >> 24) & 0xFF);
241 	chunk[1] = (char)((chunktype >> 16) & 0xFF);
242 	chunk[2] = (char)((chunktype >>  8) & 0xFF);
243 	chunk[3] = (char)((chunktype      ) & 0xFF);
244 	chunk[4] = '\0';
245 
246 	// output the error:
247 	char temp[1000];
248 	sprintf( temp, "error playing chunk %s (%d)", chunk, chunkseq );
249 	Warning( temp );
250 
251 	// No need for anymore decoding:
252 	KillTimer( hPicWin, 2 );
253 
254 	// error occured;
255 	return MNG_FALSE;
256 }
257 
258 
259 //---------------------------------------------------------------------------------------------
260 // Load a MNG/JNG/PNG file:
261 //---------------------------------------------------------------------------------------------
LoadMNG(LPSTR Filename,HWND hwnd,HDC hdc)262 VOID LoadMNG( LPSTR Filename, HWND hwnd, HDC hdc )
263 {
264 	// allocate our stream data structure
265 	mymngstuff = (mngstuff*)calloc(1, sizeof(*mymngstuff));
266 	if( mymngstuff == NULL )
267 	{
268 		Warning( "Unable to allocate MNG struct!" );
269 		return;
270 	}
271 
272 	// pass the name of the file we want to play
273 	mymngstuff->filename = Filename;
274 
275 	// set up the mng decoder for our stream
276     Curmng = mng_initialize(mymngstuff, mymngalloc, mymngfree, MNG_NULL);
277     if(Curmng == MNG_NULL)
278 	{
279 		free(mymngstuff);
280 		Warning( "MNG Init Error!" );
281 		return;
282     }
283 
284 	// No need to store chunks:
285 	mng_set_storechunks(Curmng, MNG_FALSE);
286 
287 	// Set the colorprofile, lcms uses this:
288 	mng_set_srgb( Curmng, MNG_TRUE );
289 	char DestDir[2048];
290 	SearchPath( NULL, "MNGVIEW.EXE", NULL, sizeof(DestDir), DestDir, NULL );
291 	lstrcpyn( DestDir, DestDir, lstrlen(DestDir)-lstrlen("MNGVIEW.EXE") );
292 	catpath( DestDir, "sRGB.icm" );
293 	FILE *RGBfile = fopen( DestDir, "rb" );
294 	if( RGBfile == 0 )
295 	{
296 		mng_cleanup(&Curmng);
297 		free(mymngstuff);
298 		Warning( "Need file \"sRGB.icm\" !" );
299 		return;
300     }
301 	fclose(RGBfile);
302 	mng_set_outputprofile(Curmng, DestDir);
303 
304 	// Set white as background color:
305 	WORD Red   = (255 << 8) + 255;
306 	WORD Green = (255 << 8) + 255;
307 	WORD Blue  = (255 << 8) + 255;
308 	mng_set_bgcolor( Curmng, Red, Green, Blue );
309 
310 	// If PNG Background is available, use it:
311 	mng_set_usebkgd( Curmng, MNG_TRUE );
312 
313 	// set the callbacks
314 	mng_setcb_errorproc(Curmng, mymngerror);
315     mng_setcb_openstream(Curmng, mymngopenstream);
316     mng_setcb_closestream(Curmng, mymngclosestream);
317     mng_setcb_readdata(Curmng, mymngreadstream);
318 	mng_setcb_gettickcount(Curmng, mymnggetticks);
319 	mng_setcb_settimer(Curmng, mymngsettimer);
320 	mng_setcb_processheader(Curmng, mymngprocessheader);
321 	mng_setcb_getcanvasline(Curmng, mymnggetcanvasline);
322 	mng_setcb_refresh(Curmng, mymngrefresh);
323 
324 	// Read the stuff:
325 	mng_readdisplay(Curmng);
326 
327 	AnimFile.CurFrame	 = mng_get_layercount( Curmng );
328 	AnimFile.MaxFrame	 = mng_get_framecount( Curmng );
329 	AnimFile.isAnimation = 1;
330 	AnimFile.Delay		 = mymngstuff->delay;
331 
332 	// Start the whole thing:
333 	SetTimer( hPicWin, 2, mymngstuff->delay, 0 );
334 }
335 
336 
337 //---------------------------------------------------------------------------------------------
338 // Called when loading a new file or Appquit:
339 //---------------------------------------------------------------------------------------------
CleanUpMNG()340 void CleanUpMNG()
341 {
342 	KillTimer( hPicWin, 2 );
343     mng_cleanup(&Curmng);
344 	fclose( mymngstuff->file );
345 	free(mymngstuff);
346 	delete [] mngdestbuffer;
347 }
348 
349 
350 //---------------------------------------------------------------------------------------------
351 // Called when timer says next frame/layer/update is needed:
352 //---------------------------------------------------------------------------------------------
UpdateMNG()353 void UpdateMNG()
354 {
355 	mymngstuff->delay = 0;
356 	if( MNG_NEEDTIMERWAIT == mng_display_resume(Curmng) )
357 	{
358 		KillTimer( hPicWin, 2 );
359 		SetTimer( hPicWin, 2, mymngstuff->delay, 0 );
360 	}
361 	else
362 	{
363 		CleanUpMNG();
364 		AnimFile.CurFrame	 = -1;
365 		AnimFile.MaxFrame	 = -1;
366 		AnimFile.isAnimation = -1;
367 		AnimFile.Delay       = -1;
368 	}
369 }
370 
371 
372 
373 ///////////////////////////////////////////////////////////////////////////////////////////////
374 ///////////////////////////////////////////////////////////////////////////////////////////////
375 //---------------------------------------------------------------------------------------------
376 // MNG WRITING STUFF:
377 //---------------------------------------------------------------------------------------------
378 ///////////////////////////////////////////////////////////////////////////////////////////////
379 ///////////////////////////////////////////////////////////////////////////////////////////////
380 int		OffsetX=0,OffsetY=0,OffsetW=0,OffsetH=0;
381 BYTE	*srcbuffer=0, *tmpbuffer;
382 
383 
384 //---------------------------------------------------------------------------------------------
385 // Callback for writing data:
386 //---------------------------------------------------------------------------------------------
mymngwritedata(mng_handle hMNG,mng_ptr pBuf,mng_uint32 iSize,mng_uint32 * iWritten)387 mng_bool mymngwritedata( mng_handle hMNG, mng_ptr pBuf, mng_uint32 iSize, mng_uint32 *iWritten )
388 {
389 	mngstuff *pMydata = (mngstuff*)mng_get_userdata(hMNG);
390 
391 	*iWritten = fwrite( pBuf, sizeof(BYTE), iSize, pMydata->file );
392 
393 	if( *iWritten < iSize )
394 	{
395 		Warning( "write error" );
396 		return MNG_FALSE;
397 	}
398 
399 	return MNG_TRUE;
400 }
401 
402 
403 //---------------------------------------------------------------------------------------------
404 // swap Rs and Bs...
405 //---------------------------------------------------------------------------------------------
RGBFromBGR(BYTE * buf,UINT widthPix,UINT height)406 BOOL RGBFromBGR( BYTE *buf, UINT widthPix, UINT height )
407 {
408 	UINT   col, row;
409 	LPBYTE pRed, pBlu;
410 	BYTE   tmp;
411 
412     if (buf==NULL)return FALSE;
413 
414 	INT TmpRow = 0;
415 	INT WidthBytes = widthPix*3;
416 	if(WidthBytes & 0x003) WidthBytes = (WidthBytes | 3) + 1;
417 	INT OurCol = 0;
418     for( row=0; row<height; row++ )
419 	{
420 		for( col=0; col<widthPix; col++ )
421         {
422 			pRed = buf + TmpRow + OurCol;
423             pBlu = pRed + 2;
424 
425             // swap red and blue
426             tmp = *pBlu;
427             *pBlu = *pRed;
428             *pRed = tmp;
429 
430 			OurCol += 3;
431 		}
432 		TmpRow += WidthBytes;
433 		OurCol = 0;
434 	}
435 
436 	return TRUE;
437 }
438 
439 
440 //---------------------------------------------------------------------------------------------
441 // Creates the srcuffer filled with data for saving:
442 //---------------------------------------------------------------------------------------------
CreateSrcBuffer(int Frame,int FrameCount,HBITMAP hBmp2)443 VOID CreateSrcBuffer( int Frame, int FrameCount, HBITMAP hBmp2 )
444 {
445 	PBITMAPINFO		pbmi;
446 	BITMAP			bmp;
447 
448 	srcbuffer=0;
449 
450 	// Get WidthBytes...
451 	INT WidthBytes = W*3;
452 
453 	INT LineWidth = W*3;
454 	if(LineWidth & 0x003) LineWidth = (LineWidth | 3) + 1;
455 
456 	// Bitmapstruct init...
457 	GetObject( hBmp2, sizeof(BITMAP), (LPSTR)&bmp );
458 	pbmi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER));
459 
460 	// Initialize the fields in the BITMAPINFO structure.
461 	pbmi->bmiHeader.biSize         = sizeof(BITMAPINFOHEADER);
462 	pbmi->bmiHeader.biWidth        = bmp.bmWidth;
463 	pbmi->bmiHeader.biHeight       = -bmp.bmHeight;
464 	pbmi->bmiHeader.biPlanes       = bmp.bmPlanes;
465 	pbmi->bmiHeader.biBitCount     = 24;
466 	pbmi->bmiHeader.biCompression  = BI_RGB;
467 	pbmi->bmiHeader.biSizeImage    = LineWidth * H * FrameCount;
468 	pbmi->bmiHeader.biClrImportant = 0;
469 
470 	// Alloc Memory...
471 	srcbuffer = 0;
472 	srcbuffer = new BYTE[LineWidth*H*FrameCount];
473 	if( srcbuffer == 0 )
474 		Warning( "srcbuffer == 0!" );
475 
476 	// get the buffer and modify the format:
477 	if( 0 == GetDIBits( MemDC, hBmp2, 0, (WORD) (H*FrameCount), srcbuffer, pbmi, 0 ) )
478 		Warning( "no GetDIBits!!!" );
479 	RGBFromBGR( srcbuffer, W, H*FrameCount );
480 	if( srcbuffer == 0 )
481 		Warning( "srcbuffer == 0!" );
482 
483 	// Freee.
484 	LocalFree((PBITMAPINFO)pbmi);
485 }
486 
487 
488 //---------------------------------------------------------------------------------------------
489 // Writes a single PNG datastream
490 //---------------------------------------------------------------------------------------------
WritePNG(mng_handle hMNG,int Frame,int FrameCount)491 VOID WritePNG( mng_handle hMNG, int Frame, int FrameCount )
492 {
493 	BYTE			*dstbuffer;
494 	INT				LineWidth;
495 	INT				WidthBytes;
496 
497 	OffsetX=0; OffsetY=0; OffsetW=W; OffsetH=H;
498 
499 	// Get WidthBytes...
500 	WidthBytes = W*3;
501 	LineWidth = W*3;
502 	if(LineWidth & 0x003) LineWidth = (LineWidth | 3) + 1;
503 
504 	tmpbuffer = new BYTE[(WidthBytes+1)*OffsetH];
505 	if( tmpbuffer == 0 ) Warning( "Out of Memory!" );
506 
507 	// Write DEFI chunk.
508 	mng_putchunk_defi( hMNG, 0, 0, 0, MNG_TRUE, OffsetX, OffsetY, MNG_FALSE, 0, 0, 0, 0 );
509 
510 	// Write Header:
511 	mng_putchunk_ihdr(
512 		hMNG,
513 		OffsetW, OffsetH,
514 		MNG_BITDEPTH_8/*iBitdepth*/,
515 		MNG_COLORTYPE_RGB/*iColortype*/,
516 		MNG_COMPRESSION_DEFLATE/*iCompression*/,
517 		MNG_FILTER_ADAPTIVE/*iFilter*/,
518 		MNG_INTERLACE_NONE /*iInterlace*/
519 	);
520 
521 	// transfer data, add Filterbyte:
522 	for( int Row=0; Row<OffsetH; Row++ )
523 	{
524 		// First Byte in each Scanline is Filterbyte: Currently 0 -> No Filter.
525 		tmpbuffer[Row*(WidthBytes+1)]=0;
526 
527 		// Copy the scanline:
528 		memcpy(
529 			tmpbuffer+Row*(WidthBytes+1)+1,
530 			srcbuffer+((OffsetY+Row)*(LineWidth))+OffsetX,
531 			WidthBytes
532 		);
533 	}
534 
535 	// Free srcbuffer if not animated GIF:
536 	delete [] srcbuffer;
537 
538 	// Compress data with ZLib (Deflate):
539 	dstbuffer = new BYTE[(WidthBytes+1)*OffsetH];
540 	if( dstbuffer == 0 ) Warning( "Out of Memory!" );
541 	DWORD dstbufferSize=(WidthBytes+1)*OffsetH;
542 
543 	// Compress data:
544 	if( Z_OK != compress2(
545 		(Bytef *)dstbuffer, (ULONG *)&dstbufferSize,
546 		(const Bytef*)tmpbuffer, (ULONG) (WidthBytes+1)*OffsetH,
547 		9
548 	)) Warning( "Unable to compress imagedata!" );
549 
550 	// Write Data into MNG File:
551 	mng_putchunk_idat( hMNG, dstbufferSize, (mng_ptr*)dstbuffer);
552 	mng_putchunk_iend(hMNG);
553 
554 	// Free the stuff:
555 	delete [] tmpbuffer;
556 	delete [] dstbuffer;
557 }
558 
559 
560 //---------------------------------------------------------------------------------------------
561 // Writes a MNG (24bit)
562 //---------------------------------------------------------------------------------------------
SaveMNG(LPSTR Filename,HDC hdc,HBITMAP hBmp)563 VOID SaveMNG( LPSTR Filename, HDC hdc, HBITMAP hBmp )
564 {
565 	mng_handle	hMNG;
566 
567 	// check if currently a MNG file is loaded:
568 	if( AnimFile.isAnimation == 1 )
569 		CleanUpMNG();
570 
571 	// Creates the srcbuffer for imagedata:
572 	CreateSrcBuffer( 0, 1, hBmp );
573 
574 	// allocate our stream data structure
575 	mymngstuff = (mngstuff*)calloc(1, sizeof(*mymngstuff));
576 	if( mymngstuff == NULL )
577 	{
578 		Warning( "Cannot allocate data buffer." );
579 		return;
580 	}
581 
582 	// pass the name of the file we want to play
583 	mymngstuff->filename = Filename;
584 
585 	// init the lib:
586 	hMNG = mng_initialize((mng_ptr)mymngstuff, mymngalloc, mymngfree, MNG_NULL);
587 	if( !hMNG )
588 	{
589 	    Warning( "Cannot initialize libmng." );
590 		return;
591 	}
592 	else
593 	{
594 		mng_setcb_openstream(hMNG, mymngopenstreamwrite );
595 		mng_setcb_closestream(hMNG, mymngclosestream);
596 		mng_setcb_writedata(hMNG, mymngwritedata);
597 
598 		// Write File:
599    		mng_create(hMNG);
600 
601 		// Just a single Frame (save a normal PNG):
602 		WritePNG( hMNG, 0, 1 );
603 
604 		// Now write file:
605 		mng_write(hMNG);
606 
607 		// Free the stuff:
608 		fclose( mymngstuff->file );
609 		mng_cleanup(&hMNG);
610 	}
611 
612 	free( mymngstuff );
613 }
614 
615 
616