Skip to content
This repository has been archived by the owner on Nov 30, 2024. It is now read-only.

Commit

Permalink
GG's
Browse files Browse the repository at this point in the history
  • Loading branch information
StormAxs committed Oct 3, 2024
1 parent cee2906 commit 5dafcfd
Show file tree
Hide file tree
Showing 1,066 changed files with 338,521 additions and 22,851 deletions.
293 changes: 185 additions & 108 deletions src/android/android_main.cpp
Original file line number Diff line number Diff line change
@@ -1,159 +1,236 @@
#include <base/detect.h>

#ifdef CONF_PLATFORM_ANDROID
#include <sys/stat.h>
#include <unistd.h>
#include "android_main.h"

#include <SDL.h>

#include <base/hash.h>
#include <base/log.h>
#include <base/system.h>

#include <engine/shared/linereader.h>

#include <string>
#include <vector>

extern "C" __attribute__((visibility("default"))) void InitAndroid();

static int gs_AndroidStarted = false;

void InitAndroid()
static bool UnpackAsset(const char *pFilename)
{
if(gs_AndroidStarted)
char aAssetFilename[IO_MAX_PATH_LENGTH];
str_copy(aAssetFilename, "asset_integrity_files/");
str_append(aAssetFilename, pFilename);

// This uses SDL_RWFromFile because it can read Android assets,
// which are files stored in the app's APK file. All data files
// are stored as assets and unpacked to the external storage.
SDL_RWops *pAssetFile = SDL_RWFromFile(aAssetFilename, "rb");
if(!pAssetFile)
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "DDNet", "The app was started, but not closed properly, this causes bugs. Please restart or manually delete this task.", SDL_GL_GetCurrentWindow());
std::exit(0);
log_error("android", "Failed to open asset '%s' for reading", pFilename);
return false;
}

gs_AndroidStarted = true;
const long int FileLength = SDL_RWsize(pAssetFile);
if(FileLength < 0)
{
SDL_RWclose(pAssetFile);
log_error("android", "Failed to determine length of asset '%s'", pFilename);
return false;
}

// change current path to a writable directory
const char *pPath = SDL_AndroidGetExternalStoragePath();
chdir(pPath);
dbg_msg("client", "changed path to %s", pPath);
char *pData = static_cast<char *>(malloc(FileLength));
const size_t ReadLength = SDL_RWread(pAssetFile, pData, 1, FileLength);
SDL_RWclose(pAssetFile);

// copy integrity files
if(ReadLength != (size_t)FileLength)
{
SDL_RWops *pF = SDL_RWFromFile("asset_integrity_files/integrity.txt", "rb");
if(!pF)
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "DDNet", "integrity.txt not found, consider reinstalling", SDL_GL_GetCurrentWindow());
std::exit(0);
}
free(pData);
log_error("android", "Failed to read asset '%s' (read %" PRIzu ", wanted %ld)", pFilename, ReadLength, FileLength);
return false;
}

IOHANDLE TargetFile = io_open(pFilename, IOFLAG_WRITE);
if(!TargetFile)
{
free(pData);
log_error("android", "Failed to open '%s' for writing", pFilename);
return false;
}

long int length;
SDL_RWseek(pF, 0, RW_SEEK_END);
length = SDL_RWtell(pF);
SDL_RWseek(pF, 0, RW_SEEK_SET);
const size_t WriteLength = io_write(TargetFile, pData, FileLength);
io_close(TargetFile);
free(pData);

if(WriteLength != (size_t)FileLength)
{
log_error("android", "Failed to write data to '%s' (wrote %" PRIzu ", wanted %ld)", pFilename, WriteLength, FileLength);
return false;
}

char *pAl = (char *)malloc(length);
SDL_RWread(pF, pAl, 1, length);
return true;
}

SDL_RWclose(pF);
constexpr const char *INTEGRITY_INDEX = "integrity.txt";
constexpr const char *INTEGRITY_INDEX_SAVE = "integrity_save.txt";

mkdir("data", 0755);
// The first line of each integrity file contains the combined hash for all files,
// if the hashes match then we assume that the unpacked data folder is up-to-date.
static bool EqualIntegrityFiles(const char *pAssetFilename, const char *pStorageFilename)
{
IOHANDLE StorageFile = io_open(pStorageFilename, IOFLAG_READ);
if(!StorageFile)
{
return false;
}

dbg_msg("integrity", "copying integrity.txt with size: %ld", length);
char aStorageMainSha256[SHA256_MAXSTRSIZE];
const size_t StorageReadLength = io_read(StorageFile, aStorageMainSha256, sizeof(aStorageMainSha256) - 1);
io_close(StorageFile);
if(StorageReadLength != sizeof(aStorageMainSha256) - 1)
{
return false;
}
aStorageMainSha256[sizeof(aStorageMainSha256) - 1] = '\0';

IOHANDLE pIO = io_open("integrity.txt", IOFLAG_WRITE);
io_write(pIO, pAl, length);
io_close(pIO);
char aAssetFilename[IO_MAX_PATH_LENGTH];
str_copy(aAssetFilename, "asset_integrity_files/");
str_append(aAssetFilename, pAssetFilename);

free(pAl);
SDL_RWops *pAssetFile = SDL_RWFromFile(aAssetFilename, "rb");
if(!pAssetFile)
{
return false;
}

IOHANDLE pIO = io_open("integrity.txt", IOFLAG_READ);
CLineReader LineReader;
LineReader.Init(pIO);
const char *pReadLine = NULL;
std::vector<std::string> vLines;
while((pReadLine = LineReader.Get()))
char aAssetMainSha256[SHA256_MAXSTRSIZE];
const size_t AssetReadLength = SDL_RWread(pAssetFile, aAssetMainSha256, 1, sizeof(aAssetMainSha256) - 1);
SDL_RWclose(pAssetFile);
if(AssetReadLength != sizeof(aAssetMainSha256) - 1)
{
vLines.push_back(pReadLine);
return false;
}
io_close(pIO);
aAssetMainSha256[sizeof(aAssetMainSha256) - 1] = '\0';

return str_comp(aStorageMainSha256, aAssetMainSha256) == 0;
}

class CIntegrityFileLine
{
public:
char m_aFilename[IO_MAX_PATH_LENGTH];
SHA256_DIGEST m_Sha256;
};

// first line is the whole hash
std::string AllAsOne;
for(size_t i = 1; i < vLines.size(); ++i)
static std::vector<CIntegrityFileLine> ReadIntegrityFile(const char *pFilename)
{
CLineReader LineReader;
if(!LineReader.OpenFile(io_open(pFilename, IOFLAG_READ)))
{
AllAsOne.append(vLines[i]);
AllAsOne.append("\n");
return {};
}
SHA256_DIGEST ShaAll;
bool GotSHA = false;

std::vector<CIntegrityFileLine> vLines;
while(const char *pReadLine = LineReader.Get())
{
IOHANDLE pIOR = io_open("integrity_save.txt", IOFLAG_READ);
if(pIOR != NULL)
const char *pSpaceInLine = str_rchr(pReadLine, ' ');
CIntegrityFileLine Line;
char aSha256[SHA256_MAXSTRSIZE];
if(pSpaceInLine == nullptr)
{
CLineReader LineReader;
LineReader.Init(pIOR);
const char *pLine = LineReader.Get();
if(pLine != NULL)
if(!vLines.empty())
{
sha256_from_str(&ShaAll, pLine);
GotSHA = true;
// Only the first line is allowed to not contain a filename
log_error("android", "Failed to parse line %" PRIzu " of '%s': line does not contain space", vLines.size() + 1, pFilename);
return {};
}
Line.m_aFilename[0] = '\0';
str_copy(aSha256, pReadLine);
}
else
{
str_truncate(Line.m_aFilename, sizeof(Line.m_aFilename), pReadLine, pSpaceInLine - pReadLine);
str_copy(aSha256, pSpaceInLine + 1);
}
if(sha256_from_str(&Line.m_Sha256, aSha256) != 0)
{
log_error("android", "Failed to parse line %" PRIzu " of '%s': invalid SHA256 string", vLines.size() + 1, pFilename);
return {};
}
vLines.emplace_back(std::move(Line));
}

SHA256_DIGEST ShaAllFile;
sha256_from_str(&ShaAllFile, vLines[0].c_str());
return vLines;
}

// TODO: check files individually
if(!GotSHA || ShaAllFile != ShaAll)
const char *InitAndroid()
{
// Change current working directory to our external storage location
const char *pPath = SDL_AndroidGetExternalStoragePath();
if(pPath == nullptr)
{
// then the files
for(size_t i = 1; i < vLines.size(); ++i)
{
std::string FileName, Hash;
std::string::size_type n = 0;
std::string::size_type c = 0;
while((c = vLines[i].find(' ', n)) != std::string::npos)
n = c + 1;
FileName = vLines[i].substr(0, n - 1);
Hash = vLines[i].substr(n + 1);

std::string AssetFileName = std::string("asset_integrity_files/") + FileName;
SDL_RWops *pF = SDL_RWFromFile(AssetFileName.c_str(), "rb");

dbg_msg("Integrity", "Copying from assets: %s", FileName.c_str());

std::string FileNamePath = FileName;
std::string FileNamePathSub;
c = 0;
while((c = FileNamePath.find('/', c)) != std::string::npos)
{
FileNamePathSub = FileNamePath.substr(0, c);
fs_makedir(FileNamePathSub.c_str());
++c;
}
return "The external storage is not available.";
}
if(fs_chdir(pPath) != 0)
{
return "Failed to change current directory to external storage.";
}
log_info("android", "Changed current directory to '%s'", pPath);

if(fs_makedir("data") != 0 || fs_makedir("user") != 0)
{
return "Failed to create 'data' and 'user' directories in external storage.";
}

long int length;
SDL_RWseek(pF, 0, RW_SEEK_END);
length = SDL_RWtell(pF);
SDL_RWseek(pF, 0, RW_SEEK_SET);
if(EqualIntegrityFiles(INTEGRITY_INDEX, INTEGRITY_INDEX_SAVE))
{
return nullptr;
}

if(!UnpackAsset(INTEGRITY_INDEX))
{
return "Failed to unpack the integrity index file. Consider reinstalling the app.";
}

char *pAl = (char *)malloc(length);
SDL_RWread(pF, pAl, 1, length);
std::vector<CIntegrityFileLine> vIntegrityLines = ReadIntegrityFile(INTEGRITY_INDEX);
if(vIntegrityLines.empty())
{
return "Failed to load the integrity index file. Consider reinstalling the app.";
}

SDL_RWclose(pF);
std::vector<CIntegrityFileLine> vIntegritySaveLines = ReadIntegrityFile(INTEGRITY_INDEX_SAVE);

IOHANDLE pIO = io_open(FileName.c_str(), IOFLAG_WRITE);
io_write(pIO, pAl, length);
io_close(pIO);
// The remaining lines of each integrity file list all assets and their hashes
for(size_t i = 1; i < vIntegrityLines.size(); ++i)
{
const CIntegrityFileLine &IntegrityLine = vIntegrityLines[i];

free(pAl);
// Check if the asset is unchanged from the last unpacking
const auto IntegritySaveLine = std::find_if(vIntegritySaveLines.begin(), vIntegritySaveLines.end(), [&](const CIntegrityFileLine &Line) {
return str_comp(Line.m_aFilename, IntegrityLine.m_aFilename) == 0;
});
if(IntegritySaveLine != vIntegritySaveLines.end() && IntegritySaveLine->m_Sha256 == IntegrityLine.m_Sha256)
{
continue;
}

IOHANDLE pIOR = io_open("integrity_save.txt", IOFLAG_WRITE);
if(pIOR != NULL)
if(fs_makedir_rec_for(IntegrityLine.m_aFilename) != 0 || !UnpackAsset(IntegrityLine.m_aFilename))
{
char aFileSHA[SHA256_MAXSTRSIZE];
sha256_str(ShaAllFile, aFileSHA, sizeof(aFileSHA));
io_write(pIOR, aFileSHA, str_length(aFileSHA));
io_close(pIOR);
return "Failed to unpack game assets, consider reinstalling the app.";
}
}

// The integrity file will be unpacked every time when launching,
// so we can simply rename it to update the saved integrity file.
if((fs_is_file(INTEGRITY_INDEX_SAVE) && fs_remove(INTEGRITY_INDEX_SAVE) != 0) || fs_rename(INTEGRITY_INDEX, INTEGRITY_INDEX_SAVE) != 0)
{
return "Failed to update the saved integrity index file.";
}

return nullptr;
}

#endif
// See NativeMain.java
constexpr uint32_t COMMAND_USER = 0x8000;
constexpr uint32_t COMMAND_RESTART_APP = COMMAND_USER + 1;

void RestartAndroidApp()
{
SDL_AndroidSendMessage(COMMAND_RESTART_APP, 0);
}
31 changes: 31 additions & 0 deletions src/android/android_main.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef ANDROID_ANDROID_MAIN_H
#define ANDROID_ANDROID_MAIN_H

#include <base/detect.h>
#if !defined(CONF_PLATFORM_ANDROID)
#error "This header should only be included when compiling for Android"
#endif

/**
* Initializes the Android storage. Must be called on Android-systems
* before using any of the I/O and storage functions.
*
* This will change the current working directory to the app specific external
* storage location and unpack the assets from the APK file to the `data` folder.
* The folder `user` is created in the external storage to store the user data.
*
* Failure must be handled by exiting the app.
*
* @return `nullptr` on success, error message on failure.
*/
const char *InitAndroid();

/**
* Sends an intent to the Android system to restart the app.
*
* This will restart the main activity in a new task. The current process
* must immediately terminate after this function is called.
*/
void RestartAndroidApp();

#endif // ANDROID_ANDROID_MAIN_H
6 changes: 3 additions & 3 deletions src/antibot/antibot_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ struct CAntibotData

int64_t m_Now;
int64_t m_Freq;
void (*m_pfnKick)(int ClientID, const char *pMessage, void *pUser);
void (*m_pfnKick)(int ClientId, const char *pMessage, void *pUser);
void (*m_pfnLog)(const char *pMessage, void *pUser);
void (*m_pfnReport)(int ClientID, const char *pMessage, void *pUser);
void (*m_pfnSend)(int ClientID, const void *pData, int DataSize, int Flags, void *pUser);
void (*m_pfnReport)(int ClientId, const char *pMessage, void *pUser);
void (*m_pfnSend)(int ClientId, const void *pData, int DataSize, int Flags, void *pUser);
void (*m_pfnTeehistorian)(const void *pData, int DataSize, void *pUser);
void *m_pUser;
};
Expand Down
Loading

0 comments on commit 5dafcfd

Please sign in to comment.