Skip to content

Commit cba5eab

Browse files
committed
hooray graylin
2 parents 49e6dea + eabd74b commit cba5eab

32 files changed

+2412
-1059
lines changed

new_units.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Shows new data entries from the requested build files:
2+
#
3+
# Usage: python new_data.py sc2reader/data/HotS/24764_units.csv sc2reader/data/HotS/24764_abilites.csv
4+
#
5+
# The output from this can be used to update the unit_lookup.csv and ability_lookup.csv files. Maybe the
6+
# script can be fixed to append these lines automatically...
7+
#
8+
import pkgutil
9+
import sys
10+
11+
UNIT_LOOKUP = dict()
12+
for entry in pkgutil.get_data('sc2reader.data', 'unit_lookup.csv').split('\n'):
13+
if not entry: continue
14+
str_id, title = entry.strip().split(',')
15+
UNIT_LOOKUP[str_id] = title
16+
17+
with open(sys.argv[1],'r') as new_units:
18+
for line in new_units:
19+
new_unit_name = line.strip().split(',')[1]
20+
if new_unit_name not in UNIT_LOOKUP:
21+
print "{0},{1}".format(new_unit_name,new_unit_name)
22+
23+
print
24+
print
25+
26+
ABIL_LOOKUP = dict()
27+
for entry in pkgutil.get_data('sc2reader.data', 'ability_lookup.csv').split('\n'):
28+
if not entry: continue
29+
str_id, abilities = entry.split(',',1)
30+
ABIL_LOOKUP[str_id] = abilities.split(',')
31+
32+
with open(sys.argv[2], 'r') as new_abilities:
33+
for line in new_abilities:
34+
new_ability_name = line.strip().split(',')[1]
35+
if new_ability_name not in ABIL_LOOKUP:
36+
print "{0},{1}".format(new_ability_name,new_ability_name)

sc2dump.cpp

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
/* Dump type ids for Units and Abils for SC2.
2+
*
3+
* Authors: Robert Nix (@mischanix), Graylin Kim (@GraylinKim)
4+
*
5+
* Must be linked with the Microsoft Version.lib library. If compiling on 32 bit you may require the LAA option.
6+
*
7+
* Usage: sc2dump.exe <UNIT_DATA_OUTPUT> <ABIL_DATA_OUTPUT>
8+
* Example:
9+
*
10+
* $ sc2dump.exe 24764/units.csv 24764/abils.csv
11+
* Searching for a live SC2 process.
12+
*
13+
* Found SC2.exe
14+
* Path: C:\Program Files (x86)\StarCraft II 2012 Beta\Versions\Base24764\SC2.exe
15+
* Base Address: 590000
16+
* Build: 24764
17+
*
18+
* Dumping Catalog@0x7903b54
19+
* Dumping CAbil@0x790b7f4 to 24764/24764_abils.csv
20+
* Dumping CUnit@0x791f864 to 24764/24764_units.csv
21+
*
22+
* Done.
23+
*
24+
* If the script can't find the SC2 process or the offset is bad it will tell you. If SC2 is running an
25+
* unknown offset you'll need to use CheatEngine to find a new one and make a new case for the switch
26+
* statement.
27+
*
28+
* For Heart of the Swarm:
29+
* 1. Attach to the process
30+
* 2. Make sure that writable is unchecked and executable is fully checked
31+
* 3. Do an array search for "8b 0d ?? ?? ?? ?? 8b 49"
32+
* 4. Look for the most common match.
33+
* 5. The the ?? ?? ?? ?? portion is the bytes in reverse order for the gameCatalog
34+
* 6. Subtract the base address for the process (which you can get by running this script)
35+
* 7. Add a new case for this build with that information. cUnitIndex and stringNameOffset generally won't change
36+
*
37+
* For Wings of Liberty:
38+
* 1. Use the "a1 ?? ?? ?? ?? 8b 80" search string with the HotS instructions above.
39+
*
40+
*/
41+
#include <Windows.h>
42+
#include <psapi.h>
43+
#include <TlHelp32.h>
44+
45+
#include <stdint.h>
46+
#include <stdio.h>
47+
#include <string.h>
48+
49+
const int MAX_PROC_NAME_SIZE = 512;
50+
const int MAX_PROC_LIST_SIZE = 2048;
51+
52+
void DumpIds(HANDLE sc2_handle, uint32_t catalogRecordList, uint32_t stringNameOffset, FILE* out);
53+
uint32_t ReadUInt(uint32_t address, HANDLE sc2_handle);
54+
char* ReadString(uint32_t address, uint32_t length, HANDLE sc2_handle);
55+
uint32_t GetModuleBase(DWORD, char *);
56+
57+
HANDLE getSC2Handle();
58+
char* getSC2Info(HANDLE sc2_handle, uint32_t &base_address, uint32_t &build);
59+
60+
int main(int argc, char* argv[]) {
61+
if (argc < 3) {
62+
printf("Both unit and ability output files are required (in that order).\n");
63+
ExitProcess(1);
64+
}
65+
66+
char* units_filename = argv[1];
67+
char* abils_filename = argv[2];
68+
69+
70+
printf("Searching for a live SC2 process.\n");
71+
HANDLE sc2_handle = getSC2Handle();
72+
if (sc2_handle == NULL) {
73+
printf("Error: SC2.exe not found\n");
74+
ExitProcess(1);
75+
}
76+
77+
uint32_t build;
78+
uint32_t base_address;
79+
char* sc2_exe_path = getSC2Info(sc2_handle, base_address, build);
80+
if (sc2_exe_path == NULL) {
81+
printf("Error: Unable to acquire base address and build information.\n");
82+
ExitProcess(1);
83+
} else {
84+
printf("\nFound SC2.exe\n");
85+
printf(" Path: %s\n", sc2_exe_path);
86+
printf(" Base Address: %x\n", base_address);
87+
printf(" Build: %d\n", build);
88+
}
89+
90+
uint32_t gameCatalog = 0;
91+
uint32_t cUnitIndex = 0;
92+
uint32_t stringNameOffset = 0;
93+
switch(build) {
94+
case 23260: // WoL 1.5.3.23260
95+
gameCatalog = 0x1362BA0u;
96+
cUnitIndex = 0x110u;
97+
stringNameOffset = 0x64u;
98+
break;
99+
case 23925: // HotS beta 2.0.0.23925
100+
gameCatalog = 0x1EA2BE8u;
101+
cUnitIndex = 0x110u;
102+
stringNameOffset = 0x40u;
103+
break;
104+
case 24247: // HotS beta 2.0.0.24247
105+
gameCatalog = 0x10C9B28u;
106+
cUnitIndex = 0x11cu;
107+
stringNameOffset = 0x40u;
108+
break;
109+
case 24764: // HotS beta 2.0.3.24764
110+
gameCatalog = 0x10E79B8u;
111+
cUnitIndex = 0x11cu;
112+
stringNameOffset = 0x40u;
113+
break;
114+
default:
115+
printf("Error: Missing offset values for build %d\n",build);
116+
ExitProcess(1);
117+
}
118+
119+
uint32_t gameCatalogTable = ReadUInt(base_address + gameCatalog,sc2_handle);
120+
printf("\nDumping Catalog@0x%x\n", gameCatalogTable);
121+
122+
FILE* abils_file;
123+
if (fopen_s(&abils_file,abils_filename, "w")==0) {
124+
uint32_t abilCatalogList = ReadUInt(gameCatalogTable + 0x1c,sc2_handle);
125+
printf(" Dumping CAbil@0x%x to %s\n", abilCatalogList, abils_filename);
126+
DumpIds(sc2_handle, abilCatalogList, stringNameOffset, abils_file);
127+
fclose(abils_file);
128+
} else {
129+
printf(" ERROR: Could not open %s for writing.",abils_filename);
130+
}
131+
132+
FILE* units_file;
133+
if (fopen_s(&units_file, units_filename , "w")==0) {
134+
uint32_t unitCatalogList = ReadUInt(gameCatalogTable + cUnitIndex,sc2_handle);
135+
printf(" Dumping CUnit@0x%x to %s\n", unitCatalogList, units_filename);
136+
DumpIds(sc2_handle, unitCatalogList, stringNameOffset, units_file);
137+
fclose(units_file);
138+
} else {
139+
printf(" ERROR: Could not open %s for writing.",units_filename);
140+
}
141+
142+
printf("\nDone.\n");
143+
CloseHandle(sc2_handle);
144+
return 0;
145+
}
146+
147+
void DumpIds(HANDLE sc2_handle, uint32_t catalogRecordList, uint32_t stringNameOffset, FILE* out) {
148+
uint32_t recordsList = ReadUInt(catalogRecordList + 0x5c, sc2_handle);
149+
if (recordsList == 0) {
150+
printf("-- Error dumping table@%x: no list of catalog records found.\n", catalogRecordList);
151+
return;
152+
}
153+
154+
uint32_t numEntries = ReadUInt(catalogRecordList + 0x50, sc2_handle);
155+
for (uint32_t id = 0; id < numEntries; id++) {
156+
uint32_t recordPtr = ReadUInt(recordsList + 4 * id, sc2_handle);
157+
if (recordPtr != 0) {
158+
uint32_t stringPtr = ReadUInt(ReadUInt(recordPtr + stringNameOffset, sc2_handle) + 0x10, sc2_handle) + 4;
159+
uint32_t stringLength = ReadUInt(stringPtr, sc2_handle);
160+
uint32_t string_flags = ReadUInt(stringPtr + 4,sc2_handle);
161+
162+
// Some strings are actually stored else where in memory
163+
uint32_t stringDataPtr = stringPtr+8;
164+
if (string_flags & 4) {
165+
stringDataPtr = ReadUInt(stringDataPtr,sc2_handle);
166+
}
167+
168+
char* name = ReadString(stringDataPtr, stringLength, sc2_handle);
169+
if (strlen(name) != 0) {
170+
fprintf(out, "%d,%s\n", id, name);
171+
}
172+
free(name);
173+
}
174+
}
175+
}
176+
177+
char* ReadString(uint32_t address, uint32_t length, HANDLE sc2_handle) {
178+
char* result = (char*)malloc(length+1);
179+
memset(result, 0, length+1);
180+
ReadProcessMemory(sc2_handle, (LPCVOID)address, result, length, 0);
181+
return result;
182+
}
183+
184+
uint32_t ReadUInt(uint32_t address, HANDLE sc2_handle) {
185+
uint32_t result = 0;
186+
ReadProcessMemory(sc2_handle, (LPCVOID)address, &result, sizeof(uint32_t), 0);
187+
return result;
188+
}
189+
190+
uint32_t GetModuleBase(DWORD procId, char* modName)
191+
{
192+
HANDLE snapshot;
193+
MODULEENTRY32 modInfo;
194+
snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, procId);
195+
modInfo.dwSize = sizeof(MODULEENTRY32);
196+
197+
if (Module32First(snapshot, &modInfo))
198+
{
199+
// printf("mod %s\n", modInfo.szModule);
200+
if (!strcmp(modInfo.szModule, modName))
201+
{
202+
CloseHandle(snapshot);
203+
return (uint32_t)modInfo.modBaseAddr;
204+
}
205+
206+
while (Module32Next(snapshot, &modInfo))
207+
{
208+
// printf("mod %s\n", modInfo.szModule);
209+
if (!strcmp(modInfo.szModule, modName))
210+
{
211+
CloseHandle(snapshot);
212+
return (uint32_t)modInfo.modBaseAddr;
213+
}
214+
}
215+
}
216+
CloseHandle(snapshot);
217+
return 0;
218+
}
219+
220+
char* getSC2Info(HANDLE sc2_handle, uint32_t &base_address, uint32_t &build) {
221+
char* sc2_exe_path = (char*)malloc(MAX_PROC_NAME_SIZE);
222+
if(GetModuleFileNameEx(sc2_handle, 0, sc2_exe_path, MAX_PROC_NAME_SIZE)==0) {
223+
printf("ERROR %d: Unable to retrieve executable file name", GetLastError());
224+
return NULL;
225+
}
226+
227+
DWORD infoSize = GetFileVersionInfoSize(sc2_exe_path, 0);
228+
void *infoBuffer = malloc(infoSize);
229+
VS_FIXEDFILEINFO *sc2VersionInfo;
230+
231+
GetFileVersionInfo(sc2_exe_path, 0, infoSize, infoBuffer);
232+
VerQueryValue(infoBuffer, "\\", (LPVOID*)&sc2VersionInfo, 0);
233+
build = sc2VersionInfo->dwFileVersionLS & 0xffff;
234+
free(infoBuffer);
235+
236+
DWORD proc_id = GetProcessId(sc2_handle);
237+
base_address = GetModuleBase(proc_id, "SC2.exe");
238+
return sc2_exe_path;
239+
}
240+
241+
HANDLE getSC2Handle() {
242+
DWORD bytes_returned = 0;
243+
DWORD proc_ids[MAX_PROC_LIST_SIZE]; // Should be large enough
244+
if (EnumProcesses(proc_ids, MAX_PROC_LIST_SIZE, &bytes_returned)!=0) {
245+
char buf[MAX_PROC_NAME_SIZE];
246+
DWORD proc_count = bytes_returned/sizeof(DWORD);
247+
for (DWORD i=0; i < proc_count; i++) {
248+
DWORD proc_id = proc_ids[i];
249+
HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, proc_id);
250+
if (handle != NULL) {
251+
if(GetModuleBaseName(handle, 0, buf, MAX_PROC_NAME_SIZE)!=0 && strcmp(buf, "SC2.exe")==0) {
252+
return handle;
253+
} else {
254+
CloseHandle(handle);
255+
}
256+
}
257+
}
258+
} else {
259+
printf("Error %d: Unable to enumerate processes.\n",GetLastError());
260+
}
261+
return NULL;
262+
}

sc2reader/__init__.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
# -*- coding: utf-8 -*-
12
from __future__ import absolute_import
23

3-
import sys
4+
import sys, os
45

56
# import submodules
67
from sc2reader import plugins, data, scripts
7-
88
from sc2reader import factories, log_utils
99

1010
# setup the library logging
@@ -42,6 +42,16 @@ def useDictCache(cache_max_size=0, **options):
4242
def useDoubleCache(cache_dir, cache_max_size=0, **options):
4343
setFactory(factories.DoubleCachedSC2Factory(cache_dir, cache_max_size, **options))
4444

45-
setFactory(factories.SC2Factory())
4645

46+
# Allow environment variables to activate caching
47+
cache_dir = os.getenv('SC2READER_CACHE_DIR')
48+
cache_max_size = os.getenv('SC2READER_CACHE_MAX_SIZE')
49+
if cache_dir and cache_max_size:
50+
useDoubleCache(cache_dir, cache_max_size)
51+
elif cache_dir:
52+
useFileCache(cache_dir)
53+
elif cache_max_size:
54+
useDictCache(cache_max_size)
55+
else:
56+
setFactory(factories.SC2Factory())
4757

0 commit comments

Comments
 (0)