730 lines
19 KiB
C++
730 lines
19 KiB
C++
#include "stdafx.h"
|
|
#include "UI.h"
|
|
#include "UIScene_DLCOffersMenu.h"
|
|
#include "../../../Minecraft.World/StringHelpers.h"
|
|
#include <winhttp.h>
|
|
#pragma comment(lib, "winhttp.lib")
|
|
|
|
#pragma pack(push,1)
|
|
struct LCEZipLocalHeader
|
|
{
|
|
DWORD sig;
|
|
WORD verNeeded;
|
|
WORD flags;
|
|
WORD method;
|
|
WORD modTime;
|
|
WORD modDate;
|
|
DWORD crc32;
|
|
DWORD compSize;
|
|
DWORD uncompSize;
|
|
WORD fileNameLen;
|
|
WORD extraLen;
|
|
};
|
|
#pragma pack(pop)
|
|
|
|
struct LCEZipEntry
|
|
{
|
|
std::string fname;
|
|
WORD method;
|
|
DWORD compSize;
|
|
DWORD uncompSize;
|
|
const BYTE* fileData;
|
|
};
|
|
|
|
#include "miniz.h"
|
|
#include "miniz.c"
|
|
|
|
static const char* WORKSHOP_REGISTRY_URL = "https://network-server-7kuc.onrender.com/workshop/registry.json";
|
|
static const char* WORKSHOP_RAW_BASE = "https://network-server-7kuc.onrender.com/workshop";
|
|
static const char* COMMUNITY_REGISTRY_LOCAL = "http://localhost:7601/registry.json";
|
|
static const char* COMMUNITY_RAW_BASE_LOCAL = "http://localhost:7601";
|
|
static const char* COMMUNITY_REGISTRY_REMOTE = "https://raw.githubusercontent.com/LCE-Hub/LCE-Workshop/refs/heads/main/registry.json";
|
|
static const char* COMMUNITY_RAW_BASE_REMOTE = "https://github.com/LCE-Hub/LCE-Workshop/raw/refs/heads/main";
|
|
|
|
const char* UIScene_DLCOffersMenu::CategoryForIndex(int index)
|
|
{
|
|
switch(index)
|
|
{
|
|
case 1: return "Skin";
|
|
case 2: return "Texture";
|
|
case 3: return "Mashup";
|
|
case 4: return nullptr;
|
|
default: return nullptr;
|
|
}
|
|
}
|
|
|
|
bool UIScene_DLCOffersMenu::FetchURL(const std::string& url, std::vector<BYTE>& outData)
|
|
{
|
|
outData.clear();
|
|
|
|
bool isHttps = (url.size() >= 8 && _strnicmp(url.c_str(), "https://", 8) == 0);
|
|
const char* hostStart = url.c_str() + (isHttps ? 8 : 7);
|
|
const char* pathStart = strchr(hostStart, '/');
|
|
|
|
std::string host = pathStart ? std::string(hostStart, pathStart - hostStart) : std::string(hostStart);
|
|
std::string path = pathStart ? std::string(pathStart) : "/";
|
|
|
|
int port = isHttps ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT;
|
|
|
|
std::wstring wHost(host.begin(), host.end());
|
|
std::wstring wPath(path.begin(), path.end());
|
|
|
|
HINTERNET hSession = WinHttpOpen(L"LCEWorkshop/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
|
|
if(!hSession) return false;
|
|
|
|
HINTERNET hConnect = WinHttpConnect(hSession, wHost.c_str(), static_cast<INTERNET_PORT>(port), 0);
|
|
if(!hConnect) { WinHttpCloseHandle(hSession); return false; }
|
|
|
|
DWORD flags = isHttps ? WINHTTP_FLAG_SECURE : 0;
|
|
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"GET", wPath.c_str(), nullptr, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, flags);
|
|
if(!hRequest) { WinHttpCloseHandle(hConnect); WinHttpCloseHandle(hSession); return false; }
|
|
|
|
DWORD redirectPolicy = WINHTTP_OPTION_REDIRECT_POLICY_ALWAYS;
|
|
WinHttpSetOption(hRequest, WINHTTP_OPTION_REDIRECT_POLICY, &redirectPolicy, sizeof(redirectPolicy));
|
|
|
|
bool sent = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0) != FALSE;
|
|
if(sent) sent = WinHttpReceiveResponse(hRequest, nullptr) != FALSE;
|
|
|
|
if(!sent) { WinHttpCloseHandle(hRequest); WinHttpCloseHandle(hConnect); WinHttpCloseHandle(hSession); return false; }
|
|
|
|
DWORD statusCode = 0;
|
|
DWORD statusSize = sizeof(statusCode);
|
|
WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, &statusCode, &statusSize, WINHTTP_NO_HEADER_INDEX);
|
|
|
|
if(statusCode != 200) { WinHttpCloseHandle(hRequest); WinHttpCloseHandle(hConnect); WinHttpCloseHandle(hSession); return false; }
|
|
|
|
BYTE buf[65536];
|
|
DWORD dwRead = 0;
|
|
while(WinHttpReadData(hRequest, buf, sizeof(buf), &dwRead) && dwRead > 0)
|
|
outData.insert(outData.end(), buf, buf + dwRead);
|
|
|
|
WinHttpCloseHandle(hRequest);
|
|
WinHttpCloseHandle(hConnect);
|
|
WinHttpCloseHandle(hSession);
|
|
return !outData.empty();
|
|
}
|
|
|
|
bool UIScene_DLCOffersMenu::FetchURLString(const std::string& url, std::string& outStr)
|
|
{
|
|
std::vector<BYTE> data;
|
|
if(!FetchURL(url, data)) return false;
|
|
outStr.assign(reinterpret_cast<const char*>(data.data()), data.size());
|
|
return true;
|
|
}
|
|
|
|
static std::string GetGameDir()
|
|
{
|
|
char szPath[MAX_PATH] = {};
|
|
GetModuleFileNameA(nullptr, szPath, MAX_PATH);
|
|
char* pLast = strrchr(szPath, '\\');
|
|
if(pLast) *(pLast + 1) = '\0';
|
|
return std::string(szPath);
|
|
}
|
|
|
|
static void EnsureDirExists(const std::string& path)
|
|
{
|
|
std::string cur;
|
|
for(size_t i = 0; i < path.size(); i++)
|
|
{
|
|
cur += path[i];
|
|
if((path[i] == '\\' || path[i] == '/') && cur.size() > 3)
|
|
CreateDirectoryA(cur.c_str(), nullptr);
|
|
}
|
|
CreateDirectoryA(path.c_str(), nullptr);
|
|
}
|
|
|
|
static bool InflateBuffer(const BYTE* in, DWORD inSize, std::vector<BYTE>& out, DWORD uncompSize)
|
|
{
|
|
out.resize(uncompSize);
|
|
mz_stream strm = {};
|
|
strm.next_in = reinterpret_cast<const unsigned char*>(in);
|
|
strm.avail_in = static_cast<mz_uint32>(inSize);
|
|
strm.next_out = reinterpret_cast<unsigned char*>(out.data());
|
|
strm.avail_out = static_cast<mz_uint32>(uncompSize);
|
|
if(mz_inflateInit2(&strm, -15) != MZ_OK) return false;
|
|
int ret = mz_inflate(&strm, MZ_FINISH);
|
|
mz_inflateEnd(&strm);
|
|
out.resize(strm.total_out);
|
|
return ret == MZ_STREAM_END;
|
|
}
|
|
|
|
static std::vector<LCEZipEntry> CollectEntries(const std::vector<BYTE>& zipData)
|
|
{
|
|
std::vector<LCEZipEntry> entries;
|
|
const BYTE* base = zipData.data();
|
|
const BYTE* p = base;
|
|
const BYTE* end = base + zipData.size();
|
|
|
|
while(p + sizeof(LCEZipLocalHeader) <= end)
|
|
{
|
|
const LCEZipLocalHeader* hdr = reinterpret_cast<const LCEZipLocalHeader*>(p);
|
|
if(hdr->sig == 0x02014b50 || hdr->sig == 0x06054b50) break;
|
|
if(hdr->sig != 0x04034b50) break;
|
|
|
|
WORD flags = hdr->flags;
|
|
WORD method = hdr->method;
|
|
DWORD compSize = hdr->compSize;
|
|
DWORD uncompSize = hdr->uncompSize;
|
|
WORD fnLen = hdr->fileNameLen;
|
|
WORD exLen = hdr->extraLen;
|
|
|
|
p += sizeof(LCEZipLocalHeader);
|
|
if(p + fnLen + exLen > end) break;
|
|
|
|
LCEZipEntry e;
|
|
e.fname.assign(reinterpret_cast<const char*>(p), fnLen);
|
|
p += fnLen + exLen;
|
|
|
|
e.method = method;
|
|
e.compSize = compSize;
|
|
e.uncompSize = uncompSize;
|
|
e.fileData = p;
|
|
|
|
if(flags & 0x0008)
|
|
{
|
|
if(method == 0)
|
|
{
|
|
compSize = uncompSize;
|
|
e.compSize = compSize;
|
|
}
|
|
else
|
|
{
|
|
const BYTE* scan = p;
|
|
while(scan + 4 <= end)
|
|
{
|
|
DWORD sig = *reinterpret_cast<const DWORD*>(scan);
|
|
if(sig == 0x08074b50 || sig == 0x04034b50 || sig == 0x02014b50)
|
|
{
|
|
compSize = static_cast<DWORD>(scan - p);
|
|
e.compSize = compSize;
|
|
break;
|
|
}
|
|
++scan;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(p + e.compSize > end) break;
|
|
p += e.compSize;
|
|
|
|
if(flags & 0x0008)
|
|
{
|
|
if(p + 4 <= end && *reinterpret_cast<const DWORD*>(p) == 0x08074b50)
|
|
p += 16;
|
|
else if(p + 12 <= end)
|
|
p += 12;
|
|
}
|
|
|
|
for(size_t i = 0; i < e.fname.size(); i++)
|
|
if(e.fname[i] == '/') e.fname[i] = '\\';
|
|
|
|
if(!e.fname.empty())
|
|
entries.push_back(e);
|
|
}
|
|
return entries;
|
|
}
|
|
|
|
bool UIScene_DLCOffersMenu::InstallPack(const std::vector<BYTE>& zipData, const std::string& packName, bool isCommunity)
|
|
{
|
|
if(zipData.empty()) return false;
|
|
|
|
std::string outDir = isCommunity
|
|
? GetGameDir() + "Windows64Media\\DLC\\"
|
|
: GetGameDir() + "Windows64Media\\DLC\\" + packName + "\\";
|
|
EnsureDirExists(outDir);
|
|
|
|
std::vector<LCEZipEntry> entries = CollectEntries(zipData);
|
|
if(entries.empty()) return false;
|
|
|
|
int extracted = 0;
|
|
|
|
for(size_t i = 0; i < entries.size(); i++)
|
|
{
|
|
const LCEZipEntry& e = entries[i];
|
|
if(e.fname.empty() || e.fname[e.fname.size() - 1] == '\\') continue;
|
|
|
|
std::string outPath = outDir + e.fname;
|
|
|
|
size_t lastSlash = outPath.rfind('\\');
|
|
if(lastSlash != std::string::npos)
|
|
EnsureDirExists(outPath.substr(0, lastSlash + 1));
|
|
|
|
std::vector<BYTE> outBuf;
|
|
bool ok = false;
|
|
|
|
if(e.method == 0)
|
|
{
|
|
outBuf.assign(e.fileData, e.fileData + e.compSize);
|
|
ok = true;
|
|
}
|
|
else if(e.method == 8)
|
|
{
|
|
ok = InflateBuffer(e.fileData, e.compSize, outBuf, e.uncompSize);
|
|
}
|
|
|
|
if(ok && !outBuf.empty())
|
|
{
|
|
FILE* f = fopen(outPath.c_str(), "wb");
|
|
if(f)
|
|
{
|
|
fwrite(outBuf.data(), 1, outBuf.size(), f);
|
|
fclose(f);
|
|
extracted++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return extracted > 0;
|
|
}
|
|
|
|
bool UIScene_DLCOffersMenu::IsPackInstalled(const std::string& packName)
|
|
{
|
|
std::string dir = GetGameDir() + "Windows64Media\\DLC\\" + packName;
|
|
DWORD attr = GetFileAttributesA(dir.c_str());
|
|
return (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY));
|
|
}
|
|
|
|
static void SkipWhitespace(const char*& p)
|
|
{
|
|
while(*p && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) ++p;
|
|
}
|
|
|
|
static bool ReadString(const char*& p, std::string& out)
|
|
{
|
|
if(*p != '"') return false;
|
|
++p; out.clear();
|
|
while(*p && *p != '"')
|
|
{
|
|
if(*p == '\\') { ++p; if(*p) out += *p; }
|
|
else { out += *p; }
|
|
++p;
|
|
}
|
|
if(*p == '"') ++p;
|
|
return true;
|
|
}
|
|
|
|
static bool ReadWString(const char*& p, std::wstring& out)
|
|
{
|
|
std::string s;
|
|
if(!ReadString(p, s)) return false;
|
|
out.assign(s.begin(), s.end());
|
|
return true;
|
|
}
|
|
|
|
static void SkipValue(const char*& p)
|
|
{
|
|
SkipWhitespace(p);
|
|
if(*p == '"') { std::string d; ReadString(p, d); return; }
|
|
if(*p == '{' || *p == '[')
|
|
{
|
|
char open = *p, close = (*p == '{') ? '}' : ']';
|
|
int depth = 1; ++p;
|
|
while(*p && depth > 0)
|
|
{
|
|
if(*p == '"') { std::string d; ReadString(p, d); continue; }
|
|
if(*p == open) ++depth;
|
|
if(*p == close) --depth;
|
|
++p;
|
|
}
|
|
return;
|
|
}
|
|
while(*p && *p != ',' && *p != '}' && *p != ']') ++p;
|
|
}
|
|
|
|
bool UIScene_DLCOffersMenu::ParseRegistry(const std::string& json, const std::string& rawBase)
|
|
{
|
|
m_packs.clear();
|
|
const char* p = json.c_str();
|
|
|
|
const char* arrStart = strstr(p, "\"packages\"");
|
|
if(!arrStart) arrStart = strstr(p, "\"packs\"");
|
|
if(!arrStart) { arrStart = strchr(p, '['); if(!arrStart) return false; --arrStart; }
|
|
|
|
p = strchr(arrStart, '[');
|
|
if(!p) return false;
|
|
++p;
|
|
|
|
while(true)
|
|
{
|
|
SkipWhitespace(p);
|
|
if(*p == ']' || *p == '\0') break;
|
|
if(*p != '{') { ++p; continue; }
|
|
++p;
|
|
|
|
WorkshopPack pack;
|
|
std::string thumbFile;
|
|
|
|
while(true)
|
|
{
|
|
SkipWhitespace(p);
|
|
if(*p == '}' || *p == '\0') break;
|
|
if(*p == ',') { ++p; continue; }
|
|
if(*p != '"') { ++p; continue; }
|
|
|
|
std::string key;
|
|
ReadString(p, key);
|
|
SkipWhitespace(p);
|
|
if(*p == ':') ++p;
|
|
SkipWhitespace(p);
|
|
|
|
if(key == "id") ReadString(p, pack.id);
|
|
else if(key == "name") ReadWString(p, pack.name);
|
|
else if(key == "author") ReadWString(p, pack.author);
|
|
else if(key == "description") ReadWString(p, pack.description);
|
|
else if(key == "thumbnail") ReadString(p, thumbFile);
|
|
else if(key == "version") ReadString(p, pack.version);
|
|
else if(key == "category")
|
|
{
|
|
if(*p == '[') ++p;
|
|
while(true)
|
|
{
|
|
SkipWhitespace(p);
|
|
if(*p == ']' || *p == '\0') { if(*p == ']') ++p; break; }
|
|
if(*p == ',') { ++p; continue; }
|
|
if(*p == '"') { std::string cat; ReadString(p, cat); pack.categories.push_back(cat); }
|
|
else ++p;
|
|
}
|
|
}
|
|
else if(key == "zips")
|
|
{
|
|
if(*p == '{') ++p;
|
|
while(true)
|
|
{
|
|
SkipWhitespace(p);
|
|
if(*p == '}' || *p == '\0') { if(*p == '}') ++p; break; }
|
|
if(*p == ',') { ++p; continue; }
|
|
if(*p == '"')
|
|
{
|
|
WorkshopZip z;
|
|
ReadString(p, z.filename);
|
|
SkipWhitespace(p);
|
|
if(*p == ':') ++p;
|
|
SkipWhitespace(p);
|
|
ReadString(p, z.destToken);
|
|
pack.zips.push_back(z);
|
|
}
|
|
else ++p;
|
|
}
|
|
}
|
|
else SkipValue(p);
|
|
}
|
|
if(*p == '}') ++p;
|
|
|
|
bool allDLC = !pack.zips.empty();
|
|
for(int i = 0; i < (int)pack.zips.size(); i++)
|
|
if(pack.zips[i].destToken != "{DLCDir}") { allDLC = false; break; }
|
|
|
|
if(!allDLC) continue;
|
|
|
|
if(!pack.id.empty() && !thumbFile.empty())
|
|
pack.thumbnailUrl = rawBase + "/" + pack.id + "/" + thumbFile;
|
|
|
|
if(!pack.id.empty())
|
|
m_packs.push_back(pack);
|
|
}
|
|
|
|
return !m_packs.empty();
|
|
}
|
|
|
|
UIScene_DLCOffersMenu::UIScene_DLCOffersMenu(int iPad, void* initData, UILayer* parentLayer)
|
|
: UIScene(iPad, parentLayer)
|
|
, m_eFetchState(eFetch_Idle)
|
|
, m_bIsCommunity(false)
|
|
, m_iCurrentPack(0)
|
|
, m_iProductInfoIndex(0)
|
|
, m_bIsSD(false)
|
|
, m_bHasPurchased(false)
|
|
, m_bIsSelected(false)
|
|
{
|
|
DLCOffersParam* param = static_cast<DLCOffersParam*>(initData);
|
|
m_iProductInfoIndex = param ? param->iType : 0;
|
|
m_bIsCommunity = (m_iProductInfoIndex == 4);
|
|
|
|
initialiseMovie();
|
|
app.SetLiveLinkRequired(true);
|
|
|
|
m_bIsSD = !RenderManager.IsHiDef() && !RenderManager.IsWidescreen();
|
|
|
|
m_labelOffers.init(app.GetString(IDS_DOWNLOADABLE_CONTENT_OFFERS));
|
|
m_buttonListOffers.init(eControl_OffersList);
|
|
m_labelHTMLSellText.init(L" ");
|
|
m_labelPriceTag.init(L" ");
|
|
|
|
TelemetryManager->RecordMenuShown(m_iPad, eUIScene_DLCOffersMenu, 0);
|
|
|
|
if(m_loadedResolution == eSceneResolution_1080)
|
|
m_labelXboxStore.init(L"");
|
|
|
|
m_Timer.setVisible(true);
|
|
}
|
|
|
|
UIScene_DLCOffersMenu::~UIScene_DLCOffersMenu()
|
|
{
|
|
app.SetLiveLinkRequired(false);
|
|
}
|
|
|
|
void UIScene_DLCOffersMenu::tick()
|
|
{
|
|
UIScene::tick();
|
|
|
|
if(m_eFetchState != eFetch_Idle)
|
|
return;
|
|
|
|
m_eFetchState = eFetch_Error;
|
|
|
|
std::string jsonStr;
|
|
|
|
if(m_bIsCommunity)
|
|
{
|
|
if(FetchURLString(COMMUNITY_REGISTRY_LOCAL, jsonStr))
|
|
{
|
|
m_rawBase = COMMUNITY_RAW_BASE_LOCAL;
|
|
}
|
|
else
|
|
{
|
|
if(!FetchURLString(COMMUNITY_REGISTRY_REMOTE, jsonStr))
|
|
{
|
|
m_labelOffers.setLabel(app.GetString(IDS_NO_DLCCATEGORIES));
|
|
m_Timer.setVisible(false);
|
|
return;
|
|
}
|
|
m_rawBase = COMMUNITY_RAW_BASE_REMOTE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char urlWithBust[512];
|
|
snprintf(urlWithBust, sizeof(urlWithBust), "%s?_=%llu",
|
|
WORKSHOP_REGISTRY_URL,
|
|
static_cast<unsigned long long>(GetTickCount64()));
|
|
|
|
if(!FetchURLString(urlWithBust, jsonStr))
|
|
{
|
|
m_labelOffers.setLabel(app.GetString(IDS_NO_DLCCATEGORIES));
|
|
m_Timer.setVisible(false);
|
|
return;
|
|
}
|
|
m_rawBase = WORKSHOP_RAW_BASE;
|
|
}
|
|
|
|
if(!ParseRegistry(jsonStr, m_rawBase))
|
|
{
|
|
m_labelOffers.setLabel(app.GetString(IDS_NO_DLCCATEGORIES));
|
|
m_Timer.setVisible(false);
|
|
return;
|
|
}
|
|
|
|
m_filteredPacks.clear();
|
|
const char* wantCat = CategoryForIndex(m_iProductInfoIndex);
|
|
|
|
for(int i = 0; i < (int)m_packs.size(); i++)
|
|
{
|
|
WorkshopPack& pk = m_packs[i];
|
|
if(wantCat == nullptr)
|
|
{
|
|
m_filteredPacks.push_back(&pk);
|
|
}
|
|
else
|
|
{
|
|
for(int j = 0; j < (int)pk.categories.size(); j++)
|
|
{
|
|
if(pk.categories[j] == wantCat)
|
|
{
|
|
m_filteredPacks.push_back(&pk);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(m_filteredPacks.empty())
|
|
{
|
|
m_labelOffers.setLabel(app.GetString(IDS_NO_DLCCATEGORIES));
|
|
m_Timer.setVisible(false);
|
|
return;
|
|
}
|
|
|
|
m_eFetchState = eFetch_Ready;
|
|
PopulateList();
|
|
}
|
|
|
|
void UIScene_DLCOffersMenu::PopulateList()
|
|
{
|
|
m_buttonListOffers.clearList();
|
|
|
|
for(int i = 0; i < (int)m_filteredPacks.size(); i++)
|
|
m_buttonListOffers.addItem(m_filteredPacks[i]->name, false, i);
|
|
|
|
m_Timer.setVisible(false);
|
|
|
|
if(!m_filteredPacks.empty())
|
|
{
|
|
m_iCurrentPack = 0;
|
|
m_buttonListOffers.setFocus(true);
|
|
UpdateDetailPanel();
|
|
}
|
|
}
|
|
|
|
void UIScene_DLCOffersMenu::UpdateDetailPanel()
|
|
{
|
|
if(m_iCurrentPack < 0 || m_iCurrentPack >= (int)m_filteredPacks.size())
|
|
return;
|
|
|
|
WorkshopPack* pk = m_filteredPacks[m_iCurrentPack];
|
|
|
|
std::wstring desc = pk->description;
|
|
if(!pk->version.empty())
|
|
{
|
|
desc += L"\n\nVersion: ";
|
|
desc += std::wstring(pk->version.begin(), pk->version.end());
|
|
}
|
|
|
|
m_labelHTMLSellText.setLabel(desc.c_str());
|
|
|
|
std::string packName(pk->name.begin(), pk->name.end());
|
|
if(IsPackInstalled(packName))
|
|
m_labelPriceTag.setLabel(L"Installed");
|
|
else
|
|
m_labelPriceTag.setLabel(L"Free");
|
|
|
|
if(!pk->thumbnailLoaded && !pk->thumbnailUrl.empty())
|
|
{
|
|
std::vector<BYTE> imgData;
|
|
if(FetchURL(pk->thumbnailUrl, imgData) && !imgData.empty())
|
|
{
|
|
pk->thumbnailData = imgData;
|
|
pk->thumbnailLoaded = true;
|
|
}
|
|
}
|
|
|
|
if(pk->thumbnailLoaded && !pk->thumbnailData.empty())
|
|
{
|
|
std::wstring texName = L"ws_thumb_";
|
|
texName.append(pk->id.begin(), pk->id.end());
|
|
if(!hasRegisteredSubstitutionTexture(texName))
|
|
{
|
|
registerSubstitutionTexture(
|
|
texName,
|
|
const_cast<BYTE*>(pk->thumbnailData.data()),
|
|
static_cast<int>(pk->thumbnailData.size()),
|
|
false);
|
|
}
|
|
m_bitmapIconOfferImage.setTextureName(texName);
|
|
}
|
|
else
|
|
{
|
|
m_bitmapIconOfferImage.setTextureName(L"");
|
|
}
|
|
}
|
|
|
|
void UIScene_DLCOffersMenu::handlePress(F64 controlId, F64 childId)
|
|
{
|
|
if(static_cast<int>(controlId) != eControl_OffersList) return;
|
|
if(m_eFetchState != eFetch_Ready) return;
|
|
|
|
int iIndex = static_cast<int>(childId);
|
|
if(iIndex < 0 || iIndex >= (int)m_filteredPacks.size()) return;
|
|
|
|
WorkshopPack* pk = m_filteredPacks[iIndex];
|
|
std::string packName(pk->name.begin(), pk->name.end());
|
|
|
|
if(IsPackInstalled(packName))
|
|
{
|
|
m_buttonListOffers.showTick(iIndex, true);
|
|
return;
|
|
}
|
|
|
|
if(pk->zips.empty()) return;
|
|
|
|
m_Timer.setVisible(true);
|
|
|
|
const WorkshopZip& zip = pk->zips[0];
|
|
std::string zipUrl = m_rawBase + "/" + pk->id + "/" + zip.filename;
|
|
|
|
std::vector<BYTE> zipData;
|
|
if(!FetchURL(zipUrl, zipData) || zipData.empty())
|
|
{
|
|
m_Timer.setVisible(false);
|
|
return;
|
|
}
|
|
|
|
bool installed = InstallPack(zipData, packName, m_bIsCommunity);
|
|
m_Timer.setVisible(false);
|
|
|
|
if(installed)
|
|
{
|
|
m_buttonListOffers.showTick(iIndex, true);
|
|
m_labelPriceTag.setLabel(L"Installed");
|
|
}
|
|
}
|
|
|
|
void UIScene_DLCOffersMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool& handled)
|
|
{
|
|
ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released);
|
|
|
|
switch(key)
|
|
{
|
|
case ACTION_MENU_CANCEL:
|
|
if(pressed) navigateBack();
|
|
break;
|
|
case ACTION_MENU_OK:
|
|
#ifdef __ORBIS__
|
|
case ACTION_MENU_TOUCHPAD_PRESS:
|
|
#endif
|
|
sendInputToMovie(key, repeat, pressed, released);
|
|
break;
|
|
case ACTION_MENU_UP:
|
|
if(pressed && m_eFetchState == eFetch_Ready)
|
|
if(m_iCurrentPack > 0) { m_iCurrentPack--; UpdateDetailPanel(); }
|
|
sendInputToMovie(key, repeat, pressed, released);
|
|
break;
|
|
case ACTION_MENU_DOWN:
|
|
if(pressed && m_eFetchState == eFetch_Ready)
|
|
if(m_iCurrentPack < (int)m_filteredPacks.size() - 1) { m_iCurrentPack++; UpdateDetailPanel(); }
|
|
sendInputToMovie(key, repeat, pressed, released);
|
|
break;
|
|
case ACTION_MENU_LEFT:
|
|
case ACTION_MENU_RIGHT:
|
|
case ACTION_MENU_OTHER_STICK_DOWN:
|
|
case ACTION_MENU_OTHER_STICK_UP:
|
|
sendInputToMovie(key, repeat, pressed, released);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void UIScene_DLCOffersMenu::handleFocusChange(F64 controlId, F64 childId)
|
|
{
|
|
if(static_cast<int>(controlId) != eControl_OffersList) return;
|
|
if(m_eFetchState != eFetch_Ready) return;
|
|
|
|
int iIndex = static_cast<int>(childId);
|
|
if(iIndex >= 0 && iIndex < (int)m_filteredPacks.size())
|
|
{
|
|
m_iCurrentPack = iIndex;
|
|
m_bIsSelected = true;
|
|
m_bHasPurchased = false;
|
|
UpdateDetailPanel();
|
|
updateTooltips();
|
|
}
|
|
}
|
|
|
|
void UIScene_DLCOffersMenu::handleSelectionChanged(F64 selectedId) {}
|
|
void UIScene_DLCOffersMenu::handleTimerComplete(int id) {}
|
|
|
|
void UIScene_DLCOffersMenu::updateTooltips()
|
|
{
|
|
int iA = -1;
|
|
if(m_bIsSelected) iA = IDS_TOOLTIPS_INSTALL;
|
|
ui.SetTooltips(m_iPad, iA, IDS_TOOLTIPS_BACK);
|
|
}
|
|
|
|
wstring UIScene_DLCOffersMenu::getMoviePath()
|
|
{
|
|
return L"DLCOffersMenu";
|
|
}
|
|
|
|
int UIScene_DLCOffersMenu::ExitDLCOffersMenu(void* pParam, int iPad, C4JStorage::EMessageResult result)
|
|
{
|
|
ui.NavigateToHomeMenu();
|
|
return 0;
|
|
} |