Paranoia2/utils/hlsv/sprviewer.cpp

830 lines
20 KiB
C++

//
// MDL Viewer (c) 1999 by Mete Ciragan
//
// file: md2viewer.cpp
// last modified: Apr 29 1999, Mete Ciragan
// copyright: The programs and associated files contained in this
// distribution were developed by Mete Ciragan. The programs
// are not in the public domain, but they are freely
// distributable without licensing fees. These programs are
// provided without guarantee or warrantee expressed or
// implied.
//
// version: 1.4
//
// email: mete@swissquake.ch
// web: http://www.swissquake.ch/chumbalum-soft/
//
#include <stdio.h>
#include <stdlib.h>
//#include <ostream.h>
#include <mx.h>
#include <gl.h>
#include <mxBmp.h>
#include "sprviewer.h"
#include "GlWindow.h"
#include "pakviewer.h"
#include "FileAssociation.h"
#include "stringlib.h"
#include "SpriteModel.h"
SPRViewer *g_SPRViewer = 0;
char g_appTitle[] = "Half_life Sprite Viewer v0.1 beta";
static char recentFiles[8][256] = { "", "", "", "", "", "", "", "" };
extern bool bUseWeaponOrigin;
bool g_bStopPlaying = false;
bool g_bEndOfSequence = false;
static int g_nCurrFrame = 0;
void SPRViewer::initRecentFiles ()
{
for (int i = 0; i < 8; i++)
{
if (strlen (recentFiles[i]))
{
mb->modify (IDC_FILE_RECENTMODELS1 + i, IDC_FILE_RECENTMODELS1 + i, recentFiles[i]);
}
else
{
mb->modify (IDC_FILE_RECENTMODELS1 + i, IDC_FILE_RECENTMODELS1 + i, "(empty)");
mb->setEnabled (IDC_FILE_RECENTMODELS1 + i, false);
}
}
}
void SPRViewer::loadRecentFiles( void )
{
char str[256];
for( int i = 0; i < 8; i++ )
{
mx_snprintf( str, sizeof( str ), "RecentFile%i", i );
if( !LoadString( str, recentFiles[i] ))
break;
}
}
void SPRViewer::saveRecentFiles( void )
{
char str[256];
if( !InitRegistry( ))
return;
for( int i = 0; i < 8; i++ )
{
mx_snprintf( str, sizeof( str ), "RecentFile%i", i );
if( !SaveString( str, recentFiles[i] ))
break;
}
}
SPRViewer :: SPRViewer() : mxWindow( 0, 0, 0, 0, 0, g_appTitle, mxWindow::Normal )
{
// create menu stuff
mb = new mxMenuBar (this);
mxMenu *menuFile = new mxMenu ();
mxMenu *menuOptions = new mxMenu ();
mxMenu *menuView = new mxMenu ();
mxMenu *menuHelp = new mxMenu ();
mb->addMenu ("File", menuFile);
mb->addMenu ("Options", menuOptions);
mb->addMenu ("Tools", menuView);
mb->addMenu ("Help", menuHelp);
mxMenu *menuRecentModels = new mxMenu ();
menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS1);
menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS2);
menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS3);
menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS4);
mxMenu *menuRecentPakFiles = new mxMenu ();
menuRecentPakFiles->add ("(empty)", IDC_FILE_RECENTPAKFILES1);
menuRecentPakFiles->add ("(empty)", IDC_FILE_RECENTPAKFILES2);
menuRecentPakFiles->add ("(empty)", IDC_FILE_RECENTPAKFILES3);
menuRecentPakFiles->add ("(empty)", IDC_FILE_RECENTPAKFILES4);
menuFile->add ("Load Sprite...", IDC_FILE_LOADMODEL);
menuFile->addSeparator ();
menuFile->add ("Load Background Texture...", IDC_FILE_LOADBACKGROUNDTEX);
menuFile->add ("Load Ground Texture...", IDC_FILE_LOADGROUNDTEX);
menuFile->addSeparator ();
menuFile->add ("Unload Ground Texture", IDC_FILE_UNLOADGROUNDTEX);
menuFile->addSeparator ();
menuFile->add ("Open PAK file...", IDC_FILE_OPENPAKFILE);
menuFile->add ("Close PAK file", IDC_FILE_CLOSEPAKFILE);
menuFile->addSeparator ();
menuFile->addMenu ("Recent Sprites", menuRecentModels);
menuFile->addMenu ("Recent PAK files", menuRecentPakFiles);
menuFile->addSeparator ();
menuFile->add ("Exit", IDC_FILE_EXIT);
menuOptions->add ("Background Color...", IDC_OPTIONS_COLORBACKGROUND);
menuOptions->add ("Ground Color...", IDC_OPTIONS_COLORGROUND);
menuOptions->add ("Light Color...", IDC_OPTIONS_COLORLIGHT);
menuOptions->addSeparator ();
menuOptions->add( "Sprite AutoPlay", IDC_OPTIONS_AUTOPLAY );
menuOptions->addSeparator ();
menuOptions->add( "Use weapon origin", IDC_OPTIONS_WEAPONORIGIN );
menuOptions->addSeparator ();
menuOptions->add ("Center View", IDC_OPTIONS_CENTERVIEW);
menuOptions->add ("Reset View", IDC_OPTIONS_RESETVIEW);
#ifdef WIN32
menuOptions->addSeparator ();
menuOptions->add ("Make Screenshot...", IDC_OPTIONS_MAKESCREENSHOT);
//menuOptions->add ("Dump Sprite Info", IDC_OPTIONS_DUMP);
#endif
menuView->add ("File Associations...", IDC_VIEW_FILEASSOCIATIONS);
#ifdef WIN32
menuHelp->add ("Goto Homepage...", IDC_HELP_GOTOHOMEPAGE);
menuHelp->addSeparator ();
#endif
menuHelp->add ("About", IDC_HELP_ABOUT);
mb->setChecked( IDC_OPTIONS_WEAPONORIGIN, bUseWeaponOrigin );
// create tabcontrol with subdialog windows
tab = new mxTab (this, 0, 0, 0, 0, IDC_TAB);
#ifdef WIN32
SetWindowLong ((HWND) tab->getHandle (), GWL_EXSTYLE, WS_EX_CLIENTEDGE);
#endif
mxWindow *wRender = new mxWindow (this, 0, 0, 0, 0);
tab->add (wRender, "Sprite Display");
mxLabel *RenderLabel = new mxLabel (wRender, 5, 3, 120, 20, "Render Mode");
cRenderMode = new mxChoice (wRender, 5, 17, 112, 22, IDC_RENDERMODE);
cRenderMode->add ("Normal");
cRenderMode->add ("Additive");
cRenderMode->add ("Indexalpha");
cRenderMode->add ("Alpha test");
cRenderMode->select (0);
mxToolTip::add (cRenderMode, "Select Render Mode");
mxLabel *OrientLabel = new mxLabel (wRender, 5, 43, 120, 20, "Orientation");
cOrientType = new mxChoice (wRender, 5, 60, 112, 22, IDC_ORIENTATION);
cOrientType->add ("Parallel Upright");
cOrientType->add ("Facing Upright");
cOrientType->add ("Parallel");
cOrientType->add ("Oriented");
cOrientType->add ("Parallel Oriented");
cOrientType->select (0);
mxToolTip::add (cOrientType, "Select Sprite Orientation");
cbGround = new mxCheckBox (wRender, 130, 64, 85, 20, "Show Ground", IDC_GROUND);
cbGround->setChecked( g_viewerSettings.showGround ? true : false );
cbBackground = new mxCheckBox (wRender, 224, 64, 130, 20, "Show Background", IDC_BACKGROUND);
lSpriteInfo1 = new mxLabel (wRender, 130, 5, 120, 15, "" );
// Create widgets for the Animation Tab
tbStop = new mxButton (wRender, 130, 22, 60, 18, "Stop", IDC_STOP);
mxToolTip::add (tbStop, "Stop Playing");
bPrevFrame = new mxButton (wRender, 209, 22, 30, 18, "<<", IDC_PREVFRAME);
bPrevFrame->setEnabled (false);
mxToolTip::add (bPrevFrame, "Prev Frame");
leFrame = new mxLineEdit (wRender, 244, 22, 50, 18, "", IDC_FRAME);
leFrame->setEnabled (false);
mxToolTip::add (leFrame, "Set Frame");
bNextFrame = new mxButton (wRender, 299, 22, 30, 18, ">>", IDC_NEXTFRAME);
bNextFrame->setEnabled (false);
mxToolTip::add (bNextFrame, "Next Frame");
mxLabel *SpdLabel = new mxLabel (wRender, 295, 44, 35, 18, "Speed");
slSpeedScale = new mxSlider (wRender, 125, 44, 165, 18, IDC_SPEEDSCALE);
slSpeedScale->setRange (0, 200);
slSpeedScale->setValue (40);
mxToolTip::add (slSpeedScale, "Speed Scale");
// create the OpenGL window
d_GlWindow = new GlWindow (this, 0, 0, 0, 0, "", mxWindow::Normal);
#ifdef WIN32
SetWindowLong ((HWND) d_GlWindow->getHandle (), GWL_EXSTYLE, WS_EX_CLIENTEDGE);
#endif
g_GlWindow = d_GlWindow;
// finally create the pakviewer window
pakViewer = new PAKViewer (this);
g_FileAssociation = new FileAssociation ();
loadRecentFiles ();
initRecentFiles ();
setBounds (20, 20, 690, 550);
setVisible (true);
if( g_viewerSettings.showGround )
setShowGround (true);
if( g_viewerSettings.groundTexFile[0] )
d_GlWindow->loadTexture( g_viewerSettings.groundTexFile, TEXTURE_GROUND );
else d_GlWindow->loadTexture( NULL, TEXTURE_GROUND );
}
SPRViewer::~SPRViewer ()
{
g_viewerSettings.showMaximized = isMaximized();
saveRecentFiles ();
SaveViewerSettings ();
}
void SPRViewer :: checkboxSet( int id, bool bState )
{
mb->setChecked( id, bState );
}
int
SPRViewer::handleEvent (mxEvent *event)
{
if (event->event == mxEvent::Size)
{
int w = event->width;
int h = event->height;
int y = mb->getHeight ();
#ifdef WIN32
#define HEIGHT 120
#else
#define HEIGHT 140
h -= 40;
#endif
if (pakViewer->isVisible ())
{
w -= 170;
pakViewer->setBounds (w, y, 170, h);
}
d_GlWindow->setBounds (0, y, w, h - HEIGHT);
tab->setBounds (0, y + h - HEIGHT, w, HEIGHT);
return 1;
}
if ( event->event == mxEvent::KeyDown )
{
redraw();
switch( (char)event->key )
{
case 27:
if( !getParent( )) // fullscreen mode ?
mx::quit();
break;
case 37:
if( g_viewerSettings.numSpritePathes > 0 )
loadSprite( LoadPrevSprite( ));
break;
case 39:
if( g_viewerSettings.numSpritePathes > 0 )
loadSprite( LoadNextSprite( ));
break;
case VK_F5:
{
bool oldUseWeaponOrigin = bUseWeaponOrigin;
loadSprite( g_viewerSettings.spriteFile, false );
bUseWeaponOrigin = oldUseWeaponOrigin;
break;
}
case 'v':
case 'м':
bUseWeaponOrigin = !mb->isChecked( IDC_OPTIONS_WEAPONORIGIN );
mb->setChecked( IDC_OPTIONS_WEAPONORIGIN, bUseWeaponOrigin );
break;
}
return 1;
}
switch (event->action)
{
case IDC_FILE_LOADMODEL:
{
const char *ptr = mxGetOpenFileName (this, 0, "*.spr");
if (ptr)
{
if (!loadSprite (ptr ))
{
char str[256];
sprintf (str, "Error reading sprite: %s", ptr);
mxMessageBox (this, str, "ERROR", MX_MB_OK | MX_MB_ERROR);
break;
}
// now update recent files list
int i;
char path[256];
if (event->action == IDC_FILE_LOADMODEL)
strcpy (path, "[m] ");
else
strcpy (path, "[w] ");
strcat (path, ptr);
for (i = 0; i < 4; i++)
{
if (!mx_strcasecmp (recentFiles[i], path))
break;
}
// swap existing recent file
if (i < 4)
{
char tmp[256];
strcpy (tmp, recentFiles[0]);
strcpy (recentFiles[0], recentFiles[i]);
strcpy (recentFiles[i], tmp);
}
// insert recent file
else
{
for (i = 3; i > 0; i--)
strcpy (recentFiles[i], recentFiles[i - 1]);
strcpy (recentFiles[0], path);
}
initRecentFiles ();
}
}
break;
case IDC_FILE_SAVEMODEL:
{
char *ptr = (char *) mxGetSaveFileName (this, g_viewerSettings.spritePath, "*.spr", g_viewerSettings.spritePath);
if (!ptr)
break;
char filename[256];
char ext[16];
strcpy( filename, ptr );
strcpy( ext, mx_getextension( filename ));
if( mx_strcasecmp( ext, ".spr" ))
strcat( filename, ".spr" );
if( !g_spriteModel.SaveSprite( filename ))
{
mxMessageBox( this, "Error saving sprite.", g_appTitle, MX_MB_OK | MX_MB_ERROR );
}
else
{
strcpy( g_viewerSettings.spriteFile, filename );
g_viewerSettings.numModelChanges = 0; // all the settings are handled
}
}
break;
case IDC_FILE_LOADBACKGROUNDTEX:
case IDC_FILE_LOADGROUNDTEX:
{
const char *ptr = mxGetOpenFileName (this, 0, "*.bmp;*.tga;*.pcx");
if (ptr)
{
int name = TEXTURE_UNUSED;
if( event->action == IDC_FILE_LOADBACKGROUNDTEX )
name = TEXTURE_BACKGROUND;
else if( event->action == IDC_FILE_LOADGROUNDTEX )
name = TEXTURE_GROUND;
if (d_GlWindow->loadTexture( ptr, name ))
{
if (event->action == IDC_FILE_LOADBACKGROUNDTEX)
setShowBackground (true);
else
setShowGround (true);
}
else
mxMessageBox (this, "Error loading texture.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
}
}
break;
case IDC_FILE_UNLOADGROUNDTEX:
{
d_GlWindow->loadTexture( NULL, TEXTURE_GROUND );
setShowGround (false);
}
break;
case IDC_FILE_OPENPAKFILE:
{
const char *ptr = mxGetOpenFileName (this, "\\Quake\\id1\\", "*.pak");
if (ptr)
{
int i;
pakViewer->openPAKFile (ptr);
for (i = 4; i < 8; i++)
{
if (!mx_strcasecmp (recentFiles[i], ptr))
break;
}
// swap existing recent file
if (i < 8)
{
char tmp[256];
strcpy (tmp, recentFiles[4]);
strcpy (recentFiles[4], recentFiles[i]);
strcpy (recentFiles[i], tmp);
}
// insert recent file
else
{
for (i = 7; i > 4; i--)
strcpy (recentFiles[i], recentFiles[i - 1]);
strcpy (recentFiles[4], ptr);
}
initRecentFiles ();
redraw ();
}
}
break;
case IDC_FILE_CLOSEPAKFILE:
{
pakViewer->closePAKFile ();
redraw ();
}
break;
case IDC_FILE_RECENTMODELS1:
case IDC_FILE_RECENTMODELS2:
case IDC_FILE_RECENTMODELS3:
case IDC_FILE_RECENTMODELS4:
{
int i = event->action - IDC_FILE_RECENTMODELS1;
char *ptr = &recentFiles[i][4];
if (!loadSprite( ptr ))
{
char str[256];
sprintf (str, "Error reading sprite: %s", ptr);
mxMessageBox (this, str, "ERROR", MX_MB_OK | MX_MB_ERROR);
break;
}
// update recent sprite list
char tmp[256];
strcpy (tmp, recentFiles[0]);
strcpy (recentFiles[0], recentFiles[i]);
strcpy (recentFiles[i], tmp);
initRecentFiles ();
}
break;
case IDC_FILE_RECENTPAKFILES1:
case IDC_FILE_RECENTPAKFILES2:
case IDC_FILE_RECENTPAKFILES3:
case IDC_FILE_RECENTPAKFILES4:
{
int i = event->action - IDC_FILE_RECENTPAKFILES1 + 4;
pakViewer->openPAKFile (recentFiles[i]);
char tmp[256];
strcpy (tmp, recentFiles[4]);
strcpy (recentFiles[4], recentFiles[i]);
strcpy (recentFiles[i], tmp);
initRecentFiles ();
redraw ();
}
break;
case IDC_FILE_EXIT:
pakViewer->closePAKFile ();
redraw ();
mx::quit ();
break;
case IDC_OPTIONS_COLORBACKGROUND:
case IDC_OPTIONS_COLORGROUND:
case IDC_OPTIONS_COLORLIGHT:
{
float *cols[3] = { g_viewerSettings.bgColor, g_viewerSettings.gColor, g_viewerSettings.lColor };
float *col = cols[event->action - IDC_OPTIONS_COLORBACKGROUND];
int r = (int) (col[0] * 255.0f);
int g = (int) (col[1] * 255.0f);
int b = (int) (col[2] * 255.0f);
if (mxChooseColor (this, &r, &g, &b))
{
col[0] = (float) r / 255.0f;
col[1] = (float) g / 255.0f;
col[2] = (float) b / 255.0f;
}
}
break;
#ifdef WIN32
case IDC_OPTIONS_MAKESCREENSHOT:
{
char *ptr = (char *)mxGetSaveFileName (this, 0, "*.bmp");
if (ptr)
{
if( !strstr( ptr, ".bmp" ))
strcat( ptr, ".bmp" );
makeScreenShot (ptr);
}
}
break;
#endif
case IDC_OPTIONS_WEAPONORIGIN:
bUseWeaponOrigin = !mb->isChecked( IDC_OPTIONS_WEAPONORIGIN );
mb->setChecked( IDC_OPTIONS_WEAPONORIGIN, bUseWeaponOrigin );
break;
case IDC_OPTIONS_AUTOPLAY:
g_viewerSettings.sequence_autoplay = !mb->isChecked( IDC_OPTIONS_AUTOPLAY );
mb->setChecked( IDC_OPTIONS_AUTOPLAY, g_viewerSettings.sequence_autoplay ? true : false );
break;
case IDC_OPTIONS_CENTERVIEW:
{
centerSprite ();
d_GlWindow->redraw ();
}
break;
case IDC_VIEW_FILEASSOCIATIONS:
g_FileAssociation->setAssociation (0);
g_FileAssociation->setVisible (true);
break;
#ifdef WIN32
case IDC_HELP_GOTOHOMEPAGE:
ShellExecute (0, "open", "http://cs-mapping.com.ua/forum/forumdisplay.php?f=189", 0, 0, SW_SHOW);
break;
#endif
case IDC_HELP_ABOUT:
mxMessageBox (this,
"Half-Life Sprite Viewer v0.1 beta (c) 2018 by Unkle Mike\n"
"Based on original MD2V code by Mete Ciragan\n\n"
"Left-drag to rotate.\n"
"Right-drag to zoom.\n"
"Shift-left-drag to x-y-pan.\n"
"Ctrl-drag to move lights.\n\n"
"Build:\t" __DATE__ ".\n"
"Email:\tg-cont@rambler.ru\n"
"Web:\thttp://www.hlfx.ru/forum\n\thttp://cs-mapping.com.ua/forum", "Half-Life Sprite Viewer",
MX_MB_OK | MX_MB_INFORMATION );
break;
//
// widget actions
//
//
//
// Sprite Panel
//
case IDC_RENDERMODE:
{
int index = cRenderMode->getSelectedIndex ();
if (index >= 0)
{
setRenderMode (index);
}
}
break;
case IDC_ORIENTATION:
{
int index = cOrientType->getSelectedIndex ();
if (index >= 0)
{
setOrientType (index);
}
}
break;
case IDC_GROUND:
setShowGround (((mxCheckBox *) event->widget)->isChecked ());
break;
case IDC_BACKGROUND:
setShowBackground (((mxCheckBox *) event->widget)->isChecked ());
break;
case IDC_SPEEDSCALE:
{
int v = ((mxSlider *)event->widget)->getValue ();
g_viewerSettings.speedScale = (float) (v * 5) / 200.0f;
}
break;
case IDC_STOP:
{
if( !g_bStopPlaying )
{
tbStop->setLabel ("Play");
g_bStopPlaying = true;
g_nCurrFrame = g_spriteModel.setFrame( -1 );
leFrame->setLabel ("%d", g_nCurrFrame);
bPrevFrame->setEnabled (true);
leFrame->setEnabled (true);
bNextFrame->setEnabled (true);
}
else
{
tbStop->setLabel ("Stop");
g_bStopPlaying = false;
if( g_bEndOfSequence )
g_nCurrFrame = g_spriteModel.setFrame( 0 );
bPrevFrame->setEnabled (false);
leFrame->setEnabled (false);
bNextFrame->setEnabled (false);
g_bEndOfSequence = false;
}
}
break;
case IDC_PREVFRAME:
{
g_nCurrFrame = g_spriteModel.setFrame( g_nCurrFrame - 1 );
leFrame->setLabel ("%d", g_nCurrFrame);
g_bEndOfSequence = false;
}
break;
case IDC_FRAME:
{
g_nCurrFrame = atoi (leFrame->getLabel ());
g_nCurrFrame = g_spriteModel.setFrame( g_nCurrFrame );
g_bEndOfSequence = false;
}
break;
case IDC_NEXTFRAME:
{
g_nCurrFrame = g_spriteModel.setFrame( g_nCurrFrame + 1 );
leFrame->setLabel( "%d", g_nCurrFrame );
g_bEndOfSequence = false;
}
break;
}
return 1;
}
void SPRViewer :: redraw( void )
{
mxEvent event;
event.event = mxEvent::Size;
event.width = w2 ();
event.height = h2 ();
handleEvent (&event);
}
void SPRViewer :: makeScreenShot( const char *filename )
{
#ifdef WIN32
d_GlWindow->redraw ();
int w = d_GlWindow->w2 ();
int h = d_GlWindow->h2 ();
mxImage *image = new mxImage ();
if( image->create( w, h, 24 ))
{
glReadBuffer( GL_FRONT );
glReadPixels( 0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, image->data );
image->flip_vertical();
if( !mxBmpWrite( filename, image ))
mxMessageBox( this, "Error writing screenshot.", g_appTitle, MX_MB_OK|MX_MB_ERROR );
delete image;
}
#endif
}
int SPRViewer::getTableIndex()
{
return tab->getSelectedIndex ();
}
void SPRViewer :: setRenderMode( int mode )
{
if (mode >= 0)
{
cRenderMode->select (mode);
d_GlWindow->setRenderMode (mode);
}
}
void SPRViewer :: setOrientType( int mode )
{
if (mode >= 0)
{
cOrientType->select (mode);
d_GlWindow->setOrientType (mode);
}
}
void SPRViewer :: setShowGround( bool b )
{
g_viewerSettings.showGround = b;
cbGround->setChecked (b);
}
void SPRViewer :: setShowBackground( bool b )
{
g_viewerSettings.showBackground = b;
cbBackground->setChecked (b);
}
void SPRViewer::centerSprite ()
{
g_spriteModel.centerView( false );
}
bool SPRViewer::loadSprite( const char *ptr, bool centering )
{
if( !d_GlWindow->loadSprite( ptr, centering ))
return false;
char path[256];
strcpy (path, mx_getpath (ptr));
mx_setcwd (path);
setSpriteInfo();
centerSprite();
d_GlWindow->redraw ();
return true;
}
void SPRViewer::setSpriteInfo (void)
{
static char str[1024];
msprite_t *phdr = g_spriteModel.getSpriteHeader();
if (phdr)
{
sprintf( str, "Frames: %d\n", phdr->numframes );
setRenderMode( phdr->texFormat );
setOrientType( phdr->type );
}
else
{
strcpy (str, "No Sprite.");
}
lSpriteInfo1->setLabel (str);
d_GlWindow->setFrameInfo( 0, MAX_SPRITE_FRAMES );
}
int main( int argc, char *argv[] )
{
//
// make sure, we start in the right directory
//
mx_setcwd (mx::getApplicationPath ());
char cmdline[1024] = "";
if (argc > 1)
{
strcpy (cmdline, argv[1]);
for (int i = 2; i < argc; i++)
{
strcat (cmdline, " ");
strcat (cmdline, argv[i]);
}
}
LoadViewerSettings();
mx::init (argc, argv);
g_SPRViewer = new SPRViewer ();
g_SPRViewer->setMenuBar (g_SPRViewer->getMenuBar ());
g_SPRViewer->setBounds (20, 20, 640, 540);
g_SPRViewer->setVisible (true);
if( g_viewerSettings.showMaximized )
g_SPRViewer->Maximize();
if (Q_stristr (cmdline, ".spr"))
{
g_SPRViewer->loadSprite (cmdline);
}
int ret = mx::run ();
mx::cleanup ();
return ret;
}