Descent3/legacy/musicutils/musicutilsDlg.cpp
2024-06-15 20:13:59 +02:00

1086 lines
30 KiB
C++

/*
* Descent 3
* Copyright (C) 2024 Parallax Software
*
* 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 <http://www.gnu.org/licenses/>.
*/
// musicutilsDlg.cpp : implementation file
//
#include "stdafx.h"
#include "musicutils.h"
#include "musicutilsDlg.h"
#include "RawCvtDlg.h"
#include "PlaySongDlg.h"
#include "InsSongDlg.h"
#include "RoomLinkDlg.h"
#include "TrueFalseDlg.h"
#include "musicplay.h"
#include "pserror.h"
#include "Application.h"
#include "streamaudio.h"
#include "ddio.h"
#include "macros.h"
#include "inffile.h"
#include "mem.h"
#include <io.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
bool Dedicated_server = false;
// OMF INF FILE READ
#define OMFCMD_NUM 12
#define OMFCMD_INTRO 0
#define OMFCMD_BKG 1
#define OMFCMD_COMBAT 2
#define OMFCMD_ENDCOMBAT 5
#define OMFCMD_LOOPING 10
#define OMFCMD_ATTACHROOM 11
const char *OMFCommands[OMFCMD_NUM] = {"intro", "scene", "combat", "danger", "death", "endcombat",
"q2", "q2", "q2", "q2", "looping", "attachroom"};
int OMFLex(const char *command) {
for (int i = 0; i < OMFCMD_NUM; i++)
if (strcmp(OMFCommands[i], command) == 0)
return i;
return INFFILE_ERROR;
}
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog {
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange *pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) {
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange *pDX) {
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CMusicutilsDlg dialog
CMusicutilsDlg::CMusicutilsDlg(CWnd *pParent /*=NULL*/) : CDialog(CMusicutilsDlg::IDD, pParent) {
//{{AFX_DATA_INIT(CMusicutilsDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMusicutilsDlg::DoDataExchange(CDataExchange *pDX) {
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CMusicutilsDlg)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
// returns if save modified was canceled
bool CMusicutilsDlg::SaveModified() {
if (m_modified) {
int res = AfxMessageBox("Do you wish to save the current theme?", MB_YESNOCANCEL);
if (res == IDYES) {
OnFileSave();
} else if (res == IDCANCEL)
return false;
}
return true;
}
BEGIN_MESSAGE_MAP(CMusicutilsDlg, CDialog)
//{{AFX_MSG_MAP(CMusicutilsDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_COMMAND(IDC_TOOLS_MAKESTREAM, OnToolsMakeStream)
ON_WM_DESTROY()
ON_COMMAND(IDC_FILE_QUIT, OnFileQuit)
ON_COMMAND(IDC_TOOLS_PLAYSONG, OnToolsPlaySong)
ON_COMMAND(IDC_FILE_NEW, OnFileNew)
ON_NOTIFY(TVN_KEYDOWN, IDC_THEME_TREE, OnKeydownThemeTree)
ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
ON_COMMAND(ID_FILE_SAVE, OnFileSave)
ON_COMMAND(ID_FILE_SAVEAS, OnFileSaveas)
ON_NOTIFY(NM_RETURN, IDC_THEME_TREE, OnReturnThemeTree)
ON_NOTIFY(NM_DBLCLK, IDC_THEME_TREE, OnDblclkThemeTree)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CMusicutilsDlg message handlers
BOOL CMusicutilsDlg::OnInitDialog() {
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu *pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL) {
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty()) {
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
hBkgLeaf = NULL;
hCombatLeaf = NULL;
hDangerLeaf = NULL;
hIntroLeaf = NULL;
hEndCombatLeaf = NULL;
// TODO: Add extra initialization here
tWin32AppInfo appinfo;
appinfo.hwnd = m_hWnd;
appinfo.hinst = theApp.m_hInstance;
appinfo.flags = OEAPP_WINDOWED;
MusicUtils = (oeApplication *)new oeWin32Application(&appinfo);
#ifdef _DEBUG
error_Init(true, false, "MusicUtils");
#else
error_Init(false, false);
#endif
// initialize sound systems
MusicPlayInit(MusicUtils);
// initialize tree control based off on new tree.
m_modified = false;
NewTheme();
// set focus to tree control
GetDlgItem(IDC_THEME_TREE)->SetFocus();
mem_Init();
return FALSE; // return TRUE unless you set the focus to a control
}
void CMusicutilsDlg::OnDestroy() {
FreeTree();
MusicPlayClose();
delete MusicUtils;
MusicUtils = NULL;
CDialog::OnDestroy();
}
void CMusicutilsDlg::OnSysCommand(UINT nID, LPARAM lParam) {
if ((nID & 0xFFF0) == IDM_ABOUTBOX) {
CAboutDlg dlgAbout;
dlgAbout.DoModal();
} else {
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CMusicutilsDlg::OnPaint() {
if (IsIconic()) {
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM)dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
} else {
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CMusicutilsDlg::OnQueryDragIcon() { return (HCURSOR)m_hIcon; }
void CMusicutilsDlg::OnOK() {
// do nothing! (prevents pressing enter causing app to quit!)
}
void CMusicutilsDlg::OnCancel() {
if (SaveModified()) {
CDialog::OnCancel();
}
}
BOOL CMusicutilsDlg::OnCommand(WPARAM wParam, LPARAM lParam) {
// MASSIVE HACK TO PREVENT IDOK FROM BEING PASSED TO DIALOG (BY ENTER KEY)
// SEND ENTER KEY TO TREE CONTROL.
// There must be a better way to do this!.
if (wParam != IDOK)
return CDialog::OnCommand(wParam, lParam);
else {
GetDlgItem(IDC_THEME_TREE)->SendMessage(WM_KEYDOWN, VK_RETURN, 0);
GetDlgItem(IDC_THEME_TREE)->SendMessage(WM_KEYUP, VK_RETURN, 0);
return FALSE;
}
}
///////////////////////////////////////////////////////////////////////////////
// Menu Command handlers
void CMusicutilsDlg::OnFileNew() {
if (SaveModified())
NewTheme();
}
void CMusicutilsDlg::OnFileQuit() { OnCancel(); }
void CMusicutilsDlg::OnFileOpen() {
CFileDialog opendlg(TRUE, "omf", NULL, OFN_PATHMUSTEXIST, "Outrage Music Files(*.omf)|*.omf||", this);
if (!SaveModified())
return;
if (opendlg.DoModal() == IDOK) {
OpenThemeFile(opendlg.GetPathName());
}
}
void CMusicutilsDlg::OnFileSave() {
if (m_themefilename.IsEmpty())
OnFileSaveas();
else
SaveThemeFile();
}
void CMusicutilsDlg::OnFileSaveas() {
CFileDialog savedlg(FALSE, "omf", (LPCTSTR)m_themefilename, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
"Outrage Music Files(*.omf)|*.omf||", this);
if (savedlg.DoModal() == IDOK) {
m_themefilename = savedlg.GetPathName();
SaveThemeFile();
}
}
void CMusicutilsDlg::OpenThemeFile(const CString &filename) {
#define CLEAR_PATHLIST \
for (iter = 0; iter < 16; curpath[iter++][0] = 0) \
; \
iter = 0
mprintf(0, "Opening music theme file...\n");
InfFile inf;
char operand[INFFILE_LINELEN]; // operand
char curpath[16][INFFILE_LINELEN]; // stores complete path of song.
int iter;
tSongItem *cursong = NULL;
int curcmdtype;
// open file
if (!inf.Open((LPCTSTR)filename, "[theme file]", OMFLex)) {
AfxMessageBox("Unable to open theme file.");
return;
}
// create new theme.
NewTheme();
m_themefilename = filename;
CLEAR_PATHLIST;
iter = 0;
curcmdtype = -1;
// check if valid cockpit file
while (inf.ReadLine()) {
int cmd;
while ((cmd = inf.ParseLine(operand, INFFILE_LINELEN)) > INFFILE_ERROR) {
tSongItem song;
// reset all song properties (song) used just in InsertSong
song.repeat = false;
song.roomlink = -1;
switch (cmd) {
case OMFCMD_INTRO:
song.strSongPath = curpath[0];
cursong = InsertSong(hIntroLeaf, &song);
curcmdtype = cmd;
CLEAR_PATHLIST;
break;
case OMFCMD_BKG:
song.strSongPath = curpath[0];
cursong = InsertSong(hBkgLeaf, &song);
curcmdtype = cmd;
CLEAR_PATHLIST;
break;
case OMFCMD_COMBAT:
song.strSongPath = curpath[0];
cursong = InsertSong(hCombatLeaf, &song);
curcmdtype = cmd;
CLEAR_PATHLIST;
break;
case OMFCMD_ENDCOMBAT:
song.strSongPath = curpath[0];
cursong = InsertSong(hEndCombatLeaf, &song);
curcmdtype = cmd;
CLEAR_PATHLIST;
break;
case OMFCMD_LOOPING:
if (strcmp(operand, "true") == 0)
cursong->repeat = true;
else if (strcmp(operand, "false") == 0)
cursong->repeat = false;
else {
cmd = INFFILE_ERROR;
goto force_error;
}
SetSongProperty(SONGPROP_REPEAT, cursong);
break;
case OMFCMD_ATTACHROOM:
if (curcmdtype == OMFCMD_BKG) {
cursong->roomlink = atoi(operand);
SetSongProperty(SONGPROP_ROOMLINK, cursong);
}
break;
case INFFILE_SYMBOL:
if (iter == 16)
Int3();
strcpy(curpath[iter++], operand);
break;
}
}
force_error:
if (cmd == INFFILE_ERROR)
mprintf(0, "Error in music file %s line %d.\n", filename, inf.line());
}
inf.Close();
m_modified = false;
}
void CMusicutilsDlg::SaveThemeFile() {
CTreeCtrl *tree = (CTreeCtrl *)GetDlgItem(IDC_THEME_TREE);
tSongItem *song;
HTREEITEM hTreeItem;
char filename[PSFILENAME_LEN], ext[PSFILENAME_LEN];
int iter;
mprintf(0, "Saving music theme file...\n");
// okay, we need to extract each tree item and create the necessary 'code' for
// musical theme file.
// create header
CString codestr;
CString tempstr;
codestr = "[theme file]\n\n@ Outrage Music File Theme\n@ " + m_themename + "\n@\n";
codestr += "@ All #zz??????? symbols are generated by Outrage Music Utilities\n@ they should not be modified.\n\n";
// do intro music.
codestr += "@ intro music\n";
iter = 0;
hTreeItem = tree->GetChildItem(hIntroLeaf);
while (hTreeItem) {
song = GetSongFromTreeItem(hTreeItem);
if (song) {
ddio_SplitPath((LPCTSTR)song->strSongPath, NULL, filename, ext);
tempstr.Format("#zzint%d=%s\n", iter, (LPCTSTR)song->strSongPath);
codestr += (tempstr + "intro=") + filename;
codestr += ext;
}
hTreeItem = tree->GetNextItem(hTreeItem, TVGN_NEXT);
codestr += "\n";
}
// do background
codestr += "\n@ background music\n";
iter = 0;
hTreeItem = tree->GetChildItem(hBkgLeaf);
while (hTreeItem) {
song = GetSongFromTreeItem(hTreeItem);
if (song) {
ddio_SplitPath((LPCTSTR)song->strSongPath, NULL, filename, ext);
tempstr.Format("#zzbkg%d=%s\n", iter, (LPCTSTR)song->strSongPath);
codestr += (tempstr + "scene=") + filename;
codestr += ext;
if (song->repeat)
codestr += ", looping=true";
if (song->roomlink > -1) {
tempstr.Format(", attachroom=%d", song->roomlink);
codestr += tempstr;
}
}
hTreeItem = tree->GetNextItem(hTreeItem, TVGN_NEXT);
codestr += "\n";
}
// do combat
codestr += "\n@ combat music\n";
iter = 0;
hTreeItem = tree->GetChildItem(hCombatLeaf);
while (hTreeItem) {
song = GetSongFromTreeItem(hTreeItem);
if (song) {
ddio_SplitPath((LPCTSTR)song->strSongPath, NULL, filename, ext);
tempstr.Format("#zzcmb%d=%s\n", iter, (LPCTSTR)song->strSongPath);
codestr += (tempstr + "combat=") + filename;
codestr += ext;
if (song->repeat)
codestr += ", looping=true";
}
hTreeItem = tree->GetNextItem(hTreeItem, TVGN_NEXT);
codestr += "\n";
}
// do danger
codestr += "\n@ danger music\n";
iter = 0;
hTreeItem = tree->GetChildItem(hDangerLeaf);
while (hTreeItem) {
song = GetSongFromTreeItem(hTreeItem);
if (song) {
ddio_SplitPath((LPCTSTR)song->strSongPath, NULL, filename, ext);
tempstr.Format("#zzdgr%d=%s\n", iter, (LPCTSTR)song->strSongPath);
codestr += (tempstr + "danger=") + filename;
codestr += ext;
if (song->repeat)
codestr += ", looping=true";
}
hTreeItem = tree->GetNextItem(hTreeItem, TVGN_NEXT);
codestr += "\n";
}
// do end combat
codestr += "\n@ end combat music\n";
iter = 0;
hTreeItem = tree->GetChildItem(hEndCombatLeaf);
while (hTreeItem) {
song = GetSongFromTreeItem(hTreeItem);
if (song) {
ddio_SplitPath((LPCTSTR)song->strSongPath, NULL, filename, ext);
tempstr.Format("#zzecm%d=%s\n", iter, (LPCTSTR)song->strSongPath);
codestr += (tempstr + "endcombat=") + filename;
codestr += ext;
}
hTreeItem = tree->GetNextItem(hTreeItem, TVGN_NEXT);
codestr += "\n";
}
// save out file
FILE *fp = fopen((LPCTSTR)m_themefilename, "wt");
if (fp) {
if (fputs((LPCSTR)codestr, fp) == EOF) {
fclose(fp);
AfxMessageBox("Unable to save theme file. Check disk space.");
return;
}
fclose(fp);
m_modified = false;
} else {
AfxMessageBox("Unable to save theme file. Check disk space.");
}
}
//////////////////////////////////////////////////////////////////////////////
// These are Music tools
// Stream converter. Esentially choose a 'raw' file to convert to an
// outrage stream format (osf)
void CMusicutilsDlg::OnToolsMakeStream() {
CFileDialog opendlg(TRUE, "raw", NULL, OFN_PATHMUSTEXIST, "Raw files(*.raw)|*.raw||", this);
CString srcfilestr;
CString destfilestr;
// get filename to convert (RAW file)
if (opendlg.DoModal() != IDOK) {
AfxMessageBox("Stream conversion action canceled.");
return;
}
srcfilestr = opendlg.GetPathName();
destfilestr = srcfilestr.Left(srcfilestr.Find('.'));
// do conversion dialog.
CRawCvtDlg compdlg;
CFileDialog savedlg(FALSE, "osf", (LPCTSTR)destfilestr, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
"Outrage Stream Files(*.osf)|*.osf||", this);
compdlg.m_Resolution = 1;
compdlg.m_Frequency = 1;
compdlg.m_Channels = 0;
compdlg.m_VolScale = "0.97";
compdlg.m_Xforms = 7;
if (compdlg.DoModal() != IDOK)
goto strconv_err;
// okay prompt for save location
if (savedlg.DoModal() == IDOK) {
OSFArchive osf;
uint8_t strmtype = 0, strmcomp = 0, strmfmt = 0, xforms;
uint32_t rate;
// determine stream type.
strmtype = OSF_DIGITAL_STRM;
strmcomp = (compdlg.m_Resolution == 1) ? OSF_DIGIACM_STRM : OSF_DIGIRAW_STRM;
strmfmt |= ((compdlg.m_Channels == 1) ? SAF_STEREO_MASK : SAF_MONO_MASK);
strmfmt |= ((compdlg.m_Resolution == 1) ? SAF_16BIT_MASK : SAF_8BIT_MASK);
rate = (compdlg.m_Frequency == 2) ? 44100 : (compdlg.m_Frequency == 1) ? 22050 : 11025;
xforms = (uint8_t)compdlg.m_Xforms;
// start file write.
destfilestr = savedlg.GetPathName();
if (!osf.Open((LPCTSTR)destfilestr, true)) {
AfxMessageBox("Unable to open file to write out stream.");
goto strconv_err;
}
// first compress audio file if it's the proper format, otherwise just copy into larger file.
// note that 16-bit is the only supported format, damn interplay.
if (strmtype == OSF_DIGITAL_STRM) {
if (!SaveDigitalStream(&osf, srcfilestr, strmcomp, strmfmt, rate, (LPCTSTR)compdlg.m_StreamName, xforms,
compdlg.m_MeasureSize)) {
osf.Close();
AfxMessageBox("Unable to open or save stream data.");
goto strconv_err;
}
} else {
Int3();
}
osf.Close();
}
return;
strconv_err:
AfxMessageBox("Stream conversion action canceled.");
}
// Plays a song
void CMusicutilsDlg::OnToolsPlaySong() {
CFileDialog opendlg(TRUE, "osf", NULL, OFN_PATHMUSTEXIST, "Outrage Stream Files(*.osf)|*.osf||", this);
// get filename to play
if (opendlg.DoModal() != IDOK) {
return;
}
CPlaySongDlg playdlg;
playdlg.m_StreamName = opendlg.GetPathName();
playdlg.DoModal();
}
#define FILEBUFFER_LENGTH (64 * 1024)
static uint8_t StaticFileBuffer[FILEBUFFER_LENGTH];
bool CMusicutilsDlg::SaveDigitalStream(OSFArchive *osf, const CString &rawfilename, uint8_t compression, uint8_t format,
uint32_t samples, const CString &realname, uint8_t xforms, int measure) {
FILE *fpin = NULL;
int i;
uint32_t filelen;
int nblocks;
if (compression == OSF_DIGIACM_STRM) {
// prepare to execute audcomp process.
PROCESS_INFORMATION proc_info;
STARTUPINFO si;
CString cmdline;
LPTSTR cmdbuffer;
int chan = (format & SAF_STEREO_MASK) ? 2 : 1;
int rate = (int)samples;
int cmpfmt = (rate == 22050) ? 4 : (rate == 44100) ? 8 : 1;
cmdline.Format("f:\\tools\\audcomp.exe %s _zztemp.acm /l:%d /c:%d /f:%d /r:%d", (LPCTSTR)rawfilename, xforms, chan,
cmpfmt, rate);
cmdbuffer = cmdline.GetBuffer(256);
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
if (!CreateProcess(NULL, cmdbuffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &proc_info)) {
AfxMessageBox("Failed to start audio compression tool f:\tools\audcomp.exe");
return false;
}
WaitForInputIdle(proc_info.hProcess, INFINITE);
while (WaitForSingleObject(proc_info.hProcess, 0) != WAIT_OBJECT_0) {
}
CloseHandle(proc_info.hThread);
CloseHandle(proc_info.hProcess);
// okay, we should have a file called _zztemp.acm. lets copy this directly to our output file.
fpin = fopen("_zztemp.acm", "rb");
if (!fpin) {
AfxMessageBox("Compression failed due to error in the compress utility.");
return false;
}
} else {
// raw (uncompressed) data.
fpin = fopen((LPCTSTR)rawfilename, "rb");
if (!fpin)
return false;
}
// start writing out data.
tOSFDigiHdr digihdr;
ASSERT(fpin);
filelen = (uint32_t)_filelength(fileno(fpin));
nblocks = filelen / FILEBUFFER_LENGTH;
// write out data.
for (i = 0; i < nblocks; i++) {
if (!fread(StaticFileBuffer, FILEBUFFER_LENGTH, 1, fpin)) {
fclose(fpin);
return false;
}
if (!osf->WriteBlock(StaticFileBuffer, FILEBUFFER_LENGTH)) {
fclose(fpin);
return false;
}
}
if (filelen % FILEBUFFER_LENGTH) {
if (!fread(StaticFileBuffer, filelen % FILEBUFFER_LENGTH, 1, fpin)) {
fclose(fpin);
return false;
}
if (!osf->WriteBlock(StaticFileBuffer, filelen % FILEBUFFER_LENGTH)) {
fclose(fpin);
return false;
}
}
digihdr.measure = measure;
// save header info
if (!osf->SaveHeader(OSF_DIGITAL_STRM, compression, format, samples, filelen, &digihdr, (LPCTSTR)realname)) {
AfxMessageBox("Failed to write out OSF header info.");
fclose(fpin);
return false;
}
// close
fclose(fpin);
if (compression == OSF_DIGIACM_STRM) {
remove("_zztemp.acm");
}
return true;
}
void CMusicutilsDlg::GetStreamInfo(const CString &filename, CString &realname) {
OSFArchive archive;
if (archive.Open((LPCTSTR)filename)) {
realname = archive.StreamName();
archive.Close();
} else {
realname = "Error reading stream.";
}
}
//////////////////////////////////////////////////////////////////////////////
// TREE CONTROL
void CMusicutilsDlg::OnReturnThemeTree(NMHDR *pNMHDR, LRESULT *pResult) {
CTreeCtrl *tree = (CTreeCtrl *)GetDlgItem(pNMHDR->idFrom);
HTREEITEM hSelectedItem = tree->GetSelectedItem();
DoTreeItem(hSelectedItem);
*pResult = 0;
}
void CMusicutilsDlg::OnDblclkThemeTree(NMHDR *pNMHDR, LRESULT *pResult) {
// TODO: Add your control notification handler code here
CTreeCtrl *tree = (CTreeCtrl *)GetDlgItem(pNMHDR->idFrom);
HTREEITEM hSelectedItem = tree->GetSelectedItem();
DoTreeItem(hSelectedItem);
*pResult = 0;
}
// Tree control key handler
void CMusicutilsDlg::OnKeydownThemeTree(NMHDR *pNMHDR, LRESULT *pResult) {
TV_KEYDOWN *pTVKeyDown = (TV_KEYDOWN *)pNMHDR;
CTreeCtrl *tree = (CTreeCtrl *)GetDlgItem(pTVKeyDown->hdr.idFrom);
HTREEITEM hSelectedItem = tree->GetSelectedItem();
switch (pTVKeyDown->wVKey) {
case VK_INSERT:
if (hSelectedItem == hBkgLeaf || hSelectedItem == hCombatLeaf || hSelectedItem == hDangerLeaf ||
hSelectedItem == hIntroLeaf || hSelectedItem == hEndCombatLeaf)
InsertTreeItem(hSelectedItem);
break;
case VK_DELETE:
FreeTreeItem(hSelectedItem);
break;
case VK_SPACE:
tree->Expand(hSelectedItem, TVE_TOGGLE);
break;
}
*pResult = 0;
}
// creates a new theme, resetting the tree control, etc.
void CMusicutilsDlg::NewTheme() {
// do tree control stuff
CTreeCtrl *tree = (CTreeCtrl *)GetDlgItem(IDC_THEME_TREE);
FreeTree();
hIntroLeaf = InsertLeaf(TVI_ROOT, "Intro", LEAFTYPE_CATEGORY);
hBkgLeaf = InsertLeaf(TVI_ROOT, "Background", LEAFTYPE_CATEGORY);
hCombatLeaf = InsertLeaf(TVI_ROOT, "Combat", LEAFTYPE_CATEGORY);
hDangerLeaf = InsertLeaf(TVI_ROOT, "Danger", LEAFTYPE_CATEGORY);
hEndCombatLeaf = InsertLeaf(TVI_ROOT, "End Combat", LEAFTYPE_CATEGORY);
m_themefilename.Empty();
m_modified = false;
}
void CMusicutilsDlg::FreeTree() {
CTreeCtrl *tree = (CTreeCtrl *)GetDlgItem(IDC_THEME_TREE);
// delete all items
FreeTreeItems(hEndCombatLeaf);
FreeTreeItems(hIntroLeaf);
FreeTreeItems(hBkgLeaf);
FreeTreeItems(hCombatLeaf);
FreeTreeItems(hDangerLeaf);
hBkgLeaf = NULL;
hCombatLeaf = NULL;
hDangerLeaf = NULL;
hIntroLeaf = NULL;
hEndCombatLeaf = NULL;
}
// frees all daya for a tree leaf (and all children.)
void CMusicutilsDlg::FreeTreeItems(HTREEITEM hLeaf) {
CTreeCtrl *tree = (CTreeCtrl *)GetDlgItem(IDC_THEME_TREE);
if (hLeaf) {
HTREEITEM hItem = tree->GetChildItem(hLeaf);
HTREEITEM hOldItem;
while (hItem) {
hOldItem = hItem;
hItem = FreeTreeItem(hOldItem);
}
tree->DeleteItem(hLeaf);
}
}
#define TXT_LINKLEVEL "Attach to level room = "
#define TXT_REPEAT "Looping = "
// inserts an item into the tree.
void CMusicutilsDlg::InsertTreeItem(HTREEITEM hParentItem) {
CInsSongDlg insdlg;
// only allow insertions at root leafs.
if (insdlg.DoModal() != IDOK)
return;
tSongItem song;
song.strSongPath = insdlg.m_SongPath;
song.repeat = false;
song.roomlink = -1;
InsertSong(hParentItem, &song);
m_modified = true;
}
HTREEITEM CMusicutilsDlg::FreeTreeItem(HTREEITEM hItem) {
CTreeCtrl *tree = (CTreeCtrl *)GetDlgItem(IDC_THEME_TREE);
LPARAM param;
HTREEITEM hNextItem = NULL;
// only allow us to delete a song (not a property)
param = GetLeafParam(hItem);
if (param > 0) {
// delete all children too.
tSongItem *song = (tSongItem *)param;
if (song->hTI_repeat)
tree->DeleteItem(song->hTI_repeat);
if (song->hTI_lvllink)
tree->DeleteItem(song->hTI_lvllink);
delete song;
hNextItem = tree->GetNextItem(hItem, TVGN_NEXT);
tree->DeleteItem(hItem);
}
m_modified = true;
return hNextItem;
}
// does editing function on tree item.
void CMusicutilsDlg::DoTreeItem(HTREEITEM hItem) {
CTreeCtrl *tree = (CTreeCtrl *)GetDlgItem(IDC_THEME_TREE);
tSongItem *song;
LPARAM param;
// get tree item info. if a song, then expand, if a property, do property.
param = GetLeafParam(hItem);
song = GetSongFromTreeItem(hItem);
switch (param) {
case LEAFTYPE_PROPERTY:
// check if item matches any item inside the song structure (except for hTreeItem)
if (hItem == song->hTI_lvllink) {
CRoomLinkDlg dlg;
dlg.m_Room = song->roomlink;
if (dlg.DoModal() == IDOK) {
if (dlg.m_RoomCheck)
song->roomlink = dlg.m_Room;
else
song->roomlink = -1;
SetSongProperty(SONGPROP_ROOMLINK, song);
}
} else if (hItem == song->hTI_repeat) {
TrueFalseDlg dlg;
dlg.m_IsFalse = song->repeat ? 0 : 1;
if (dlg.DoModal() == IDOK) {
if (dlg.m_IsFalse)
song->repeat = false;
else
song->repeat = true;
SetSongProperty(SONGPROP_REPEAT, song);
}
}
break;
case LEAFTYPE_CATEGORY:
tree->Expand(hItem, TVE_TOGGLE);
break;
case LEAFTYPE_INVALID:
Int3();
break;
default:
// this MUST be a song name
{
CInsSongDlg insdlg;
insdlg.m_SongPath = song->strSongPath;
if (insdlg.DoModal() == IDOK) {
song->strSongPath = insdlg.m_SongPath;
SetSongProperty(SONGPROP_FILE, song);
}
}
}
}
// inserts a song into the designated tree.
CMusicutilsDlg::tSongItem *CMusicutilsDlg::InsertSong(HTREEITEM hTree, const tSongItem *songitm) {
tSongItem *song = new tSongItem;
CString tempstr;
CString strSong;
ASSERT(hTree == hIntroLeaf || hTree == hBkgLeaf || hTree == hCombatLeaf || hTree == hDangerLeaf ||
hTree == hEndCombatLeaf);
song->strSongPath = songitm->strSongPath;
// grab information from stream file
GetStreamInfo(song->strSongPath, strSong);
strSong += " (";
strSong += (song->strSongPath + ")");
song->hTreeItem = InsertLeaf(hTree, strSong.GetBuffer(_MAX_PATH + 64), (LPARAM)song);
song->hTI_repeat = NULL;
if (hTree != hIntroLeaf && hTree != hEndCombatLeaf) {
tempstr.Format("%s%s", TXT_REPEAT, songitm->repeat ? "true" : "false");
song->hTI_repeat = InsertLeaf(song->hTreeItem, (LPCTSTR)tempstr, LEAFTYPE_PROPERTY);
song->repeat = songitm->repeat;
}
song->hTI_lvllink = NULL;
song->roomlink = songitm->roomlink;
if (hTree == hBkgLeaf) {
// add sub items to background song leaf
tempstr.Format("%s%d", TXT_LINKLEVEL, song->roomlink);
song->hTI_lvllink = InsertLeaf(song->hTreeItem, (LPCTSTR)tempstr, LEAFTYPE_PROPERTY);
} else if (hTree == hCombatLeaf) {
} else if (hTree == hDangerLeaf) {
}
return song;
}
// Handy utilities to insert, retreive information from a leaf on the tree control.
HTREEITEM CMusicutilsDlg::InsertLeaf(HTREEITEM hParent, const char *text, LPARAM param) {
CTreeCtrl *tree = (CTreeCtrl *)GetDlgItem(IDC_THEME_TREE);
return tree->InsertItem(TVIF_TEXT | TVIF_PARAM, text, 0, 0, 0, 0, param, hParent, TVI_LAST);
}
LPARAM CMusicutilsDlg::GetLeafParam(HTREEITEM hItem) {
CTreeCtrl *tree = (CTreeCtrl *)GetDlgItem(IDC_THEME_TREE);
TV_ITEM tv_item;
tv_item.hItem = hItem;
tv_item.mask = TVIF_PARAM;
return tree->GetItem(&tv_item) ? tv_item.lParam : LEAFTYPE_INVALID;
}
CMusicutilsDlg::tSongItem *CMusicutilsDlg::GetSongFromTreeItem(HTREEITEM hLeaf) {
CTreeCtrl *tree = (CTreeCtrl *)GetDlgItem(IDC_THEME_TREE);
LPARAM param;
// get song object. if a property, the song object exists above this leaf.
param = GetLeafParam(hLeaf);
if (param == LEAFTYPE_PROPERTY) {
param = GetLeafParam(tree->GetParentItem(hLeaf));
ASSERT(param > 0);
} else if (param == LEAFTYPE_CATEGORY) {
param = 0;
}
return (tSongItem *)param;
}
void CMusicutilsDlg::SetSongProperty(tSongProp property, tSongItem *song) {
CTreeCtrl *tree = (CTreeCtrl *)GetDlgItem(IDC_THEME_TREE);
tSongItem *real_song;
CString tempstr;
HTREEITEM hItem = NULL;
switch (property) {
case SONGPROP_REPEAT:
real_song = GetSongFromTreeItem(song->hTI_repeat);
hItem = song->hTI_repeat;
real_song->repeat = song->repeat;
tempstr.Format("%s%s", TXT_REPEAT, real_song->repeat ? "true" : "false");
break;
case SONGPROP_ROOMLINK:
real_song = GetSongFromTreeItem(song->hTI_lvllink);
hItem = song->hTI_lvllink;
real_song->roomlink = song->roomlink;
tempstr.Format("%s%d", TXT_LINKLEVEL, real_song->roomlink);
break;
case SONGPROP_FILE:
real_song = GetSongFromTreeItem(song->hTreeItem);
hItem = song->hTreeItem;
real_song->strSongPath = song->strSongPath;
GetStreamInfo(real_song->strSongPath, tempstr); // grab information from stream file
tempstr += (" (" + real_song->strSongPath + ")");
break;
default:
Int3();
}
if (!hItem)
return;
tree->SetItemText(hItem, (LPCTSTR)tempstr);
m_modified = true;
}