diff --git a/AboutDialog.cpp b/AboutDialog.cpp new file mode 100644 index 0000000..047d5cc --- /dev/null +++ b/AboutDialog.cpp @@ -0,0 +1,38 @@ +/* + File: AboutDialog.cpp + Created on: 02/07/2015 + Author: Felix de las Pozas Alvarez + + 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 3 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, see . + */ + +// Project +#include "AboutDialog.h" + +const QString AboutDialog::VERSION = QString("version 1.5.0"); + +//----------------------------------------------------------------- +AboutDialog::AboutDialog(QWidget *parent, Qt::WindowFlags flags) +: QDialog(parent, flags) +{ + setupUi(this); + + setWindowFlags(windowFlags() & ~(Qt::WindowContextHelpButtonHint) & ~(Qt::WindowMaximizeButtonHint) & ~(Qt::WindowMinimizeButtonHint)); + + auto compilation_date = QString(__DATE__); + auto compilation_time = QString(" (") + QString(__TIME__) + QString(")"); + + m_compilationDate->setText(tr("Compiled on ") + compilation_date + compilation_time); + m_version->setText(VERSION); +} diff --git a/AboutDialog.h b/AboutDialog.h new file mode 100644 index 0000000..642a842 --- /dev/null +++ b/AboutDialog.h @@ -0,0 +1,56 @@ +/* + File: AboutDialog.h + Created on: 02/07/2015 + Author: Felix de las Pozas Alvarez + + 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 3 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, see . + */ + +#ifndef ABOUTDIALOG_H_ +#define ABOUTDIALOG_H_ + +// Project +#include "ui_AboutDialog.h" + +// Qt +#include + +/** \class AboutDialog + * \brief Egocentrical dialog with version and date of build. + * + */ +class AboutDialog +: public QDialog +, public Ui_AboutDialog +{ + Q_OBJECT + public: + /** \brief AboutDialog class constructor. + * \param[in] parent pointer to the widget parent of this one. + * \param[in] flags window flags. + * + */ + AboutDialog(QWidget *parent = nullptr, Qt::WindowFlags flags = 0); + + /** \brief AboutDialog class virtual destructor. + * + */ + virtual ~AboutDialog() + {}; + + private: + static const QString VERSION; /** application version string. */ +}; + +#endif // ABOUTDIALOG_H_ diff --git a/AboutDialog.ui b/AboutDialog.ui new file mode 100644 index 0000000..d13b463 --- /dev/null +++ b/AboutDialog.ui @@ -0,0 +1,386 @@ + + + AboutDialog + + + + 0 + 0 + 397 + 287 + + + + + 397 + 287 + + + + + 397 + 287 + + + + About... + + + + :/OGGExtractor/information.svg:/OGGExtractor/information.svg + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 64 + 64 + + + + + + + :/OGGExtractor/application.svg + + + true + + + Qt::AlignCenter + + + + + + + + + + 75 + true + + + + OGG Extractor + + + Qt::AlignCenter + + + + + + + version 1.5.0 + + + Qt::AlignCenter + + + + + + + Copyright (c) 2016 Félix de las Pozas Álvarez + + + Qt::AlignCenter + + + + + + + Compiled on + + + Qt::AlignCenter + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + + + + + + 75 + true + + + + QGroupBox { + border: 1px solid gray; + border-radius: 5px; + margin-top: 2ex; +} + +QGroupBox::title { + subcontrol-origin: margin; + subcontrol-position: top center; /* position at the top center */ + padding: 0px 5px; +} + + + Powered by + + + Qt::AlignHCenter|Qt::AlignTop + + + false + + + + + + 0 + + + + + + 32 + 32 + + + + + + + :/OGGExtractor/qt.ico + + + true + + + + + + + + + + 75 + true + + + + libqt opensource +Qt Framework + + + Qt::AlignCenter + + + + + + + version 5.5 + + + Qt::AlignCenter + + + + + + + + + <a href="http://www.qt.io/">http://www.qt.io/</a> + + + Qt::AlignCenter + + + true + + + + + + + + + Qt::Horizontal + + + + + + + + + + 32 + 32 + + + + + + + :/OGGExtractor/XiphOrg.svg + + + true + + + + + + + + + + 75 + true + + + + Xiph.Org OGG libraries +libvorbis + + + Qt::AlignCenter + + + + + + + version 1.3.5 + + + Qt::AlignCenter + + + + + + + + + <a href="http://xiph.org/vorbis/">http://xiph.org/vorbis/</a> + + + Qt::AlignCenter + + + + + + + + + + + + + + <html><head/><body><p>Some icons by <a href="http://www.freepik.com"><span style=" text-decoration: underline; color:#0000ff;">Freepik</span></a> from <a href="http://www.flaticon.com"><span style=" text-decoration: underline; color:#0000ff;">www.flaticon.com</span></a> (licensed by <a href="http://creativecommons.org/licenses/by/3.0/"><span style=" text-decoration: underline; color:#0000ff;">CC 3.0</span></a>).</p></body></html> + + + Qt::AlignCenter + + + + + + + + 575 + 23 + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + AboutDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AboutDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f9c369d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,77 @@ +project(OGGExtractor) + +cmake_minimum_required (VERSION 2.8.6) + +# Version Number +set (OGG_EXTRACTOR_VERSION_MAJOR 1) +set (OGG_EXTRACTOR_VERSION_MINOR 5) +set (OGG_EXTRACTOR_VERSION_PATCH 0) + +# Find includes in corresponding build directories +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +# Instruct CMake to run moc automatically when needed. +set(CMAKE_AUTOMOC ON) + +# Find the QtWidgets library +find_package(Qt5 COMPONENTS Widgets Multimedia) + +# We need add -DQT_WIDGETS_LIB when using QtWidgets in Qt 5. +#add_definitions(${Qt5Widgets_DEFINITIONS}) +#add_definitions(${Qt5Multimedia_DEFINITIONS}) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Multimedia_EXECUTABLE_COMPILE_FLAGS}") + +if (CMAKE_BUILD_TYPE MATCHES Debug) + set(CORE_EXTERNAL_LIBS ${CORE_EXTERNAL_LIBS} ${QT_QTTEST_LIBRARY}) +endif (CMAKE_BUILD_TYPE MATCHES Debug) + +if(DEFINED MINGW) + configure_file("${PROJECT_SOURCE_DIR}/resources.rc.in" "${PROJECT_BINARY_DIR}/resources.rc") + set(CORE_SOURCES ${CORE_SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/resources.rc) + set(CMAKE_RC_COMPILE_OBJECT " -O coff -o -i ") + enable_language(RC) +endif(DEFINED MINGW) + +include_directories( + ${CMAKE_SOURCE_DIR} + ${PROJECT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_BINARY_DIR} # Generated .h files + ${CMAKE_CURRENT_BINARY_DIR} # For wrap/ui files + ) + +set(CMAKE_CXX_FLAGS " -Wall -Wno-deprecated -std=c++11 -mwindows") + +# Add Qt Resource files +qt5_add_resources(RESOURCES + rsc/resources.qrc +) + +qt5_wrap_ui(CORE_UI + # .ui for Qt + AboutDialog.ui + OGGExtractor.ui + item.ui +) + +set (CORE_SOURCES + # project files + ${CORE_SOURCES} + ${RESOURCES} + ${CORE_MOC} + ${CORE_UI} + main.cpp + OGGExtractor.cpp + AboutDialog.cpp +) + +set(CORE_EXTERNAL_LIBS + Qt5::Widgets + Qt5::Multimedia +) + +add_executable(OGGExtractor ${CORE_SOURCES}) +target_link_libraries (OGGExtractor ${CORE_EXTERNAL_LIBS}) +qt5_use_modules(OGGExtractor Widgets Multimedia) \ No newline at end of file diff --git a/OGGExtractor.cpp b/OGGExtractor.cpp new file mode 100644 index 0000000..11da25c --- /dev/null +++ b/OGGExtractor.cpp @@ -0,0 +1,503 @@ +/* + File: OGGExtractor.cpp + Created on: 26/05/2016 + Author: Felix de las Pozas Alvarez + + 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 3 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, see . + */ + +// Project +#include "OGGExtractor.h" +#include "AboutDialog.h" + +// Qt +#include +#include +#include +#include +#include + +// C++ +#include + +const long long BUFFER_SIZE = 5242880; /** 5 MB size buffer. */ +const char *OGG_HEADER = "OggS"; /** Ogg header signature. */ + +//---------------------------------------------------------------- +OGGExtractor::OGGExtractor(QWidget *parent, Qt::WindowFlags flags) +: QMainWindow {parent, flags} +, m_cancelProcess{false} +{ + setupUi(this); + + m_cancel->hide(); + m_progress->hide(); + + m_containersList->setModel(new QStringListModel(m_containers)); + m_containersList->setSelectionMode(QListView::SelectionMode::MultiSelection); + + m_filesTable->setSortingEnabled(false); + m_filesTable->horizontalHeader()->setDefaultAlignment(Qt::AlignCenter); + m_filesTable->horizontalHeader()->setSectionsMovable(false); + m_filesTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents); + m_filesTable->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); + m_filesTable->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Stretch); + m_filesTable->horizontalHeader()->setSectionResizeMode(3, QHeaderView::ResizeToContents); + m_filesTable->horizontalHeader()->setSectionResizeMode(4, QHeaderView::ResizeToContents); + + connectSignals(); +} + +//---------------------------------------------------------------- +OGGExtractor::~OGGExtractor() +{ +} + +//---------------------------------------------------------------- +void OGGExtractor::connectSignals() +{ + connect(m_addFile, SIGNAL(pressed()), this, SLOT(onFileAdd())); + connect(m_removeFile, SIGNAL(pressed()), this, SLOT(onFileRemove())); + connect(m_about, SIGNAL(pressed()), this, SLOT(showAboutDialog())); + connect(m_quit, SIGNAL(pressed()), this, SLOT(close())); + connect(m_scan, SIGNAL(pressed()), this, SLOT(scanContainers())); + connect(m_extract, SIGNAL(pressed()), this, SLOT(extractFiles())); + connect(m_cancel, SIGNAL(pressed()), this, SLOT(cancelScan())); + connect(m_size, SIGNAL(stateChanged(int)), this, SLOT(onSizeStateChange(int))); + + connect(m_containersList->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), + this, SLOT(onContainerSelectionChanged())); +} + +//---------------------------------------------------------------- +void OGGExtractor::onFileAdd() +{ + auto containers = QFileDialog::getOpenFileNames(centralWidget(), tr("Add container files to scan"), QDir::currentPath(), QString(), nullptr, QFileDialog::Option::ReadOnly); + + if(!containers.empty()) + { + m_containers << containers; + + auto model = qobject_cast(m_containersList->model()); + model->setStringList(m_containers); + } + + m_scan->setEnabled(!m_containers.isEmpty()); +} + +//---------------------------------------------------------------- +void OGGExtractor::onFileRemove() +{ + auto indexes = m_containersList->selectionModel()->selectedIndexes(); + + std::vector positions; + for(auto index: indexes) + { + if(index.isValid()) + { + auto row = index.row(); + positions.push_back(row); + } + } + + auto compare = [](int a, int b) { return a > b; }; + std::sort(positions.begin(), positions.end(), compare); + + while(!positions.empty()) + { + m_containers.removeAt(positions.front()); + positions.erase(positions.begin()); + } + + auto model = qobject_cast(m_containersList->model()); + model->setStringList(m_containers); + + m_scan->setEnabled(!m_containers.isEmpty()); +} + +//---------------------------------------------------------------- +void OGGExtractor::onContainerSelectionChanged() +{ + auto selection = m_containersList->selectionModel()->selectedIndexes(); + auto valid = false; + + for(auto index: selection) + { + valid |= index.isValid(); + } + + m_removeFile->setEnabled(valid); +} + +//---------------------------------------------------------------- +void OGGExtractor::showAboutDialog() +{ + AboutDialog dialog; + dialog.exec(); +} + +//---------------------------------------------------------------- +void OGGExtractor::cancelScan() +{ + m_cancelProcess = true; + + m_cancel->hide(); + m_progress->hide(); +} + +//---------------------------------------------------------------- +void OGGExtractor::scanContainers() +{ + m_cancelProcess = false; + + disconnect(m_containersList->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), + this, SLOT(onContainerSelectionChanged())); + + m_addFile->setEnabled(false); + m_removeFile->setEnabled(false); + + m_filesTable->clearContents(); + + auto buffer = new char[BUFFER_SIZE]; + + unsigned long long int partialSize = 0; + unsigned long long int totalSize = 0; + + for(auto filename: m_containers) + { + QFile file(filename); + totalSize += file.size(); + } + + m_progress->setValue(0); + m_progress->setMaximum(100); + m_progress->show(); + m_cancel->show(); + + for(auto filename: m_containers) + { + if(m_cancelProcess) break; + + QFile file(filename); + if(!file.open(QFile::ReadOnly)) + { + QMessageBox dialog; + dialog.setWindowTitle(tr("Error reading file")); + dialog.setWindowIcon(QIcon(":/OGGExtractor/application.svg")); + dialog.setModal(true); + dialog.setText(tr("Error opening file '%1'").arg(filename)); + dialog.setDetailedText(tr("Error: %2").arg(file.errorString())); + + dialog.exec(); + continue; + } + + long long processed = 0; + unsigned long long oggBeginning = 0; + unsigned long long oggEnding = 0; + + bool beginFound = false; + bool endFound = false; + bool eof = false; + + unsigned char oggHeader[27]; + + while (!eof) + { + file.seek(processed); + + auto bytesRead = file.read(buffer, BUFFER_SIZE); + + for (long long loop = 0; loop < bytesRead && !eof && !m_cancelProcess; ++loop) + { + /* check for "OggS" header and flags */ + if (buffer[loop] == 0x4F) + { + auto position = file.pos(); + auto seekResult = file.seek(processed + loop); + unsigned long long readResult = file.read(reinterpret_cast(&oggHeader[0]), sizeof(oggHeader)); + if(!seekResult || (sizeof(oggHeader) != readResult)) + { + QMessageBox dialog; + dialog.setWindowTitle(tr("Error reading file")); + dialog.setWindowIcon(QIcon(":/OGGExtractor/application.svg")); + dialog.setModal(true); + dialog.setText(tr("Error scanning file '%1'").arg(filename)); + dialog.exec(); + + eof = true; + continue; + } + file.seek(position); + + if (0 == (strncmp((const char *) oggHeader, OGG_HEADER, 4))) + { + /* detected beginning of ogg file */ + if (oggHeader[5] == 0x02) + { + beginFound = true; + oggBeginning = processed + loop; + continue; + } + + /* detected ending of ogg file, more difficult because of trailing frames */ + if (beginFound && ((oggHeader[5] == 0x04) || (oggHeader[5] == 0x05))) + { + endFound = true; + oggEnding = processed + loop + 27; + + /* we need to do this because we can be at the very end */ + /* of the buffer and don't want to look outside it */ + auto trailingSize = static_cast(oggHeader[26]); + + auto trailingFrames = new char[trailingSize]; + + position = file.pos(); + seekResult = file.seek(oggEnding); + readResult = file.read(trailingFrames, trailingSize); + + if (!seekResult || (trailingSize != readResult)) + { + QMessageBox dialog; + dialog.setWindowTitle(tr("Error reading file")); + dialog.setWindowIcon(QIcon(":/OGGExtractor/application.svg")); + dialog.setModal(true); + dialog.setText(tr("I/O error reading input file, probably tried to read past EOF while scanning '%1'").arg(filename)); + dialog.setDetailedText(tr("ERROR: %1").arg(file.errorString())); + dialog.exec(); + + delete [] trailingFrames; + + eof = true; + continue; + } + file.seek(position); + + oggEnding += (unsigned long long) oggHeader[26]; + + for (unsigned long loop2 = 0; loop2 < (unsigned long) oggHeader[26]; loop2++) + { + oggEnding += (unsigned long long) trailingFrames[loop2]; + } + + delete [] trailingFrames; + } + + /* every beginning has an end ;-) */ + if ((beginFound == true) && (endFound == true)) + { + beginFound = false; + endFound = false; + + long long size = oggEnding - oggBeginning; + if (size < (m_minimum->value() * 1024)) + { + // skip file + continue; + } + + ogg_data data; + data.start = oggBeginning; + data.end = oggEnding; + data.container = filename; + + m_soundFiles << data; + + auto row = m_soundFiles.size() - 1; + m_filesTable->insertRow(row); + + auto widget = new QWidget(); + auto checkBox = new QCheckBox(); + checkBox->setChecked(true); + + connect(checkBox, SIGNAL(stateChanged(int)), this, SLOT(checkSelectedFiles())); + auto layout = new QHBoxLayout(widget); + layout->addWidget(checkBox); + layout->setAlignment(Qt::AlignCenter); + layout->setContentsMargins(0,0,0,0); + widget->setLayout(layout); + m_filesTable->setCellWidget(row,0,widget); + + auto name = new QLabel(tr("found_ogg_%1").arg(row, 6, 10, QChar('0'))); + name->setAlignment(Qt::AlignCenter); + m_filesTable->setCellWidget(row,1, name); + + auto containerName = data.container.split('/').last().split('.').first(); + auto containerWidget = new QLabel(containerName); + containerWidget->setAlignment(Qt::AlignCenter); + m_filesTable->setCellWidget(row,2, containerWidget); + + m_filesTable->setCellWidget(row,3, new QLabel("HH:MM:SS")); + + widget = new QWidget(); + auto button = new QPushButton(QIcon(":/OGGExtractor/play.svg"), ""); + button->setFixedSize(24,24); + layout = new QHBoxLayout(widget); + layout->addWidget(button); + layout->setAlignment(Qt::AlignCenter); + layout->setContentsMargins(0,0,0,0); + widget->setLayout(layout); + m_filesTable->setCellWidget(row,4,widget); + + } + } + } + } + + processed += bytesRead; + partialSize += bytesRead; + + if (bytesRead < BUFFER_SIZE) eof = true; + + auto value = 100.0*(static_cast(partialSize)/totalSize); + m_progress->setValue(value); + QApplication::processEvents(); + } + } + + delete [] buffer; + + m_progress->hide(); + m_progress->setValue(0); + m_cancel->hide(); + + connect(m_containersList->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), + this, SLOT(onContainerSelectionChanged())); + + m_addFile->setEnabled(true); + onContainerSelectionChanged(); + + m_filesTable->setEnabled(!m_soundFiles.isEmpty()); + m_filesTable->adjustSize(); + + m_extract->setEnabled(!m_soundFiles.isEmpty()); +} + +//---------------------------------------------------------------- +void OGGExtractor::extractFiles() +{ + auto destination = QFileDialog::getExistingDirectory(centralWidget(), tr("Select destination directory"), QDir::currentPath()); + if(destination.isEmpty()) return; + + m_cancelProcess = false; + m_progress->setValue(0); + m_progress->show(); + m_cancel->show(); + + for(int i = 0; i < m_soundFiles.size() && !m_cancelProcess; ++i) + { + m_progress->setValue(100.0*(static_cast(i/m_soundFiles.size()))); + QApplication::processEvents(); + + auto data = m_soundFiles.at(i); + auto widget = qobject_cast(m_filesTable->cellWidget(i, 0)); + auto checkBox = qobject_cast(widget->layout()->itemAt(0)->widget()); + + if(!checkBox) + { + QMessageBox dialog; + dialog.setWindowTitle(tr("Error extracting OGG file")); + dialog.setWindowIcon(QIcon(":/OGGExtractor/application.svg")); + dialog.setModal(true); + dialog.setText(tr("Error extracting file '%1'").arg(i)); + dialog.exec(); + + return; + } + + if(checkBox->isChecked()) + { + QDir dir(destination); + QFile file(dir.absoluteFilePath(tr("found_ogg_%1.ogg").arg(i, 6, 10, QChar('0')))); + + if(!file.open(QFile::Truncate|QFile::WriteOnly)) + { + QMessageBox dialog; + dialog.setWindowTitle(tr("Error extracting OGG file")); + dialog.setWindowIcon(QIcon(":/OGGExtractor/application.svg")); + dialog.setModal(true); + dialog.setText(tr("Couldn't create file '%1'").arg(file.fileName())); + dialog.setDetailedText(tr("Error: %1").arg(file.errorString())); + dialog.exec(); + + return; + } + + QFile source(data.container); + + if(!source.open(QFile::ReadOnly)) + { + QMessageBox dialog; + dialog.setWindowTitle(tr("Error extracting OGG file")); + dialog.setWindowIcon(QIcon(":/OGGExtractor/application.svg")); + dialog.setModal(true); + dialog.setText(tr("Couldn't open container file '%1'").arg(data.container)); + dialog.setDetailedText(tr("Error: %1").arg(source.errorString())); + dialog.exec(); + + return; + } + + source.seek(data.start); + file.write(source.read(data.end-data.start)); + + if(!file.flush()) + { + QMessageBox dialog; + dialog.setWindowTitle(tr("Error extracting OGG file")); + dialog.setWindowIcon(QIcon(":/OGGExtractor/application.svg")); + dialog.setModal(true); + dialog.setText(tr("Error flushing '%1'").arg(file.fileName())); + dialog.setDetailedText(tr("Error: %1").arg(file.errorString())); + dialog.exec(); + + return; + } + + file.close(); + source.close(); + } + } + + m_cancelProcess = false; + m_progress->setValue(0); + m_progress->hide(); + m_cancel->hide(); + QApplication::processEvents(); +} + +//---------------------------------------------------------------- +void OGGExtractor::onSizeStateChange(int value) +{ + m_minimum->setEnabled(value == Qt::Checked); +} + +//---------------------------------------------------------------- +void OGGExtractor::checkSelectedFiles() +{ + bool enable = false; + for(int i = 0; i < m_soundFiles.size(); ++i) + { + auto widget = qobject_cast(m_filesTable->cellWidget(i, 0)); + auto checkBox = qobject_cast(widget->layout()->itemAt(0)->widget()); + + if(checkBox && checkBox->isChecked()) + { + enable = true; + break; + } + } + + m_extract->setEnabled(enable); +} diff --git a/OGGExtractor.h b/OGGExtractor.h new file mode 100644 index 0000000..8e5c35d --- /dev/null +++ b/OGGExtractor.h @@ -0,0 +1,110 @@ +/* + File: OGGExtractor.h + Created on: 26/05/2016 + Author: Felix de las Pozas Alvarez + + 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 3 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, see . + */ + +// Qt +#include +#include "ui_OGGExtractor.h" + +/** \class OGGExtractor + * \brief Main dialog class. + * + */ +class OGGExtractor +: public QMainWindow +, private Ui_OGGExtractorMainWindow +{ + Q_OBJECT + public: + /** \brief OGGExtractor class constructor. + * \param[in] parent raw pointer of the QWidget parent of this one. + * \param[in] flags window flags. + * + */ + explicit OGGExtractor(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); + + /** \brief OGGExtractor class virtual destructor. + * + */ + virtual ~OGGExtractor(); + + private slots: + /** \brief Opens a file selection dialog to select container files. + * + */ + void onFileAdd(); + + /** \brief Removes the selected container from the contailers list. + * + */ + void onFileRemove(); + + /** \brief Shows the about dialog. + * + */ + void showAboutDialog(); + + /** \brief Scans the container files for OGG music files. + * + */ + void scanContainers(); + + /** \brief Shows the file dialog to select destination and extracts the found music files. + * + */ + void extractFiles(); + + /** \brief Updates the remove container button state. Disables it if no selection and enables on selection. + * + */ + void onContainerSelectionChanged(); + + /** \brief Cancels scanning process. + * + */ + void cancelScan(); + + /** \brief Updates the UI when the state of the size checkbox changes. + * \param[in] value checkbox state. + * + */ + void onSizeStateChange(int value); + + /** \brief Updates the extraction button when a OGG selection checkbox changes status. + * + */ + void checkSelectedFiles(); + + private: + /** \brief Helper method that connects the signals of the UI with its correspondent slots. + * + */ + void connectSignals(); + + struct ogg_data + { + unsigned long long start; + unsigned long long end; + QString container; + }; + + QStringList m_containers; /** file names of the containers. */ + QList m_soundFiles; + + bool m_cancelProcess; /** true if current process has been cancelled. */ +}; diff --git a/OGGExtractor.ui b/OGGExtractor.ui new file mode 100644 index 0000000..3d5e541 --- /dev/null +++ b/OGGExtractor.ui @@ -0,0 +1,330 @@ + + + OGGExtractorMainWindow + + + + 0 + 0 + 800 + 593 + + + + OGG Extractor + + + + :/OGGExtractor/application.svg:/OGGExtractor/application.svg + + + false + + + true + + + + + + + Data files to scan for OGG sound files: + + + + + + + 0 + + + + + Data files to be scanned for OGG files. + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Add data files to the list to be scanned for OGG files. + + + Add + + + + + + + false + + + Remove data files from the list to be scanned for OGG files. + + + Remove + + + + + + + + + Qt::Horizontal + + + + + + + OGG files found: + + + + + + + false + + + Found OGG files. + + + QAbstractScrollArea::AdjustToContents + + + true + + + 5 + + + false + + + + Selected + + + Selected for extraction + + + + + Name + + + Extraction file name + + + + + Container + + + Container of the file + + + + + Duration + + + File Duration + + + + + Play/Stop + + + Sound file player + + + + + + + + + + Don't show OGG files smaller than the given size. + + + Do not show files smaller than + + + + + + + false + + + Size in Kilobytes. + + + Kb + + + 100 + + + 999999999 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Current process progress. + + + 24 + + + Qt::AlignCenter + + + + + + + + 24 + 24 + + + + + 24 + 24 + + + + Cancel current process. + + + ... + + + + :/OGGExtractor/cancel.svg:/OGGExtractor/cancel.svg + + + + 20 + 20 + + + + + + + + + + + + Quit the application. + + + Quit + + + + + + + About OGG Extractor. + + + About... + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + Scan the data files in the list for OGG files. + + + Scan + + + + + + + false + + + Extract selected OGG files to disk. + + + Extract + + + + + + + + + + + + + diff --git a/item.ui b/item.ui new file mode 100644 index 0000000..1baa176 --- /dev/null +++ b/item.ui @@ -0,0 +1,101 @@ + + + MusicItem + + + + 0 + 0 + 384 + 28 + + + + Form + + + + 4 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + + + true + + + + + + + + 75 + true + + + + Sound_Name + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Origin_File + + + + + + + + 75 + true + + + + Duration + + + + + + + + 24 + 24 + + + + ... + + + + :/OGGExtractor/play.svg:/OGGExtractor/play.svg + + + + + + + + + + diff --git a/main.c b/main.c deleted file mode 100644 index e0046d8..0000000 --- a/main.c +++ /dev/null @@ -1,459 +0,0 @@ -/*------------------------------------------------------------------------ - Module: main.c - Author: Felix de las Pozas Alvarez - Project: Generic OGG Ripper - Version: v1.4a - Creation Date: February 27 2004 - Description: Extracts Ogg files from game datafiles. Main file. - License: GNU Public License - - 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA -------------------------------------------------------------------------*/ - -/* NOTE -** Mingw uses %I64d and %I64u to print 64 bits integers in signed and -** unsigned format, with other compilers you should probably change it -** to %lld and %llu equivalents. -** -** Mingw32, Digital Mars and Microsoft compilers will compile this code -** without changes. -*/ - -#define VERSION "v1.4a" - -#include -#include -#include -#include -#include -#include -#include - -typedef enum {_error_ = -1, _success_, _false_ = 0, _true_} booleantype; - -/* global variables */ -static unsigned long long num_files = 0; - -static unsigned long long ogg_found = 0; -static unsigned long long ogg_skip = 0; -static unsigned long long ogg_extract = 0; -static unsigned long long ogg_size = 0; - -static unsigned long long to_skip = 0; -static unsigned long long to_extract = 0; -static unsigned long long to_skip_size = 0; - -static booleantype do_extract = _false_; - -/* Ogg files signature */ -static const char *OGG_HEADER = (const char *)"OggS"; - -/* let's use a 5 mb buffer, I don't want to getc the entire file because game -** datafiles tend to be huge it's a bit less simple but who cares. */ -static const unsigned long long BUFFER_SIZE = 5242880; - -/* Information shown at the end of the program -*/ -static void stats(void) -{ - printf("| Processed %I64u files.\n", num_files); - printf("| Found %I64u Ogg files.\n", ogg_found); - - if (to_skip != 0) - printf("| Skipped %I64u files.\n", to_skip-ogg_skip); - - if (to_skip_size != 0) - printf("| Skipped %I64u files because of it's size.\n", to_skip_size); - - if(to_extract != 0) - printf("| Extracted %I64u files.\n", to_extract-ogg_extract); - return; -} - -/* After the beginning and end of an Ogg file has been detected this function -** dumps it as a separate file. Only ogg_search() calls this function. -*/ -static void dumpfile(FILE *file, - unsigned long long begin, - unsigned long long end) -{ - unsigned char *buffer; - char filename[30], temp[10]; - unsigned long long old_filepointer; - unsigned long long filesize = end-begin; - FILE *output; - - /* generate name for output */ - (void) sprintf(temp,"%08lu", (unsigned long)ogg_found); - strcpy(filename,"found_"); - strcat(filename,temp); - strcat(filename,".ogg"); - - printf("(%08ld) Ogg found at : %I64u - ", (unsigned long)ogg_found, begin); - printf("Writting : '%s'\n", filename); - - if (NULL == (output = fopen(filename, "wb"))) - { - printf("ERROR: Error opening output file %s.\n",filename); - exit(_error_); - } - - if (NULL == (buffer = calloc(1, BUFFER_SIZE))) - { - printf("ERROR: Not enough memory for output buffer (%I64u bytes).\n", - (unsigned long long) sizeof(BUFFER_SIZE)); - exit(_error_); - } - - /* ogg_search needs the file pointer to stay the same */ - old_filepointer = (unsigned long) ftell(file); - (void) fseek(file, begin, SEEK_SET); - - while (BUFFER_SIZE < filesize) - { - if (BUFFER_SIZE != fread(buffer, 1, BUFFER_SIZE, file)) - { - printf("ERROR: I/O error reading input file.\n"); - exit(_error_); - } - - if (BUFFER_SIZE != fwrite(buffer, 1, BUFFER_SIZE, output)) - { - printf("ERROR: I/O error writing output file %s.\n", filename); - exit(_error_); - } - - filesize -= BUFFER_SIZE; - } - - if (filesize != fread(buffer, 1, filesize, file)) - { - printf("ERROR: I/O error reading input file.\n"); - exit(_error_); - } - - if (filesize != fwrite(buffer, 1, filesize, output)) - { - printf("ERROR: I/O error writing output file %s.\n", filename); - exit(_error_); - } - - free(buffer); - - if (fclose(output) == EOF) - { - printf("ERROR: Error closing output file %s.\n", filename); - exit(_error_); - } - - /* ogg_search needs the file pointer to stay the same */ - (void) fseek(file, old_filepointer, SEEK_SET); - return; -} - -/* When the file to inspect has been opened this function searchs for -** Ogg file signatures, and extracts or skips found files depending -** on command-line options. If 'extract' option has been set this -** function exits the program without searching for more files. -*/ -static void ogg_search(FILE *filename) -{ - unsigned long loop, loop2; - unsigned char *buffer, *trailing_frames, *ogg_header_buf; - unsigned long long filepointer, bytes, filesize = 0; - unsigned long long ogg_beginning= 0; - unsigned long long ogg_ending = 0; - booleantype begin_found = _false_; - booleantype end_of_file = _false_; - booleantype end_found = _false_; - - if (NULL == (buffer = calloc(1, BUFFER_SIZE))) - { - printf("ERROR: Not enough memory for %I64u bytes buffer.\n", - (unsigned long long) sizeof(BUFFER_SIZE)); - exit(_error_); - } - - while (end_of_file != _true_) - { - bytes = fread(buffer, 1, BUFFER_SIZE, filename); - - for (loop = 0; loop < bytes; loop++) - { - /* check for "OggS" header and flags */ - if (buffer[loop] == 0x4F) - { - /* we must be paranoids because loop can be BUFFER_SIZE */ - /* and looking buffer[loop+5] is out of the buffer */ - if (NULL == (ogg_header_buf = calloc(1, 27))) - { - printf("ERROR: Not enough memory for 27 bytes, absurd!.\n"); - free(buffer); - exit(_error_); - } - - filepointer = (unsigned long long) ftell(filename); - (void) fseek(filename, filesize+loop, SEEK_SET); - - if (27 != fread(ogg_header_buf, 1, 27, filename)) - { - printf("ERROR: I/O error reading input file, probably tried to read past EOF (%I64u).\n", filesize+loop); - free(ogg_header_buf); - free(buffer); - return; - } - - (void) fseek(filename, filepointer, SEEK_SET); - - if (0 == (strncmp((const char *)ogg_header_buf, OGG_HEADER, 4))) - { - /* detected beginning of ogg file */ - if (ogg_header_buf[5] == 0x02) - { - begin_found = _true_; - ogg_beginning = filesize + loop; - continue; - } - - /* detected ending of ogg file, more difficult because of trailing frames */ - if ((ogg_header_buf[5] == 0x04) || (ogg_header_buf[5] == 0x05)) - { - end_found = _true_; - ogg_ending = filesize + loop + 27; - - /* we need to do this because we can be at the very end */ - /* of the buffer and don't want to look outside it */ - if (NULL == (trailing_frames = calloc(1, ogg_header_buf[26]))) - { - printf("ERROR: Not enough memory for %c bytes, absurd!.\n", - ogg_header_buf[26]); - free(ogg_header_buf); - free(buffer); - exit(_error_); - } - - filepointer = (unsigned long long) ftell(filename); - (void) fseek(filename, ogg_ending, SEEK_SET); - if (ogg_header_buf[26] != fread(trailing_frames, 1, ogg_header_buf[26], filename)) - { - printf("ERROR: I/O error reading input file, probably tried to read past EOF.\n"); - free(trailing_frames); - free(ogg_header_buf); - free(buffer); - return; - } - - (void) fseek(filename, filepointer, SEEK_SET); - ogg_ending += (unsigned long long)ogg_header_buf[26]; - - for (loop2 = 0; loop2 < (unsigned long)ogg_header_buf[26]; loop2++) - ogg_ending += (unsigned long long)trailing_frames[loop2]; - - free(trailing_frames); - } - - /* every beginning has an end ;-) */ - if ((begin_found == _true_) && (end_found == _true_)) - { - begin_found = _false_; - end_found = _false_; - ogg_found++; - - if (ogg_skip > 0) - { - ogg_skip--; - printf("(%08ld) Ogg found at : %I64u - Skipped.\n", - (unsigned long)ogg_found, ogg_beginning); - continue; - } - - if (ogg_ending-ogg_beginning < (ogg_size * 1024)) - { - to_skip_size++; - printf("(%08ld) Ogg found at : %I64u - Skipped because of it's size.\n", - (unsigned long)ogg_found, ogg_beginning); - continue; - } - - if (ogg_extract > 0) - { - ogg_extract--; - dumpfile(filename, ogg_beginning, ogg_ending); - if (ogg_extract != 0) - continue; - } - - if (do_extract == _true_) - { - printf("\n| Partial statistics, not all files were fully processed:\n"); - stats(); - free(ogg_header_buf); - free(buffer); - exit(_success_); - } - else - dumpfile(filename, ogg_beginning, ogg_ending); - } - } - free(ogg_header_buf); - } - } - filesize += bytes; - /* reached end of file? */ - if (bytes < BUFFER_SIZE) - end_of_file = _true_; - } - free(buffer); - return; -} - -/* Information about the options and some examples -*/ -static void usage(char* name) -{ - printf("Usage: %s [-snnn] [-ennn] [-znnn] file1 [file2] ... [fileN]\n\n", name); - - printf("Options:\n"); - printf(" -snnn Skip the first nnn files.\n"); - printf(" -ennn Extract only nnn files.\n"); - printf(" -znnn Do not extract files smaller than nnn kb.\n"); - printf(" file File(s) to inspect, wilcards accepted.\n\n"); - - printf("Example: %s *.* ..\\*.pak ..\\..\\*.wav\n", name); - printf(" Extracts all ogg files found in the specified files.\n\n"); - - printf("Example: %s -s10 -e15 *.pak\n",name); - printf(" Skip the first 10 files and extract the next 15 in all .pak files.\n\n"); - - printf("Example: %s -z1024 *.pak\n", name); - printf(" Do not extract files smaller than 1024kb.\n\n"); - - printf("Note: 'skip', 'extract' and 'size' options cross file boundaries.\n"); - printf(" 'file' is the only parameter required, others are optional.\n"); - return; -} - -int main(int argc, char *argv[]) -{ - unsigned char *path; - unsigned char full_path[260]; - unsigned long file_number = 0; - char **unused = NULL; - int error_handle = 0; - struct _finddata_t files; - FILE *filename; - - /* getopt() var */ - int options; - - /* The egocentric banner */ - printf("Generic Ogg Ripper %s\n", VERSION); - printf("Felix de las Pozas Alvarez, %s\n\n",__DATE__); - - while ((options = getopt (argc, argv, ":s:e:z:")) != -1) - switch (options) - { - case 's': - to_skip = strtol(optarg, unused, 0); - break; - case 'e': - if ((to_extract = strtol(optarg, unused, 0)) != 0) - do_extract = _true_; - break; - case 'z': - ogg_size = strtol(optarg, unused, 0); - ; - break; - case ':': - printf("ERROR: Option '%c' requires an argument.\n", optopt); - return _error_; - break; - case '?': - if (isprint (optopt)) - printf("ERROR: Unknown option `-%c'.\n", optopt); - else - printf("ERROR: Unknown option character `\\x%x'.\n", optopt); - return _error_; - default: - exit(_error_); - } - - if (argc == 1) - { - usage(argv[0]); - return _error_; - } - - if (optind == argc) - { - printf("ERROR: Missing file(s) parameter(s).\n"); - return _error_; - } - - ogg_skip = to_skip; - ogg_extract = to_extract; - - - /* Test if the file exists and tries to open it, notify errors */ - for (file_number = optind; file_number < (unsigned long)argc; file_number++) - { - /* ok, who goes first? */ - if (-1 == (error_handle = _findfirst(argv[file_number], &files))) - { - printf("ERROR: Error opening file %s\n", argv[file_number]); - continue; - } - strcpy((char *)full_path, argv[file_number]); - - do - { - /* quick and dirty way to jump to directories */ - if (NULL != (path = (unsigned char *)strrchr((char const *)full_path,92))) - { - path++; - strcpy((char *)path, files.name); - } - else - strcpy((char *)full_path, files.name); - - /* i don't want those listed */ - if (_A_SUBDIR == (files.attrib & 0x10)) - continue; - - /* try to open, notify errors and continue with next file */ - if (NULL == (filename = fopen((char const *)full_path, "rb"))) - { - printf("ERROR: Error opening file %s\n",(char *)full_path); - continue; - } - - /* search for ogg and update stats */ - printf("Searching in %s\n", (char *)full_path); - num_files++; - ogg_search(filename); - - /* Close the file*/ - if (fclose(filename) == EOF) - printf("ERROR: Error closing the file %s.\n", (char*)full_path); - } - while (0 == _findnext(error_handle,&files)); - - (void) _findclose(error_handle); - } - printf("\n| Full statistics:\n"); - stats(); - return _success_; -} diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..afbb7da --- /dev/null +++ b/main.cpp @@ -0,0 +1,461 @@ +/* + File: main.cpp + Created on: 27/02/2004 + Author: Felix de las Pozas Alvarez + + 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 3 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, see . + */ + +// Project +#include "OGGExtractor.h" + +// Qt +#include +#include + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + OGGExtractor extractor; + extractor.show(); + + return app.exec(); +} + +//#define VERSION "v1.4a" +// +//#include +//#include +//#include +//#include +//#include +//#include +//#include +// +//typedef enum {_error_ = -1, _success_, _false_ = 0, _true_} booleantype; +// +///* global variables */ +//static unsigned long long num_files = 0; +// +//static unsigned long long ogg_found = 0; +//static unsigned long long ogg_skip = 0; +//static unsigned long long ogg_extract = 0; +//static unsigned long long ogg_size = 0; +// +//static unsigned long long to_skip = 0; +//static unsigned long long to_extract = 0; +//static unsigned long long to_skip_size = 0; +// +//static booleantype do_extract = _false_; +// +///* Ogg files signature */ +//static const char *OGG_HEADER = (const char *)"OggS"; +// +///* let's use a 5 mb buffer, I don't want to getc the entire file because game +//** datafiles tend to be huge it's a bit less simple but who cares. */ +//static const unsigned long long BUFFER_SIZE = 5242880; +// +///* Information shown at the end of the program +//*/ +//static void stats(void) +//{ +// printf("| Processed %I64u files.\n", num_files); +// printf("| Found %I64u Ogg files.\n", ogg_found); +// +// if (to_skip != 0) +// printf("| Skipped %I64u files.\n", to_skip-ogg_skip); +// +// if (to_skip_size != 0) +// printf("| Skipped %I64u files because of it's size.\n", to_skip_size); +// +// if(to_extract != 0) +// printf("| Extracted %I64u files.\n", to_extract-ogg_extract); +// return; +//} +// +///* After the beginning and end of an Ogg file has been detected this function +//** dumps it as a separate file. Only ogg_search() calls this function. +//*/ +//static void dumpfile(FILE *file, +// unsigned long long begin, +// unsigned long long end) +//{ +// unsigned char *buffer; +// char filename[30], temp[10]; +// unsigned long long old_filepointer; +// unsigned long long filesize = end-begin; +// FILE *output; +// +// /* generate name for output */ +// (void) sprintf(temp,"%08lu", (unsigned long)ogg_found); +// strcpy(filename,"found_"); +// strcat(filename,temp); +// strcat(filename,".ogg"); +// +// printf("(%08ld) Ogg found at : %I64u - ", (unsigned long)ogg_found, begin); +// printf("Writting : '%s'\n", filename); +// +// if (NULL == (output = fopen(filename, "wb"))) +// { +// printf("ERROR: Error opening output file %s.\n",filename); +// exit(_error_); +// } +// +// if (NULL == (buffer = calloc(1, BUFFER_SIZE))) +// { +// printf("ERROR: Not enough memory for output buffer (%I64u bytes).\n", +// (unsigned long long) sizeof(BUFFER_SIZE)); +// exit(_error_); +// } +// +// /* ogg_search needs the file pointer to stay the same */ +// old_filepointer = (unsigned long) ftell(file); +// (void) fseek(file, begin, SEEK_SET); +// +// while (BUFFER_SIZE < filesize) +// { +// if (BUFFER_SIZE != fread(buffer, 1, BUFFER_SIZE, file)) +// { +// printf("ERROR: I/O error reading input file.\n"); +// exit(_error_); +// } +// +// if (BUFFER_SIZE != fwrite(buffer, 1, BUFFER_SIZE, output)) +// { +// printf("ERROR: I/O error writing output file %s.\n", filename); +// exit(_error_); +// } +// +// filesize -= BUFFER_SIZE; +// } +// +// if (filesize != fread(buffer, 1, filesize, file)) +// { +// printf("ERROR: I/O error reading input file.\n"); +// exit(_error_); +// } +// +// if (filesize != fwrite(buffer, 1, filesize, output)) +// { +// printf("ERROR: I/O error writing output file %s.\n", filename); +// exit(_error_); +// } +// +// free(buffer); +// +// if (fclose(output) == EOF) +// { +// printf("ERROR: Error closing output file %s.\n", filename); +// exit(_error_); +// } +// +// /* ogg_search needs the file pointer to stay the same */ +// (void) fseek(file, old_filepointer, SEEK_SET); +// return; +//} +// +///* When the file to inspect has been opened this function searchs for +//** Ogg file signatures, and extracts or skips found files depending +//** on command-line options. If 'extract' option has been set this +//** function exits the program without searching for more files. +//*/ +//static void ogg_search(FILE *filename) +//{ +// unsigned long loop, loop2; +// unsigned char *buffer, *trailing_frames, *ogg_header_buf; +// unsigned long long filepointer, bytes, filesize = 0; +// unsigned long long ogg_beginning= 0; +// unsigned long long ogg_ending = 0; +// booleantype begin_found = _false_; +// booleantype end_of_file = _false_; +// booleantype end_found = _false_; +// +// if (NULL == (buffer = calloc(1, BUFFER_SIZE))) +// { +// printf("ERROR: Not enough memory for %I64u bytes buffer.\n", +// (unsigned long long) sizeof(BUFFER_SIZE)); +// exit(_error_); +// } +// +// while (end_of_file != _true_) +// { +// bytes = fread(buffer, 1, BUFFER_SIZE, filename); +// +// for (loop = 0; loop < bytes; loop++) +// { +// /* check for "OggS" header and flags */ +// if (buffer[loop] == 0x4F) +// { +// /* we must be paranoids because loop can be BUFFER_SIZE */ +// /* and looking buffer[loop+5] is out of the buffer */ +// if (NULL == (ogg_header_buf = calloc(1, 27))) +// { +// printf("ERROR: Not enough memory for 27 bytes, absurd!.\n"); +// free(buffer); +// exit(_error_); +// } +// +// filepointer = (unsigned long long) ftell(filename); +// (void) fseek(filename, filesize+loop, SEEK_SET); +// +// if (27 != fread(ogg_header_buf, 1, 27, filename)) +// { +// printf("ERROR: I/O error reading input file, probably tried to read past EOF (%I64u).\n", filesize+loop); +// free(ogg_header_buf); +// free(buffer); +// return; +// } +// +// (void) fseek(filename, filepointer, SEEK_SET); +// +// if (0 == (strncmp((const char *)ogg_header_buf, OGG_HEADER, 4))) +// { +// /* detected beginning of ogg file */ +// if (ogg_header_buf[5] == 0x02) +// { +// begin_found = _true_; +// ogg_beginning = filesize + loop; +// continue; +// } +// +// /* detected ending of ogg file, more difficult because of trailing frames */ +// if ((ogg_header_buf[5] == 0x04) || (ogg_header_buf[5] == 0x05)) +// { +// end_found = _true_; +// ogg_ending = filesize + loop + 27; +// +// /* we need to do this because we can be at the very end */ +// /* of the buffer and don't want to look outside it */ +// if (NULL == (trailing_frames = calloc(1, ogg_header_buf[26]))) +// { +// printf("ERROR: Not enough memory for %c bytes, absurd!.\n", +// ogg_header_buf[26]); +// free(ogg_header_buf); +// free(buffer); +// exit(_error_); +// } +// +// filepointer = (unsigned long long) ftell(filename); +// (void) fseek(filename, ogg_ending, SEEK_SET); +// if (ogg_header_buf[26] != fread(trailing_frames, 1, ogg_header_buf[26], filename)) +// { +// printf("ERROR: I/O error reading input file, probably tried to read past EOF.\n"); +// free(trailing_frames); +// free(ogg_header_buf); +// free(buffer); +// return; +// } +// +// (void) fseek(filename, filepointer, SEEK_SET); +// ogg_ending += (unsigned long long)ogg_header_buf[26]; +// +// for (loop2 = 0; loop2 < (unsigned long)ogg_header_buf[26]; loop2++) +// ogg_ending += (unsigned long long)trailing_frames[loop2]; +// +// free(trailing_frames); +// } +// +// /* every beginning has an end ;-) */ +// if ((begin_found == _true_) && (end_found == _true_)) +// { +// begin_found = _false_; +// end_found = _false_; +// ogg_found++; +// +// if (ogg_skip > 0) +// { +// ogg_skip--; +// printf("(%08ld) Ogg found at : %I64u - Skipped.\n", +// (unsigned long)ogg_found, ogg_beginning); +// continue; +// } +// +// if (ogg_ending-ogg_beginning < (ogg_size * 1024)) +// { +// to_skip_size++; +// printf("(%08ld) Ogg found at : %I64u - Skipped because of it's size.\n", +// (unsigned long)ogg_found, ogg_beginning); +// continue; +// } +// +// if (ogg_extract > 0) +// { +// ogg_extract--; +// dumpfile(filename, ogg_beginning, ogg_ending); +// if (ogg_extract != 0) +// continue; +// } +// +// if (do_extract == _true_) +// { +// printf("\n| Partial statistics, not all files were fully processed:\n"); +// stats(); +// free(ogg_header_buf); +// free(buffer); +// exit(_success_); +// } +// else +// dumpfile(filename, ogg_beginning, ogg_ending); +// } +// } +// free(ogg_header_buf); +// } +// } +// filesize += bytes; +// /* reached end of file? */ +// if (bytes < BUFFER_SIZE) +// end_of_file = _true_; +// } +// free(buffer); +// return; +//} +// +///* Information about the options and some examples +//*/ +//static void usage(char* name) +//{ +// printf("Usage: %s [-snnn] [-ennn] [-znnn] file1 [file2] ... [fileN]\n\n", name); +// +// printf("Options:\n"); +// printf(" -snnn Skip the first nnn files.\n"); +// printf(" -ennn Extract only nnn files.\n"); +// printf(" -znnn Do not extract files smaller than nnn kb.\n"); +// printf(" file File(s) to inspect, wilcards accepted.\n\n"); +// +// printf("Example: %s *.* ..\\*.pak ..\\..\\*.wav\n", name); +// printf(" Extracts all ogg files found in the specified files.\n\n"); +// +// printf("Example: %s -s10 -e15 *.pak\n",name); +// printf(" Skip the first 10 files and extract the next 15 in all .pak files.\n\n"); +// +// printf("Example: %s -z1024 *.pak\n", name); +// printf(" Do not extract files smaller than 1024kb.\n\n"); +// +// printf("Note: 'skip', 'extract' and 'size' options cross file boundaries.\n"); +// printf(" 'file' is the only parameter required, others are optional.\n"); +// return; +//} +// +//int main(int argc, char *argv[]) +//{ +// unsigned char *path; +// unsigned char full_path[260]; +// unsigned long file_number = 0; +// char **unused = NULL; +// int error_handle = 0; +// struct _finddata_t files; +// FILE *filename; +// +// /* getopt() var */ +// int options; +// +// /* The egocentric banner */ +// printf("Generic Ogg Ripper %s\n", VERSION); +// printf("Felix de las Pozas Alvarez, %s\n\n",__DATE__); +// +// while ((options = getopt (argc, argv, ":s:e:z:")) != -1) +// switch (options) +// { +// case 's': +// to_skip = strtol(optarg, unused, 0); +// break; +// case 'e': +// if ((to_extract = strtol(optarg, unused, 0)) != 0) +// do_extract = _true_; +// break; +// case 'z': +// ogg_size = strtol(optarg, unused, 0); +// ; +// break; +// case ':': +// printf("ERROR: Option '%c' requires an argument.\n", optopt); +// return _error_; +// break; +// case '?': +// if (isprint (optopt)) +// printf("ERROR: Unknown option `-%c'.\n", optopt); +// else +// printf("ERROR: Unknown option character `\\x%x'.\n", optopt); +// return _error_; +// default: +// exit(_error_); +// } +// +// if (argc == 1) +// { +// usage(argv[0]); +// return _error_; +// } +// +// if (optind == argc) +// { +// printf("ERROR: Missing file(s) parameter(s).\n"); +// return _error_; +// } +// +// ogg_skip = to_skip; +// ogg_extract = to_extract; +// +// +// /* Test if the file exists and tries to open it, notify errors */ +// for (file_number = optind; file_number < (unsigned long)argc; file_number++) +// { +// /* ok, who goes first? */ +// if (-1 == (error_handle = _findfirst(argv[file_number], &files))) +// { +// printf("ERROR: Error opening file %s\n", argv[file_number]); +// continue; +// } +// strcpy((char *)full_path, argv[file_number]); +// +// do +// { +// /* quick and dirty way to jump to directories */ +// if (NULL != (path = (unsigned char *)strrchr((char const *)full_path,92))) +// { +// path++; +// strcpy((char *)path, files.name); +// } +// else +// strcpy((char *)full_path, files.name); +// +// /* i don't want those listed */ +// if (_A_SUBDIR == (files.attrib & 0x10)) +// continue; +// +// /* try to open, notify errors and continue with next file */ +// if (NULL == (filename = fopen((char const *)full_path, "rb"))) +// { +// printf("ERROR: Error opening file %s\n",(char *)full_path); +// continue; +// } +// +// /* search for ogg and update stats */ +// printf("Searching in %s\n", (char *)full_path); +// num_files++; +// ogg_search(filename); +// +// /* Close the file*/ +// if (fclose(filename) == EOF) +// printf("ERROR: Error closing the file %s.\n", (char*)full_path); +// } +// while (0 == _findnext(error_handle,&files)); +// +// (void) _findclose(error_handle); +// } +// printf("\n| Full statistics:\n"); +// stats(); +// return _success_; +//} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..ef2e990 --- /dev/null +++ b/readme.md @@ -0,0 +1,56 @@ +Ogg Extractor +============= + +# Summary +- [Description](#description) +- [Compilation](#compilation-requirements) +- [Install](#install) +- [Screenshots](#screenshots) +- [Repository information](#repository-information) + +# Description +Tool to scan and extract OGG sound files from big game data files. This program has been used to extract the music from the following games: +* 'The Talos Principle' by Croteam. +* 'Thief: Deadly Shadows' by Ion Storm. +* 'Hitman 2: Silent Assassin' by Io Interactive. +* 'Hitman: Contracts' by Io Interactive. +* 'Freedom Fighters' by Io Interactive. +* 'Blood Omen 2' by Crystal Dynamics. + +## Options +The tool can be configured to show only files with a minimum size making easier to identify music files from smaller dialog sound files. + +# Compilation requirements +## To build the tool: +* cross-platform build system: [CMake](http://www.cmake.org/cmake/resources/software.html). +* compiler: [Mingw64](http://sourceforge.net/projects/mingw-w64/) on Windows or [gcc](http://gcc.gnu.org/) on Linux. + +## External dependencies: +The following libraries are required: +* [Qt opensource framework](http://www.qt.io/). + +# Install +The only current option is build from source as binaries are not provided. + +# Screenshots +Simple main dialog. + +![Main dialog](https://cloud.githubusercontent.com/assets/12167134/7867872/e2fd4c28-0578-11e5-93bb-56c7ee8b26df.jpg) + +Dialog shown while scanning for files. + +![Process Dialog](https://cloud.githubusercontent.com/assets/12167134/7867873/e48c0714-0578-11e5-8de4-ba1b44b1b72f.jpg) + +# Repository information +**Version**: 1.4.0 + +**Status**: finished + +**cloc statistics** + +| Language |files |blank |comment |code | +|:-----------------------------|--------------:|------------:|-----------------:|-----:| +| C++ | 11 | 584 | 374 |2362 | +| C/C++ Header | 10 | 242 | 631 | 448 | +| CMake | 1 | 19 | 11 | 90 | +| **Total** | **22** | **845** | **1016** |**2900**| diff --git a/readme.txt b/readme.txt deleted file mode 100644 index 3a2630a..0000000 --- a/readme.txt +++ /dev/null @@ -1,115 +0,0 @@ ------------------------------------------------------------------------- -Generic Ogg Ripper v1.4 .O. -(c)Felix de las Pozas Alvarez ..O -August 14 2004 OOO ------------------------------------------------------------------------- - -License ------------------------------------------------------------------------- -This work is under the GPL license included in the distribution, read -it for detailed information about what you can do or don't with the -source code and the binaries. - - -Program Information ------------------------------------------------------------------------- -This program extracts Ogg files from game datafiles. I coded it to -extract the music from the game "Hitman 2: Silent Assasin" by IO -Interactive, but it will work with any file so it will probably works -with games like "Soul Reaver 2", "Blood Omen 2" and any other game -that uses the Ogg format. - -So far I've used it to extract the music and sounds from : -- Hitman 2 -- Freedom Fighters -- Hitman Contracts -- Thief Deadly Shadows. - -If you know of another game, please contact me. - - -OggRipper Win32 Binary Usage ------------------------------------------------------------------------- -Usage: - OggRipper [-snnn] [-ennn] [-znnn] file1 [file2] ... [fileN] - -Options: - -snnn Skip the FIRST nnn ogg files found. If ommited - no files will be skipped. - -ennn Extract the NEXT nnn ogg files, not more. If - ommited all found files will be extracted - except if 'size' is specified. - -znnn Only extracts files bigger than nnn kb. - file File(s) to inspect, wildcards accepted in each. - -'Skip', 'Extract' and 'Size' options cross file boundaries. 'file' -parameter is required, others are optional. -Remember that a file name with spaces in it must be written between -commas, like this "d:\file name". - -Examples : - OggRipper streams.wav - OggRipper *.dat ..\*.pak "d:\game dir\data file.dat" - OggRipper *.* ..\*.* - OggRipper -s5 *.wav ..\*.pak - OggRipper -e10 *.wav - OggRipper -s100 -e50 streams.wav - OggRipper -s10 -e5 -z1024 *.lib - - -History ------------------------------------------------------------------------- -v1.4 - August 14, 2004 - Changed some internal structure and fixed a bug where the - program aborted when encountered a I/O error in a input file, - now the program notifies the error and continues until all - files had been inspected. Maybe final version until GUI version. - Uses 'getopt' so probably only compiles with Mingw and not with - Digital Mars or Microsoft, but not tested with those. - -v1.3 - August 10, 2004 - Added 'Size' option. - -v1.2 - August 4, 2004 - Added 'Skip' and 'Extract' options. Number of found files now - has a 8 digit limit. Changed some error messages. - -v1.1 - February 27, 2004 - Cleaned the source code and did a bit of type checking in - comparisons, assignations and return values. - -v1.01 - January 7, 2004 - More descriptive error messages. - -v1.0 - December 15, 2003 - Revised the old source code and made some changes. It will - compile without problems under Mingw and Digital Mars Compilers. - - -Contact (Don't send me spam or we won't be friends ;-) ------------------------------------------------------------------------- -email : FelixdelasPozas@ToughGuy.net -website: http://perso.wanadoo.es/longcoldwalk/ - - -Thanks to... ------------------------------------------------------------------------- -IO Interactive for their great games. -Composer of the Hitman music Jesper Kyd. - - -F.A.Q. ------------------------------------------------------------------------- -Q: This program does not work for the game !! -A: Probably it does not use the ogg format or it's encrypted. - -Q: This program gave me an error while writing/reading!! -A: Probably there is a fault of your hard drive or you don't have - enough free space. - -Q: I've tested it with the game and I can't listen to the - Ogg extracted files! -A: Maybe the data stored on the hard disk in not a pure Ogg format. This - happens for example with "Commandos 3: Destination Berlin" (datafiles - have some bytes changed as some sort of protection). - diff --git a/resources.rc.in b/resources.rc.in new file mode 100644 index 0000000..572b2dc --- /dev/null +++ b/resources.rc.in @@ -0,0 +1,27 @@ +AppIcon ICON "@CMAKE_CURRENT_SOURCE_DIR@/rsc/application.ico" +1 VERSIONINFO +FILEVERSION @OGG_EXTRACTOR_VERSION_MAJOR@, @OGG_EXTRACTOR_VERSION_MINOR@, @OGG_EXTRACTOR_VERSION_PATCH@, 0 +PRODUCTVERSION @OGG_EXTRACTOR_VERSION_MAJOR@, @OGG_EXTRACTOR_VERSION_MINOR@, @OGG_EXTRACTOR_VERSION_PATCH@, 0 +FILEFLAGSMASK 0 +FILEOS 0x40000 +FILETYPE 1 +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "Felix de las Pozas Alvarez" + VALUE "FileDescription", "OGG_EXTRACTOR" + VALUE "FileVersion", "@OGG_EXTRACTOR_VERSION_MAJOR@.@OGG_EXTRACTOR_VERSION_MINOR@.@OGG_EXTRACTOR_VERSION_PATCH@" + VALUE "InternalName", "OGG Extractor" + VALUE "LegalCopyright", "GNU Public License" + VALUE "OriginalFilename", "OGGExtractor.exe" + VALUE "ProductName", "OGG Extractor" + VALUE "ProductVersion", "@OGG_EXTRACTOR_VERSION_MAJOR@.@OGG_EXTRACTOR_VERSION_MINOR@.@OGG_EXTRACTOR_VERSION_PATCH@" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/rsc/Qt.ico b/rsc/Qt.ico new file mode 100644 index 0000000..fef1dee Binary files /dev/null and b/rsc/Qt.ico differ diff --git a/rsc/XiphOrg.svg b/rsc/XiphOrg.svg new file mode 100644 index 0000000..359bea6 --- /dev/null +++ b/rsc/XiphOrg.svg @@ -0,0 +1,116 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/rsc/application.ico b/rsc/application.ico new file mode 100644 index 0000000..918b3a1 Binary files /dev/null and b/rsc/application.ico differ diff --git a/rsc/application.svg b/rsc/application.svg new file mode 100644 index 0000000..e626b95 --- /dev/null +++ b/rsc/application.svg @@ -0,0 +1,76 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/rsc/cancel.svg b/rsc/cancel.svg new file mode 100644 index 0000000..0cf813f --- /dev/null +++ b/rsc/cancel.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rsc/exit.svg b/rsc/exit.svg new file mode 100644 index 0000000..a8d414a --- /dev/null +++ b/rsc/exit.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rsc/folder.svg b/rsc/folder.svg new file mode 100644 index 0000000..5c11bcc --- /dev/null +++ b/rsc/folder.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rsc/information.svg b/rsc/information.svg new file mode 100644 index 0000000..7ffe4c7 --- /dev/null +++ b/rsc/information.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rsc/play.svg b/rsc/play.svg new file mode 100644 index 0000000..a93c158 --- /dev/null +++ b/rsc/play.svg @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/rsc/resources.qrc b/rsc/resources.qrc new file mode 100644 index 0000000..63870c5 --- /dev/null +++ b/rsc/resources.qrc @@ -0,0 +1,16 @@ + + + application.ico + application.svg + cancel.svg + play.svg + stop.svg + information.svg + exit.svg + folder.svg + search.svg + stop.svg + qt.ico + XiphOrg.svg + + diff --git a/rsc/search.svg b/rsc/search.svg new file mode 100644 index 0000000..ee63b0a --- /dev/null +++ b/rsc/search.svg @@ -0,0 +1,69 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/rsc/stop.svg b/rsc/stop.svg new file mode 100644 index 0000000..1f37886 --- /dev/null +++ b/rsc/stop.svg @@ -0,0 +1,12 @@ + + + + + + + +