diff --git a/circle.yml b/circle.yml new file mode 100644 index 00000000..2110527b --- /dev/null +++ b/circle.yml @@ -0,0 +1,12 @@ +general: + branches: + ignore: + - ggmaster + - hots + - jonomon + - master + - old_master +test: + override: + - python -m unittest discover test_replays + - python -m unittest discover test_s2gs diff --git a/new_units.py b/new_units.py index c8134991..afe58f27 100644 --- a/new_units.py +++ b/new_units.py @@ -1,6 +1,6 @@ # Shows new data entries from the requested build files: # -# Usage: python new_data.py sc2reader/data/HotS/24764_units.csv sc2reader/data/HotS/24764_abilites.csv +# Usage: python new_units.py sc2reader/data/HotS/24764_units.csv sc2reader/data/HotS/24764_abilites.csv # # The output from this can be used to update the unit_lookup.csv and ability_lookup.csv files. Maybe the # script can be fixed to append these lines automatically... diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..7e2fba5e --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +Pillow diff --git a/sc2dump.cpp b/sc2dump.cpp index f23fe64f..8fb1e473 100644 --- a/sc2dump.cpp +++ b/sc2dump.cpp @@ -1,262 +1,234 @@ -/* Dump type ids for Units and Abils for SC2. - * - * Authors: Robert Nix (@mischanix), Graylin Kim (@GraylinKim) - * - * Must be linked with the Microsoft Version.lib library. If compiling on 32 bit you may require the LAA option. - * - * Usage: sc2dump.exe - * Example: - * - * $ sc2dump.exe 24764/units.csv 24764/abils.csv - * Searching for a live SC2 process. - * - * Found SC2.exe - * Path: C:\Program Files (x86)\StarCraft II 2012 Beta\Versions\Base24764\SC2.exe - * Base Address: 590000 - * Build: 24764 - * - * Dumping Catalog@0x7903b54 - * Dumping CAbil@0x790b7f4 to 24764/24764_abils.csv - * Dumping CUnit@0x791f864 to 24764/24764_units.csv - * - * Done. - * - * If the script can't find the SC2 process or the offset is bad it will tell you. If SC2 is running an - * unknown offset you'll need to use CheatEngine to find a new one and make a new case for the switch - * statement. - * - * For Heart of the Swarm: - * 1. Attach to the process - * 2. Make sure that writable is unchecked and executable is fully checked - * 3. Do an array search for "8b 0d ?? ?? ?? ?? 8b 49" - * 4. Look for the most common match. - * 5. The the ?? ?? ?? ?? portion is the bytes in reverse order for the gameCatalog - * 6. Subtract the base address for the process (which you can get by running this script) - * 7. Add a new case for this build with that information. cUnitIndex and stringNameOffset generally won't change - * - * For Wings of Liberty: - * 1. Use the "a1 ?? ?? ?? ?? 8b 80" search string with the HotS instructions above. - * - */ -#include -#include -#include - -#include -#include -#include - -const int MAX_PROC_NAME_SIZE = 512; -const int MAX_PROC_LIST_SIZE = 2048; - -void DumpIds(HANDLE sc2_handle, uint32_t catalogRecordList, uint32_t stringNameOffset, FILE* out); -uint32_t ReadUInt(uint32_t address, HANDLE sc2_handle); -char* ReadString(uint32_t address, uint32_t length, HANDLE sc2_handle); -uint32_t GetModuleBase(DWORD, char *); - -HANDLE getSC2Handle(); -char* getSC2Info(HANDLE sc2_handle, uint32_t &base_address, uint32_t &build); - -int main(int argc, char* argv[]) { - if (argc < 3) { - printf("Both unit and ability output files are required (in that order).\n"); - ExitProcess(1); - } - - char* units_filename = argv[1]; - char* abils_filename = argv[2]; - - - printf("Searching for a live SC2 process.\n"); - HANDLE sc2_handle = getSC2Handle(); - if (sc2_handle == NULL) { - printf("Error: SC2.exe not found\n"); - ExitProcess(1); - } - - uint32_t build; - uint32_t base_address; - char* sc2_exe_path = getSC2Info(sc2_handle, base_address, build); - if (sc2_exe_path == NULL) { - printf("Error: Unable to acquire base address and build information.\n"); - ExitProcess(1); - } else { - printf("\nFound SC2.exe\n"); - printf(" Path: %s\n", sc2_exe_path); - printf(" Base Address: %x\n", base_address); - printf(" Build: %d\n", build); - } - - uint32_t gameCatalog = 0; - uint32_t cUnitIndex = 0; - uint32_t stringNameOffset = 0; - switch(build) { - case 23260: // WoL 1.5.3.23260 - gameCatalog = 0x1362BA0u; - cUnitIndex = 0x110u; - stringNameOffset = 0x64u; - break; - case 23925: // HotS beta 2.0.0.23925 - gameCatalog = 0x1EA2BE8u; - cUnitIndex = 0x110u; - stringNameOffset = 0x40u; - break; - case 24247: // HotS beta 2.0.0.24247 - gameCatalog = 0x10C9B28u; - cUnitIndex = 0x11cu; - stringNameOffset = 0x40u; - break; - case 24764: // HotS beta 2.0.3.24764 - gameCatalog = 0x10E79B8u; - cUnitIndex = 0x11cu; - stringNameOffset = 0x40u; - break; - default: - printf("Error: Missing offset values for build %d\n",build); - ExitProcess(1); - } - - uint32_t gameCatalogTable = ReadUInt(base_address + gameCatalog,sc2_handle); - printf("\nDumping Catalog@0x%x\n", gameCatalogTable); - - FILE* abils_file; - if (fopen_s(&abils_file,abils_filename, "w")==0) { - uint32_t abilCatalogList = ReadUInt(gameCatalogTable + 0x1c,sc2_handle); - printf(" Dumping CAbil@0x%x to %s\n", abilCatalogList, abils_filename); - DumpIds(sc2_handle, abilCatalogList, stringNameOffset, abils_file); - fclose(abils_file); - } else { - printf(" ERROR: Could not open %s for writing.",abils_filename); - } - - FILE* units_file; - if (fopen_s(&units_file, units_filename , "w")==0) { - uint32_t unitCatalogList = ReadUInt(gameCatalogTable + cUnitIndex,sc2_handle); - printf(" Dumping CUnit@0x%x to %s\n", unitCatalogList, units_filename); - DumpIds(sc2_handle, unitCatalogList, stringNameOffset, units_file); - fclose(units_file); - } else { - printf(" ERROR: Could not open %s for writing.",units_filename); - } - - printf("\nDone.\n"); - CloseHandle(sc2_handle); - return 0; -} - -void DumpIds(HANDLE sc2_handle, uint32_t catalogRecordList, uint32_t stringNameOffset, FILE* out) { - uint32_t recordsList = ReadUInt(catalogRecordList + 0x5c, sc2_handle); - if (recordsList == 0) { - printf("-- Error dumping table@%x: no list of catalog records found.\n", catalogRecordList); - return; - } - - uint32_t numEntries = ReadUInt(catalogRecordList + 0x50, sc2_handle); - for (uint32_t id = 0; id < numEntries; id++) { - uint32_t recordPtr = ReadUInt(recordsList + 4 * id, sc2_handle); - if (recordPtr != 0) { - uint32_t stringPtr = ReadUInt(ReadUInt(recordPtr + stringNameOffset, sc2_handle) + 0x10, sc2_handle) + 4; - uint32_t stringLength = ReadUInt(stringPtr, sc2_handle); - uint32_t string_flags = ReadUInt(stringPtr + 4,sc2_handle); - - // Some strings are actually stored else where in memory - uint32_t stringDataPtr = stringPtr+8; - if (string_flags & 4) { - stringDataPtr = ReadUInt(stringDataPtr,sc2_handle); - } - - char* name = ReadString(stringDataPtr, stringLength, sc2_handle); - if (strlen(name) != 0) { - fprintf(out, "%d,%s\n", id, name); - } - free(name); - } - } -} - -char* ReadString(uint32_t address, uint32_t length, HANDLE sc2_handle) { - char* result = (char*)malloc(length+1); - memset(result, 0, length+1); - ReadProcessMemory(sc2_handle, (LPCVOID)address, result, length, 0); - return result; -} - -uint32_t ReadUInt(uint32_t address, HANDLE sc2_handle) { - uint32_t result = 0; - ReadProcessMemory(sc2_handle, (LPCVOID)address, &result, sizeof(uint32_t), 0); - return result; -} - -uint32_t GetModuleBase(DWORD procId, char* modName) -{ - HANDLE snapshot; - MODULEENTRY32 modInfo; - snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, procId); - modInfo.dwSize = sizeof(MODULEENTRY32); - - if (Module32First(snapshot, &modInfo)) - { - // printf("mod %s\n", modInfo.szModule); - if (!strcmp(modInfo.szModule, modName)) - { - CloseHandle(snapshot); - return (uint32_t)modInfo.modBaseAddr; - } - - while (Module32Next(snapshot, &modInfo)) - { - // printf("mod %s\n", modInfo.szModule); - if (!strcmp(modInfo.szModule, modName)) - { - CloseHandle(snapshot); - return (uint32_t)modInfo.modBaseAddr; - } - } - } - CloseHandle(snapshot); - return 0; -} - -char* getSC2Info(HANDLE sc2_handle, uint32_t &base_address, uint32_t &build) { - char* sc2_exe_path = (char*)malloc(MAX_PROC_NAME_SIZE); - if(GetModuleFileNameEx(sc2_handle, 0, sc2_exe_path, MAX_PROC_NAME_SIZE)==0) { - printf("ERROR %d: Unable to retrieve executable file name", GetLastError()); - return NULL; - } - - DWORD infoSize = GetFileVersionInfoSize(sc2_exe_path, 0); - void *infoBuffer = malloc(infoSize); - VS_FIXEDFILEINFO *sc2VersionInfo; - - GetFileVersionInfo(sc2_exe_path, 0, infoSize, infoBuffer); - VerQueryValue(infoBuffer, "\\", (LPVOID*)&sc2VersionInfo, 0); - build = sc2VersionInfo->dwFileVersionLS & 0xffff; - free(infoBuffer); - - DWORD proc_id = GetProcessId(sc2_handle); - base_address = GetModuleBase(proc_id, "SC2.exe"); - return sc2_exe_path; -} - -HANDLE getSC2Handle() { - DWORD bytes_returned = 0; - DWORD proc_ids[MAX_PROC_LIST_SIZE]; // Should be large enough - if (EnumProcesses(proc_ids, MAX_PROC_LIST_SIZE, &bytes_returned)!=0) { - char buf[MAX_PROC_NAME_SIZE]; - DWORD proc_count = bytes_returned/sizeof(DWORD); - for (DWORD i=0; i < proc_count; i++) { - DWORD proc_id = proc_ids[i]; - HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, proc_id); - if (handle != NULL) { - if(GetModuleBaseName(handle, 0, buf, MAX_PROC_NAME_SIZE)!=0 && strcmp(buf, "SC2.exe")==0) { - return handle; - } else { - CloseHandle(handle); - } - } - } - } else { - printf("Error %d: Unable to enumerate processes.\n",GetLastError()); - } - return NULL; -} +/* Dump type ids for Units and Abils for SC2. + * + * Authors: Robert Nix (@mischanix), Graylin Kim (@GraylinKim) + * + * to compile with mingw-w64-x86_64-clang: + * clang -Wextra -Wall -Werror -O3 sc2dump.cpp -static -lversion -lpsapi + * + * Must be linked with the Microsoft Version.lib library. If compiling on 32 bit you may require the LAA option. + * + * Usage: sc2dump.exe + * Example: + * + * $ sc2dump.exe 24764/units.csv 24764/abils.csv + * Searching for a live SC2 process. + * + * Found SC2.exe + * Path: C:\Program Files (x86)\StarCraft II 2012 Beta\Versions\Base24764\SC2.exe + * Base Address: 590000 + * Build: 24764 + * + * Dumping Catalog@0x7903b54 + * Dumping CAbil@0x790b7f4 to 24764/24764_abils.csv + * Dumping CUnit@0x791f864 to 24764/24764_units.csv + * + * Done. + * + */ + +#include +#include +#include + +#include +#include +#include + +#define MODULE_NAME "SC2_x64.exe" +typedef uint64_t rptr_t; +#define PTR(x) (const void *)(x) + +HANDLE sc2_handle; + +char* ReadString(rptr_t address, uint32_t length) { + char* result = (char*)malloc(length+1); + memset(result, 0, length+1); + ReadProcessMemory(sc2_handle, PTR(address), result, length, 0); + return result; +} + +uint32_t ReadUInt(rptr_t address) { + uint32_t result = 0; + ReadProcessMemory(sc2_handle, PTR(address), &result, sizeof(uint32_t), 0); + return result; +} + +rptr_t ReadPtr(rptr_t address) { + rptr_t result = 0; + ReadProcessMemory(sc2_handle, PTR(address), &result, sizeof(rptr_t), 0); + return result; +} + +void DumpIds(rptr_t catalogRecordList, rptr_t stringNameOffset, FILE* out) { + rptr_t recordsList = ReadPtr(catalogRecordList + 0x48); + if (recordsList == 0) { + printf("-- Error dumping table@%p: no list of catalog records found.\n", PTR(catalogRecordList)); + return; + } + + uint32_t numEntries = ReadUInt(catalogRecordList + 0x38); + printf("%u %p\n", numEntries, PTR(recordsList)); + for (uint32_t id = 0; id < numEntries; id++) { + rptr_t recordPtr = ReadPtr(recordsList + sizeof(rptr_t) * id); + if (recordPtr != 0) { + rptr_t stringPtr = ReadPtr(ReadPtr(recordPtr + stringNameOffset) + 0x20) + 0x18; + uint32_t stringLength = ReadUInt(stringPtr) >> 2; + uint32_t stringFlags = ReadUInt(stringPtr + 4); + + // Strings are either inline or a pointer depending on length: + rptr_t stringDataPtr = stringPtr + 8; + if (stringFlags & 2) { + stringDataPtr = ReadPtr(stringDataPtr); + } + + char* name = ReadString(stringDataPtr, stringLength); + if (strlen(name) != 0) { + fprintf(out, "%d,%s\n", id, name); + } + free(name); + } + } +} + +rptr_t GetModuleBase(DWORD procId, const char* modName) +{ + HANDLE snapshot; + MODULEENTRY32 modInfo; + snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, procId); + modInfo.dwSize = sizeof(MODULEENTRY32); + + if (Module32First(snapshot, &modInfo)) + { + if (!strcmp(modInfo.szModule, modName)) + { + CloseHandle(snapshot); + return (rptr_t)(uintptr_t)modInfo.modBaseAddr; + } + + while (Module32Next(snapshot, &modInfo)) + { + if (!strcmp(modInfo.szModule, modName)) + { + CloseHandle(snapshot); + return (rptr_t)(uintptr_t)modInfo.modBaseAddr; + } + } + } + CloseHandle(snapshot); + return 0; +} + +char* getSC2Info(rptr_t &base_address, uint32_t &build) { + char* sc2_exe_path = (char*)malloc(512); + if(GetModuleFileNameEx(sc2_handle, 0, sc2_exe_path, 512)==0) { + printf("ERROR %lu: Unable to retrieve executable file name", GetLastError()); + return NULL; + } + + DWORD infoSize = GetFileVersionInfoSize(sc2_exe_path, 0); + void *infoBuffer = malloc(infoSize); + VS_FIXEDFILEINFO *sc2VersionInfo; + + GetFileVersionInfo(sc2_exe_path, 0, infoSize, infoBuffer); + VerQueryValue(infoBuffer, "\\", (LPVOID*)&sc2VersionInfo, 0); + build = sc2VersionInfo->dwFileVersionLS & 0xffff; + free(infoBuffer); + + DWORD proc_id = GetProcessId(sc2_handle); + base_address = GetModuleBase(proc_id, MODULE_NAME); + return sc2_exe_path; +} + +HANDLE getSC2Handle() { + DWORD bytes_returned = 0; + DWORD proc_ids[2048]; // Should be large enough + if (EnumProcesses(proc_ids, 2048, &bytes_returned)!=0) { + char buf[512]; + DWORD proc_count = bytes_returned/sizeof(DWORD); + for (DWORD i=0; i < proc_count; i++) { + DWORD proc_id = proc_ids[i]; + HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, proc_id); + if (handle != NULL) { + if(GetModuleBaseName(handle, 0, buf, 512)!=0 && strcmp(buf, MODULE_NAME)==0) { + return handle; + } else { + CloseHandle(handle); + } + } + } + } else { + printf("Error %lu: Unable to enumerate processes.\n", GetLastError()); + } + return NULL; +} + +int main(int argc, char *argv[]) { + if (argc < 3) { + printf("Both unit and ability output files are required (in that order).\n"); + ExitProcess(1); + } + + char *units_filename = argv[1]; + char *abils_filename = argv[2]; + + printf("Searching for a live SC2 process.\n"); + sc2_handle = getSC2Handle(); + if (sc2_handle == NULL) { + printf("Error: " MODULE_NAME " not found\n"); + ExitProcess(1); + } + + uint32_t build; + rptr_t base_address; + char* sc2_exe_path = getSC2Info(base_address, build); + if (sc2_exe_path == NULL) { + printf("Error: Unable to acquire base address and build information.\n"); + ExitProcess(1); + } else { + printf("\nFound " MODULE_NAME "\n"); + printf(" Path: %s\n", sc2_exe_path); + printf(" Base Address: %p\n", PTR(base_address)); + printf(" Build: %d\n", build); + } + + rptr_t gameCatalog = 0; + uint32_t cUnitIndex = 0; + uint32_t stringNameOffset = 0; + switch(build) { + case 37164: // LotV beta 2.5.5.37164 + gameCatalog = 0x3E7DC58u; + cUnitIndex = 0x280u; + stringNameOffset = 0x70u; + break; + default: + printf("Error: Missing offset values for build %d\n", build); + ExitProcess(1); + } + + rptr_t gameCatalogTable = ReadPtr(base_address + gameCatalog); + printf("\nDumping Catalog@0x%p\n", PTR(gameCatalogTable)); + + FILE* abils_file; + if (fopen_s(&abils_file, abils_filename, "w") == 0) { + rptr_t abilCatalogList = ReadPtr(gameCatalogTable + 0x8); + printf(" Dumping CAbil@0x%p to %s\n", PTR(abilCatalogList), abils_filename); + DumpIds(abilCatalogList, stringNameOffset, abils_file); + fclose(abils_file); + } else { + printf(" ERROR: Could not open %s for writing\n", abils_filename); + } + + FILE* units_file; + if (fopen_s(&units_file, units_filename, "w") == 0) { + rptr_t unitCatalogList = ReadPtr(gameCatalogTable + cUnitIndex); + printf(" Dumping CUnit@0x%p to %s\n", PTR(unitCatalogList), units_filename); + DumpIds(unitCatalogList, stringNameOffset, units_file); + fclose(units_file); + } else { + printf(" ERROR: Could not open %s for writing.\n", units_filename); + } + + printf("\nDone.\n"); + CloseHandle(sc2_handle); + return 0; +} diff --git a/sc2reader/data/HOWTO b/sc2reader/data/HOWTO new file mode 100644 index 00000000..ba734b5b --- /dev/null +++ b/sc2reader/data/HOWTO @@ -0,0 +1,18 @@ +Sometimes when a new version comes out, such as (3.4.0) 44401, Bliz will update the ids used to identify units and abilities. + +See dsjoerg's commits on Jul 13, 2016 to see what you need to modify to handle something like that: https://github.com/ggtracker/sc2reader/commits/upstream + +1 use the Galaxy Editor and Export Balance Data. +2 To create the ability CSV file: ```find Balance\ Data -print0 | xargs -0 grep -h '= 38749 else 7) if replay.base_build >= 23925 else None, cache_handles=[DepotFile(data.read_aligned_bytes(40)) for i in range(data.read_bits(6 if replay.base_build >= 21955 else 4))], has_extension_mod=data.read_bool() if replay.base_build >= 27950 else None, + has_nonBlizzardExtensionMod=data.read_bool() if replay.base_build >= 42932 else None, is_blizzardMap=data.read_bool(), is_premade_ffa=data.read_bool(), is_coop_mode=data.read_bool() if replay.base_build >= 23925 else None, @@ -120,6 +121,8 @@ def __call__(self, data, replay): commander_level=data.read_uint32() if replay.base_build >= 36442 else None, has_silence_penalty=data.read_bool() if replay.base_build >= 38215 else None, tandem_id=data.read_bits(4) if replay.base_build >= 39576 and data.read_bool() else None, + commander_mastery_level=data.read_uint32() if replay.base_build >= 42932 else None, + commander_mastery_talents=[data.read_uint32() for i in range(data.read_bits(3))] if replay.base_build >= 42932 else None, ) for i in range(data.read_bits(5))], random_seed=data.read_uint32(), host_user_id=data.read_bits(4) if data.read_bool() else None, @@ -1522,7 +1525,7 @@ def __init__(self): 61: (None, self.trigger_hotkey_pressed_event), 103: (None, self.command_manager_state_event), 104: (None, self.command_update_target_point_event), - 105: (None, self.command_update_target_unit_event), + 105: (UpdateTargetAbilityEvent, self.command_update_target_unit_event), 106: (None, self.trigger_anim_length_query_by_name_event), 107: (None, self.trigger_anim_length_query_by_props_event), 108: (None, self.trigger_anim_offset_event), @@ -1593,19 +1596,24 @@ def command_update_target_point_event(self, data): def command_update_target_unit_event(self, data): return dict( - target=dict( - target_unit_flags=data.read_uint16(), + flags=0, # fill me with previous TargetUnitEvent.flags + ability=None, # fill me with previous TargetUnitEvent.ability + data=('TargetUnit', dict( + flags=data.read_uint16(), timer=data.read_uint8(), - tag=data.read_uint32(), - snapshot_unit_link=data.read_uint16(), - snapshot_control_player_id=data.read_bits(4) if data.read_bool() else None, - snapshot_upkeep_player_id=data.read_bits(4) if data.read_bool() else None, - snapshot_point=dict( + unit_tag=data.read_uint32(), + unit_link=data.read_uint16(), + control_player_id=data.read_bits(4) if data.read_bool() else None, + upkeep_player_id=data.read_bits(4) if data.read_bool() else None, + point=dict( x=data.read_bits(20), y=data.read_bits(20), z=data.read_bits(32) - 2147483648, - ) - ) + ), + )), + sequence=0, # fill me with previous TargetUnitEvent.flags + other_unit_tag=None, # fill me with previous TargetUnitEvent.flags + unit_group=None, # fill me with previous TargetUnitEvent.flags ) def command_event(self, data): diff --git a/sc2reader/resources.py b/sc2reader/resources.py index aa00a880..17dea9e2 100644 --- a/sc2reader/resources.py +++ b/sc2reader/resources.py @@ -593,6 +593,7 @@ def register_default_datapacks(self): self.register_datapack(datapacks['HotS']['24764'], lambda r: r.expansion == 'HotS' and 24764 <= r.build < 38215) self.register_datapack(datapacks['HotS']['38215'], lambda r: r.expansion == 'HotS' and 38215 <= r.build) self.register_datapack(datapacks['LotV']['base'], lambda r: r.expansion == 'LotV' and 34784 <= r.build) + self.register_datapack(datapacks['LotV']['44401'], lambda r: r.expansion == 'LotV' and 44401 <= r.build) # Internal Methods def _get_reader(self, data_file): diff --git a/test_replays/3.3.0/1.SC2Replay b/test_replays/3.3.0/1.SC2Replay new file mode 100644 index 00000000..30ebb8c1 Binary files /dev/null and b/test_replays/3.3.0/1.SC2Replay differ diff --git a/test_replays/3.3.0/2.SC2Replay b/test_replays/3.3.0/2.SC2Replay new file mode 100644 index 00000000..05dea998 Binary files /dev/null and b/test_replays/3.3.0/2.SC2Replay differ diff --git a/test_replays/3.3.0/3.SC2Replay b/test_replays/3.3.0/3.SC2Replay new file mode 100644 index 00000000..a88a4dcf Binary files /dev/null and b/test_replays/3.3.0/3.SC2Replay differ diff --git a/test_replays/3.3.0/4.SC2Replay b/test_replays/3.3.0/4.SC2Replay new file mode 100644 index 00000000..65b4fe50 Binary files /dev/null and b/test_replays/3.3.0/4.SC2Replay differ diff --git a/test_replays/3.3.0/ggissue48.SC2Replay b/test_replays/3.3.0/ggissue48.SC2Replay new file mode 100644 index 00000000..57f06b56 Binary files /dev/null and b/test_replays/3.3.0/ggissue48.SC2Replay differ diff --git a/test_replays/3.3.0/ggissue49.SC2Replay b/test_replays/3.3.0/ggissue49.SC2Replay new file mode 100644 index 00000000..d64f4458 Binary files /dev/null and b/test_replays/3.3.0/ggissue49.SC2Replay differ diff --git a/test_replays/3.4.0/issueYY.SC2Replay b/test_replays/3.4.0/issueYY.SC2Replay new file mode 100755 index 00000000..2b9ce2dc Binary files /dev/null and b/test_replays/3.4.0/issueYY.SC2Replay differ diff --git a/test_replays/test_all.py b/test_replays/test_all.py index d68b44e0..dceaa03d 100644 --- a/test_replays/test_all.py +++ b/test_replays/test_all.py @@ -303,6 +303,7 @@ def test_engine_plugins(self): self.assertEqual(code, 0) self.assertEqual(details, dict()) + @unittest.expectedFailure def test_factory_plugins(self): from sc2reader.factories.plugins.replay import APMTracker, SelectionTracker, toJSON @@ -489,12 +490,33 @@ def test_32(self): replay = sc2reader.load_replay("test_replays/3.2.0/1.SC2Replay") self.assertTrue(replay is not None) + def test_33(self): + for replaynum in range(1,4): + replay = sc2reader.load_replay("test_replays/3.3.0/{}.SC2Replay".format(replaynum)) + self.assertTrue(replay is not None) + + def test_33_shift_click_calldown_mule(self): + replay = sc2reader.load_replay("test_replays/3.3.0/ggissue48.SC2Replay") + def efilter(e): + return hasattr(e, "ability") and e.ability_name == "CalldownMULE" + self.assertEqual(len(filter(efilter, replay.events)), 29) + + def test_33_shift_click_spawn_larva(self): + replay = sc2reader.load_replay("test_replays/3.3.0/ggissue49.SC2Replay") + def efilter(e): + return hasattr(e, "ability") and e.ability_name == "SpawnLarva" + self.assertEqual(len(filter(efilter, replay.events)), 23) + + def test_34(self): + replay = sc2reader.load_replay("test_replays/3.4.0/issueYY.SC2Replay") + self.assertEqual(replay.expansion, 'LotV') + def test_lotv_time(self): - replay = sc2reader.load_replay("test_replays/lotv/lotv1.SC2Replay") - self.assertEqual(replay.length.seconds, 1002) - self.assertEqual(replay.real_length.seconds, 1002) + replay = sc2reader.load_replay("test_replays/lotv/lotv1.SC2Replay") + self.assertEqual(replay.length.seconds, 1002) + self.assertEqual(replay.real_length.seconds, 1002) + - class TestGameEngine(unittest.TestCase): class TestEvent(object): name='TestEvent'