Package information file writing and reading support added

This commit is contained in:
Simon Rozman 2016-03-15 20:11:52 +01:00
parent fa607ed0e7
commit 170f78b9cb
17 changed files with 572 additions and 33 deletions

45
Makefile Normal file
View File

@ -0,0 +1,45 @@
KEY_DIR=..\include
######################################################################
# Main targets
######################################################################
All :: \
GenRSAKeypair
Clean ::
# Ommited intentionally not to delete keys accidentaly.
######################################################################
# Folder creation
######################################################################
"$(KEY_DIR)" :
if not exist $@ md $@
######################################################################
# Building
######################################################################
GenRSAKeypair :: \
"$(KEY_DIR)" \
"$(KEY_DIR)\UpdaterKeyPrivate.bin" \
"$(KEY_DIR)\UpdaterKeyPublic.bin"
"$(KEY_DIR)\UpdaterKeypair.txt" :
openssl.exe genrsa -out $@ 4096
"$(KEY_DIR)\UpdaterKeyPrivate.bin" : "$(KEY_DIR)\UpdaterKeypair.txt"
if exist $@ del /f /q $@
if exist "$(@:"=).tmp" del /f /q "$(@:"=).tmp"
openssl.exe rsa -in $** -inform PEM -outform DER -out "$(@:"=).tmp"
move /y "$(@:"=).tmp" $@ > NUL
"$(KEY_DIR)\UpdaterKeyPublic.bin" : "$(KEY_DIR)\UpdaterKeypair.txt"
if exist $@ del /f /q $@
if exist "$(@:"=).tmp" del /f /q "$(@:"=).tmp"
openssl.exe rsa -in $** -inform PEM -outform DER -out "$(@:"=).tmp" -pubout
move /y "$(@:"=).tmp" $@ > NUL

View File

@ -19,25 +19,7 @@
#pragma once
//
// Product version as a single DWORD
// Note: Used for version comparison within C/C++ code.
//
#define UPDATER_VERSION 0x01000000
#define IDR_KEY_PUBLIC 1
//
// Product version by components
// Note: Resource Compiler has limited preprocessing capability,
// thus we need to specify major, minor and other version components
// separately.
//
#define UPDATER_VERSION_MAJ 1
#define UPDATER_VERSION_MIN 0
#define UPDATER_VERSION_REV 0
#define UPDATER_VERSION_BUILD 0
//
// Human readable product version and build year for UI
//
#define UPDATER_VERSION_STR "2.0-alpha"
#define UPDATER_BUILD_YEAR_STR "2016"
#if !defined(RC_INVOKED) && !defined(MIDL_PASS)
#endif // !defined(RC_INVOKED) && !defined(MIDL_PASS)

View File

@ -9,6 +9,9 @@
<ClCompile>
<AdditionalIncludeDirectories>..\..\lib\wxExtend\include;..\..\lib\stdex\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
</Link>
</ItemDefinitionGroup>
<ItemGroup />
</Project>

Binary file not shown.

View File

@ -85,6 +85,7 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h" />
<ClInclude Include="UpdCheck.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="UpdCheck.rc" />

View File

@ -30,6 +30,9 @@
<ClInclude Include="stdafx.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="UpdCheck.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="UpdCheck.rc">

View File

@ -18,6 +18,73 @@
*/
#include "stdafx.h"
#pragma comment(lib, "Crypt32.lib")
///
/// Reads package information from a stream
///
/// \param[in] stream Input stream
/// \param[out] pkg Package information
///
/// \returns The stream \p stream
///
inline std::istream& operator >>(_In_ std::istream& stream, _Out_ Updater::package_info &pkg)
{
// Read binary version.
stream.read((char*)&pkg.ver, sizeof(pkg.ver));
if (!stream.good()) return stream;
// Read human readable description (length prefixed).
unsigned __int32 count;
stream.read((char*)&count, sizeof(count));
if (!stream.good()) return stream;
pkg.desc.resize(count);
stream.read((char*)pkg.desc.data(), sizeof(wchar_t)*count);
// Read package language (length prefixed).
stream.read((char*)&count, sizeof(count));
if (!stream.good()) return stream;
pkg.lang.resize(count);
stream.read((char*)pkg.lang.data(), sizeof(char)*count);
// Read package download URL (length prefixed).
stream.read((char*)&count, sizeof(count));
if (!stream.good()) return stream;
pkg.url.resize(count);
stream.read((char*)pkg.url.data(), sizeof(char)*count);
return stream;
}
///
/// Reads signature from a stream
///
/// \param[in] stream Input stream
/// \param[out] sig Signature
///
/// \returns The stream \p stream
///
inline std::istream& operator >>(_In_ std::istream& stream, _Out_ Updater::signature &sig)
{
// Read signature (length prefixed).
unsigned __int32 count;
stream.read((char*)&count, sizeof(count));
if (!stream.good()) return stream;
// Read, and reverse signature byte order (to be OpenSSL compatible).
std::vector<unsigned char> sig_swap(count);
stream.read((char*)sig_swap.data(), sizeof(unsigned char)*count);
if (!stream.good()) return stream;
sig.resize(count);
for (unsigned __int32 i = 0, j = count - 1; i < count; i++, j--)
sig[i] = sig_swap[j];
return stream;
}
///
@ -29,12 +96,104 @@ int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_ HINSTANCE hPrevInstance, _In
// Inizialize wxWidgets.
wxInitializer initializer;
if (!initializer) {
_ftprintf(stderr, wxT("Failed to initialize the wxWidgets library, aborting.\n"));
if (!initializer)
return -1;
// Open data file.
std::ifstream dat(_T("..\\..\\output\\test.bin"), std::ios_base::in | std::ios_base::binary);
if (!dat.good()) {
wxFAIL_MSG(wxT("Error reading data file."));
return 1;
}
// Find the signature first.
if (stdex::idrec::find<Updater::recordid_t, Updater::recordsize_t, UPDATER_RECORD_ALIGN>(dat, Updater::signature_rec::id)) {
// Signature found. Remember file position.
Updater::recordsize_t
sig_pos = (Updater::recordsize_t)dat.tellg(), // Position to return to to read the signature.
sig_start = sig_pos - sizeof(Updater::recordid_t); // Beginning of the signature block
// Create cryptographic context.
HCRYPTPROV cp = NULL;
wxVERIFY(::CryptAcquireContext(&cp, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT));
// Hash the file up to signature block start.
HCRYPTHASH ch = NULL;
wxVERIFY(::CryptCreateHash(cp, CALG_SHA1, 0, 0, &ch));
{
dat.seekg(0);
std::vector<BYTE> buf(8192);
for (Updater::recordsize_t data_left = sig_start; dat.good() && data_left;) {
dat.read((char*)buf.data(), std::min<Updater::recordsize_t>(buf.size(), data_left));
Updater::recordsize_t count = dat.gcount();
wxVERIFY(::CryptHashData(ch, buf.data(), count, 0));
data_left -= count;
}
}
// Read the signature.
dat.seekg(sig_pos);
Updater::signature sig;
dat >> Updater::signature_rec(sig);
if (dat.good()) {
// Import public key.
HCRYPTKEY ck = NULL;
{
HRSRC res = ::FindResource(NULL, MAKEINTRESOURCE(IDR_KEY_PUBLIC), RT_RCDATA);
wxASSERT_MSG(res, wxT("public key not found"));
HGLOBAL res_handle = ::LoadResource(NULL, res);
wxASSERT_MSG(res_handle, wxT("loading resource failed"));
CERT_PUBLIC_KEY_INFO *keyinfo_data = NULL;
DWORD keyinfo_size = 0;
wxVERIFY(::CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, (const BYTE*)::LockResource(res_handle), ::SizeofResource(NULL, res), CRYPT_DECODE_ALLOC_FLAG, NULL, &keyinfo_data, &keyinfo_size));
BYTE *key_data = NULL;
DWORD key_size = 0;
wxVERIFY(::CryptDecodeObjectEx(X509_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, (const BYTE*)keyinfo_data->PublicKey.pbData, keyinfo_data->PublicKey.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &key_data, &key_size));
wxVERIFY(::CryptImportKey(cp, key_data, key_size, NULL, 0, &ck));
::LocalFree(key_data);
::LocalFree(keyinfo_data);
}
// We have the hash, we have the signature, we have the public key. Now verify.
if (::CryptVerifySignature(ch, (BYTE*)sig.data(), sig.size(), ck, NULL, 0)) {
// The signature is correct. Now parse the file.
dat.seekg(0);
if (stdex::idrec::find<Updater::recordid_t, Updater::recordsize_t, UPDATER_RECORD_ALIGN>(dat, UPDATER_DB_ID, sizeof(Updater::recordid_t))) {
Updater::recordsize_t size;
dat.read((char*)&size, sizeof(Updater::recordsize_t));
if (dat.good()) {
if (size > sig_start) {
// Limit the size up to the file signature. Should have not get here.
wxFAIL_MSG(wxT("Updater file wrong record size."));
size = sig_start;
}
if (stdex::idrec::find<Updater::recordid_t, Updater::recordsize_t, UPDATER_RECORD_ALIGN>(dat, Updater::package_info_rec::id, size)) {
// Read package information.
Updater::package_info pi;
dat >> Updater::package_info_rec(pi);
if (dat.good()) {
} else
wxFAIL_MSG(wxT("Error reading package information from Updater file."));
} else
wxFAIL_MSG(wxT("Updater file has no package information."));
} else
wxFAIL_MSG(wxT("Updater file read error."));
}
} else
wxFAIL_MSG(wxT("Updater file signature does not match file content."));
wxVERIFY(::CryptDestroyKey(ck));
} else
wxFAIL_MSG(wxT("Error reading signature from the Updater file."));
wxVERIFY(::CryptDestroyHash(ch));
wxVERIFY(::CryptReleaseContext(cp, 0));
} else
wxFAIL_MSG(wxT("Signature not found in the Updater file."));
return 0;
}

View File

@ -19,8 +19,13 @@
#pragma once
#include "UpdCheck.h"
#include "../include/Updater.h"
#include <wx/app.h>
#include <wxex/common.h>
#include <stdex/idrec.h>
#include <fstream>

25
UpdMkDesc/UpdMkDesc.h Normal file
View File

@ -0,0 +1,25 @@
/*
Copyright 2016 Amebis
This file is part of Updater.
Updater 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.
Updater 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 Updater. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define IDR_KEY_PRIVATE 1
#if !defined(RC_INVOKED) && !defined(MIDL_PASS)
#endif // !defined(RC_INVOKED) && !defined(MIDL_PASS)

Binary file not shown.

View File

@ -88,6 +88,7 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h" />
<ClInclude Include="UpdMkDesc.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\lib\wxExtend\build\wxExtend.vcxproj">

View File

@ -30,6 +30,9 @@
<ClInclude Include="stdafx.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="UpdMkDesc.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="UpdMkDesc.rc">

View File

@ -1,8 +1,8 @@
msgid ""
msgstr ""
"Project-Id-Version: UpdMkDesc\n"
"POT-Creation-Date: 2016-03-15 10:56+0100\n"
"PO-Revision-Date: 2016-03-15 10:57+0100\n"
"POT-Creation-Date: 2016-03-15 20:09+0100\n"
"PO-Revision-Date: 2016-03-15 20:10+0100\n"
"Last-Translator: Simon Rozman <simon.rozman@amebis.si>\n"
"Language-Team: Amebis, d. o. o., Kamnik <info@amebis.si>\n"
"Language: sl_SI\n"
@ -17,14 +17,29 @@ msgstr ""
"X-Poedit-KeywordsList: _\n"
"X-Poedit-SearchPath-0: .\n"
#: main.cpp:40
#: main.cpp:138
msgid "Show this help message"
msgstr "Pokaži to sporočilo pomoči"
#: main.cpp:41
msgid "input file"
msgstr "vhodna datoteka"
#: main.cpp:139
msgid "Hexadecimal version of the product"
msgstr "Verzija izdelka šesnajstiško"
#: main.cpp:42
#: main.cpp:140
msgid "String version of the product"
msgstr "Verzija izdelka opisno"
#: main.cpp:141
msgid "Package language"
msgstr "Jezik paketa"
#: main.cpp:142
msgid "Package download URL"
msgstr "URL za prenos paketa"
#: main.cpp:143
msgid "output file"
msgstr "izhodna datoteka"
#~ msgid "Package URL in the form of platform;language;URL"
#~ msgstr "Naslov URL paketa oblike platforma;jezik;URL"

View File

@ -18,6 +18,104 @@
*/
#include "stdafx.h"
#pragma comment(lib, "Crypt32.lib")
///
/// Writes package information to a stream
///
/// \param[in] stream Output stream
/// \param[in] pkg Package information
///
/// \returns The stream \p stream
///
inline std::ostream& operator <<(_In_ std::ostream& stream, _In_ const Updater::package_info &pkg)
{
// Write binary version.
if (stream.fail()) return stream;
stream.write((const char*)&pkg.ver, sizeof(pkg.ver));
// Write human readable description (length prefixed).
std::wstring::size_type char_count = pkg.desc.length();
#if defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__)
// 4G check
if (char_count > 0xffffffff) {
stream.setstate(std::ios_base::failbit);
return stream;
}
#endif
if (stream.fail()) return stream;
unsigned __int32 count = (unsigned __int32)char_count;
stream.write((const char*)&count, sizeof(count));
if (stream.fail()) return stream;
stream.write((const char*)pkg.desc.c_str(), sizeof(wchar_t)*count);
// Write package language (length prefixed)
char_count = pkg.lang.length();
#if defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__)
// 4G check
if (char_count > 0xffffffff) {
stream.setstate(std::ios_base::failbit);
return stream;
}
#endif
if (stream.fail()) return stream;
count = (unsigned __int32)char_count;
stream.write((const char*)&count, sizeof(count));
if (stream.fail()) return stream;
stream.write(pkg.lang.c_str(), sizeof(char)*count);
// Write package download URL (length prefixed)
char_count = pkg.url.length();
#if defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__)
// 4G check
if (char_count > 0xffffffff) {
stream.setstate(std::ios_base::failbit);
return stream;
}
#endif
if (stream.fail()) return stream;
count = (unsigned __int32)char_count;
stream.write((const char*)&count, sizeof(count));
if (stream.fail()) return stream;
stream.write(pkg.url.c_str(), sizeof(char)*count);
return stream;
}
///
/// Writes signature to a stream
///
/// \param[in] stream Output stream
/// \param[in] sig Signature
///
/// \returns The stream \p stream
///
inline std::ostream& operator <<(_In_ std::ostream& stream, _In_ const Updater::signature &sig)
{
// Write signature (length prefixed).
Updater::signature::size_type sig_count = sig.size();
#if defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__)
// 4G check
if (sig_count > 0xffffffff) {
stream.setstate(std::ios_base::failbit);
return stream;
}
#endif
if (stream.fail()) return stream;
unsigned __int32 count = (unsigned __int32)sig_count;
stream.write((const char*)&count, sizeof(count));
// Reverse signature byte order (to make OpenSSL compatible), and write.
if (stream.fail()) return stream;
std::vector<unsigned char> sig_swap(count);
for (unsigned __int32 i = 0, j = count - 1; i < count; i++, j--)
sig_swap[j] = sig[i];
stream.write((const char*)sig_swap.data(), sizeof(unsigned char)*count);
return stream;
}
///
@ -38,7 +136,10 @@ int _tmain(int argc, _TCHAR *argv[])
static const wxCmdLineEntryDesc cmdLineDesc[] =
{
{ wxCMD_LINE_SWITCH, "h" , "help" , _("Show this help message" ), wxCMD_LINE_VAL_NONE , wxCMD_LINE_OPTION_HELP },
{ wxCMD_LINE_SWITCH, "vh" , "ver-hex", _("Hexadecimal version of the product"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_OPTION_MANDATORY },
{ wxCMD_LINE_OPTION, "x" , "ver-hex", _("Hexadecimal version of the product"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_OPTION_MANDATORY },
{ wxCMD_LINE_OPTION, "s" , "ver-str", _("String version of the product" ), wxCMD_LINE_VAL_STRING, wxCMD_LINE_OPTION_MANDATORY },
{ wxCMD_LINE_OPTION, "l" , "lang" , _("Package language" ), wxCMD_LINE_VAL_STRING, wxCMD_LINE_OPTION_MANDATORY },
{ wxCMD_LINE_OPTION, "u" , "url" , _("Package download URL" ), wxCMD_LINE_VAL_STRING, wxCMD_LINE_OPTION_MANDATORY },
{ wxCMD_LINE_PARAM , NULL, NULL , _("output file" ), wxCMD_LINE_VAL_STRING, wxCMD_LINE_OPTION_MANDATORY },
{ wxCMD_LINE_NONE }
@ -58,5 +159,96 @@ int _tmain(int argc, _TCHAR *argv[])
return -1;
}
return 0;
const wxString& filenameOut = parser.GetParam(0);
std::fstream dst((LPCTSTR)filenameOut, std::ios_base::out | std::ios_base::in | std::ios_base::trunc | std::ios_base::binary);
if (dst.fail()) {
_ftprintf(stderr, wxT("%s: error UMD0001: Error opening output file.\n"), filenameOut.fn_str());
return 1;
}
bool has_errors = false;
// Open file ID.
std::streamoff dst_start = stdex::idrec::open<Updater::recordid_t, Updater::recordsize_t>(dst, UPDATER_DB_ID);
Updater::package_info pi;
wxString val;
pi.ver = parser.Found(wxT("x"), &val) ? _tcstoul(val, NULL, 16) : 0;
if (parser.Found(wxT("s"), &val))
pi.desc = val;
if (parser.Found(wxT("l"), &val))
pi.lang = val.ToAscii();
if (parser.Found(wxT("u"), &val))
pi.url = val.ToUTF8();
dst << Updater::package_info_rec(pi);
if (dst.fail()) {
_ftprintf(stderr, wxT("%s: error UMD0002: Writing to output file failed.\n"), filenameOut.fn_str());
has_errors = true;
}
stdex::idrec::close<Updater::recordid_t, Updater::recordsize_t, UPDATER_RECORD_ALIGN>(dst, dst_start);
// Create cryptographic context.
HCRYPTPROV cp = NULL;
wxVERIFY(::CryptAcquireContext(&cp, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT));
// Import private key.
{
HRSRC res = ::FindResource(NULL, MAKEINTRESOURCE(IDR_KEY_PRIVATE), RT_RCDATA);
wxASSERT_MSG(res, wxT("private key not found"));
HGLOBAL res_handle = ::LoadResource(NULL, res);
wxASSERT_MSG(res_handle, wxT("loading resource failed"));
PUBLICKEYSTRUC *key_data = NULL;
DWORD key_size = 0;
wxVERIFY(::CryptDecodeObjectEx(X509_ASN_ENCODING, PKCS_RSA_PRIVATE_KEY, (const BYTE*)::LockResource(res_handle), ::SizeofResource(NULL, res), CRYPT_DECODE_ALLOC_FLAG, NULL, &key_data, &key_size));
// See: http://pumka.net/2009/12/16/rsa-encryption-cplusplus-delphi-cryptoapi-php-openssl-2/comment-page-1/#comment-429
key_data->aiKeyAlg = CALG_RSA_SIGN;
HCRYPTKEY ck = NULL;
wxVERIFY(::CryptImportKey(cp, (const BYTE*)key_data, key_size, NULL, 0, &ck));
wxVERIFY(::CryptDestroyKey(ck));
::LocalFree(key_data);
}
// Hash the file.
HCRYPTHASH ch = NULL;
wxVERIFY(::CryptCreateHash(cp, CALG_SHA1, 0, 0, &ch));
{
std::vector<BYTE> buf(8192);
for (dst.seekg(0); !dst.eof();) {
dst.read((char*)buf.data(), buf.size());
wxVERIFY(::CryptHashData(ch, buf.data(), dst.gcount(), 0));
}
dst.clear();
}
// Sign the hash.
DWORD sig_size = 0;
if (!::CryptSignHash(ch, AT_SIGNATURE, NULL, 0, NULL, &sig_size)) {
_ftprintf(stderr, wxT("%s: error UMD0003: Signing output file failed (%i).\n"), filenameOut.fn_str(), ::GetLastError());
has_errors = true;
}
Updater::signature sig(sig_size);
if (!::CryptSignHash(ch, AT_SIGNATURE, NULL, 0, sig.data(), &sig_size)) {
_ftprintf(stderr, wxT("%s: error UMD0003: Signing output file failed (%i).\n"), filenameOut.fn_str(), ::GetLastError());
has_errors = true;
}
wxVERIFY(::CryptDestroyHash(ch));
wxVERIFY(::CryptReleaseContext(cp, 0));
// Append to the end of the file.
dst << Updater::signature_rec(sig);
if (has_errors) {
dst.close();
wxRemoveFile(filenameOut);
return 1;
} else
return 0;
}

View File

@ -19,9 +19,18 @@
#pragma once
#include "UpdMkDesc.h"
#include "../include/Updater.h"
#include <wx/app.h>
#include <wx/cmdline.h>
#include <wxex/common.h>
#include <stdex/idrec.h>
#include <algorithm>
#include <fstream>
#include <vector>
#include <tchar.h>

View File

@ -5,7 +5,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{ECE3F336-FFD2-41EE-AD8F-17BD7472BDCB}"
ProjectSection(SolutionItems) = preProject
include\version.h = include\version.h
include\Updater.h = include\Updater.h
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UpdMkDesc", "UpdMkDesc\UpdMkDesc.vcxproj", "{516AFFF6-F1E7-4806-B2E2-5CD9911ED2FB}"

96
include/Updater.h Normal file
View File

@ -0,0 +1,96 @@
/*
Copyright 2016 Amebis
This file is part of Updater.
Updater 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.
Updater 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 Updater. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
//
// Product version as a single DWORD
// Note: Used for version comparison within C/C++ code.
//
#define UPDATER_VERSION 0x01000000
//
// Product version by components
// Note: Resource Compiler has limited preprocessing capability,
// thus we need to specify major, minor and other version components
// separately.
//
#define UPDATER_VERSION_MAJ 1
#define UPDATER_VERSION_MIN 0
#define UPDATER_VERSION_REV 0
#define UPDATER_VERSION_BUILD 0
//
// Human readable product version and build year for UI
//
#define UPDATER_VERSION_STR "2.0-alpha"
#define UPDATER_BUILD_YEAR_STR "2016"
#if !defined(RC_INVOKED) && !defined(MIDL_PASS)
#include <stdex/idrec.h>
#include <string>
#include <vector>
///
/// Data records alignment
///
#define UPDATER_RECORD_ALIGN 1
///
/// Database IDs
///
#define UPDATER_DB_ID (*(Updater::recordid_t*)"UPD")
namespace Updater {
typedef unsigned __int32 recordid_t;
typedef unsigned __int32 recordsize_t;
///
/// Package information
///
struct package_info {
unsigned __int32 ver; ///< Binary Version
std::wstring desc; ///< Human Readable Description (i. e. "1.2")
std::string lang; ///< Package Language (ISO 639-1 language code followed by underscore and ISO 3166 country code)
std::string url; ///< Package download URL (UTF-8 encoded)
};
typedef stdex::idrec::record<package_info, recordid_t, recordsize_t, UPDATER_RECORD_ALIGN> package_info_rec;
///
/// Signature
///
typedef std::vector<unsigned char> signature;
typedef stdex::idrec::record<signature, recordid_t, recordsize_t, UPDATER_RECORD_ALIGN> signature_rec;
};
const Updater::recordid_t stdex::idrec::record<Updater::package_info, Updater::recordid_t, Updater::recordsize_t, UPDATER_RECORD_ALIGN>::id = *(Updater::recordid_t*)"PKG";
const Updater::recordid_t stdex::idrec::record<Updater::signature , Updater::recordid_t, Updater::recordsize_t, UPDATER_RECORD_ALIGN>::id = *(Updater::recordid_t*)"SGN";
#endif // !defined(RC_INVOKED) && !defined(MIDL_PASS)