367 lines
13 KiB
C++
367 lines
13 KiB
C++
// Copyright (C) 2003-2008 Dolphin Project.
|
|
|
|
// 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, version 2.0.
|
|
|
|
// 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 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official SVN repository and contact information can be found at
|
|
// http://code.google.com/p/dolphin-emu/
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
// Include
|
|
// ¯¯¯¯¯¯¯¯¯¯
|
|
#include <iostream>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <windows.h>
|
|
|
|
#include "Common.h" // Common
|
|
#include "IniFile.h"
|
|
#include "Log.h"
|
|
|
|
#include "PowerPC/PowerPc.h" // Core
|
|
|
|
#include "../../../../Source/Core/DiscIO/Src/FileSystemGCWii.h" // This file has #include "Filesystem.h"
|
|
#include "../../../../Source/Core/DiscIO/Src/VolumeCreator.h"
|
|
|
|
#include "../../Player/Src/PlayerExport.h" // Local player
|
|
//////////////////////////////////
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
// Declarations and definitions
|
|
// ¯¯¯¯¯¯¯¯¯¯
|
|
namespace MusicMod
|
|
{
|
|
|
|
struct MyFilesStructure
|
|
{
|
|
std::string path;
|
|
int offset; // Is int enough, how high does offset go?
|
|
};
|
|
|
|
std::vector <MyFilesStructure> MyFiles;
|
|
void StructSort (std::vector <MyFilesStructure> &MyFiles);
|
|
|
|
|
|
// Playback
|
|
std::string CurrentFile;
|
|
std::string CurrentPlayFile;
|
|
std::string CurrentPlayFilePath;
|
|
std::string unique_gameid;
|
|
|
|
std::string MusicPath;
|
|
|
|
DiscIO::CFileSystemGCWii* my_pFileSystem;
|
|
|
|
int WritingFile = false;
|
|
bool dllloaded = false;
|
|
|
|
extern bool bShowConsole; // Externally define
|
|
extern int GlobalVolume;
|
|
//////////////////////////////////
|
|
|
|
|
|
// =======================================================================================
|
|
// Supported music files
|
|
// ---------------------------------------------------------------------------------------
|
|
bool CheckFileEnding(std::string FileName)
|
|
{
|
|
if (
|
|
(FileName.find(".adp") != std::string::npos) // 1080 Avalanche, Crash Bandicoot etc
|
|
|| (FileName.find(".afc") != std::string::npos) // Zelda WW
|
|
|| (FileName.find(".ast") != std::string::npos) // Zelda TP, Mario Kart
|
|
|| (FileName.find(".dsp") != std::string::npos) // Metroid Prime
|
|
|| (FileName.find(".hps") != std::string::npos) // SSB Melee
|
|
)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
// =======================================================================================
|
|
|
|
|
|
// =======================================================================================
|
|
// A function to sort the filelist table after offset
|
|
// ------------------------
|
|
void StructSort (std::vector <MyFilesStructure> &MyFiles)
|
|
{
|
|
MyFilesStructure temp;
|
|
|
|
//NOTICE_LOG(AUDIO,"StructSort > Begin\n");
|
|
|
|
for(int i = 0; i < MyFiles.size() - 1; i++)
|
|
{
|
|
for (int j = i + 1; j < MyFiles.size(); j++)
|
|
{
|
|
if (MyFiles[ i ].offset > MyFiles[ j ].offset) //comparing cost
|
|
{
|
|
temp = MyFiles[ i ]; // Swapping entire struct
|
|
MyFiles[ i ] = MyFiles[ j ];
|
|
MyFiles[ j ] = temp;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
for (long i=1; i<(long)MyFiles.size(); ++i)
|
|
{
|
|
std::cout << i << " " << MyFiles[i].path.c_str() << "#" << MyFiles[i].offset << "\n";
|
|
}
|
|
|
|
//NOTICE_LOG(AUDIO,"StructSort > Done\n");
|
|
}
|
|
// ============================
|
|
|
|
|
|
// =======================================================================================
|
|
/* Run these things once when Dolphin starts */
|
|
// ------------------------
|
|
void ShowConsole(bool Show)
|
|
{
|
|
if (Show)
|
|
{
|
|
/* What we do here is run StartConsoleWin() in Common directly after each
|
|
other first in the exe then in the DLL, sometimes this would give me a rampant memory
|
|
usage increase until the exe crashed at 700 MB memory usage or something like that.
|
|
For that reason I'm trying to sleep for a moment between them here. */
|
|
Sleep(100);
|
|
Player_Console(true);
|
|
}
|
|
else
|
|
{
|
|
Player_Console(false);
|
|
}
|
|
// Console::Open(100, 2000, "MusicMod", true); // Give room for 2000 rows
|
|
}
|
|
|
|
void Init()
|
|
{
|
|
// These things below will not need to be updated after a new game is started
|
|
if (dllloaded) return;
|
|
|
|
// ---------------------------------------
|
|
// Load config
|
|
// ---------------------
|
|
IniFile file;
|
|
file.Load("Plainamp.ini");
|
|
file.Get("Interface", "ShowConsole", &MusicMod::bShowConsole, false);
|
|
file.Get("Plainamp", "Volume", &MusicMod::GlobalVolume, 125);
|
|
// -------
|
|
|
|
// ---------------------------------------
|
|
// Make a debugging window
|
|
// ---------------------
|
|
//if(MusicMod::bShowConsole) ShowConsole();
|
|
|
|
// Write version
|
|
#ifdef _M_X64
|
|
NOTICE_LOG(AUDIO,"64 bit version\n");
|
|
#else
|
|
NOTICE_LOG(AUDIO,"32 bit version\n");
|
|
#endif
|
|
// -----------
|
|
|
|
// Set volume
|
|
Player_Volume(MusicMod::GlobalVolume);
|
|
|
|
// Show DLL status
|
|
Player_Main(MusicMod::bShowConsole);
|
|
//play_file("c:\\demo36_02.ast");
|
|
//NOTICE_LOG(AUDIO,"DLL loaded\n");
|
|
|
|
dllloaded = true; // Do this once
|
|
}
|
|
// ============================
|
|
|
|
|
|
// =======================================================================================
|
|
/* Function: Read the GC file system when a game is booted
|
|
Called from: BootManager.cpp */
|
|
// ------------------------
|
|
void Main(std::string FileName)
|
|
{
|
|
//
|
|
DiscIO::IVolume* pVolume = DiscIO::CreateVolumeFromFilename(FileName);
|
|
//
|
|
my_pFileSystem = new DiscIO::CFileSystemGCWii(pVolume);
|
|
// Check that it worked
|
|
if (my_pFileSystem->m_FileInfoVector.size() == 0) NOTICE_LOG(AUDIO, "Volume creation failed")
|
|
|
|
/* We have to sort the files according to offset so that out scan in Blob.cpp works.
|
|
Because StructSort() only works for MyFilesStructure I copy the offset and filenames
|
|
to a new vetor first. */
|
|
MyFiles.resize(my_pFileSystem->m_FileInfoVector.size()); // Set size
|
|
for (size_t i = 0; i < my_pFileSystem->m_FileInfoVector.size(); i++)
|
|
{
|
|
MyFiles.at(i).offset = my_pFileSystem->m_FileInfoVector.at(i).m_Offset;
|
|
MyFiles.at(i).path = my_pFileSystem->m_FileInfoVector.at(i).m_FullPath;
|
|
}
|
|
|
|
// Sort the files by offset
|
|
StructSort(MyFiles);
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// Make Music directory
|
|
// -------------------------
|
|
LPSECURITY_ATTRIBUTES attr;
|
|
attr = NULL;
|
|
MusicPath = "Music\\";
|
|
NOTICE_LOG(AUDIO,"Created a Music directory\n");
|
|
CreateDirectory(MusicPath.c_str(), attr);
|
|
// ----------------------------------------------------------------------------------------
|
|
}
|
|
|
|
|
|
// =======================================================================================
|
|
// Check if we should play this file
|
|
// ---------------------------------------------------------------------------------------
|
|
void CheckFile(std::string File, int FileNumber)
|
|
{
|
|
// Do nothing if we found the same file again
|
|
if (CurrentFile == File) return;
|
|
|
|
// Check if it's a music file
|
|
if (CheckFileEnding(File.c_str()))
|
|
{
|
|
/* Don't restart the playback if we find the same music file again. If the game is playing
|
|
a streaming music file it may read from it once in a while, after it has read other
|
|
files in between, if did not do this check we would restart the playback in those cases */
|
|
if (CurrentPlayFile == File) return;
|
|
|
|
// Notify the user
|
|
NOTICE_LOG(AUDIO,"\n >>> (%i/%i) Match %s\n", FileNumber, MyFiles.size(), File.c_str());
|
|
|
|
// Save the matched file
|
|
CurrentPlayFile = File;
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// We will now save the file to the PC hard drive
|
|
// ------------------
|
|
// Get the filename
|
|
std::size_t pointer = File.find_last_of("/");
|
|
std::string fragment = File.substr (0, (pointer-0));
|
|
int compare = File.length() - fragment.length(); // Get the length of the filename
|
|
fragment = File.substr ((pointer+1), compare); // Now we have the filename
|
|
|
|
// Create the target file path
|
|
std::string FilePath = (MusicPath + fragment);
|
|
|
|
WritingFile = true; // Avoid detecting the file we are writing
|
|
NOTICE_LOG(AUDIO, "Writing '%s' to '%s'", File.c_str(), FilePath.c_str());
|
|
if (!my_pFileSystem->ExportFile(File.c_str(), FilePath.c_str()))
|
|
NOTICE_LOG(AUDIO, "ERROR: ExportFile failed");
|
|
WritingFile = false;
|
|
// ------------------------------------------------------
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// Play the file we found
|
|
// ------------------
|
|
if(dllloaded)
|
|
{
|
|
Player_Play((char*)FilePath.c_str()); // retype it from const char* to char*
|
|
} else {
|
|
NOTICE_LOG(AUDIO, "Warning > Music DLL is not loaded");
|
|
}
|
|
// ------------------------------------------------------
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// Remove the last file, if any
|
|
// ------------------
|
|
if(CurrentPlayFilePath.length() > 0)
|
|
{
|
|
if(!remove(CurrentPlayFilePath.c_str()))
|
|
{
|
|
NOTICE_LOG(AUDIO,"The program failed to remove '%s'", CurrentPlayFilePath.c_str());
|
|
} else {
|
|
NOTICE_LOG(AUDIO,"The program removed '%s'", CurrentPlayFilePath.c_str());
|
|
}
|
|
}
|
|
// ------------------------------------------------------
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
// Save the current playing file
|
|
// ------------------
|
|
CurrentPlayFilePath = FilePath; // Save the filename so we can remove it later
|
|
}
|
|
|
|
// Tell the user about the files we ignored
|
|
NOTICE_LOG(AUDIO,"(%i/%i) Ignored '%s'\n", FileNumber, MyFiles.size(), File.c_str());
|
|
|
|
// Update the current file
|
|
CurrentFile = File;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
// Find the current filename for a certain offset on the GC fileystem
|
|
// ¯¯¯¯¯¯¯¯¯¯¯¯¯
|
|
void FindFilename(u64 offset, u64 size)
|
|
{
|
|
// =======================================================================================
|
|
/* Only do this test:
|
|
1. After boot, not on ISO scan
|
|
2. Not if offset = 0.
|
|
3. Not when WritingFile. We will lead to calls back to here (Read) and it will mess
|
|
upp the scanning */
|
|
if(PowerPC::GetState() == PowerPC::CPUState::CPU_RUNNING && offset != 0 && !WritingFile)
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
/* Get the filename. Here we go through all files until we come to the file that has
|
|
the matching offset. Before MyFiles has data this loop will go nowhere. We have to
|
|
specify (MyFiles.size() - 1) because we will be reading MyFiles.at(i + 1).
|
|
MyFiles.size() is the amount of files on the disc, and the last file is
|
|
MyFiles.at(MyFiles.size() - 1) */
|
|
// ---------------------------------------------------------------------------------------
|
|
for (int i = 0; i < (int)(MyFiles.size() - 1); ++i)
|
|
{
|
|
// Log
|
|
//NOTICE_LOG(AUDIO, ">>> Comparing %s [#%i, Size: %i, Location: %u] with %u", MyFiles[i].path.c_str(), i, size, MyFiles[i].offset, offset);
|
|
|
|
// ---------------------------------------------------------------------------------------
|
|
/* If we assume that myoffset is the begginning of every file this works.
|
|
Suppose there are three files
|
|
1 is 0 to 149
|
|
2 is 150 to 170
|
|
3 is 171 to 200
|
|
If the offset is 160 the game is reading file number two, for example
|
|
myoffset = 150: (myoffset >= offset) == false
|
|
myoffset = 171: (myoffset >= offset) == true, break
|
|
|
|
However if the offset is 195 the game is reading the last file and we will get
|
|
myoffset = 171: (myoffset >= offset) == false
|
|
we therefore need to add the condition (offset > MyFiles[MyFiles.size() - 1].offset)
|
|
to. */
|
|
if (MyFiles[i + 1].offset >= offset || offset > MyFiles[MyFiles.size() - 1].offset)
|
|
{
|
|
// Now we know that the game is reading from MyFiles[i].path
|
|
CheckFile(MyFiles[i].path, i);
|
|
|
|
// Stop checking
|
|
break;
|
|
}
|
|
|
|
// If the file check failed
|
|
if (i == (int)(MyFiles.size() - 1) - 1)
|
|
{
|
|
NOTICE_LOG(AUDIO, "ERROR: GC filename search failed");
|
|
}
|
|
}
|
|
} // This ends the entire filescan
|
|
// =======================================================================================
|
|
}
|
|
/////////////////////////////////
|
|
|
|
|
|
} // end of namespace
|