// // DirectXPage.xaml.cpp // Implementation of the DirectXPage class. // #include "pch.h" #include #include "DirectXPage.xaml.h" #include #include "EmulatorFileHandler.h" #include "SelectROMPane.xaml.h" #include "Database\ROMDatabase.h" #include "Definitions.h" #include "stringhelper.h" #include "App.xaml.h" #include "NavMenuItem.h" #include "NavMenuListView.h" #include "SelectROMPane.xaml.h" #include "SettingsPage.xaml.h" #include "HelpPage.xaml.h" #include "CheatPane.xaml.h" #include "ExportPage.xaml.h" #include "ImportPage.xaml.h" #include "PurchasePage.xaml.h" #include "BlankPage.xaml.h" 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; using namespace Windows::UI::Xaml::Media::Imaging; using namespace concurrency; using namespace Windows::Storage::Pickers; using namespace Windows::Storage; using namespace Windows::Storage::Streams; using namespace Windows::Storage::FileProperties; using namespace Windows::UI::ViewManagement; using namespace Windows::Graphics::Imaging; using namespace Windows::Globalization; //to get date time using namespace Windows::System::Display; using namespace Windows::ApplicationModel::Activation; using namespace Windows::Storage::AccessCache; using namespace Windows::Devices::HumanInterfaceDevice; using namespace Windows::Devices::Enumeration; using namespace std; using namespace VBA10; using namespace VBA10::Controls; DirectXPage^ DirectXPage::_current; 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(); } if (Windows::Foundation::Metadata::ApiInformation::IsTypePresent("Windows.Phone.UI.Input.HardwareButtons")) { //BackButton::Visibility = Visibility.Collapsed; Windows::Phone::UI::Input::HardwareButtons::BackPressed += ref new EventHandler(this, &DirectXPage::OnHardwareBackButtonPressed); } // Register event handlers for page lifecycle. CoreWindow^ window = Window::Current->CoreWindow; window->VisibilityChanged += ref new TypedEventHandler(this, &DirectXPage::OnVisibilityChanged); //we replace the pointer events by doing it on the UI thread Window::Current->CoreWindow->PointerPressed += ref new TypedEventHandler(this, &DirectXPage::OnPointerPressed); Window::Current->CoreWindow->PointerMoved += ref new TypedEventHandler(this, &DirectXPage::OnPointerMoved); Window::Current->CoreWindow->PointerReleased += ref new TypedEventHandler(this, &DirectXPage::OnPointerReleased); DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView(); currentDisplayInformation->DpiChanged += ref new TypedEventHandler(this, &DirectXPage::OnDpiChanged); currentDisplayInformation->OrientationChanged += ref new TypedEventHandler(this, &DirectXPage::OnOrientationChanged); DisplayInformation::DisplayContentsInvalidated += ref new TypedEventHandler(this, &DirectXPage::OnDisplayContentsInvalidated); swapChainPanel->CompositionScaleChanged += ref new TypedEventHandler(this, &DirectXPage::OnCompositionScaleChanged); swapChainPanel->SizeChanged += ref new SizeChangedEventHandler(this, &DirectXPage::OnSwapChainPanelSizeChanged); m_eventToken = CompositionTarget::Rendering::add(ref new EventHandler(this, &DirectXPage::OnRendering)); // At this point we have access to the device. // We can create the device-dependent resources. m_deviceResources = std::make_shared(); 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 // ); // // Register for pointer events, which will be raised on the background thread. // m_coreInput->PointerPressed += ref new TypedEventHandler(this, &DirectXPage::OnPointerPressed); // m_coreInput->PointerMoved += ref new TypedEventHandler(this, &DirectXPage::OnPointerMoved); // m_coreInput->PointerReleased += ref new TypedEventHandler(this, &DirectXPage::OnPointerReleased); // // Begin processing input messages as they're delivered. // m_coreInput->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessUntilQuit); //}); //App::ESettings->LinearFilterEnabled = true; //// Run task on a dedicated high priority background thread. //m_inputLoopWorker = ThreadPool::RunAsync(workItemHandler, WorkItemPriority::High, WorkItemOptions::TimeSliced); // Declare the top level nav items navlist = ref new Vector(); auto loader = Windows::ApplicationModel::Resources::ResourceLoader::GetForViewIndependentUse(); navlist->Append( ref new NavMenuItem( loader->GetString("HomeText"), Symbol::Home, TypeName(SelectROMPane::typeid))); navlist->Append( ref new NavMenuItem( loader->GetString("CheatsText"), Symbol::ReportHacked, TypeName(CheatPane::typeid))); navlist->Append( ref new NavMenuItem( loader->GetString("ImportText"), Symbol::Download, TypeName(ImportPage::typeid))); navlist->Append( ref new NavMenuItem( loader->GetString("ExportText"), Symbol::Upload, TypeName(ExportPage::typeid))); navlist->Append( ref new NavMenuItem( loader->GetString("PurchaseText"), Symbol::Shop, TypeName(PurchasePage::typeid))); navlist->Append( ref new NavMenuItem( loader->GetString("SettingsText"), Symbol::Setting, TypeName(SettingsPage::typeid))); navlist->Append( ref new NavMenuItem( loader->GetString("HelpText"), Symbol::Help, TypeName(HelpPage::typeid))); NavMenuList->ItemsSource = navlist; //set command position this->ChangeCommandPosition(); //load settings auto settings = ApplicationData::Current->LocalSettings->Values; //initalize main object for rendering m_main = std::unique_ptr(new VBA10Main(m_deviceResources)); //start rendering //DL: modified to not do it autmatically //m_main->StartRenderLoop(); // 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); CopyDemoROMAsync() .then([this] //NOTE: this let CopyDemonROM to run on UI thread { RootSplitView->IsPaneOpen = true; }, task_continuation_context::use_current()); //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()) .then([loader, this](DeviceInformationCollection^ collection) { //VID_045E = microsoft Vector^ HIDDeviceList = ref new Vector(); Vector^ deviceNames = ref new Vector(); int totalHIDDeviceNumber = 0; 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); } } if (HIDDeviceList->Size > 0) { //connect to the first known device return create_task(EventHandlerForDevice::Current->OpenDeviceAsync(HIDDeviceList->GetAt(0))) .then([this, HIDDeviceList, deviceNames](task 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([] {}); } else return create_task([] {}); }, task_continuation_context::use_current()); } 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) { } 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 DirectXPage::CopyDemoROMAsync(void) { auto settings = ApplicationData::Current->LocalSettings->Values; if (settings->HasKey("FIRSTSTART")) return concurrency::create_task([] {}); settings->Insert("FIRSTSTART", dynamic_cast(PropertyValue::CreateBoolean(false))); 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) { //this file->DisplayName has extension //copy rom from installed dir to local folder return file->CopyAsync(ApplicationData::Current->LocalFolder); }).then([this](StorageFile ^file) { #if _DEBUG Platform::String ^message = file->Path; wstring wstr(message->Begin(), message->End()); OutputDebugStringW(wstr.c_str()); #endif //add entry to database and rom list 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 entry->Folder = ApplicationData::Current->LocalFolder; //store pointer to folder 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 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); }).then([](task t) { try { t.get(); // .get() didn't throw, so we succeeded, } catch (Platform::Exception ^ex) { #if _DEBUG Platform::String ^message = ex->Message; wstring wstr(message->Begin(), message->End()); OutputDebugStringW(wstr.c_str()); #endif } }); } DirectXPage::~DirectXPage() { // Stop rendering and processing events on destruction. //m_main->StopRenderLoop(); m_coreInput->Dispatcher->StopProcessEvents(); } // Saves the current state of the app for suspend and terminate events. task DirectXPage::SaveInternalState(IPropertySet^ state) { critical_section::scoped_lock lock(m_main->GetCriticalSection()); m_deviceResources->Trim(); // Stop rendering when the app is suspended. //m_main->StopRenderLoop(); // Put code to save app state here. if (IsROMLoaded()) { return SaveBeforeStop(); } else return concurrency::create_task([] {}); } task 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; } }); } // 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(); } // 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(); if (RootSplitView->IsPaneOpen) //pause if menu is open m_main->emulator->Pause(); else m_main->emulator->Unpause(); m_main->emulator->HidInput->StartListening(); } else { //m_main->StopRenderLoop(); m_main->emulator->Pause(); m_main->emulator->HidInput->UnregisterFromInputReportEvent(); } } // 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();*/ } void DirectXPage::OnPointerPressed(CoreWindow ^window, PointerEventArgs ^args) { // When the pointer is pressed begin tracking the pointer movement. m_main->emulator->GetVirtualController()->PointerPressed(args->CurrentPoint); } void DirectXPage::OnPointerMoved(CoreWindow ^window, PointerEventArgs ^args) { // Update the pointer tracking code. m_main->emulator->GetVirtualController()->PointerMoved(args->CurrentPoint); } void DirectXPage::OnPointerReleased(CoreWindow ^window, PointerEventArgs ^args) { // Stop tracking pointer movement when the pointer is released. m_main->emulator->GetVirtualController()->PointerReleased(args->CurrentPoint); } 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(); } /// /// Navigate to the Page for the selected . /// /// /// void DirectXPage::NavMenuList_ItemInvoked(Object^ sender, ListViewItem^ listViewItem) { auto item = (NavMenuItem^)((NavMenuListView^)(sender))->ItemFromContainer(listViewItem); if (item != nullptr) { //if (item->DestPage.Name != AppFrame->CurrentSourcePageType.Name) { AppFrame->Navigate(item->DestPage, item->Arguments); } } } /// /// Enable accessibility on each nav menu item by setting the AutomationProperties.Name on each container /// using the associated Label of each item. /// /// /// void DirectXPage::NavMenuItemContainerContentChanging(ListViewBase^ sender, ContainerContentChangingEventArgs^ args) { if (!args->InRecycleQueue && args->Item != nullptr && dynamic_cast(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); } } /// /// 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. /// /// /// void DirectXPage::TogglePaneButton_UnChecked(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { //change splitview to overlay, so that it disappear //RootSplitView->DisplayMode = SplitViewDisplayMode::Overlay; //disable AppFrame so that it does not receive input accidentally AppFrame->IsEnabled = false; //navigate to a blank page to avoid ad control reload AppFrame->Navigate(TypeName(BlankPage::typeid)); //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; //unselect item //NavMenuList->SetSelectedItem(nullptr); //save and apply cheats if needed if (ShouldApplyNewCheats) { ApplyCheats(ROMCheats); SaveCheats(); } //CheckTogglePaneButtonSizeChanged(); //unpause emulator m_main->emulator->Unpause(); m_main->emulator->HidInput->StartListening(); //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 (...) {} } void DirectXPage::TogglePaneButton_Checked(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { //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(); //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(); } //enable app frame AppFrame->IsEnabled = true; //change width to 100%, NAN means auto AppFrame->Width = NAN; //show hamburber button this->TogglePaneButton->Visibility = Windows::UI::Xaml::Visibility::Visible; //navigate to the first item //GoToPage(0); int test = NavMenuList->SelectedIndex; if (!IsROMLoaded() || NavMenuList->SelectedIndex < 0) //always go to page 0 if no rom has been started GoToPage(0); else //reload current page GoToPage(NavMenuList->SelectedIndex); //even when ROM is loaded, we force the Pane to reload } void DirectXPage::GoToPage(int pageindex) { auto item = NavMenuList->ContainerFromItem(NavMenuList->Items->GetAt(pageindex)); NavMenuList->InvokeItem(item); } void DirectXPage::AppShell_KeyDown(Object^ sender, KeyRoutedEventArgs^ e) { if (e->Key == VirtualKey::Escape ) { RootSplitView->IsPaneOpen = !RootSplitView->IsPaneOpen; } m_main->emulator->GetVirtualController()->Reset(); } void DirectXPage::CloseMenu() { RootSplitView->IsPaneOpen = false; //this will toggle the hamburger menu because of the 2-way binding m_main->emulator->GetVirtualController()->Reset(); } void DirectXPage::LoadROM(ROMDBEntry^ entry) { CloseMenu(); loadedEntry = entry; //store loaded entry for later use SavestateSlot = entry->LastSaveIndex; if (IsROMLoaded() && entry->FolderPath + L"\\" + entry->FileName == ROMFile->Path) //don't have to do anything return; //NOTE: create task like this will put the task on the same thread (UI thread) //create_task(SaveBeforeStop()) //create task like this will put the task on background thread concurrency::create_task([this, entry] { 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); }).then([entry] { if (entry->AutoLoadLastState) return LoadStateAsync(AUTOSAVESTATE_SLOT); else return concurrency::create_task([] {}); }); //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(); //}); } void DirectXPage::SaveState() { SaveStateAsync().then([this] { CloseMenu(); }); } void DirectXPage::LoadState() { LoadStateAsync(SavestateSlot).then([this] { CloseMenu(); }); } void DirectXPage::Reset() { CloseMenu(); ResetSync(); } void DirectXPage::SelectSaveState(int slot) { loadedEntry->LastSaveIndex = slot; SelectSavestateSlot(slot); } 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 ^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 insp(reinterpret_cast(obj)); // Query the IBufferByteAccess interface. ComPtr 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 DirectXPage::SaveSnapshot() { //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 ^pixels = GetSnapshotBuffer(backbuffer, pitch, width, height); //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) { 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 t) { try { t.get(); } catch (COMException ^ex) { } }); } task DirectXPage::UpdateDBEntry() { return App::ROMDB->UpdateAsync(loadedEntry); } void DirectXPage::ImportRomFromFile(FileActivatedEventArgs^ args) { StorageFile^ file = (StorageFile^)(args->Files->GetAt(0)); //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> 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 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 t) { try { t.get(); } catch (...) { } }) );//end of tasks.emplace_back //auto tmpentry = make_shared(entry); //====write to database file tasks.emplace_back( concurrency::create_task(App::ROMDB->AddAsync(entry)) .then([](task t) { try { t.get(); } catch (...) { } }) ); //end of tasks.emplace_back } //when all finish when_all(begin(tasks), end(tasks)).then([this, entry](task t) { try { t.get(); LoadROM(entry); } catch (...) { // We'll handle the specific errors below. } }, task_continuation_context::use_current()); } void DirectXPage::EditButtonLayout() { this->m_main->emulator->EnterButtonEditMode(); CloseMenu(); this->panelEditButton->Visibility = Windows::UI::Xaml::Visibility::Visible; } 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; } 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(); }