#include "muscles.h"
#include "structs.h"
#include <algorithm>
#include <windows.h>
#include <Psapi.h>
#include <DbgHelp.h>
const char *get_folder_separator() {
return "\\";
}
const char *get_root_folder() {
return "C:\\";
}
const char *get_project_path() {
return ".";
}
bool is_folder(const char *name) {
WIN32_FIND_DATAA info = {0};
HANDLE h = FindFirstFileA(name, &info);
if (h == INVALID_HANDLE_VALUE)
return false;
bool folder = (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
FindClose(h);
return folder;
}
HANDLE open_file(LPCSTR name) {
return CreateFileA(
name,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr
);
}
std::pair<int, std::unique_ptr<u8[]>> read_file(std::string& path) {
std::pair<int, std::unique_ptr<u8[]>> buf = {0, nullptr};
HANDLE h = open_file(path.c_str());
if (!h)
return buf;
BY_HANDLE_FILE_INFORMATION info = {0};
GetFileInformationByHandle(h, &info);
buf.first = info.nFileSizeLow;
buf.second = std::make_unique<u8[]>(buf.first);
DWORD retrieved = 0;
ReadFile(h, buf.second.get(), buf.first, &retrieved, nullptr);
return buf;
}
Texture load_icon(const char *path);
void get_process_id_list(std::vector<s64>& list) {
const int max_pids = 1024;
auto pids = std::make_unique<DWORD[]>(max_pids);
DWORD size = 0;
EnumProcesses(pids.get(), max_pids * sizeof(DWORD), &size);
int n_pids = size / sizeof(DWORD);
list.resize(n_pids);
for (int i = 0; i < n_pids; i++)
list[i] = (s64)pids[i];
}
int get_process_names(std::vector<s64> *full_list, Map& icon_map, std::vector<void*>& icons, std::vector<void*>& pids, std::vector<void*>& names, int count_per_cell) {
const int proc_path_len = 1024;
auto proc_path = std::make_unique<char[]>(proc_path_len);
std::vector<s64> *list = full_list ? full_list : (std::vector<s64>*)&pids;
int size = list->size() < names.size() ? list->size() : names.size();
int idx = 0;
for (int i = 0; i < size; i++) {
// Many of the PIDs provided by EnumProcesses can't be opened, so we filter them out here
int pid = (*list)[i];
HANDLE proc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, pid);
if (!proc)
continue;
char *path = proc_path.get();
// gets us the full name of the current process
//int res = GetProcessImageFileNameA(proc, path, proc_path_len);
int len = proc_path_len;
int res = QueryFullProcessImageNameA(proc, 0, path, (PDWORD)&len);
CloseHandle(proc);
if (!res)
continue;
// The name in 'proc_path' is currently represented as a path to the file,
// so we find the last backslash and use the string from that point
char *p = strrchr(path, '\\');
if (!p)
p = path;
else
p++;
Bucket& buck = icon_map.insert(p);
if (buck.flags & FLAG_NEW) {
buck.pointer = load_icon(path);
buck.flags |= FLAG_EXTERNAL;
}
icons[idx] = buck.pointer;
s64 pid_long = (s64)pid;
pids[idx] = (void*)pid_long;
char *name = (char*)names[idx];
strncpy(name, p, count_per_cell-1);
name[strlen(p)] = 0;
idx++;
}
return idx;
}
Texture load_icon(const char *path) {
HICON icon = ExtractIconA(GetModuleHandle(nullptr), path, 0);
if (!icon)
return nullptr;
ICONINFO info = {0};
GetIconInfo(icon, &info);
HBITMAP copy = (HBITMAP)CopyImage(info.hbmColor, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
BITMAP bmp;
GetObject(copy, sizeof(BITMAP), &bmp);
if (bmp.bmBitsPixel != 32) {
DeleteObject(copy);
DestroyIcon(icon);
return nullptr;
}
int w = bmp.bmWidth, h = bmp.bmHeight;
u8 *buf = new u8[w*h*4];
for (int i = 0; i < h; i++) {
int in_idx = (h-i-1) * w * 4;
int out_idx = i * w * 4;
for (int j = 0; j < w; j++) {
u8 *p = &((u8*)bmp.bmBits)[in_idx];
buf[out_idx++] = p[3];
buf[out_idx++] = p[0];
buf[out_idx++] = p[1];
buf[out_idx++] = p[2];
in_idx += 4;
}
}
DeleteObject(copy);
DestroyIcon(icon);
Texture tex = sdl_create_texture((u32*)buf, w, h);
delete[] buf;
return tex;
}
void enumerate_files(char *path, std::vector<File_Entry*>& files, Arena& arena) {
int len = strlen(path);
auto path_buf = std::make_unique<char[]>(len + 4);
snprintf(path_buf.get(), len + 4, "%s\\*", path);
WIN32_FIND_DATAA info = {0};
HANDLE h = FindFirstFileA(path_buf.get(), &info);
auto it = files.begin();
bool increase = false;
int size = 0;
while (true) {
if (!strlen(info.cFileName) || !strcmp(info.cFileName, ".") || !strcmp(info.cFileName, "..")) {
if (!FindNextFileA(h, &info))
break;
continue;
}
if (it == files.end())
increase = true;
if (increase) {
files.push_back((File_Entry*)arena.allocate(sizeof(File_Entry)));
it = files.begin() + (files.size() - 1);
}
File_Entry *&file = *it;
if (!file)
file = (File_Entry*)arena.allocate(sizeof(File_Entry));
bool dir = (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
bool pm_read = true;
bool pm_write = (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0;
bool pm_exec = true;
file->flags = (dir << 3) | (pm_read << 2) | (pm_write << 1) | pm_exec;
file->path = path;
strncpy(file->name, info.cFileName, MAX_FNAME-1);
int end = MAX_FNAME-1;
len = strlen(info.cFileName);
file->name[end < len ? end : len] = 0;
size++;
++it;
if (!FindNextFileA(h, &info))
break;
}
FindClose(h);
files.resize(size);
if (size > 0) {
std::sort(files.begin(), files.end(), [](File_Entry *&a, File_Entry *&b){
if ((a->flags & 8) != 0 && (b->flags & 8) == 0)
return true;
if ((a->flags & 8) == 0 && (b->flags & 8) != 0)
return false;
return strcmp(a->name, b->name) < 0;
});
}
}
void refresh_file_region(Source& source) {
if (source.regions.size() <= 0)
source.regions.push_back({});
Region& reg = source.regions[0];
if (!source.handle)
source.handle = open_file((LPCSTR)source.identifier);
BY_HANDLE_FILE_INFORMATION info = {0};
GetFileInformationByHandle(source.handle, &info);
reg.size = ((u64)info.nFileSizeHigh << 32) | info.nFileSizeLow;
if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
reg.size = 0;
bool pm_read = true;
bool pm_write = (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0;
bool pm_exec = true;
reg.flags = (pm_read << REG_PM_READ) | (pm_write << REG_PM_WRITE) | (pm_exec << REG_PM_EXEC);
reg.base = 0;
reg.name = (char*)source.name.c_str();
}
void refresh_file_spans(Source& source, std::vector<Span>& input) {
if (!source.handle)
source.handle = open_file((LPCSTR)source.identifier);
LARGE_INTEGER offset;
for (auto& s : input) {
if (s.size <= 0)
continue;
offset.QuadPart = (LONGLONG)s.address;
SetFilePointerEx(source.handle, offset, nullptr, FILE_BEGIN);
DWORD retrieved = 0;
ReadFile(source.handle, &source.buffer[s.offset], s.size, &retrieved, nullptr);
auto error = GetLastError();
s.retrieved = retrieved;
}
}
u8 *header = nullptr;
#define IMAGE_HEADER_PAGE_SIZE 0x1000
#define PROCESS_BASE_NAME_SIZE 1000
#define PROCESS_NAME_LEN 100
bool find_section_names(HANDLE proc, u64 base, u8 *buf, Source& source) {
SIZE_T len = 0;
ReadProcessMemory(proc, (LPCVOID)base, (LPVOID)buf, IMAGE_HEADER_PAGE_SIZE, &len);
if (len != IMAGE_HEADER_PAGE_SIZE)
return false;
IMAGE_NT_HEADERS *headers = ImageNtHeader((LPVOID)buf);
if (!headers)
return false;
int n_sections = headers->FileHeader.NumberOfSections;
IMAGE_SECTION_HEADER *section = IMAGE_FIRST_SECTION(headers);
for (int i = 0; i < n_sections; i++) {
u64 addr = base + section[i].VirtualAddress;
int map_idx = source.address_to_region.get(reinterpret_cast<const char*>(addr), sizeof(u64));
if ((source.address_to_region.data[map_idx].flags & FLAG_OCCUPIED) == 0)
continue;
Region& r = source.regions[source.address_to_region.data[map_idx].value];
if (r.base == addr)
r.name = source.arena.alloc_string((char*)section[i].Name);
}
return n_sections > 0;
}
void refresh_process_regions(Source& source) {
auto& proc = (HANDLE&)source.handle;
if (!proc)
proc = OpenProcess(PROCESS_ALL_ACCESS, false, source.pid);
MEMORY_BASIC_INFORMATION info = {0};
VirtualQueryEx(proc, (LPCVOID)0, &info, sizeof(MEMORY_BASIC_INFORMATION));
u64 base = info.RegionSize;
source.address_to_region.erase_all();
source.regions.resize(0);
while (1) {
if (!VirtualQueryEx(proc, (LPCVOID)base, &info, sizeof(MEMORY_BASIC_INFORMATION)))
break;
int protect = info.Protect & 0xff;
bool acceptable = false;
if (info.State == 0x1000 && (info.Protect & PAGE_GUARD) == 0)
acceptable = (protect != PAGE_NOACCESS && protect != PAGE_EXECUTE);
// simply unacceptable behaviour
if (!acceptable) {
base += info.RegionSize;
continue;
}
u64 size = info.RegionSize;
bool pm_read = true;
bool pm_write =
protect != PAGE_EXECUTE_READ &&
protect != PAGE_READONLY;
bool pm_exec =
protect == PAGE_EXECUTE_READ ||
protect == PAGE_EXECUTE_READWRITE ||
protect == PAGE_EXECUTE_WRITECOPY;
u32 flags = (pm_read << REG_PM_READ) | (pm_write << REG_PM_WRITE) | (pm_exec << REG_PM_EXEC);
Bucket& b = source.address_to_region.insert(reinterpret_cast<const char*>(base), sizeof(u64));
b.value = source.regions.size();
source.regions.push_back({
.name = nullptr,
.base = base,
.size = size,
.flags = flags
});
base += info.RegionSize;
}
source.arena.rewind();
source.arena.set_rewind_point();
char name[PROCESS_BASE_NAME_SIZE];
if (!header)
header = new u8[IMAGE_HEADER_PAGE_SIZE];
int idx = 0;
for (int i = 0; i < source.regions.size(); i++) {
auto& reg = source.regions[i];
if (reg.size == IMAGE_HEADER_PAGE_SIZE)
find_section_names(proc, reg.base, header, source);
char *exe = nullptr;
int len = GetMappedFileNameA(proc, (LPVOID)reg.base, name, PROCESS_BASE_NAME_SIZE);
if (len > 0) {
char *str = strrchr(name, '\\');
exe = str ? str+1 : name;
}
if (exe || reg.name) {
char *full_name = (char*)source.arena.allocate(PROCESS_NAME_LEN);
snprintf(full_name, PROCESS_NAME_LEN-1, "%s%s%s",
exe ? exe : "",
reg.name ? " " : "",
reg.name ? reg.name : ""
);
reg.name = full_name;
}
}
}
void refresh_process_spans(Source& source, std::vector<Span>& input) {
auto& proc = (HANDLE&)source.handle;
if (!proc)
proc = OpenProcess(PROCESS_ALL_ACCESS, false, source.pid);
for (auto& s : input) {
SIZE_T retrieved = 0;
ReadProcessMemory(proc, (LPCVOID)s.address, (LPVOID)&source.buffer[s.offset], s.size, &retrieved);
s.retrieved = retrieved;
}
}
void close_source(Source& source) {
if (source.handle) {
CloseHandle(source.handle);
source.handle = nullptr;
}
}
SOURCE_HANDLE get_readonly_file_handle(void *identifier) {
return open_file((LPCSTR)identifier);
}
SOURCE_HANDLE get_readonly_process_handle(int pid) {
return OpenProcess(PROCESS_ALL_ACCESS, false, pid);
}
int read_page(SOURCE_HANDLE handle, SourceType type, u64 address, char *buf) {
int retrieved = 0;
if (type == SourceFile) {
LARGE_INTEGER offset;
offset.QuadPart = (LONGLONG)address;
SetFilePointerEx(handle, offset, nullptr, FILE_BEGIN);
DWORD r = 0;
ReadFile(handle, buf, PAGE_SIZE, &r, nullptr);
retrieved = (int)r;
}
else if (type == SourceProcess) {
SIZE_T r = 0;
ReadProcessMemory(handle, (LPCVOID)address, (LPVOID)buf, PAGE_SIZE, &r);
retrieved = (int)r;
}
return retrieved;
}
void wait_ms(int ms) {
Sleep(ms);
}
bool start_thread(void **thread_ptr, void *data, THREAD_RETURN_TYPE (*function)(void*)) {
*thread_ptr = (HANDLE)CreateThread(nullptr, 0, function, data, 0, nullptr);
return *thread_ptr == nullptr;
}