Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
497 lines
13 KiB
C++
497 lines
13 KiB
C++
// NodeDataValueFileDoc.cpp : implementation file
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include "ContentTrack.h"
|
|
#include "NodeDataValueFileDoc.h"
|
|
#include "DialogPasteRename.h"
|
|
|
|
#include <stuff\database.hpp>
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
CString g_szbrowsepath;
|
|
int CALLBACK BrowseCallbackProc(HWND hWnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
|
|
{
|
|
RECT rect;
|
|
//
|
|
//Center the browse dialog on the screen...
|
|
//
|
|
switch (uMsg)
|
|
{
|
|
case BFFM_INITIALIZED:
|
|
{
|
|
SendMessage(hWnd,BFFM_SETSELECTION,TRUE,(LPARAM)(LPCTSTR)g_szbrowsepath);
|
|
|
|
if (GetWindowRect(hWnd, &rect))
|
|
{
|
|
SetWindowPos(hWnd,
|
|
HWND_TOP,
|
|
(GetSystemMetrics(SM_CXSCREEN) - (rect.right - rect.left)) >> 1,
|
|
(GetSystemMetrics(SM_CYSCREEN) - (rect.bottom - rect.top)) >> 1,
|
|
0,
|
|
0,
|
|
SWP_NOSIZE);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
CString BrowseDirectory(const char *strCurrent,CWnd *pWnd)
|
|
{
|
|
CString str;
|
|
TCHAR szBuffer[MAX_PATH];
|
|
LPITEMIDLIST pidlBrowse,pidlRoot;
|
|
BROWSEINFO bi;
|
|
|
|
LPMALLOC lpMalloc;
|
|
SHGetMalloc(&lpMalloc);
|
|
ASSERT(lpMalloc);
|
|
bi.hwndOwner = pWnd->GetSafeHwnd();
|
|
// Get the ID for the "My Computer" root folder
|
|
SHGetSpecialFolderLocation(pWnd->GetSafeHwnd(),CSIDL_DRIVES,&pidlRoot);
|
|
|
|
bi.pidlRoot = pidlRoot;
|
|
bi.pszDisplayName = szBuffer;
|
|
|
|
// Localize the descriptive text
|
|
bi.lpszTitle = "Select a MW4 Game Root folder. This folder should should have a Contents subdirectory.";
|
|
|
|
// BIF_EDITBOX and BIF_VALIDATE are for versions 4.71 and later, they
|
|
// might be ignored by older versions
|
|
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT;// | BIF_EDITBOX;// | BIF_USENEWUI;// | BIF_VALIDATE;
|
|
bi.lParam = (LPARAM)pWnd;
|
|
bi.iImage = NULL;
|
|
bi.lpfn = BrowseCallbackProc;
|
|
bi.lParam = (LPARAM)strCurrent;
|
|
g_szbrowsepath = strCurrent;
|
|
//
|
|
//Call the Windows shell folder browsing routine...
|
|
//
|
|
pidlBrowse = SHBrowseForFolder(&bi);
|
|
if (NULL != pidlBrowse)
|
|
{
|
|
if (SHGetPathFromIDList(pidlBrowse, szBuffer))
|
|
{
|
|
if ('\\' == szBuffer[lstrlen(szBuffer)-1])
|
|
szBuffer[lstrlen(szBuffer)-1] = '\0';
|
|
str = CString(szBuffer);
|
|
}
|
|
else
|
|
str = "";
|
|
//
|
|
//Free up shell memory used...
|
|
//
|
|
lpMalloc->Free(pidlBrowse);
|
|
}
|
|
else
|
|
str = "";
|
|
lpMalloc->Free(pidlRoot);
|
|
lpMalloc->Release();
|
|
return str;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CNodeDataValueFileDoc
|
|
|
|
IMPLEMENT_DYNCREATE(CNodeDataValueFileDoc, CContentTrackDoc)
|
|
|
|
CNodeDataValueFileDoc::CNodeDataValueFileDoc()
|
|
{
|
|
m_pNodeDataValueFileProject = NULL;
|
|
m_pNodeDirectory = NULL;
|
|
m_pActiveChild = NULL;
|
|
m_iReplaceAll = -1;
|
|
}
|
|
|
|
BOOL CNodeDataValueFileDoc::OnNewDocument()
|
|
{
|
|
if (!CContentTrackDoc::OnNewDocument())
|
|
return FALSE;
|
|
|
|
/* CFileDialog dlg(false,"*.project",NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,"*.project",NULL);
|
|
if (IDOK == dlg.DoModal())
|
|
{
|
|
m_pNodeDataValueFileProject = new CNodeDataValueFileProject(NULL,dlg.GetPathName());
|
|
return TRUE;
|
|
}*/
|
|
char buf[MAX_PATH];
|
|
GetCurrentDirectory(MAX_PATH,buf);
|
|
CString path = BrowseDirectory(buf,NULL);
|
|
if (path.GetLength())
|
|
{
|
|
CString contentpath = path+"\\content";
|
|
DWORD attrib = ::GetFileAttributes(contentpath);
|
|
if (-1 == attrib)
|
|
{
|
|
// File does not exist
|
|
CString str = "Game root folder "+path+" does not have a Contents directory. Do you want to continue? A Contents directory will be created.";
|
|
if (IDNO == MessageBox(NULL,str,"Create New Workspace",MB_YESNO))
|
|
return false;
|
|
}
|
|
|
|
// Scan the content directory
|
|
SetCurrentDirectory(path);
|
|
m_strRoot = path;
|
|
// Scan the content directory
|
|
CreateDirectory(contentpath,NULL);
|
|
CFrameWnd *pFrame = (CFrameWnd *)AfxGetMainWnd();
|
|
pFrame->SetMessageText("Scanning Content directory...");
|
|
this->m_pNodeDirectory = new CNodeDiskDirectory(NULL,/*path+"\\*/"content");
|
|
|
|
m_pNodeDataValueFileProject = new CNodeDataValueFileProject(this,path);
|
|
m_pNodeDirectory->SetParentNode(m_pNodeDataValueFileProject);
|
|
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
CNodeDataValueFileDoc::~CNodeDataValueFileDoc()
|
|
{
|
|
if (m_pNodeDataValueFileProject)
|
|
delete m_pNodeDataValueFileProject;
|
|
m_pNodeDataValueFileProject = NULL;
|
|
|
|
if (m_pNodeDirectory)
|
|
delete m_pNodeDirectory;
|
|
m_pNodeDirectory = NULL;
|
|
}
|
|
|
|
|
|
BEGIN_MESSAGE_MAP(CNodeDataValueFileDoc, CContentTrackDoc)
|
|
//{{AFX_MSG_MAP(CNodeDataValueFileDoc)
|
|
ON_COMMAND(ID_ADDROOT, OnAddroot)
|
|
ON_COMMAND(ID_FILE_SAVE, OnFileSave)
|
|
ON_COMMAND(ID_FILE_SAVE_AS, OnFileSaveAs)
|
|
ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
|
|
ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
|
|
ON_COMMAND(ID_EDIT_PASTE_RENAME, OnEditPasteRename)
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CNodeDataValueFileDoc diagnostics
|
|
|
|
#ifdef _DEBUG
|
|
void CNodeDataValueFileDoc::AssertValid() const
|
|
{
|
|
CContentTrackDoc::AssertValid();
|
|
}
|
|
|
|
void CNodeDataValueFileDoc::Dump(CDumpContext& dc) const
|
|
{
|
|
CContentTrackDoc::Dump(dc);
|
|
}
|
|
#endif //_DEBUG
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CNodeDataValueFileDoc serialization
|
|
|
|
void CNodeDataValueFileDoc::Serialize(CArchive& ar)
|
|
{
|
|
if (ar.IsStoring())
|
|
{
|
|
// TODO: add storing code here
|
|
}
|
|
else
|
|
{
|
|
// TODO: add loading code here
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CNodeDataValueFileDoc commands
|
|
|
|
extern int IdentifyFileType(CString path);
|
|
|
|
BOOL CNodeDataValueFileDoc::OnOpenDocument(LPCTSTR lpszPathName)
|
|
{
|
|
// if (!CContentTrackDoc::OnOpenDocument(lpszPathName))
|
|
// return FALSE;
|
|
CFrameWnd *pFrame = (CFrameWnd *)AfxGetMainWnd();
|
|
if (this->m_pNodeDataValueFileProject)
|
|
{
|
|
this->m_pActiveChild = NULL;
|
|
pFrame->SetMessageText("Updating...");
|
|
m_pNodeDataValueFileProject->Update();
|
|
m_pNodeDataValueFileProject->ResetVisitAll();
|
|
this->UpdateAllViews(NULL,NULL,NULL);
|
|
pFrame->SetMessageText("Ready.");
|
|
//TODO: Clear the dirty bits
|
|
return TRUE;
|
|
}
|
|
|
|
NotationFile notefile(lpszPathName);
|
|
Page *page = notefile.GetPage("Project");
|
|
if (NULL == page)
|
|
return FALSE;
|
|
Check_Object(page);
|
|
|
|
const char *path;
|
|
if (!page->GetEntry("Path",&path))
|
|
return FALSE;
|
|
|
|
SetCurrentDirectory(path);
|
|
m_strRoot = path;
|
|
// Scan the content directory
|
|
CreateDirectory(CString(path)+"\\content",NULL);
|
|
pFrame->SetMessageText("Scanning Content directory...");
|
|
this->m_pNodeDirectory = new CNodeDiskDirectory(NULL,/*CString(path)+"\\*/"content");
|
|
|
|
m_pNodeDataValueFileProject = new CNodeDataValueFileProject(this,path);
|
|
m_pNodeDirectory->SetParentNode(m_pNodeDataValueFileProject);
|
|
|
|
page = notefile.GetPage("Roots");
|
|
if (page)
|
|
{
|
|
Check_Object(page);
|
|
|
|
Page::NoteIterator *entries = page->MakeNoteIterator();
|
|
Check_Object(entries);
|
|
|
|
Note *entry;
|
|
// Enumerate the entries in the section
|
|
while ((entry = entries->ReadAndNext()) != NULL)
|
|
{
|
|
Check_Object(entry);
|
|
|
|
if (0 == (strnicmp(entry->GetName(),"Path",4)))
|
|
{
|
|
const char *newpath;
|
|
entry->GetEntry(&newpath);
|
|
CString fullpath = CString(path)+"\\"+newpath;
|
|
CNodeObject *pNode = CNodeData::CreateChild(m_pNodeDataValueFileProject,IdentifyFileType(fullpath),newpath);
|
|
m_pNodeDataValueFileProject->AddChild(pNode);
|
|
}
|
|
}
|
|
Check_Object(entries);
|
|
delete entries;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
CNodeDisk *CNodeDataValueFileDoc::FindDirectoryNode(CString strName)
|
|
{
|
|
return FindDirectoryNode(strName,GetRootNode());
|
|
}
|
|
|
|
CNodeDisk *CNodeDataValueFileDoc::FindDirectoryNode(CString strName,CNodeDisk *pNode)
|
|
{
|
|
#if 0
|
|
if (strName.GetLength() ==0)
|
|
return NULL;
|
|
if (strName[0] == '\\')
|
|
strName.Delete(0,1);
|
|
CString strDir = pNode->GetName();
|
|
int iLen = strDir.GetLength();
|
|
if (strnicmp(strDir,strName,iLen)==0)
|
|
{
|
|
strName.Delete(0,iLen);
|
|
if (strName.IsEmpty())
|
|
// we found it
|
|
return pNode;
|
|
strName.Delete(0,1);
|
|
POSITION pos = pNode->GetFirstChild();
|
|
while (pos)
|
|
{
|
|
if (out_pos)
|
|
*out_pos = pos;
|
|
CNodeDisk *pChild = (CNodeDisk *)pNode->GetNextChild(pos);
|
|
CNodeDisk *pRet = FindDirectoryNode(strName,pChild,out_pos);
|
|
if (pRet)
|
|
return pRet;
|
|
}
|
|
}
|
|
return NULL;
|
|
#endif
|
|
|
|
char *src_name = (char *)(LPCTSTR)strName;
|
|
// int src_size = strName.GetLength();
|
|
char dir_name[MAX_PATH];
|
|
//char *dir_name = (char *)(LPCTSTR)pNode->GetName();
|
|
pNode->GetName(dir_name,MAX_PATH);
|
|
// int dir_size = pNode->GetName().GetLength();
|
|
|
|
// this isn't really necessary since the last character in src_name is '\0'
|
|
// and the loop below will exit there too
|
|
// if (src_size < dir_size)
|
|
// return NULL;
|
|
|
|
//for (int x=0;x<dir_size;x++)
|
|
for (char *dn = dir_name;*dn;++dn,++src_name)
|
|
{
|
|
// 0x5f mask makes the ascii character uppercase
|
|
if ( (*src_name&0x5f) != (*dn&0x5f))
|
|
return NULL;
|
|
//src_name++;
|
|
}
|
|
|
|
if (src_name[0])
|
|
{
|
|
if ('\\' == src_name[0])
|
|
{
|
|
src_name++;
|
|
POSITION pos = pNode->GetFirstChild();
|
|
while (pos)
|
|
{
|
|
CNodeDisk *pChild = (CNodeDisk *)pNode->GetNextChild(pos);
|
|
CNodeDisk *pRet = FindDirectoryNode(src_name,pChild);
|
|
if (pRet)
|
|
return pRet;
|
|
}
|
|
}
|
|
// We found some other characters in the name, so it doesn't match.
|
|
return NULL;
|
|
}
|
|
// Means the names match and we found the right node
|
|
return pNode;
|
|
}
|
|
|
|
void CNodeDataValueFileDoc::OnAddroot()
|
|
{
|
|
CFileDialog dlg(true);
|
|
CString str;
|
|
this->m_pNodeDataValueFileProject->GetName(str);
|
|
dlg.m_ofn.lpstrInitialDir = str;
|
|
if (IDOK == dlg.DoModal())
|
|
{
|
|
//m_pNodeDataValueFileProject = new CNodeDataValueFileProject(NULL,dlg.GetPathName());
|
|
CString path = dlg.GetPathName();
|
|
CString dir;
|
|
m_pNodeDataValueFileProject->GetName(dir);
|
|
if (strnicmp(dir,path,dir.GetLength()))
|
|
{
|
|
// Error - the new root file must be in a subdir of the project path
|
|
MessageBox(NULL,"Error: The new root file must be in a subdir of the project path!","Error",MB_OK);
|
|
return;
|
|
}
|
|
CString newpath = path.Right(path.GetLength()-dir.GetLength()-1);
|
|
|
|
SetCurrentDirectory(dir);
|
|
CNodeObject *pNode = CNodeData::CreateChild(m_pNodeDataValueFileProject,IdentifyFileType(path),newpath);
|
|
m_pNodeDataValueFileProject->AddChild(pNode);
|
|
|
|
// m_pNodeDataValueFileProject->Update();
|
|
this->UpdateAllViews(NULL,NULL,NULL);
|
|
}
|
|
}
|
|
|
|
void CNodeDataValueFileDoc::OnFileSave()
|
|
{
|
|
CString fname = GetPathName();
|
|
if (0 == fname.GetLength())
|
|
{
|
|
OnFileSaveAs();
|
|
return;
|
|
}
|
|
NotationFile notefile;
|
|
Page *page = notefile.AddPage("Project");
|
|
CString str;
|
|
m_pNodeDataValueFileProject->GetName(str);
|
|
page->AppendEntry("Path",str);
|
|
|
|
page = notefile.AddPage("Roots");
|
|
POSITION pos = m_pNodeDataValueFileProject->GetFirstChild();
|
|
while (pos)
|
|
{
|
|
CNodeObject *pnode = m_pNodeDataValueFileProject->GetNextChild(pos);
|
|
CString str;
|
|
pnode->GetName(str);
|
|
page->AppendEntry("Path",str);
|
|
}
|
|
notefile.SaveAs(GetPathName());
|
|
}
|
|
|
|
void CNodeDataValueFileDoc::OnFileSaveAs()
|
|
{
|
|
CFileDialog dlg(false);
|
|
if (IDOK == dlg.DoModal())
|
|
{
|
|
this->SetPathName(dlg.GetPathName());
|
|
OnFileSave();
|
|
}
|
|
}
|
|
|
|
void CNodeDataValueFileDoc::OnEditCopy()
|
|
{
|
|
((CContentTrackApp *)AfxGetApp())->m_pNodeSelected = m_pActiveChild;
|
|
}
|
|
|
|
void CNodeDataValueFileDoc::OnEditPaste()
|
|
{
|
|
CNodeObject *pSelected = ((CContentTrackApp *)AfxGetApp())->m_pNodeSelected;
|
|
CNodeObject *pClone = _OnEditPaste(pSelected);
|
|
if (pClone)
|
|
this->UpdateAllViews(NULL,NULL,NULL);
|
|
}
|
|
|
|
CNodeObject *CNodeDataValueFileDoc::_OnEditPaste(CNodeObject *pSelected,CString oldName,CString newName)
|
|
{
|
|
this->m_iReplaceAll = -1;
|
|
// Paste the copied node as a child of the selected node
|
|
CNodeObject *pChild = m_pActiveChild?m_pActiveChild:this->GetNodeProject();
|
|
CNodeObject *pClone = NULL;
|
|
if (pSelected&&pChild)
|
|
{
|
|
pClone = pSelected->Clone(pChild,oldName,newName);
|
|
pChild->AddChild(pClone);
|
|
this->GetRootNode()->ResetVisitAll();
|
|
}
|
|
return pClone;
|
|
}
|
|
|
|
int VisitTreeFiles(CNodeObject *pNode)
|
|
{
|
|
int count = 0;
|
|
if (NULL == pNode)
|
|
return 0;
|
|
|
|
bool bvisited = false;
|
|
if (pNode->IsFile())
|
|
{
|
|
CNodeDisk *pDisk= ((CNodeDataValueFile*)pNode)->GetNodeDisk();
|
|
bvisited = pDisk->GetVisit(VISIT_FLAG1);
|
|
if (!bvisited)
|
|
{
|
|
pDisk->SetVisit(VISIT_FLAG1);
|
|
count++;
|
|
}
|
|
}
|
|
if (!bvisited)
|
|
{
|
|
POSITION pos = pNode->GetFirstChild();
|
|
while (pos)
|
|
{
|
|
CNodeObject *pChild = pNode->GetNextChild(pos);
|
|
count += VisitTreeFiles(pChild);
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void CNodeDataValueFileDoc::OnEditPasteRename()
|
|
{
|
|
CNodeObject *pSelected = ((CContentTrackApp *)AfxGetApp())->m_pNodeSelected;
|
|
|
|
CDialogPasteRename dlg;
|
|
if (pSelected&&(IDOK == dlg.DoModal()))
|
|
{
|
|
CString newName = dlg.m_strNew;
|
|
CString oldName = dlg.m_strOld;
|
|
|
|
CNodeObject *pNode = pSelected;
|
|
while (pNode && (!pNode->IsFile()))
|
|
pNode = pNode->GetParentNode();
|
|
VisitTreeFiles(pNode);
|
|
|
|
CNodeObject *pClone = _OnEditPaste(pSelected,oldName,newName);
|
|
this->UpdateAllViews(NULL,NULL,NULL);
|
|
}
|
|
}
|