Made the Wiimote status go away in GC mode

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@1458 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
John Peterson 2008-12-09 14:57:55 +00:00
parent 5c75ba6177
commit 743a12f53e
11 changed files with 181 additions and 86 deletions

View File

@ -30,7 +30,8 @@
include\wx\setup.h ? include\wx\setup.h ?
include\wx\msw\setup.h ? include\wx\msw\setup.h ?
include\wx\univ\setup.h ? include\wx\univ\setup.h ?
include\wx\msw\setup0.h - The original univ file, not used include\wx\msw\setup0.h - The original msw file, not used
include\wx\univ\setup0.h - The original univ file, not used
The configuration consistency check files: The configuration consistency check files:

View File

@ -15,12 +15,13 @@
// Official SVN repository and contact information can be found at // Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/ // http://code.google.com/p/dolphin-emu/
#include "Common.h"
#include "Common.h" // Common
#include "StringUtil.h" #include "StringUtil.h"
#include "FileUtil.h" #include "FileUtil.h"
#include "MappedFile.h"
#include "../HLE/HLE.h" #include "../HLE/HLE.h" // Core
#include "../PowerPC/PowerPC.h" #include "../PowerPC/PowerPC.h"
#include "../PowerPC/PPCAnalyst.h" #include "../PowerPC/PPCAnalyst.h"
#include "../Core.h" #include "../Core.h"
@ -32,7 +33,7 @@
#include "../HW/VideoInterface.h" #include "../HW/VideoInterface.h"
#include "../HW/CPU.h" #include "../HW/CPU.h"
#include "../Debugger/Debugger_SymbolMap.h" #include "../Debugger/Debugger_SymbolMap.h" // Debugger
#include "../Debugger/Debugger_BreakPoints.h" #include "../Debugger/Debugger_BreakPoints.h"
#include "Boot_DOL.h" #include "Boot_DOL.h"
@ -42,11 +43,9 @@
#include "../PatchEngine.h" #include "../PatchEngine.h"
#include "../PowerPC/SignatureDB.h" #include "../PowerPC/SignatureDB.h"
#include "../PowerPC/SymbolDB.h" #include "../PowerPC/SymbolDB.h"
#include "../MemTools.h" #include "../MemTools.h"
#include "MappedFile.h"
#include "VolumeCreator.h" #include "VolumeCreator.h" // DiscIO
void CBoot::Load_FST(bool _bIsWii) void CBoot::Load_FST(bool _bIsWii)
{ {
@ -153,7 +152,7 @@ bool CBoot::BootUp(const SCoreStartupParameter& _StartupPara)
VideoInterface::PreInit(_StartupPara.bNTSC); VideoInterface::PreInit(_StartupPara.bNTSC);
switch (_StartupPara.m_BootType) switch (_StartupPara.m_BootType)
{ {
// GCM // GCM and Wii
// =================================================================================== // ===================================================================================
case SCoreStartupParameter::BOOT_ISO: case SCoreStartupParameter::BOOT_ISO:
{ {
@ -176,6 +175,7 @@ bool CBoot::BootUp(const SCoreStartupParameter& _StartupPara)
DVDInterface::SetDiscInside(true); DVDInterface::SetDiscInside(true);
// Use HLE BIOS or not
if (_StartupPara.bHLEBios) if (_StartupPara.bHLEBios)
{ {
if (!VolumeHandler::IsWii()) if (!VolumeHandler::IsWii())
@ -200,6 +200,9 @@ bool CBoot::BootUp(const SCoreStartupParameter& _StartupPara)
} }
} }
} }
/* Try to load the symbol map if there is one, and then scan it for
and eventually replace code */
if (LoadMapFromFilename(_StartupPara.m_strFilename, gameID)) if (LoadMapFromFilename(_StartupPara.m_strFilename, gameID))
HLE::PatchFunctions(); HLE::PatchFunctions();
} }

View File

@ -15,12 +15,15 @@
// Official SVN repository and contact information can be found at // Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/ // http://code.google.com/p/dolphin-emu/
#include "Common.h" #include "Common.h" // Common
#include "Boot/Boot.h"
#include "FileUtil.h" #include "FileUtil.h"
#include "StringUtil.h" #include "StringUtil.h"
#include "VolumeCreator.h" // DiscIO
#include "Boot/Boot.h" // Core
#include "CoreParameter.h" #include "CoreParameter.h"
#include "VolumeCreator.h" #include "Core.h" // for bWii
SCoreStartupParameter::SCoreStartupParameter() SCoreStartupParameter::SCoreStartupParameter()
{ {
@ -79,7 +82,9 @@ bool SCoreStartupParameter::AutoSetup(EBootBios _BootBios)
} }
m_strName = pVolume->GetName(); m_strName = pVolume->GetName();
m_strUniqueID = pVolume->GetUniqueID(); m_strUniqueID = pVolume->GetUniqueID();
bWii = DiscIO::IsVolumeWiiDisc(pVolume);
// Check if we have a Wii disc
bWii = DiscIO::IsVolumeWiiDisc(pVolume);
switch (pVolume->GetCountry()) switch (pVolume->GetCountry())
{ {
@ -146,7 +151,7 @@ bool SCoreStartupParameter::AutoSetup(EBootBios _BootBios)
break; break;
} }
// setup paths // Setup paths
m_strBios = FULL_GC_SYS_DIR + Region + DIR_SEP GC_IPL; m_strBios = FULL_GC_SYS_DIR + Region + DIR_SEP GC_IPL;
m_strMemoryCardA = FULL_GC_USER_DIR + Region + DIR_SEP GC_MEMCARDA; m_strMemoryCardA = FULL_GC_USER_DIR + Region + DIR_SEP GC_MEMCARDA;
m_strMemoryCardB = FULL_GC_USER_DIR + Region + DIR_SEP GC_MEMCARDB; m_strMemoryCardB = FULL_GC_USER_DIR + Region + DIR_SEP GC_MEMCARDB;

View File

@ -15,17 +15,18 @@
// Official SVN repository and contact information can be found at // Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/ // http://code.google.com/p/dolphin-emu/
#include "Common.h" #include "Common.h" // Common
#include "StringUtil.h" #include "StringUtil.h"
#include "WII_IPC_HLE_WiiMote.h"
#include "../../DolphinWX/Src/Frame.h" #include "WII_IPC_HLE_WiiMote.h" // Core
#include "WII_IPC_HLE_Device_usb.h"
#include "../Plugins/Plugin_Wiimote.h" #include "../Plugins/Plugin_Wiimote.h"
#include "../Host.h" #include "../Host.h"
#include "../Core.h"
#include "WII_IPC_HLE_Device_usb.h" #include "../../DolphinWX/Src/Frame.h" // DolphinWX, for the wiimote status
#include "l2cap.h" #include "l2cap.h" // Local
#include "WiiMote_HID_Attr.h" #include "WiiMote_HID_Attr.h"
extern CFrame* main_frame; // for the status report extern CFrame* main_frame; // for the status report
@ -386,6 +387,12 @@ void CWII_IPC_HLE_WiiMote::SendACLFrame(u8* _pData, u32 _Size)
// ---------------- // ----------------
void CWII_IPC_HLE_WiiMote::ShowStatus(const void* _pData) void CWII_IPC_HLE_WiiMote::ShowStatus(const void* _pData)
{ {
// Check if it's enabled
bool LedsOn = Core::g_CoreStartupParameter.bWiiLeds;
bool SpeakersOn = Core::g_CoreStartupParameter.bWiiSpeakers;
if(! (LedsOn || SpeakersOn) ) return;
const u8* data = (const u8*)_pData; const u8* data = (const u8*)_pData;
if(data[1] == 0x11 || data[1] == 0x14 || data[1] == 0x16 if(data[1] == 0x11 || data[1] == 0x14 || data[1] == 0x16
@ -397,7 +404,7 @@ void CWII_IPC_HLE_WiiMote::ShowStatus(const void* _pData)
// Get the last four bits with LED info // Get the last four bits with LED info
u8 Bits; u8 Bits;
if(data[1] == 0x11) if(data[1] == 0x11 && LedsOn)
{ {
Bits = (data[2] >> 4); Bits = (data[2] >> 4);
@ -410,7 +417,7 @@ void CWII_IPC_HLE_WiiMote::ShowStatus(const void* _pData)
main_frame->UpdateLeds(); main_frame->UpdateLeds();
} }
if(data[1] == 0x14) // Enable and disable speakers if(data[1] == 0x14 && SpeakersOn) // Enable and disable speakers
{ {
// Get the value // Get the value
if(data[2] == 0x02) Bits = 0; if(data[2] == 0x02) Bits = 0;
@ -419,7 +426,7 @@ void CWII_IPC_HLE_WiiMote::ShowStatus(const void* _pData)
main_frame->UpdateSpeakers(); main_frame->UpdateSpeakers();
} }
if(data[1] == 0x19) // Mute and unmute if(data[1] == 0x19 && SpeakersOn) // Mute and unmute
{ {
// Get the value // Get the value
if(data[2] == 0x02) Bits = 0; if(data[2] == 0x02) Bits = 0;
@ -428,7 +435,7 @@ void CWII_IPC_HLE_WiiMote::ShowStatus(const void* _pData)
main_frame->UpdateSpeakers(); main_frame->UpdateSpeakers();
} }
if(data[1] == 0x16) // Write to speaker registry if(data[1] == 0x16 && SpeakersOn) // Write to speaker registry
{ {
// Don't care what it does, call all activity // Don't care what it does, call all activity
main_frame->g_Speakers[2] = 1; main_frame->g_Speakers[2] = 1;
@ -439,6 +446,9 @@ void CWII_IPC_HLE_WiiMote::ShowStatus(const void* _pData)
// Turn off the activity icon again // Turn off the activity icon again
void CWII_IPC_HLE_WiiMote::UpdateStatus() void CWII_IPC_HLE_WiiMote::UpdateStatus()
{ {
// Check if it's enabled
if(!Core::g_CoreStartupParameter.bWiiSpeakers) return;
std::string Tmp = ArrayToString(main_frame->g_Speakers, sizeof(main_frame->g_Speakers)); std::string Tmp = ArrayToString(main_frame->g_Speakers, sizeof(main_frame->g_Speakers));
std::string Tmp2 = ArrayToString(main_frame->g_Speakers_, sizeof(main_frame->g_Speakers_)); std::string Tmp2 = ArrayToString(main_frame->g_Speakers_, sizeof(main_frame->g_Speakers_));
LOGV(CONSOLE, 0, "Tmp: %s", Tmp.c_str()); LOGV(CONSOLE, 0, "Tmp: %s", Tmp.c_str());

View File

@ -956,7 +956,7 @@ void CCodeWindow::DoTip(wxString text)
#endif #endif
} }
// See the comment under BEGIN_EVENT_TABLE for explanation of why we use these two
void CCodeWindow::OnStatusBar(wxMenuEvent& event) void CCodeWindow::OnStatusBar(wxMenuEvent& event)
{ {
DoTip(pMenuBar->GetHelpString(event.GetId())); DoTip(pMenuBar->GetHelpString(event.GetId()));
@ -965,7 +965,6 @@ void CCodeWindow::OnStatusBar_(wxUpdateUIEvent& event)
{ {
DoTip(pMenuBar->GetHelpString(event.GetId())); DoTip(pMenuBar->GetHelpString(event.GetId()));
} }
// =========== // ===========

View File

@ -15,6 +15,29 @@
// Official SVN repository and contact information can be found at // Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/ // http://code.google.com/p/dolphin-emu/
// =======================================================
// File description
// -------------
/* Purpose of this file: Collect boot settings for Core::Init()
Call sequenc: This file has one of the first function called when a game is booted,
the boot sequence in the code is:
DolphinWX: GameListCtrl.cpp OnActivated
BootManager.cpp BootCore
Core Core.cpp Init Thread creation
EmuThread Calls CBoot::BootUp
Boot.cpp
*/
// =============
#include <string> #include <string>
#include <vector> #include <vector>
@ -52,34 +75,35 @@ bool BootCore(const std::string& _rFilename)
{ {
SCoreStartupParameter StartUp = SConfig::GetInstance().m_LocalCoreStartupParameter; SCoreStartupParameter StartUp = SConfig::GetInstance().m_LocalCoreStartupParameter;
#if defined(HAVE_WX) && HAVE_WX // Use custom settings for debugging mode
if (g_pCodeWindow) #if defined(HAVE_WX) && HAVE_WX
{ if (g_pCodeWindow)
// StartUp.bUseDualCore = code_frame->UseDualCore(); {
StartUp.bUseJIT = !g_pCodeWindow->UseInterpreter(); // StartUp.bUseDualCore = code_frame->UseDualCore();
StartUp.bAutomaticStart = g_pCodeWindow->AutomaticStart(); StartUp.bUseJIT = !g_pCodeWindow->UseInterpreter();
} StartUp.bAutomaticStart = g_pCodeWindow->AutomaticStart();
else }
{ else
// StartUp.bUseDualCore = false; {
// StartUp.bUseJIT = true; // StartUp.bUseDualCore = false;
} // StartUp.bUseJIT = true;
#endif }
StartUp.bEnableDebugging = g_pCodeWindow ? true : false; // RUNNING_DEBUG
#endif
StartUp.m_BootType = SCoreStartupParameter::BOOT_ISO; StartUp.m_BootType = SCoreStartupParameter::BOOT_ISO;
StartUp.m_strFilename = _rFilename; StartUp.m_strFilename = _rFilename;
SConfig::GetInstance().m_LastFilename = StartUp.m_strFilename; SConfig::GetInstance().m_LastFilename = StartUp.m_strFilename;
StartUp.bRunCompareClient = false; StartUp.bRunCompareClient = false;
StartUp.bRunCompareServer = false; StartUp.bRunCompareServer = false;
#if defined(HAVE_WX) && HAVE_WX
StartUp.bEnableDebugging = g_pCodeWindow ? true : false; // RUNNING_DEBUG
#endif
std::string BaseDataPath; std::string BaseDataPath;
#ifdef _WIN32
StartUp.hInstance = wxGetInstance(); #ifdef _WIN32
#ifdef _M_X64 StartUp.hInstance = wxGetInstance();
StartUp.bUseFastMem = true; #ifdef _M_X64
#endif StartUp.bUseFastMem = true;
#endif #endif
#endif
if ( !StartUp.AutoSetup(SCoreStartupParameter::BOOT_DEFAULT) ) if ( !StartUp.AutoSetup(SCoreStartupParameter::BOOT_DEFAULT) )
{ {
@ -151,9 +175,20 @@ bool BootCore(const std::string& _rFilename)
// --------- // ---------
} }
// --------- // ---------
// Save some values to our local version of SCoreStartupParameter
SConfig::GetInstance().m_LocalCoreStartupParameter.bWii = StartUp.bWii;
SConfig::GetInstance().m_LocalCoreStartupParameter.bNTSC = StartUp.bNTSC;
#if defined(HAVE_WX) && HAVE_WX #if defined(HAVE_WX) && HAVE_WX
if(main_frame) if(main_frame)
{
StartUp.hMainWindow = main_frame->GetRenderHandle(); StartUp.hMainWindow = main_frame->GetRenderHandle();
// Now that we know if we have a Wii game we can run this
main_frame->ModifyStatusBar();
}
#endif #endif
// init the core // init the core
if (!Core::Init(StartUp)) if (!Core::Init(StartUp))

View File

@ -42,14 +42,14 @@ struct SConfig
// save settings // save settings
void SaveSettings(); void SaveSettings();
// load settings // load settings
void LoadSettings(); void LoadSettings();
/* Return the permanent and somewhat globally used instance of this struct
there is also a Core::GetStartupParameter() instance of it with almost
the same values */
static SConfig& GetInstance() {return(m_Instance);} static SConfig& GetInstance() {return(m_Instance);}
private: private:
// constructor // constructor

View File

@ -343,7 +343,7 @@ void CConfigMain::OnClose(wxCloseEvent& WXUNUSED (event))
SConfig::GetInstance().SaveSettings(); SConfig::GetInstance().SaveSettings();
// Update the status bar // Update the status bar
main_frame->ModifyStatusBar(WiiLeds->IsChecked(), WiiSpeakers->IsChecked()); main_frame->ModifyStatusBar();
} }
void CConfigMain::CloseClick(wxCommandEvent& WXUNUSED (event)) void CConfigMain::CloseClick(wxCommandEvent& WXUNUSED (event))

View File

@ -154,8 +154,8 @@ CFrame::CFrame(wxFrame* parent,
IconTemp.CopyFromBitmap(wxGetBitmapFromMemory(dolphin_png)); IconTemp.CopyFromBitmap(wxGetBitmapFromMemory(dolphin_png));
SetIcon(IconTemp); SetIcon(IconTemp);
// Give it a status line // Give it a status bar
CreateStatusBar_(); m_pStatusBar = CreateStatusBar();
CreateMenu(); CreateMenu();

View File

@ -23,8 +23,8 @@ class CFrame : public wxFrame
// -------------------------------- // --------------------------------
// Wiimote leds // Wiimote leds
// --------- // ---------
void CreateStatusBar_();
void ModifyStatusBar(bool LedsOn, bool SpeakerOn); void ModifyStatusBar();
void CreateDestroy(int Case); void CreateDestroy(int Case);
void CreateLeds(); void CreateSpeakers(); void CreateLeds(); void CreateSpeakers();
void UpdateLeds(); void UpdateSpeakers(); void UpdateLeds(); void UpdateSpeakers();

View File

@ -44,38 +44,77 @@ namespace WiimoteLeds
int SPEAKER_SIZE_X = 8; int SPEAKER_SIZE_X = 8;
int SPEAKER_SIZE_Y = 8; int SPEAKER_SIZE_Y = 8;
static const int WidthsOn[] = { -1, 100, 50 + LED_SIZE_X * 4 }; int ConnectionStatusWidth = 103;
int ConnectionStatusOnlyAdj = 7;
int RightmostMargin = 6;
int SpIconMargin = 11;
int LedIconMargin = 11;
// Leds only
static const int LdWidthsOn[] =
{
-1,
ConnectionStatusWidth,
(LedIconMargin + LED_SIZE_X) * 4 + RightmostMargin
};
static const int StylesFieldOn[] = { wxSB_NORMAL, wxSB_NORMAL, wxSB_NORMAL }; static const int StylesFieldOn[] = { wxSB_NORMAL, wxSB_NORMAL, wxSB_NORMAL };
static const int SpWidthsOn[] = { -1, 100, 40 + SPEAKER_SIZE_X * 3 }; // Speakers only
static const int SpWidthsOn[] =
{
-1,
ConnectionStatusWidth,
( SpIconMargin + SPEAKER_SIZE_X ) * 3 + RightmostMargin
};
static const int SpStylesFieldOn[] = { wxSB_NORMAL, wxSB_NORMAL, wxSB_NORMAL }; static const int SpStylesFieldOn[] = { wxSB_NORMAL, wxSB_NORMAL, wxSB_NORMAL };
static const int LdSpWidthsOn[] = { -1, 100, 32 + SPEAKER_SIZE_X * 3, 50 + LED_SIZE_X * 4 }; // Both
static const int LdSpWidthsOn[] =
{
-1,
ConnectionStatusWidth,
(SpIconMargin + SPEAKER_SIZE_X) * 3,
(LedIconMargin + LED_SIZE_X) * 4 + RightmostMargin
};
static const int LdSpStylesFieldOn[] = { wxSB_NORMAL, wxSB_NORMAL, wxSB_NORMAL }; static const int LdSpStylesFieldOn[] = { wxSB_NORMAL, wxSB_NORMAL, wxSB_NORMAL };
static const int WidthsOff[] = { -1, 50 + 100 }; // Only the Wiimote connection Status
static const int WidthsOff[] =
{
-1,
ConnectionStatusWidth + ConnectionStatusOnlyAdj + RightmostMargin
};
static const int StylesFieldOff[] = { wxSB_NORMAL, wxSB_NORMAL }; static const int StylesFieldOff[] = { wxSB_NORMAL, wxSB_NORMAL };
// GC mode
static const int WidthsOffGC[] = { -1 };
static const int StylesFieldOffGC[] = { wxSB_NORMAL };
}; };
// =======================================================
// Create status bar
// -------------
void CFrame::CreateStatusBar_()
{
// Get settings
bool LedsOn = SConfig::GetInstance().m_LocalCoreStartupParameter.bWiiLeds;
bool SpeakersOn = SConfig::GetInstance().m_LocalCoreStartupParameter.bWiiSpeakers;
m_pStatusBar = CreateStatusBar();
ModifyStatusBar(LedsOn, SpeakersOn);
}
// ======================================================= // =======================================================
// Modify status bar // Modify status bar
// ------------- // -------------
void CFrame::ModifyStatusBar(bool LedsOn, bool SpeakersOn) void CFrame::ModifyStatusBar()
{ {
// Get settings
bool LedsOn = SConfig::GetInstance().m_LocalCoreStartupParameter.bWiiLeds;
bool SpeakersOn = SConfig::GetInstance().m_LocalCoreStartupParameter.bWiiSpeakers;
// Don't use this for GC games, or before a game is loaded
if(!SConfig::GetInstance().m_LocalCoreStartupParameter.bWii)
{ LedsOn = false; SpeakersOn = false; }
/* For some reason the Debug build of wxWidgets can't use these bitmaps so it will
produce a "assert "wxWidgets Debug Alert, bmp.Ok()" failed" message and show
blank bitmaps. I check that wxUSE_STATBMP was enabled even for Debug builds so
I don't see why it wouldn't work. You can uncomment this if you find the bug.
In my case the same thing occured for CFrame::InitBitmaps() so it was not just
thse bitmaps that failed. So even with this comment you may get that warning. */
#ifdef _DEBUG
LedsOn = false; SpeakersOn = false;
#endif
// Declarations // Declarations
int Fields; int Fields;
int *Widths; int *Widths;
@ -87,7 +126,7 @@ void CFrame::ModifyStatusBar(bool LedsOn, bool SpeakersOn)
if(LedsOn && !SpeakersOn) if(LedsOn && !SpeakersOn)
{ {
Fields = 3; Fields = 3;
Widths = (int*)WiimoteLeds::WidthsOn; Widths = (int*)WiimoteLeds::LdWidthsOn;
StylesFields = (int*)WiimoteLeds::StylesFieldOn; StylesFields = (int*)WiimoteLeds::StylesFieldOn;
} }
// --------------------------------------- // ---------------------------------------
@ -116,6 +155,14 @@ void CFrame::ModifyStatusBar(bool LedsOn, bool SpeakersOn)
Fields = 2; Fields = 2;
Widths = (int*)WiimoteLeds::WidthsOff; Widths = (int*)WiimoteLeds::WidthsOff;
StylesFields = (int*)WiimoteLeds::StylesFieldOff; StylesFields = (int*)WiimoteLeds::StylesFieldOff;
// Maybe we should even go down to one field
if(!SConfig::GetInstance().m_LocalCoreStartupParameter.bWii)
{
Fields = 1;
Widths = (int*)WiimoteLeds::WidthsOffGC;
StylesFields = (int*)WiimoteLeds::StylesFieldOffGC;
}
} }
/* Destroy and create all, and check for HaveLeds and HaveSpeakers in case we have /* Destroy and create all, and check for HaveLeds and HaveSpeakers in case we have
@ -222,20 +269,15 @@ void CFrame::CreateSpeakers()
// Update icons // Update icons
void CFrame::UpdateSpeakers() void CFrame::UpdateSpeakers()
{ {
/*if(!SConfig::GetInstance().m_LocalCoreStartupParameter.bNTSC)
{ PanicAlert("Not NTSC");}
if(!SConfig::GetInstance().m_LocalCoreStartupParameter.bWii)
{ PanicAlert("Not Wii");}*/
for(int i = 0; i < 3; i++) for(int i = 0; i < 3; i++)
{ {
m_StatBmp[i+4]->SetBitmap(CreateBitmapForSpeakers(i, (bool)g_Speakers[i])); m_StatBmp[i+4]->SetBitmap(CreateBitmapForSpeakers(i, (bool)g_Speakers[i]));
} }
if(g_Leds[0] == 0)
{
// LOGV(CONSOLE, 0, "Break");
}
std::string Temp = ArrayToString(g_Speakers, sizeof(g_Speakers));
LOGV(CONSOLE, 0, "Speakers: %s", Temp.c_str());
Temp = ArrayToString(g_Leds, sizeof(g_Leds));
LOGV(CONSOLE, 0, "Leds: %s", Temp.c_str());
} }
// ============== // ==============