Skip to content

Commit

Permalink
Move version checker into its own class
Browse files Browse the repository at this point in the history
  • Loading branch information
Gumball2415 committed Nov 14, 2020
1 parent 85a5fa3 commit 50233a8
Show file tree
Hide file tree
Showing 8 changed files with 237 additions and 105 deletions.
2 changes: 2 additions & 0 deletions Dn-FamiTracker.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@
<ClCompile Include="Source\SwapDlg.cpp" />
<ClCompile Include="Source\TransposeDlg.cpp" />
<ClCompile Include="Source\utils\ftmath.cpp" />
<ClCompile Include="Source\VersionChecker.cpp" />
<ClCompile Include="Source\VisualizerBase.cpp" />
<ClCompile Include="Source\WaveEditor.cpp" />
<ClCompile Include="Source\GraphEditor.cpp" />
Expand Down Expand Up @@ -635,6 +636,7 @@ makehm /h /a afxhh.h IDW_,HIDW_,0x50000 "%(FullPath)" &gt;&gt; "hlp\HTMLDefines.
<ClInclude Include="Source\type_safe\visitor.hpp" />
<ClInclude Include="Source\utils\ftmath.h" />
<ClInclude Include="Source\utils\input.h" />
<ClInclude Include="Source\VersionChecker.h" />
<ClInclude Include="Source\VisualizerBase.h" />
<ClInclude Include="Source\WaveformGenerator.h" />
<ClInclude Include="Source\WavegenBuiltin.h" />
Expand Down
6 changes: 6 additions & 0 deletions Dn-FamiTracker.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,9 @@
<ClCompile Include="Source\APU\nezplug\FDSSound.cpp">
<Filter>Source Files\Sound Driver\Emulation\Sound Chips\External\nezplug</Filter>
</ClCompile>
<ClCompile Include="Source\VersionChecker.cpp">
<Filter>Source Files\Other</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Source\Exception.h">
Expand Down Expand Up @@ -1492,6 +1495,9 @@
<ClInclude Include="Source\APU\nsfplay\legacy\vrc7tone_nuke.h">
<Filter>Header Files\Sound Driver Headers\Emulation Headers\Sound Chip Headers\External\nsfplay</Filter>
</ClInclude>
<ClInclude Include="Source\VersionChecker.h">
<Filter>Header Files\Other Headers</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Dn-FamiTracker.rc">
Expand Down
118 changes: 15 additions & 103 deletions Source/FamiTracker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
#include "stdafx.h"
#include "Exception.h"
#include "FamiTracker.h"
#include "version.h" // // //
#include "Exception.h"
#include "FamiTrackerDoc.h"
#include "FamiTrackerView.h"
#include "MainFrm.h"
Expand All @@ -40,8 +42,9 @@
#include "WinSDK/VersionHelpers.h" // // //
#include "VisualizerWnd.h" // // //
#include "htmlhelp.h" // // !!
#include "WinInet.h" // // //
#pragma comment(lib, "wininet.lib")
#include "VersionChecker.h" // // //
#include <iostream> // // //
#include "str_conv/str_conv.hpp" // // //

// 0CC uses AfxRegSetValue() and AfxGetModuleShortFileName(),
// found in the undocumented header afxpriv.h.
Expand Down Expand Up @@ -327,8 +330,7 @@ int CFamiTrackerApp::ExitInstance()
}
#endif

if (m_thVersionCheck.joinable()) // // //
m_thVersionCheck.join();
m_pVersionChecker.reset(); // // //

TRACE("App: End ExitInstance\n");

Expand Down Expand Up @@ -542,101 +544,8 @@ void CFamiTrackerApp::UnregisterSingleInstance()

void CFamiTrackerApp::CheckNewVersion(bool StartUp) // // //
{
return;

static PCTSTR rgpszAcceptTypes[] = {_T("application/json"), NULL};

m_bVersionReady = false;
m_pVersionMessage = _T("");
m_iVersionStyle = 0U;

const auto CheckFunc = [&] (bool Start) {
HINTERNET hOpen, hConnect, hRequest;
CString jsonStr;

// FIXME FIXME github repo differs from APP_NAME
// also burn this code with fire

try {
if ((hOpen = InternetOpen(_T("0CC_FamiTracker"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0)) &&
(hConnect = InternetConnect(hOpen, _T("api.github.com"),
INTERNET_DEFAULT_HTTPS_PORT, _T(""), _T(""), INTERNET_SERVICE_HTTP, 0, 0)) &&
(hRequest = HttpOpenRequest(hConnect, _T("GET"), _T("/repos/Gumball2415/Dn-FamiTracker/releases"),
_T("HTTP/1.0"), NULL, rgpszAcceptTypes,
INTERNET_FLAG_RELOAD | INTERNET_FLAG_SECURE | INTERNET_FLAG_NO_CACHE_WRITE, NULL))) {
HttpAddRequestHeaders(hRequest, _T("Content-Type: application/json\r\n"), -1, HTTP_ADDREQ_FLAG_ADD);

if (!HttpSendRequest(hRequest, NULL, 0, NULL, 0)) throw GetLastError();
while (true) {
DWORD Size;
if (!InternetQueryDataAvailable(hRequest, &Size, 0, 0)) throw GetLastError();
if (!Size) break;
char *Buf = new char[Size + 1]();
DWORD Received = 0;
for (DWORD i = 0; i < Size; i += 1024) {
DWORD Length = (Size - i < 1024) ? Size % 1024 : 1024;
if (!InternetReadFile(hRequest, Buf + i, Length, &Received))
throw GetLastError();
}
jsonStr += Buf;
SAFE_RELEASE_ARRAY(Buf);
}
nlohmann::json j = nlohmann::json::parse(jsonStr.GetBuffer());
for (const auto &i : j) {
int Ver[4] = { };
sscanf_s(i["tag_name"].get<std::string>().c_str(),
"v%u.%u.%u%*1[.r]%u", Ver, Ver + 1, Ver + 2, Ver + 3);

// TODO std::vector comparison

if (Ver[0] > VERSION_API || Ver[0] == VERSION_API &&
(Ver[1] > VERSION_MAJ || Ver[1] == VERSION_MAJ &&
(Ver[2] > VERSION_MIN || Ver[2] == VERSION_MIN &&
Ver[3] > VERSION_REV))) {
int Y = 1970, M = 1, D = 1;
sscanf_s(i["published_at"].get<std::string>().c_str(), "%d-%d-%d", &Y, &M, &D);
static const CString MONTHS[] = {
_T("Jan"), _T("Feb"), _T("Mar"), _T("Apr"), _T("May"), _T("Jun"),
_T("Jul"), _T("Aug"), _T("Sept"), _T("Oct"), _T("Nov"), _T("Dec"),
};

CString desc = i["body"].get<std::string>().c_str();
int Index = desc.Find(_T("\r\n\r\n"));
if (Index >= 0)
desc.Delete(0, Index + 4);
Index = desc.Find(_T("\r\n\r\n#"));
if (Index >= 0)
desc.Truncate(Index);

m_pVersionMessage.Format(_T("A new version of " APP_NAME " is now available:\n\n"
"Version %d.%d.%d.%d (released %s %d, %d)\n\n%s\n\n"
"Pressing \"Yes\" will launch the Github web page for this release."),
Ver[0], Ver[1], Ver[2], Ver[3], MONTHS[--M], D, Y, desc);
if (Start)
m_pVersionMessage.Append(_T(" (Version checking on startup may be disabled in the configuration menu.)"));
m_pVersionURL.Format(_T("https://github.com/Gumball2415/Dn-FamiTracker/releases/tag/Dn%d.%d.%d.%d"),
Ver[0], Ver[1], Ver[2], Ver[3]);
m_iVersionStyle = MB_YESNO | MB_ICONINFORMATION;
m_bVersionReady = true;
break;
}
}
}
}
catch (DWORD &) {
m_pVersionMessage = _T("Unable to get version information from the source repository.");
m_iVersionStyle = MB_ICONERROR;
m_bVersionReady = true;
}

if (hRequest) InternetCloseHandle(hRequest);
if (hConnect) InternetCloseHandle(hConnect);
if (hOpen) InternetCloseHandle(hOpen);
};

if (m_thVersionCheck.joinable())
m_thVersionCheck.join();
m_thVersionCheck = std::thread {CheckFunc, StartUp};
//return;
m_pVersionChecker = std::make_unique<CVersionChecker>(StartUp); // // //
}

bool CFamiTrackerApp::CheckSingleInstance(CFTCommandLineInfo &cmdInfo)
Expand Down Expand Up @@ -772,12 +681,15 @@ BOOL CFamiTrackerApp::OnIdle(LONG lCount) // // //
{
if (CWinApp::OnIdle(lCount))
return TRUE;

if (m_bVersionReady && !m_pVersionMessage.IsEmpty()) {
/*if (m_bVersionReady && !m_pVersionMessage.IsEmpty()) {
m_bVersionReady = false;
if (AfxMessageBox(m_pVersionMessage, m_iVersionStyle) == IDYES)
ShellExecute(NULL, _T("open"), m_pVersionURL, NULL, NULL, SW_SHOWNORMAL);
}
}*/
if (m_pVersionChecker && m_pVersionChecker->IsReady())
if (auto pChecker = std::move(m_pVersionChecker); auto result = pChecker->GetVersionCheckResult())
if (AfxMessageBox(conv::to_t(result->Message).data(), result->MessageBoxStyle) == IDYES)
ShellExecuteW(NULL, L"open", conv::to_wide(result->URL).data(), NULL, NULL, SW_SHOWNORMAL);

return FALSE;
}
Expand Down Expand Up @@ -1087,7 +999,7 @@ BOOL CDocManager0CC::DoPromptFileName(CString &fileName, UINT nIDSTitle, DWORD l
CString path = theApp.GetSettings()->GetPath(PATH_FTM) + _T("\\");

CFileDialog OpenFileDlg(bOpenFileDialog, _T("0cc"), NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
_T(APP_NAME " modules (*.0cc;*.ftm)|*.0cc; *.ftm|All files (*.*)|*.*||"), // // //
_T("0CC-FamiTracker modules (*.0cc)|*.0cc|FamiTracker modules (*.ftm)|*.ftm|All files (*.*)|*.*||"), // // !!
AfxGetMainWnd(), 0);
OpenFileDlg.m_ofn.Flags |= lFlags;
OpenFileDlg.m_ofn.lpstrFile = fileName.GetBuffer(_MAX_PATH);
Expand Down
1 change: 1 addition & 0 deletions Source/FamiTracker.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ class CFamiTrackerApp : public CWinApp
CString m_pVersionMessage, m_pVersionURL;
UINT m_iVersionStyle;
bool m_bVersionReady;
std::unique_ptr<CVersionChecker> m_pVersionChecker; // // //

#ifdef SUPPORT_TRANSLATIONS
HINSTANCE m_hInstResDLL;
Expand Down
2 changes: 1 addition & 1 deletion Source/ModulePropertiesDlg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ void CModulePropertiesDlg::OnBnClickedSongImport()

// TODO use string table
CFileDialog OpenFileDlg(TRUE, _T("0cc"), 0, OFN_HIDEREADONLY,
_T(APP_NAME " modules (*.0cc;*.ftm)|*.0cc; *.ftm|All files (*.*)|*.*||"), // // //
_T("0CC-FamiTracker modules (*.0cc)|*.0cc|FamiTracker modules (*.ftm)|*.ftm|All files (*.*)|*.*||"), // // //
theApp.GetMainWnd(), 0);

if (OpenFileDlg.DoModal() == IDCANCEL)
Expand Down
159 changes: 159 additions & 0 deletions Source/VersionChecker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*
** FamiTracker - NES/Famicom sound tracker
** Copyright (C) 2005-2014 Jonathan Liss
**
** 0CC-FamiTracker is (C) 2014-2018 HertzDevil
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Library General Public License for more details. To obtain a
** copy of the GNU Library General Public License, write to the Free
** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
** Any permitted reproduction of these routines, in whole or in part,
** must bear this legend.
*/

#include "VersionChecker.h"
#pragma warning (push)
#pragma warning (disable : 4706) // assignment within conditional expression
#include "json/json.hpp"
#pragma warning (pop)
#include <vector>
#include "stdafx.h"
#include "version.h"
#include "WinInet.h"
#pragma comment(lib, "wininet.lib")

namespace {

LPCWSTR rgpszAcceptTypes[] = { L"application/json", NULL };

// // // TODO: cpr maybe
struct CHttpStringReader {
CHttpStringReader() {
hOpen = InternetOpenW(L"Dn_FamiTracker", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
hConnect = InternetConnectW(hOpen, L"api.github.com",
INTERNET_DEFAULT_HTTPS_PORT, L"", L"", INTERNET_SERVICE_HTTP, 0, 0);
hRequest = HttpOpenRequestW(hConnect, L"GET", L"/repos/Gumball2415/Dn-FamiTracker/releases",
L"HTTP/1.0", NULL, rgpszAcceptTypes,
INTERNET_FLAG_RELOAD | INTERNET_FLAG_SECURE | INTERNET_FLAG_NO_CACHE_WRITE, NULL);
}

~CHttpStringReader() noexcept {
if (hRequest)
InternetCloseHandle(hRequest);
if (hConnect)
InternetCloseHandle(hConnect);
if (hOpen)
InternetCloseHandle(hOpen);
}

nlohmann::json ReadJson() {
if (!hOpen || !hConnect || !hRequest)
return nlohmann::json{ };
if (!HttpSendRequest(hRequest, NULL, 0, NULL, 0))
return nlohmann::json{ };

std::string jsonStr;
while (true) {
DWORD Size;
if (!InternetQueryDataAvailable(hRequest, &Size, 0, 0))
return nlohmann::json{ };
if (!Size)
break;
std::vector<char> Buf(Size + 1);
DWORD Received = 0;
for (DWORD i = 0; i < Size; i += 1024) {
DWORD Length = (Size - i < 1024) ? Size % 1024 : 1024;
if (!InternetReadFile(hRequest, Buf.data() + i, Length, &Received))
return nlohmann::json{ };
}
jsonStr += Buf.data();
}

return nlohmann::json::parse(jsonStr);
}

private:
HINTERNET hOpen;
HINTERNET hConnect;
HINTERNET hRequest;
};

using ft_version_t = std::tuple<int, int, int, int>;

std::pair<nlohmann::json, ft_version_t> FindBestVersion(const nlohmann::json& j) {
ft_version_t current = { VERSION_API, VERSION_MAJ, VERSION_MIN, VERSION_REV };
const nlohmann::json* jPtr = nullptr;

for (const auto& i : j) {
ft_version_t ver = { };
auto& [api, maj, min, rev] = ver;
const std::string& tag = i["tag_name"];
::sscanf_s(tag.data(), "v%u.%u.%u%*1[.r]%u", &api, &maj, &min, &rev);
if (ver > current) {
current = ver;
jPtr = &i;
}
}

return std::make_pair(jPtr ? *jPtr : nlohmann::json{ }, current);
}

} // namespace



CVersionChecker::CVersionChecker(bool StartUp) : th_{ &ThreadFn, StartUp, std::move(promise_) } {
}

CVersionChecker::~CVersionChecker() {
if (th_.joinable()) // // //
th_.join();
}

bool CVersionChecker::IsReady() const {
return future_.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready;
}

std::optional<stVersionCheckResult> CVersionChecker::GetVersionCheckResult() {
return future_.get();
}

void CVersionChecker::ThreadFn(bool startup, std::promise<std::optional<stVersionCheckResult>> p) noexcept try {
if (auto [json, verNum] = FindBestVersion(CHttpStringReader{ }.ReadJson()); !json.empty()) {
auto [api, maj, min, rev] = verNum;
std::string verStr = std::to_string(api) + '.' + std::to_string(maj) + '.' + std::to_string(min) + '.' + std::to_string(rev);

std::string timeStr = json["published_at"];

std::string desc = json["body"];
if (auto pos = desc.find("\r\n\r\n"); pos != std::string::npos)
desc = desc.substr(pos + 4);
if (auto pos = desc.find("\r\n\r\n#"); pos != std::string::npos)
desc = desc.substr(0, pos);

std::string msg = "A new version of Dn-FamiTracker is now available:\n\n";
msg += "Version " + verStr + " (released on " + timeStr + " %s)\n\n";
msg += "Pressing \"Yes\" will launch the Github web page for this release.";
if (startup)
msg += " (Version checking on startup may be disabled in the configuration menu.)";
std::string url = "https://github.com/Gumball2415/Dn-FamiTracker/releases/tag/Dn%s" + verStr;

p.set_value(stVersionCheckResult{ std::move(msg), std::move(url), MB_YESNO | MB_ICONINFORMATION });
}
else
p.set_value(std::nullopt);
}
catch (...) {
p.set_value(std::nullopt);
// p.set_value(stVersionCheckResult {
// "Unable to get version information from the source repository.", "", MB_ICONERROR});
}
Loading

0 comments on commit 50233a8

Please sign in to comment.