!XT (Code) Update copyright headers in Code/Sandbox.
[CRYENGINE.git] / Code / Sandbox / Plugins / PerforcePlugin / PerforceSourceControl.cpp
blob41e321d798c13bf089ca42af8ad815cf45836b0e
1 // Copyright 2001-2018 Crytek GmbH / Crytek Group. All rights reserved.
3 #include "stdafx.h"
4 #include <CrySystem/File/CryFile.h>
5 #include "PerforceSourceControl.h"
6 #include "PasswordDlg.h"
7 #include <CryCore/Platform/platform_impl.inl>
8 #include <CryThreading/IThreadManager.h>
10 // EditorCommon
11 #include <EditorFramework/TrayArea.h>
12 #include <CryIcon.h>
13 #include <Controls/QPopupWidget.h>
15 // Qt
16 #include <QHBoxLayout>
17 #include <QVBoxLayout>
18 #include <QCheckBox>
19 #include <QLabel>
20 #include <QLineEdit>
21 #include <QtUtil.h>
23 extern CPerforceSourceControl* g_pPerforceControl;
25 REGISTER_TRAY_AREA_WIDGET(CPerforceTrayWidget, 100)
27 class PerforceSettings : public QWidget
29 public:
30 PerforceSettings(QWidget* pParent = nullptr)
32 QHBoxLayout* pMainLayout = new QHBoxLayout();
34 m_pLabelLayout = new QVBoxLayout();
35 m_pInputLayout = new QVBoxLayout();
36 pMainLayout->addLayout(m_pLabelLayout);
37 pMainLayout->addLayout(m_pInputLayout);
39 m_pLabelLayout->addWidget(new QLabel("Online"));
40 m_pConnectionCheckBox = new QCheckBox();
41 m_pConnectionCheckBox->setChecked(!g_pPerforceControl->IsWorkOffline());
42 connect(m_pConnectionCheckBox, &QCheckBox::toggled, [this](bool bChecked)
44 g_pPerforceControl->SetWorkOffline(!bChecked);
45 m_pServerInput->setEnabled(bChecked);
46 m_pUserInput->setEnabled(bChecked);
47 m_pWorkspaceInput->setEnabled(bChecked);
48 m_pPasswordInput->setEnabled(bChecked);
50 });
51 m_pInputLayout->addWidget(m_pConnectionCheckBox);
53 CMyClientApi& clientApi = g_pPerforceControl->GetClientApi();
54 m_pServerInput = CreateSection("Server", clientApi.GetPort().Value());
55 m_pUserInput = CreateSection("User", clientApi.GetUser().Value());
56 m_pWorkspaceInput = CreateSection("Workspace", clientApi.GetClient().Value());
57 m_pPasswordInput = CreateSection("Password", clientApi.GetPassword().Value(), true);
59 g_pPerforceControl->signalWorkOfflineChanged.Connect(this, &PerforceSettings::OnConnectionStatusChanged);
61 setLayout(pMainLayout);
64 private:
65 QLineEdit* CreateSection(const char* szLabel, const char* szInput, bool bIsPassword = false)
67 m_pLabelLayout->addWidget(new QLabel(szLabel));
68 QLineEdit* pLineEdit = new QLineEdit();
69 if (bIsPassword)
70 pLineEdit->setEchoMode(QLineEdit::Password);
71 pLineEdit->setText(szInput);
72 connect(pLineEdit, &QLineEdit::textChanged, this, &PerforceSettings::OnSettingsChanged);
73 m_pInputLayout->addWidget(pLineEdit);
75 return pLineEdit;
78 void OnConnectionStatusChanged()
80 m_pConnectionCheckBox->setChecked(!g_pPerforceControl->IsWorkOffline());
83 void OnSettingsChanged() const
85 CMyClientApi& clientApi = g_pPerforceControl->GetClientApi();
86 Error e;
87 clientApi.DefinePassword(m_pPasswordInput->text().toLocal8Bit(), &e);
88 clientApi.DefineClient(m_pWorkspaceInput->text().toLocal8Bit(), &e);
89 clientApi.DefinePort(m_pServerInput->text().toLocal8Bit(), &e);
90 clientApi.DefineUser(m_pUserInput->text().toLocal8Bit(), &e);
93 private:
94 QVBoxLayout* m_pLabelLayout;
95 QVBoxLayout* m_pInputLayout;
97 QCheckBox* m_pConnectionCheckBox;
98 QLineEdit* m_pServerInput;
99 QLineEdit* m_pUserInput;
100 QLineEdit* m_pWorkspaceInput;
101 QLineEdit* m_pPasswordInput;
104 CPerforceTrayWidget::CPerforceTrayWidget(QWidget* pParent /*= nullptr*/)
106 CMyClientApi& clientApi = g_pPerforceControl->GetClientApi();
107 m_pPopUpMenu = new QPopupWidget("PerforceSettingsPopUp", QtUtil::MakeScrollable(new PerforceSettings()), QSize(340, 150), true);
108 m_pPopUpMenu->SetFocusShareWidget(this);
109 connect(this, &QToolButton::clicked, this, &CPerforceTrayWidget::OnClicked);
110 g_pPerforceControl->signalWorkOfflineChanged.Connect(this, &CPerforceTrayWidget::OnConnectionStatusChanged);
111 OnConnectionStatusChanged();
114 void CPerforceTrayWidget::OnConnectionStatusChanged()
116 CryIconColorMap colorMap;
117 colorMap[QIcon::Selected] = QColor(125, 198, 83);
118 if (!g_pPerforceControl->IsWorkOffline())
119 setIcon(CryIcon("icons:p4.ico", colorMap).pixmap(24, 24, QIcon::Normal, QIcon::On));
120 else
121 setIcon(CryIcon("icons:p4.ico"));
124 void CPerforceTrayWidget::OnClicked(bool bChecked)
126 if (m_pPopUpMenu->isVisible())
128 m_pPopUpMenu->hide();
129 return;
132 m_pPopUpMenu->ShowAt(mapToGlobal(QPoint(width(), height())));
135 namespace
137 CryCriticalSection g_cPerforceValues;
140 struct CPerforceThread : public IThread
142 const char* m_filename;
143 CPerforceSourceControl* m_pSourceControl;
145 CPerforceThread(CPerforceSourceControl* pSourceControl, const char* filename)
147 m_pSourceControl = pSourceControl;
148 m_filename = filename;
151 virtual ~CPerforceThread()
153 gEnv->pThreadManager->JoinThread(this, eJM_Join);
156 protected:
158 // Start accepting work on thread
159 virtual void ThreadEntry()
161 m_pSourceControl->GetFileAttributesThread(m_filename);
165 void CMyClientUser::HandleError(Error* e)
168 StrBuf m;
169 e->Fmt( &m );
170 if ( strstr( m.Text(), "file(s) not in client view." ) )
171 e->Clear();
172 else if ( strstr( m.Text(), "no such file(s)" ) )
173 e->Clear();
174 else if ( strstr( m.Text(), "access denied" ) )
175 e->Clear();
176 /**/
177 m_e = *e;
180 void CMyClientUser::Init()
182 m_bIsClientUnknown = false;
183 m_bIsSetup = false;
184 m_bIsPreCreateFile = false;
185 m_output.Empty();
186 m_input.Empty();
187 m_e.Clear();
190 void CMyClientUser::PreCreateFileName(const char* file)
192 m_bIsPreCreateFile = true;
193 cry_strcpy(m_file, file);
194 m_findedFile[0] = 0;
197 void CMyClientUser::OutputStat(StrDict* varList)
199 if (m_bIsSetup && !m_bIsPreCreateFile)
200 return;
202 StrRef var, val;
203 *m_action = 0;
204 *m_headAction = 0;
205 *m_otherAction = 0;
206 *m_depotFile = 0;
207 *m_otherUser = 0;
208 m_lockStatus = SCC_LOCK_STATUS_UNLOCKED;
210 for (int i = 0; varList->GetVar(i, var, val); i++)
212 if (m_bIsPreCreateFile)
214 if (var == "clientFile")
216 char tmpval[MAX_PATH];
217 cry_strcpy(tmpval, val.Text());
218 char* ch = tmpval;
219 while (ch = strchr(ch, '/'))
220 *ch = '\\';
222 if (!stricmp(tmpval, m_file))
224 cry_strcpy(m_findedFile, val.Text());
225 m_bIsPreCreateFile = false;
229 else
231 if (var == "action")
232 cry_strcpy(m_action, val.Text());
233 else if (var == "headAction")
234 cry_strcpy(m_headAction, val.Text());
235 else if (var == "headRev")
237 cry_strcpy(m_clientHasLatestRev, val.Text());
238 m_nFileHeadRev = val.Atoi64();
240 else if (!strncmp(var.Text(), "otherAction", 11) && !strcmp(val.Text(), "edit"))
241 cry_strcpy(m_otherAction, val.Text());
242 else if (var == "haveRev")
244 if (val != m_clientHasLatestRev)
245 m_clientHasLatestRev[0] = 0;
246 m_nFileHaveRev = val.Atoi64();
248 else if (var == "depotFile")
249 cry_strcpy(m_depotFile, val.Text());
250 else if (var == "otherOpen0")
251 cry_strcpy(m_otherUser, val.Text());
252 else if (!strncmp(var.Text(), "otherLock0", 10))
254 m_lockStatus = SCC_LOCK_STATUS_LOCKED_BY_OTHERS;
255 cry_strcpy(m_lockedBy, val.Text());
257 else if (var == "ourLock")
258 m_lockStatus = SCC_LOCK_STATUS_LOCKED_BY_US;
261 m_bIsSetup = true;
264 void CMyClientUser::OutputInfo(char level, const char* data)
266 m_output.Append(data);
268 string strData(data);
269 if ("Client unknown." == strData)
271 m_bIsClientUnknown = true;
272 return;
275 int nStart = 0;
276 string var = strData.Tokenize(":", nStart);
277 string val;
278 if (!var.IsEmpty() && nStart > 1)
279 val = strData.Mid(nStart).Trim(" ");
281 if ("Client root" == var)
282 m_clientRoot = val;
283 else if ("Client host" == var)
284 m_clientHost = val;
285 else if ("Client name" == var)
286 m_clientName = val;
287 else if ("Current directory" == var)
288 m_currentDirectory = val;
290 if (!m_bIsPreCreateFile)
291 return;
293 const char* ch = strrchr(data, '/');
294 if (ch)
296 if (!stricmp(ch + 1, m_file))
298 cry_strcpy(m_findedFile, ch + 1);
299 m_bIsPreCreateFile = false;
304 void CMyClientUser::InputData(StrBuf* buf, Error* e)
306 if (m_bIsCreatingChangelist)
307 buf->Set(m_input);
310 void CMyClientUser::Edit(FileSys* f1, Error* e)
312 char msrc[4000];
313 char mdst[4000];
315 char* src = &msrc[0];
316 char* dst = &mdst[0];
318 f1->Open(FOM_READ, e);
319 int size = f1->Read(msrc, 10240, e);
320 msrc[size] = 0;
321 f1->Close(e);
323 while (*dst = *src)
325 if (!strnicmp(src, "\nDescription", 11))
327 src++;
328 while (*src != '\n' && *src != '\0')
329 src++;
330 src++;
331 while (*src != '\n' && *src != '\0')
332 src++;
333 strcpy(dst, "\nDescription:\n\t");
334 dst += 15;
335 strcpy(dst, m_desc);
336 dst += strlen(m_desc);
337 strcpy(dst, " (by Perforce Plug-in)");
338 dst += 22;
340 else
342 src++;
343 dst++;
347 f1->Open(FOM_WRITE, e);
348 f1->Write(mdst, strlen(mdst), e);
349 f1->Close(e);
351 cry_strcpy(m_desc, m_initDesc);
354 ////////////////////////////////////////////////////////////
356 void CMyClientApi::Run(const char* func)
358 // The "enableStreams" var has to be set prior to any Run call in order to be able to support Perforce streams
359 ClientApi::SetVar("enableStreams");
360 ClientApi::Run(func);
363 void CMyClientApi::Run(const char* func, ClientUser* ui)
365 ClientApi::SetVar("enableStreams");
366 ClientApi::Run(func, ui);
369 ////////////////////////////////////////////////////////////
371 CPerforceSourceControl::CPerforceSourceControl() : m_ref(0)
373 m_thread = 0;
374 m_bIsWorkOffline = false;
375 m_bIsFailConnectionLogged = false;
376 m_dwLastAccessTime = 0;
377 m_isSecondThread = false;
378 Connect();
379 InitCommonControls();
382 CPerforceSourceControl::~CPerforceSourceControl()
384 if (m_thread)
386 gEnv->pThreadManager->JoinThread(m_thread, eJM_Join);
387 delete m_thread;
391 bool CPerforceSourceControl::Connect()
393 m_client.Init(&m_e);
394 if (m_e.Test())
396 SetWorkOffline(true);
397 if (!m_bIsFailConnectionLogged)
399 CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_ERROR, "\nPerforce plugin: Failed to connect.");
400 m_bIsFailConnectionLogged = true;
402 return false;
404 else
406 CryLog("\nPerforce plugin: Connected.");
407 m_bIsFailConnectionLogged = false;
409 return true;
412 bool CPerforceSourceControl::Reconnect()
414 if (m_client.Dropped())
416 if (!m_bIsFailConnectionLogged)
418 CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_ERROR, "\nPerforce connection dropped: attempting reconnect");
420 FreeData();
421 if (!Connect())
423 SetWorkOffline(true);
424 return false;
427 return true;
430 void CPerforceSourceControl::FreeData()
432 m_client.Final(&m_e);
433 m_e.Clear();
436 void CPerforceSourceControl::Init()
438 CheckConnection();
442 Note:
444 ConvertFileNameCS doesn't seem to do any conversion.
446 First it checks if the path is available on local HDD, then if it hasn't found
447 the path locally it looks in perforce (passing in local paths to P4v's API).
449 The path stored in sDst is the location which either exists and was found, or
450 will exist once you get latest...
452 If the file neither exists locally or in the depot, sDst will be as far as exists in Perforce...
453 Why is this a problem? Well, if you called GetFileAttributes on a file that doesn't exist in P4V
454 you'll be given the attributes for the deepest folder of the path you provided which exists on
455 your local machine - which is probably why you're here reading this comment :)
458 void CPerforceSourceControl::ConvertFileNameCS(char* sDst, const char* sSrcFilename)
460 *sDst = 0;
461 bool bFinded = true;
463 char szAdjustedFile[ICryPak::g_nMaxPath];
464 cry_strcpy(szAdjustedFile, sSrcFilename);
466 //_finddata_t fd;
467 WIN32_FIND_DATA fd;
469 char csPath[ICryPak::g_nMaxPath] = { 0 };
471 char* ch, * ch1;
473 ch = strrchr(szAdjustedFile, '\\');
474 ch1 = strrchr(szAdjustedFile, '/');
475 if (ch < ch1) ch = ch1;
476 bool bIsFirstTime = true;
478 bool bIsEndSlash = false;
479 if (ch && ch - szAdjustedFile + 1 == strlen(szAdjustedFile))
480 bIsEndSlash = true;
482 while (ch)
484 //intptr_t handle;
485 //handle = gEnv->pCryPak->FindFirst( szAdjustedFile, &fd );
486 HANDLE handle = FindFirstFile(szAdjustedFile, &fd);
487 //if (handle != -1)
488 if (handle != INVALID_HANDLE_VALUE)
490 char tmp[ICryPak::g_nMaxPath];
491 cry_strcpy(tmp, csPath);
492 //cry_strcpy(csPath, fd.name);
493 cry_strcpy(csPath, fd.cFileName);
494 if (!bIsFirstTime)
495 cry_strcat(csPath, "\\");
496 bIsFirstTime = false;
497 cry_strcat(csPath, tmp);
499 //gEnv->pCryPak->FindClose( handle );
500 FindClose(handle);
503 *ch = 0;
504 ch = strrchr(szAdjustedFile, '\\');
505 ch1 = strrchr(szAdjustedFile, '/');
506 if (ch < ch1) ch = ch1;
509 if (!*csPath)
510 return;
512 cry_strcat(szAdjustedFile, "\\");
513 cry_strcat(szAdjustedFile, csPath);
514 if (bIsEndSlash || strlen(szAdjustedFile) < strlen(sSrcFilename))
515 cry_strcat(szAdjustedFile, "\\");
517 // if we have only folder on disk find in perforce
518 if (strlen(szAdjustedFile) < strlen(sSrcFilename))
520 if (IsFileManageable(szAdjustedFile))
522 char file[MAX_PATH];
523 char clienFile[MAX_PATH] = { 0 };
525 bool bCont = true;
526 while (bCont)
528 cry_strcpy(file, &sSrcFilename[strlen(szAdjustedFile)]);
529 char* ch = strchr(file, '/');
530 char* ch1 = strchr(file, '\\');
531 if (ch < ch1) ch = ch1;
533 if (ch)
535 *ch = 0;
536 bFinded = bCont = FindDir(clienFile, szAdjustedFile, file);
538 else
540 bFinded = FindFile(clienFile, szAdjustedFile, file);
541 bCont = false;
543 cry_strcpy(szAdjustedFile, clienFile);
544 if (bCont && strlen(clienFile) >= strlen(sSrcFilename))
545 bCont = false;
550 if (bFinded)
551 strcpy(sDst, szAdjustedFile);
554 void CPerforceSourceControl::MakePathCS(char* sDst, const char* sSrcFilename)
556 char szAdjustedFile[ICryPak::g_nMaxPath];
557 char szCheckedPath[ICryPak::g_nMaxPath];
558 cry_strcpy(szAdjustedFile, sSrcFilename);
560 char* ch = &szAdjustedFile[0];
562 while (ch)
564 char* ch1 = strchr(ch, '/');
565 ch = strchr(ch, '\\');
567 if (ch1 && ch > ch1) ch = ch1;
569 if (ch)
571 cry_strcpy(szCheckedPath, szAdjustedFile, ch - szAdjustedFile + 1);
573 if (strlen(szCheckedPath) == 3 && szCheckedPath[1] == ':')
575 ch++;
576 continue;
579 if (IsFileManageable(szCheckedPath, false))
581 cry_strcpy(szAdjustedFile, szCheckedPath);
582 break;
584 ch++;
588 if (strlen(szAdjustedFile) < strlen(sSrcFilename))
590 if (IsFileManageable(szAdjustedFile))
592 char file[MAX_PATH];
593 char clienFile[MAX_PATH] = { 0 };
595 bool bCont = true;
596 while (bCont)
598 cry_strcpy(file, &sSrcFilename[strlen(szAdjustedFile)]);
599 char* ch = strchr(file, '/');
600 char* ch1 = strchr(file, '\\');
601 if (ch < ch1) ch = ch1;
603 bool bFinded = false;
605 if (ch)
607 *ch = 0;
608 bFinded = bCont = FindDir(clienFile, szAdjustedFile, file);
611 if (!bFinded)
613 strcpy(&szAdjustedFile[strlen(szAdjustedFile)], &sSrcFilename[strlen(szAdjustedFile)]);
614 break;
617 cry_strcpy(szAdjustedFile, clienFile);
618 if (bCont && strlen(clienFile) >= strlen(sSrcFilename))
619 bCont = false;
624 if (strlen(szAdjustedFile) == strlen(sSrcFilename))
625 strcpy(sDst, szAdjustedFile);
626 else
627 strcpy(sDst, sSrcFilename);
630 void CPerforceSourceControl::RenameFolders(const char* path, const char* pathOld)
632 const char* ch = strchr(pathOld, '\\');
633 if (ch)
634 ch = strchr(ch + 1, '\\');
635 while (ch)
637 ch++;
638 const char* const ch1 = strchr(ch, '\\');
639 if (ch1 && strncmp(ch, &path[ch - pathOld], ch1 - ch))
641 char newpath[ICryPak::g_nMaxPath];
642 char newpathOld[ICryPak::g_nMaxPath];
643 cry_strcpy(newpath, path, (size_t)(ch1 - pathOld));
644 cry_strcpy(newpathOld, pathOld, (size_t)(ch1 - pathOld));
645 MoveFile(newpathOld, newpath);
647 ch = ch1;
651 bool CPerforceSourceControl::FindDir(char* clientFile, const char* folder, const char* dir)
653 if (!Reconnect())
654 return false;
656 char fl[MAX_PATH];
657 cry_strcpy(fl, folder);
658 cry_strcat(fl, "*");
659 char* argv[] = { fl };
660 m_ui.PreCreateFileName(dir);
662 m_client.SetArgv(1, argv);
663 m_client.Run("dirs", &m_ui);
664 m_client.WaitTag();
666 if (m_ui.m_e.Test())
667 return false;
669 strcpy(clientFile, folder);
670 strcat(clientFile, m_ui.m_findedFile);
671 strcat(clientFile, "\\");
672 if (*m_ui.m_findedFile)
673 return true;
675 return false;
678 bool CPerforceSourceControl::FindFile(char* clientFile, const char* folder, const char* file)
680 if (!Reconnect())
681 return false;
683 char fullPath[MAX_PATH];
685 cry_strcpy(fullPath, folder);
686 cry_strcat(fullPath, file);
688 char fl[MAX_PATH];
689 cry_strcpy(fl, folder);
690 cry_strcat(fl, "*");
691 char* argv[] = { fl };
692 m_ui.PreCreateFileName(fullPath);
694 m_client.SetArgv(1, argv);
695 m_client.Run("fstat", &m_ui);
696 m_client.WaitTag();
698 if (m_ui.m_e.Test())
699 return false;
701 strcpy(clientFile, m_ui.m_findedFile);
702 if (*clientFile)
703 return true;
705 return false;
708 bool CPerforceSourceControl::IsFileManageable(const char* sFilename, bool bCheckFatal)
710 if (!Reconnect())
711 return false;
713 bool bRet = false;
714 bool fatal = false;
716 if (m_bIsWorkOffline)
717 return false;
719 char fl[MAX_PATH];
720 cry_strcpy(fl, sFilename);
721 //ConvertFileNameCS(fl, sFilename);
723 char* argv[] = { fl };
724 Run("fstat", 1, argv, true);
726 m_ui.Init();
727 m_client.SetArgv(1, argv);
728 m_client.Run("fstat", &m_ui);
729 m_client.WaitTag();
731 if (bCheckFatal && m_ui.m_e.IsFatal())
733 fatal = true;
736 if (!fatal && !m_ui.m_e.IsError())
738 bRet = true;
741 m_ui.m_e.Clear();
742 return bRet;
745 bool CPerforceSourceControl::IsFileExistsInDatabase(const char* sFilename)
747 if (!Reconnect())
748 return false;
750 bool bRet = false;
752 char fl[MAX_PATH];
753 cry_strcpy(fl, sFilename);
754 char* argv[] = { fl };
755 m_ui.Init();
756 m_client.SetArgv(1, argv);
757 m_client.Run("fstat", &m_ui);
758 m_client.WaitTag();
760 if (!m_ui.m_e.Test())
762 if (strcmp(m_ui.m_headAction, "delete"))
763 bRet = true;
765 else
767 // StrBuf errorMsg;
768 // m_ui.m_e.Fmt(&errorMsg);
770 m_ui.m_e.Clear();
771 return bRet;
774 bool CPerforceSourceControl::IsFileCheckedOutByUser(const char* sFilename, bool* pIsByAnotherUser)
776 if (!Reconnect())
777 return false;
779 bool bRet = false;
781 char fl[MAX_PATH];
782 cry_strcpy(fl, sFilename);
783 char* argv[] = { fl };
784 m_ui.Init();
785 m_client.SetArgv(1, argv);
786 m_client.Run("fstat", &m_ui);
787 m_client.WaitTag();
789 if ((!strcmp(m_ui.m_action, "edit") || !strcmp(m_ui.m_action, "add")) && !m_ui.m_e.Test())
790 bRet = true;
792 if (pIsByAnotherUser)
794 *pIsByAnotherUser = false;
795 if (!strcmp(m_ui.m_otherAction, "edit") && !m_ui.m_e.Test())
796 *pIsByAnotherUser = true;
799 m_ui.m_e.Clear();
800 return bRet;
803 bool CPerforceSourceControl::IsFileLatestVersion(const char* sFilename)
805 if (!Reconnect())
806 return false;
808 bool bRet = false;
810 char fl[MAX_PATH];
811 cry_strcpy(fl, sFilename);
812 char* argv[] = { fl };
813 m_ui.Init();
814 m_client.SetArgv(1, argv);
815 m_client.Run("fstat", &m_ui);
816 m_client.WaitTag();
818 if (m_ui.m_clientHasLatestRev[0] != 0)
819 bRet = true;
820 m_ui.m_e.Clear();
821 return bRet;
824 uint32 CPerforceSourceControl::GetFileAttributesAndFileName(const char* filename, char* FullFileName)
826 // g_pSystem->GetILog()->Log("\n checking connection");
827 if (!Reconnect())
828 return false;
830 if (FullFileName)
831 FullFileName[0] = 0;
833 CCryFile file;
834 bool bCryFile = file.Open(filename, "rb");
836 uint32 attributes = 0;
838 char sFullFilenameLC[MAX_PATH];
839 GetCurrentDirectory(MAX_PATH, sFullFilenameLC);
840 cry_strcat(sFullFilenameLC, "\\");
841 if (bCryFile)
843 if (*sFullFilenameLC && !strnicmp(sFullFilenameLC, filename, strlen(sFullFilenameLC)))
844 cry_strcpy(sFullFilenameLC, file.GetAdjustedFilename());
845 else
846 cry_strcat(sFullFilenameLC, file.GetAdjustedFilename());
848 else
849 cry_strcat(sFullFilenameLC, filename);
851 char sFullFilename[ICryPak::g_nMaxPath];
852 ConvertFileNameCS(sFullFilename, sFullFilenameLC);
854 if (FullFileName)
855 strcpy(FullFileName, sFullFilename);
857 if (bCryFile && file.IsInPak())
859 // g_pSystem->GetILog()->Log("\n file is in pak");
860 attributes = SCC_FILE_ATTRIBUTE_READONLY | SCC_FILE_ATTRIBUTE_INPAK;
862 if (IsFileManageable(sFullFilename) && IsFileExistsInDatabase(sFullFilename))
864 // g_pSystem->GetILog()->Log("\n file is managable and also exists in the database");
865 attributes |= SCC_FILE_ATTRIBUTE_MANAGED;
866 bool isByAnotherUser;
867 if (IsFileCheckedOutByUser(sFullFilename, &isByAnotherUser))
868 attributes |= SCC_FILE_ATTRIBUTE_CHECKEDOUT;
869 if (isByAnotherUser)
870 attributes |= SCC_FILE_ATTRIBUTE_BYANOTHER;
873 else
875 DWORD dwAttrib = ::GetFileAttributes(sFullFilename);
876 if (dwAttrib != INVALID_FILE_ATTRIBUTES)
878 // g_pSystem->GetILog()->Log("\n we have valid file attributes");
879 attributes = SCC_FILE_ATTRIBUTE_NORMAL;
880 if (dwAttrib & FILE_ATTRIBUTE_READONLY)
881 attributes |= SCC_FILE_ATTRIBUTE_READONLY;
883 if (IsFileManageable(sFullFilename))
885 // g_pSystem->GetILog()->Log("\n file is manageable");
886 if (IsFileExistsInDatabase(sFullFilename))
888 attributes |= SCC_FILE_ATTRIBUTE_MANAGED;
889 // g_pSystem->GetILog()->Log("\n file exists in database");
890 bool isByAnotherUser;
891 if (IsFileCheckedOutByUser(sFullFilename, &isByAnotherUser))
893 // g_pSystem->GetILog()->Log("\n file is checked out");
894 attributes |= SCC_FILE_ATTRIBUTE_CHECKEDOUT;
896 if (isByAnotherUser)
898 // g_pSystem->GetILog()->Log("\n by another user");
899 attributes |= SCC_FILE_ATTRIBUTE_BYANOTHER;
902 else
904 if (*sFullFilename && sFullFilename[strlen(sFullFilename) - 1] == '\\')
906 attributes |= SCC_FILE_ATTRIBUTE_MANAGED | SCC_FILE_ATTRIBUTE_FOLDER;
913 // g_pSystem->GetILog()->Log("\n file has invalid file attributes");
914 if (!attributes && !bCryFile)
916 if (IsFileManageable(sFullFilename))
918 if (IsFileExistsInDatabase(sFullFilename))
920 // g_pSystem->GetILog()->Log("\n file is managable and exists in the database");
921 attributes = SCC_FILE_ATTRIBUTE_MANAGED | SCC_FILE_ATTRIBUTE_NORMAL | SCC_FILE_ATTRIBUTE_READONLY;
922 bool isByAnotherUser;
923 if (IsFileCheckedOutByUser(sFullFilename, &isByAnotherUser))
924 attributes |= SCC_FILE_ATTRIBUTE_CHECKEDOUT;
925 if (isByAnotherUser)
926 attributes |= SCC_FILE_ATTRIBUTE_BYANOTHER;
928 else
930 if (*sFullFilename && sFullFilename[strlen(sFullFilename) - 1] == '\\')
932 attributes |= SCC_FILE_ATTRIBUTE_MANAGED | SCC_FILE_ATTRIBUTE_FOLDER;
938 if (!attributes && IsFolder(filename, FullFileName))
939 attributes = SCC_FILE_ATTRIBUTE_FOLDER;
941 if ((attributes & SCC_FILE_ATTRIBUTE_FOLDER) && FullFileName)
942 strcat(FullFileName, "...");
944 return attributes ? attributes : SCC_FILE_ATTRIBUTE_INVALID;
947 void CPerforceSourceControl::GetFileAttributesThread(const char* filename)
949 uint32 unRetValue = GetFileAttributesAndFileName(filename, 0);
951 AUTO_LOCK(g_cPerforceValues);
952 m_unRetValue = unRetValue;
953 m_isSetuped = true;
956 uint32 CPerforceSourceControl::GetFileAttributes(const char* filename)
958 //return GetFileAttributesAndFileName(filename, 0);
960 DWORD dwTime = GetTickCount();
962 if (m_bIsWorkOffline || dwTime - m_dwLastAccessTime < 1000)
964 m_dwLastAccessTime = dwTime;
965 return GetFileAttributesAndFileName(filename, 0);
968 m_isSkipThread = false;
969 m_isSetuped = false;
970 uint32 unRetValue = SCC_FILE_ATTRIBUTE_INVALID;
972 if (m_thread)
974 gEnv->pThreadManager->JoinThread(m_thread, eJM_Join);
975 delete m_thread;
978 m_isSecondThread = true;
980 m_thread = new CPerforceThread(this, filename);
981 if (!gEnv->pThreadManager->SpawnThread(m_thread, "PerforcePlugin"))
983 CryFatalError("Error spawning \"PerforcePlugin\" thread.");
986 DWORD dwWaitTime = 10000; // 10 sec
987 DWORD dwCurTime = dwTime;
989 while (1)
991 if (!m_isSkipThread && GetTickCount() - dwCurTime > dwWaitTime)
993 if (CryMessageBox(_T("Connection to Perforce is not responding. Do you want to switch to the offline mode?"), _T("Perforce Plug-in Error"), eMB_YesCancel) == eQR_Yes)
995 SetWorkOffline(true);
996 CheckConnection();
997 break;
999 dwCurTime = GetTickCount();
1000 dwWaitTime = 10000; // 10 sec
1003 CrySleep(50);
1005 if (m_isSetuped)
1007 AUTO_LOCK(g_cPerforceValues);
1008 unRetValue = m_unRetValue;
1009 break;
1013 m_isSecondThread = false;
1014 m_dwLastAccessTime = dwTime;
1016 return unRetValue;
1019 bool CPerforceSourceControl::IsFolder(const char* filename, char* FullFileName)
1021 bool bFolder = false;
1023 char sFullFilename[ICryPak::g_nMaxPath];
1024 ConvertFileNameCS(sFullFilename, filename);
1026 uint32 attr = ::GetFileAttributes(sFullFilename);
1027 if (attr == INVALID_FILE_ATTRIBUTES)
1029 if (*sFullFilename && sFullFilename[strlen(sFullFilename) - 1] == '\\')
1030 bFolder = true;
1031 else
1032 return false;
1034 else if ((attr & FILE_ATTRIBUTE_DIRECTORY))
1035 bFolder = true;
1037 if (bFolder && FullFileName)
1038 strcpy(FullFileName, sFullFilename);
1040 return bFolder;
1043 bool CPerforceSourceControl::DoesChangeListExist(const char* pDesc, char* changeid, int nLen)
1045 if (!Reconnect())
1046 return false;
1048 bool ret = false;
1050 char* argv[] = { "-c", m_client.GetClient().Text(), "-s", "pending", "-l" };
1051 m_ui.Init();
1052 m_client.SetArgv(5, argv);
1053 m_client.Run("changes", &m_ui);
1055 string id("");
1056 bool foundChange = false;
1057 string changes(m_ui.m_output);
1058 string token("\r\n");
1059 int nStart = 0;
1060 string item = changes.Tokenize(token, nStart);
1061 while (!item.IsEmpty())
1063 int idx = item.Find("Change ");
1064 if (idx != -1)
1066 int last = item.Find(" on ");
1067 int first = item.Find(" ");
1068 id = item.Left(last);
1069 id = id.Right(id.GetLength() - (first + 1));
1071 idx = item.Find(pDesc);
1072 if (idx != -1)
1074 foundChange = true;
1075 break;
1078 item = changes.Tokenize(token, nStart);
1081 m_ui.m_output.Empty();
1082 m_ui.m_input.Empty();
1084 if (!m_ui.m_e.Test() && !id.IsEmpty() && foundChange)
1086 cry_sprintf(changeid, nLen, "%s", id.GetBuffer());
1087 ret = true;
1090 return ret;
1093 bool CPerforceSourceControl::CreateChangeList(const char* pDesc, char* changeid, int nLen)
1095 if (!Reconnect())
1096 return false;
1098 bool ret = false;
1100 char* argv[] = { "-o" };
1101 m_ui.Init();
1102 m_client.SetArgv(1, argv);
1103 m_client.Run("change", &m_ui);
1105 string change(m_ui.m_output);
1106 string description;
1107 description.Format("%s", pDesc);
1108 change.Replace("<enter description here>", description);
1110 string files("Files:");
1111 int end = change.Find(files.GetBuffer()) + files.GetLength();
1112 int iFiles = change.Find(files, end);
1113 if (iFiles >= 0)
1115 end = iFiles;
1116 if (change.GetLength() > end)
1117 change = change.Left(end);
1120 m_ui.Init();
1121 m_ui.m_input = change;
1122 m_ui.m_bIsCreatingChangelist = true;
1123 char* argv2[] = { "-i" };
1124 m_client.SetArgv(1, argv2);
1125 m_client.Run("change", &m_ui);
1126 m_ui.m_bIsCreatingChangelist = false;
1128 string changeId(m_ui.m_output);
1129 int lastIdx = changeId.ReverseFind(' ');
1130 int firstIdx = changeId.Find(' ');
1131 string left = changeId.Left(lastIdx);
1132 string id(left.Right(left.GetLength() - (firstIdx + 1)));
1133 cry_sprintf(changeid, nLen, "%s", id.GetBuffer());
1135 if (!m_ui.m_e.Test())
1136 ret = true;
1138 return ret;
1141 bool CPerforceSourceControl::SubmitChangeList(char* changeid)
1143 if (!Reconnect())
1144 return false;
1146 bool bRet = false;
1148 char* argv[] = { "-c", changeid };
1149 m_ui.Init();
1150 m_client.SetArgv(2, argv);
1151 m_client.Run("submit", &m_ui);
1153 if (!m_ui.m_e.Test())
1154 bRet = true;
1156 return bRet;
1159 bool CPerforceSourceControl::DeleteChangeList(char* changeid)
1161 if (!Reconnect())
1162 return false;
1164 bool bRet = false;
1166 char* argv[] = { "-d", changeid };
1167 m_ui.Init();
1168 m_client.SetArgv(2, argv);
1169 m_client.Run("change", &m_ui);
1171 if (!m_ui.m_e.Test())
1172 bRet = true;
1174 return bRet;
1177 bool CPerforceSourceControl::Reopen(const char* filename, char* changeid)
1179 if (!Reconnect())
1180 return false;
1182 bool bRet = false;
1184 char FullFileName[MAX_PATH];
1185 string files = filename;
1186 int curPos = 0;
1187 string file = files.Tokenize(";", curPos);
1188 for (; !file.IsEmpty(); file = files.Tokenize(";", curPos))
1190 if (file.Trim().IsEmpty())
1191 continue;
1192 uint32 attrib = GetFileAttributesAndFileName(file, FullFileName);
1194 if ((attrib != SCC_FILE_ATTRIBUTE_INVALID) && (attrib & SCC_FILE_ATTRIBUTE_MANAGED) && (attrib & SCC_FILE_ATTRIBUTE_CHECKEDOUT))
1196 char* argv[] = { "-c", changeid, FullFileName };
1197 m_ui.Init();
1198 m_client.SetArgv(3, argv);
1199 m_client.Run("reopen", &m_ui);
1201 if (!m_ui.m_e.Test())
1202 bRet = true;
1206 return bRet;
1209 bool CPerforceSourceControl::Add(const char* filename, const char* desc, int nFlags, char* changelistId)
1211 if (!Reconnect())
1212 return false;
1214 bool bRet = false;
1216 char FullFileName[MAX_PATH];
1217 string files = filename;
1218 int curPos = 0;
1219 string file = files.Tokenize(";", curPos);
1220 for (; !file.IsEmpty(); file = files.Tokenize(";", curPos))
1222 if (file.Trim().IsEmpty())
1223 continue;
1224 uint32 attrib = GetFileAttributesAndFileName(file, FullFileName);
1225 char sFullFilename[ICryPak::g_nMaxPath];
1226 if (attrib & SCC_FILE_ATTRIBUTE_FOLDER)
1228 cry_strcpy(sFullFilename, filename);
1229 cry_strcat(sFullFilename, "*");
1231 else
1233 MakePathCS(sFullFilename, FullFileName);
1234 if (strcmp(sFullFilename, FullFileName))
1235 RenameFolders(sFullFilename, FullFileName);
1238 if ((attrib & SCC_FILE_ATTRIBUTE_FOLDER) || ((attrib != SCC_FILE_ATTRIBUTE_INVALID) && !(attrib & SCC_FILE_ATTRIBUTE_MANAGED) && IsFileManageable(sFullFilename)))
1240 if (nFlags & ADD_CHANGELIST && changelistId)
1242 char* argv[] = { "-c", changelistId, sFullFilename };
1243 m_ui.Init();
1244 m_client.SetArgv(3, argv);
1245 m_client.Run("add", &m_ui);
1247 if (desc && !(nFlags & ADD_WITHOUT_SUBMIT))
1249 cry_strcpy(m_ui.m_desc, desc);
1250 m_ui.Init();
1251 m_client.SetArgv(2, argv);
1252 m_client.Run("submit", &m_ui);
1255 else
1257 char* argv[] = { sFullFilename };
1258 m_ui.Init();
1259 m_client.SetArgv(1, argv);
1260 m_client.Run("add", &m_ui);
1262 if (desc && !(nFlags & ADD_WITHOUT_SUBMIT))
1264 cry_strcpy(m_ui.m_desc, desc);
1265 m_ui.Init();
1266 m_client.SetArgv(1, argv);
1267 m_client.Run("submit", &m_ui);
1271 if (!m_ui.m_e.Test())
1272 bRet = true;
1276 return bRet;
1279 bool CPerforceSourceControl::CheckIn(const char* filename, const char* desc, int nFlags)
1281 if (!Reconnect())
1282 return false;
1284 bool bRet = false;
1286 char FullFileName[MAX_PATH];
1288 string files = filename;
1289 int curPos = 0;
1290 string file = files.Tokenize(";", curPos);
1291 for (; !file.IsEmpty(); file = files.Tokenize(";", curPos))
1293 if (file.Trim().IsEmpty())
1294 continue;
1295 uint32 attrib = GetFileAttributesAndFileName(file, FullFileName);
1297 if ((attrib & SCC_FILE_ATTRIBUTE_FOLDER) || ((attrib != SCC_FILE_ATTRIBUTE_INVALID) && (attrib & SCC_FILE_ATTRIBUTE_MANAGED) && (attrib & SCC_FILE_ATTRIBUTE_CHECKEDOUT)))
1300 char* argv[] = { "-c", "default", FullFileName };
1301 m_client.SetArgv(3, argv);
1302 m_client.Run("reopen", &m_ui);
1305 if (desc)
1306 cry_strcpy(m_ui.m_desc, desc);
1308 char* argv[] = { FullFileName };
1309 m_client.SetArgv(1, argv);
1310 m_client.Run("submit", &m_ui);
1313 if (!m_ui.m_e.Test())
1314 bRet = true;
1318 return bRet;
1321 bool CPerforceSourceControl::CheckOut(const char* filename, int nFlags, char* changelistId)
1323 if (!Reconnect())
1324 return false;
1326 bool bRet = false;
1328 char FullFileName[MAX_PATH];
1330 string files = filename;
1331 int curPos = 0;
1332 string file = files.Tokenize(";", curPos);
1333 for (; !file.IsEmpty(); file = files.Tokenize(";", curPos))
1335 if (file.Trim().IsEmpty())
1336 continue;
1337 uint32 attrib = GetFileAttributesAndFileName(file, FullFileName);
1339 if ((attrib & SCC_FILE_ATTRIBUTE_FOLDER) || ((attrib & SCC_FILE_ATTRIBUTE_MANAGED) && !(attrib & SCC_FILE_ATTRIBUTE_CHECKEDOUT)))
1341 if (nFlags & ADD_CHANGELIST && changelistId)
1343 char* argv[] = { "-c", changelistId, FullFileName };
1344 m_client.SetArgv(3, argv);
1345 m_client.Run("edit", &m_ui);
1347 else
1349 char* argv[] = { FullFileName };
1350 m_client.SetArgv(1, argv);
1351 m_client.Run("edit", &m_ui);
1354 if (!m_ui.m_e.Test())
1355 bRet = true;
1358 return bRet;
1361 bool CPerforceSourceControl::UndoCheckOut(const char* filename, int nFlags)
1363 if (!Reconnect())
1364 return false;
1366 bool bRet = false;
1368 char FullFileName[MAX_PATH];
1370 string files = filename;
1371 int curPos = 0;
1372 string file = files.Tokenize(";", curPos);
1373 for (; !file.IsEmpty(); file = files.Tokenize(";", curPos))
1375 if (file.Trim().IsEmpty())
1376 continue;
1377 uint32 attrib = GetFileAttributesAndFileName(file, FullFileName);
1379 if ((attrib & SCC_FILE_ATTRIBUTE_FOLDER) || ((attrib & SCC_FILE_ATTRIBUTE_MANAGED) && (attrib & SCC_FILE_ATTRIBUTE_CHECKEDOUT)))
1381 char* argv[] = { FullFileName };
1382 m_client.SetArgv(1, argv);
1383 m_client.Run("revert", &m_ui);
1384 if (!m_ui.m_e.Test())
1385 bRet = true;
1388 return true; // always return true to avoid error message box on folder operation
1391 bool CPerforceSourceControl::Rename(const char* filename, const char* newname, const char* desc, int nFlags)
1393 if (!Reconnect())
1394 return false;
1396 bool bRet = false;
1397 char FullFileName[MAX_PATH];
1398 uint32 attrib = GetFileAttributesAndFileName(filename, FullFileName);
1400 if (!(attrib & SCC_FILE_ATTRIBUTE_MANAGED))
1401 return true;
1403 if (attrib & SCC_FILE_ATTRIBUTE_CHECKEDOUT)
1404 UndoCheckOut(filename, 0);
1406 //if(m_pIntegrator->FileCheckedOutByAnotherUser(FullFileName))
1407 //return false;
1409 char FullNewFileName[MAX_PATH];
1410 cry_strcpy(FullNewFileName, newname);
1413 char* argv[] = { FullFileName, FullNewFileName };
1414 m_client.SetArgv(2, argv);
1415 m_client.Run("integrate", &m_ui);
1419 char* argv[] = { FullFileName };
1420 m_client.SetArgv(1, argv);
1421 m_client.Run("delete", &m_ui);
1423 if (desc)
1424 cry_strcpy(m_ui.m_desc, desc);
1426 char* argv[] = { FullFileName };
1427 m_client.SetArgv(1, argv);
1428 m_client.Run("submit", &m_ui);
1431 if (desc)
1432 cry_strcpy(m_ui.m_desc, desc);
1434 char* argv[] = { FullNewFileName };
1435 m_client.SetArgv(1, argv);
1436 m_client.Run("submit", &m_ui);
1439 if (!m_ui.m_e.Test())
1440 bRet = true;
1442 //p4 integrate source_file target_file
1443 //p4 delete source_file
1444 //p4 submit
1446 return bRet;
1449 bool CPerforceSourceControl::Delete(const char* filename, const char* desc, int nFlags, char* changelistId /*= NULL*/)
1451 if (!Reconnect())
1452 return false;
1454 bool bRet = false;
1455 char FullFileName[MAX_PATH];
1456 uint32 attrib = GetFileAttributesAndFileName(filename, FullFileName);
1458 if (!(attrib & SCC_FILE_ATTRIBUTE_MANAGED))
1459 return true;
1461 if ((attrib & SCC_FILE_ATTRIBUTE_MANAGED) && (attrib & SCC_FILE_ATTRIBUTE_CHECKEDOUT))
1463 char* argv[] = { FullFileName };
1464 m_client.SetArgv(1, argv);
1465 m_client.Run("revert", &m_ui);
1468 if ((nFlags & ADD_CHANGELIST) && (changelistId != NULL))
1470 char* argv[] = { "-c", changelistId, FullFileName };
1471 m_client.SetArgv(3, argv);
1472 m_client.Run("delete", &m_ui);
1474 if (desc && !(nFlags & DELETE_WITHOUT_SUBMIT))
1476 cry_strcpy(m_ui.m_desc, desc);
1477 m_client.SetArgv(2, argv);
1478 m_client.Run("submit", &m_ui);
1481 else
1483 char* argv[] = { FullFileName };
1484 m_client.SetArgv(1, argv);
1485 m_client.Run("delete", &m_ui);
1487 if (desc && !(nFlags & DELETE_WITHOUT_SUBMIT))
1489 cry_strcpy(m_ui.m_desc, desc);
1490 m_client.SetArgv(1, argv);
1491 m_client.Run("submit", &m_ui);
1495 if (!m_ui.m_e.Test())
1496 bRet = true;
1498 return bRet;
1501 void CPerforceSourceControl::SetWorkOffline(bool bWorkOffline)
1503 m_bIsWorkOffline = bWorkOffline;
1504 signalWorkOfflineChanged();
1507 void CPerforceSourceControl::ShowSettings()
1509 m_isSkipThread = true;
1511 string strPassword(m_client.GetPassword().Value());
1512 string strClient(m_client.GetClient().Value());
1513 string strUser(m_client.GetUser().Value());
1514 string strPort(m_client.GetPort().Value());
1516 if (PerforceConnection::OpenPasswordDlg(strPort, strUser, strClient, strPassword, m_bIsWorkOffline))
1518 Error e;
1519 m_client.DefinePassword(strPassword, &e);
1520 m_client.DefineClient(strClient, &e);
1521 m_client.DefinePort(strPort, &e);
1522 m_client.DefineUser(strUser, &e);
1523 CheckConnection();
1527 const char* CPerforceSourceControl::GetErrorByGenericCode(int nGeneric)
1529 switch (nGeneric)
1531 case EV_USAGE:
1532 return "Request is not consistent with documentation or cannot support a server version";
1533 case EV_UNKNOWN:
1534 return "Using unknown entity";
1535 case EV_CONTEXT:
1536 return "Using entity in wrong context";
1537 case EV_ILLEGAL:
1538 return "Trying to do something you can't";
1539 case EV_NOTYET:
1540 return "Something must be corrected first";
1541 case EV_PROTECT:
1542 return "Operation was prevented by protection level";
1544 // No fault at all
1545 case EV_EMPTY:
1546 return "Action returned empty results";
1548 // not the fault of the user
1549 case EV_FAULT:
1550 return "Inexplicable program fault";
1551 case EV_CLIENT:
1552 return "Client side program errors";
1553 case EV_ADMIN:
1554 return "Server administrative action required";
1555 case EV_CONFIG:
1556 return "Client configuration is inadequate";
1557 case EV_UPGRADE:
1558 return "Client or server is too old to interact";
1559 case EV_COMM:
1560 return "Communication error";
1561 case EV_TOOBIG:
1562 return "File is too big";
1565 return "Undefined";
1568 bool CPerforceSourceControl::Run(const char* func, int nArgs, char* argv[], bool bOnlyFatal)
1570 for (int x = 0; x < nArgs; ++x)
1572 if (argv[x][0] == '\0')
1573 return false;
1576 if (!Reconnect())
1577 return false;
1579 bool bRet = false;
1581 m_ui.Init();
1582 m_client.SetArgv(nArgs, argv);
1583 m_client.Run(func, &m_ui);
1584 m_client.WaitTag();
1586 #if 0 // connection debug
1587 for (int argid = 0; argid < nArgs; argid++)
1589 CryLog("\n arg %d : %s", argid, argv[argid]);
1591 #endif // connection debug
1593 if (bOnlyFatal)
1595 if (!m_ui.m_e.IsFatal())
1596 bRet = true;
1598 else
1600 if (!m_ui.m_e.Test())
1601 bRet = true;
1604 if (m_ui.m_e.GetSeverity() == E_FAILED || m_ui.m_e.GetSeverity() == E_FATAL)
1606 static int nGenericPrev = 0;
1607 const int nGeneric = m_ui.m_e.GetGeneric();
1609 if (IsSomeTimePassed())
1610 nGenericPrev = 0;
1612 if (nGenericPrev != nGeneric)
1614 CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_ERROR, "Perforce plugin: %s", GetErrorByGenericCode(nGeneric));
1616 StrBuf m;
1617 m_ui.m_e.Fmt(&m);
1618 CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_ERROR, "Perforce plugin: %s", m.Text());
1619 nGenericPrev = nGeneric;
1623 m_ui.m_e.Clear();
1624 return bRet;
1627 bool CPerforceSourceControl::CheckConnection()
1629 m_ui.m_bIsClientUnknown = false;
1630 bool bRet = false;
1632 if (!m_bIsWorkOffline)
1634 bRet = Run("info", 0, 0);
1637 if (m_bIsWorkOffline)
1639 CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_ERROR, "Perforce plugin: Perforce is offline");
1640 return false;
1643 if (m_ui.m_bIsClientUnknown || m_ui.m_clientRoot.IsEmpty())
1645 string errorMsg;
1646 errorMsg.Format("Workspace %s for host %s is unknown. Check Perforce settings.", m_ui.m_clientName, m_ui.m_clientHost);
1647 CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_ERROR, string("Perforce plugin: ") + errorMsg);
1648 bRet = false;
1650 else if (m_ui.m_clientRoot.CompareNoCase(m_ui.m_currentDirectory.Left(m_ui.m_clientRoot.GetLength())))
1652 string errorMsg;
1653 errorMsg.Format("Working folder (%s) is out of Perforce root (%s). Change Perforce settings or start Editor from %s...", m_ui.m_currentDirectory, m_ui.m_clientRoot, m_ui.m_clientRoot);
1654 CryWarning(VALIDATOR_MODULE_EDITOR, VALIDATOR_ERROR, string("Perforce plugin: ") + errorMsg);
1655 bRet = false;
1657 return bRet;
1660 bool CPerforceSourceControl::GetLatestVersion(const char* filename, int nFlags)
1662 if (!Reconnect())
1663 return false;
1665 bool bRet = false;
1666 char FullFileName[MAX_PATH];
1668 string files = filename;
1669 int curPos = 0;
1670 string file = files.Tokenize(";", curPos);
1671 for (; !file.IsEmpty(); file = files.Tokenize(";", curPos))
1673 if (file.Trim().IsEmpty())
1674 continue;
1675 uint32 attrib = GetFileAttributesAndFileName(file, FullFileName);
1677 if (!(attrib & SCC_FILE_ATTRIBUTE_MANAGED))
1678 continue;
1680 if ((attrib & SCC_FILE_ATTRIBUTE_CHECKEDOUT) && ((nFlags & GETLATEST_REVERT) == 0))
1681 continue;
1683 if (nFlags & GETLATEST_REVERT)
1685 char* argv[] = { FullFileName };
1686 m_client.SetArgv(1, argv);
1687 m_client.Run("revert", &m_ui);
1690 if (nFlags & GETLATEST_ONLY_CHECK)
1692 if (attrib & SCC_FILE_ATTRIBUTE_FOLDER)
1693 bRet = true;
1694 else
1695 bRet = IsFileLatestVersion(FullFileName);
1697 else
1699 char* argv[] = { "-f", FullFileName };
1700 m_client.SetArgv(2, argv);
1701 m_client.Run("sync", &m_ui);
1702 bRet = true;
1706 return bRet;
1709 bool CPerforceSourceControl::GetInternalPath(const char* filename, char* outPath, int nOutPathSize)
1711 if (!filename || !outPath)
1712 return false;
1714 uint32 attrib = GetFileAttributesAndFileName(filename, 0);
1716 if (attrib & SCC_FILE_ATTRIBUTE_MANAGED && *m_ui.m_depotFile)
1718 cry_strcpy(outPath, nOutPathSize, m_ui.m_depotFile);
1719 return true;
1721 return false;
1724 bool CPerforceSourceControl::GetOtherUser(const char* filename, char* outUser, int nOutUserSize)
1726 if (!filename || !outUser)
1727 return false;
1729 uint32 attrib = GetFileAttributesAndFileName(filename, 0);
1731 if (attrib & SCC_FILE_ATTRIBUTE_MANAGED && *m_ui.m_otherUser)
1733 cry_strcpy(outUser, nOutUserSize, m_ui.m_otherUser);
1734 return true;
1736 return false;
1739 bool CPerforceSourceControl::History(const char* filename)
1741 if (!filename)
1742 return false;
1744 uint32 attrib = GetFileAttributesAndFileName(filename, 0);
1746 if (attrib & SCC_FILE_ATTRIBUTE_MANAGED && *m_ui.m_depotFile)
1748 string commandLine = string("-cmd \"history ") + string(m_ui.m_depotFile) + "\"";
1749 ShellExecute(NULL, NULL, "p4v.exe", commandLine, NULL, SW_SHOW);
1750 return true;
1752 return false;
1755 bool CPerforceSourceControl::IsSomeTimePassed()
1757 const DWORD dwSomeTime = 10000; // 10 sec
1758 static DWORD dwLastTime = 0;
1759 DWORD dwCurTime = GetTickCount();
1761 if (dwCurTime - dwLastTime > dwSomeTime)
1763 dwLastTime = dwCurTime;
1764 return true;
1767 return false;
1770 bool CPerforceSourceControl::GetOtherLockOwner(const char* filename, char* outUser, int nOutUserSize)
1772 if (NULL == filename || NULL == outUser)
1773 return false;
1775 uint32 attrib = GetFileAttributesAndFileName(filename, 0);
1776 if (attrib & SCC_FILE_ATTRIBUTE_LOCKEDBYANOTHER && *m_ui.m_lockedBy)
1778 cry_strcpy(outUser, nOutUserSize, m_ui.m_lockedBy);
1779 return true;
1782 return false;
1785 ESccLockStatus CPerforceSourceControl::GetLockStatus(const char* filename)
1787 Reconnect();
1788 ESccLockStatus nRet = SCC_LOCK_STATUS_UNLOCKED;
1790 m_ui.m_e.Clear();
1792 char fl[MAX_PATH];
1793 cry_strcpy(fl, filename);
1794 char* argv[] = { fl };
1795 m_ui.Init();
1796 m_client.SetArgv(1, argv);
1797 m_client.Run("fstat", &m_ui);
1798 m_client.WaitTag();
1800 if (!m_ui.m_e.Test())
1801 nRet = SCC_LOCK_STATUS_UNLOCKED; // default to not locked
1803 if (nRet >= 0 // if no error
1804 && (0 == strcmp(m_ui.m_action, "edit") || 0 == strcmp(m_ui.m_action, "add")) // checked out
1807 nRet = m_ui.m_lockStatus;
1810 m_ui.m_e.Clear();
1811 return nRet;
1814 bool CPerforceSourceControl::Lock(const char* filename, int nFlags)
1816 Reconnect();
1817 char fullFileName[MAX_PATH];
1818 uint32 attrib = GetFileAttributesAndFileName(filename, fullFileName);
1820 if (!(attrib & SCC_FILE_ATTRIBUTE_MANAGED))
1821 return false;
1823 if (!(attrib & SCC_FILE_ATTRIBUTE_CHECKEDOUT))
1825 if (false == CheckOut(filename, 0))
1826 return false;
1829 if (attrib & SCC_FILE_ATTRIBUTE_LOCKEDBYANOTHER)
1830 return false;
1832 char* argv[] = { fullFileName };
1833 m_client.SetArgv(1, argv);
1834 m_client.Run("lock", &m_ui);
1835 return !m_ui.m_e.Test();
1838 bool CPerforceSourceControl::Unlock(const char* filename, int nFlags)
1840 Reconnect();
1841 char fullFileName[MAX_PATH];
1842 uint32 attrib = GetFileAttributesAndFileName(filename, fullFileName);
1844 if (!(attrib & SCC_FILE_ATTRIBUTE_MANAGED))
1845 return false;
1847 if (attrib & SCC_FILE_ATTRIBUTE_LOCKEDBYANOTHER)
1848 return false;
1850 if (!(attrib & SCC_FILE_ATTRIBUTE_CHECKEDOUT))
1851 return true;
1853 char* argv[] = { fullFileName };
1854 m_client.SetArgv(1, argv);
1855 m_client.Run("unlock", &m_ui);
1856 return !m_ui.m_e.Test();
1859 bool CPerforceSourceControl::GetUserName(char* outUser, int nOutUserSize)
1861 cry_strcpy(outUser, nOutUserSize, m_client.GetUser().Text());
1862 return true;
1865 bool CPerforceSourceControl::GetFileRev(const char* sFilename, int64* pHaveRev, int64* pHeadRev)
1867 Reconnect();
1868 bool bRet = false;
1870 m_ui.Init();
1871 m_ui.m_nFileHaveRev = -1;
1872 m_ui.m_nFileHeadRev = -1;
1874 char fl[MAX_PATH];
1875 cry_strcpy(fl, sFilename);
1876 char* argv[] = { fl };
1877 m_client.SetArgv(1, argv);
1878 m_client.Run("fstat", &m_ui);
1879 m_client.WaitTag();
1881 if (m_ui.m_nFileHaveRev >= 0 || m_ui.m_nFileHeadRev >= 0)
1882 bRet = true;
1884 if (pHaveRev)
1885 *pHaveRev = m_ui.m_nFileHaveRev;
1886 if (pHeadRev)
1887 *pHeadRev = m_ui.m_nFileHeadRev;
1889 m_ui.m_e.Clear();
1890 return bRet;
1893 bool CPerforceSourceControl::GetRevision(const char* filename, int64 nRev, int nFlags)
1895 bool bRet = false;
1896 char FullFileName[MAX_PATH];
1898 string files = filename;
1899 int curPos = 0;
1900 string file = files.Tokenize(";", curPos);
1901 for (; !file.IsEmpty(); file = files.Tokenize(";", curPos))
1903 if (file.Trim().IsEmpty())
1904 continue;
1905 uint32 attrib = GetFileAttributesAndFileName(file, FullFileName);
1907 cry_sprintf(&FullFileName[strlen(FullFileName)], sizeof(FullFileName) - strlen(FullFileName), "#%I64d", nRev);
1909 if (!(attrib & SCC_FILE_ATTRIBUTE_MANAGED))
1910 continue;
1912 if (attrib & SCC_FILE_ATTRIBUTE_CHECKEDOUT && (nFlags & GETLATEST_REVERT))
1914 char* argv[] = { FullFileName };
1915 m_client.SetArgv(1, argv);
1916 m_client.Run("revert", &m_ui);
1918 if (nFlags & GETLATEST_ONLY_CHECK)
1920 if (attrib & SCC_FILE_ATTRIBUTE_FOLDER)
1921 bRet = true;
1922 else
1923 bRet = IsFileLatestVersion(FullFileName);
1925 else
1927 char* argv[] = { "-f", FullFileName };
1928 m_client.SetArgv(2, argv);
1929 m_client.Run("sync", &m_ui);
1930 bRet = true;
1934 return bRet;