Files
vba10/DirectXPage.xaml.cpp

1216 lines
34 KiB
C++
Raw Permalink Normal View History

2015-06-06 18:17:15 +00:00
//
// DirectXPage.xaml.cpp
// Implementation of the DirectXPage class.
//
#include "pch.h"
#include <robuffer.h>
2015-06-06 18:17:15 +00:00
#include "DirectXPage.xaml.h"
2015-06-06 21:45:18 +00:00
#include <ppltasks.h>
#include "EmulatorFileHandler.h"
#include "SelectROMPane.xaml.h"
2015-06-18 17:33:07 +00:00
#include "Database\ROMDatabase.h"
#include "Definitions.h"
#include "stringhelper.h"
#include "App.xaml.h"
2015-09-01 04:15:23 +00:00
2015-06-10 23:06:42 +00:00
#include "NavMenuItem.h"
#include "NavMenuListView.h"
#include "SelectROMPane.xaml.h"
2015-06-12 04:26:48 +00:00
#include "SettingsPage.xaml.h"
2015-06-12 20:34:55 +00:00
#include "HelpPage.xaml.h"
2015-06-14 23:02:59 +00:00
#include "CheatPane.xaml.h"
2015-06-18 17:33:07 +00:00
#include "ExportPage.xaml.h"
2015-06-19 14:24:15 +00:00
#include "ImportPage.xaml.h"
2015-07-29 19:41:41 +00:00
#include "PurchasePage.xaml.h"
2015-09-01 04:15:23 +00:00
#include "BlankPage.xaml.h"
2015-06-18 17:33:07 +00:00
2015-06-10 23:06:42 +00:00
2015-06-06 18:17:15 +00:00
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::Graphics::Display;
using namespace Windows::System::Threading;
using namespace Windows::UI::Core;
using namespace Windows::UI::Input;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Navigation;
2015-06-22 17:27:18 +00:00
using namespace Windows::UI::Xaml::Media::Imaging;
2015-06-06 18:17:15 +00:00
using namespace concurrency;
2015-06-06 21:45:18 +00:00
using namespace Windows::Storage::Pickers;
using namespace Windows::Storage;
using namespace Windows::Storage::Streams;
using namespace Windows::Storage::FileProperties;
using namespace Windows::UI::ViewManagement;
2015-06-22 17:27:18 +00:00
using namespace Windows::Graphics::Imaging;
using namespace Windows::Globalization; //to get date time
2015-08-24 13:01:32 +00:00
using namespace Windows::System::Display;
using namespace Windows::ApplicationModel::Activation;
using namespace Windows::Storage::AccessCache;
2015-10-09 13:48:56 +00:00
using namespace Windows::Devices::HumanInterfaceDevice;
using namespace Windows::Devices::Enumeration;
2015-07-30 04:29:00 +00:00
2015-06-18 17:33:07 +00:00
using namespace std;
using namespace VBA10;
using namespace VBA10::Controls;
2015-06-11 00:59:55 +00:00
2015-06-10 23:06:42 +00:00
DirectXPage^ DirectXPage::_current;
2015-06-06 18:17:15 +00:00
DirectXPage::DirectXPage():
m_windowVisible(true),
m_coreInput(nullptr)
{
InitializeComponent();
DirectXPage::_current = this;
//hide status bar on phone
if (Windows::Foundation::Metadata::ApiInformation::IsTypePresent("Windows.UI.ViewManagement.StatusBar"))
{
Windows::UI::ViewManagement::StatusBar::GetForCurrentView()->HideAsync();
}
2015-07-30 04:29:00 +00:00
if (Windows::Foundation::Metadata::ApiInformation::IsTypePresent("Windows.Phone.UI.Input.HardwareButtons"))
{
//BackButton::Visibility = Visibility.Collapsed;
Windows::Phone::UI::Input::HardwareButtons::BackPressed += ref new EventHandler<Windows::Phone::UI::Input::BackPressedEventArgs^>(this, &DirectXPage::OnHardwareBackButtonPressed);
}
2015-06-06 18:17:15 +00:00
// Register event handlers for page lifecycle.
CoreWindow^ window = Window::Current->CoreWindow;
window->VisibilityChanged +=
ref new TypedEventHandler<CoreWindow^, VisibilityChangedEventArgs^>(this, &DirectXPage::OnVisibilityChanged);
//we replace the pointer events by doing it on the UI thread
Window::Current->CoreWindow->PointerPressed +=
ref new TypedEventHandler<CoreWindow ^, PointerEventArgs ^>(this, &DirectXPage::OnPointerPressed);
Window::Current->CoreWindow->PointerMoved +=
ref new TypedEventHandler<CoreWindow ^, PointerEventArgs ^>(this, &DirectXPage::OnPointerMoved);
Window::Current->CoreWindow->PointerReleased +=
ref new TypedEventHandler<CoreWindow ^, PointerEventArgs ^>(this, &DirectXPage::OnPointerReleased);
2015-06-06 18:17:15 +00:00
DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView();
currentDisplayInformation->DpiChanged +=
ref new TypedEventHandler<DisplayInformation^, Object^>(this, &DirectXPage::OnDpiChanged);
currentDisplayInformation->OrientationChanged +=
ref new TypedEventHandler<DisplayInformation^, Object^>(this, &DirectXPage::OnOrientationChanged);
DisplayInformation::DisplayContentsInvalidated +=
ref new TypedEventHandler<DisplayInformation^, Object^>(this, &DirectXPage::OnDisplayContentsInvalidated);
swapChainPanel->CompositionScaleChanged +=
ref new TypedEventHandler<SwapChainPanel^, Object^>(this, &DirectXPage::OnCompositionScaleChanged);
swapChainPanel->SizeChanged +=
ref new SizeChangedEventHandler(this, &DirectXPage::OnSwapChainPanelSizeChanged);
m_eventToken = CompositionTarget::Rendering::add(ref new EventHandler<Object^>(this, &DirectXPage::OnRendering));
2015-06-06 18:17:15 +00:00
// At this point we have access to the device.
// We can create the device-dependent resources.
m_deviceResources = std::make_shared<DX::DeviceResources>();
m_deviceResources->SetSwapChainPanel(swapChainPanel);
// Register our SwapChainPanel to get independent input pointer events
//auto workItemHandler = ref new WorkItemHandler([this] (IAsyncAction ^)
//{
// // The CoreIndependentInputSource will raise pointer events for the specified device types on whichever thread it's created on.
// m_coreInput = swapChainPanel->CreateCoreIndependentInputSource(
// Windows::UI::Core::CoreInputDeviceTypes::Mouse |
// Windows::UI::Core::CoreInputDeviceTypes::Touch |
// Windows::UI::Core::CoreInputDeviceTypes::Pen
// );
2015-06-06 18:17:15 +00:00
// // Register for pointer events, which will be raised on the background thread.
// m_coreInput->PointerPressed += ref new TypedEventHandler<Object^, PointerEventArgs^>(this, &DirectXPage::OnPointerPressed);
// m_coreInput->PointerMoved += ref new TypedEventHandler<Object^, PointerEventArgs^>(this, &DirectXPage::OnPointerMoved);
// m_coreInput->PointerReleased += ref new TypedEventHandler<Object^, PointerEventArgs^>(this, &DirectXPage::OnPointerReleased);
2015-06-06 18:17:15 +00:00
// // Begin processing input messages as they're delivered.
// m_coreInput->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessUntilQuit);
//});
2015-06-06 18:17:15 +00:00
//App::ESettings->LinearFilterEnabled = true;
2015-07-08 05:52:03 +00:00
//// Run task on a dedicated high priority background thread.
//m_inputLoopWorker = ThreadPool::RunAsync(workItemHandler, WorkItemPriority::High, WorkItemOptions::TimeSliced);
2015-06-06 18:17:15 +00:00
2015-06-10 23:06:42 +00:00
// Declare the top level nav items
navlist = ref new Vector<NavMenuItem^>();
auto loader = Windows::ApplicationModel::Resources::ResourceLoader::GetForViewIndependentUse();
navlist->Append(
ref new NavMenuItem(
2015-09-25 05:18:39 +00:00
loader->GetString("HomeText"),
2015-06-12 04:26:48 +00:00
Symbol::Home,
TypeName(SelectROMPane::typeid)));
2015-06-14 23:02:59 +00:00
navlist->Append(
ref new NavMenuItem(
2015-09-25 05:18:39 +00:00
loader->GetString("CheatsText"),
2015-07-29 19:41:41 +00:00
Symbol::ReportHacked,
2015-06-14 23:02:59 +00:00
TypeName(CheatPane::typeid)));
2015-06-19 14:24:15 +00:00
navlist->Append(
ref new NavMenuItem(
2015-09-25 05:18:39 +00:00
loader->GetString("ImportText"),
2015-06-19 14:24:15 +00:00
Symbol::Download,
TypeName(ImportPage::typeid)));
2015-06-18 17:33:07 +00:00
navlist->Append(
ref new NavMenuItem(
2015-09-25 05:18:39 +00:00
loader->GetString("ExportText"),
2015-06-18 17:33:07 +00:00
Symbol::Upload,
TypeName(ExportPage::typeid)));
2015-07-29 19:41:41 +00:00
navlist->Append(
ref new NavMenuItem(
2015-09-25 05:18:39 +00:00
loader->GetString("PurchaseText"),
2015-07-29 19:41:41 +00:00
Symbol::Shop,
TypeName(PurchasePage::typeid)));
2015-06-10 23:06:42 +00:00
navlist->Append(
ref new NavMenuItem(
2015-09-25 05:18:39 +00:00
loader->GetString("SettingsText"),
2015-06-12 04:26:48 +00:00
Symbol::Setting,
TypeName(SettingsPage::typeid)));
2015-06-12 20:34:55 +00:00
navlist->Append(
ref new NavMenuItem(
2015-09-25 05:18:39 +00:00
loader->GetString("HelpText"),
2015-06-12 20:34:55 +00:00
Symbol::Help,
TypeName(HelpPage::typeid)));
2015-06-10 23:06:42 +00:00
NavMenuList->ItemsSource = navlist;
2015-09-27 05:11:09 +00:00
//set command position
this->ChangeCommandPosition();
//load settings
2015-06-06 21:45:18 +00:00
auto settings = ApplicationData::Current->LocalSettings->Values;
2015-06-14 17:41:27 +00:00
2015-06-06 21:45:18 +00:00
//initalize main object for rendering
2015-06-06 18:17:15 +00:00
m_main = std::unique_ptr<VBA10Main>(new VBA10Main(m_deviceResources));
//start rendering
//DL: modified to not do it autmatically
//m_main->StartRenderLoop();
2015-06-14 17:41:27 +00:00
// create_task([this] {return CopyDemoROMAsync();}).then([this] //NOTE: this will make CopyDemoROM to run on background thread
//and cause exception at entry->Snapshot->SetSourceAsync(stream);
//For some reason, the first call to check xbox controller status always return false, so we call it first here,
//then the next call in settings will return the true status.
XINPUT_STATE state;
ZeroMemory(&state,sizeof(XINPUT_STATE));
for (int i = 0; i <= 3; i++)
XInputGetState(i, &state);
2015-06-27 20:13:00 +00:00
CopyDemoROMAsync()
.then([this] //NOTE: this let CopyDemonROM to run on UI thread
2015-06-14 17:41:27 +00:00
{
RootSplitView->IsPaneOpen = true;
}, task_continuation_context::use_current());
2015-10-09 13:48:56 +00:00
//check hid gamepad connection
create_task(LoadHidConfig())
.then([loader, this] ()
{
auto deviceSelector = HidDevice::GetDeviceSelector(0x0001, 0x0005);
return DeviceInformation::FindAllAsync(deviceSelector);
}, task_continuation_context::use_current())
2015-10-09 13:48:56 +00:00
.then([loader, this](DeviceInformationCollection^ collection)
{
//VID_045E = microsoft
Vector<DeviceInformation^>^ HIDDeviceList = ref new Vector<DeviceInformation^>();
Vector<String^>^ deviceNames = ref new Vector<String^>();
int totalHIDDeviceNumber = 0;
2015-10-09 13:48:56 +00:00
for (int i = 0; i < collection->Size; i++)
{
DeviceInformation^ device = collection->GetAt(i);
//ignore microsoft xbox controller
wstring deviceid(device->Id->Begin(), device->Id->End());
if (deviceid.find(L"VID_045E") != string::npos)
continue;
totalHIDDeviceNumber++;
//only add known device to the list
if (hidConfigs->HasKey(device->Id))
{
HIDDeviceList->Append(device);
deviceNames->Append(device->Name);
}
2015-10-09 13:48:56 +00:00
}
if (HIDDeviceList->Size > 0)
{
//connect to the first known device
return create_task(EventHandlerForDevice::Current->OpenDeviceAsync(HIDDeviceList->GetAt(0)))
.then([this, HIDDeviceList, deviceNames](task<bool> openDeviceTask)
{
try
{
bool openSuccess = openDeviceTask.get();
auto loader = Windows::ApplicationModel::Resources::ResourceLoader::GetForViewIndependentUse();
if (openSuccess)
{
this->m_main->emulator->RestoreHidConfig();
Platform::String^ notification = EventHandlerForDevice::Current->DeviceInformation->Name + " " + loader->GetString("IsConnectedText");
if (!App::IsPremium)
notification += " " + loader->GetString("WatchVideoPrompt");
this->ShowNotification(notification);
}
else
{
this->ShowNotification( loader->GetString("FailedConnectToText") + " " + HIDDeviceList->GetAt(0)->Name);
}
}
catch (...) {}
}, task_continuation_context::use_current());
}
else if (totalHIDDeviceNumber > 0)
{
this->ShowNotification(loader->GetString("NewGamePadText"));
return create_task([] {});
2015-10-09 13:48:56 +00:00
}
else
return create_task([] {});
2015-10-09 13:48:56 +00:00
}, task_continuation_context::use_current());
2015-06-18 17:33:07 +00:00
2015-06-06 18:17:15 +00:00
}
2015-09-27 05:11:09 +00:00
void DirectXPage::ChangeCommandPosition()
{
//move position of hamburger button to bottom if settings say so
if ((EmulatorSettings::Current->CommandButtonPosition == 0 && Windows::Foundation::Metadata::ApiInformation::IsTypePresent("Windows.Phone.UI.Input.HardwareButtons"))
|| EmulatorSettings::Current->CommandButtonPosition == 2)
{
TogglePaneButton->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Bottom;
NavMenuList->Margin = Windows::UI::Xaml::Thickness(0, 0, 0, 48);
NavMenuList->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Bottom;
}
else
{
TogglePaneButton->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Top;
NavMenuList->Margin = Windows::UI::Xaml::Thickness(0, 48, 0, 0);
NavMenuList->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Top;
}
}
void DirectXPage::OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e)
{
}
2015-07-30 04:29:00 +00:00
void DirectXPage::OnHardwareBackButtonPressed(Platform::Object^ sender,
Windows::Phone::UI::Input::BackPressedEventArgs ^args )
{
if (RootSplitView->IsPaneOpen)
{
RootSplitView->IsPaneOpen = false;
args->Handled = true;
//args->Handled = false; //default behavior, i.e. close the app
}
else
{
// The game is currently in active play mode, so hitting the hardware back button
// will cause the game to pause.
RootSplitView->IsPaneOpen = true;
args->Handled = true;
}
}
task<void> DirectXPage::CopyDemoROMAsync(void)
2015-06-06 21:45:18 +00:00
{
auto settings = ApplicationData::Current->LocalSettings->Values;
if (settings->HasKey("FIRSTSTART"))
return concurrency::create_task([] {});
settings->Insert("FIRSTSTART", dynamic_cast<PropertyValue^>(PropertyValue::CreateBoolean(false)));
2015-06-06 21:45:18 +00:00
StorageFolder ^installDir = Windows::ApplicationModel::Package::Current->InstalledLocation;
return concurrency::create_task(installDir->GetFolderAsync("Assets/"))
.then([this](StorageFolder^ folder)
{
tmpfolder = folder;
//get the rom file
return tmpfolder->GetFileAsync("Bunny Advance (Demo).gba");
}).then([this](StorageFile ^file)
2015-06-06 21:45:18 +00:00
{
2015-06-22 17:27:18 +00:00
//this file->DisplayName has extension
//copy rom from installed dir to local folder
return file->CopyAsync(ApplicationData::Current->LocalFolder);
2015-06-06 21:45:18 +00:00
}).then([this](StorageFile ^file)
{
2015-06-22 17:27:18 +00:00
#if _DEBUG
2015-06-24 17:33:03 +00:00
Platform::String ^message = file->Path;
2015-06-22 17:27:18 +00:00
wstring wstr(message->Begin(), message->End());
OutputDebugStringW(wstr.c_str());
#endif
2015-06-24 17:33:03 +00:00
//add entry to database and rom list
2015-07-29 21:14:08 +00:00
ROMDBEntry^ entry = ref new ROMDBEntry(0, file->DisplayName, file->Name, ApplicationData::Current->LocalFolder->Path,
"none","Bunny Advance (Demo).jpg"); //snapshot just need the file name
2015-06-24 17:33:03 +00:00
entry->Folder = ApplicationData::Current->LocalFolder; //store pointer to folder
2015-08-22 16:59:37 +00:00
2015-06-22 17:27:18 +00:00
App::ROMDB->AllROMDBEntries->Append(entry);
return App::ROMDB->AddAsync(entry);
}).then([this]
{
//get the snapshot file
return tmpfolder->GetFileAsync("no_snapshot.png");
}).then([this](StorageFile ^file)
{
//copy snapshot file to would be rom location
2015-06-23 14:46:09 +00:00
return file->CopyAsync(ApplicationData::Current->LocalFolder, "Bunny Advance (Demo).jpg");
}).then([this](StorageFile ^file)
{
//open file
return file->OpenAsync(FileAccessMode::Read);
}).then([this](IRandomAccessStream^ stream)
{
//load bitmap image for snapshot
auto entry = App::ROMDB->AllROMDBEntries->GetAt(0);
entry->Snapshot = ref new BitmapImage();
return entry->Snapshot->SetSourceAsync(stream);
2015-06-06 21:45:18 +00:00
}).then([](task<void> t)
{
try
{
t.get();
// .get() didn't throw, so we succeeded,
2015-06-06 21:45:18 +00:00
}
catch (Platform::Exception ^ex)
{
#if _DEBUG
Platform::String ^message = ex->Message;
wstring wstr(message->Begin(), message->End());
OutputDebugStringW(wstr.c_str());
#endif
}
});
}
2015-06-06 18:17:15 +00:00
DirectXPage::~DirectXPage()
{
// Stop rendering and processing events on destruction.
//m_main->StopRenderLoop();
2015-06-06 18:17:15 +00:00
m_coreInput->Dispatcher->StopProcessEvents();
}
// Saves the current state of the app for suspend and terminate events.
task<void> DirectXPage::SaveInternalState(IPropertySet^ state)
2015-06-06 18:17:15 +00:00
{
critical_section::scoped_lock lock(m_main->GetCriticalSection());
m_deviceResources->Trim();
2015-06-24 16:45:34 +00:00
2015-06-06 18:17:15 +00:00
// Stop rendering when the app is suspended.
//m_main->StopRenderLoop();
2015-06-06 18:17:15 +00:00
// Put code to save app state here.
2015-06-24 16:45:34 +00:00
if (IsROMLoaded())
{
return SaveBeforeStop();
2015-06-24 16:45:34 +00:00
}
else
return concurrency::create_task([] {});
2015-06-06 18:17:15 +00:00
}
task<void> DirectXPage::SaveBeforeStop()
{
return concurrency::create_task([this] {
//move saving stuff from StopROMAsync over here + add save snapshot
if (IsROMLoaded())
{
m_main->emulator->Pause();
//update last played time
Calendar^ calendar = ref new Calendar();
calendar->SetToNow();
loadedEntry->LastPlayed = calendar->GetDateTime();
//save stuff
SaveSnapshot().wait();
UpdateDBEntry().wait();
SaveSRAMAsync().wait();
int oldstate = SavestateSlot;
SavestateSlot = AUTOSAVESTATE_SLOT;
SaveStateAsync().wait();
SavestateSlot = oldstate;
}
});
}
2015-06-06 18:17:15 +00:00
// Loads the current state of the app for resume events.
void DirectXPage::LoadInternalState(IPropertySet^ state)
{
// Put code to load app state here.
// Start rendering when the app is resumed.
//m_main->StartRenderLoop();
2015-06-06 18:17:15 +00:00
}
// Window event handlers.
void DirectXPage::OnVisibilityChanged(CoreWindow^ sender, VisibilityChangedEventArgs^ args)
{
m_windowVisible = args->Visible;
if (m_windowVisible)
{
//need code to pause game here
//m_main->StartRenderLoop();
2015-08-02 03:22:41 +00:00
if (RootSplitView->IsPaneOpen) //pause if menu is open
m_main->emulator->Pause();
else
m_main->emulator->Unpause();
m_main->emulator->HidInput->StartListening();
2015-06-06 18:17:15 +00:00
}
else
{
//m_main->StopRenderLoop();
m_main->emulator->Pause();
m_main->emulator->HidInput->UnregisterFromInputReportEvent();
2015-06-06 18:17:15 +00:00
}
}
// DisplayInformation event handlers.
void DirectXPage::OnDpiChanged(DisplayInformation^ sender, Object^ args)
{
critical_section::scoped_lock lock(m_main->GetCriticalSection());
m_deviceResources->SetDpi(sender->LogicalDpi);
m_main->CreateWindowSizeDependentResources();
}
void DirectXPage::OnOrientationChanged(DisplayInformation^ sender, Object^ args)
{
critical_section::scoped_lock lock(m_main->GetCriticalSection());
m_deviceResources->SetCurrentOrientation(sender->CurrentOrientation);
m_main->CreateWindowSizeDependentResources();
}
void DirectXPage::OnDisplayContentsInvalidated(DisplayInformation^ sender, Object^ args)
{
critical_section::scoped_lock lock(m_main->GetCriticalSection());
m_deviceResources->ValidateDevice();
}
void DirectXPage::OnRendering(Object^ sender, Object^ args)
{
//this->manager->SingleUpdate(this->emulator);
m_main->Update();
m_main->Render();
m_main->Present();
/*this->emulator->Update();
this->emulator->Draw();*/
}
2015-06-06 18:17:15 +00:00
void DirectXPage::OnPointerPressed(CoreWindow ^window, PointerEventArgs ^args)
2015-06-06 18:17:15 +00:00
{
// When the pointer is pressed begin tracking the pointer movement.
m_main->emulator->GetVirtualController()->PointerPressed(args->CurrentPoint);
2015-06-06 18:17:15 +00:00
}
void DirectXPage::OnPointerMoved(CoreWindow ^window, PointerEventArgs ^args)
2015-06-06 18:17:15 +00:00
{
// Update the pointer tracking code.
m_main->emulator->GetVirtualController()->PointerMoved(args->CurrentPoint);
2015-06-06 18:17:15 +00:00
}
void DirectXPage::OnPointerReleased(CoreWindow ^window, PointerEventArgs ^args)
2015-06-06 18:17:15 +00:00
{
// Stop tracking pointer movement when the pointer is released.
m_main->emulator->GetVirtualController()->PointerReleased(args->CurrentPoint);
2015-06-06 18:17:15 +00:00
}
void DirectXPage::OnCompositionScaleChanged(SwapChainPanel^ sender, Object^ args)
{
critical_section::scoped_lock lock(m_main->GetCriticalSection());
m_deviceResources->SetCompositionScale(sender->CompositionScaleX, sender->CompositionScaleY);
m_main->CreateWindowSizeDependentResources();
}
void DirectXPage::OnSwapChainPanelSizeChanged(Object^ sender, SizeChangedEventArgs^ e)
{
critical_section::scoped_lock lock(m_main->GetCriticalSection());
m_deviceResources->SetLogicalSize(e->NewSize);
m_main->CreateWindowSizeDependentResources();
}
2015-06-06 21:45:18 +00:00
2015-06-10 23:06:42 +00:00
/// <summary>
/// Navigate to the Page for the selected <paramref name="listViewItem"/>.
/// </summary>
/// <param name="sender"></param>
/// <param name="listViewItem"></param>
void DirectXPage::NavMenuList_ItemInvoked(Object^ sender, ListViewItem^ listViewItem)
{
auto item = (NavMenuItem^)((NavMenuListView^)(sender))->ItemFromContainer(listViewItem);
if (item != nullptr)
{
2015-06-11 00:59:55 +00:00
//if (item->DestPage.Name != AppFrame->CurrentSourcePageType.Name)
2015-06-10 23:06:42 +00:00
{
AppFrame->Navigate(item->DestPage, item->Arguments);
}
}
}
2015-06-10 23:06:42 +00:00
2015-06-10 23:06:42 +00:00
/// <summary>
/// Enable accessibility on each nav menu item by setting the AutomationProperties.Name on each container
/// using the associated Label of each item.
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
void DirectXPage::NavMenuItemContainerContentChanging(ListViewBase^ sender, ContainerContentChangingEventArgs^ args)
{
if (!args->InRecycleQueue && args->Item != nullptr && dynamic_cast<NavMenuItem^>(args->Item) != nullptr)
{
args->ItemContainer->SetValue(Windows::UI::Xaml::Automation::AutomationProperties::NameProperty, ((NavMenuItem^)args->Item)->Label);
}
else
{
args->ItemContainer->ClearValue(Windows::UI::Xaml::Automation::AutomationProperties::NameProperty);
}
}
2015-06-11 00:59:55 +00:00
2015-06-12 20:34:55 +00:00
/// <summary>
/// Callback when the SplitView's Pane is toggled close. When the Pane is not visible
/// then the floating hamburger may be occluding other content in the app unless it is aware.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void DirectXPage::TogglePaneButton_UnChecked(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
//change splitview to overlay, so that it disappear
//RootSplitView->DisplayMode = SplitViewDisplayMode::Overlay;
2015-06-11 00:59:55 +00:00
2015-06-14 21:50:53 +00:00
//disable AppFrame so that it does not receive input accidentally
AppFrame->IsEnabled = false;
2015-09-01 04:15:23 +00:00
//navigate to a blank page to avoid ad control reload
AppFrame->Navigate(TypeName(BlankPage::typeid));
2015-06-12 20:34:55 +00:00
//change the size of app frame to zero to hide content
AppFrame->Width = 0.0f;
//hide the hamburger button
if (EmulatorSettings::Current->HideHamburger)
this->TogglePaneButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
2015-09-01 04:15:23 +00:00
2015-06-12 20:34:55 +00:00
//unselect item
//NavMenuList->SetSelectedItem(nullptr);
2015-06-12 20:34:55 +00:00
//save and apply cheats if needed
if (ShouldApplyNewCheats)
{
ApplyCheats(ROMCheats);
SaveCheats();
}
2015-07-12 04:06:18 +00:00
//CheckTogglePaneButtonSizeChanged();
2015-06-12 20:34:55 +00:00
//unpause emulator
m_main->emulator->Unpause();
m_main->emulator->HidInput->StartListening();
2015-08-24 13:01:32 +00:00
//prevent screen lock when game is playing
try
{
if (g_DisplayRequest == nullptr) {
// This call creates an instance of the displayRequest object
g_DisplayRequest = ref new DisplayRequest();
}
if (g_DisplayRequest != nullptr)
{
// This call activates a display-required request. If successful,
// the screen is guaranteed not to turn off automatically due to user inactivity.
g_DisplayRequest->RequestActive();
}
}
catch (...) {}
2015-06-12 20:34:55 +00:00
}
2015-06-11 00:59:55 +00:00
void DirectXPage::TogglePaneButton_Checked(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
2015-08-24 13:01:32 +00:00
//enable screen lock again
try
{
if (g_DisplayRequest != nullptr)
{
// This call activates a display-required request. If successful,
// the screen is guaranteed not to turn off automatically due to user inactivity.
g_DisplayRequest->RequestRelease();
}
}
catch (...) {}
m_main->emulator->HidInput->StopListening();
2015-06-12 04:26:48 +00:00
//pause emulator
m_main->emulator->Pause();
//change splitview to compact overlay and open pane
//RootSplitView->DisplayMode = SplitViewDisplayMode::CompactOverlay;
//RootSplitView->IsPaneOpen = true;
if (IsROMLoaded())
{
//calculate snapshot name
Platform::String ^file_path = ROMFile->Path;
wstring wfilepath(file_path->Begin(), file_path->End());
wstring folderpath;
wstring filename;
wstring filenamenoext;
wstring ext;
splitFilePath(wfilepath, folderpath, filename, filenamenoext, ext);
wstring snapshotpath = folderpath + L"\\" + filenamenoext + L".jpg";
Platform::String^ psnapshotname = ref new Platform::String(snapshotpath.c_str());
// new snapshot
loadedEntry->Snapshot = TakeSnapshot();
//update last played time
Calendar^ calendar = ref new Calendar();
calendar->SetToNow();
loadedEntry->LastPlayed = calendar->GetDateTime();
}
2015-06-14 21:50:53 +00:00
//enable app frame
AppFrame->IsEnabled = true;
2015-06-12 04:26:48 +00:00
//change width to 100%, NAN means auto
AppFrame->Width = NAN;
//show hamburber button
this->TogglePaneButton->Visibility = Windows::UI::Xaml::Visibility::Visible;
2015-06-22 17:27:18 +00:00
2015-08-02 03:22:41 +00:00
2015-06-11 00:59:55 +00:00
//navigate to the first item
2015-07-12 04:06:18 +00:00
//GoToPage(0);
int test = NavMenuList->SelectedIndex;
2015-08-28 14:19:58 +00:00
if (!IsROMLoaded() || NavMenuList->SelectedIndex < 0) //always go to page 0 if no rom has been started
2015-07-12 04:06:18 +00:00
GoToPage(0);
2015-08-03 17:00:09 +00:00
else //reload current page
GoToPage(NavMenuList->SelectedIndex); //even when ROM is loaded, we force the Pane to reload
2015-06-11 00:59:55 +00:00
2015-06-11 00:59:55 +00:00
}
2015-06-19 14:24:15 +00:00
void DirectXPage::GoToPage(int pageindex)
{
auto item = NavMenuList->ContainerFromItem(NavMenuList->Items->GetAt(pageindex));
NavMenuList->InvokeItem(item);
}
2015-06-12 20:34:55 +00:00
void DirectXPage::AppShell_KeyDown(Object^ sender, KeyRoutedEventArgs^ e)
{
if (e->Key == VirtualKey::Escape )
{
RootSplitView->IsPaneOpen = !RootSplitView->IsPaneOpen;
}
2015-06-13 19:11:53 +00:00
m_main->emulator->GetVirtualController()->Reset();
2015-06-12 20:34:55 +00:00
2015-06-13 19:11:53 +00:00
}
2015-06-12 20:34:55 +00:00
2015-06-13 19:11:53 +00:00
void DirectXPage::CloseMenu()
{
2015-06-13 19:11:53 +00:00
RootSplitView->IsPaneOpen = false; //this will toggle the hamburger menu because of the 2-way binding
m_main->emulator->GetVirtualController()->Reset();
2015-06-12 20:34:55 +00:00
}
void DirectXPage::LoadROM(ROMDBEntry^ entry)
{
2015-06-13 19:11:53 +00:00
CloseMenu();
loadedEntry = entry; //store loaded entry for later use
SavestateSlot = entry->LastSaveIndex;
2015-06-24 16:45:34 +00:00
2015-07-29 21:14:08 +00:00
if (IsROMLoaded() && entry->FolderPath + L"\\" + entry->FileName == ROMFile->Path) //don't have to do anything
2015-06-24 16:45:34 +00:00
return;
2015-08-04 13:47:05 +00:00
//NOTE: create task like this will put the task on the same thread (UI thread)
//create_task(SaveBeforeStop())
2015-08-04 13:47:05 +00:00
//create task like this will put the task on background thread
concurrency::create_task([this, entry] {
2015-07-29 21:14:08 +00:00
if (IsROMLoaded() && entry->FolderPath + L"\\" + entry->FileName != ROMFile->Path) //different rom, save old rom state
{
return SaveBeforeStop();
}
else
return concurrency::create_task([] {});
}).then([entry] {
return entry->Folder->GetFileAsync(entry->FileName);
}).then([entry] (StorageFile^ file){
return LoadROMAsync(file, entry->Folder);
2015-07-15 05:02:33 +00:00
}).then([entry] {
if (entry->AutoLoadLastState)
return LoadStateAsync(AUTOSAVESTATE_SLOT);
else
return concurrency::create_task([] {});
});
2015-06-13 19:11:53 +00:00
//this is OK after we fixed the ParseVBAiniAsync so that it does not branch to another thread but it makes the UI unreponsive
//LoadROMAsync(file, folder).then([this]
//{
// CloseMenu();
//});
2015-06-12 20:34:55 +00:00
}
2015-06-24 16:45:34 +00:00
2015-06-12 20:34:55 +00:00
void DirectXPage::SaveState()
{
2015-06-13 19:11:53 +00:00
SaveStateAsync().then([this]
{
CloseMenu();
});
}
void DirectXPage::LoadState()
{
2015-06-24 16:45:34 +00:00
LoadStateAsync(SavestateSlot).then([this]
2015-06-13 19:11:53 +00:00
{
CloseMenu();
});
}
void DirectXPage::Reset()
{
CloseMenu();
ResetSync();
}
2015-06-14 17:41:27 +00:00
void DirectXPage::SelectSaveState(int slot)
{
loadedEntry->LastSaveIndex = slot;
2015-06-14 17:41:27 +00:00
SelectSavestateSlot(slot);
}
2015-06-22 17:27:18 +00:00
BitmapSource^ DirectXPage::TakeSnapshot()
{
//get the pixel information from buffer
unsigned char *backbuffer;
size_t pitch;
int width, height;
this->m_main->renderer->GetBackbufferData(&backbuffer, &pitch, &width, &height);
Platform::Array<unsigned char> ^pixels = GetSnapshotBuffer(backbuffer, pitch, width, height);
WriteableBitmap^ bitmap = ref new WriteableBitmap(width, height);
// Cast to Object^, then to its underlying IInspectable interface.
Object^ obj = bitmap->PixelBuffer;
ComPtr<IInspectable> insp(reinterpret_cast<IInspectable*>(obj));
// Query the IBufferByteAccess interface.
ComPtr<IBufferByteAccess> bufferByteAccess;
insp.As(&bufferByteAccess);
// Retrieve the buffer data.
byte* pPixels = nullptr;
bufferByteAccess->Buffer(&pPixels);
//not pixels store image in RGBA format while pPixels store image in BGRA format
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
pPixels[(x + y * width) * 4] = pixels[(x + y * width) * 4 + 2]; // B
pPixels[(x + y * width) * 4 + 1] = pixels[(x + y * width) * 4 + 1]; // G
pPixels[(x + y * width) * 4 + 2] = pixels[(x + y * width) * 4 ]; // R
pPixels[(x + y * width) * 4 + 3] = pixels[(x + y * width) * 4 + 3]; // A
}
}
return bitmap;
}
task<void> DirectXPage::SaveSnapshot()
2015-06-22 17:27:18 +00:00
{
//get the pixel information from buffer
unsigned char *backbuffer;
size_t pitch;
int width, height;
this->m_main->renderer->GetBackbufferData(&backbuffer, &pitch, &width, &height);
Platform::Array<unsigned char> ^pixels = GetSnapshotBuffer(backbuffer, pitch, width, height);
2015-06-22 17:27:18 +00:00
//calculate snapshot name
Platform::String ^file_path = ROMFile->Path;
wstring wfilepath(file_path->Begin(), file_path->End());
wstring folderpath;
wstring filename;
wstring filenamenoext;
wstring ext;
splitFilePath(wfilepath, folderpath, filename, filenamenoext, ext);
wstring snapshotname = filenamenoext + L".jpg";
Platform::String^ psnapshotname = ref new Platform::String(snapshotname.c_str());
return concurrency::create_task(ROMFolder->CreateFileAsync(psnapshotname, CreationCollisionOption::OpenIfExists)
).then([this, width, height, pixels](StorageFile ^file)
2015-06-22 17:27:18 +00:00
{
tmpfile = file;
return file->OpenAsync(FileAccessMode::ReadWrite);
}).then([this, width, height, pixels](IRandomAccessStream^ stream)
{
return BitmapEncoder::CreateAsync(BitmapEncoder::JpegEncoderId, stream);
}).then([this, width, height, pixels](BitmapEncoder^ encoder)
{
encoder->SetPixelData(BitmapPixelFormat::Rgba8, BitmapAlphaMode::Ignore, width, height, 72.0f, 72.0f, pixels);
return encoder->FlushAsync();
}).then([](task<void> t)
{
try
{
t.get();
}
catch (COMException ^ex)
{
}
});
}
task<void> DirectXPage::UpdateDBEntry()
{
return App::ROMDB->UpdateAsync(loadedEntry);
}
2015-06-22 17:27:18 +00:00
void DirectXPage::ImportRomFromFile(FileActivatedEventArgs^ args)
{
StorageFile^ file = (StorageFile^)(args->Files->GetAt(0));
2015-08-06 14:24:47 +00:00
//calculate folder path and snapshot name
Platform::String ^file_path = file->Path;
wstring wfilepath(file_path->Begin(), file_path->End());
wstring folderpath;
wstring filename;
wstring filenamenoext;
wstring ext;
splitFilePath(wfilepath, folderpath, filename, filenamenoext, ext);
wstring snapshotname = filenamenoext + L".jpg";
Platform::String^ psnapshotname = ref new Platform::String(snapshotname.c_str());
Platform::String^ pfilenamenoext = ref new Platform::String(filenamenoext.c_str());
Platform::String^ pfolderpath = ref new Platform::String(folderpath.c_str());
//calculate token
replace(folderpath.begin(), folderpath.end(), ':', '_');
replace(folderpath.begin(), folderpath.end(), '/', '_');
replace(folderpath.begin(), folderpath.end(), '\\', '_');
Platform::String^ ptoken = ref new Platform::String(folderpath.c_str());
//add folder to future accesslist
int locationType = 0; //default to private storage
if (StorageApplicationPermissions::FutureAccessList->ContainsItem(ptoken))
locationType = 1; //set to user accessible storage
//check to see if this file is already in the database
bool exist = false;
ROMDBEntry^ entry = nullptr;
for (int j = 0; j < App::ROMDB->AllROMDBEntries->Size; j++)
{
entry = App::ROMDB->AllROMDBEntries->GetAt(j);
if ( (locationType == 0 && entry->FileName == file->Name)
|| (locationType == 1 && entry->FileName == file->Name && entry->Token == ptoken))
{
exist = true;
break;
}
}
//=====create rom entry
if (!exist)
{
if (locationType == 0)
entry = ref new ROMDBEntry(locationType, pfilenamenoext, file->Name, ApplicationData::Current->LocalFolder->Path, "none", psnapshotname);
else
entry = ref new ROMDBEntry(locationType, pfilenamenoext, file->Name, pfolderpath, ptoken, psnapshotname);
App::ROMDB->AllROMDBEntries->Append(entry);
}
vector<task<void>> tasks;
//copy rom file over if we store in private storage (no matter if rom already exists)
if (locationType == 0)
tasks.emplace_back(
concurrency::create_task(file->CopyAsync(ApplicationData::Current->LocalFolder, file->Name, NameCollisionOption::ReplaceExisting))
.then([](task<StorageFile^> t)
{
try
{
t.get();
}
catch (...)
{
}
}));
if (!exist)
{
//=====copy snapshot over
tasks.emplace_back(
concurrency::create_task([locationType, ptoken, entry]
{
if (locationType == 0)
{
entry->Folder = ApplicationData::Current->LocalFolder;
return create_task([] {});
}
else
{
return concurrency::create_task([ptoken]() {
return StorageApplicationPermissions::FutureAccessList->GetFolderAsync(ptoken);
}).then([entry](StorageFolder^ folder) {
entry->Folder = folder;
});
}
}).then([entry] ()
{
StorageFolder ^installDir = Windows::ApplicationModel::Package::Current->InstalledLocation;
return installDir->GetFolderAsync("Assets/");
}, task_continuation_context::use_current()).then([entry](StorageFolder^ assetFolder)
{
return assetFolder->GetFileAsync("no_snapshot.png");
}).then([entry](StorageFile ^file)
{
//copy snapshot file to would be location
return file->CopyAsync(entry->Folder, entry->SnapshotUri, NameCollisionOption::ReplaceExisting);
}).then([entry](StorageFile ^file)
{
//open file
return file->OpenAsync(FileAccessMode::Read);
}).then([entry](IRandomAccessStream^ stream)
{
//load bitmap image for snapshot
entry->Snapshot = ref new BitmapImage();
return entry->Snapshot->SetSourceAsync(stream);
}).then([](task<void> t)
{
try
{
t.get();
}
catch (...)
{
}
})
);//end of tasks.emplace_back
//auto tmpentry = make_shared<ROMDBEntry^>(entry);
//====write to database file
tasks.emplace_back( concurrency::create_task(App::ROMDB->AddAsync(entry))
.then([](task<void> t)
{
try
{
t.get();
}
catch (...)
{
}
})
); //end of tasks.emplace_back
}
//when all finish
when_all(begin(tasks), end(tasks)).then([this, entry](task<void> t)
{
try
{
t.get();
LoadROM(entry);
}
catch (...)
{
// We'll handle the specific errors below.
}
}, task_continuation_context::use_current());
}
2015-09-01 04:15:23 +00:00
void DirectXPage::EditButtonLayout()
{
this->m_main->emulator->EnterButtonEditMode();
CloseMenu();
this->panelEditButton->Visibility = Windows::UI::Xaml::Visibility::Visible;
2015-09-01 04:15:23 +00:00
}
void DirectXPage::CancelPositionBtn_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
this->m_main->emulator->LeaveButtonEditMode(false);
this->panelEditButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
}
void DirectXPage::AcceptPositionBtn_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
this->m_main->emulator->LeaveButtonEditMode(true);
this->panelEditButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
}
2015-10-09 13:48:56 +00:00
void DirectXPage::ShowNotification(Platform::String^ notificationText)
{
this->txtNotification->Text = notificationText;
//this->notificationPanel->Visibility = Windows::UI::Xaml::Visibility::Collapsed;
this->notificationEntranceExit->Begin();
//DispatcherTimer dispatcherTimer = ref new DispatcherTimer();
}