// // AVI変換ダイアログクラス // // #define WIN32_LEAN_AND_MEAN #include #include #include #include using namespace std; #include "VirtuaNESres.h" #include "DebugOut.h" #include "App.h" #include "Config.h" #include "Pathlib.h" #include "Wnd.h" #include "AviConvDlg.h" #include "nes.h" #include "mmu.h" #include "ppu.h" #include "apu.h" #include "rom.h" #include "DirectDraw.h" CHAR CAviConvDlg::g_szErrorMessage[256]; DLG_MESSAGE_BEGIN(CAviConvDlg) DLG_ON_MESSAGE( WM_INITDIALOG, OnInitDialog ) DLG_ON_MESSAGE( WM_TIMER, OnTimer ) DLG_ON_MESSAGE( WM_APP+100, OnConvertDone ) DLG_ON_MESSAGE( WM_APP+101, OnConvertError ) DLG_COMMAND_BEGIN() DLG_ON_COMMAND( IDCANCEL, OnCancel ) DLG_ON_COMMAND( IDC_MCV_ROMBROWSE, OnRomFileBrowse ) DLG_ON_COMMAND( IDC_MCV_MOVIEBROWSE, OnMovieFileBrowse ) DLG_ON_COMMAND( IDC_MCV_AVIBROWSE, OnAviFileBrowse ) DLG_ON_COMMAND( IDC_MCV_VIDEOSELECT, OnVideoCompressSelect ) DLG_ON_COMMAND( IDC_MCV_CONVERT, OnConvertStart ) DLG_COMMAND_END() DLG_MESSAGE_END() CAviConvDlg::CAviConvDlg() : m_lpRender(NULL), m_lpLinecolor(NULL), m_lpScreen(NULL), m_lpSound(NULL), m_pNes(NULL), m_hThread(NULL) { if( !(m_lpRender = (LPBYTE)::malloc( CDirectDraw::RENDER_WIDTH*CDirectDraw::RENDER_HEIGHT )) ) { throw CApp::GetErrorString( IDS_ERROR_OUTOFMEMORY ); } if( !(m_lpScreen = (LPBYTE)::malloc( CDirectDraw::SCREEN_WIDTH*CDirectDraw::SCREEN_HEIGHT*3+16 )) ) { throw CApp::GetErrorString( IDS_ERROR_OUTOFMEMORY ); } if( !(m_lpLinecolor = (LPBYTE)::malloc( CDirectDraw::RENDER_HEIGHT )) ) { throw CApp::GetErrorString( IDS_ERROR_OUTOFMEMORY ); } if( !(m_lpSound = (LPBYTE)::malloc( 8192 )) ) { throw CApp::GetErrorString( IDS_ERROR_OUTOFMEMORY ); } } CAviConvDlg::~CAviConvDlg() { FREE( m_lpSound ); FREE( m_lpLinecolor ); FREE( m_lpScreen ); FREE( m_lpRender ); } INT CAviConvDlg::DoModal( HWND hWndParent ) { return ::DialogBoxParam( CApp::GetPlugin(), MAKEINTRESOURCE(IDD_MOVIECONV), hWndParent, g_DlgProc, (LPARAM)this ); } DLGMSG CAviConvDlg::OnInitDialog( DLGMSGPARAM ) { // DEBUGOUT( "CAviConvDlg::OnInitDialog\n" ); m_szRomFile[0] = '\0'; m_szMovieFile[0] = '\0'; m_szAviFile[0] = '\0'; ::SendDlgItemMessage( m_hWnd, IDC_MCV_FRAMERATE_COMBO, CB_RESETCONTENT, 0, 0 ); ::SendDlgItemMessage( m_hWnd, IDC_MCV_FRAMERATE_COMBO, CB_INSERTSTRING, (WPARAM)0, (LPARAM)"Full FPS" ); ::SendDlgItemMessage( m_hWnd, IDC_MCV_FRAMERATE_COMBO, CB_INSERTSTRING, (WPARAM)1, (LPARAM)"1/2 FPS" ); ::SendDlgItemMessage( m_hWnd, IDC_MCV_FRAMERATE_COMBO, CB_INSERTSTRING, (WPARAM)2, (LPARAM)"1/3 FPS" ); ::SendDlgItemMessage( m_hWnd, IDC_MCV_FRAMERATE_COMBO, CB_INSERTSTRING, (WPARAM)3, (LPARAM)"1/4 FPS" ); ::SendDlgItemMessage( m_hWnd, IDC_MCV_FRAMERATE_COMBO, CB_INSERTSTRING, (WPARAM)4, (LPARAM)"1/5 FPS" ); ::SendDlgItemMessage( m_hWnd, IDC_MCV_FRAMERATE_COMBO, CB_INSERTSTRING, (WPARAM)5, (LPARAM)"1/6 FPS" ); ::SendDlgItemMessage( m_hWnd, IDC_MCV_FRAMERATE_COMBO, CB_SETCURSEL, 0, 0 ); INT i, select = 0; CHAR szMode[64]; ::SendDlgItemMessage( m_hWnd, IDC_MCV_SAMPLERATE_COMBO, CB_RESETCONTENT, 0, 0 ); // for( i = 0; i < 8; i++ ) { for( i = 0; CConfig::SamplingRateTable[i*2+0]; i++ ) { ::wsprintf( szMode, "%d.%03dKHz %2dbits", CConfig::SamplingRateTable[i*2+0]/1000, CConfig::SamplingRateTable[i*2+0]%1000, CConfig::SamplingRateTable[i*2+1] ); ::SendDlgItemMessage( m_hWnd, IDC_MCV_SAMPLERATE_COMBO, CB_INSERTSTRING, (WPARAM)i, (LPARAM)szMode ); if( Config.sound.nRate == CConfig::SamplingRateTable[i*2+0] && Config.sound.nBits == CConfig::SamplingRateTable[i*2+1] ) { select = i; } } ::SendDlgItemMessage( m_hWnd, IDC_MCV_FILTERTYPE_COMBO, CB_RESETCONTENT, 0, 0 ); for( i = IDS_SND_FILTER_NONE; i <= IDS_SND_FILTER_TYPE4; i++ ) { CApp::LoadString( i, szMode, sizeof(szMode) ); ::SendDlgItemMessage( m_hWnd, IDC_MCV_FILTERTYPE_COMBO, CB_INSERTSTRING, (WPARAM)i-IDS_SND_FILTER_NONE, (LPARAM)szMode ); } ::SendDlgItemMessage( m_hWnd, IDC_MCV_SAMPLERATE_COMBO, CB_SETCURSEL, (WPARAM)select, 0 ); ::SendDlgItemMessage( m_hWnd, IDC_MCV_FILTERTYPE_COMBO, CB_SETCURSEL, (WPARAM)Config.sound.nFilterType, 0 ); CTRLENABLE( IDC_MCV_CONVERT, FALSE ); ZEROMEMORY( &m_BitmapHdr, sizeof(m_BitmapHdr) ); m_BitmapHdr.biSize = sizeof(BITMAPINFOHEADER); m_BitmapHdr.biWidth = 256; m_BitmapHdr.biHeight = 240; m_BitmapHdr.biPlanes = 1; m_BitmapHdr.biBitCount = 24; m_BitmapHdr.biCompression = BI_RGB; m_BitmapHdr.biSizeImage = 256*240*3; ::GetWindowText( m_hWnd, m_szTitle, sizeof(m_szTitle)-1 ); m_AviWriter.SetVideoFormat( &m_BitmapHdr ); m_hThread = NULL; return TRUE; } DLGCMD CAviConvDlg::OnCancel( DLGCMDPARAM ) { // DEBUGOUT( "CAviConvDlg::OnCancel\n" ); ::KillTimer( m_hWnd, 0x1234 ); if( m_hThread ) { m_bThreadExit = TRUE; ::WaitForSingleObject( m_hThread, INFINITE ); CLOSEHANDLE( m_hThread ); m_bThreadExit = FALSE; if( m_pNes ) { m_pNes->MovieStop(); DELETEPTR( m_pNes ); } m_AviWriter.Close(); ::SetWindowText( m_hWnd, m_szTitle ); ControlEnable( TRUE ); } else { ::EndDialog( m_hWnd, IDCANCEL ); } } DLGCMD CAviConvDlg::OnRomFileBrowse( DLGCMDPARAM ) { string pathstr; if( Config.path.bRomPath ) { pathstr = CPathlib::CreatePath( CApp::GetModulePath(), Config.path.szRomPath ); } else { pathstr = CApp::GetModulePath(); } ::GetDlgItemText( m_hWnd, IDC_MCV_ROMFILE, m_szRomFile, sizeof(m_szRomFile)-1 ); CHAR szTitle[256]; CApp::LoadString( IDS_UI_OPENROM, szTitle, sizeof(szTitle) ); OPENFILENAME ofn = { sizeof(OPENFILENAME) }; ofn.hwndOwner = m_hWnd; ofn.lpstrFile = m_szRomFile; ofn.nMaxFile = sizeof(m_szRomFile); ofn.lpstrFilter = "All Support Types\0*.nes;*.fds;*.lzh;*.zip;*.rar;*.cab\0" "NES ROM (*.nes)\0*.nes\0Disk Image (*.fds)\0*.fds\0" "Archive File\0*.lzh;*.zip;*.rar;*.cab\0"; ofn.nFilterIndex = 1; ofn.Flags = OFN_READONLY|OFN_HIDEREADONLY|OFN_EXPLORER|OFN_PATHMUSTEXIST; ofn.lpstrInitialDir = pathstr.c_str(); ofn.lpstrTitle = szTitle; if( ::GetOpenFileName( &ofn ) ) { ::SetDlgItemText( m_hWnd, IDC_MCV_ROMFILE, m_szRomFile ); } } DLGCMD CAviConvDlg::OnMovieFileBrowse( DLGCMDPARAM ) { string pathstr; if( Config.path.bMoviePath ) { pathstr = CPathlib::CreatePath( CApp::GetModulePath(), Config.path.szMoviePath ); ::CreateDirectory( pathstr.c_str(), NULL ); } else { pathstr = CApp::GetModulePath(); } ::GetDlgItemText( m_hWnd, IDC_MCV_MOVIEFILE, m_szMovieFile, sizeof(m_szMovieFile)-1 ); OPENFILENAME ofn = { sizeof(OPENFILENAME) }; ofn.hwndOwner = m_hWnd; ofn.lpstrFile = m_szMovieFile; ofn.lpstrDefExt = "vmv"; ofn.nMaxFile = sizeof(m_szMovieFile); ofn.lpstrFilter = "Movie Files(*.vmv)\0*.vmv\0All Files(*.*)\0*.*\0"; ofn.nFilterIndex = 1; ofn.Flags = OFN_EXPLORER|OFN_PATHMUSTEXIST; ofn.lpstrInitialDir = pathstr.c_str(); CHAR szTitle[256]; CApp::LoadString( IDS_UI_PLAYMOVIE, szTitle, sizeof(szTitle) ); ofn.lpstrTitle = szTitle; if( ::GetOpenFileName( &ofn ) ) { ::SetDlgItemText( m_hWnd, IDC_MCV_MOVIEFILE, m_szMovieFile ); } } DLGCMD CAviConvDlg::OnAviFileBrowse( DLGCMDPARAM ) { string pathstr, tempstr; if( Config.path.bMoviePath ) { pathstr = CPathlib::CreatePath( CApp::GetModulePath(), Config.path.szMoviePath ); ::CreateDirectory( pathstr.c_str(), NULL ); } else { pathstr = CApp::GetModulePath(); } ::GetDlgItemText( m_hWnd, IDC_MCV_AVIFILE, m_szAviFile, sizeof(m_szAviFile)-1 ); OPENFILENAME ofn = { sizeof(OPENFILENAME) }; ofn.hwndOwner = m_hWnd; ofn.lpstrFile = m_szAviFile; ofn.lpstrDefExt = "avi"; ofn.nMaxFile = sizeof(m_szAviFile); ofn.lpstrFilter = "Movie Files(*.avi)\0*.avi\0All Files(*.*)\0*.*\0"; ofn.nFilterIndex = 1; ofn.Flags = OFN_EXPLORER|OFN_PATHMUSTEXIST|OFN_OVERWRITEPROMPT; ofn.lpstrInitialDir = pathstr.c_str(); // CHAR szTitle[256]; // CApp::LoadString( IDS_UI_AVICONV, szTitle, sizeof(szTitle) ); // ofn.lpstrTitle = szTitle; if( ::GetSaveFileName( &ofn ) ) { ::SetDlgItemText( m_hWnd, IDC_MCV_AVIFILE, m_szAviFile ); } } DLGCMD CAviConvDlg::OnVideoCompressSelect( DLGCMDPARAM ) { // DEBUGOUT( "CAviConvDlg::OnCompressSelect\n" ); if( !m_AviWriter.SelectVideoFormat( m_hWnd ) ) { return; } CTRLENABLE( IDC_MCV_CONVERT, TRUE ); } DLGMSG CAviConvDlg::OnConvertDone( DLGMSGPARAM ) { // DEBUGOUT( "CAviConvDlg::OnConvertDone\n" ); ::KillTimer( m_hWnd, 0x1234 ); ::WaitForSingleObject( m_hThread, INFINITE ); CLOSEHANDLE( m_hThread ); ::SetWindowText( m_hWnd, m_szTitle ); m_AviWriter.Close(); DELETEPTR( m_pNes ); ControlEnable( TRUE ); CHAR szTemp[256]; DWORD totaltime = (m_FinishTime - m_StartTime) / 1000; INT h, m, s; h = totaltime / 3600; totaltime %= 3600; m = totaltime / 60; s = totaltime % 60; ::wsprintf( szTemp, "Convert Finished. Time:%02d:%02d:%02d", h, m, s ); ::MessageBox( m_hWnd, szTemp, "VirtuaNES", MB_OK ); return TRUE; } DLGMSG CAviConvDlg::OnConvertError( DLGMSGPARAM ) { // DEBUGOUT( "CAviConvDlg::OnConvertError\n" ); ::KillTimer( m_hWnd, 0x1234 ); ::WaitForSingleObject( m_hThread, INFINITE ); CLOSEHANDLE( m_hThread ); ::SetWindowText( m_hWnd, m_szTitle ); m_AviWriter.Close(); DELETEPTR( m_pNes ); ControlEnable( TRUE ); ::MessageBox( m_hWnd, (LPSTR)lParam, "VirtuaNES", MB_OK ); return TRUE; } DLGCMD CAviConvDlg::OnConvertStart( DLGCMDPARAM ) { // DEBUGOUT( "CAviConvDlg::OnConvertStart\n" ); ::GetDlgItemText( m_hWnd, IDC_MCV_ROMFILE, m_szRomFile, sizeof(m_szRomFile)-1 ); ::GetDlgItemText( m_hWnd, IDC_MCV_MOVIEFILE, m_szMovieFile, sizeof(m_szMovieFile)-1 ); ::GetDlgItemText( m_hWnd, IDC_MCV_AVIFILE, m_szAviFile, sizeof(m_szAviFile)-1 ); INT select; select = ::SendDlgItemMessage( m_hWnd, IDC_MCV_FRAMERATE_COMBO, CB_GETCURSEL, 0, 0 ); #if 1 m_FrameDivider = select + 1; #else switch( select ) { default: case 0: m_FrameDivider = 1; break; case 1: m_FrameDivider = 2; break; case 2: m_FrameDivider = 3; break; case 3: m_FrameDivider = 4; break; case 4: m_FrameDivider = 5; break; case 5: m_FrameDivider = 6; break; } #endif select = ::SendDlgItemMessage( m_hWnd, IDC_MCV_SAMPLERATE_COMBO, CB_GETCURSEL, 0, 0 ); Config.sound.nRate = CConfig::SamplingRateTable[select*2+0]; Config.sound.nBits = CConfig::SamplingRateTable[select*2+1]; Config.sound.nFilterType = ::SendDlgItemMessage( m_hWnd, IDC_MCV_FILTERTYPE_COMBO, CB_GETCURSEL, 0, 0 ); m_AviWriter.SetAudioFormat( Config.sound.nRate, Config.sound.nBits, 1 ); m_hThread = NULL; try { m_pNes = new NES(m_szRomFile); m_pNes->ppu->SetScreenPtr( m_lpRender, m_lpLinecolor ); m_pNes->Reset(); if( !m_pNes->MoviePlay( m_szMovieFile ) ) throw "MoviePlay error."; WORD wRecVersion, wVersion; DWORD dwRecordTimes; m_pNes->GetMovieInfo( wRecVersion, wVersion, m_RecordFrames, dwRecordTimes ); m_AviWriter.SetFrameRate( 10000, m_pNes->nescfg->FrameRate * 10000 / m_FrameDivider ); if( !m_AviWriter.Open( m_szAviFile ) ) { CHAR szTemp[256]; ::wsprintf( szTemp, CApp::GetErrorString(IDS_ERROR_OPEN), m_szAviFile ); throw szTemp; } if( !(m_hThread = ::CreateThread( NULL, 0, ThreadProc, this, CREATE_SUSPENDED, &m_dwThreadID )) ) { throw "CreateThread failed."; } ControlEnable( FALSE ); DirectDraw.GetPaletteData2( m_Palette ); m_FrameNo = -1; // Time Remain ::SetTimer( m_hWnd, 0x1234, 500, NULL ); // start converter m_bThreadExit = FALSE; // m_StartTime = m_FinishTime = ::timeGetTime(); ::ResumeThread( m_hThread ); } catch( CHAR* str ) { if( m_hThread ) { ::TerminateThread( m_hThread, 0 ); CLOSEHANDLE( m_hThread ); } ::KillTimer( m_hWnd, 0x1234 ); m_AviWriter.Close(); DELETEPTR( m_pNes ); ::MessageBox( m_hWnd, str, "VirtuaNES", MB_OK ); #ifndef _DEBUG } catch(...) { if( m_hThread ) { ::TerminateThread( m_hThread, 0 ); CLOSEHANDLE( m_hThread ); } ::KillTimer( m_hWnd, 0x1234 ); m_AviWriter.Close(); DELETEPTR( m_pNes ); ::MessageBox( m_hWnd, CApp::GetErrorString( IDS_ERROR_UNKNOWN ), "VirtuaNES", MB_OK ); #endif } } DLGMSG CAviConvDlg::OnTimer( DLGMSGPARAM ) { CHAR szTemp[256]; INT nowframe = m_FrameNo; DWORD elapsed = ::timeGetTime() - m_StartTime; if( m_FrameNo >= 0 && elapsed > 5000 ) { DWORD elapsedave = elapsed / nowframe; DWORD estimation = elapsedave * (m_RecordFrames-nowframe) / 1000; INT h, m, s; h = estimation / 3600; estimation %= 3600; m = estimation / 60; s = estimation % 60; ::wsprintf( szTemp, "%s [%d/%d] Time remain %02d:%02d:%02d", m_szTitle, m_FrameNo, m_RecordFrames, h, m, s ); } else { ::wsprintf( szTemp, "%s [%d/%d] Time remain --:--:--", m_szTitle, m_FrameNo, m_RecordFrames ); } ::SetWindowText( m_hWnd, szTemp ); return FALSE; } DWORD WINAPI CAviConvDlg::ThreadProc( LPVOID lpVoid ) { CAviConvDlg* pAviConvDlg = (CAviConvDlg*)lpVoid; NES* pNes = pAviConvDlg->m_pNes; CAVIWriter& AviWriter = pAviConvDlg->m_AviWriter; LPBYTE pRender = pAviConvDlg->m_lpRender; LPBYTE pScreen = pAviConvDlg->m_lpScreen; LPBYTE pLinecolor = pAviConvDlg->m_lpLinecolor; RGBQUAD* pPalette = pAviConvDlg->m_Palette; LPBYTE pSound = pAviConvDlg->m_lpSound; INT FrameDivider = pAviConvDlg->m_FrameDivider; BOOL bFinish = FALSE; double Samples = 0.0; pAviConvDlg->m_FrameNo = 0; pAviConvDlg->m_StartTime = pAviConvDlg->m_FinishTime = ::timeGetTime(); while( !pAviConvDlg->m_bThreadExit ) { if( !pNes->IsMoviePlay() ) { bFinish = TRUE; break; } try { pNes->Movie(); if( (pAviConvDlg->m_FrameNo % FrameDivider) == 0 ) { pNes->EmulateFrame( TRUE ); for( INT i = 0; i < CDirectDraw::SCREEN_HEIGHT; i++ ) { LPBYTE pSrc = &pRender[CDirectDraw::RENDER_WIDTH*i+8]; LPBYTE pDst = &pScreen[CDirectDraw::SCREEN_WIDTH*3*(CDirectDraw::SCREEN_HEIGHT-i-1)]; RGBQUAD* pPal = &pPalette[ (pLinecolor[i] & 7)*256+((pLinecolor[i] & 0x80)?0x40:0x00) ]; #if 0 for( INT j = 0; j < CDirectDraw::SCREEN_WIDTH; j++ ) { BYTE pixel = *pSrc++; pixel |= (pLinecolor[i] & 0x80)?0x40:0; *pDst++ = pPal[ pixel ].rgbBlue; *pDst++ = pPal[ pixel ].rgbGreen; *pDst++ = pPal[ pixel ].rgbRed; } #else DWORD width = CDirectDraw::SCREEN_WIDTH; __asm { mov eax, pSrc mov esi, pPal mov edi, pDst _render_loop: mov edx, [eax] movzx ebx, dl mov ebx, [esi+ebx*4] shr edx, 8 mov [edi+ 0], ebx movzx ebx, dl mov ebx, [esi+ebx*4] shr edx, 8 mov [edi+ 3], ebx movzx ebx, dl shr edx, 8 mov ebx, [esi+ebx*4] mov edx, [esi+edx*4] mov [edi+ 6], ebx mov [edi+ 9], dx shr edx, 16 mov [edi+11], dl mov edx, [eax+4] movzx ebx, dl mov ebx, [esi+ebx*4] shr edx, 8 mov [edi+12], ebx movzx ebx, dl mov ebx, [esi+ebx*4] shr edx, 8 mov [edi+15], ebx movzx ebx, dl shr edx, 8 mov ebx, [esi+ebx*4] mov edx, [esi+edx*4] mov [edi+18], ebx mov [edi+21], dx shr edx, 16 mov [edi+23], dl lea eax, [eax+ 8] lea edi, [edi+24] sub width, 8 jg _render_loop } #endif } if( !AviWriter.WriteVideo( (const char*)pScreen ) ) { throw "Write error.(video)"; } } else { pNes->EmulateFrame( FALSE ); } INT size; INT blockalign = Config.sound.nBits / 8; Samples += ((double)Config.sound.nRate / (double)pNes->nescfg->FrameRate) * (double)blockalign; size = (INT)Samples - ((INT)Samples % blockalign); Samples -= size; pNes->apu->Process( pSound, size ); if( !AviWriter.WriteAudio( (const char*)pSound, size ) ) { throw "Write error.(audio)"; } pAviConvDlg->m_FrameNo++; } catch( CHAR* str ) { pNes->MovieStop(); AviWriter.Close(); ::strcpy( g_szErrorMessage, str ); ::PostMessage( pAviConvDlg->m_hWnd, WM_APP+101, 0, (LPARAM)g_szErrorMessage ); pAviConvDlg->m_bThreadExit = TRUE; #ifndef _DEBUG } catch(...) { pNes->MovieStop(); AviWriter.Close(); ::PostMessage( pAviConvDlg->m_hWnd, WM_APP+101, 0, (LPARAM)CApp::GetErrorString( IDS_ERROR_UNKNOWN ) ); pAviConvDlg->m_bThreadExit = TRUE; #endif } // For Windows9x ::Sleep( 0 ); } pNes->MovieStop(); AviWriter.Close(); if( bFinish ) { pAviConvDlg->m_FinishTime = ::timeGetTime(); // Convert Done ::PostMessage( pAviConvDlg->m_hWnd, WM_APP+100, 0, 0 ); } return 0; } void CAviConvDlg::ControlEnable( BOOL bEnable ) { CTRLENABLE( IDC_MCV_ROMBROWSE, bEnable ); CTRLENABLE( IDC_MCV_MOVIEBROWSE, bEnable ); CTRLENABLE( IDC_MCV_AVIBROWSE, bEnable ); CTRLENABLE( IDC_MCV_ROMFILE, bEnable ); CTRLENABLE( IDC_MCV_MOVIEFILE, bEnable ); CTRLENABLE( IDC_MCV_AVIFILE, bEnable ); CTRLENABLE( IDC_MCV_FRAMERATE_COMBO, bEnable ); CTRLENABLE( IDC_MCV_SAMPLERATE_COMBO, bEnable ); CTRLENABLE( IDC_MCV_FILTERTYPE_COMBO, bEnable ); CTRLENABLE( IDC_MCV_VIDEOSELECT, bEnable ); CTRLENABLE( IDC_MCV_CONVERT, bEnable ); }