You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2716 lines
76 KiB
2716 lines
76 KiB
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
|
|
#undef fopen
|
|
|
|
#if !defined( _GAMECONSOLE ) && !defined( _OSX ) && !defined (LINUX)
|
|
#include <windows.h> // SRC only!!
|
|
#endif
|
|
|
|
#if defined( POSIX ) && !defined( _PS3 )
|
|
#ifdef OSX
|
|
#include <copyfile.h>
|
|
#endif
|
|
#define DeleteFile unlink
|
|
#endif
|
|
|
|
#include "optionssubmultiplayer.h"
|
|
#include "multiplayeradvanceddialog.h"
|
|
#include <stdio.h>
|
|
|
|
#include <vgui_controls/Button.h>
|
|
#include <vgui_controls/CheckButton.h>
|
|
#include "tier1/keyvalues.h"
|
|
#include <vgui_controls/Label.h>
|
|
#include <vgui/ISystem.h>
|
|
#include <vgui/ISurface.h>
|
|
#include <vgui/Cursor.h>
|
|
#include <vgui_controls/RadioButton.h>
|
|
#include <vgui_controls/ComboBox.h>
|
|
#include <vgui_controls/ImagePanel.h>
|
|
#include <vgui_controls/FileOpenDialog.h>
|
|
#include <vgui_controls/MessageBox.h>
|
|
#include <vgui/IVGui.h>
|
|
#include <vgui/ILocalize.h>
|
|
#include <vgui/IPanel.h>
|
|
#include <vgui_controls/MessageBox.h>
|
|
|
|
#include "cvartextentry.h"
|
|
#include "cvartogglecheckbutton.h"
|
|
#include "cvarslider.h"
|
|
#include "labeledcommandcombobox.h"
|
|
#include "filesystem.h"
|
|
#include "engineinterface.h"
|
|
#include "bitmapimagepanel.h"
|
|
#include "utlbuffer.h"
|
|
#include "modinfo.h"
|
|
#include "tier1/convar.h"
|
|
|
|
|
|
#include "materialsystem/imaterial.h"
|
|
#include "materialsystem/imesh.h"
|
|
#include "materialsystem/imaterialvar.h"
|
|
|
|
// use the JPEGLIB_USE_STDIO define so that we can read in jpeg's from outside the game directory tree. For Spray Import.
|
|
#define JPEGLIB_USE_STDIO
|
|
#include "jpeglib/jpeglib.h"
|
|
#undef JPEGLIB_USE_STDIO
|
|
|
|
#include <setjmp.h>
|
|
|
|
#include "bitmap/tgawriter.h"
|
|
#include "ivtex.h"
|
|
// dgoodenough - io.h doesn't exist on the PS3
|
|
// PS3_BUILDFIX
|
|
#if !defined( _PS3 ) && !defined( _OSX ) && !defined (LINUX)
|
|
#include <io.h>
|
|
#endif
|
|
|
|
// dgoodenough - select the correct stubs header based on current console
|
|
// PS3_BUILDFIX
|
|
#if defined( _PS3 )
|
|
#include "ps3/ps3_win32stubs.h"
|
|
#endif
|
|
#if defined( _X360 )
|
|
#include "xbox/xbox_win32stubs.h"
|
|
#endif
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include <tier0/memdbgon.h>
|
|
|
|
using namespace vgui;
|
|
|
|
|
|
#define DEFAULT_SUIT_HUE 30
|
|
#define DEFAULT_PLATE_HUE 6
|
|
|
|
void UpdateLogoWAD( void *hdib, int r, int g, int b );
|
|
|
|
struct ColorItem_t
|
|
{
|
|
char *name;
|
|
int r, g, b;
|
|
};
|
|
|
|
static ColorItem_t itemlist[]=
|
|
{
|
|
{ "#Valve_Orange", 255, 120, 24 },
|
|
{ "#Valve_Yellow", 225, 180, 24 },
|
|
{ "#Valve_Blue", 0, 60, 255 },
|
|
{ "#Valve_Ltblue", 0, 167, 255 },
|
|
{ "#Valve_Green", 0, 167, 0 },
|
|
{ "#Valve_Red", 255, 43, 0 },
|
|
{ "#Valve_Brown", 123, 73, 0 },
|
|
{ "#Valve_Ltgray", 100, 100, 100 },
|
|
{ "#Valve_Dkgray", 36, 36, 36 },
|
|
};
|
|
|
|
static ColorItem_t s_crosshairColors[] =
|
|
{
|
|
{ "#Valve_Green", 50, 250, 50 },
|
|
{ "#Valve_Red", 250, 50, 50 },
|
|
{ "#Valve_Blue", 50, 50, 250 },
|
|
{ "#Valve_Yellow", 250, 250, 50 },
|
|
{ "#Valve_Ltblue", 50, 250, 250 }
|
|
};
|
|
static const int NumCrosshairColors = sizeof( s_crosshairColors ) / sizeof( s_crosshairColors[0] );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
class CrosshairImagePanel : public ImagePanel
|
|
{
|
|
typedef ImagePanel BaseClass;
|
|
public:
|
|
CrosshairImagePanel( Panel *parent, const char *name, CCvarToggleCheckButton *pAdditive );
|
|
|
|
virtual void Paint();
|
|
|
|
void UpdateCrosshair( int r, int g, int b, int size );
|
|
|
|
protected:
|
|
int m_R, m_G, m_B;
|
|
int m_barSize;
|
|
int m_barGap;
|
|
CCvarToggleCheckButton *m_pAdditive;
|
|
int m_iCrosshairTextureID;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
CrosshairImagePanel::CrosshairImagePanel( Panel *parent, const char *name, CCvarToggleCheckButton *pAdditive ) : ImagePanel( parent, name )
|
|
{
|
|
m_pAdditive = pAdditive;
|
|
UpdateCrosshair( 50, 250, 50, 0 );
|
|
|
|
m_iCrosshairTextureID = vgui::surface()->CreateNewTextureID();
|
|
vgui::surface()->DrawSetTextureFile( m_iCrosshairTextureID, "vgui/white_additive" , true, false);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void CrosshairImagePanel::UpdateCrosshair( int r, int g, int b, int size )
|
|
{
|
|
m_R = r;
|
|
m_G = g;
|
|
m_B = b;
|
|
|
|
int screenWide, screenTall;
|
|
surface()->GetScreenSize( screenWide, screenTall );
|
|
if ( size == 0 )
|
|
{
|
|
if (screenWide <= 640)
|
|
{
|
|
// if the screen width is 640 or less, set the crosshair num to 3 (large)
|
|
size = 1;
|
|
}
|
|
else if (screenWide < 1024)
|
|
{
|
|
// if the screen width is between 640 and 1024, set the crosshair num to 2 (medium)
|
|
size = 2;
|
|
}
|
|
else
|
|
{
|
|
// if the screen width is 1024 or greater, set the crosshair num to 1 (small)
|
|
size = 3;
|
|
}
|
|
}
|
|
|
|
int scaleBase = 1024;
|
|
switch( size )
|
|
{
|
|
case 3:
|
|
scaleBase = 640;
|
|
break;
|
|
case 2:
|
|
scaleBase = 800;
|
|
break;
|
|
default:
|
|
scaleBase = 1024;
|
|
break;
|
|
}
|
|
|
|
m_barSize = (int) 9 * screenWide / scaleBase;
|
|
m_barGap = (int) 5 * screenWide / scaleBase;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
static void DrawCrosshairRect( int x, int y, int w, int h, bool additive )
|
|
{
|
|
if ( additive )
|
|
{
|
|
vgui::surface()->DrawTexturedRect( x, y, x+w, y+h );
|
|
}
|
|
else
|
|
{
|
|
vgui::surface()->DrawFilledRect( x, y, x+w, y+h );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void CrosshairImagePanel::Paint()
|
|
{
|
|
BaseClass::Paint();
|
|
|
|
if ( !m_pAdditive )
|
|
return;
|
|
|
|
int wide, tall;
|
|
GetSize( wide, tall );
|
|
|
|
bool additive = m_pAdditive->IsSelected();
|
|
|
|
int a = 200;
|
|
if ( !additive )
|
|
{
|
|
ConVarRef cl_crosshairalpha( "cl_crosshairalpha" );
|
|
if ( cl_crosshairalpha.IsValid() )
|
|
{
|
|
a = clamp( cl_crosshairalpha.GetInt(), 0, 255 );
|
|
}
|
|
}
|
|
vgui::surface()->DrawSetColor( m_R, m_G, m_B, a );
|
|
|
|
if ( additive )
|
|
{
|
|
vgui::surface()->DrawSetTexture( m_iCrosshairTextureID );
|
|
}
|
|
|
|
DrawCrosshairRect( (wide / 2 - m_barGap - m_barSize) + 1, tall / 2, m_barSize, 1, additive );
|
|
DrawCrosshairRect( wide / 2 + m_barGap, tall / 2, m_barSize, 1, additive );
|
|
DrawCrosshairRect( wide / 2, tall / 2 - m_barGap - m_barSize, 1, m_barSize, additive );
|
|
DrawCrosshairRect( wide / 2, tall / 2 + m_barGap, 1, m_barSize, additive );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
class AdvancedCrosshairImagePanel : public ImagePanel
|
|
{
|
|
typedef ImagePanel BaseClass;
|
|
public:
|
|
AdvancedCrosshairImagePanel( Panel *parent, const char *name );
|
|
virtual ~AdvancedCrosshairImagePanel();
|
|
|
|
virtual void Paint();
|
|
|
|
void UpdateCrosshair( int r, int g, int b, float scale, const char *crosshairname );
|
|
|
|
protected:
|
|
int m_R, m_G, m_B;
|
|
float m_flScale;
|
|
|
|
// material
|
|
int m_iCrosshairTextureID;
|
|
IVguiMatInfo *m_pAdvCrosshair;
|
|
|
|
// animation
|
|
IVguiMatInfoVar *m_pFrameVar;
|
|
float m_flNextFrameChange;
|
|
int m_nNumFrames;
|
|
bool m_bAscending; // animating forward or in reverse?
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
AdvancedCrosshairImagePanel::AdvancedCrosshairImagePanel( Panel *parent, const char *name ) : ImagePanel( parent, name )
|
|
{
|
|
m_pAdvCrosshair = NULL;
|
|
m_pFrameVar = NULL;
|
|
|
|
if ( ModInfo().AdvCrosshair() )
|
|
{
|
|
m_iCrosshairTextureID = vgui::surface()->CreateNewTextureID();
|
|
UpdateCrosshair( 50, 250, 50, 32.0, "vgui/crosshairs/crosshair1" );
|
|
}
|
|
}
|
|
|
|
AdvancedCrosshairImagePanel::~AdvancedCrosshairImagePanel()
|
|
{
|
|
if ( m_pFrameVar )
|
|
{
|
|
delete m_pFrameVar;
|
|
m_pFrameVar = NULL;
|
|
}
|
|
|
|
if ( m_pAdvCrosshair )
|
|
{
|
|
delete m_pAdvCrosshair;
|
|
m_pAdvCrosshair = NULL;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void AdvancedCrosshairImagePanel::UpdateCrosshair( int r, int g, int b, float scale, const char *crosshairname )
|
|
{
|
|
m_R = r;
|
|
m_G = g;
|
|
m_B = b;
|
|
|
|
m_flScale = scale;
|
|
|
|
vgui::surface()->DrawSetTextureFile( m_iCrosshairTextureID, crosshairname, true, false );
|
|
|
|
if ( m_pAdvCrosshair )
|
|
{
|
|
delete m_pAdvCrosshair;
|
|
}
|
|
|
|
m_pAdvCrosshair = vgui::surface()->DrawGetTextureMatInfoFactory( m_iCrosshairTextureID );
|
|
|
|
Assert(m_pAdvCrosshair);
|
|
|
|
m_pFrameVar = m_pAdvCrosshair->FindVarFactory( "$frame", NULL );
|
|
m_nNumFrames = m_pAdvCrosshair->GetNumAnimationFrames();
|
|
|
|
m_flNextFrameChange = system()->GetFrameTime() + 0.2;
|
|
m_bAscending = true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void AdvancedCrosshairImagePanel::Paint()
|
|
{
|
|
BaseClass::Paint();
|
|
|
|
int wide, tall;
|
|
GetSize( wide, tall );
|
|
|
|
int iClipX0, iClipY0, iClipX1, iClipY1;
|
|
ipanel()->GetClipRect(GetVPanel(), iClipX0, iClipY0, iClipX1, iClipY1 );
|
|
|
|
// scroll through all frames
|
|
if ( m_pFrameVar )
|
|
{
|
|
float curtime = system()->GetFrameTime();
|
|
|
|
if ( curtime >= m_flNextFrameChange )
|
|
{
|
|
m_flNextFrameChange = curtime + 0.2;
|
|
|
|
int frame = m_pFrameVar->GetIntValue();
|
|
|
|
if ( m_bAscending )
|
|
{
|
|
frame++;
|
|
if ( frame >= m_nNumFrames )
|
|
{
|
|
m_bAscending = !m_bAscending;
|
|
frame--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
frame--;
|
|
if ( frame < 0 )
|
|
{
|
|
m_bAscending = !m_bAscending;
|
|
frame++;
|
|
}
|
|
}
|
|
|
|
m_pFrameVar->SetIntValue(frame);
|
|
}
|
|
}
|
|
|
|
float x, y;
|
|
|
|
// assume square
|
|
float flDrawWidth = ( m_flScale/48.0 ) * (float)wide;
|
|
int flHalfWidth = (int)( flDrawWidth / 2 );
|
|
|
|
x = wide/2 - flHalfWidth;
|
|
y = tall/2 - flHalfWidth;
|
|
|
|
vgui::surface()->DrawSetColor( m_R, m_G, m_B, 255 );
|
|
vgui::surface()->DrawSetTexture( m_iCrosshairTextureID );
|
|
vgui::surface()->DrawTexturedRect( x, y, x+flDrawWidth, y+flDrawWidth );
|
|
vgui::surface()->DrawSetTexture(0);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Basic help dialog
|
|
//-----------------------------------------------------------------------------
|
|
COptionsSubMultiplayer::COptionsSubMultiplayer(vgui::Panel *parent) : vgui::PropertyPage(parent, "OptionsSubMultiplayer")
|
|
{
|
|
Button *cancel = new Button( this, "Cancel", "#GameUI_Cancel" );
|
|
cancel->SetCommand( "Close" );
|
|
|
|
Button *ok = new Button( this, "OK", "#GameUI_OK" );
|
|
ok->SetCommand( "Ok" );
|
|
|
|
Button *apply = new Button( this, "Apply", "#GameUI_Apply" );
|
|
apply->SetCommand( "Apply" );
|
|
|
|
Button *advanced = new Button( this, "Advanced", "#GameUI_AdvancedEllipsis" );
|
|
advanced->SetCommand( "Advanced" );
|
|
|
|
Button *importSprayImage = new Button( this, "ImportSprayImage", "#GameUI_ImportSprayEllipsis" );
|
|
importSprayImage->SetCommand("ImportSprayImage");
|
|
|
|
m_hImportSprayDialog = NULL;
|
|
|
|
m_pPrimaryColorSlider = new CCvarSlider( this, "Primary Color Slider", "#GameUI_PrimaryColor",
|
|
0.0f, 255.0f, "topcolor" );
|
|
|
|
m_pSecondaryColorSlider = new CCvarSlider( this, "Secondary Color Slider", "#GameUI_SecondaryColor",
|
|
0.0f, 255.0f, "bottomcolor" );
|
|
|
|
m_pHighQualityModelCheckBox = new CCvarToggleCheckButton( this, "High Quality Models", "#GameUI_HighModels", "cl_himodels" );
|
|
|
|
m_pModelList = new CLabeledCommandComboBox( this, "Player model" );
|
|
m_ModelName[0] = 0;
|
|
InitModelList( m_pModelList );
|
|
|
|
m_pLogoList = new CLabeledCommandComboBox( this, "SpraypaintList" );
|
|
m_LogoName[0] = 0;
|
|
InitLogoList( m_pLogoList );
|
|
|
|
m_pModelImage = new CBitmapImagePanel( this, "ModelImage", NULL );
|
|
m_pModelImage->AddActionSignalTarget( this );
|
|
|
|
m_pLogoImage = new ImagePanel( this, "LogoImage" );
|
|
m_pLogoImage->AddActionSignalTarget( this );
|
|
|
|
m_nTopColor = DEFAULT_SUIT_HUE;
|
|
m_nBottomColor = DEFAULT_PLATE_HUE;
|
|
|
|
m_nLogoR = 255;
|
|
m_nLogoG = 255;
|
|
m_nLogoB = 255;
|
|
|
|
// crosshair controls
|
|
m_pCrosshairColorComboBox = new ComboBox(this, "CrosshairColorComboBox", 6, false);
|
|
m_pCrosshairSize = new CLabeledCommandComboBox(this, "CrosshairSizeComboBox");
|
|
m_pCrosshairTranslucencyCheckbox = new CCvarToggleCheckButton(this, "CrosshairTranslucencyCheckbox", "#GameUI_Translucent", "cl_crosshairusealpha");
|
|
m_pCrosshairImage = new CrosshairImagePanel( this, "CrosshairImage", m_pCrosshairTranslucencyCheckbox );
|
|
|
|
// advanced crosshair controls
|
|
//==========
|
|
m_pAdvCrosshairRedSlider = new CCvarSlider( this, "Red Color Slider", "#GameUI_CrosshairColor_Red",
|
|
0.0f, 255.0f, "cl_crosshair_red" );
|
|
m_pAdvCrosshairGreenSlider = new CCvarSlider( this, "Green Color Slider", "#GameUI_CrosshairColor_Green",
|
|
0.0f, 255.0f, "cl_crosshair_green" );
|
|
m_pAdvCrosshairBlueSlider = new CCvarSlider( this, "Blue Color Slider", "#GameUI_CrosshairColor_Blue",
|
|
0.0f, 255.0f, "cl_crosshair_blue" );
|
|
|
|
m_pAdvCrosshairScaleSlider = new CCvarSlider( this, "Scale Slider", "#GameUI_CrosshairScale",
|
|
16.0f, 48.0f, "cl_crosshair_scale" );
|
|
|
|
m_pAdvCrosshairRedSlider->AddActionSignalTarget( this );
|
|
m_pAdvCrosshairGreenSlider->AddActionSignalTarget( this );
|
|
m_pAdvCrosshairBlueSlider->AddActionSignalTarget( this );
|
|
m_pAdvCrosshairScaleSlider->AddActionSignalTarget( this );
|
|
|
|
m_pAdvCrosshairStyle = new CLabeledCommandComboBox( this, "AdvCrosshairList" );
|
|
m_pAdvCrosshairImage = new AdvancedCrosshairImagePanel( this, "AdvCrosshairImage" );
|
|
|
|
InitAdvCrosshairStyleList(m_pAdvCrosshairStyle);
|
|
RedrawAdvCrosshairImage();
|
|
//=========
|
|
|
|
m_pDownloadFilterCombo = new ComboBox( this, "DownloadFilterCheck", 3, false );
|
|
m_pDownloadFilterCombo->AddItem( "#GameUI_DownloadFilter_ALL", NULL );
|
|
m_pDownloadFilterCombo->AddItem( "#GameUI_DownloadFilter_NoSounds", NULL );
|
|
m_pDownloadFilterCombo->AddItem( "#GameUI_DownloadFilter_None", NULL );
|
|
|
|
//=========
|
|
|
|
LoadControlSettings("Resource/OptionsSubMultiplayer.res");
|
|
|
|
InitCrosshairColorEntries();
|
|
InitCrosshairSizeList(m_pCrosshairSize);
|
|
RedrawCrosshairImage();
|
|
|
|
// turn off the crosshair stuff if the mod specifies "nocrosshair" in the gameinfo.txt file
|
|
if ( ModInfo().NoCrosshair() )
|
|
{
|
|
m_pCrosshairColorComboBox->SetVisible( false );
|
|
m_pCrosshairSize->SetVisible( false );
|
|
m_pCrosshairTranslucencyCheckbox->SetVisible( false );
|
|
m_pCrosshairImage->SetVisible( false );
|
|
|
|
Panel *pTempPanel = NULL;
|
|
|
|
// #GameUI_CrosshairDescription (from "Resource/OptionsSubMultiplayer.res")
|
|
pTempPanel = FindChildByName( "CrosshairLabel" );
|
|
|
|
if ( pTempPanel )
|
|
{
|
|
pTempPanel->SetVisible( false );
|
|
}
|
|
}
|
|
|
|
// turn off model selection stuff if the mod specifies "nomodels" in the gameinfo.txt file
|
|
if ( ModInfo().NoModels() )
|
|
{
|
|
Panel *pTempPanel = NULL;
|
|
|
|
if ( m_pModelImage )
|
|
{
|
|
m_pModelImage->SetVisible( false );
|
|
}
|
|
|
|
if ( m_pModelList )
|
|
{
|
|
m_pModelList->SetVisible( false );
|
|
}
|
|
|
|
if ( m_pPrimaryColorSlider )
|
|
{
|
|
m_pPrimaryColorSlider->SetVisible( false );
|
|
}
|
|
|
|
if ( m_pSecondaryColorSlider )
|
|
{
|
|
m_pSecondaryColorSlider->SetVisible( false );
|
|
}
|
|
|
|
// #GameUI_PlayerModel (from "Resource/OptionsSubMultiplayer.res")
|
|
pTempPanel = FindChildByName( "Label1" );
|
|
|
|
if ( pTempPanel )
|
|
{
|
|
pTempPanel->SetVisible( false );
|
|
}
|
|
|
|
// #GameUI_ColorSliders (from "Resource/OptionsSubMultiplayer.res")
|
|
pTempPanel = FindChildByName( "Colors" );
|
|
|
|
if ( pTempPanel )
|
|
{
|
|
pTempPanel->SetVisible( false );
|
|
}
|
|
}
|
|
|
|
// turn off the himodel stuff if the mod specifies "nohimodel" in the gameinfo.txt file
|
|
if ( ModInfo().NoHiModel() )
|
|
{
|
|
if ( m_pHighQualityModelCheckBox )
|
|
{
|
|
m_pHighQualityModelCheckBox->SetVisible( false );
|
|
}
|
|
}
|
|
|
|
// Advanced crosshair selection
|
|
if ( !ModInfo().AdvCrosshair() )
|
|
{
|
|
m_pAdvCrosshairImage->SetVisible( false );
|
|
|
|
m_pAdvCrosshairRedSlider->SetVisible( false );
|
|
m_pAdvCrosshairBlueSlider->SetVisible( false );
|
|
m_pAdvCrosshairGreenSlider->SetVisible( false );
|
|
m_pAdvCrosshairScaleSlider->SetVisible( false );
|
|
m_pAdvCrosshairStyle->SetVisible( false );
|
|
|
|
Panel *pTempPanel = NULL;
|
|
|
|
// #GameUI_AdvCrosshairDescription (from "Resource/OptionsSubMultiplayer.res")
|
|
pTempPanel = FindChildByName( "AdvCrosshairLabel" );
|
|
if ( pTempPanel )
|
|
{
|
|
pTempPanel->SetVisible( false );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
COptionsSubMultiplayer::~COptionsSubMultiplayer()
|
|
{
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void COptionsSubMultiplayer::OnCommand( const char *command )
|
|
{
|
|
if ( !stricmp( command, "Advanced" ) )
|
|
{
|
|
if (!m_hMultiplayerAdvancedDialog.Get())
|
|
{
|
|
m_hMultiplayerAdvancedDialog = new CMultiplayerAdvancedDialog( this );
|
|
}
|
|
m_hMultiplayerAdvancedDialog->Activate();
|
|
}
|
|
else if (!stricmp( command, "ImportSprayImage" ) )
|
|
{
|
|
if (m_hImportSprayDialog == NULL)
|
|
{
|
|
m_hImportSprayDialog = new FileOpenDialog(NULL, "#GameUI_ImportSprayImage", true);
|
|
m_hImportSprayDialog->AddFilter("*.tga,*.jpg,*.bmp,*.vtf", "#GameUI_All_Images", true);
|
|
m_hImportSprayDialog->AddFilter("*.tga", "#GameUI_TGA_Images", false);
|
|
m_hImportSprayDialog->AddFilter("*.jpg", "#GameUI_JPEG_Images", false);
|
|
m_hImportSprayDialog->AddFilter("*.bmp", "#GameUI_BMP_Images", false);
|
|
m_hImportSprayDialog->AddFilter("*.vtf", "#GameUI_VTF_Images", false);
|
|
m_hImportSprayDialog->AddActionSignalTarget(this);
|
|
}
|
|
m_hImportSprayDialog->DoModal(false);
|
|
m_hImportSprayDialog->Activate();
|
|
}
|
|
|
|
BaseClass::OnCommand( command );
|
|
}
|
|
|
|
// file selected. This can only happen when someone selects an image to be imported as a spray logo.
|
|
void COptionsSubMultiplayer::OnFileSelected(const char *fullpath)
|
|
{
|
|
if ((fullpath == NULL) || (fullpath[0] == 0))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ConversionErrorType errcode;
|
|
|
|
// this can take a while, put up a waiting cursor
|
|
surface()->SetCursor(dc_hourglass);
|
|
|
|
// get the extension of the file we're to convert
|
|
char extension[MAX_PATH];
|
|
const char *constchar = fullpath + strlen(fullpath);
|
|
while ((constchar > fullpath) && (*(constchar-1) != '.'))
|
|
{
|
|
--constchar;
|
|
}
|
|
Q_strncpy(extension, constchar, MAX_PATH);
|
|
|
|
bool deleteIntermediateTGA = false;
|
|
bool deleteIntermediateVTF = false;
|
|
bool convertTGAToVTF = true;
|
|
char tgaPath[MAX_PATH*2];
|
|
char *c;
|
|
bool failed = false;
|
|
|
|
Q_strncpy(tgaPath, fullpath, sizeof(tgaPath));
|
|
|
|
if (stricmp(extension, "tga"))
|
|
{
|
|
// construct a .tga version of this file path.
|
|
|
|
c = tgaPath + strlen(tgaPath);
|
|
while ((c > tgaPath) && (*(c-1) != '\\') && (*(c-1) != '/'))
|
|
{
|
|
--c;
|
|
}
|
|
*c = 0;
|
|
|
|
char origpath[MAX_PATH*2];
|
|
Q_strncpy(origpath, tgaPath, sizeof(origpath));
|
|
|
|
int index = 0;
|
|
// dgoodenough - remove this section on PS3 since we don't have a filesystem _access(...) function yet
|
|
// PS3_BUILDFIX
|
|
// FIXME FIXME FIXME
|
|
#if !defined( _PS3 )
|
|
do {
|
|
Q_snprintf(tgaPath, sizeof(tgaPath), "%stemp%d.tga", origpath, index);
|
|
++index;
|
|
} while (_access(tgaPath, 0) != -1);
|
|
#endif
|
|
|
|
if (!stricmp(extension, "jpg") || !stricmp(extension, "jpeg"))
|
|
{
|
|
// convert from the jpeg file format to the TGA file format
|
|
errcode = ConvertJPEGToTGA(fullpath, tgaPath);
|
|
if (errcode == CE_SUCCESS)
|
|
{
|
|
deleteIntermediateTGA = true;
|
|
}
|
|
else
|
|
{
|
|
failed = true;
|
|
vgui::MessageBox *errorDialog = NULL;
|
|
|
|
if (errcode == CE_MEMORY_ERROR)
|
|
{
|
|
errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Error_Memory");
|
|
}
|
|
else if (errcode == CE_CANT_OPEN_SOURCE_FILE)
|
|
{
|
|
errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Error_Reading_Image");
|
|
}
|
|
else if (errcode == CE_ERROR_PARSING_SOURCE)
|
|
{
|
|
errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Error_Image_File_Corrupt");
|
|
}
|
|
else if (errcode == CE_ERROR_WRITING_OUTPUT_FILE)
|
|
{
|
|
errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Error_Writing_Temp_Output");
|
|
}
|
|
else if (errcode == CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED )
|
|
{
|
|
errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Image_Wrong_Size");
|
|
}
|
|
|
|
if (errorDialog != NULL)
|
|
{
|
|
errorDialog->DoModal();
|
|
}
|
|
}
|
|
}
|
|
else if (!stricmp(extension, "bmp"))
|
|
{
|
|
// convert from the bmp file format to the TGA file format
|
|
errcode = ConvertBMPToTGA(fullpath, tgaPath);
|
|
|
|
if (errcode == CE_SUCCESS)
|
|
{
|
|
deleteIntermediateTGA = true;
|
|
}
|
|
else
|
|
{
|
|
failed = true;
|
|
vgui::MessageBox *errorDialog = NULL;
|
|
|
|
if (errcode == CE_MEMORY_ERROR)
|
|
{
|
|
errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Error_Memory");
|
|
}
|
|
else if (errcode == CE_CANT_OPEN_SOURCE_FILE)
|
|
{
|
|
errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Error_Reading_Image");
|
|
}
|
|
else if (errcode == CE_ERROR_PARSING_SOURCE)
|
|
{
|
|
errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Error_Image_File_Corrupt");
|
|
}
|
|
else if (errcode == CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED)
|
|
{
|
|
errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Error_BMP_Format_Not_Supported");
|
|
}
|
|
else if (errcode == CE_ERROR_WRITING_OUTPUT_FILE)
|
|
{
|
|
errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Error_Writing_Temp_Output");
|
|
}
|
|
|
|
if (errorDialog != NULL)
|
|
{
|
|
errorDialog->DoModal();
|
|
}
|
|
}
|
|
}
|
|
else if (!stricmp(extension, "vtf"))
|
|
{
|
|
// if the file is already in the vtf format there's no need to convert it.
|
|
convertTGAToVTF = false;
|
|
}
|
|
}
|
|
|
|
if (convertTGAToVTF && !failed)
|
|
{
|
|
// convert the TGA file to the VTF format.
|
|
errcode = ConvertTGA(tgaPath); // resize TGA so that it has power-of-two dimensions with a max size of 256x256.
|
|
if (errcode != CE_SUCCESS)
|
|
{
|
|
failed = true;
|
|
vgui::MessageBox *errorDialog = NULL;
|
|
|
|
if (errcode == CE_MEMORY_ERROR)
|
|
{
|
|
errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Error_Memory");
|
|
}
|
|
else if (errcode == CE_CANT_OPEN_SOURCE_FILE)
|
|
{
|
|
errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Error_Reading_Image");
|
|
}
|
|
else if (errcode == CE_ERROR_PARSING_SOURCE)
|
|
{
|
|
errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Error_Image_File_Corrupt");
|
|
}
|
|
else if (errcode == CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED)
|
|
{
|
|
errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Error_TGA_Format_Not_Supported");
|
|
}
|
|
else if (errcode == CE_ERROR_WRITING_OUTPUT_FILE)
|
|
{
|
|
errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Error_Writing_Temp_Output");
|
|
}
|
|
|
|
if (errorDialog != NULL)
|
|
{
|
|
errorDialog->DoModal();
|
|
}
|
|
}
|
|
|
|
if (!failed)
|
|
{
|
|
char tempPath[MAX_PATH*2];
|
|
Q_strncpy(tempPath, tgaPath, sizeof(tempPath));
|
|
|
|
errcode = ConvertTGAToVTF(tempPath);
|
|
if (errcode == CE_SUCCESS)
|
|
{
|
|
deleteIntermediateVTF = true;
|
|
}
|
|
else
|
|
{
|
|
failed = true;
|
|
vgui::MessageBox *errorDialog = NULL;
|
|
|
|
if (errcode == CE_MEMORY_ERROR)
|
|
{
|
|
errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Error_Memory");
|
|
}
|
|
else if (errcode == CE_CANT_OPEN_SOURCE_FILE)
|
|
{
|
|
errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Error_Reading_Image");
|
|
}
|
|
else if (errcode == CE_ERROR_PARSING_SOURCE)
|
|
{
|
|
errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Error_Image_File_Corrupt");
|
|
}
|
|
else if (errcode == CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED)
|
|
{
|
|
errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Error_TGA_Format_Not_Supported");
|
|
}
|
|
else if (errcode == CE_ERROR_WRITING_OUTPUT_FILE)
|
|
{
|
|
errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Error_Writing_Temp_Output");
|
|
}
|
|
else if (errcode == CE_ERROR_LOADING_DLL)
|
|
{
|
|
errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Error_Cant_Load_VTEX_DLL");
|
|
}
|
|
|
|
if (errorDialog != NULL)
|
|
{
|
|
errorDialog->DoModal();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
char finalPath[MAX_PATH*2];
|
|
finalPath[0] = 0;
|
|
char vtfPath[MAX_PATH*2];
|
|
vtfPath[0] = 0;
|
|
|
|
if (!failed)
|
|
{
|
|
Q_strncpy(vtfPath, tgaPath, sizeof(vtfPath));
|
|
|
|
// rename the tga file to be a vtf file.
|
|
c = vtfPath + strlen(vtfPath);
|
|
while ((c > vtfPath) && (*(c-1) != '.'))
|
|
{
|
|
--c;
|
|
}
|
|
*c = 0;
|
|
Q_strncat(vtfPath, "vtf", sizeof(vtfPath), COPY_ALL_CHARACTERS);
|
|
|
|
// get the vtfFilename from the path.
|
|
const char *vtfFilename = fullpath + strlen(fullpath);
|
|
while ((vtfFilename > fullpath) && (*(vtfFilename-1) != '\\') && (*(vtfFilename-1) != '/'))
|
|
{
|
|
--vtfFilename;
|
|
}
|
|
|
|
Q_strncpy(finalPath, engine->GetGameDirectory(), sizeof(finalPath));
|
|
Q_strncat(finalPath, "\\materials\\VGUI\\logos\\", sizeof(finalPath), COPY_ALL_CHARACTERS);
|
|
Q_strncat(finalPath, vtfFilename, sizeof(finalPath), COPY_ALL_CHARACTERS);
|
|
|
|
c = finalPath + strlen(finalPath);
|
|
while ((c > finalPath) && (*(c-1) != '.'))
|
|
{
|
|
--c;
|
|
}
|
|
*c = 0;
|
|
Q_strncat(finalPath,"vtf", sizeof(finalPath), COPY_ALL_CHARACTERS);
|
|
|
|
// make sure the directory exists before we try to copy the file.
|
|
g_pFullFileSystem->CreateDirHierarchy("materials/VGUI/logos/", "GAME");
|
|
|
|
// write out the spray VMT file.
|
|
errcode = WriteSprayVMT(finalPath);
|
|
if (errcode != CE_SUCCESS)
|
|
{
|
|
failed = true;
|
|
|
|
vgui::MessageBox *errorDialog = new vgui::MessageBox("#GameUI_Spray_Import_Error_Title", "#GameUI_Spray_Import_Error_Writing_Output");
|
|
errorDialog->DoModal();
|
|
}
|
|
|
|
if (!failed)
|
|
{
|
|
// copy vtf file to the final location.
|
|
#ifdef WIN32
|
|
CopyFile(vtfPath, finalPath, true);
|
|
#elif defined( OSX )
|
|
copyfile( vtfPath, finalPath, 0, 0 );
|
|
#elif !defined( _PS3 )
|
|
engine->CopyLocalFile( vtfPath, finalPath );
|
|
#endif
|
|
|
|
// refresh the logo list so the new spray shows up.
|
|
InitLogoList(m_pLogoList);
|
|
|
|
char rootFilename[MAX_PATH];
|
|
Q_strncpy(rootFilename, vtfFilename, MAX_PATH);
|
|
|
|
// get the root filename so we can select in the spray list.
|
|
rootFilename[strlen(rootFilename) - 4] = 0;
|
|
|
|
// automatically select the logo that was just imported.
|
|
SelectLogo(rootFilename);
|
|
}
|
|
}
|
|
|
|
// delete the intermediate VTF file if one was made.
|
|
if (deleteIntermediateVTF)
|
|
{
|
|
// dgoodenough - DeleteFile is Win32 specific, remove it for PS3 build
|
|
// PS3_BUILDFIX
|
|
// FIXME - How will this be handled on PS3?
|
|
#if !defined( _PS3 )
|
|
DeleteFile(vtfPath);
|
|
#endif
|
|
// the TGA->VTF conversion process generates a .txt file if one wasn't already there.
|
|
// in this case, delete the .txt file.
|
|
c = vtfPath + strlen(vtfPath);
|
|
while ((c > vtfPath) && (*(c-1) != '.'))
|
|
{
|
|
--c;
|
|
}
|
|
Q_strncpy(c, "txt", sizeof(vtfPath)-(c-vtfPath));
|
|
// dgoodenough - DeleteFile is Win32 specific, remove it for PS3 build
|
|
// PS3_BUILDFIX
|
|
// FIXME - How will this be handled on PS3?
|
|
#if !defined( _PS3 )
|
|
DeleteFile(vtfPath);
|
|
#endif
|
|
}
|
|
|
|
// delete the intermediate TGA file if one was made.
|
|
if (deleteIntermediateTGA)
|
|
{
|
|
// dgoodenough - DeleteFile is Win32 specific, remove it for PS3 build
|
|
// PS3_BUILDFIX
|
|
// FIXME - How will this be handled on PS3?
|
|
#if !defined( _PS3 )
|
|
DeleteFile(tgaPath);
|
|
#endif
|
|
}
|
|
|
|
// change the cursor back to normal
|
|
surface()->SetCursor(dc_user);
|
|
}
|
|
|
|
struct ValveJpegErrorHandler_t
|
|
{
|
|
// The default manager
|
|
struct jpeg_error_mgr m_Base;
|
|
// For handling any errors
|
|
jmp_buf m_ErrorContext;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: We'll override the default error handler so we can deal with errors without having to exit the engine
|
|
//-----------------------------------------------------------------------------
|
|
static void ValveJpegErrorHandler( j_common_ptr cinfo )
|
|
{
|
|
ValveJpegErrorHandler_t *pError = reinterpret_cast< ValveJpegErrorHandler_t * >( cinfo->err );
|
|
|
|
char buffer[ JMSG_LENGTH_MAX ];
|
|
|
|
/* Create the message */
|
|
( *cinfo->err->format_message )( cinfo, buffer );
|
|
|
|
Warning( "%s\n", buffer );
|
|
|
|
// Bail
|
|
longjmp( pError->m_ErrorContext, 1 );
|
|
}
|
|
|
|
// convert the JPEG file given to a TGA file at the given output path.
|
|
ConversionErrorType COptionsSubMultiplayer::ConvertJPEGToTGA(const char *jpegpath, const char *tgaPath)
|
|
{
|
|
#if !defined( _GAMECONSOLE )
|
|
struct jpeg_decompress_struct jpegInfo;
|
|
struct ValveJpegErrorHandler_t jerr;
|
|
JSAMPROW row_pointer[1];
|
|
int row_stride;
|
|
int cur_row = 0;
|
|
|
|
// image attributes
|
|
int image_height;
|
|
int image_width;
|
|
|
|
// open the jpeg image file.
|
|
FILE *infile = fopen(jpegpath, "rb");
|
|
if (infile == NULL)
|
|
{
|
|
return CE_CANT_OPEN_SOURCE_FILE;
|
|
}
|
|
|
|
// setup error to print to stderr.
|
|
jpegInfo.err = jpeg_std_error(&jerr.m_Base);
|
|
|
|
jpegInfo.err->error_exit = &ValveJpegErrorHandler;
|
|
|
|
// create the decompress struct.
|
|
jpeg_create_decompress(&jpegInfo);
|
|
|
|
if ( setjmp( jerr.m_ErrorContext ) )
|
|
{
|
|
// Get here if there is any error
|
|
jpeg_destroy_decompress( &jpegInfo );
|
|
|
|
fclose(infile);
|
|
|
|
return CE_ERROR_PARSING_SOURCE;
|
|
}
|
|
|
|
jpeg_stdio_src(&jpegInfo, infile);
|
|
|
|
// read in the jpeg header and make sure that's all good.
|
|
if (jpeg_read_header(&jpegInfo, TRUE) != JPEG_HEADER_OK)
|
|
{
|
|
fclose(infile);
|
|
return CE_ERROR_PARSING_SOURCE;
|
|
}
|
|
|
|
// start the decompress with the jpeg engine.
|
|
if (jpeg_start_decompress(&jpegInfo) != TRUE)
|
|
{
|
|
jpeg_destroy_decompress(&jpegInfo);
|
|
fclose(infile);
|
|
return CE_ERROR_PARSING_SOURCE;
|
|
}
|
|
|
|
// Check for valid width and height (ie. power of 2 and print out an error and exit if not).
|
|
if ( !IsPowerOfTwo( jpegInfo.image_height ) || !IsPowerOfTwo( jpegInfo.image_width ) )
|
|
{
|
|
jpeg_destroy_decompress(&jpegInfo);
|
|
fclose( infile );
|
|
return CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
|
|
// now that we've started the decompress with the jpeg lib, we have the attributes of the
|
|
// image ready to be read out of the decompress struct.
|
|
row_stride = jpegInfo.output_width * jpegInfo.output_components;
|
|
image_height = jpegInfo.image_height;
|
|
image_width = jpegInfo.image_width;
|
|
int mem_required = jpegInfo.image_height * jpegInfo.image_width * jpegInfo.output_components;
|
|
|
|
// allocate the memory to read the image data into.
|
|
unsigned char *buf = (unsigned char *)malloc(mem_required);
|
|
if (buf == NULL)
|
|
{
|
|
jpeg_destroy_decompress(&jpegInfo);
|
|
fclose(infile);
|
|
return CE_MEMORY_ERROR;
|
|
}
|
|
|
|
// read in all the scan lines of the image into our image data buffer.
|
|
bool working = true;
|
|
while (working && (jpegInfo.output_scanline < jpegInfo.output_height))
|
|
{
|
|
row_pointer[0] = &(buf[cur_row * row_stride]);
|
|
if (jpeg_read_scanlines(&jpegInfo, row_pointer, 1) != TRUE)
|
|
{
|
|
working = false;
|
|
}
|
|
++cur_row;
|
|
}
|
|
|
|
if (!working)
|
|
{
|
|
free(buf);
|
|
jpeg_destroy_decompress(&jpegInfo);
|
|
fclose(infile);
|
|
return CE_ERROR_PARSING_SOURCE;
|
|
}
|
|
|
|
jpeg_finish_decompress(&jpegInfo);
|
|
|
|
fclose(infile);
|
|
|
|
// ok, at this point we have read in the JPEG image to our buffer, now we need to write it out as a TGA file.
|
|
CUtlBuffer outBuf;
|
|
bool bRetVal = TGAWriter::WriteToBuffer( buf, outBuf, image_width, image_height, IMAGE_FORMAT_RGB888, IMAGE_FORMAT_RGB888 );
|
|
if ( bRetVal )
|
|
{
|
|
if ( !g_pFullFileSystem->WriteFile( tgaPath, NULL, outBuf ) )
|
|
{
|
|
bRetVal = false;
|
|
}
|
|
}
|
|
|
|
free(buf);
|
|
return bRetVal ? CE_SUCCESS : CE_ERROR_WRITING_OUTPUT_FILE;
|
|
|
|
#else
|
|
return CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
|
|
#endif
|
|
}
|
|
|
|
// convert the bmp file given to a TGA file at the given destination path.
|
|
ConversionErrorType COptionsSubMultiplayer::ConvertBMPToTGA(const char *bmpPath, const char *tgaPath)
|
|
{
|
|
if ( !IsPC() )
|
|
return CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
|
|
|
|
// @wge TODO FIXME - fix OSX build
|
|
#if defined( _OSX ) || defined (LINUX)
|
|
return CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
|
|
#else
|
|
HBITMAP hBitmap = (HBITMAP)LoadImage(NULL, bmpPath, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE | LR_DEFAULTSIZE);
|
|
BITMAP bitmap;
|
|
|
|
GetObject(hBitmap, sizeof(bitmap), &bitmap);
|
|
|
|
BITMAPINFO *bitmapInfo;
|
|
|
|
if (bitmap.bmBitsPixel == 24)
|
|
{
|
|
bitmapInfo = (BITMAPINFO *)malloc(sizeof(BITMAPINFO));
|
|
}
|
|
else
|
|
{
|
|
int colorsUsed = 1 << bitmap.bmBitsPixel;
|
|
bitmapInfo = (BITMAPINFO *)malloc(colorsUsed * sizeof(RGBQUAD) + sizeof(BITMAPINFO));
|
|
}
|
|
|
|
memset(bitmapInfo, 0, sizeof(BITMAPINFO));
|
|
bitmapInfo->bmiHeader.biSize = sizeof(bitmapInfo->bmiHeader);
|
|
if (bitmap.bmBitsPixel != 24)
|
|
{
|
|
bitmapInfo->bmiHeader.biBitCount = bitmap.bmBitsPixel; // need to specify the bits per pixel so GDI will generate a color table for us.
|
|
}
|
|
|
|
HDC dc = CreateCompatibleDC(NULL);
|
|
|
|
int retcode = GetDIBits(dc, hBitmap, 0, bitmap.bmHeight, NULL, bitmapInfo, DIB_RGB_COLORS);
|
|
|
|
DeleteDC(dc);
|
|
|
|
if (retcode == 0)
|
|
{
|
|
// error getting the bitmap info for some reason.
|
|
free(bitmapInfo);
|
|
return CE_ERROR_PARSING_SOURCE;
|
|
}
|
|
|
|
int mem_required = 3 * bitmap.bmWidth * bitmap.bmHeight; // mem required for copying the data out into RGB format.
|
|
|
|
unsigned char *buf = (unsigned char *)malloc(mem_required);
|
|
if (buf == NULL)
|
|
{
|
|
free(bitmapInfo);
|
|
return CE_MEMORY_ERROR;
|
|
}
|
|
|
|
if (bitmapInfo->bmiHeader.biBitCount == 24)
|
|
{
|
|
if ((bitmap.bmWidth * 3) == bitmap.bmWidthBytes)
|
|
{
|
|
// 24-bit BGR color bitmap that is word aligned.
|
|
memcpy(buf, bitmap.bmBits, mem_required);
|
|
}
|
|
else
|
|
{
|
|
// 24-bit BGR color bitmap that is not word aligned.
|
|
// need to read it in one row at a time since it isn't word aligned.
|
|
int row;
|
|
for (row = 0; row < bitmap.bmHeight; ++row)
|
|
{
|
|
memcpy(buf + (row * 3 * bitmap.bmWidth), (unsigned char *)(bitmap.bmBits) + (row * bitmap.bmWidthBytes), 3 * bitmap.bmWidth);
|
|
}
|
|
}
|
|
|
|
// bitmaps are loaded as BGR, need to convert it to RGB.
|
|
|
|
int numPixels = bitmap.bmHeight * bitmap.bmWidth;
|
|
|
|
// convert to RGB
|
|
int index;
|
|
for (index = 0; index < numPixels; ++index)
|
|
{
|
|
unsigned char blue = buf[3 * index]; // save blue value
|
|
buf[3 * index] = buf[3 * index + 2]; // copy red value.
|
|
buf[3 * index + 2] = blue; // copy blue value.
|
|
}
|
|
}
|
|
else if (bitmapInfo->bmiHeader.biBitCount == 8)
|
|
{
|
|
// 8-bit 256 color bitmap.
|
|
int y, x, index, colorTableEntry;
|
|
for (y = 0; y < bitmap.bmHeight; ++y)
|
|
{
|
|
for (x = 0; x < bitmap.bmWidth; ++x)
|
|
{
|
|
// compute the color map entry for this pixel
|
|
index = y * bitmap.bmWidthBytes + x;
|
|
colorTableEntry = ((unsigned char *)bitmap.bmBits)[index];
|
|
|
|
// get the color for this color map entry.
|
|
RGBQUAD *rgbQuad = &(bitmapInfo->bmiColors[colorTableEntry]);
|
|
|
|
// copy the color values for this pixel to the destination buffer.
|
|
buf[(y * bitmap.bmWidth * 3) + (x * 3)] = rgbQuad->rgbRed;
|
|
buf[(y * bitmap.bmWidth * 3) + (x * 3) + 1] = rgbQuad->rgbGreen;
|
|
buf[(y * bitmap.bmWidth * 3) + (x * 3) + 2] = rgbQuad->rgbBlue;
|
|
}
|
|
}
|
|
}
|
|
else if (bitmapInfo->bmiHeader.biBitCount == 4)
|
|
{
|
|
// 4-bit 16 color bitmap.
|
|
int y, x, index, colorTableEntry;
|
|
for (y = 0; y < bitmap.bmHeight; ++y)
|
|
{
|
|
for (x = 0; x < bitmap.bmWidth; x += 2)
|
|
{
|
|
// two color table entries per byte
|
|
index = y * bitmap.bmWidthBytes + x / 2;
|
|
|
|
// get the color table entry for this pixel
|
|
colorTableEntry = (0xf0 & ((unsigned char *)bitmap.bmBits)[index]) >> 4;
|
|
|
|
// get the color values for this pixel's color table entry.
|
|
RGBQUAD *rgbQuad = &(bitmapInfo->bmiColors[colorTableEntry]);
|
|
|
|
// copy the pixel's color values to the destination buffer.
|
|
buf[(y * bitmap.bmWidth * 3) + (x * 3)] = rgbQuad->rgbRed;
|
|
buf[(y * bitmap.bmWidth * 3) + (x * 3) + 1] = rgbQuad->rgbGreen;
|
|
buf[(y * bitmap.bmWidth * 3) + (x * 3) + 2] = rgbQuad->rgbBlue;
|
|
|
|
// make sure we haven't reached the end of the row.
|
|
if ((x + 1) > bitmap.bmWidth)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// get the color table entry for this pixel.
|
|
colorTableEntry = 0x0f & ((unsigned char *)bitmap.bmBits)[index];
|
|
|
|
// get the color values for this pixel's color table entry.
|
|
rgbQuad = &(bitmapInfo->bmiColors[colorTableEntry]);
|
|
|
|
// copy the pixel's color values to the destination buffer.
|
|
buf[(y * bitmap.bmWidth * 3) + ((x+1) * 3)] = rgbQuad->rgbRed;
|
|
buf[(y * bitmap.bmWidth * 3) + ((x+1) * 3) + 1] = rgbQuad->rgbGreen;
|
|
buf[(y * bitmap.bmWidth * 3) + ((x+1) * 3) + 2] = rgbQuad->rgbBlue;
|
|
}
|
|
}
|
|
}
|
|
else if (bitmapInfo->bmiHeader.biBitCount == 1)
|
|
{
|
|
// 1-bit monochrome bitmap.
|
|
int y, x, index, bit, bitMask;
|
|
for (y = 0; y < bitmap.bmHeight; ++y)
|
|
{
|
|
x = 0;
|
|
while (x < bitmap.bmWidth)
|
|
{
|
|
RGBQUAD *rgbQuad = NULL;
|
|
bitMask = 0x80;
|
|
|
|
// get the index into the bitmap data for the next 8 pixels.
|
|
index = y * bitmap.bmWidthBytes + x / 8;
|
|
|
|
// go through all 8 bits in this byte to get all 8 pixel colors.
|
|
do
|
|
{
|
|
// get the value of the bit for this pixel.
|
|
bit = ((unsigned char *)bitmap.bmBits)[index] & bitMask;
|
|
|
|
// bit will either be 0 or non-zero since there are only two colors.
|
|
if (bit == 0)
|
|
{
|
|
rgbQuad = &(bitmapInfo->bmiColors[0]);
|
|
}
|
|
else
|
|
{
|
|
rgbQuad = &(bitmapInfo->bmiColors[1]);
|
|
}
|
|
|
|
// copy this pixel's color values into the destination buffer.
|
|
buf[(y * bitmap.bmWidth * 3) + (x * 3)] = rgbQuad->rgbRed;
|
|
buf[(y * bitmap.bmWidth * 3) + (x * 3) + 1] = rgbQuad->rgbGreen;
|
|
buf[(y * bitmap.bmWidth * 3) + (x * 3) + 2] = rgbQuad->rgbBlue;
|
|
|
|
// go to the next pixel.
|
|
++x;
|
|
bitMask = bitMask >> 1;
|
|
} while ((x < bitmap.bmWidth) && (bitMask > 0));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
free(bitmapInfo);
|
|
free(buf);
|
|
DeleteObject(hBitmap);
|
|
|
|
return CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
|
|
free(bitmapInfo);
|
|
|
|
// bitmaps are stored upside down, so flip the image vertically
|
|
int index;
|
|
int rowWidth = 3 * bitmap.bmWidth;
|
|
unsigned char *row = (unsigned char *)malloc(rowWidth);
|
|
for (index = 0; index < (bitmap.bmHeight / 2); ++index)
|
|
{
|
|
memcpy(row, buf + (index * rowWidth), rowWidth);
|
|
memcpy(buf + (index * rowWidth), buf + (((bitmap.bmHeight - 1) - index) * rowWidth), rowWidth);
|
|
memcpy(buf + (((bitmap.bmHeight - 1) - index) * rowWidth), row, rowWidth);
|
|
}
|
|
|
|
// write out the TGA file using the RGB data buffer.
|
|
CUtlBuffer outBuf;
|
|
bool retval = TGAWriter::WriteToBuffer(buf, outBuf, bitmap.bmWidth, bitmap.bmHeight, IMAGE_FORMAT_RGB888, IMAGE_FORMAT_RGB888);
|
|
if ( retval )
|
|
{
|
|
if ( !g_pFullFileSystem->WriteFile( tgaPath, NULL, outBuf ) )
|
|
{
|
|
retval = false;
|
|
}
|
|
}
|
|
DeleteObject(hBitmap);
|
|
return retval ? CE_SUCCESS : CE_ERROR_WRITING_OUTPUT_FILE;
|
|
#endif // !_OSX
|
|
}
|
|
|
|
// read a TGA header from the current point in the file stream.
|
|
static void ReadTGAHeader(FILE *infile, TGAHeader &header)
|
|
{
|
|
if (infile == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
fread(&header.identsize, sizeof(header.identsize), 1, infile);
|
|
fread(&header.colourmaptype, sizeof(header.colourmaptype), 1, infile);
|
|
fread(&header.imagetype, sizeof(header.imagetype), 1, infile);
|
|
fread(&header.colourmapstart, sizeof(header.colourmapstart), 1, infile);
|
|
fread(&header.colourmaplength, sizeof(header.colourmaplength), 1, infile);
|
|
fread(&header.colourmapbits, sizeof(header.colourmapbits), 1, infile);
|
|
fread(&header.xstart, sizeof(header.xstart), 1, infile);
|
|
fread(&header.ystart, sizeof(header.ystart), 1, infile);
|
|
fread(&header.width, sizeof(header.width), 1, infile);
|
|
fread(&header.height, sizeof(header.height), 1, infile);
|
|
fread(&header.bits, sizeof(header.bits), 1, infile);
|
|
fread(&header.descriptor, sizeof(header.descriptor), 1, infile);
|
|
}
|
|
|
|
// write a TGA header to the current point in the file stream.
|
|
static void WriteTGAHeader(FILE *outfile, TGAHeader &header)
|
|
{
|
|
if (outfile == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
fwrite(&header.identsize, sizeof(header.identsize), 1, outfile);
|
|
fwrite(&header.colourmaptype, sizeof(header.colourmaptype), 1, outfile);
|
|
fwrite(&header.imagetype, sizeof(header.imagetype), 1, outfile);
|
|
fwrite(&header.colourmapstart, sizeof(header.colourmapstart), 1, outfile);
|
|
fwrite(&header.colourmaplength, sizeof(header.colourmaplength), 1, outfile);
|
|
fwrite(&header.colourmapbits, sizeof(header.colourmapbits), 1, outfile);
|
|
fwrite(&header.xstart, sizeof(header.xstart), 1, outfile);
|
|
fwrite(&header.ystart, sizeof(header.ystart), 1, outfile);
|
|
fwrite(&header.width, sizeof(header.width), 1, outfile);
|
|
fwrite(&header.height, sizeof(header.height), 1, outfile);
|
|
fwrite(&header.bits, sizeof(header.bits), 1, outfile);
|
|
fwrite(&header.descriptor, sizeof(header.descriptor), 1, outfile);
|
|
}
|
|
|
|
// reads in a TGA file and converts it to 32 bit RGBA color values in a memory buffer.
|
|
unsigned char * COptionsSubMultiplayer::ReadTGAAsRGBA(const char *tgaPath, int &width, int &height, ConversionErrorType &errcode, TGAHeader &tgaHeader )
|
|
{
|
|
FILE *tgaFile = fopen(tgaPath, "rb");
|
|
if (tgaFile == NULL)
|
|
{
|
|
errcode = CE_CANT_OPEN_SOURCE_FILE;
|
|
return NULL;
|
|
}
|
|
|
|
// read header for TGA file.
|
|
ReadTGAHeader(tgaFile, tgaHeader);
|
|
|
|
// image type 2 is RGB, other types not supported.
|
|
if (tgaHeader.imagetype != 2)
|
|
{
|
|
fclose(tgaFile);
|
|
|
|
errcode = CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
|
|
return NULL;
|
|
}
|
|
|
|
int tgaDataSize = tgaHeader.width * tgaHeader.height * tgaHeader.bits / 8;
|
|
unsigned char *tgaData = (unsigned char *)malloc(tgaDataSize);
|
|
if (tgaData == NULL)
|
|
{
|
|
fclose(tgaFile);
|
|
|
|
errcode = CE_MEMORY_ERROR;
|
|
return NULL;
|
|
}
|
|
|
|
fread(tgaData, 1, tgaDataSize, tgaFile);
|
|
|
|
fclose(tgaFile);
|
|
|
|
width = tgaHeader.width;
|
|
height = tgaHeader.height;
|
|
|
|
if (tgaHeader.bits == 24)
|
|
{
|
|
// image needs to be converted to a 32-bit image.
|
|
int numPixels = tgaHeader.width * tgaHeader.height;
|
|
|
|
unsigned char *retBuf = (unsigned char *)malloc(numPixels * 4);
|
|
if (retBuf == NULL)
|
|
{
|
|
free(tgaData);
|
|
|
|
errcode = CE_MEMORY_ERROR;
|
|
return NULL;
|
|
}
|
|
|
|
// convert from RGB to RGBA color format.
|
|
int index;
|
|
for (index = 0; index < numPixels; ++index)
|
|
{
|
|
retBuf[index * 4] = tgaData[index * 3];
|
|
retBuf[index * 4 + 1] = tgaData[index * 3 + 1];
|
|
retBuf[index * 4 + 2] = tgaData[index * 3 + 2];
|
|
retBuf[index * 4 + 3] = 0xff;
|
|
}
|
|
|
|
free(tgaData);
|
|
|
|
tgaHeader.bits = 32;
|
|
|
|
errcode = CE_SUCCESS;
|
|
return retBuf;
|
|
}
|
|
else if (tgaHeader.bits == 32)
|
|
{
|
|
// already in RGBA format so just return it.
|
|
errcode = CE_SUCCESS;
|
|
return tgaData;
|
|
}
|
|
|
|
// format not supported, fail.
|
|
free(tgaData);
|
|
|
|
errcode = CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
|
|
return NULL;
|
|
}
|
|
|
|
// resizes the file specified by tgaPath so that it has dimensions that are
|
|
// powers-of-two and is equal to or smaller than 256x256.
|
|
// also converts from 24-bit RGB to 32-bit RGB (with 8-bit alpha)
|
|
ConversionErrorType COptionsSubMultiplayer::ConvertTGA(const char *tgaPath)
|
|
{
|
|
int tgaWidth = 0, tgaHeight = 0;
|
|
ConversionErrorType errcode;
|
|
TGAHeader tgaHeader;
|
|
unsigned char *srcBuffer = ReadTGAAsRGBA(tgaPath, tgaWidth, tgaHeight, errcode, tgaHeader);
|
|
|
|
if (srcBuffer == NULL)
|
|
{
|
|
return errcode;
|
|
}
|
|
|
|
int paddedImageWidth, paddedImageHeight;
|
|
|
|
if ((tgaWidth <= 0) || (tgaHeight <= 0))
|
|
{
|
|
free(srcBuffer);
|
|
return CE_ERROR_PARSING_SOURCE;
|
|
}
|
|
|
|
// get the nearest power of two that is greater than the width of the image.
|
|
paddedImageWidth = tgaWidth;
|
|
if (!IsPowerOfTwo(paddedImageWidth))
|
|
{
|
|
// width is not a power of two, calculate the next highest power of two value.
|
|
int i = 1;
|
|
while (paddedImageWidth > 1)
|
|
{
|
|
paddedImageWidth = paddedImageWidth >> 1;
|
|
++i;
|
|
}
|
|
|
|
paddedImageWidth = paddedImageWidth << i;
|
|
}
|
|
|
|
// make sure the width is less than or equal to 256
|
|
if (paddedImageWidth > 256)
|
|
{
|
|
paddedImageWidth = 256;
|
|
}
|
|
|
|
// get the nearest power of two that is greater than the height of the image
|
|
paddedImageHeight = tgaHeight;
|
|
if (!IsPowerOfTwo(paddedImageHeight))
|
|
{
|
|
// height is not a power of two, calculate the next highest power of two value.
|
|
int i = 1;
|
|
while (paddedImageHeight > 1)
|
|
{
|
|
paddedImageHeight = paddedImageHeight >> 1;
|
|
++i;
|
|
}
|
|
|
|
paddedImageHeight = paddedImageHeight << i;
|
|
}
|
|
|
|
// make sure the height is less than or equal to 256
|
|
if (paddedImageHeight > 256)
|
|
{
|
|
paddedImageHeight = 256;
|
|
}
|
|
|
|
// compute the amount of stretching that needs to be done to both width and height to get the image to fit.
|
|
float widthRatio = (float)paddedImageWidth / tgaWidth;
|
|
float heightRatio = (float)paddedImageHeight / tgaHeight;
|
|
|
|
int finalWidth;
|
|
int finalHeight;
|
|
|
|
// compute the final dimensions of the stretched image.
|
|
if (widthRatio < heightRatio)
|
|
{
|
|
finalWidth = paddedImageWidth;
|
|
finalHeight = (int)(tgaHeight * widthRatio + 0.5f);
|
|
// i.e. for 1x1 size pixels in the resized image we will take color from sourceRatio x sourceRatio sized pixels in the source image.
|
|
}
|
|
else if (heightRatio < widthRatio)
|
|
{
|
|
finalHeight = paddedImageHeight;
|
|
finalWidth = (int)(tgaWidth * heightRatio + 0.5f);
|
|
}
|
|
else
|
|
{
|
|
finalHeight = paddedImageHeight;
|
|
finalWidth = paddedImageWidth;
|
|
}
|
|
|
|
unsigned char *resizeBuffer = (unsigned char *)malloc(finalWidth * finalHeight * 4);
|
|
|
|
// do the actual stretching
|
|
StretchRGBAImage(srcBuffer, tgaWidth, tgaHeight, resizeBuffer, finalWidth, finalHeight);
|
|
|
|
free(srcBuffer); // don't need this anymore.
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
///// need to pad the image so both dimensions are power of two's /////
|
|
///////////////////////////////////////////////////////////////////////
|
|
unsigned char *finalBuffer = (unsigned char *)malloc(paddedImageWidth * paddedImageHeight * 4);
|
|
PadRGBAImage(resizeBuffer, finalWidth, finalHeight, finalBuffer, paddedImageWidth, paddedImageHeight);
|
|
|
|
FILE *outfile = fopen(tgaPath, "wb");
|
|
if (outfile == NULL)
|
|
{
|
|
free(resizeBuffer);
|
|
free(finalBuffer);
|
|
|
|
return CE_ERROR_WRITING_OUTPUT_FILE;
|
|
}
|
|
|
|
tgaHeader.width = paddedImageWidth;
|
|
tgaHeader.height = paddedImageHeight;
|
|
|
|
WriteTGAHeader(outfile, tgaHeader);
|
|
|
|
fwrite(finalBuffer, 1, paddedImageWidth * paddedImageHeight * 4, outfile);
|
|
|
|
fclose(outfile);
|
|
|
|
free(resizeBuffer);
|
|
free(finalBuffer);
|
|
|
|
return CE_SUCCESS;
|
|
}
|
|
|
|
// resize by stretching (or compressing) an RGBA image pointed to by srcBuf into the buffer pointed to by destBuf.
|
|
// the buffers are assumed to be sized appropriately to accomidate RGBA images of the given widths and heights.
|
|
ConversionErrorType COptionsSubMultiplayer::StretchRGBAImage(const unsigned char *srcBuf, const int srcWidth, const int srcHeight,
|
|
unsigned char *destBuf, const int destWidth, const int destHeight)
|
|
{
|
|
if ((srcBuf == NULL) || (destBuf == NULL))
|
|
{
|
|
return CE_CANT_OPEN_SOURCE_FILE;
|
|
}
|
|
|
|
int destRow,destColumn;
|
|
|
|
float ratioX = (float)srcWidth / (float)destWidth;
|
|
float ratioY = (float)srcHeight / (float)destHeight;
|
|
|
|
// loop through all the pixels in the destination image.
|
|
for (destRow = 0; destRow < destHeight; ++destRow)
|
|
{
|
|
for (destColumn = 0; destColumn < destWidth; ++destColumn)
|
|
{
|
|
// calculate the center of the pixel in the source image.
|
|
float srcCenterX = ratioX * (destColumn + 0.5f);
|
|
float srcCenterY = ratioY * (destRow + 0.5f);
|
|
|
|
// calculate the starting and ending coords for this destination pixel in the source image.
|
|
float srcStartX = srcCenterX - (ratioX / 2.0f);
|
|
if (srcStartX < 0.0f)
|
|
{
|
|
srcStartX = 0.0f; // this should never happen, but just in case.
|
|
}
|
|
|
|
float srcStartY = srcCenterY - (ratioY / 2.0f);
|
|
if (srcStartY < 0.0f)
|
|
{
|
|
srcStartY = 0.0f; // this should never happen, but just in case.
|
|
}
|
|
|
|
float srcEndX = srcCenterX + (ratioX / 2.0f);
|
|
if (srcEndX > srcWidth)
|
|
{
|
|
srcEndX = srcWidth; // this should never happen, but just in case.
|
|
}
|
|
|
|
float srcEndY = srcCenterY + (ratioY / 2.0f);
|
|
if (srcEndY > srcHeight)
|
|
{
|
|
srcEndY = srcHeight; // this should never happen, but just in case.
|
|
}
|
|
|
|
// Calculate the percentage of each source pixels' contribution to the destination pixel color.
|
|
|
|
float srcCurrentX; // initialized at the start of the y loop.
|
|
float srcCurrentY = srcStartY;
|
|
|
|
float destRed = 0.0f;
|
|
float destGreen = 0.0f;
|
|
float destBlue = 0.0f;
|
|
float destAlpha = 0.0f;
|
|
|
|
//// loop for the parts of the source image that will contribute color to the destination pixel.
|
|
while (srcCurrentY < srcEndY)
|
|
{
|
|
float srcCurrentEndY = (float)((int)srcCurrentY + 1);
|
|
if (srcCurrentEndY > srcEndY)
|
|
{
|
|
srcCurrentEndY = srcEndY;
|
|
}
|
|
|
|
float srcCurrentHeight = srcCurrentEndY - srcCurrentY;
|
|
|
|
srcCurrentX = srcStartX;
|
|
|
|
while (srcCurrentX < srcEndX)
|
|
{
|
|
float srcCurrentEndX = (float)((int)srcCurrentX + 1);
|
|
if (srcCurrentEndX > srcEndX)
|
|
{
|
|
srcCurrentEndX = srcEndX;
|
|
}
|
|
float srcCurrentWidth = srcCurrentEndX - srcCurrentX;
|
|
|
|
// compute the percentage of the destination pixel's color this source pixel will contribute.
|
|
float srcColorPercentage = (srcCurrentWidth / ratioX) * (srcCurrentHeight / ratioY);
|
|
|
|
int srcCurrentPixelX = (int)srcCurrentX;
|
|
int srcCurrentPixelY = (int)srcCurrentY;
|
|
|
|
// get the color values for this source pixel.
|
|
unsigned char srcCurrentRed = srcBuf[(srcCurrentPixelY * srcWidth * 4) + (srcCurrentPixelX * 4)];
|
|
unsigned char srcCurrentGreen = srcBuf[(srcCurrentPixelY * srcWidth * 4) + (srcCurrentPixelX * 4) + 1];
|
|
unsigned char srcCurrentBlue = srcBuf[(srcCurrentPixelY * srcWidth * 4) + (srcCurrentPixelX * 4) + 2];
|
|
unsigned char srcCurrentAlpha = srcBuf[(srcCurrentPixelY * srcWidth * 4) + (srcCurrentPixelX * 4) + 3];
|
|
|
|
// add the color contribution from this source pixel to the destination pixel.
|
|
destRed += srcCurrentRed * srcColorPercentage;
|
|
destGreen += srcCurrentGreen * srcColorPercentage;
|
|
destBlue += srcCurrentBlue * srcColorPercentage;
|
|
destAlpha += srcCurrentAlpha * srcColorPercentage;
|
|
|
|
srcCurrentX = srcCurrentEndX;
|
|
}
|
|
|
|
srcCurrentY = srcCurrentEndY;
|
|
}
|
|
|
|
// assign the computed color to the destination pixel, round to the nearest value. Make sure the value doesn't exceed 255.
|
|
destBuf[(destRow * destWidth * 4) + (destColumn * 4)] = MIN((int)(destRed + 0.5f), 255);
|
|
destBuf[(destRow * destWidth * 4) + (destColumn * 4) + 1] = MIN((int)(destGreen + 0.5f), 255);
|
|
destBuf[(destRow * destWidth * 4) + (destColumn * 4) + 2] = MIN((int)(destBlue + 0.5f), 255);
|
|
destBuf[(destRow * destWidth * 4) + (destColumn * 4) + 3] = MIN((int)(destAlpha + 0.5f), 255);
|
|
} // column loop
|
|
} // row loop
|
|
|
|
return CE_SUCCESS;
|
|
}
|
|
|
|
ConversionErrorType COptionsSubMultiplayer::PadRGBAImage(const unsigned char *srcBuf, const int srcWidth, const int srcHeight,
|
|
unsigned char *destBuf, const int destWidth, const int destHeight)
|
|
{
|
|
if ((srcBuf == NULL) || (destBuf == NULL))
|
|
{
|
|
return CE_CANT_OPEN_SOURCE_FILE;
|
|
}
|
|
|
|
memset(destBuf, 0, destWidth * destHeight * 4);
|
|
|
|
if ((destWidth < srcWidth) || (destHeight < srcHeight))
|
|
{
|
|
return CE_ERROR_PARSING_SOURCE;
|
|
}
|
|
|
|
if ((srcWidth == destWidth) && (srcHeight == destHeight))
|
|
{
|
|
// no padding is needed, just copy the buffer straight over and call it done.
|
|
memcpy(destBuf, srcBuf, destWidth * destHeight * 4);
|
|
return CE_SUCCESS;
|
|
}
|
|
|
|
if (destWidth == srcWidth)
|
|
{
|
|
// only the top and bottom of the image need padding.
|
|
// do this separately since we can do this more efficiently than the other cases.
|
|
int numRowsToPad = (destHeight - srcHeight) / 2;
|
|
memcpy(destBuf + (numRowsToPad * destWidth * 4), srcBuf, srcWidth * srcHeight * 4);
|
|
}
|
|
else
|
|
{
|
|
int numColumnsToPad = (destWidth - srcWidth) / 2;
|
|
int numRowsToPad = (destHeight - srcHeight) / 2;
|
|
int lastRow = numRowsToPad + srcHeight;
|
|
int row;
|
|
for (row = numRowsToPad; row < lastRow; ++row)
|
|
{
|
|
unsigned char * destOffset = destBuf + (row * destWidth * 4) + (numColumnsToPad * 4);
|
|
const unsigned char * srcOffset = srcBuf + ((row - numRowsToPad) * srcWidth * 4);
|
|
memcpy(destOffset, srcOffset, srcWidth * 4);
|
|
}
|
|
}
|
|
|
|
return CE_SUCCESS;
|
|
}
|
|
|
|
// convert TGA file at the given location to a VTF file of the same root name at the same location.
|
|
ConversionErrorType COptionsSubMultiplayer::ConvertTGAToVTF(const char *tgaPath)
|
|
{
|
|
FILE *infile = fopen(tgaPath, "rb");
|
|
if (infile == NULL)
|
|
{
|
|
return CE_CANT_OPEN_SOURCE_FILE;
|
|
}
|
|
|
|
// read out the header of the image.
|
|
TGAHeader header;
|
|
ReadTGAHeader(infile, header);
|
|
|
|
// check to make sure that the TGA has the proper dimensions and size.
|
|
if (!IsPowerOfTwo(header.width) || !IsPowerOfTwo(header.height))
|
|
{
|
|
fclose(infile);
|
|
return CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
|
|
// check to make sure that the TGA isn't too big.
|
|
if ((header.width > 256) || (header.height > 256))
|
|
{
|
|
fclose(infile);
|
|
return CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
|
|
int imageMemoryFootprint = header.width * header.height * header.bits / 8;
|
|
|
|
CUtlBuffer inbuf(0, imageMemoryFootprint);
|
|
|
|
// read in the image
|
|
int nBytesRead = fread(inbuf.Base(), imageMemoryFootprint, 1, infile);
|
|
|
|
fclose(infile);
|
|
inbuf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead );
|
|
|
|
// load vtex_dll.dll and get the interface to it.
|
|
CSysModule *vtexmod = Sys_LoadModule("vtex_dll");
|
|
if (vtexmod == NULL)
|
|
{
|
|
return CE_ERROR_LOADING_DLL;
|
|
}
|
|
|
|
CreateInterfaceFn factory = Sys_GetFactory(vtexmod);
|
|
if (factory == NULL)
|
|
{
|
|
Sys_UnloadModule(vtexmod);
|
|
return CE_ERROR_LOADING_DLL;
|
|
}
|
|
|
|
IVTex *vtex = (IVTex *)factory(IVTEX_VERSION_STRING, NULL);
|
|
if (vtex == NULL)
|
|
{
|
|
Sys_UnloadModule(vtexmod);
|
|
return CE_ERROR_LOADING_DLL;
|
|
}
|
|
|
|
char *vtfParams[4];
|
|
|
|
// the 0th entry is skipped cause normally thats the program name.
|
|
vtfParams[0] = "";
|
|
vtfParams[1] = "-quiet";
|
|
vtfParams[2] = "-dontusegamedir";
|
|
vtfParams[3] = (char *)tgaPath;
|
|
|
|
// call vtex to do the conversion.
|
|
vtex->VTex(4, vtfParams);
|
|
|
|
Sys_UnloadModule(vtexmod);
|
|
|
|
return CE_SUCCESS;
|
|
}
|
|
|
|
// write a VMT file for the spray VTF file at the given path.
|
|
ConversionErrorType COptionsSubMultiplayer::WriteSprayVMT(const char *vtfPath)
|
|
{
|
|
if (vtfPath == NULL)
|
|
{
|
|
return CE_ERROR_WRITING_OUTPUT_FILE;
|
|
}
|
|
|
|
// make the vmt filename
|
|
char vmtPath[MAX_PATH*4];
|
|
Q_strncpy(vmtPath, vtfPath, sizeof(vmtPath));
|
|
char *c = vmtPath + strlen(vmtPath);
|
|
while ((c > vmtPath) && (*(c-1) != '.'))
|
|
{
|
|
--c;
|
|
}
|
|
Q_strncpy(c, "vmt", sizeof(vmtPath) - (c - vmtPath));
|
|
|
|
// get the root filename for the vtf file
|
|
char filename[MAX_PATH];
|
|
while ((c > vmtPath) && (*(c-1) != '/') && (*(c-1) != '\\'))
|
|
{
|
|
--c;
|
|
}
|
|
|
|
int i = 0;
|
|
while ((*c != 0) && (*c != '.'))
|
|
{
|
|
filename[i++] = *(c++);
|
|
}
|
|
filename[i] = 0;
|
|
|
|
// create the vmt file.
|
|
FILE *vmtFile = fopen(vmtPath, "w");
|
|
if (vmtFile == NULL)
|
|
{
|
|
return CE_ERROR_WRITING_OUTPUT_FILE;
|
|
}
|
|
|
|
// write the contents of the file.
|
|
fprintf(vmtFile, "LightmappedGeneric\n{\n\t\"$basetexture\" \"vgui\\logos\\%s\"\n\t\"$translucent\" \"1\"\n\t\"$decal\" \"1\"\n\t\"$decalscale\" \"0.250\"\n}\n", filename);
|
|
|
|
fclose(vmtFile);
|
|
|
|
return CE_SUCCESS;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Builds the list of logos
|
|
//-----------------------------------------------------------------------------
|
|
void COptionsSubMultiplayer::InitLogoList( CLabeledCommandComboBox *cb )
|
|
{
|
|
// Find out images
|
|
FileFindHandle_t fh;
|
|
char directory[ 512 ];
|
|
|
|
// NOTE: <<vitaliy>> this feature is obsolete in CS:GO
|
|
ConVarRef cl_logofile( "cl_logofile" );
|
|
if ( !cl_logofile.IsValid() )
|
|
return;
|
|
|
|
cb->DeleteAllItems();
|
|
|
|
const char *logofile = cl_logofile.GetString();
|
|
Q_snprintf( directory, sizeof( directory ), "materials/vgui/logos/*.vtf" );
|
|
const char *fn = g_pFullFileSystem->FindFirst( directory, &fh );
|
|
int i = 0, initialItem = 0;
|
|
while (fn)
|
|
{
|
|
char filename[ 512 ];
|
|
Q_snprintf( filename, sizeof(filename), "materials/vgui/logos/%s", fn );
|
|
if ( strlen( filename ) >= 4 )
|
|
{
|
|
filename[ strlen( filename ) - 4 ] = 0;
|
|
Q_strncat( filename, ".vmt", sizeof( filename ), COPY_ALL_CHARACTERS );
|
|
if ( g_pFullFileSystem->FileExists( filename ) )
|
|
{
|
|
// strip off the extension
|
|
Q_strncpy( filename, fn, sizeof( filename ) );
|
|
filename[ strlen( filename ) - 4 ] = 0;
|
|
cb->AddItem( filename, "" );
|
|
|
|
// check to see if this is the one we have set
|
|
Q_snprintf( filename, sizeof(filename), "materials/vgui/logos/%s", fn );
|
|
if (!Q_stricmp(filename, logofile))
|
|
{
|
|
initialItem = i;
|
|
}
|
|
|
|
++i;
|
|
}
|
|
}
|
|
|
|
fn = g_pFullFileSystem->FindNext( fh );
|
|
}
|
|
|
|
g_pFullFileSystem->FindClose( fh );
|
|
cb->SetInitialItem(initialItem);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Selects the given logo in the logo list.
|
|
//-----------------------------------------------------------------------------
|
|
void COptionsSubMultiplayer::SelectLogo(const char *logoName)
|
|
{
|
|
int numEntries = m_pLogoList->GetItemCount();
|
|
int index;
|
|
wchar_t itemText[MAX_PATH];
|
|
wchar_t itemToSelectText[MAX_PATH];
|
|
|
|
// convert the logo filename to unicode
|
|
g_pVGuiLocalize->ConvertANSIToUnicode(logoName, itemToSelectText, sizeof(itemToSelectText));
|
|
|
|
// find the index of the spray we want.
|
|
for (index = 0; index < numEntries; ++index)
|
|
{
|
|
m_pLogoList->GetItemText(index, itemText, sizeof(itemText));
|
|
if (!wcscmp(itemText, itemToSelectText))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (index < numEntries)
|
|
{
|
|
// select the logo.
|
|
m_pLogoList->ActivateItem(index);
|
|
}
|
|
}
|
|
|
|
#define MODEL_MATERIAL_BASE_FOLDER "materials/vgui/playermodels/"
|
|
|
|
|
|
void StripStringOutOfString( const char *pPattern, const char *pIn, char *pOut )
|
|
{
|
|
int iLengthBase = strlen( pPattern );
|
|
int iLengthString = strlen( pIn );
|
|
|
|
int k = 0;
|
|
|
|
for ( int j = iLengthBase; j < iLengthString; j++ )
|
|
{
|
|
pOut[k] = pIn[j];
|
|
k++;
|
|
}
|
|
|
|
pOut[k] = 0;
|
|
}
|
|
|
|
void FindVMTFilesInFolder( const char *pFolder, const char *pFolderName, CLabeledCommandComboBox *cb, int &iCount, int &iInitialItem )
|
|
{
|
|
ConVarRef cl_modelfile( "cl_playermodel" );
|
|
if ( !cl_modelfile.IsValid() )
|
|
return;
|
|
|
|
char directory[ 512 ];
|
|
Q_snprintf( directory, sizeof( directory ), "%s/*.*", pFolder );
|
|
|
|
FileFindHandle_t fh;
|
|
|
|
const char *fn = g_pFullFileSystem->FindFirst( directory, &fh );
|
|
const char *modelfile = cl_modelfile.GetString();
|
|
|
|
while ( fn )
|
|
{
|
|
if ( !stricmp( fn, ".") || !stricmp( fn, "..") )
|
|
{
|
|
fn = g_pFullFileSystem->FindNext( fh );
|
|
continue;
|
|
}
|
|
|
|
if ( g_pFullFileSystem->FindIsDirectory( fh ) )
|
|
{
|
|
char folderpath[512];
|
|
|
|
Q_snprintf( folderpath, sizeof( folderpath ), "%s/%s", pFolder, fn );
|
|
|
|
FindVMTFilesInFolder( folderpath, fn, cb, iCount, iInitialItem );
|
|
fn = g_pFullFileSystem->FindNext( fh );
|
|
continue;
|
|
}
|
|
|
|
if ( !strstr( fn, ".vmt" ) )
|
|
{
|
|
fn = g_pFullFileSystem->FindNext( fh );
|
|
continue;
|
|
}
|
|
|
|
|
|
char filename[ 512 ];
|
|
Q_snprintf( filename, sizeof(filename), "%s/%s", pFolder, fn );
|
|
if ( strlen( filename ) >= 4 )
|
|
{
|
|
filename[ strlen( filename ) - 4 ] = 0;
|
|
Q_strncat( filename, ".vmt", sizeof( filename ), COPY_ALL_CHARACTERS );
|
|
if ( g_pFullFileSystem->FileExists( filename ) )
|
|
{
|
|
char displayname[ 512 ];
|
|
char texturepath[ 512 ];
|
|
// strip off the extension
|
|
Q_strncpy( displayname, fn, sizeof( displayname ) );
|
|
StripStringOutOfString( MODEL_MATERIAL_BASE_FOLDER, filename, texturepath );
|
|
|
|
displayname[ strlen( displayname ) - 4 ] = 0;
|
|
|
|
cb->AddItem( displayname, texturepath + 1 ); // ignore the initial "/" in texture path
|
|
|
|
char realname[ 512 ];
|
|
Q_FileBase( modelfile, realname, sizeof( realname ) );
|
|
Q_FileBase( filename, filename, sizeof( filename ) );
|
|
|
|
if (!stricmp(filename, realname))
|
|
{
|
|
iInitialItem = iCount;
|
|
}
|
|
|
|
++iCount;
|
|
}
|
|
}
|
|
|
|
fn = g_pFullFileSystem->FindNext( fh );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Builds model list
|
|
//-----------------------------------------------------------------------------
|
|
void COptionsSubMultiplayer::InitModelList( CLabeledCommandComboBox *cb )
|
|
{
|
|
// Find out images
|
|
int i = 0, initialItem = 0;
|
|
|
|
cb->DeleteAllItems();
|
|
FindVMTFilesInFolder( MODEL_MATERIAL_BASE_FOLDER, "", cb, i, initialItem );
|
|
cb->SetInitialItem( initialItem );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void COptionsSubMultiplayer::InitCrosshairColorEntries()
|
|
{
|
|
// parse the string for the custom color settings and get the initial settings.
|
|
ConVarRef cl_crosshaircolor( "cl_crosshaircolor" );
|
|
int index = 0;
|
|
if ( cl_crosshaircolor.IsValid() )
|
|
{
|
|
index = clamp( cl_crosshaircolor.GetInt(), 0, NumCrosshairColors );
|
|
}
|
|
|
|
if (m_pCrosshairColorComboBox != NULL)
|
|
{
|
|
KeyValues *data = new KeyValues("data");
|
|
|
|
// add in the "Default" selection
|
|
data->Clear();
|
|
|
|
// add in the colors for the color list
|
|
for ( int i = 0; i < NumCrosshairColors; i++ )
|
|
{
|
|
data->SetInt("color", i);
|
|
m_pCrosshairColorComboBox->AddItem( s_crosshairColors[ i ].name, data);
|
|
}
|
|
|
|
m_pCrosshairColorComboBox->ActivateItemByRow(index);
|
|
|
|
data->deleteThis();
|
|
}
|
|
|
|
// force the crosshair to redraw.
|
|
RedrawCrosshairImage(); // don't need to do this since we're not drawing anything.
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: takes the settings from the crosshair settings combo boxes and sliders
|
|
// and apply it to the crosshair illustrations.
|
|
//-----------------------------------------------------------------------------
|
|
void COptionsSubMultiplayer::RedrawCrosshairImage()
|
|
{
|
|
if (m_pCrosshairColorComboBox == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool enableApplyButton = false;
|
|
|
|
// get the color selected in the combo box.
|
|
KeyValues *data = m_pCrosshairColorComboBox->GetActiveItemUserData();
|
|
int colorIndex = data->GetInt("color");
|
|
colorIndex = clamp( colorIndex, 0, NumCrosshairColors );
|
|
|
|
int selectedVal = 0;
|
|
int actualVal = 0;
|
|
if (m_pCrosshairColorComboBox != NULL)
|
|
{
|
|
selectedVal = m_pCrosshairColorComboBox->GetActiveItem();
|
|
}
|
|
|
|
ConVarRef cl_crosshaircolor( "cl_crosshaircolor" );
|
|
if ( cl_crosshaircolor.IsValid() )
|
|
{
|
|
actualVal = clamp( cl_crosshaircolor.GetInt(), 0, NumCrosshairColors );
|
|
}
|
|
|
|
if ( selectedVal != actualVal )
|
|
{
|
|
enableApplyButton = true;
|
|
}
|
|
|
|
if (enableApplyButton)
|
|
{
|
|
OnApplyButtonEnable();
|
|
}
|
|
|
|
if ( m_pCrosshairImage && m_pCrosshairSize )
|
|
{
|
|
int size = m_pCrosshairSize->GetActiveItem();
|
|
m_pCrosshairImage->UpdateCrosshair(
|
|
s_crosshairColors[selectedVal].r,
|
|
s_crosshairColors[selectedVal].g,
|
|
s_crosshairColors[selectedVal].b,
|
|
size );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: takes the settings from the crosshair settings combo boxes and sliders
|
|
// and apply it to the crosshair illustrations.
|
|
//-----------------------------------------------------------------------------
|
|
void COptionsSubMultiplayer::RedrawAdvCrosshairImage()
|
|
{
|
|
if ( !ModInfo().AdvCrosshair() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// get the color selected in the combo box.
|
|
int r,g,b;
|
|
|
|
r = clamp( m_pAdvCrosshairRedSlider->GetSliderValue(), 0, 255 );
|
|
g = clamp( m_pAdvCrosshairGreenSlider->GetSliderValue(), 0, 255 );
|
|
b = clamp( m_pAdvCrosshairBlueSlider->GetSliderValue(), 0, 255 );
|
|
|
|
float scale = m_pAdvCrosshairScaleSlider->GetSliderValue();
|
|
|
|
if ( m_pAdvCrosshairImage && m_pAdvCrosshairStyle )
|
|
{
|
|
char crosshairname[256];
|
|
m_pAdvCrosshairStyle->GetText( crosshairname, sizeof(crosshairname) );
|
|
|
|
char texture[ 256 ];
|
|
Q_snprintf ( texture, sizeof( texture ), "vgui/crosshairs/%s", crosshairname );
|
|
|
|
m_pAdvCrosshairImage->UpdateCrosshair( r, g, b, scale, texture );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: initialize the crosshair size list.
|
|
//-----------------------------------------------------------------------------
|
|
void COptionsSubMultiplayer::InitCrosshairSizeList(CLabeledCommandComboBox *cb)
|
|
{
|
|
if (cb == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
cb->Reset();
|
|
|
|
// add in the auto, small, medium, and large size selections.
|
|
cb->AddItem("#GameUI_Auto", "cl_crosshairscale 0");
|
|
cb->AddItem("#GameUI_Small", "cl_crosshairscale 1200");
|
|
cb->AddItem("#GameUI_Medium", "cl_crosshairscale 768");
|
|
cb->AddItem("#GameUI_Large", "cl_crosshairscale 600");
|
|
|
|
// parse out the size value from the cvar and set the initial value.
|
|
int initialScale = 0;
|
|
ConVarRef cl_crosshairscale( "cl_crosshairscale" );
|
|
if ( cl_crosshairscale.IsValid() )
|
|
{
|
|
initialScale = cl_crosshairscale.GetInt();
|
|
if ( initialScale <= 0 )
|
|
{
|
|
initialScale = 0;
|
|
}
|
|
else if ( initialScale <= 600 )
|
|
{
|
|
initialScale = 3;
|
|
}
|
|
else if ( initialScale <= 768 )
|
|
{
|
|
initialScale = 2;
|
|
}
|
|
else
|
|
{
|
|
initialScale = 1;
|
|
}
|
|
}
|
|
cb->SetInitialItem( initialScale );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: initialize the crosshair style list
|
|
//-----------------------------------------------------------------------------
|
|
void COptionsSubMultiplayer::InitAdvCrosshairStyleList(CLabeledCommandComboBox *cb)
|
|
{
|
|
// Find out images
|
|
FileFindHandle_t fh;
|
|
char directory[ 512 ];
|
|
|
|
ConVarRef cl_crosshair_file( "cl_crosshair_file" );
|
|
if ( !cl_crosshair_file.IsValid() )
|
|
return;
|
|
|
|
cb->DeleteAllItems();
|
|
|
|
char crosshairfile[256];
|
|
Q_snprintf( crosshairfile, sizeof(crosshairfile), "materials/vgui/crosshairs/%s.vtf", cl_crosshair_file.GetString() );
|
|
|
|
Q_snprintf( directory, sizeof( directory ), "materials/vgui/crosshairs/*.vtf" );
|
|
const char *fn = g_pFullFileSystem->FindFirst( directory, &fh );
|
|
int i = 0, initialItem = 0;
|
|
while (fn)
|
|
{
|
|
char filename[ 512 ];
|
|
Q_snprintf( filename, sizeof(filename), "materials/vgui/crosshairs/%s", fn );
|
|
if ( strlen( filename ) >= 4 )
|
|
{
|
|
filename[ strlen( filename ) - 4 ] = 0;
|
|
Q_strncat( filename, ".vmt", sizeof( filename ), COPY_ALL_CHARACTERS );
|
|
if ( g_pFullFileSystem->FileExists( filename ) )
|
|
{
|
|
// strip off the extension
|
|
Q_strncpy( filename, fn, sizeof( filename ) );
|
|
filename[ strlen( filename ) - 4 ] = 0;
|
|
cb->AddItem( filename, "" );
|
|
|
|
// check to see if this is the one we have set
|
|
Q_snprintf( filename, sizeof(filename), "materials/vgui/crosshairs/%s", fn );
|
|
if (!stricmp(filename, crosshairfile))
|
|
{
|
|
initialItem = i;
|
|
}
|
|
|
|
++i;
|
|
}
|
|
}
|
|
|
|
fn = g_pFullFileSystem->FindNext( fh );
|
|
}
|
|
|
|
g_pFullFileSystem->FindClose( fh );
|
|
cb->SetInitialItem(initialItem);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void COptionsSubMultiplayer::RemapLogo()
|
|
{
|
|
char logoname[256];
|
|
|
|
m_pLogoList->GetText( logoname, sizeof( logoname ) );
|
|
if( !logoname[ 0 ] )
|
|
return;
|
|
|
|
char fullLogoName[512];
|
|
|
|
// make sure there is a version with the proper shader
|
|
g_pFullFileSystem->CreateDirHierarchy( "materials/VGUI/logos/UI", "GAME" );
|
|
Q_snprintf( fullLogoName, sizeof( fullLogoName ), "materials/VGUI/logos/UI/%s.vmt", logoname );
|
|
if ( !g_pFullFileSystem->FileExists( fullLogoName ) )
|
|
{
|
|
FileHandle_t fp = g_pFullFileSystem->Open( fullLogoName, "wb" );
|
|
if ( !fp )
|
|
return;
|
|
|
|
char data[1024];
|
|
Q_snprintf( data, sizeof( data ), "\"UnlitGeneric\"\n\
|
|
{\n\
|
|
// Original shader: BaseTimesVertexColorAlphaBlendNoOverbright\n\
|
|
\"$translucent\" 1\n\
|
|
\"$basetexture\" \"VGUI\\logos\\%s\"\n\
|
|
\"$vertexcolor\" 1\n\
|
|
\"$vertexalpha\" 1\n\
|
|
\"$no_fullbright\" 1\n\
|
|
\"$ignorez\" 1\n\
|
|
}\n\
|
|
", logoname );
|
|
|
|
g_pFullFileSystem->Write( data, strlen( data ), fp );
|
|
g_pFullFileSystem->Close( fp );
|
|
}
|
|
|
|
Q_snprintf( fullLogoName, sizeof( fullLogoName ), "logos/UI/%s", logoname );
|
|
m_pLogoImage->SetImage( fullLogoName );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void COptionsSubMultiplayer::RemapModel()
|
|
{
|
|
const char *pModelName = m_pModelList->GetActiveItemCommand();
|
|
|
|
if( pModelName == NULL )
|
|
return;
|
|
|
|
char texture[ 256 ];
|
|
Q_snprintf ( texture, sizeof( texture ), "vgui/playermodels/%s", pModelName );
|
|
texture[ strlen( texture ) - 4 ] = 0;
|
|
|
|
m_pModelImage->setTexture( texture );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Called whenever model name changes
|
|
//-----------------------------------------------------------------------------
|
|
void COptionsSubMultiplayer::OnTextChanged(vgui::Panel *panel)
|
|
{
|
|
RemapModel();
|
|
RemapLogo();
|
|
|
|
RedrawCrosshairImage(); // redraw the crosshair.
|
|
|
|
RedrawAdvCrosshairImage();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void COptionsSubMultiplayer::OnSliderMoved(KeyValues *data)
|
|
{
|
|
m_nTopColor = (int) m_pPrimaryColorSlider->GetSliderValue();
|
|
m_nBottomColor = (int) m_pSecondaryColorSlider->GetSliderValue();
|
|
|
|
RemapModel();
|
|
RedrawAdvCrosshairImage();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void COptionsSubMultiplayer::OnApplyButtonEnable()
|
|
{
|
|
PostMessage(GetParent(), new KeyValues("ApplyButtonEnable"));
|
|
InvalidateLayout();
|
|
}
|
|
|
|
//#include <windows.h>
|
|
|
|
#define DIB_HEADER_MARKER ((WORD) ('M' << 8) | 'B')
|
|
#define SUIT_HUE_START 192
|
|
#define SUIT_HUE_END 223
|
|
#define PLATE_HUE_START 160
|
|
#define PLATE_HUE_END 191
|
|
|
|
// @wge this struct definition is from Portal 2 source, but didn't want to conflict with other platforms so made it OSX only.
|
|
#if defined( _OSX ) || defined (LINUX)
|
|
typedef struct tagRGBQUAD {
|
|
uint8 rgbBlue;
|
|
uint8 rgbGreen;
|
|
uint8 rgbRed;
|
|
uint8 rgbReserved;
|
|
} RGBQUAD;
|
|
#endif // _OSX
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
static void PaletteHueReplace( RGBQUAD *palSrc, int newHue, int Start, int end )
|
|
{
|
|
int i;
|
|
float r, b, g;
|
|
float maxcol, mincol;
|
|
float hue, val, sat;
|
|
|
|
hue = (float)(newHue * (360.0 / 255));
|
|
|
|
for (i = Start; i <= end; i++)
|
|
{
|
|
b = palSrc[ i ].rgbBlue;
|
|
g = palSrc[ i ].rgbGreen;
|
|
r = palSrc[ i ].rgbRed;
|
|
|
|
maxcol = MAX( MAX( r, g ), b ) / 255.0f;
|
|
mincol = MIN( MIN( r, g ), b ) / 255.0f;
|
|
|
|
val = maxcol;
|
|
sat = (maxcol - mincol) / maxcol;
|
|
|
|
mincol = val * (1.0f - sat);
|
|
|
|
if (hue <= 120)
|
|
{
|
|
b = mincol;
|
|
if (hue < 60)
|
|
{
|
|
r = val;
|
|
g = mincol + hue * (val - mincol)/(120 - hue);
|
|
}
|
|
else
|
|
{
|
|
g = val;
|
|
r = mincol + (120 - hue)*(val-mincol)/hue;
|
|
}
|
|
}
|
|
else if (hue <= 240)
|
|
{
|
|
r = mincol;
|
|
if (hue < 180)
|
|
{
|
|
g = val;
|
|
b = mincol + (hue - 120)*(val-mincol)/(240 - hue);
|
|
}
|
|
else
|
|
{
|
|
b = val;
|
|
g = mincol + (240 - hue)*(val-mincol)/(hue - 120);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g = mincol;
|
|
if (hue < 300)
|
|
{
|
|
b = val;
|
|
r = mincol + (hue - 240)*(val-mincol)/(360 - hue);
|
|
}
|
|
else
|
|
{
|
|
r = val;
|
|
b = mincol + (360 - hue)*(val-mincol)/(hue - 240);
|
|
}
|
|
}
|
|
|
|
palSrc[ i ].rgbBlue = (unsigned char)(b * 255);
|
|
palSrc[ i ].rgbGreen = (unsigned char)(g * 255);
|
|
palSrc[ i ].rgbRed = (unsigned char)(r * 255);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void COptionsSubMultiplayer::RemapPalette( char *filename, int topcolor, int bottomcolor )
|
|
{
|
|
#if !defined( _OSX ) && !defined( _PS3 ) && !defined (LINUX)
|
|
char infile[ 256 ];
|
|
char outfile[ 256 ];
|
|
|
|
FileHandle_t file;
|
|
CUtlBuffer outbuffer( 16384, 16384 );
|
|
|
|
Q_snprintf( infile, sizeof( infile ), "models/player/%s/%s.bmp", filename, filename );
|
|
Q_strncpy( outfile, "models/player/remapped.bmp", sizeof( outfile ) );
|
|
|
|
file = g_pFullFileSystem->Open( infile, "rb" );
|
|
if ( file == FILESYSTEM_INVALID_HANDLE )
|
|
return;
|
|
|
|
// Parse bitmap
|
|
BITMAPFILEHEADER bmfHeader;
|
|
DWORD dwBitsSize, dwFileSize;
|
|
LPBITMAPINFO lpbmi;
|
|
|
|
dwFileSize = g_pFullFileSystem->Size( file );
|
|
|
|
g_pFullFileSystem->Read( &bmfHeader, sizeof(bmfHeader), file );
|
|
|
|
outbuffer.Put( &bmfHeader, sizeof( bmfHeader ) );
|
|
|
|
if (bmfHeader.bfType == DIB_HEADER_MARKER)
|
|
{
|
|
dwBitsSize = dwFileSize - sizeof(bmfHeader);
|
|
|
|
HGLOBAL hDIB = GlobalAlloc( GMEM_MOVEABLE | GMEM_ZEROINIT, dwBitsSize );
|
|
char *pDIB = (LPSTR)GlobalLock((HGLOBAL)hDIB);
|
|
{
|
|
g_pFullFileSystem->Read(pDIB, dwBitsSize, file );
|
|
|
|
lpbmi = (LPBITMAPINFO)pDIB;
|
|
|
|
// Remap palette
|
|
PaletteHueReplace( lpbmi->bmiColors, topcolor, SUIT_HUE_START, SUIT_HUE_END );
|
|
PaletteHueReplace( lpbmi->bmiColors, bottomcolor, PLATE_HUE_START, PLATE_HUE_END );
|
|
|
|
outbuffer.Put( pDIB, dwBitsSize );
|
|
}
|
|
|
|
GlobalUnlock( hDIB);
|
|
GlobalFree((HGLOBAL) hDIB);
|
|
}
|
|
|
|
g_pFullFileSystem->Close(file);
|
|
|
|
g_pFullFileSystem->RemoveFile( outfile, NULL );
|
|
|
|
g_pFullFileSystem->CreateDirHierarchy("models/player", NULL);
|
|
file = g_pFullFileSystem->Open( outfile, "wb" );
|
|
if ( file != FILESYSTEM_INVALID_HANDLE )
|
|
{
|
|
g_pFullFileSystem->Write( outbuffer.Base(), outbuffer.TellPut(), file );
|
|
g_pFullFileSystem->Close( file );
|
|
}
|
|
#endif // !_OSX && !_PS3
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void COptionsSubMultiplayer::ColorForName( char const *pszColorName, int&r, int&g, int&b )
|
|
{
|
|
r = g = b = 0;
|
|
|
|
int count = sizeof( itemlist ) / sizeof( itemlist[0] );
|
|
|
|
for ( int i = 0; i < count; i++ )
|
|
{
|
|
if ( StringHasPrefix( pszColorName, itemlist[ i ].name ) )
|
|
{
|
|
r = itemlist[ i ].r;
|
|
g = itemlist[ i ].g;
|
|
b = itemlist[ i ].b;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void COptionsSubMultiplayer::OnResetData()
|
|
{
|
|
// reset the DownloadFilter combo box
|
|
if ( m_pDownloadFilterCombo )
|
|
{
|
|
// cl_downloadfilter
|
|
ConVarRef cl_downloadfilter( "cl_downloadfilter" );
|
|
|
|
if ( Q_stricmp( cl_downloadfilter.GetString(), "none" ) == 0 )
|
|
{
|
|
m_pDownloadFilterCombo->ActivateItem( 2 );
|
|
}
|
|
else if ( Q_stricmp( cl_downloadfilter.GetString(), "nosounds" ) == 0 )
|
|
{
|
|
m_pDownloadFilterCombo->ActivateItem( 1 );
|
|
}
|
|
else
|
|
{
|
|
m_pDownloadFilterCombo->ActivateItem( 0 );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void COptionsSubMultiplayer::OnApplyChanges()
|
|
{
|
|
m_pPrimaryColorSlider->ApplyChanges();
|
|
m_pSecondaryColorSlider->ApplyChanges();
|
|
// m_pModelList->ApplyChanges();
|
|
m_pLogoList->ApplyChanges();
|
|
m_pLogoList->GetText(m_LogoName, sizeof(m_LogoName));
|
|
m_pHighQualityModelCheckBox->ApplyChanges();
|
|
|
|
for ( int i=0; i<m_cvarToggleCheckButtons.GetCount(); ++i )
|
|
{
|
|
CCvarToggleCheckButton *toggleButton = m_cvarToggleCheckButtons[i];
|
|
if( toggleButton->IsVisible() && toggleButton->IsEnabled() )
|
|
{
|
|
toggleButton->ApplyChanges();
|
|
}
|
|
}
|
|
|
|
if ( !ModInfo().NoCrosshair() )
|
|
{
|
|
if (m_pCrosshairSize != NULL)
|
|
{
|
|
m_pCrosshairSize->ApplyChanges();
|
|
}
|
|
|
|
if (m_pCrosshairTranslucencyCheckbox != NULL)
|
|
{
|
|
m_pCrosshairTranslucencyCheckbox->ApplyChanges();
|
|
}
|
|
|
|
ApplyCrosshairColorChanges();
|
|
}
|
|
|
|
if ( ModInfo().AdvCrosshair() )
|
|
{
|
|
m_pAdvCrosshairRedSlider->ApplyChanges();
|
|
m_pAdvCrosshairGreenSlider->ApplyChanges();
|
|
m_pAdvCrosshairBlueSlider->ApplyChanges();
|
|
m_pAdvCrosshairScaleSlider->ApplyChanges();
|
|
m_pAdvCrosshairStyle->ApplyChanges();
|
|
|
|
// save the crosshair
|
|
char cmd[512];
|
|
char crosshair[256];
|
|
m_pAdvCrosshairStyle->GetText(crosshair, sizeof(crosshair));
|
|
Q_snprintf(cmd, sizeof(cmd), "cl_crosshair_file %s\n", crosshair);
|
|
engine->ClientCmd_Unrestricted(cmd);
|
|
}
|
|
|
|
// NOTE: <<vitaliy>> this feature is obsolete in CS:GO
|
|
// save the logo name
|
|
char cmd[512];
|
|
if ( m_LogoName[ 0 ] )
|
|
{
|
|
Q_snprintf(cmd, sizeof(cmd), "cl_logofile materials/vgui/logos/%s.vtf\n", m_LogoName);
|
|
}
|
|
else
|
|
{
|
|
Q_strncpy( cmd, "cl_logofile \"\"\n", sizeof( cmd ) );
|
|
}
|
|
engine->ClientCmd_Unrestricted(cmd);
|
|
|
|
if ( m_pModelList && m_pModelList->IsVisible() && m_pModelList->GetActiveItemCommand() )
|
|
{
|
|
Q_strncpy( m_ModelName, m_pModelList->GetActiveItemCommand(), sizeof( m_ModelName ) );
|
|
Q_StripExtension( m_ModelName, m_ModelName, sizeof ( m_ModelName ) );
|
|
|
|
// save the player model name
|
|
Q_snprintf(cmd, sizeof(cmd), "cl_playermodel models/%s.mdl\n", m_ModelName );
|
|
engine->ClientCmd_Unrestricted(cmd);
|
|
}
|
|
else
|
|
{
|
|
m_ModelName[0] = 0;
|
|
}
|
|
|
|
// set the DownloadFilter cvar
|
|
if ( m_pDownloadFilterCombo )
|
|
{
|
|
ConVarRef cl_downloadfilter( "cl_downloadfilter" );
|
|
|
|
switch ( m_pDownloadFilterCombo->GetActiveItem() )
|
|
{
|
|
default:
|
|
case 0:
|
|
cl_downloadfilter.SetValue( "all" );
|
|
break;
|
|
case 1:
|
|
cl_downloadfilter.SetValue( "nosounds" );
|
|
break;
|
|
case 2:
|
|
cl_downloadfilter.SetValue( "none" );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: apply the crosshair color values to the cvar.
|
|
// also set the slider values to match the new value.
|
|
//-----------------------------------------------------------------------------
|
|
void COptionsSubMultiplayer::ApplyCrosshairColorChanges()
|
|
{
|
|
char cmd[256];
|
|
cmd[0] = 0;
|
|
|
|
if (m_pCrosshairColorComboBox != NULL)
|
|
{
|
|
int val = m_pCrosshairColorComboBox->GetActiveItem();
|
|
Q_snprintf( cmd, sizeof(cmd), "cl_crosshaircolor %d\n", val );
|
|
engine->ClientCmd_Unrestricted( cmd );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Allow the res file to create controls on per-mod basis
|
|
//-----------------------------------------------------------------------------
|
|
Panel *COptionsSubMultiplayer::CreateControlByName( const char *controlName )
|
|
{
|
|
if( !Q_stricmp( "CCvarToggleCheckButton", controlName ) )
|
|
{
|
|
CCvarToggleCheckButton *newButton = new CCvarToggleCheckButton( this, controlName, "", "" );
|
|
m_cvarToggleCheckButtons.AddElement( newButton );
|
|
return newButton;
|
|
}
|
|
else
|
|
{
|
|
return BaseClass::CreateControlByName( controlName );
|
|
}
|
|
}
|
|
|