Added first cut at AVI RIFF Viewer tool window.

This commit is contained in:
mjbudd77 2021-10-02 11:33:35 -04:00
parent 9853ff37b0
commit 41e04ece0b
7 changed files with 572 additions and 1 deletions

View File

@ -524,6 +524,7 @@ set(SRC_DRIVERS_SDL
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/sdl-throttle.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/unix-netplay.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/AviRecord.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/AviRiffViewer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/avi/avi-utils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/avi/fileio.cpp
${CMAKE_CURRENT_SOURCE_DIR}/drivers/Qt/avi/gwavi.cpp

View File

@ -0,0 +1,419 @@
/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2020 mjbudd77
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
// AviRiffViewer.cpp
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <QHeaderView>
#include <QCloseEvent>
#include <QMessageBox>
#include <QFileDialog>
#include <QStandardPaths>
#include "Qt/main.h"
#include "Qt/config.h"
#include "Qt/fceuWrapper.h"
#include "Qt/AviRiffViewer.h"
#include "Qt/ConsoleUtilities.h"
static AviRiffViewerDialog *win = NULL;
//----------------------------------------------------------------------------
static int riffWalkCallback( int type, long long int fpos, const char *fourcc, size_t size, void *userData )
{
int ret = 0;
if ( win )
{
ret = win->riffWalkCallback( type, fpos, fourcc, size );
}
return ret;
}
//----------------------------------------------------------------------------
//--- AVI RIFF Viewer Dialog
//----------------------------------------------------------------------------
AviRiffViewerDialog::AviRiffViewerDialog(QWidget *parent)
: QDialog(parent)
{
QMenuBar *menuBar;
QVBoxLayout *mainLayout;
QHBoxLayout *hbox;
QPushButton *closeButton;
QTreeWidgetItem *item;
win = this;
avi = NULL;
setWindowTitle("AVI RIFF Viewer");
resize(512, 512);
menuBar = buildMenuBar();
mainLayout = new QVBoxLayout();
setLayout(mainLayout);
mainLayout->setMenuBar( menuBar );
riffTree = new AviRiffTree();
riffTree->setColumnCount(4);
riffTree->setSelectionMode( QAbstractItemView::SingleSelection );
riffTree->setAlternatingRowColors(true);
item = new QTreeWidgetItem();
item->setText(0, QString::fromStdString("Block"));
item->setText(1, QString::fromStdString("FCC"));
item->setText(2, QString::fromStdString("Size"));
item->setText(3, QString::fromStdString("FilePos"));
item->setTextAlignment(0, Qt::AlignLeft);
item->setTextAlignment(1, Qt::AlignLeft);
item->setTextAlignment(2, Qt::AlignLeft);
item->setTextAlignment(3, Qt::AlignLeft);
riffTree->setHeaderItem(item);
riffTree->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
//connect( riffTree, SIGNAL(itemActivated(QTreeWidgetItem*,int)), this, SLOT(hotKeyActivated(QTreeWidgetItem*,int) ) );
mainLayout->addWidget(riffTree);
closeButton = new QPushButton( tr("Close") );
closeButton->setIcon(style()->standardIcon(QStyle::SP_DialogCloseButton));
connect(closeButton, SIGNAL(clicked(void)), this, SLOT(closeWindow(void)));
hbox = new QHBoxLayout();
hbox->addStretch(5);
hbox->addWidget( closeButton, 1 );
mainLayout->addLayout( hbox );
}
//----------------------------------------------------------------------------
AviRiffViewerDialog::~AviRiffViewerDialog(void)
{
printf("Destroy AVI RIFF Viewer Window\n");
if ( avi )
{
delete avi; avi = NULL;
}
win = NULL;
}
//----------------------------------------------------------------------------
void AviRiffViewerDialog::closeEvent(QCloseEvent *event)
{
printf("AVI RIFF Viewer Window Event\n");
done(0);
deleteLater();
event->accept();
}
//----------------------------------------------------------------------------
void AviRiffViewerDialog::closeWindow(void)
{
//printf("Close Window\n");
done(0);
deleteLater();
}
//----------------------------------------------------------------------------
QMenuBar *AviRiffViewerDialog::buildMenuBar(void)
{
QMenu *fileMenu;
//QActionGroup *actGroup;
QAction *act;
int opt, useNativeMenuBar=0;
QMenuBar *menuBar = new QMenuBar();
// This is needed for menu bar to show up on MacOS
g_config->getOption( "SDL.UseNativeMenuBar", &useNativeMenuBar );
menuBar->setNativeMenuBar( useNativeMenuBar ? true : false );
//-----------------------------------------------------------------------
// Menu Start
//-----------------------------------------------------------------------
// File
fileMenu = menuBar->addMenu(tr("&File"));
// File -> Open
act = new QAction(tr("&Open"), this);
act->setShortcut(QKeySequence::Open);
act->setStatusTip(tr("Open AVI File"));
connect(act, SIGNAL(triggered()), this, SLOT(openAviFileDialog(void)) );
fileMenu->addAction(act);
// File -> Close
act = new QAction(tr("&Close"), this);
act->setShortcut(QKeySequence::Close);
act->setStatusTip(tr("Close Window"));
connect(act, SIGNAL(triggered()), this, SLOT(closeWindow(void)) );
fileMenu->addAction(act);
return menuBar;
}
//----------------------------------------------------------------------------
void AviRiffViewerDialog::openAviFileDialog(void)
{
std::string last;
int ret, useNativeFileDialogVal;
QString filename;
std::string lastPath;
//char dir[512];
const char *base, *rom;
QFileDialog dialog(this, tr("Open AVI Movie for Inspection") );
QList<QUrl> urls;
QDir d;
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setNameFilter(tr("AVI Movies (*.avi) ;; All files (*)"));
dialog.setViewMode(QFileDialog::List);
dialog.setFilter( QDir::AllEntries | QDir::AllDirs | QDir::Hidden );
dialog.setLabelText( QFileDialog::Accept, tr("Open") );
base = FCEUI_GetBaseDirectory();
urls << QUrl::fromLocalFile( QDir::rootPath() );
urls << QUrl::fromLocalFile(QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first());
urls << QUrl::fromLocalFile(QStandardPaths::standardLocations(QStandardPaths::DownloadLocation).first());
if ( base )
{
urls << QUrl::fromLocalFile( QDir( base ).absolutePath() );
d.setPath( QString(base) + "/avi");
if ( d.exists() )
{
urls << QUrl::fromLocalFile( d.absolutePath() );
}
dialog.setDirectory( d.absolutePath() );
}
dialog.setDefaultSuffix( tr(".avi") );
g_config->getOption ("SDL.AviFilePath", &lastPath);
if ( lastPath.size() > 0 )
{
dialog.setDirectory( QString::fromStdString(lastPath) );
}
rom = getRomFile();
if ( rom )
{
char baseName[512];
getFileBaseName( rom, baseName );
if ( baseName[0] != 0 )
{
dialog.selectFile(baseName);
}
}
// Check config option to use native file dialog or not
g_config->getOption ("SDL.UseNativeFileDialog", &useNativeFileDialogVal);
dialog.setOption(QFileDialog::DontUseNativeDialog, !useNativeFileDialogVal);
dialog.setSidebarUrls(urls);
ret = dialog.exec();
if ( ret )
{
QStringList fileList;
fileList = dialog.selectedFiles();
if ( fileList.size() > 0 )
{
filename = fileList[0];
}
}
if ( filename.isNull() )
{
return;
}
//qDebug() << "selected file path : " << filename.toUtf8();
printf( "AVI Debug movie %s\n", filename.toStdString().c_str() );
lastPath = QFileInfo(filename).absolutePath().toStdString();
if ( lastPath.size() > 0 )
{
g_config->setOption ("SDL.AviFilePath", lastPath);
}
openFile( filename.toStdString().c_str() );
}
//----------------------------------------------------------------------------
int AviRiffViewerDialog::openFile( const char *filepath )
{
if ( avi )
{
closeFile();
}
avi = new gwavi_t();
if ( avi->openIn( filepath ) )
{
return -1;
}
itemStack.clear();
avi->setRiffWalkCallback( ::riffWalkCallback, this );
avi->printHeaders();
return 0;
}
//----------------------------------------------------------------------------
int AviRiffViewerDialog::closeFile(void)
{
if ( avi )
{
delete avi; avi = NULL;
}
riffTree->clear();
itemStack.clear();
return 0;
}
//----------------------------------------------------------------------------
int AviRiffViewerDialog::riffWalkCallback( int type, long long int fpos, const char *fourcc, size_t size )
{
AviRiffTreeItem *item, *groupItem;
switch ( type )
{
case gwavi_t::RIFF_START:
{
item = new AviRiffTreeItem(type, fpos, fourcc, size);
itemStack.push_back(item);
riffTree->addTopLevelItem(item);
}
break;
case gwavi_t::RIFF_END:
{
itemStack.pop_back();
}
break;
case gwavi_t::LIST_START:
{
item = new AviRiffTreeItem(type, fpos, fourcc, size);
groupItem = itemStack.back();
itemStack.push_back(item);
groupItem->addChild(item);
}
break;
case gwavi_t::LIST_END:
{
itemStack.pop_back();
}
break;
case gwavi_t::CHUNK_START:
{
item = new AviRiffTreeItem(type, fpos, fourcc, size);
groupItem = itemStack.back();
groupItem->addChild(item);
}
break;
default:
// UnHandled Type
break;
}
return 0;
}
//----------------------------------------------------------------------------
//--- AVI RIFF Tree View
//----------------------------------------------------------------------------
AviRiffTree::AviRiffTree(QWidget *parent)
: QTreeWidget(parent)
{
}
//----------------------------------------------------------------------------
AviRiffTree::~AviRiffTree(void)
{
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//--- AVI RIFF Viewer Dialog
//----------------------------------------------------------------------------
AviRiffTreeItem::AviRiffTreeItem(int typeIn, long long int fposIn, const char *fourccIn, size_t sizeIn, QTreeWidgetItem *parent)
: QTreeWidgetItem(parent)
{
char stmp[64];
type = typeIn;
fpos = fposIn;
size = sizeIn;
strcpy( fourcc, fourccIn );
//sprintf( stmp, "0x%08llX", fposIn );
switch ( type )
{
case gwavi_t::RIFF_START:
case gwavi_t::RIFF_END:
setText( 0, QString("RIFF") );
break;
case gwavi_t::LIST_START:
case gwavi_t::LIST_END:
setText( 0, QString("LIST") );
break;
default:
case gwavi_t::CHUNK_START:
setText( 0, QString("CHUNK") );
break;
}
setText( 1, QString(fourcc) );
sprintf( stmp, "%zu", size );
setText( 2, QString(stmp) );
sprintf( stmp, "0x%08llX", fposIn );
setText( 3, QString(stmp) );
}
//----------------------------------------------------------------------------
AviRiffTreeItem::~AviRiffTreeItem(void)
{
}
//----------------------------------------------------------------------------

View File

@ -0,0 +1,83 @@
// AviRiffViewer.h
//
#pragma once
#include <string>
#include <list>
#include <map>
#include <QWidget>
#include <QDialog>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QComboBox>
#include <QCheckBox>
#include <QPushButton>
#include <QLineEdit>
#include <QLabel>
#include <QFrame>
#include <QGroupBox>
#include <QTreeView>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QMenuBar>
#include <QAction>
#include "Qt/avi/gwavi.h"
class AviRiffTreeItem : public QTreeWidgetItem
{
//Q_OBJECT
public:
AviRiffTreeItem(int type, long long int fpos, const char *fourcc, size_t size, QTreeWidgetItem *parent = nullptr);
~AviRiffTreeItem(void);
private:
int type;
char fourcc[8];
size_t size;
long long int fpos;
};
class AviRiffTree : public QTreeWidget
{
Q_OBJECT
public:
AviRiffTree(QWidget *parent = 0);
~AviRiffTree(void);
protected:
};
class AviRiffViewerDialog : public QDialog
{
Q_OBJECT
public:
AviRiffViewerDialog(QWidget *parent = 0);
~AviRiffViewerDialog(void);
int riffWalkCallback( int type, long long int fpos, const char *fourcc, size_t size );
protected:
void closeEvent(QCloseEvent *event);
QMenuBar *buildMenuBar(void);
int openFile( const char *filepath );
int closeFile(void);
gwavi_t *avi;
AviRiffTree *riffTree;
std::list <AviRiffTreeItem*> itemStack;
private:
public slots:
void closeWindow(void);
private slots:
void openAviFileDialog(void);
//void ItemActivated(QTreeWidgetItem *item, int column);
};

View File

@ -72,6 +72,7 @@
#include "Qt/HelpPages.h"
#include "Qt/GuiConf.h"
#include "Qt/AviRecord.h"
#include "Qt/AviRiffViewer.h"
#include "Qt/MoviePlay.h"
#include "Qt/MovieRecord.h"
#include "Qt/MovieOptions.h"
@ -1595,6 +1596,14 @@ void consoleWin_t::createMainMenu(void)
act->setStatusTip(tr("Open Palette Editor Window"));
connect(act, SIGNAL(triggered()), this, SLOT(openPaletteEditorWin(void)) );
toolsMenu->addAction(act);
// Tools -> AVI RIFF Viewer
act = new QAction(tr("&AVI RIFF Viewer ..."), this);
//act->setShortcut( QKeySequence(tr("Shift+F7")));
act->setStatusTip(tr("Open AVI RIFF Viewer Window"));
connect(act, SIGNAL(triggered()), this, SLOT(openAviRiffViewer(void)) );
toolsMenu->addAction(act);
//-----------------------------------------------------------------------
@ -2799,6 +2808,17 @@ void consoleWin_t::openPaletteEditorWin(void)
win->show();
}
void consoleWin_t::openAviRiffViewer(void)
{
AviRiffViewerDialog *win;
//printf("Open AVI RIFF Viewer Window\n");
win = new AviRiffViewerDialog(this);
win->show();
}
void consoleWin_t::openMovieOptWin(void)
{
MovieOptionsDialog_t *win;

View File

@ -334,6 +334,7 @@ class consoleWin_t : public QMainWindow
void openGuiConfWin(void);
void openTimingConfWin(void);
void openPaletteEditorWin(void);
void openAviRiffViewer(void);
void openTimingStatWin(void);
void openMovieOptWin(void);
void openCodeDataLogger(void);

View File

@ -83,6 +83,7 @@ gwavi_t::gwavi_t(void)
bits_per_pixel = 24;
avi_std = 2;
audioEnabled = false;
riffWalkCallback = NULL;
}
gwavi_t::~gwavi_t(void)
@ -660,11 +661,13 @@ int gwavi_t::printHeaders(void)
{
char fourcc[8];
unsigned int ret, fileSize, size;
long long int fpos;
if ( in == NULL )
{
return -1;
}
fpos = ftell(in);
if (read_chars_bin(in, fourcc, 4) == -1)
return -1;
@ -687,6 +690,11 @@ int gwavi_t::printHeaders(void)
fourcc[4] = 0;
printf("FileType: '%s'\n", fourcc );
if ( riffWalkCallback )
{
riffWalkCallback( RIFF_START, fpos, fourcc, fileSize, riffWalkUserData );
}
while ( size >= 4 )
{
if (read_chars_bin(in, fourcc, 4) == -1)
@ -728,10 +736,13 @@ unsigned int gwavi_t::readList(int lvl)
char fourcc[8], listType[8], pad[4];
unsigned int size, listSize=0;
char indent[256];
long long int fpos;
memset( indent, ' ', lvl*3);
indent[lvl*3] = 0;
fpos = ftell(in);
if (read_uint(in, listSize) == -1)
{
(void)fprintf(stderr, "readList: read_int() failed\n");
@ -754,6 +765,10 @@ unsigned int gwavi_t::readList(int lvl)
size -= 4;
bytesRead += 4;
if ( riffWalkCallback )
{
riffWalkCallback( LIST_START, fpos-4, listType, listSize, riffWalkUserData );
}
printf("%sList Start: '%s' %u\n", indent, listType, listSize );
while ( size >= 4 )
@ -805,6 +820,12 @@ unsigned int gwavi_t::readList(int lvl)
}
printf("%sList End: %s %u\n", indent, listType, bytesRead);
if ( riffWalkCallback )
{
fpos = ftell(in);
riffWalkCallback( LIST_END, fpos, listType, listSize, riffWalkUserData );
}
return bytesRead+4;
}
@ -813,10 +834,13 @@ unsigned int gwavi_t::readChunk(const char *id, int lvl)
unsigned int r, ret, size, chunkSize, bytesRead=0;
unsigned short dataWord;
char indent[256];
long long int fpos;
memset( indent, ' ', lvl*3);
indent[lvl*3] = 0;
fpos = ftell(in);
if (read_uint(in, chunkSize) == -1)
{
(void)fprintf(stderr, "readChunk: read_uint() failed\n");
@ -824,9 +848,14 @@ unsigned int gwavi_t::readChunk(const char *id, int lvl)
}
printf("%sChunk Start: %s %u\n", indent, id, chunkSize);
if ( riffWalkCallback )
{
riffWalkCallback( CHUNK_START, fpos-4, id, chunkSize, riffWalkUserData );
}
if ( chunkSize == 0 )
{
return 0;
return 4;
}
size = chunkSize;

View File

@ -175,6 +175,15 @@ class gwavi_t
static const unsigned int IF_KEYFRAME = 0x00000010;
static const unsigned int IF_NO_TIME = 0x00000100;
enum
{
RIFF_START,
RIFF_END,
LIST_START,
LIST_END,
CHUNK_START
};
gwavi_t(void);
~gwavi_t(void);
@ -198,6 +207,12 @@ class gwavi_t
int printHeaders(void);
void setRiffWalkCallback( int (*cb)( int type, long long int fpos, const char *fourcc, size_t size, void *userData ), void *userData )
{
riffWalkCallback = cb;
riffWalkUserData = userData;
};
private:
FILE *in;
FILE *out;
@ -252,6 +267,9 @@ class gwavi_t
unsigned int readAviHeader(void);
unsigned int readStreamHeader(void);
unsigned int readIndexBlock( unsigned int chunkSize );
void *riffWalkUserData;
int (*riffWalkCallback)( int type, long long int fpos, const char *fourcc, size_t size, void *userData );
};
#endif /* ndef H_GWAVI */