├── adpcm-decoder ├── CMakeLists.txt ├── README.md ├── adpcm_block.h ├── adpcm.h └── main.c ├── dump-xbox ├── Makefile ├── README.md ├── Makefile.oxdk ├── common │ ├── pe.h │ └── x86.h └── dump-xbox.c ├── controller-tool ├── README.md ├── ems-topgun-ii.json ├── standard.json ├── main.py └── steel-battalion.json ├── ss-parser ├── CMakeLists.txt └── main.c ├── .gitignore ├── README.md ├── python-scripts ├── README.md ├── inject_code.py ├── dump-eeprom.py ├── tiny-gp.inc ├── trace_ac97.py ├── dump-flash.py ├── dump-keys.py ├── trace_apu_mixbuf.py ├── get_av_settings.py ├── screenshot.py ├── dvd_info.py ├── dsp_homebrew.py ├── play_ac97.py ├── get_timers.py ├── inspect_apu_vp.py └── usb_gamepad.py └── communicator-tool └── main.py /adpcm-decoder/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(adpcm-decode main.c adpcm.h) 2 | -------------------------------------------------------------------------------- /adpcm-decoder/README.md: -------------------------------------------------------------------------------- 1 | Mostly stolen from https://wiki.multimedia.cx/index.php/IMA_ADPCM 2 | I wanted to use an existing decoder but most of them were pretty ugly 3 | 4 | Also see http://xboxdevwiki.net/Xbox_ADPCM 5 | -------------------------------------------------------------------------------- /dump-xbox/Makefile: -------------------------------------------------------------------------------- 1 | # Make sure that NXDK_DIR= and DEBUG= are set properly! 2 | 3 | XBE_TITLE=dump-xbox 4 | 5 | SRCS += dump-xbox.c # $(wildcard $(CURDIR)/*.c) 6 | 7 | NXDK_NET= 8 | include $(NXDK_DIR)/Makefile 9 | -------------------------------------------------------------------------------- /controller-tool/README.md: -------------------------------------------------------------------------------- 1 | A tool to load controller configuations to accept input from Xbox input devices. 2 | 3 | You might have to manually unload your current kernel driver. 4 | On Linux this is typically done using `sudo rmmod xpad`. 5 | -------------------------------------------------------------------------------- /ss-parser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7) 2 | 3 | find_package(OpenSSL REQUIRED) 4 | 5 | include_directories(${OPENSSL_INCLUDE_DIR}) 6 | add_executable(ss-parser main.c) 7 | target_link_libraries(ss-parser ${OPENSSL_LIBRARIES}) 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.bin 2 | *.BIN 3 | *.o 4 | *.exe 5 | *.xbe 6 | *.txt 7 | !CMakeLists.txt 8 | *.wav 9 | *.png 10 | bin/ 11 | i386-pc-xbox/ 12 | include/ 13 | lib/ 14 | build/ 15 | Build/ 16 | __pycache__/ 17 | *.pyc 18 | *.xtf 19 | *.ttf 20 | *.svg 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A collection of tools related to Xbox. 2 | 3 | Check the README in the respective project folders. 4 | 5 | *Some tools that used to be here, have [moved to dedicated XboxDev projects](https://github.com/XboxDev).* 6 | *More tools will be moved in the future.* 7 | -------------------------------------------------------------------------------- /python-scripts/README.md: -------------------------------------------------------------------------------- 1 | # Xbox Python-Scripts 2 | 3 | This is a collection of scripts to access various Xbox hardware. 4 | 5 | 6 | ## Getting Started 7 | 8 | [Install xboxpy](https://github.com/XboxDev/xboxpy#install) and read its usage documentation. 9 | 10 | Once you configured your xboxpy connection, you should be able to run these scripts. 11 | -------------------------------------------------------------------------------- /python-scripts/inject_code.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Injects code and runs it 4 | 5 | from xboxpy import * 6 | 7 | import sys 8 | 9 | f = open(sys.argv[1], 'rb') 10 | code = f.read() 11 | 12 | pointer = ke.MmAllocateContiguousMemory(len(code)) 13 | memory.write(pointer, code) 14 | api.call(pointer, bytes([])) 15 | ke.MmFreeContiguousMemory(pointer) 16 | -------------------------------------------------------------------------------- /dump-xbox/README.md: -------------------------------------------------------------------------------- 1 | # dump-xbox.c 2 | 3 | Compile using OpenXDK, put in writeable path on xbox HDD with a couple of free MB. 4 | Run and copy bin files back to your PC for analsis / use in xqemu 5 | 6 | ## Dumped files 7 | 8 | * flash.bin: Image of the Flash ROM lower 1MB 9 | * eeprom.bin: Image of the first 256 bytes of EEPROM 10 | * hdd_A-B.bin: Dump of sector number A to sector number B 11 | 12 | ## Dumping the MCPX / 2BL / RC4 Keys 13 | 14 | Dumping the secret southbridge ROM (including the RC4 Key) is not possible through pure software attacks (yet?). 15 | The 2BL is also only ever visible before it could be dumped in software. 16 | You can do it with hardware attacks only at the moment. 17 | -------------------------------------------------------------------------------- /python-scripts/dump-eeprom.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Dumps EEPROM image to eeprom.bin 4 | 5 | from xboxpy import * 6 | 7 | 8 | # Allocate a temporary ULONG 9 | tmp_ptr = ke.MmAllocateContiguousMemory(4) 10 | 11 | # Read EEPROM 12 | eeprom = bytes([]) 13 | for i in range(0, 256, 2): 14 | 15 | # Read 16-bit from EEPROM into a ULONG 16 | ret = ke.HalReadSMBusValue(0xA9, i, ke.TRUE, tmp_ptr) # 0xA9 = EEPROM Read 17 | assert(ret == 0) 18 | tmp = memory.read_u32(tmp_ptr) 19 | 20 | # Split the 16-bit word into 2 bytes 21 | assert(tmp & ~0xFFFF == 0) 22 | eeprom += bytes([tmp & 0xFF, (tmp >> 8) & 0xFF]) 23 | 24 | # Free our temporary buffer 25 | ke.MmFreeContiguousMemory(tmp_ptr) 26 | 27 | # Output EEPROM image to file 28 | with open("eeprom.bin", "wb") as f: 29 | f.write(eeprom) 30 | 31 | print("Wrote EEPROM to eeprom.bin") 32 | -------------------------------------------------------------------------------- /python-scripts/tiny-gp.inc: -------------------------------------------------------------------------------- 1 | ; Disable all interrupts 2 | 3 | movep #$FFF, x:$FFFFC5 ; Clear interrupts 4 | 5 | ; Register A0 will be used to count the number of frames 6 | 7 | move #0, a 8 | 9 | mainloop 10 | 11 | ; Keep a framecount in XMEM[0] 12 | move a0, x0 13 | move x0, x:$000000 14 | 15 | ; Load constant 1 and increment frame counter 16 | move #1, b0 17 | add b, a 18 | 19 | ; 0xFFFFC4 is the control register (in XMEM peripheral space) 20 | ; * (1 << 0) is the idle pin 21 | 22 | movep #1<<0, x:$FFFFC4 ; Set idle 23 | 24 | ; 0xFFFFC5 is the interrupt register (in XMEM peripheral space) 25 | ; * (1 << 1) is the start of frame interrupt 26 | 27 | wait_for_frame 28 | ; jclr operates on the n-th bit, it's not a mask! 29 | jclr #1, x:$FFFFC5, wait_for_frame ; Keep in tight loop until frame is hit 30 | movep #1<<1, x:$FFFFC5 ; Clear interrupts 31 | 32 | ; Normally you'd `rts` here and do whatever you want to do 33 | jmp mainloop 34 | -------------------------------------------------------------------------------- /python-scripts/trace_ac97.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Assuming DirectSound: 4 | # - We get 2 bytes per sample * 2 channels 5 | # - We get 1024 bytes at a time = 256 samples 6 | # - We run at 48000 Hz 7 | # 8 | # 256 / (480000 Hz) = 5.333 ms 9 | # 10 | # So at most you can spend 5.333 ms in the `callback` below. 11 | # However, consider network and API overhead. So try to spend time wisely. 12 | 13 | seconds = 10.0 14 | 15 | 16 | from xboxpy import * 17 | 18 | # Open outputs 19 | wav = aci.export_wav("pcm_trace.wav") 20 | try: 21 | import pyaudio 22 | pya = pyaudio.PyAudio() 23 | stream = pya.open(format=pya.get_format_from_width(width=2), channels=2, rate=48000, output=True) 24 | except ImportError: 25 | print("Could not find pyaudio, will only stream to disk.") 26 | stream = None 27 | 28 | # Define the handler which will do the output. 29 | def callback(duration, data): 30 | if stream != None: 31 | stream.write(data) 32 | wav.writeframes(data) 33 | return duration >= seconds 34 | 35 | # Start tracing 36 | aci.TraceAC97(callback) 37 | 38 | # Stop and close all outputs 39 | if stream != None: 40 | stream.stop_stream() 41 | stream.close() 42 | wav.close() 43 | -------------------------------------------------------------------------------- /python-scripts/dump-flash.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Dumps flash image to flash.bin 4 | 5 | from xboxpy import * 6 | 7 | 8 | # Assume that the flash is the maximum size (16 MiB) 9 | max_flash_size = 16*1024*1024 10 | 11 | # The last 512 byte of flash overlap the MCPX ROM. 12 | # However, if MCPX ROM is hidden, Xbox 1.1 and newer will crash on access. 13 | # So we only ever dump 8 MiB (16 MiB is unlikely anyway) 14 | max_flash_dump_size = max_flash_size // 2 15 | 16 | # Map flash into memory 17 | flash_ptr = ke.MmMapIoSpace(0xFF000000, max_flash_dump_size, ke.PAGE_READONLY | ke.PAGE_NOCACHE) 18 | 19 | flash = memory.read(flash_ptr, max_flash_dump_size) 20 | 21 | # Unmap flash 22 | ke.MmUnmapIoSpace(flash_ptr, max_flash_dump_size) 23 | 24 | # Try to find out the actual flash size 25 | while len(flash) > 1: 26 | 27 | # Split the flash so the highest address bit is ignored 28 | assert(len(flash) % 2 == 0) 29 | first_half = flash[0:len(flash)//2] 30 | second_half = flash[len(flash)//2:] 31 | 32 | # If the parts are different, then we must keep both 33 | if first_half != second_half: 34 | break 35 | 36 | # Only keep the first half 37 | flash = first_half 38 | 39 | # Report flash trimming 40 | print("Assuming flash size of %u bytes (or duplicated contents)" % len(flash)) 41 | 42 | # Warn about our limitations 43 | if len(flash) == max_flash_dump_size: 44 | print() 45 | print("The flash might be larger, this tool dumps at most %u bytes" % max_flash_dump_size) 46 | print() 47 | 48 | # Output flash image to file 49 | with open("flash.bin", "wb") as f: 50 | f.write(flash) 51 | 52 | print("Wrote flash to flash.bin") 53 | -------------------------------------------------------------------------------- /python-scripts/dump-keys.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Dumps kernel keys which are used to derive most other keys. 4 | # Also outputs to keys.bin (non human readable to avoid leaks). 5 | 6 | from xboxpy import * 7 | 8 | try: 9 | from cryptography.hazmat.backends import default_backend 10 | from cryptography.hazmat.primitives import hashes, hmac 11 | 12 | # Emulation of XcHMAC 13 | def HMAC(key, input1 = None, input2 = None): 14 | h = hmac.HMAC(key, hashes.SHA1(), backend=default_backend()) 15 | if input1 is not None: 16 | h.update(input1) 17 | if input2 is not None: 18 | h.update(input2) 19 | return h.finalize() 20 | except: 21 | #FIXME: Check if Xbox can do calls 22 | # (Optional, to not exclude some xboxpy backends) 23 | 24 | print("Warning: Unable to find pyca/cryptography! Key correctness will not be verified.") 25 | pass 26 | 27 | 28 | def get_XboxEEPROMKey(): 29 | return memory.read(ke.XboxEEPROMKey(), 16) 30 | 31 | def get_XboxCERTKey(): 32 | 33 | # XboxCERTKey is just infront of XboxHDKey 34 | cert_key = memory.read(ke.XboxHDKey() - 16, 16) 35 | 36 | # As we guessed the key, we'll try to verify it with known derived keys 37 | if 'HMAC' in globals(): 38 | cert_address = memory.read_u32(0x10000 + 0x118) 39 | cert_lan_key = memory.read(cert_address + 0xB0, 16) 40 | derived_lan_key = HMAC(cert_key, cert_lan_key)[0:16] 41 | expected_lan_key = memory.read(ke.XboxLANKey(), 16) 42 | assert(derived_lan_key == expected_lan_key) 43 | 44 | return cert_key 45 | 46 | print() 47 | print("These keys are protected by law.") 48 | print("Do not redistribute them.") 49 | print() 50 | 51 | XboxEEPROMKey = get_XboxEEPROMKey() 52 | print("EEPROM key: %s" % XboxEEPROMKey.hex().upper()) 53 | 54 | XboxCERTKey = get_XboxCERTKey() 55 | print("CERT key: %s" % XboxCERTKey.hex().upper()) 56 | 57 | print() 58 | 59 | with open('keys.bin', 'wb') as f: 60 | f.write(XboxEEPROMKey) 61 | f.write(XboxCERTKey) 62 | 63 | print("Wrote keys to keys.bin") 64 | -------------------------------------------------------------------------------- /adpcm-decoder/adpcm_block.h: -------------------------------------------------------------------------------- 1 | #include "adpcm.h" 2 | 3 | static int16_t adpcm_decode_block_setup(ADPCMDecoder* decoder, uint32_t word) { 4 | int16_t predictor = word & 0xFFFF; 5 | uint8_t step_index = (word >> 16) & 0xFF; 6 | adpcm_decoder_initialize(decoder, predictor, step_index); 7 | return predictor; 8 | } 9 | 10 | static int16_t* adpcm_decode_word(ADPCMDecoder* decoder, int16_t* samples, uint32_t word, int first, int last) { 11 | for(int i = 0; i < 8; i++) { 12 | if (i >= first) { 13 | samples++; 14 | } 15 | if (i <= last) { 16 | *samples = adpcm_decoder_step(decoder, word); 17 | word >>= 4; 18 | } 19 | } 20 | return samples; 21 | } 22 | 23 | // For stereo we decode 2x 32 bit each iteration (as 32 bits). 24 | static void adpcm_decode_stereo_block(int16_t* samples_l, int16_t* samples_r, const uint8_t* data, unsigned int first, unsigned int last) { 25 | uint32_t* word = (uint32_t*)data; 26 | ADPCMDecoder decoder_l; 27 | ADPCMDecoder decoder_r; 28 | *samples_l = adpcm_decode_block_setup(&decoder_l, *word++); 29 | *samples_r = adpcm_decode_block_setup(&decoder_r, *word++); 30 | for(unsigned int i = 0; i < 8; i++) { 31 | for(unsigned int j = 0; j < 2; j++) { 32 | if (j == 0) { 33 | samples_l = adpcm_decode_word(&decoder_l, samples_l, *word++, first, last); 34 | } else { 35 | samples_r = adpcm_decode_word(&decoder_r, samples_r, *word++, first, last); 36 | } 37 | } 38 | first -= 8; 39 | last -= 8; 40 | } 41 | } 42 | 43 | // For mono we decode 32 bit at once in each iteration. 44 | // We could do 64 bits here, but if we parallelize this algorithm (later) we 45 | // would limit ourselves to 64 bit operands. However, most of ADPCM is 16 to 46 | // 32 bits (for overflows). So we stick with 32 bit and should even consider 47 | // going back to 16 bit (if enough decoders run at once)! 48 | static void adpcm_decode_mono_block(int16_t* samples, const uint8_t* data, unsigned int first, unsigned int last) { 49 | uint32_t* word = (uint32_t*)data; 50 | ADPCMDecoder decoder; 51 | *samples = adpcm_decode_block_setup(&decoder, *word++); 52 | for(unsigned int i = 0; i < 8; i++) { 53 | samples = adpcm_decode_word(&decoder, samples, *word++, first, last); 54 | first -= 8; 55 | last -= 8; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /adpcm-decoder/adpcm.h: -------------------------------------------------------------------------------- 1 | // See https://wiki.multimedia.cx/index.php/IMA_ADPCM for more information 2 | 3 | #include 4 | #include 5 | 6 | static int8_t ima_index_table[16] = { 7 | -1, -1, -1, -1, 2, 4, 6, 8, 8 | -1, -1, -1, -1, 2, 4, 6, 8 9 | }; 10 | 11 | static uint16_t ima_step_table[89] = { 12 | 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 13 | 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 14 | 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 15 | 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 16 | 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 17 | 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 18 | 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 19 | 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 20 | 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 21 | }; 22 | 23 | typedef struct { 24 | int32_t predictor; 25 | int8_t step_index; 26 | uint16_t step; 27 | } ADPCMDecoder; 28 | 29 | static void adpcm_decoder_initialize(ADPCMDecoder* d, int16_t predictor, int8_t step_index) { 30 | d->predictor = predictor; 31 | d->step_index = step_index; 32 | } 33 | 34 | // The upper portion of the `nibble` argument is ignored. 35 | static int16_t adpcm_decoder_step(ADPCMDecoder* d, uint8_t nibble) { 36 | 37 | // Get step and prepare index for next sample 38 | if (d->step_index < 0) { 39 | d->step_index = 0; 40 | } else if (d->step_index > 88) { 41 | d->step_index = 88; 42 | } 43 | d->step = ima_step_table[d->step_index]; 44 | d->step_index += ima_index_table[nibble & 0xF]; 45 | 46 | // Calculate diff 47 | int32_t diff = d->step >> 3; 48 | if (nibble & 1) { 49 | diff += d->step >> 2; 50 | } 51 | if (nibble & 2) { 52 | diff += d->step >> 1; 53 | } 54 | if (nibble & 4) { 55 | diff += d->step; 56 | } 57 | if (nibble & 8) { 58 | diff = -diff; 59 | } 60 | 61 | // Update predictor and clamp to signed 16 bit 62 | d->predictor += diff; 63 | if (d->predictor < -0x8000) { 64 | d->predictor = -0x8000; 65 | } else if (d->predictor > 0x7FFF) { 66 | d->predictor = 0x7FFF; 67 | } 68 | 69 | return d->predictor; 70 | } 71 | -------------------------------------------------------------------------------- /python-scripts/trace_apu_mixbuf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Fixed in hardware: 4 | # - We get 3 bytes per sample (packed into 4 bytes) * 1 channel 5 | # - We get 128 bytes at a time = 32 samples 6 | # - We run at 48000 Hz 7 | # 8 | # 32 / (480000 Hz) = 0.666 ms 9 | # 10 | # So at most you can spend 0.666 ms in the `callback` below. 11 | # Considering network and API overhead, this forces us to offload work into 12 | # a worker thread. 13 | # 14 | # 15 | # There are 32 different bins you can trace. However, due to the timing 16 | # constraints you could only reliably dump 1 or 2 at a time. 17 | # Due to this, the API can not do parallel dumping. 18 | 19 | seconds = 10.0 20 | bin_index = 0 21 | 22 | 23 | from xboxpy import * 24 | 25 | from concurrent.futures import * 26 | 27 | # Open outputs 28 | wav = aci.export_wav("mixbuf_trace.wav", channels=1, sample_width=3) 29 | try: 30 | import pyaudio 31 | pya = pyaudio.PyAudio() 32 | stream = pya.open(format=pya.get_format_from_width(width=3), channels=1, rate=48000, output=True) 33 | except ImportError: 34 | print("Could not find pyaudio, will only stream to disk.") 35 | stream = None 36 | 37 | # Timing is VERY critical with the short APU frames. 38 | # so instead of doing the work instantly, we defer it to another thread. 39 | # This is said asynchronous action 40 | def callback_deferred(duration, data): 41 | # Do the format conversion (extracting the 3 used bytes from 4 bytes) 42 | data = dsp.to24(data) 43 | # Forward to output 44 | if stream != None: 45 | stream.write(data) 46 | wav.writeframes(data) 47 | 48 | # This manages the worker we offload work to. 49 | executor = ThreadPoolExecutor(max_workers=1) 50 | 51 | # Define the handler which will send data to the worker. 52 | def callback(duration, data): 53 | global tracked_duration 54 | global running_thread 55 | executor.submit(callback_deferred, duration, data) 56 | tracked_duration = duration 57 | return duration >= seconds 58 | 59 | # Start tracing 60 | apu.TraceMIXBUF(bin_index, callback) 61 | 62 | # Report if the simple callback alone was enough to kill us. 63 | # Then wait for our worker to finish, too. 64 | print("Took " + str(tracked_duration * 1000.0) + "ms. Expected " + str(seconds * 1000.0) + "ms") 65 | executor.shutdown(wait=True) 66 | 67 | # Stop and close all outputs 68 | if stream != None: 69 | stream.stop_stream() 70 | stream.close() 71 | wav.close() 72 | -------------------------------------------------------------------------------- /dump-xbox/Makefile.oxdk: -------------------------------------------------------------------------------- 1 | # Put the IP address of your FTP enabled xbox here 2 | #XBOX=192.168.177.80 3 | XBOX=192.168.178.63 4 | XBOX_PATH=/E/Games/xqemu-tools-dump-xbox 5 | 6 | #OpenXDK path 7 | PREFIX=. 8 | 9 | # Your 32 bit Windows compiler 10 | CC = i686-w64-mingw32-gcc 11 | 12 | # Path to CXBE 13 | CXBE = bin/cxbe 14 | 15 | # --- 16 | 17 | #SDLFLAGS = -DENABLE_XBOX -DDISABLE_CDROM 18 | 19 | CC_FLAGS = -m32 -march=i386 -O0 -g -shared -std=gnu99 -ffreestanding -nostdlib -fno-builtin -fno-exceptions # $(SDLFLAGS) 20 | INCLUDE = -I$(PREFIX)/i386-pc-xbox/include -I$(PREFIX)/include #-I$(PREFIX)/include/SDL 21 | 22 | CLINK = -nostdlib -m32 -march=i386 -O0 -g 23 | ALIGN = -Wl,--file-alignment,0x20 -Wl,--section-alignment,0x20 24 | SHARED = -shared 25 | ENTRYPOINT = -Wl,--entry,_WinMainCRTStartup 26 | STRIP = # -Wl,--strip-all 27 | LD_FLAGS = -m32 -march=i386 -O0 $(CLINK) $(ALIGN) $(SHARED) $(ENTRYPOINT) $(STRIP) 28 | LD_DIRS = -L$(PREFIX)/i386-pc-xbox/lib -L$(PREFIX)/lib 29 | LD_LIBS = $(LD_DIRS) -lopenxdk -lhal -lusb -lc -lhal -lc -lxboxkrnl #-lSDL 30 | 31 | # --- 32 | 33 | all: default.xbe 34 | 35 | # Upload program to xbox 36 | transfer: default.xbe 37 | wput -u ftp://xbox:xbox@$(XBOX):21/E/Games/xqemu-tools-dump-xbox/default.xbe default.xbe 38 | 39 | # Download dumped files from xbox 40 | get: 41 | @wget -q -O xboxkrnl.exe ftp://xbox:xbox@$(XBOX):21$(XBOX_PATH)/xboxkrnl.exe 42 | @wget -q -O keys.bin ftp://xbox:xbox@$(XBOX):21$(XBOX_PATH)/keys.bin 43 | @wget -q -O flash.bin ftp://xbox:xbox@$(XBOX):21$(XBOX_PATH)/flash.bin 44 | @wget -q -O eeprom.bin ftp://xbox:xbox@$(XBOX):21$(XBOX_PATH)/eeprom.bin 45 | @wget -q -O hdd_0x0-0x3ff.bin ftp://xbox:xbox@$(XBOX):21$(XBOX_PATH)/hdd_0x0-0x3ff.bin 46 | 47 | # Test to see if turning on / off the MCPX works 48 | cmp: 49 | @wget -q -O mcpx-on.bin ftp://xbox:xbox@$(XBOX):21$(XBOX_PATH)/mcpx-on.bin 50 | @wget -q -O mcpx-off.bin ftp://xbox:xbox@$(XBOX):21$(XBOX_PATH)/mcpx-off.bin 51 | md5sum mcpx-on.bin mcpx-off.bin 52 | 53 | # Download and display log 54 | log: 55 | @echo "---" 56 | @wget -q -O - ftp://xbox:xbox@$(XBOX):21$(XBOX_PATH)/log.txt 57 | @echo "---" 58 | 59 | .c.o: reboot.h 60 | $(CC) -c $< $(CC_FLAGS) $(INCLUDE) 61 | 62 | default.exe: dump-xbox.o 63 | $(CC) -o $@ $< $(LD_LIBS) $(LD_FLAGS) 64 | 65 | default.xbe: default.exe 66 | $(CXBE) -MODE:RETAIL -TITLE:"XQEMU-Tools: dump-xbox" -DUMPINFO:"cxbe.txt" -OUT:"$@" $< > /dev/null 67 | 68 | clean: 69 | rm -f *.o *.exe *.dll *.xbe *.cxbe cxbe.txt 70 | -------------------------------------------------------------------------------- /controller-tool/ems-topgun-ii.json: -------------------------------------------------------------------------------- 1 | { 2 | "vid": "0B9A", 3 | "pid": "016B", 4 | 5 | "_in": { 6 | "endpoint": 2 7 | }, 8 | 9 | "in": { 10 | "DPAD_UP": { 11 | "offset": 2, 12 | "mask": "01" 13 | }, 14 | "DPAD_DOWN": { 15 | "offset": 2, 16 | "mask": "02" 17 | }, 18 | "DPAD_LEFT": { 19 | "offset": 2, 20 | "mask": "04" 21 | }, 22 | "DPAD_RIGHT ": { 23 | "offset": 2, 24 | "mask": "08" 25 | }, 26 | "START": { 27 | "offset": 2, 28 | "mask": "10" 29 | }, 30 | "BACK": { 31 | "offset": 2, 32 | "mask": "20" 33 | }, 34 | "LEFT_THUMB": { 35 | "offset": 2, 36 | "mask": "40" 37 | }, 38 | "RIGHT_THUMB": { 39 | "offset": 2, 40 | "mask": "80" 41 | }, 42 | "pad?": { 43 | "offset": 3, 44 | "mask": "FF" 45 | }, 46 | "A": { 47 | "offset": 4, 48 | "mask": "FF", 49 | "notes": "Trigger is analog" 50 | }, 51 | "B": { 52 | "offset": 5, 53 | "mask": "FF", 54 | "notes": "Trigger is analog" 55 | }, 56 | "X": { 57 | "offset": 6, 58 | "mask": "FF", 59 | "notes": "Trigger is analog" 60 | }, 61 | "Y": { 62 | "offset": 7, 63 | "mask": "FF", 64 | "notes": "Trigger is analog" 65 | }, 66 | "BLACK": { 67 | "offset": 8, 68 | "mask": "FF", 69 | "notes": "Trigger is analog" 70 | }, 71 | "WHITE": { 72 | "offset": 9, 73 | "mask": "FF", 74 | "notes": "Trigger is analog" 75 | }, 76 | "LEFT_TRIGGER": { 77 | "offset": 10, 78 | "mask": "FF", 79 | "notes": "Trigger is analog" 80 | }, 81 | "RIGHT_TRIGGER": { 82 | "offset": 11, 83 | "mask": "FF", 84 | "notes": "Trigger is analog" 85 | }, 86 | "sThumbLX": { 87 | "offset": 12, 88 | "mask": "FFFF", 89 | "signed": true 90 | }, 91 | "sThumbLY": { 92 | "offset": 14, 93 | "mask": "FFFF", 94 | "signed": true 95 | }, 96 | "sThumbRX": { 97 | "offset": 16, 98 | "mask": "FFFF", 99 | "signed": true 100 | }, 101 | "sThumbRY": { 102 | "offset": 18, 103 | "mask": "FFFF", 104 | "signed": true 105 | } 106 | }, 107 | "out": { 108 | "Left motor": { 109 | "offset": 2, 110 | "mask": "FFFF" 111 | }, 112 | "Right motor": { 113 | "offset": 4, 114 | "mask": "FFFF" 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /controller-tool/standard.json: -------------------------------------------------------------------------------- 1 | { 2 | "products": [ 3 | { "vid": "045E", "pid": "0289", "in-endpoint":1, "name":"controller-s" }, 4 | { "vid": "3767", "pid": "0101", "in-endpoint":2, "name":"speedster" } 5 | ], 6 | "in": { 7 | "DPAD_UP": { 8 | "offset": 2, 9 | "mask": "01" 10 | }, 11 | "DPAD_DOWN": { 12 | "offset": 2, 13 | "mask": "02" 14 | }, 15 | "DPAD_LEFT": { 16 | "offset": 2, 17 | "mask": "04" 18 | }, 19 | "DPAD_RIGHT ": { 20 | "offset": 2, 21 | "mask": "08" 22 | }, 23 | "START": { 24 | "offset": 2, 25 | "mask": "10" 26 | }, 27 | "BACK": { 28 | "offset": 2, 29 | "mask": "20" 30 | }, 31 | "LEFT_THUMB": { 32 | "offset": 2, 33 | "mask": "40" 34 | }, 35 | "RIGHT_THUMB": { 36 | "offset": 2, 37 | "mask": "80" 38 | }, 39 | "pad?": { 40 | "offset": 3, 41 | "mask": "FF" 42 | }, 43 | "A": { 44 | "offset": 4, 45 | "mask": "FF", 46 | "notes": "Is analog" 47 | }, 48 | "B": { 49 | "offset": 5, 50 | "mask": "FF", 51 | "notes": "Is analog" 52 | }, 53 | "X": { 54 | "offset": 6, 55 | "mask": "FF", 56 | "notes": "Is analog" 57 | }, 58 | "Y": { 59 | "offset": 7, 60 | "mask": "FF", 61 | "notes": "Is analog" 62 | }, 63 | "BLACK": { 64 | "offset": 8, 65 | "mask": "FF", 66 | "notes": "Is analog" 67 | }, 68 | "WHITE": { 69 | "offset": 9, 70 | "mask": "FF", 71 | "notes": "Is analog" 72 | }, 73 | "LEFT_TRIGGER": { 74 | "offset": 10, 75 | "mask": "FF", 76 | "notes": "Is analog" 77 | }, 78 | "RIGHT_TRIGGER": { 79 | "offset": 11, 80 | "mask": "FF", 81 | "notes": "Is analog" 82 | }, 83 | "sThumbLX": { 84 | "offset": 12, 85 | "mask": "FFFF", 86 | "signed": true 87 | }, 88 | "sThumbLY": { 89 | "offset": 14, 90 | "mask": "FFFF", 91 | "signed": true 92 | }, 93 | "sThumbRX": { 94 | "offset": 16, 95 | "mask": "FFFF", 96 | "signed": true 97 | }, 98 | "sThumbRY": { 99 | "offset": 18, 100 | "mask": "FFFF", 101 | "signed": true 102 | } 103 | }, 104 | "out": { 105 | "Left motor": { 106 | "offset": 2, 107 | "mask": "FFFF" 108 | }, 109 | "Right motor": { 110 | "offset": 4, 111 | "mask": "FFFF" 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /communicator-tool/main.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3 2 | 3 | import sys 4 | import math 5 | import usb1 6 | import struct 7 | import pyaudio 8 | 9 | sample_rate = 0 10 | 11 | sample_rates = [ 12 | # Sample rate in kHz, usb frame size, timing data? 13 | [ 8000, 16, 0], # 0 14 | [11025, 22, 40], # 1 15 | [16000, 32, 0], # 2 16 | [22050, 44, 20], # 3 17 | [24000, 48, 0] # 4 18 | ] 19 | 20 | pya = pyaudio.PyAudio() 21 | stream = pya.open(format=pya.get_format_from_width(width=2), channels=1, rate=sample_rates[sample_rate][0], output=True) 22 | 23 | with usb1.USBContext() as context: 24 | 25 | vid = 0x045e 26 | pid = 0x0283 27 | interface = 1 28 | 29 | handle = context.openByVendorIDAndProductID(vid, pid, skip_on_error=True) 30 | if handle is None: 31 | # Device not present, or user is not allowed to access device. 32 | print("oops?!") 33 | 34 | set_sample_rate = 0 35 | 36 | handle.controlWrite(usb1.REQUEST_TYPE_VENDOR | usb1.RECIPIENT_INTERFACE, usb1.REQUEST_SET_FEATURE, 0x0100 | sample_rate, set_sample_rate, bytes([])) 37 | 38 | with handle.claimInterface(interface): 39 | 40 | def in_callback(transfer): 41 | if transfer.getStatus() != usb1.TRANSFER_COMPLETED: 42 | return 43 | #data = transfer.getBuffer()[:transfer.getActualLength()] 44 | 45 | for data in transfer.iterISO(): 46 | #print(data[0]) 47 | print(data[1]) 48 | 49 | 50 | # Process data... 51 | stream.write(bytes(data[1])) 52 | 53 | # Resubmit transfer once data is processed. 54 | transfer.submit() 55 | 56 | def out_callback(transfer): 57 | if transfer.getStatus() != usb1.TRANSFER_COMPLETED: 58 | return 59 | print("out") 60 | transfer.submit() 61 | 62 | BUFFER_SIZE = 0x30 #FIXME 63 | 64 | # Build a list of transfer objects and submit them to prime the pump. 65 | transfer_list = [] 66 | for _ in range(1): 67 | transfer = handle.getTransfer(1) 68 | transfer.setIsochronous(usb1.ENDPOINT_IN | 5, BUFFER_SIZE, callback=in_callback) 69 | transfer.submit() 70 | transfer_list.append(transfer) 71 | 72 | # Prepare output data 73 | data = bytes([]) 74 | for i in range(BUFFER_SIZE // 2): 75 | data += bytes([i, i]) 76 | 77 | transfer = handle.getTransfer(1) 78 | transfer.setIsochronous(usb1.ENDPOINT_OUT | 4, data, callback=out_callback) 79 | transfer.submit() 80 | transfer_list.append(transfer) 81 | 82 | # Loop as long as there is at least one submitted transfer. 83 | while any(x.isSubmitted() for x in transfer_list): 84 | try: 85 | context.handleEvents() 86 | except usb1.USBErrorInterrupted: 87 | pass 88 | -------------------------------------------------------------------------------- /python-scripts/get_av_settings.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Retrieves the AV cable type 4 | # 5 | # For more information about these, see: 6 | # http://xboxdevwiki.net/AV_Cables#Supported_signals_.2F_AV_cables 7 | 8 | 9 | from xboxpy import * 10 | 11 | 12 | #FIXME: Move these libc style functions to some helper module? 13 | 14 | def malloc(size): 15 | #FIXME: Use keNtAllocateVirtualMemory(&addr, 0, &size, ke.MEM_RESERVE | ke.MEM_COMMIT, ke.PAGE_READWRITE) 16 | # (Where addr is a pointer to 32 bit of 0) 17 | return ke.MmAllocateContiguousMemory(size) 18 | 19 | def free(ptr): 20 | #FIXME: Once malloc is fixed, use ke.NtFreeVirtualMemory(ptr, 0, ke.MEM_RELEASE) 21 | ke.MmFreeContiguousMemory(ptr) 22 | 23 | 24 | def print_av_settings(): 25 | val_addr = malloc(4) 26 | VIDEO_BASE = 0xFD000000 27 | VIDEO_ENC_GET_SETTINGS = 6 28 | ke.AvSendTVEncoderOption(VIDEO_BASE, VIDEO_ENC_GET_SETTINGS, 0, val_addr); 29 | val = read_u32(val_addr) 30 | free(val_addr) 31 | 32 | print("Value is: 0x" + format(val, '08X')) 33 | 34 | cable = val & 0xFF 35 | cables = [ 36 | "No cable connected", 37 | "Standard AV Cable (Composite)", 38 | "RF Adapter", 39 | "Advanced SCART Cable", 40 | "High Definition AV Pack (Component)", 41 | "Unofficial: VGA", 42 | "Advanced AV Pack (S-Video)" 43 | ] 44 | print("Cable type: 0x" + format(cable, '02X') + " (" + cables[cable] + ")") 45 | 46 | standard = (val >> 8) & 0xFF 47 | standards = [ 48 | "Unknown", 49 | "NTSC-M", 50 | "NTSC-J", 51 | "PAL-I", 52 | "PAL-M" 53 | ] 54 | print("Video standard: 0x" + format(standard, '02X') + " (" + standards[standard] + ")") 55 | 56 | refresh_rate = val & 0x00C00000 57 | if (refresh_rate == 0x00800000): 58 | print("50 Hz") 59 | elif (refresh_rate == 0x00400000): 60 | print("60 Hz") 61 | else: 62 | print("Unknown refresh rate: 0x" + format(refresh_rate, '08X')) 63 | 64 | print("Enabled HDTV modes {") 65 | mode = val & 0x000E0000 66 | if (mode & 0x00010000): 67 | print(" HDTV 480i") 68 | if (mode & 0x00020000): 69 | print(" HDTV 720p") 70 | if (mode & 0x00040000): 71 | print(" HDTV 1080i") 72 | if (mode & 0x00080000): 73 | print(" HDTV 480p") 74 | print("}") 75 | 76 | print("Flags {") 77 | 78 | if val & 0x00010000: 79 | print(" Widescreen") 80 | 81 | if val & 0x00100000: 82 | print(" Letterbox") 83 | 84 | if val & 0x02000000: 85 | print(" 10x11 Pixels") 86 | 87 | if val & 0x00200000: 88 | print(" Interlaced") 89 | 90 | if val & 0x01000000: 91 | print(" Field rendering") 92 | 93 | print("}") 94 | 95 | def main(): 96 | print_av_settings() 97 | 98 | if __name__ == '__main__': 99 | main() 100 | -------------------------------------------------------------------------------- /controller-tool/main.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3 2 | 3 | import sys 4 | import math 5 | import usb1 6 | import json 7 | 8 | BUFFER_SIZE = 200 9 | 10 | json_data=open(sys.argv[1]).read() 11 | 12 | structure = json.loads(json_data) 13 | #print(structure) 14 | 15 | with usb1.USBContext() as context: 16 | 17 | for product in structure['products']: 18 | vid = int(product['vid'], 16) 19 | pid = int(product['pid'], 16) 20 | INTERFACE = 0 21 | ENDPOINT = product['in-endpoint'] 22 | 23 | handle = context.openByVendorIDAndProductID(vid, pid, skip_on_error=True) 24 | if handle is None: 25 | # Device not present, or user is not allowed to access device. 26 | print("oops?!") 27 | continue 28 | 29 | print("Found '" + product['name'] + "'") 30 | 31 | break 32 | 33 | with handle.claimInterface(INTERFACE): 34 | # Do stuff with endpoints on claimed interface. 35 | print("claimed!"); 36 | 37 | def MaskLength(mask): 38 | if (mask == 0): 39 | return 0 40 | return int(math.log(mask, 2)) + 1 41 | def ffs(x): 42 | return MaskLength((x ^ (x - 1)) >> 1) 43 | def setU(data, offset, mask, value): 44 | #FIXME: Make this work on bits and not bytes 45 | mask_length = (MaskLength(mask) + 7) // 8 46 | for i in range(0, mask_length): 47 | data[offset + i] = (value >> (i * 8)) & 0xFF 48 | return 49 | def getU(data, offset, mask): 50 | mask_length = (MaskLength(mask) + 7) // 8 51 | value = 0 52 | for i in range(0, mask_length): 53 | value |= data[offset + i] << (i * 8) 54 | return (value & mask) >> ffs(mask) 55 | def getS(data, offset, mask): 56 | value = getU(data, offset, mask) 57 | mask_length = (MaskLength(mask) - ffs(mask)) 58 | sign = -(value & (1 << (mask_length - 1))) 59 | value = sign + (value & ~(1 << (mask_length - 1))) 60 | return value 61 | 62 | while True: 63 | try: 64 | in_data = handle.interruptRead(ENDPOINT, BUFFER_SIZE, timeout=100) 65 | except usb1.USBErrorTimeout: 66 | continue 67 | 68 | for k, e in structure['in'].items(): 69 | offset = e['offset'] 70 | mask = int(e['mask'], 16) 71 | try: 72 | signed = e['signed'] 73 | except: 74 | signed = False 75 | 76 | if signed: 77 | value = getS(in_data, offset, mask) 78 | else: 79 | value = getU(in_data, offset, mask) 80 | print(k + " offset: " + str(offset) + "; value: " + str(value)) 81 | 82 | 83 | 84 | # Process data... 85 | print(in_data) 86 | #out_data = bytearray([0,14,0x00,0x00,0x00,0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) 87 | out_data = bytearray([0,6,0x00,0x00,0x00,0x00]) 88 | setU(out_data, 2, 0xFFFF, getU(in_data, 10, 0xFF) * 0x101) 89 | setU(out_data, 4, 0xFFFF, getU(in_data, 11, 0xFF) * 0x101) 90 | data = handle.interruptWrite(0x02, out_data) 91 | -------------------------------------------------------------------------------- /python-scripts/screenshot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Takes a screenshot 4 | 5 | from xboxpy import * 6 | 7 | from PIL import Image 8 | 9 | def screenshot(path): 10 | #FIXME: Move some of this stuff into xbox module 11 | 12 | 13 | pitch = nv2a.ReadCRTC(0x13) 14 | pitch |= (nv2a.ReadCRTC(0x19) & 0xE0) << 3 15 | pitch |= (nv2a.ReadCRTC(0x25) & 0x20) << 6 16 | pitch *= 8 17 | bytes_per_pixel = nv2a.ReadCRTC(0x28) & 0x7F #FIXME: 3 when it's actually 4.. 18 | is_565 = False if bytes_per_pixel != 2 else (nv2a.read_u32(0x680600) & 0x1000) > 0 19 | framebuffer_addr = nv2a.read_u32(0x600800) 20 | print("fb: " + format(framebuffer_addr, '08X')) 21 | print("pitch: " + str(pitch)) 22 | print("565: " + str(is_565)) 23 | print("bytes_per_pixel: " + str(bytes_per_pixel)) 24 | 25 | # Stolen from QEMU: 26 | if False: # if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) { 27 | # width = s->vbe_regs[VBE_DISPI_INDEX_XRES]; 28 | # height = s->vbe_regs[VBE_DISPI_INDEX_YRES]; 29 | assert(False) 30 | else: 31 | VGA_CRTC_H_DISP = 1 32 | VGA_CRTC_OVERFLOW = 7 33 | VGA_CRTC_V_DISP_END = 0x12 34 | width = (nv2a.ReadCRTC(VGA_CRTC_H_DISP) + 1) * 8 35 | height = nv2a.ReadCRTC(VGA_CRTC_V_DISP_END) 36 | height |= (nv2a.ReadCRTC(VGA_CRTC_OVERFLOW) & 0x02) << 7 37 | height |= (nv2a.ReadCRTC(VGA_CRTC_OVERFLOW) & 0x40) << 3 38 | height += 1; 39 | 40 | print(str(width) + " x " + str(height)) 41 | 42 | framebuffer_data = read(framebuffer_addr, pitch * height, True) 43 | 44 | # FIXME: Why does this happen?! 45 | if bytes_per_pixel == 3: 46 | bytes_per_pixel = 4 47 | 48 | unswizzled = nv2a.Unswizzle(framebuffer_data, bytes_per_pixel * 8, (width, height), pitch) 49 | 50 | screenshot = Image.new('RGB', (width, height)) 51 | pixels = screenshot.load() 52 | for y in range (0,height): 53 | for x in range (0,width): 54 | p = y * pitch + x * bytes_per_pixel 55 | if bytes_per_pixel == 4: 56 | b = unswizzled[p + 0] 57 | g = unswizzled[p + 1] 58 | r = unswizzled[p + 2] 59 | a = unswizzled[p + 3] 60 | else: 61 | if is_565: 62 | #FIXME: Untested! 63 | b = unswizzled[p + 0] & 0x1F 64 | g = (unswizzled[p + 0] >> 5) & 0x7 65 | g |= unswizzled[p + 1] & 0x7 66 | r = (unswizzled[p + 1] >> 3) & 0x1F 67 | #FIXME: Bring to [0, 255] range?! 68 | a = 0 69 | 70 | b = int(r / 0x1F * 0xFF) 71 | g = int(g / 0x1F * 0xFF) 72 | r = int(b / 0x1F * 0xFF) 73 | # alpha is zero anyway, needs no rescaling 74 | 75 | else: 76 | #FIXME: Untested! 77 | b = unswizzled[p + 0] & 0x1F 78 | g = (unswizzled[p + 0] >> 5) & 0x7 79 | g |= unswizzled[p + 1] & 0x3 80 | r = (unswizzled[p + 1] >> 2) & 0x1F 81 | a = (unswizzled[p + 1] >> 7) & 0x1 82 | 83 | b = int(r / 0x1F * 0xFF) 84 | g = int(g / 0x3F * 0xFF) 85 | r = int(b / 0x1F * 0xFF) 86 | a = int(a / 0x1 * 0xFF) 87 | 88 | pixels[x, y]=(r,g,b) 89 | 90 | screenshot.save(path) 91 | 92 | screenshot("screenshot.png") 93 | -------------------------------------------------------------------------------- /dump-xbox/common/pe.h: -------------------------------------------------------------------------------- 1 | #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 2 | #define IMAGE_SIZEOF_SHORT_NAME 8 3 | 4 | typedef struct _IMAGE_FILE_HEADER { 5 | uint16_t Machine; 6 | uint16_t NumberOfSections; 7 | uint32_t TimeDateStamp; 8 | uint32_t PointerToSymbolTable; 9 | uint32_t NumberOfSymbols; 10 | uint16_t SizeOfOptionalHeader; 11 | uint16_t Characteristics; 12 | } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; 13 | 14 | typedef struct _IMAGE_DATA_DIRECTORY { 15 | uint32_t VirtualAddress; 16 | uint32_t Size; 17 | } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; 18 | 19 | typedef struct _IMAGE_OPTIONAL_HEADER { 20 | uint16_t Magic; 21 | uint8_t MajorLinkerVersion; 22 | uint8_t MinorLinkerVersion; 23 | uint32_t SizeOfCode; 24 | uint32_t SizeOfInitializedData; 25 | uint32_t SizeOfUninitializedData; 26 | uint32_t AddressOfEntryPoint; 27 | uint32_t BaseOfCode; 28 | uint32_t BaseOfData; 29 | uint32_t ImageBase; 30 | uint32_t SectionAlignment; 31 | uint32_t FileAlignment; 32 | uint16_t MajorOperatingSystemVersion; 33 | uint16_t MinorOperatingSystemVersion; 34 | uint16_t MajorImageVersion; 35 | uint16_t MinorImageVersion; 36 | uint16_t MajorSubsystemVersion; 37 | uint16_t MinorSubsystemVersion; 38 | uint32_t Win32VersionValue; 39 | uint32_t SizeOfImage; 40 | uint32_t SizeOfHeaders; 41 | uint32_t CheckSum; 42 | uint16_t Subsystem; 43 | uint16_t DllCharacteristics; 44 | uint32_t SizeOfStackReserve; 45 | uint32_t SizeOfStackCommit; 46 | uint32_t SizeOfHeapReserve; 47 | uint32_t SizeOfHeapCommit; 48 | uint32_t LoaderFlags; 49 | uint32_t NumberOfRvaAndSizes; 50 | IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; 51 | } IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER; 52 | 53 | typedef struct _IMAGE_NT_HEADERS { 54 | uint32_t Signature; 55 | IMAGE_FILE_HEADER FileHeader; 56 | IMAGE_OPTIONAL_HEADER OptionalHeader; 57 | } IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS; 58 | 59 | typedef struct _IMAGE_SECTION_HEADER { 60 | uint8_t Name[IMAGE_SIZEOF_SHORT_NAME]; 61 | union { 62 | uint32_t PhysicalAddress; 63 | uint32_t VirtualSize; 64 | } Misc; 65 | uint32_t VirtualAddress; 66 | uint32_t SizeOfRawData; 67 | uint32_t PointerToRawData; 68 | uint32_t PointerToRelocations; 69 | uint32_t PointerToLinenumbers; 70 | uint16_t NumberOfRelocations; 71 | uint16_t NumberOfLinenumbers; 72 | uint32_t Characteristics; 73 | } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; 74 | 75 | typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header 76 | uint16_t e_magic; // Magic number 77 | uint16_t e_cblp; // Bytes on last page of file 78 | uint16_t e_cp; // Pages in file 79 | uint16_t e_crlc; // Relocations 80 | uint16_t e_cparhdr; // Size of header in paragraphs 81 | uint16_t e_minalloc; // Minimum extra paragraphs needed 82 | uint16_t e_maxalloc; // Maximum extra paragraphs needed 83 | uint16_t e_ss; // Initial (relative) SS value 84 | uint16_t e_sp; // Initial SP value 85 | uint16_t e_csum; // Checksum 86 | uint16_t e_ip; // Initial IP value 87 | uint16_t e_cs; // Initial (relative) CS value 88 | uint16_t e_lfarlc; // File address of relocation table 89 | uint16_t e_ovno; // Overlay number 90 | uint16_t e_res[4]; // Reserved words 91 | uint16_t e_oemid; // OEM identifier (for e_oeminfo) 92 | uint16_t e_oeminfo; // OEM information; e_oemid specific 93 | uint16_t e_res2[10]; // Reserved words 94 | uint32_t e_lfanew; // File address of new exe header 95 | } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; 96 | 97 | -------------------------------------------------------------------------------- /python-scripts/dvd_info.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # This dumps out the DVDs SS.bin, DMI.bin and PFI.bin 4 | # 5 | # For more information about these files, see: 6 | # http://xboxdevwiki.net/Xbox_Game_Disc 7 | 8 | 9 | from xboxpy import * 10 | 11 | 12 | #FIXME: Move these libc style functions to some helper module? 13 | 14 | def malloc(size): 15 | #FIXME: Use keNtAllocateVirtualMemory(&addr, 0, &size, ke.MEM_RESERVE | ke.MEM_COMMIT, ke.PAGE_READWRITE) 16 | # (Where addr is a pointer to 32 bit of 0) 17 | return ke.MmAllocateContiguousMemory(size) 18 | 19 | def free(ptr): 20 | #FIXME: Once malloc is fixed, use ke.NtFreeVirtualMemory(ptr, 0, ke.MEM_RELEASE) 21 | ke.MmFreeContiguousMemory(ptr) 22 | 23 | def strdup(string): 24 | addr = malloc(len(string) + 1) 25 | write(addr, bytes(string + '\x00', encoding='ascii')) 26 | return addr 27 | 28 | 29 | def get_dvd_device_object(): 30 | #static PDEVICE_OBJECT device = NULL; 31 | #if (device == NULL): 32 | #ANSI_STRING cdrom 33 | 34 | ANSI_STRING_len = 8 35 | cdrom_addr = malloc(ANSI_STRING_len) 36 | 37 | string = strdup("\\Device\\Cdrom0") 38 | ke.RtlInitAnsiString(cdrom_addr, string) 39 | 40 | # Get a reference to the dvd object so that we can query it for info. 41 | device_ptr_addr = malloc(4) # Pointer to device 42 | status = ke.ObReferenceObjectByName(cdrom_addr, 0, ke.IoDeviceObjectType(), ke.NULL, device_ptr_addr) 43 | device_ptr = read_u32(device_ptr_addr) 44 | free(device_ptr_addr) 45 | 46 | free(string) 47 | free(cdrom_addr) 48 | 49 | print("Status: 0x" + format(status, '08X')) 50 | print("Device: 0x" + format(device_ptr, '08X')) 51 | 52 | if (status != 0): 53 | return ke.NULL 54 | 55 | assert(device_ptr != ke.NULL) 56 | return device_ptr 57 | 58 | def main(): 59 | device_ptr = get_dvd_device_object() 60 | assert(device_ptr != ke.NULL) 61 | 62 | #SCSI_PASS_THROUGH_DIRECT pass_through; 63 | #RtlZeroMemory(&pass_through, sizeof(SCSI_PASS_THROUGH_DIRECT)); 64 | 65 | if True: 66 | length = 2048+4 67 | cdb = [0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (length >> 8) & 0xFF, length & 0xFF, 0x00, 0xC0] # Get PFI 68 | #cdb = [0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, (length >> 8) & 0xFF, length & 0xFF, 0x00, 0xC0] # Get DMI 69 | #cdb = [0xAD, 0x00, 0xFF, 0x02, 0xFD, 0xFF, 0xFE, 0x00, (length >> 8) & 0xFF, length & 0xFF, 0x00, 0xC0] # Get SS 70 | else: 71 | length = 20+8 # Length of auth page + mode sense header 72 | cdb = [0x5A, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, (length >> 8) & 0xFF, length & 0xFF, 0x00] 73 | 74 | buffer_length = length 75 | buffer_addr = malloc(buffer_length) #FIXME: How long does this have to be? 76 | write(buffer_addr, [0xFF] * buffer_length) 77 | 78 | # Now write the SCSI_PASS_THROUGH_DIRECT structure: 79 | #USHORT Length; // 0 80 | #UCHAR ScsiStatus; // 2 81 | #UCHAR PathId; // 3 82 | #UCHAR TargetId; // 4 83 | #UCHAR Lun; // 5 84 | #UCHAR CdbLength; // 6 85 | #UCHAR SenseInfoLength; // 7 86 | #UCHAR DataIn; // 8 87 | #ULONG DataTransferLength; // 12 88 | #ULONG TimeOutValue; // 16 89 | #PVOID DataBuffer; // 20 90 | #ULONG SenseInfoOffset; // 24 91 | #UCHAR Cdb[16]; // 28 92 | #// 44 93 | 94 | SCSI_PASS_THROUGH_DIRECT_len = 44 95 | pass_through_addr = malloc(SCSI_PASS_THROUGH_DIRECT_len) 96 | write(pass_through_addr, [0] * SCSI_PASS_THROUGH_DIRECT_len) 97 | write_u16(pass_through_addr + 0, SCSI_PASS_THROUGH_DIRECT_len) # Length 98 | #write_u8(pass_through_addr + 6, len(cdb)) #CdbLength # FIXME: Not necessary.. remove! 99 | write_u8(pass_through_addr + 8, ke.SCSI_IOCTL_DATA_IN) # DataIn 100 | write_u32(pass_through_addr + 12, buffer_length) # DataTransferLength 101 | write_u32(pass_through_addr + 20, buffer_addr) # DataBuffer 102 | assert(len(cdb) <= 16) 103 | write(pass_through_addr + 28, cdb) # Cdb 104 | 105 | status = ke.IoSynchronousDeviceIoControlRequest(ke.IOCTL_SCSI_PASS_THROUGH_DIRECT, device_ptr, pass_through_addr, SCSI_PASS_THROUGH_DIRECT_len, ke.NULL, 0, ke.NULL, ke.FALSE) 106 | 107 | print("Status: 0x" + format(status, '08X')) 108 | 109 | buffer_data = read(buffer_addr, buffer_length) 110 | print(buffer_data) 111 | 112 | free(buffer_addr) 113 | free(pass_through_addr) 114 | 115 | if __name__ == '__main__': 116 | main() 117 | -------------------------------------------------------------------------------- /python-scripts/dsp_homebrew.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Run this from a clean Xbox environment (= not while a game is running) 4 | # NXDK-RDT is a good environment 5 | 6 | from xboxpy import * 7 | 8 | import sys 9 | import time 10 | 11 | def dsp_homebrew(): 12 | #FIXME: Pass dsp object which provides all the device details and registers instead 13 | 14 | # Reset GP and GP DSP 15 | apu.write_u32(NV_PAPU_GPRST, 0) 16 | time.sleep(0.1) # FIXME: Not sure if DSP reset is synchronous, so we wait for now 17 | 18 | # Enable GP first, otherwise memory writes won't be handled 19 | apu.write_u32(NV_PAPU_GPRST, NV_PAPU_GPRST_GPRST) 20 | 21 | # Allocate some scratch space (at least 2 pages!) 22 | #FIXME: Why did I put: "(at least 2 pages!)" ? 23 | page_count = 2 24 | 25 | # Apparently NV_PAPU_GPSADDR has to be aligned to 0x4000 bytes?! 26 | #FIXME: Free memory after running 27 | page_head = ke.MmAllocateContiguousMemoryEx(4096, 0x00000000, 0xFFFFFFFF, 0x4000, ke.PAGE_READWRITE) 28 | page_head_p = ke.MmGetPhysicalAddress(page_head) 29 | apu.write_u32(NV_PAPU_GPSADDR, page_head_p) 30 | page_base = ke.MmAllocateContiguousMemory(4096 * page_count) 31 | for i in range(0, page_count): 32 | write_u32(page_head + i * 8 + 0, ke.MmGetPhysicalAddress(page_base + 0x1000 * i)) 33 | write_u32(page_head + i * 8 + 4, 0) # Control 34 | 35 | # I'm not sure if this is off-by-one (maybe `page_count - 1`) 36 | apu.write_u32(NV_PAPU_GPSMAXSGE, page_count) 37 | 38 | # It was assembled using `a56 loop.inc && toomf < a56.out`. 39 | # The resulting code was then copied here. 40 | # `a56` (inc. `toomf`) can be found at: http://www.zdomain.com/a56.html 41 | if False: 42 | print("Starting assembler") 43 | #raise #FIXME: Test this codepath 44 | data = dsp.assemble(""" 45 | ; Simple test program 46 | start 47 | move x:$000000, a 48 | move a, y:$000000 49 | jmp start 50 | """) 51 | print("Using assembler result!") 52 | else: 53 | code = "56F000 000000 5E7000 000000 0AF080 000000" 54 | code_words = code.split() 55 | data = bytearray() 56 | for i in range(0, len(code_words)): 57 | data += int.to_bytes(int(code_words[i], 16), length=3, byteorder='little', signed=False) 58 | 59 | if False: 60 | code = open("tiny-gp.inc").read() 61 | print(code) 62 | data = dsp.assemble(code) 63 | 64 | print(data) 65 | 66 | # Convert the 24 bit words to 32 bit words 67 | data = dsp.from24(data) 68 | 69 | # Write code to PMEM (normally you can just use write() but we don't support that for apu MMIO yet.. boo!) 70 | for i in range(0, len(data) // 4): 71 | word = int.from_bytes(data[i*4:i*4+4], byteorder='little', signed=False) & 0xFFFFFF 72 | apu.write_u32(NV_PAPU_GPPMEM + i*4, word) 73 | # According to XQEMU, 0x800 * 4 bytes will be loaded from scratch to PMEM at startup. 74 | # So just to be sure, let's also write this to the scratch.. 75 | write_u32(page_base + i*4, word) 76 | 77 | 78 | # Set XMEM 79 | apu.write_u32(NV_PAPU_GPXMEM + 0*4, 0x001337) 80 | 81 | # Set YMEM 82 | apu.write_u32(NV_PAPU_GPYMEM + 0*4, 0x000000) 83 | 84 | # Test readback 85 | print("Read back X[0]:0x" + format(apu.read_u32(NV_PAPU_GPXMEM + 0*4), '06X')) 86 | print("Read back Y[0]:0x" + format(apu.read_u32(NV_PAPU_GPYMEM + 0*4), '06X')) 87 | 88 | print("Read back P[0]:0x" + format(apu.read_u32(NV_PAPU_GPPMEM + 0*4), '06X')) 89 | print("Read back P[1]:0x" + format(apu.read_u32(NV_PAPU_GPPMEM + 1*4), '06X')) 90 | 91 | # Set frame duration (?!) 92 | if False: 93 | apu.write_u32(NV_PAPU_SECTL, 3 << 3) 94 | 95 | # Mark GP as ready? 96 | NV_PAPU_GPIDRDY = 0x3FF10 97 | NV_PAPU_GPIDRDY_GPSETIDLE = (1 << 0) 98 | apu.write_u32(NV_PAPU_GPIDRDY, NV_PAPU_GPIDRDY_GPSETIDLE) 99 | 100 | # Clear interrupts 101 | NV_PAPU_GPISTS = 0x3FF14 102 | apu.write_u32(NV_PAPU_GPISTS, 0xFF) 103 | 104 | # Now run GP DSP by removing reset bit 105 | apu.write_u32(NV_PAPU_GPRST, NV_PAPU_GPRST_GPRST | NV_PAPU_GPRST_GPDSPRST) 106 | time.sleep(0.1) 107 | 108 | 109 | # Read destination data from YMEM 110 | t = 0 111 | while True: 112 | # Write X again. Bootcode in the DSP seems to overwrite XMEM + YMEM 113 | t += 1 114 | apu.write_u32(NV_PAPU_GPXMEM + 0*4, t) 115 | apu.write_u32(NV_PAPU_GPYMEM + 0*4, 0x00DEAD) 116 | i = 0 117 | print("") 118 | print("Read back S[%i]:0x%06X" % (i, read_u32(page_base + i*4))) 119 | print("Read back P[%i]:0x%06X" % (i, apu.read_u32(NV_PAPU_GPPMEM + i*4))) 120 | print("Read back X[%i]:0x%06X" % (i, apu.read_u32(NV_PAPU_GPXMEM + i*4))) 121 | print("Read back Y[%i]:0x%06X" % (i, apu.read_u32(NV_PAPU_GPYMEM + i*4))) 122 | time.sleep(0.5) 123 | 124 | 125 | dsp_homebrew() 126 | -------------------------------------------------------------------------------- /python-scripts/play_ac97.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # A standalone AC97 audio player, based on XAudio from OpenXDK 4 | # The interrupt related code has been removed as it's currently not supported. 5 | 6 | from xboxpy import * 7 | 8 | import sys 9 | import time 10 | import wave 11 | 12 | pcmDescriptors = 0 13 | spdifDescriptors = 0 14 | nextDescriptor = 0 15 | 16 | def XAudioPlay(): 17 | #aci.write_u32(0x118, 0x1D000000) # PCM out - run, allow interrupts 18 | #aci.write_u32(0x178, 0x1D000000) # SPDIF out - run, allow interrupts 19 | aci.write_u32(0x118, 0x01000000) # PCM out - run 20 | aci.write_u32(0x178, 0x01000000) # SPDIF out - run 21 | 22 | def XAudioPause(): 23 | #aci.write_u32(0x118, 0x1C000000) # PCM out - PAUSE, allow interrupts 24 | #aci.write_u32(0x178, 0x1C000000) # SPDIF out - PAUSE, allow interrupts 25 | aci.write_u32(0x118, 0x00000000) # PCM out - PAUSE 26 | aci.write_u32(0x178, 0x00000000) # SPDIF out - PAUSE 27 | 28 | # This is the function you should call when you want to give the 29 | # audio chip some more data. If you have registered a callback, it 30 | # should call this method. If you are providing the samples manually, 31 | # you need to make sure you call this function often enough so the 32 | 33 | # chip doesn't run out of data 34 | def XAudioProvideSamples(address, length, final = False): 35 | global pcmDescriptors 36 | global spdifDescriptors 37 | global nextDescriptor 38 | 39 | bufferControl = 0 40 | 41 | if final: 42 | bufferControl |= 0x4000 # b14=1=last in stream 43 | if False: 44 | bufferControl |= 0x8000 # b15=1=issue IRQ on completion 45 | 46 | write_u32(pcmDescriptors + nextDescriptor * 8 + 0, ke.MmGetPhysicalAddress(address)) 47 | write_u16(pcmDescriptors + nextDescriptor * 8 + 4, length) 48 | write_u16(pcmDescriptors + nextDescriptor * 8 + 6, bufferControl) 49 | aci.write_u8(0x115, nextDescriptor) # set last active PCM descriptor 50 | 51 | write_u32(spdifDescriptors + nextDescriptor * 8 + 0, ke.MmGetPhysicalAddress(address)) 52 | write_u16(spdifDescriptors + nextDescriptor * 8 + 4, length) 53 | write_u16(spdifDescriptors + nextDescriptor * 8 + 6, bufferControl) 54 | aci.write_u8(0x175, nextDescriptor) # set last active SPDIF descriptor 55 | 56 | # increment to the next buffer descriptor (rolling around to 0 once you get to 31) 57 | nextDescriptor = (nextDescriptor + 1) % 32 58 | 59 | def XAudioInit(): 60 | global pcmDescriptors 61 | global spdifDescriptors 62 | 63 | # perform a cold reset 64 | tmp = aci.read_u32(0x12C) 65 | aci.write_u32(0x12C, tmp & 0xFFFFFFFD) 66 | time.sleep(0.1) 67 | aci.write_u32(0x12C, tmp | 2) 68 | 69 | # wait until the chip is finished resetting... 70 | while not aci.read_u32(0x130) & 0x100: 71 | pass 72 | 73 | # clear all interrupts 74 | aci.write_u8(0x116, 0xFF) 75 | aci.write_u8(0x176, 0xFF) 76 | 77 | # According to OpenXDK code, alignment for these should be 8, BUT.. 78 | # ..I don't want it across 2 pages (for safety) 79 | pcmDescriptors = ke.MmAllocateContiguousMemory(32 * 8) 80 | spdifDescriptors = ke.MmAllocateContiguousMemory(32 * 8) 81 | 82 | # Clear the descriptors 83 | write(pcmDescriptors, [0] * 32 * 8) 84 | write(spdifDescriptors, [0] * 32 * 8) 85 | print("PCM desc is v 0x" + format(pcmDescriptors, '08X')) 86 | print("PCM desc is p 0x" + format(ke.MmGetPhysicalAddress(pcmDescriptors), '08X')) 87 | 88 | # Tell the audio chip where it should look for the descriptors 89 | aci.write_u32(0x100, 0) # no PCM input 90 | aci.write_u32(0x110, ke.MmGetPhysicalAddress(pcmDescriptors)) # PCM 91 | aci.write_u32(0x170, ke.MmGetPhysicalAddress(spdifDescriptors)) # SPDIF 92 | 93 | # default to being silent... 94 | XAudioPause() 95 | 96 | # Register our ISR 97 | #AUDIO_IRQ = 6 98 | #irql_address = malloc(1) 99 | #vector = HalGetInterruptVector(AUDIO_IRQ, irql_address) 100 | #KeInitializeDpc(&DPCObject,&DPC,NULL) 101 | #KeInitializeInterrupt(&InterruptObject, &ISR, NULL, vector, read_u8(irql_address), LevelSensitive, FALSE) 102 | #KeConnectInterrupt(&InterruptObject) 103 | 104 | def main(): 105 | wav = wave.open(sys.argv[1], 'rb') 106 | 107 | # This code currently only supports signed 16-bit stereo PCM at 48000 Hz 108 | assert(wav.getnchannels() == 2) 109 | assert(wav.getsampwidth() == 2) 110 | assert(wav.getframerate() == 48000) 111 | 112 | # Initialize audio 113 | XAudioInit() 114 | print("Audio initialized!") 115 | 116 | # Don't use more than 32 buffers.. or you will overwrite the beginning 117 | while True: 118 | data = wav.readframes(0xFFFF // 2) 119 | if len(data) == 0: 120 | break 121 | address = ke.MmAllocateContiguousMemory(len(data)) 122 | print("Allocated " + str(len(data)) + " bytes") 123 | write(address, data) 124 | XAudioProvideSamples(address, len(data) // 2) 125 | print("Next buffer") 126 | 127 | print("Starting audio playback!") 128 | XAudioPlay() 129 | 130 | if __name__ == '__main__': 131 | main() 132 | -------------------------------------------------------------------------------- /python-scripts/get_timers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Prints information about some of the Xbox timers 4 | 5 | from xboxpy import * 6 | 7 | 8 | 9 | from io import StringIO 10 | import sys 11 | import time 12 | import struct 13 | 14 | NV_PRAMDAC = 0x680000 15 | NV_PRAMDAC_NVPLL_COEFF = 0x500 16 | NV_PRAMDAC_NVPLL_COEFF_MDIV = 0x000000FF 17 | NV_PRAMDAC_NVPLL_COEFF_NDIV = 0x0000FF00 18 | NV_PRAMDAC_NVPLL_COEFF_PDIV = 0x00070000 19 | 20 | # Some early debug units use 13500000 Hz instead 21 | # Retail should have 16666666 Hz 22 | NV2A_CRYSTAL_FREQ = 16666666 # Hz 23 | 24 | NV_PTIMER = 0x9000 25 | NV_PTIMER_NUMERATOR = 0x200 26 | NV_PTIMER_DENOMINATOR = 0x210 27 | NV_PTIMER_TIME_0 = 0x400 28 | NV_PTIMER_TIME_1 = 0x410 29 | 30 | class Timer: 31 | 32 | def __init__(self, name): 33 | self.name = name 34 | self.base_ticks = 0 35 | self.last_ticks = None 36 | self.last_update = None 37 | 38 | def Adjust(self): 39 | self.base_ticks = self.GetTicks(False) 40 | 41 | def GetTicks(self, adjusted=True): 42 | return self.ticks - (self.base_ticks if adjusted else 0) 43 | 44 | def GetFrequency(self): 45 | return self.frequency 46 | 47 | def GetActualFrequency(self): 48 | return self.actual_frequency 49 | 50 | def UpdateFrequency(self): 51 | self.frequency = self.RetrieveFrequency() 52 | 53 | def UpdateTicks(self): 54 | update_time = time.time() 55 | self.ticks = self.RetrieveTicks() 56 | if self.last_ticks is not None and self.last_update_time is not None: 57 | if update_time > self.last_update_time: 58 | self.actual_frequency = (self.ticks - self.last_ticks) / (update_time - self.last_update_time) 59 | self.last_ticks = self.ticks 60 | self.last_update_time = update_time 61 | 62 | def Print(self): 63 | ticks = self.GetTicks() 64 | frequency = self.GetFrequency() 65 | #FIXME: Also show non-adjusted ticks somewhere? 66 | if frequency > 0: 67 | actual_frequency = self.GetActualFrequency() 68 | realtime_information = " = %5.1f s [f: %12.1f Hz]" % (ticks / frequency, actual_frequency) 69 | else: 70 | realtime_information = "" 71 | print("%-14s %12d [f: %12.1f Hz]%s" % (self.name + ":", ticks, frequency, realtime_information)) 72 | 73 | 74 | class KeTickCountTimer(Timer): 75 | 76 | def __init__(self): 77 | super(KeTickCountTimer, self).__init__("KeTickCount") 78 | 79 | def RetrieveFrequency(self): 80 | return 1000.0 81 | 82 | def RetrieveTicks(self): 83 | return memory.read_u32(ke.KeTickCount()) 84 | 85 | class RDTSCTimer(Timer): 86 | 87 | def __init__(self): 88 | super(RDTSCTimer, self).__init__("RDTSC") 89 | code = bytes([ 90 | 0x0F, 0x31, # rdtsc 91 | 0x8B, 0x4C, 0x24, 0x04, # mov ecx,DWORD PTR [esp+0x4] 92 | 0x89, 0x01, # mov DWORD PTR [ecx],eax 93 | 0x89, 0x51, 0x04, # mov DWORD PTR [ecx+0x4],edx 94 | 0xC2, 0x04, 0x00 # ret 0x4 95 | ]) 96 | pointer = ke.MmAllocateContiguousMemory(len(code) + 8) 97 | memory.write(pointer, code) 98 | self.code = pointer 99 | self.data = pointer + len(code) 100 | 101 | def RetrieveFrequency(self): 102 | return 2200000000.0 / 3.0 # 733.3 MHz 103 | 104 | def RetrieveTicks(self): 105 | api.call(self.code, struct.pack("> 8 121 | self.pdiv = (self.nvpll_coeff & NV_PRAMDAC_NVPLL_COEFF_PDIV) >> 16 122 | try: 123 | self.GPUClockrate = (NV2A_CRYSTAL_FREQ * self.ndiv) / (1 << self.pdiv) / self.mdiv 124 | return self.GPUClockrate / (self.GPUNumerator / self.GPUDenominator) 125 | except: 126 | self.GPUClockrate = 0 127 | return 0 128 | 129 | def RetrieveTicks(self): 130 | GPUTimer0 = nv2a.read_u32(NV_PTIMER + NV_PTIMER_TIME_0) 131 | GPUTimer1 = nv2a.read_u32(NV_PTIMER + NV_PTIMER_TIME_1) 132 | GPUTimer0dec = (GPUTimer0 >> 5) & 0x7FFFFFF 133 | GPUTimer1dec = (GPUTimer1 & 0x1FFFFFFF) << 27 134 | return GPUTimer1dec | GPUTimer0dec 135 | 136 | def Print(self): 137 | super().Print() 138 | print(" Core clockrate: ( XTAL * n) / (1 << p) / m") 139 | print(" (%-8d * %3d) / (1 << %3d) / %3d = %.1f Hz" % (NV2A_CRYSTAL_FREQ, self.ndiv, self.pdiv, self.mdiv, self.GPUClockrate)) 140 | print(" Timer clockrate: %.1f / (%d / %d)" % (self.GPUClockrate, self.GPUNumerator, self.GPUDenominator)) 141 | 142 | 143 | if __name__ == "__main__": 144 | 145 | timers = [] 146 | 147 | timers.append(RDTSCTimer()) 148 | timers.append(KeTickCountTimer()) 149 | timers.append(GPUTimer()) 150 | 151 | for timer in timers: 152 | timer.UpdateFrequency() 153 | timer.UpdateTicks() 154 | timer.Adjust() #FIXME: Make this optional 155 | 156 | while(True): 157 | 158 | # Setup a temporary stdout, so we can buffer the print for all timers 159 | real_stdout = sys.stdout 160 | sys.stdout = buffer_stdout = StringIO() 161 | 162 | # Update and print each timer 163 | for timer in timers: 164 | timer.UpdateTicks() 165 | timer.Print() 166 | 167 | # Switch to the real stdout again to output all timers at once 168 | sys.stdout = real_stdout 169 | print(buffer_stdout.getvalue(), flush=True) 170 | 171 | time.sleep(0.02) 172 | -------------------------------------------------------------------------------- /ss-parser/main.c: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Jannik Vogel 2 | // Licensed under GPLv3 or any later version. 3 | // Refer to the LICENSE.txt file included. 4 | 5 | // Based on original code by Truman Hy, Jackal and iR0b0t. 6 | 7 | //#include "ss.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | static uint32_t psn_to_lba(uint32_t psn) { 20 | const uint32_t dvd_layerbreak = 0x030000; 21 | const int32_t layerbreak_offset = 1913776; 22 | uint32_t xbox_dvd_layerbreak = dvd_layerbreak + layerbreak_offset; 23 | if(psn < xbox_dvd_layerbreak) { 24 | // Layer 0 PSN to LBA. 25 | return psn - dvd_layerbreak; 26 | } else { 27 | // Layer 1 PSN to LBA. 28 | return (xbox_dvd_layerbreak) * 2 - ((psn ^ 0xFFFFFF) + 1) - dvd_layerbreak; 29 | } 30 | return 0; 31 | } 32 | 33 | static uint32_t read24(const uint8_t data[3]) { 34 | return (data[0] << 16) | (data[1] << 8) | data[2]; 35 | } 36 | 37 | #if 0 38 | SSType get_ss_type(const uint8_t* ss) { 39 | //Get last layer_0 sector PSN 40 | uint32_t layer0_last_psn = (ss[13] << 16) | (ss[14] << 8) | ss[15]; 41 | 42 | switch(layer0_last_psn) { 43 | case 0x2033AF: return Xbox; 44 | case 0x20339F: return Xbox360; 45 | case 0x238E0F: return Xbox360_XGD3; 46 | default: break; 47 | } 48 | return Unknown; 49 | } 50 | #endif 51 | 52 | typedef struct { 53 | uint8_t valid; 54 | uint8_t challenge_id; 55 | uint32_t challenge_value; 56 | uint8_t response_modifier; 57 | uint32_t response_value; 58 | } __attribute__((packed)) ChallengeEntry; 59 | 60 | typedef struct { 61 | uint8_t unk1[3]; 62 | uint8_t start[3]; 63 | uint8_t end[3]; 64 | } __attribute__((packed)) SectorRange; 65 | 66 | typedef struct { 67 | uint64_t timestamp; // ?? 68 | uint32_t unk1; 69 | 70 | uint8_t unk1b[15]; // Always zero? 71 | 72 | uint8_t unk2; 73 | uint8_t unk3[16]; 74 | } __attribute__((packed)) Unk1; 75 | 76 | //FIXME: This must always be 720 bytes 77 | typedef struct { 78 | uint8_t unk1[720]; 79 | } __attribute__((packed)) PFI720; 80 | 81 | typedef struct { 82 | PFI720 pfi; 83 | uint32_t unk1; 84 | 85 | uint8_t unk1b[44]; // Always zero? 86 | 87 | uint8_t challenge_version; // 1?1 88 | uint8_t challenge_count; // 23?! 89 | ChallengeEntry challenges[23]; // Encrypted! 90 | 91 | uint8_t unk1c[32]; // Always zero? 92 | 93 | uint64_t timestamp; 94 | 95 | uint8_t unk1d[20]; // Always zero? 96 | 97 | uint8_t unk2[16]; 98 | 99 | uint8_t unk2b[84]; // Always zero? 100 | 101 | Unk1 unk3; // Hash is the encryption key for the challenge_entries 102 | uint8_t hash[20]; 103 | uint8_t signature[256]; 104 | 105 | Unk1 unk4; 106 | uint8_t hash2[20]; 107 | uint8_t signature2[64]; 108 | 109 | uint8_t unk5; // Always zero? 110 | 111 | uint8_t range_count; // 23?! 112 | SectorRange ranges1[23]; 113 | SectorRange ranges2[23]; 114 | 115 | uint8_t unk6; // Always zero? 116 | } __attribute__((packed)) SS; 117 | 118 | 119 | #define PRINT_OFFSET(type, field) \ 120 | printf("offset of " #type "." #field ": %u\n", (unsigned int)(uintptr_t)&((type*)NULL)->field); 121 | 122 | void shax(uint8_t* hash, const uint8_t* data, uint32_t len) { 123 | SHA_CTX c; 124 | SHA1_Init(&c); 125 | uint8_t hash_len[] = { 126 | len & 0xFF, 127 | (len >> 8) & 0xFF, 128 | (len >> 16) & 0xFF, 129 | (len >> 24) & 0xFF 130 | }; 131 | SHA1_Update(&c, (const void*)hash_len, sizeof(hash_len)); 132 | SHA1_Update(&c, (const void*)data, len); 133 | SHA1_Final(hash, &c); 134 | return; 135 | } 136 | 137 | int main(int argc, char* argv[]) { 138 | 139 | SS ss; 140 | FILE* f = fopen(argv[1], "rb"); 141 | size_t read_bytes = fread(&ss, 1, sizeof(ss), f); 142 | assert(read_bytes == sizeof(ss)); 143 | fclose(f); 144 | 145 | printf("sizeof: %d == %d\n", sizeof(SS), read_bytes); 146 | PRINT_OFFSET(SS, challenge_count); 147 | PRINT_OFFSET(SS, timestamp); 148 | PRINT_OFFSET(SS, unk2); 149 | PRINT_OFFSET(SS, unk3); 150 | PRINT_OFFSET(SS, unk4); 151 | PRINT_OFFSET(SS, range_count); 152 | PRINT_OFFSET(SS, ranges1); 153 | PRINT_OFFSET(SS, ranges2); 154 | 155 | printf("0x%02X\n", ((uint8_t*)&ss)[0]); 156 | 157 | PRINT_OFFSET(SS, hash); 158 | 159 | //for(unsigned int j = 1000; j < 2000; j++) { 160 | int j = 1227; //FIXME Does not work yet?! 161 | uint8_t hash[20]; 162 | shax(hash, (const unsigned char*)&ss, j); 163 | for(unsigned int i = 0; i < 20; i++) { 164 | printf("%02X", hash[i]); 165 | } 166 | printf(" %d\n", j); 167 | 168 | uint8_t* raw = &ss; 169 | for(unsigned int i = 0; i < 20; i++) { 170 | printf("%02X", /*ss.hash[i]*/ raw[j + i]); 171 | } 172 | printf("\n\n"); 173 | //} 174 | 175 | // Decrypt the challenge table! 176 | uint8_t sha1_hash[20]; 177 | SHA1(&raw[1183], 44, sha1_hash); 178 | 179 | // Now use the first part of the hash as RC4 key 180 | RC4_KEY key; 181 | RC4_set_key(&key, 7, sha1_hash); 182 | uint8_t output[253]; 183 | RC4(&key, 253, &raw[770], output); 184 | 185 | // Dump out the challenge entries 186 | ChallengeEntry* challenges = output; 187 | for(unsigned int i = 0; i < 23; i++) { 188 | printf("[%2u] %s valid=0x%02X challenge_id=0x%02X challenge_value=0x%08X response_modifier=0x%02X response_value=0x%08X\n", 189 | i, 190 | challenges[i].valid == 0x01 ? "***" : " ", 191 | challenges[i].valid, 192 | challenges[i].challenge_id, 193 | challenges[i].challenge_value, 194 | challenges[i].response_modifier, 195 | challenges[i].response_value); 196 | } 197 | 198 | // The 2048 byte Xbox1 decrypted security sector file contains 2 copies of the table with sector ranges: 199 | // 200 | // - table 1: 1633 to 1839 (207 bytes) 201 | // - table 2: 1840 to 2046 (207 bytes) 202 | // 203 | // The entries are 9 bytes wide, so there are 9x23 entries (or rows). The sectors are the last 2x3=6 bytes 204 | // of each row. On the Xbox1 there is only 16 sector ranges, so you only need to display the first 16 rows. 205 | 206 | for(unsigned int i = 0; i < 23; i++) { 207 | //Get PSN (Physical Sector Number). 208 | assert(!memcmp(&ss.ranges1[i], &ss.ranges2[i], sizeof(SectorRange))); 209 | //FIXME: psn_to_lba 210 | printf("[%2u] ", i); 211 | printf("unk: 0x%06X; ", read24(ss.ranges1[i].unk1)); //FIXME: Also print raw as I'm not sure what this is et 212 | //printf("%u\n", psn_to_lba(read24(ss.ranges1[i].start))); 213 | printf("start: 0x%06X; ", read24(ss.ranges1[i].start)); 214 | printf("end: 0x%06X\n", read24(ss.ranges1[i].end)); 215 | } 216 | 217 | return 0; 218 | } 219 | -------------------------------------------------------------------------------- /dump-xbox/common/x86.h: -------------------------------------------------------------------------------- 1 | static inline uint64_t rdmsr(uint32_t msr, uint32_t* pHigh, uint32_t* pLow) { 2 | uint32_t low, high; 3 | 4 | __asm__ __volatile__ ("rdmsr" 5 | :"=a"(low), "=d"(high) 6 | :"c"(msr)); 7 | 8 | uint64_t value = ((uint64_t)high << 32) | (uint64_t)low; 9 | if (pLow) { *pLow = low; } 10 | if (pHigh) { *pHigh = high; } 11 | return value; 12 | } 13 | 14 | static inline void wrmsr(uint32_t msr, uint32_t high, uint32_t low) { 15 | __asm__ __volatile__ ("wrmsr" 16 | : 17 | :"c"(msr),"a"(low),"d"(high)); 18 | return; 19 | } 20 | 21 | static inline void disableInterrupts(void) { 22 | __asm__ __volatile__("cli"); 23 | return; 24 | } 25 | 26 | static inline void enableInterrupts(void) { 27 | __asm__ __volatile__("sti"); 28 | return; 29 | } 30 | 31 | static inline void disableCache(void) { 32 | uint32_t mask = (1 << 30) | (1 << 29); // CD | NW 33 | __asm__ __volatile__ ("movl %%cr0,%%eax\n" 34 | "orl %%ecx,%%eax\n" 35 | "movl %%eax,%%cr0" 36 | : 37 | :"c"(mask) 38 | :"eax","memory"); 39 | return; 40 | } 41 | 42 | static inline void enableCache(void) { 43 | uint32_t mask = ~((1 << 30) | (1 << 29)); // CD | NW 44 | __asm__ __volatile__ ("movl %%cr0,%%eax\n" 45 | "andl %%ecx,%%eax\n" 46 | "movl %%eax,%%cr0" 47 | : 48 | :"c"(mask) 49 | :"eax","memory"); 50 | return; 51 | } 52 | 53 | static inline void disablePaging(void) { 54 | uint32_t mask = ~(1 << 31); // PG 55 | __asm__ __volatile__ ("movl %%cr0,%%eax\n" 56 | "andl %%ecx,%%eax\n" 57 | "movl %%eax,%%cr0" 58 | : 59 | :"c"(mask) 60 | :"eax","memory"); 61 | return; 62 | } 63 | 64 | static inline void enablePaging(void) { 65 | uint32_t mask = 1 << 31; // PG 66 | __asm__ __volatile__ ("movl %%cr0,%%eax\n" 67 | "orl %%ecx,%%eax\n" 68 | "movl %%eax,%%cr0" 69 | : 70 | :"c"(mask) 71 | :"eax","memory"); 72 | return; 73 | } 74 | 75 | static inline void disableWriteProtect(void) { 76 | uint32_t mask = ~(1 << 16); // WP 77 | __asm__ __volatile__ ("movl %%cr0,%%eax\n" 78 | "andl %%ecx,%%eax\n" 79 | "movl %%eax,%%cr0" 80 | : 81 | :"c"(mask) 82 | :"eax","memory"); 83 | return; 84 | } 85 | 86 | static inline void enableWriteProtect(void) { 87 | uint32_t mask = (1 << 16); // WP 88 | __asm__ __volatile__ ("movl %%cr0,%%eax\n" 89 | "orl %%ecx,%%eax\n" 90 | "movl %%eax,%%cr0" 91 | : 92 | :"c"(mask) 93 | :"eax","memory"); 94 | return; 95 | } 96 | 97 | static inline void flushCache(void) { 98 | __asm__ __volatile__("wbinvd"); 99 | return; 100 | } 101 | 102 | static inline void flushTlb(void) { 103 | __asm__ __volatile__("movl %%cr3, %%eax\n" 104 | "movl %%eax, %%cr3" 105 | : 106 | : 107 | :"eax","memory"); 108 | return; 109 | } 110 | 111 | static inline void breakpoint(void) { 112 | __asm__ __volatile__("int3"); 113 | return; 114 | } 115 | 116 | static inline uint16_t getCs(void) { 117 | uint16_t cs; 118 | __asm__ __volatile__ ("mov %%cs,%%ax" 119 | :"=a"(cs)); 120 | return cs; 121 | } 122 | 123 | static inline uint16_t getDs(void) { 124 | uint16_t ds; 125 | __asm__ __volatile__ ("mov %%ds,%%ax" 126 | :"=a"(ds)); 127 | return ds; 128 | } 129 | 130 | static inline uint16_t getSs(void) { 131 | uint16_t ss; 132 | __asm__ __volatile__ ("mov %%ss,%%ax" 133 | :"=a"(ss)); 134 | return ss; 135 | } 136 | 137 | static inline uint32_t getCr4(void) { 138 | uint32_t cr4; 139 | __asm__ __volatile__ ("mov %%cr4,%%eax" 140 | :"=a"(cr4)); 141 | return cr4; 142 | } 143 | 144 | static inline uint16_t getTr(void) { 145 | uint16_t tr; 146 | __asm__ __volatile__ ("str %%eax" 147 | :"=a"(tr)); 148 | return tr; 149 | } 150 | 151 | static inline void getGdt(uint16_t* pLimit, uintptr_t* pBase) { 152 | uint32_t low, high; 153 | 154 | __asm__ __volatile__ ("sub $8,%%esp\n" 155 | "sgdt 2(%%esp)\n" 156 | "pop %%eax\n" 157 | "pop %%edx" 158 | :"=a"(low), "=d"(high)); 159 | 160 | if (pBase) { *pBase = high; } 161 | if (pLimit) { *pLimit = low >> 16; } 162 | 163 | return; 164 | } 165 | 166 | static inline void setGdt(uint16_t limit, uintptr_t base) { 167 | __asm__ __volatile__ ("push %%edx\n" 168 | "push %%eax\n" 169 | "lgdt 2(%%esp)\n" 170 | "add $8,%%esp" 171 | : 172 | :"a"(limit << 16), "d"(base)); 173 | return; 174 | } 175 | 176 | static inline void setCr4(uint32_t cr4) { 177 | __asm__ __volatile__ ("mov %%eax, %%cr4" 178 | : 179 | :"a"(cr4)); 180 | return; 181 | } 182 | 183 | static inline void setTr(uint16_t tr) { 184 | __asm__ __volatile__ ("ltr %%ax" 185 | : 186 | :"a"(tr)); 187 | return; 188 | } 189 | 190 | static inline void setCs(uint16_t cs) { 191 | __asm__ __volatile__ ("push %%ax\n" 192 | "call ret1\n" 193 | "ret1:\n" 194 | "addl $(ret2 - ret1), (%%esp)\n" 195 | "retl\n" 196 | "ret2:" 197 | : 198 | :"a"(cs)); 199 | return; 200 | } 201 | 202 | static inline void lcall(uint16_t segment, uint32_t offset) { 203 | uint32_t destination[] = { 204 | offset, 205 | segment 206 | }; 207 | __asm__ __volatile__("lcall *(%%eax)" 208 | : 209 | :"a"(destination)); 210 | return; 211 | } 212 | 213 | void ret(void); 214 | __asm__("_ret: ret\n"); 215 | 216 | void iret(void); 217 | __asm__("_iret: iret\nret"); 218 | 219 | void iretLoop(void); 220 | __asm__("_iretLoop: iret\njmp _iretLoop"); 221 | 222 | uintptr_t getEip(void); 223 | __asm__("_getEip: pop %eax\nret"); 224 | -------------------------------------------------------------------------------- /adpcm-decoder/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "adpcm_block.h" 8 | 9 | 10 | int main(int argc, char* argv[]) { 11 | if (argc != 3) { 12 | printf("Usage: %s \n", argv[0]); 13 | return 1; 14 | } 15 | 16 | FILE* in = fopen(argv[1], "rb"); 17 | 18 | char riff_chunk_id[4]; 19 | fread(riff_chunk_id, 4, 1, in); 20 | if (strncmp(riff_chunk_id, "RIFF", 4) != 0) { 21 | fprintf(stderr, "Unexpected chunk: '%.4s'. Expected 'RIFF'\n", riff_chunk_id); 22 | return 1; 23 | } 24 | 25 | uint32_t riff_chunk_size; 26 | fread(&riff_chunk_size, 4, 1, in); 27 | printf("RIFF size: %d\n", riff_chunk_size); 28 | 29 | char wave_chunk_id[4]; 30 | fread(wave_chunk_id, 4, 1, in); 31 | if (strncmp(wave_chunk_id, "WAVE", 4) != 0) { 32 | fprintf(stderr, "Unexpected chunk: '%.4s'. Expected 'WAVE'\n", wave_chunk_id); 33 | return 1; 34 | } 35 | 36 | char fmt_chunk_id[4]; 37 | fread(fmt_chunk_id, 4, 1, in); 38 | if (strncmp(fmt_chunk_id, "fmt ", 4) != 0) { 39 | fprintf(stderr, "Unexpected chunk: '%.4s'. Expected 'fmt '\n", fmt_chunk_id); 40 | return 1; 41 | } 42 | 43 | uint32_t fmt_chunk_size; 44 | fread(&fmt_chunk_size, 4, 1, in); 45 | printf("fmt size: %d\n", fmt_chunk_size); 46 | 47 | uint16_t format; 48 | fread(&format, 2, 1, in); 49 | if ((format != 0x0011) && (format != 0x0069)) { 50 | fprintf(stderr, "Expected WAV format 0x0011 (IMA ADPCM) or 0x0069 (Xbox ADPCM). Got 0x%04X.\n", format); 51 | return 1; 52 | } 53 | 54 | uint16_t channels; 55 | fread(&channels, 2, 1, in); 56 | printf("Channels: %d\n", channels); 57 | if (channels != 1 && channels != 2) { 58 | fprintf(stderr, "Expected mono or stereo file. Got %u channels.\n", channels); 59 | return 1; 60 | } 61 | 62 | uint32_t samples_per_sec; 63 | fread(&samples_per_sec, 4, 1, in); 64 | printf("Sampling rate: %u Hz\n", samples_per_sec); 65 | 66 | uint32_t bytes_per_sec; 67 | fread(&bytes_per_sec, 4, 1, in); 68 | printf("Bandwidth: %u kiB/s\n", bytes_per_sec / 1024); 69 | 70 | uint16_t block_align; 71 | fread(&block_align, 2, 1, in); 72 | printf("Block align: %u Bytes\n", block_align); 73 | //assert(block_align <= 1); // FIXME: Add blockalign support 74 | 75 | uint16_t bits_per_sample; 76 | fread(&bits_per_sample, 2, 1, in); 77 | if (bits_per_sample != 4) { 78 | fprintf(stderr, "Expected 4 bits per sample. Got %u.\n", bits_per_sample); 79 | return 1; 80 | } 81 | 82 | uint16_t extra_size; 83 | fread(&extra_size, 2, 1, in); 84 | if (extra_size != 2) { 85 | fprintf(stderr, "Expected 2 bytes of extra data. Got %u.\n", extra_size); 86 | return 1; 87 | } 88 | 89 | uint16_t channel_samples_per_block; 90 | fread(&channel_samples_per_block, 2, 1, in); 91 | if (channel_samples_per_block != 64) { 92 | fprintf(stderr, "Expected 64 channel samples per block. Got %u.\n", channel_samples_per_block); 93 | return 1; 94 | } 95 | //FIXME: What's in the extra-data? 96 | 97 | //FIXME: Skip possible fact headers etc 98 | 99 | char data_chunk_id[4]; 100 | fread(data_chunk_id, 4, 1, in); 101 | if (strncmp(data_chunk_id, "data", 4) != 0) { 102 | fprintf(stderr, "Unexpected chunk: '%.4s'. Expected 'data'\n", data_chunk_id); 103 | return 1; 104 | } 105 | 106 | uint32_t data_chunk_size; 107 | fread(&data_chunk_size, 4, 1, in); 108 | printf("Data size: %u Bytes\n", data_chunk_size); 109 | 110 | // Mark start of output 111 | printf("\n"); 112 | 113 | FILE* out = fopen(argv[2], "wb"); 114 | 115 | // Calculate the number of samples: 116 | // * First get the number of blocks. 117 | // * In each block we have 4 bytes of header per channel. 118 | // * In each block we have 2 samples per byte. 119 | // * One sample per channel from initial predictor. 120 | // (= 65 samples per block). 121 | uint32_t blocks = data_chunk_size / block_align; 122 | printf("Duration: %u Blocks\n", blocks); 123 | uint32_t samples_per_block = (block_align - 4 * channels) * 2 + channels; 124 | 125 | // Check if the header lied to us 126 | channel_samples_per_block = samples_per_block / channels; 127 | if (channel_samples_per_block != 65) { 128 | fprintf(stderr, "Channel samples per block differs from header. Got %u.\n"); 129 | return 1; 130 | } 131 | printf("Duration per block: %d Samples\n", channel_samples_per_block); 132 | 133 | uint32_t samples = blocks * samples_per_block; 134 | printf("Duration: %u Samples\n", samples / channels); 135 | 136 | // Calculate size of decoded data 137 | uint32_t data_size = samples * 2; 138 | 139 | // Write wave header 140 | uint8_t header[] = { 141 | 'R', 'I', 'F', 'F', 0x00, 0x00, 0x00, 0x00, 'W', 'A', 142 | 'V', 'E', 'f', 'm', 't', ' ', 0x10, 0x00, 0x00, 0x00, 143 | 0x01, 0x00, 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 144 | 0x02, 0x00, 0x04, 0x00, 0x10, 0x00, 'd', 'a', 't', 'a', 145 | 0x00, 0x00, 0x00, 0x00 146 | }; 147 | 148 | bits_per_sample = 16; 149 | 150 | *(uint32_t*)&header[4] = data_size + 36; 151 | *(uint16_t*)&header[22] = channels; 152 | *(uint32_t*)&header[24] = samples_per_sec; 153 | *(uint32_t*)&header[28] = channels * bits_per_sample / 8 * samples_per_sec; 154 | *(uint16_t*)&header[32] = channels * bits_per_sample / 8; 155 | *(uint16_t*)&header[34] = bits_per_sample; 156 | *(uint32_t*)&header[40] = data_size; 157 | 158 | fwrite(&header, sizeof(header), 1, out); 159 | 160 | // Allocate space for samples 161 | uint8_t* block = malloc(block_align); 162 | int16_t* sample_out = malloc(channel_samples_per_block * channels * 2); 163 | 164 | #if 0 165 | // Chunk data which contains blocks of samples 166 | for(unsigned int k = 0; k < blocks; k++) { 167 | 168 | // Read and decode the block 169 | fread(block, block_align, 1, in); 170 | if (channels == 2) { 171 | adpcm_decode_stereo_block(&sample_out[0], &sample_out[channel_samples_per_block], block, 0, channel_samples_per_block - 1); 172 | } else { 173 | adpcm_decode_mono_block(sample_out, block, 0, channel_samples_per_block - 1); 174 | } 175 | 176 | // Interleave the 8 samples for PCM out 177 | for(unsigned int l = 0; l < channel_samples_per_block; l++) { 178 | for(unsigned int j = 0; j < channels; j++) { 179 | fwrite(&sample_out[j * channel_samples_per_block + l], 2, 1, out); 180 | } 181 | } 182 | 183 | } 184 | #else 185 | // Chunk data which contains blocks of samples 186 | for(unsigned int k = 0; k < samples; k++) { 187 | 188 | // Read and decode the block 189 | unsigned int i = k % 65; 190 | if (i == 0) { 191 | fread(block, block_align, 1, in); 192 | } 193 | if (channels == 2) { 194 | adpcm_decode_stereo_block(&sample_out[0], &sample_out[channel_samples_per_block], block, i, i); 195 | } else { 196 | adpcm_decode_mono_block(sample_out, block, i, i); 197 | } 198 | 199 | // Interleave the 8 samples for PCM out 200 | for(unsigned int j = 0; j < channels; j++) { 201 | fwrite(&sample_out[j * channel_samples_per_block], 2, 1, out); 202 | } 203 | 204 | } 205 | #endif 206 | 207 | free(sample_out); 208 | free(block); 209 | 210 | fclose(in); 211 | fclose(out); 212 | 213 | return 0; 214 | } 215 | -------------------------------------------------------------------------------- /python-scripts/inspect_apu_vp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from xboxpy import * 4 | 5 | #FIXME: Move to fields, however, globals() has another context there >.< 6 | def Field(offset_name, mask_name=None): 7 | offset_value = globals().get(offset_name) 8 | if offset_value == None: 9 | raise NameError(offset_name) 10 | if mask_name == None: 11 | mask_value = 0xFFFFFFFF 12 | else: 13 | mask_name = offset_name + '_' + mask_name 14 | mask_value = globals().get(mask_name) 15 | if mask_value == None: 16 | raise NameError(mask_name) 17 | mask_value = GetMask(mask_value) 18 | return(offset_value, mask_value) 19 | 20 | def ReadVoiceField(voice, field_name, mask_name=None, comment=None, show=True): 21 | real_field_name = 'NV_PAVS_VOICE_' + field_name 22 | field = Field(real_field_name, mask_name) 23 | value = apu.ReadVoice(voice, *field) 24 | if show: 25 | mask_length = (field[1] >> ffs(field[1])).bit_length() 26 | if mask_length == 1: #FIXME: If mask is 1 bit only, display bool 27 | value_str = str(value > 0) 28 | else: 29 | #FIXME: Format by mask length 30 | value_str = "0x" + format(value, 'X') 31 | #FIXME: Use decimal if asked to do so 32 | #FIXME: append comment if there is one 33 | print(field_name + (("." + mask_name) if mask_name != None else "") + ": " + value_str) 34 | return value 35 | 36 | 37 | def ProcessStream(voice): 38 | ssla_count = ReadVoiceField(voice, 'CUR_PSL_START', 'SSLA_COUNT') 39 | ssla_base = ReadVoiceField(voice, 'CUR_PSL_START', 'SSLA_BASE') 40 | cso = ReadVoiceField(voice,'PAR_OFFSET', 'CSO') 41 | sslb_count = ReadVoiceField(voice, 'PAR_NEXT', 'SSLB_COUNT') 42 | sslb_base = ReadVoiceField(voice, 'PAR_NEXT', 'SSLB_BASE') 43 | 44 | #FIXME: More research.. this might be wrong 45 | #FIXME: Read internal memory to figure out where the SSL data is stored? 46 | #FIXME: Read VPSSLADDR as it stores sample data for SSL? 47 | 48 | pass 49 | 50 | last_list = None 51 | def ListVoices(voice, name): 52 | global last_list 53 | if name != last_list: 54 | print('Starting ' + name + ' list') 55 | last_list = name 56 | 57 | is_stream = ReadVoiceField(voice, 'CFG_FMT', 'DATA_TYPE', show=False) > 0 58 | 59 | # Enable this / modify this if you only want to show one type: buffers or streams 60 | if False: 61 | if not is_stream: 62 | next_voice = ReadVoiceField(voice, 'TAR_PITCH_LINK', 'NEXT_VOICE_HANDLE', show=False) 63 | return next_voice 64 | 65 | print("") 66 | print("Voice: 0x" + format(voice, '04X')) 67 | 68 | vbin = ReadVoiceField(voice, 'CFG_VBIN', show=False) 69 | # lots of unk stuff 70 | 71 | #FIXME: Don't print 72 | voice_fmt = ReadVoiceField(voice, 'CFG_FMT', show=False) 73 | 74 | samples_per_block = ReadVoiceField(voice, 'CFG_FMT', 'SAMPLES_PER_BLOCK') 75 | 76 | print("Data type: " + ("stream" if is_stream else "buffer")) 77 | 78 | 79 | loop = ReadVoiceField(voice, 'CFG_FMT', 'LOOP') > 0 80 | 81 | # (26,26) ??? 82 | 83 | is_stereo = ReadVoiceField(voice, 'CFG_FMT', 'STEREO') > 0 84 | 85 | NV_PAVS_VOICE_CFG_FMT_SAMPLE_SIZE_values = [ 'U8', 'S16', 'S24', 'S32' ] 86 | sample_size = ReadVoiceField(voice, 'CFG_FMT', 'SAMPLE_SIZE', show=False) 87 | print("Sample size: " + NV_PAVS_VOICE_CFG_FMT_SAMPLE_SIZE_values[sample_size]) 88 | 89 | NV_PAVS_VOICE_CFG_FMT_CONTAINER_SIZE_values = ['B8', 'B16', 'ADPCM', 'B32'] 90 | container_size = ReadVoiceField(voice, 'CFG_FMT', 'CONTAINER_SIZE', show=False) 91 | print("Container size: " + NV_PAVS_VOICE_CFG_FMT_CONTAINER_SIZE_values[container_size]) 92 | # lots of unk stuff 93 | 94 | paused = ReadVoiceField(voice, 'PAR_STATE', 'PAUSED') 95 | active = ReadVoiceField(voice, 'PAR_STATE', 'ACTIVE_VOICE') 96 | 97 | cur_vola = ReadVoiceField(voice, 'CUR_VOLA', show=False) 98 | cur_volb = ReadVoiceField(voice, 'CUR_VOLB', show=False) 99 | cur_volc = ReadVoiceField(voice, 'CUR_VOLC', show=False) 100 | 101 | tar_vola = ReadVoiceField(voice, 'TAR_VOLA', show=False) 102 | tar_volb = ReadVoiceField(voice, 'TAR_VOLB', show=False) 103 | tar_volc = ReadVoiceField(voice, 'TAR_VOLC', show=False) 104 | 105 | bins_data = [] 106 | bins_data.append(vbin & 0x1F) # 0 107 | bins_data.append((vbin >> 5) & 0x1F) # 1 108 | bins_data.append((vbin >> 10) & 0x1F) # 2 109 | # (15,15) ? 110 | bins_data.append((vbin >> 16) & 0x1F) # 3 111 | bins_data.append((vbin >> 21) & 0x1F) # 4 112 | bins_data.append((vbin >> 26) & 0x1F) # 5 113 | # (31,31) ? 114 | bins_data.append(voice_fmt & 0x1F) # 6 115 | bins_data.append((voice_fmt >> 5) & 0x1F) # 7 116 | 117 | def decode_volumes(a, b, c): 118 | data = [] 119 | data.append((a >> 4) & 0xFFF) # 0 120 | data.append((a >> 20) & 0xFFF) # 1 121 | data.append((b >> 4) & 0xFFF) # 2 122 | data.append((b >> 20) & 0xFFF) # 3 123 | data.append((c >> 4) & 0xFFF) # 4 124 | data.append((c >> 20) & 0xFFF) # 5 125 | data.append(((c & 0xF) << 8) | ((b & 0xF) << 4) | (a & 0xF)) # 6 126 | data.append(((c >> 8) & 0xF00) | ((b >> 12) & 0xF0) | ((a >> 16) & 0xF)) # 7 127 | return data 128 | cur_vols_data = decode_volumes(cur_vola, cur_volb, cur_volc) 129 | tar_vols_data = decode_volumes(tar_vola, tar_volb, tar_volc) 130 | 131 | #assert(False) #FIXME: Untested! 132 | 133 | bins = "" 134 | cur_vols = "" 135 | tar_vols = "" 136 | for i in range(0, 8): 137 | bins += " " + ("0x" + format(bins_data[i], 'X')).ljust(5) 138 | cur_vols += " 0x" + format(cur_vols_data[i], '03X') 139 | tar_vols += " 0x" + format(tar_vols_data[i], '03X') 140 | print("VBIN " + bins) 141 | print("CUR_VOL" + cur_vols) 142 | print("TAR_VOL" + tar_vols) 143 | 144 | pitch = ReadVoiceField(voice, 'TAR_PITCH_LINK', 'PITCH') 145 | # Make sure our pitch is signed 146 | pitch = int.from_bytes(int.to_bytes(pitch, signed=False, byteorder='little', length=2), signed=True, byteorder='little') 147 | freq = apu.PitchToFrequency(pitch) 148 | print("Frequency: " + str(freq) + " Hz") 149 | 150 | # Envelopes 151 | 152 | def DumpEnvelope(prefix1, prefix2, reg): 153 | ReadVoiceField(voice, prefix1, reg + '_DELAYTIME') 154 | ReadVoiceField(voice, prefix1, reg + '_ATTACKRATE') 155 | ReadVoiceField(voice, prefix2, reg + '_HOLDTIME') 156 | ReadVoiceField(voice, prefix2, reg + '_DECAYRATE') 157 | ReadVoiceField(voice, prefix2, reg + '_SUSTAINLEVEL') 158 | 159 | # Envelope for volume 160 | print("Volume Envelope:") 161 | DumpEnvelope('CFG_ENV0', 'CFG_ENVA', 'EA') 162 | ReadVoiceField(voice, 'TAR_LFO_ENV', 'EA_RELEASERATE') 163 | ReadVoiceField(voice, 'PAR_STATE','EACUR') 164 | ReadVoiceField(voice, 'CUR_ECNT','EACOUNT') 165 | 166 | # Envelope for pitch 167 | print("Pitch / Cutoff Envelope:") 168 | DumpEnvelope('CFG_ENV1', 'CFG_ENVF', 'EF') 169 | ReadVoiceField(voice, 'CFG_MISC', 'EF_RELEASERATE') 170 | ReadVoiceField(voice, 'PAR_STATE','EFCUR') 171 | ReadVoiceField(voice, 'CUR_ECNT','EFCOUNT') 172 | ReadVoiceField(voice, 'CFG_ENV0', 'EF_PITCHSCALE') 173 | ReadVoiceField(voice, 'CFG_ENV1', 'EF_FCSCALE') 174 | 175 | if is_stream: 176 | 177 | ProcessStream(voice) 178 | 179 | else: 180 | 181 | psl_start_ba = ReadVoiceField(voice, 'CUR_PSL_START','BA') 182 | cur_psh_sample = ReadVoiceField(voice, 'CUR_PSH_SAMPLE','LBO') 183 | par_offset = ReadVoiceField(voice, 'PAR_OFFSET','CBO') # Warning: Upper 8 bits will be 0xFF (?) on hw! 184 | ebo = ReadVoiceField(voice, 'PAR_NEXT','EBO') 185 | 186 | # FIXME: Move to apu.py with callbacks? 187 | def DumpVoiceBuffer(path): 188 | #FIXME: Respect samples per block 189 | #FIXME: Rewrite, this is overall pretty horrible 190 | channels = 2 if is_stereo else 1 191 | container_size_values = [1, 2, 4, 4] 192 | in_sample_size = container_size_values[container_size] 193 | fmt = 0x0011 if container_size == 2 else 0x0001 194 | wav = aci.export_wav(path, channels, in_sample_size, freq, fmt) 195 | 196 | samples = ebo + 1 197 | if fmt == 0x0011: # Check for ADPCM 198 | block_size = 36 * channels 199 | bytes = samples // 65 * block_size 200 | else: 201 | block_size = in_sample_size * channels 202 | bytes = samples * block_size 203 | 204 | data = apu.ReadSGE(psl_start_ba, bytes) 205 | wav.writeframes(data) 206 | 207 | wav.close() 208 | 209 | #FIXME: Make this an option 210 | if True: 211 | print("Dumping buffer") 212 | DumpVoiceBuffer("vp-buffer-" + format(psl_start_ba, '08X') + ".wav") 213 | 214 | next_voice = ReadVoiceField(voice, 'TAR_PITCH_LINK', 'NEXT_VOICE_HANDLE') 215 | return next_voice 216 | 217 | apu.IterateVoiceLists(ListVoices) 218 | -------------------------------------------------------------------------------- /controller-tool/steel-battalion.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "vid": "0A7B", 4 | "pid": "D000", 5 | 6 | "in": { 7 | "RightJoyMainWeapon": { 8 | "offset": 2, 9 | "mask": "01" 10 | }, 11 | "RightJoyFire": { 12 | "offset": 2, 13 | "mask": "03", 14 | "notes": "FIXME: WTF?! Mask might be bad?" 15 | }, 16 | "RightJoyLockOn": { 17 | "offset": 2, 18 | "mask": "04" 19 | }, 20 | "Eject": { 21 | "offset": 2, 22 | "mask": "08" 23 | }, 24 | "CockpitHatch": { 25 | "offset": 2, 26 | "mask": "10" 27 | }, 28 | "Ignition": { 29 | "offset": 2, 30 | "mask": "20" 31 | }, 32 | "Start": { 33 | "offset": 2, 34 | "mask": "40" 35 | }, 36 | "MultiMonOpenClose": { 37 | "offset": 2, 38 | "mask": "80" 39 | }, 40 | "MultiMonMapZoomInOut": { 41 | "offset": 3, 42 | "mask": "01" 43 | }, 44 | "MultiMonModeSelect": { 45 | "offset": 3, 46 | "mask": "02" 47 | }, 48 | "MultiMonSubMonitor": { 49 | "offset": 3, 50 | "mask": "04" 51 | }, 52 | "MainMonZoomIn": { 53 | "offset": 3, 54 | "mask": "08" 55 | }, 56 | "MainMonZoomOut": { 57 | "offset": 3, 58 | "mask": "10" 59 | }, 60 | "FunctionFSS": { 61 | "offset": 3, 62 | "mask": "20" 63 | }, 64 | "FunctionManipulator": { 65 | "offset": 3, 66 | "mask": "40" 67 | }, 68 | "FunctionLineColorChange": { 69 | "offset": 3, 70 | "mask": "80" 71 | }, 72 | "Washing": { 73 | "offset": 4, 74 | "mask": "01" 75 | }, 76 | "Extinguisher": { 77 | "offset": 4, 78 | "mask": "02" 79 | }, 80 | "Chaff": { 81 | "offset": 4, 82 | "mask": "04" 83 | }, 84 | "FunctionTankDetach": { 85 | "offset": 4, 86 | "mask": "08" 87 | }, 88 | "FunctionOverride": { 89 | "offset": 4, 90 | "mask": "10" 91 | }, 92 | "FunctionNightScope": { 93 | "offset": 4, 94 | "mask": "20" 95 | }, 96 | "FunctionF1": { 97 | "offset": 4, 98 | "mask": "40" 99 | }, 100 | "FunctionF2": { 101 | "offset": 4, 102 | "mask": "80" 103 | }, 104 | "FunctionF3": { 105 | "offset": 5, 106 | "mask": "01" 107 | }, 108 | "WeaponConMain": { 109 | "offset": 5, 110 | "mask": "02" 111 | }, 112 | "WeaponConSub": { 113 | "offset": 5, 114 | "mask": "04" 115 | }, 116 | "WeaponConMagazine": { 117 | "offset": 5, 118 | "mask": "08" 119 | }, 120 | "Comm1": { 121 | "offset": 5, 122 | "mask": "10" 123 | }, 124 | "Comm2": { 125 | "offset": 5, 126 | "mask": "20" 127 | }, 128 | "Comm3": { 129 | "offset": 5, 130 | "mask": "40" 131 | }, 132 | "Comm4": { 133 | "offset": 5, 134 | "mask": "80" 135 | }, 136 | "Comm5": { 137 | "offset": 6, 138 | "mask": "01" 139 | }, 140 | "LeftJoySightChange": { 141 | "offset": 6, 142 | "mask": "02" 143 | }, 144 | "ToggleFilterControl": { 145 | "offset": 6, 146 | "mask": "04" 147 | }, 148 | "ToggleOxygenSupply": { 149 | "offset": 6, 150 | "mask": "08" 151 | }, 152 | "ToggleFuelFlowRate": { 153 | "offset": 6, 154 | "mask": "10" 155 | }, 156 | "ToggleBuffreMaterial": { 157 | "offset": 6, 158 | "mask": "20" 159 | }, 160 | "ToggleVTLocation": { 161 | "offset": 6, 162 | "mask": "40" 163 | }, 164 | "unk6": { 165 | "offset": 6, 166 | "mask": "80" 167 | }, 168 | "unk7": { 169 | "offset": 7, 170 | "mask": "FF" 171 | }, 172 | "unk8": { 173 | "offset": 8, 174 | "mask": "FF" 175 | }, 176 | "AimingX": { 177 | "offset": 9, 178 | "mask": "FF", 179 | "notes": "maybe 0xFFFF at offset 8? 'Aiming Lever' joystick on the right. X Axis value." 180 | }, 181 | "AimingY": { 182 | "offset": 11, 183 | "mask": "FF", 184 | "notes": "maybe 0xFFFF at offset 10? 'Aiming Lever' joystick on the right. Y Axis value." 185 | }, 186 | "RotationLever": { 187 | "offset": 13, 188 | "mask": "FF", 189 | "notes": "maybe 0xFFFF at offset 12? 'Rotation Lever' joystick on the left." 190 | }, 191 | "SightChangeX": { 192 | "offset": 15, 193 | "mask": "FF", 194 | "notes": "maybe 0xFFFF at offset 14? 'Sight Change' analog stick on the 'Rotation Lever' joystick. X Axis value." 195 | }, 196 | "SightChangeY": { 197 | "offset": 17, 198 | "mask": "FF", 199 | "notes": "maybe 0xFFFF at offset 16? 'Sight Change' analog stick on the 'Rotation Lever' joystick. Y Axis value." 200 | }, 201 | "LeftPedal": { 202 | "offset": 19, 203 | "mask": "FF", 204 | "notes": "maybe 0xFFFF at offset 18?" 205 | }, 206 | "MiddlePedal": { 207 | "offset": 21, 208 | "mask": "FF", 209 | "notes": "maybe 0xFFFF at offset 20?" 210 | }, 211 | "RightPedal": { 212 | "offset": 23, 213 | "mask": "FF", 214 | "notes": "maybe 0xFFFF at offset 22?" 215 | }, 216 | "TunerDial": { 217 | "offset": 24, 218 | "mask": "0F", 219 | "notes": "The 9 o'clock postion is 0, and the 6 o'clock position is 12. The blank area between the 6 and 9 o'clock positions is 13, 14, and 15 clockwise." 220 | }, 221 | "unk24": { 222 | "offset": 24, 223 | "mask": "F0" 224 | }, 225 | "GearLever": { 226 | "offset": 25, 227 | "mask": "FF", 228 | "notes": "The gear lever on the left block." 229 | } 230 | }, 231 | 232 | "out": { 233 | "EmergencyEject": { 234 | "offset": 2, 235 | "mask": "0F" 236 | }, 237 | "CockpitHatch": { 238 | "offset": 2, 239 | "mask": "F0" 240 | }, 241 | "Ignition": { 242 | "offset": 3, 243 | "mask": "0F" 244 | }, 245 | "Start": { 246 | "offset": 3, 247 | "mask": "F0" 248 | }, 249 | "OpenClose": { 250 | "offset": 4, 251 | "mask": "0F" 252 | }, 253 | "MapZoomInOut": { 254 | "offset": 4, 255 | "mask": "F0" 256 | }, 257 | "ModeSelect": { 258 | "offset": 5, 259 | "mask": "0F" 260 | }, 261 | "SubMonitorModeSelect": { 262 | "offset": 5, 263 | "mask": "F0" 264 | }, 265 | "MainMonitorZoomIn": { 266 | "offset": 6, 267 | "mask": "0F" 268 | }, 269 | "MainMonitorZoomOut": { 270 | "offset": 6, 271 | "mask": "F0" 272 | }, 273 | "ForecastShootingSystem": { 274 | "offset": 7, 275 | "mask": "0F" 276 | }, 277 | "Manipulator": { 278 | "offset": 7, 279 | "mask": "F0" 280 | }, 281 | "LineColorChange": { 282 | "offset": 8, 283 | "mask": "0F" 284 | }, 285 | "Washing": { 286 | "offset": 8, 287 | "mask": "F0" 288 | }, 289 | "Extinguisher": { 290 | "offset": 9, 291 | "mask": "0F" 292 | }, 293 | "Chaff": { 294 | "offset": 9, 295 | "mask": "F0" 296 | }, 297 | "TankDetach": { 298 | "offset": 10, 299 | "mask": "0F" 300 | }, 301 | "Override": { 302 | "offset": 10, 303 | "mask": "F0" 304 | }, 305 | "NightScope": { 306 | "offset": 11, 307 | "mask": "0F" 308 | }, 309 | "F1": { 310 | "offset": 11, 311 | "mask": "F0" 312 | }, 313 | "F2": { 314 | "offset": 12, 315 | "mask": "0F" 316 | }, 317 | "F3": { 318 | "offset": 12, 319 | "mask": "F0" 320 | }, 321 | "MainWeaponControl": { 322 | "offset": 13, 323 | "mask": "0F" 324 | }, 325 | "SubWeaponControl": { 326 | "offset": 13, 327 | "mask": "F0" 328 | }, 329 | "MagazineChange": { 330 | "offset": 14, 331 | "mask": "0F" 332 | }, 333 | "Comm1": { 334 | "offset": 14, 335 | "mask": "F0" 336 | }, 337 | "Comm2": { 338 | "offset": 15, 339 | "mask": "0F" 340 | }, 341 | "Comm3": { 342 | "offset": 15, 343 | "mask": "F0" 344 | }, 345 | "Comm4": { 346 | "offset": 16, 347 | "mask": "0F" 348 | }, 349 | "Comm5": { 350 | "offset": 16, 351 | "mask": "F0" 352 | }, 353 | "unk17": { 354 | "offset": 17, 355 | "mask": "0F" 356 | }, 357 | "GearR": { 358 | "offset": 17, 359 | "mask": "F0" 360 | }, 361 | "GearN": { 362 | "offset": 18, 363 | "mask": "0F" 364 | }, 365 | "Gear1": { 366 | "offset": 18, 367 | "mask": "F0" 368 | }, 369 | "Gear2": { 370 | "offset": 19, 371 | "mask": "0F" 372 | }, 373 | "Gear3": { 374 | "offset": 19, 375 | "mask": "F0" 376 | }, 377 | "Gear4": { 378 | "offset": 20, 379 | "mask": "0F" 380 | }, 381 | "Gear5": { 382 | "offset": 20, 383 | "mask": "F0" 384 | } 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /python-scripts/usb_gamepad.py: -------------------------------------------------------------------------------- 1 | #/******************************************************************************/ 2 | #/* */ 3 | #/* File: xinput.cpp */ 4 | #/* Auth: bkenwright@xbdev.net */ 5 | #/* Desc: GamePad entry code for use on the xbox. */ 6 | #/* */ 7 | #/******************************************************************************/ 8 | 9 | #include "misc.h" 10 | #include "ohci.h" 11 | #include "hub.h" 12 | #include "pad.h" 13 | 14 | 15 | 16 | #/* http://www.beyondlogic.org/usbnutshell/usb5.htm */ 17 | 18 | 19 | 20 | Debug = True 21 | 22 | 23 | 24 | 25 | extern __u8 found; // declared at the top of ohci.cpp 26 | 27 | 28 | 29 | 30 | //extern void InitialiseD3D(); 31 | //extern void DisplayText(char * szStr, long xpos=100, long ypos=100 ); 32 | //extern void DataDisplay(void* data); 33 | 34 | 35 | #/* OHCI Code */ 36 | #/* */ 37 | #/* We use our ohci library functions here, from ohci.h/cpp,...to scan for */ 38 | #/* devices and changes etc...get how many are plugged in..what type they are */ 39 | #/* simple and sweet. */ 40 | 41 | def xInitInput(stXINPUT* p): 42 | __u8 Buffer[256]; 43 | 44 | 45 | ohci_t * my_ohci = &(p->my_ohci); 46 | 47 | 48 | 49 | Ports = FindOHC( my_ohci, (void*)0xFED00000) 50 | 51 | 52 | if Debug: 53 | if( Ports==0 ): 54 | print("Error no ports found\n"); 55 | 56 | i = 0 # Port number?! 57 | int DEV = FindDev(my_ohci, i); 58 | 59 | if Debug: 60 | if DEV == 0: 61 | print("*NO* device found at port " + str(i)) 62 | elif DEV == 1: 63 | print("lowspeed-device found at port " + str(i)) 64 | elif DEV == 2: 65 | print("fullspeed-device found at port " + str(i)) 66 | else: 67 | print("Unknown device type on port " + str(i)) 68 | #//if( DEV == 0 ) return; 69 | 70 | 71 | s_USB_Devicedescriptor DD[4] = {0}; 72 | 73 | ResetPort(my_ohci, i); 74 | SetAddres(my_ohci, 0, found); 75 | 76 | if Debug: 77 | print("\n\ngood to go\n\n"); 78 | 79 | GetDesc(my_ohci, found, 1, 0, 18, Buffer); 80 | 81 | xmemcpy(&(DD[0]), Buffer, 18); 82 | 83 | if Debug: 84 | DebugDescriptor( &DD[0] ); 85 | sprintf(buf, "ConfigNumber:%x\n", DD[0].ConfigNumber ); dbg(buf); 86 | 87 | s_USB_Configurationdescriptor CD[4][8]={0}; 88 | s_USB_Interfacedescriptor ID[4][8][8] = {0}; 89 | s_USB_Endpointdescriptor ED[4][8][8][8]={0}; 90 | 91 | for j in range(1, DD[i].ConfigNumber + 1) 92 | index = 0 93 | 94 | SetConfigur(my_ohci, found, j); 95 | GetDesc(my_ohci, found, 2, 0, 9, Buffer) #// Configdescr. 96 | xmemcpy(&CD[i][j - 1], Buffer, 9); 97 | 98 | if Debug: 99 | DebugConfigDescriptor( &(CD[i][j-1]) ) 100 | 101 | GetDesc(my_ohci, found, 2, 0, (__u8) *((__u16 *) &Buffer[2]), Buffer) #// Configdescr. 102 | xmemcpy(&CD[i][j - 1], Buffer, 9); 103 | 104 | if Debug: 105 | DebugConfigDescriptor( &(CD[i][j-1]) ) 106 | 107 | index += 9 108 | for k in range(0, CD[i][j - 1].NumberofInterfaces): 109 | 110 | xmemcpy(&ID[i][j][k], Buffer + index, 9); 111 | if Debug: 112 | DebugInterfaceDescriptor( &(ID[i][j][k]) ); 113 | 114 | if (ID[i][j][k].InterfaceIndex): 115 | GetDesc(my_ohci,found, 3, ID[i][j][k].InterfaceIndex, 8, Buffer); // String 116 | #//DebugInterfaceDescriptor( &ID[i][j][k] ); 117 | GetDesc(my_ohci,found, 3, ID[i][j][k].InterfaceIndex, Buffer[0], Buffer); // String 118 | 119 | for (j = 1; j < (Buffer[0] / 2); j++) 120 | Buffer[j + 1] = Buffer[2 * j]; 121 | Buffer[j + 1] = 0; 122 | 123 | s_USB_Stringdescriptor *SD = malloc(0x4000); 124 | xmemcpy((__u8 *) SD, Buffer, Buffer[0]); 125 | if Debug: 126 | print("\nInterface %d: %s\n" % (k + 1, &Buffer[2])); 127 | 128 | index += 9; 129 | for l in range(0, ID[i][j][k].NumberofEndpoints): 130 | #// Causes a link error? 131 | #//xmemcpy(&ED[i][j][k][l], Buffer + index, 9); 132 | 133 | if Debug: 134 | DebugEndPointDescriptor( &ED[i][j][k][l] ); 135 | 136 | #// we set the default configuration here 137 | #//usb_set_configuration(dev, dev->config[0].bConfigurationValue)) 138 | #// usb_set_configuration(my_ohci, found, CD[i][j-1].ConfigValue); 139 | #//CD[i][j-1].ConfigValue; 140 | 141 | set_control_msg(my_ohci, found, 142 | USB_REQ_SET_CONFIGURATION, 0, CD[i][j-1].ConfigValue, 0, 0, NULL); 143 | 144 | 145 | #//-----------------------work on the hub!-------------------------/ 146 | 147 | #// ref: http://fxr.watson.org/fxr/source/dev/usb/uhub.c 148 | #/// 149 | #// To have the best chance of success we do things in the exact same 150 | #// order as Windoze98. This should not be necessary, but some 151 | #// devices do not follow the USB specs to the letter. 152 | #// 153 | #// These are the events on the bus when a hub is attached: 154 | #// Get device and config descriptors (see attach code) 155 | #// Get hub descriptor (see above) 156 | #// For all ports 157 | #// turn on power 158 | #// wait for power to become stable 159 | #// (all below happens in explore code) 160 | #// For all ports 161 | #// clear C_PORT_CONNECTION 162 | #// For all ports 163 | #// get port status 164 | #// if device connected 165 | #// wait 100 ms 166 | #// turn on reset 167 | #// wait 168 | #// clear C_PORT_RESET 169 | #// get port status 170 | #// proceed with device attachment 171 | #/// 172 | 173 | 174 | if Debug: 175 | print("\n\nHUB INTERIGATION AND SETTING UP\n\n"); 176 | 177 | 178 | xmemset(Buffer, 0, 255); 179 | 180 | //usb_get_device_descriptor(my_ohci, 1, 18, Buffer); 181 | //DebugDescriptor( (s_USB_Devicedescriptor *)Buffer ); 182 | 183 | usbd_device dev; 184 | dev.p_ohci = my_ohci; 185 | dev.address = found; // root hub should be addr 1 186 | 187 | 188 | do_hub_work(&dev); 189 | 190 | 191 | 192 | 193 | #// Testing...gamepad 1 is allocated to addr 3 194 | 195 | 196 | #//Assumptions - for this small test section...I've assumed taht only 197 | #//a single gamepad is plugged in. 198 | 199 | 200 | if Debug: 201 | print("\n\n--gamepad_0---\n\n"); 202 | 203 | 204 | 205 | #//Getting Descriptor 206 | s_USB_Devicedescriptor devdesc = {0}; 207 | dev.address = 3; 208 | 209 | devrequest req = {0}; 210 | req.requesttype = UT_READ_DEVICE; 211 | req.request = UR_GET_DESCRIPTOR; 212 | req.value = 0; 213 | req.value |= (0x00ff & 0); 214 | req.value |= (0xff00 & (UDESC_DEVICE<<8)); 215 | req.index = 0; 216 | req.length = 18; 217 | 218 | #// What is this new function?...why didn't we use the normal 219 | #// usd_do_request(..)?....hmm...well it seems that the gamepad 220 | #// max packet size is 0x40...and if we use the 0x8 which I've done upto 221 | #// now we only get the first 8 bytes of the desciptor...using this slightly 222 | #// modified version, we get the full descriptor of our gamepad :) 223 | usbd_do_request_big_packet(&dev, &req, &devdesc); 224 | 225 | if Debug: 226 | DebugDescriptor( &devdesc ); 227 | 228 | 229 | xSleep(10); 230 | 231 | #// USB_REQ_SET_CONFIGURATION, 0, configuration, 0, 0, NULL); 232 | 233 | #// Set the config descriptor for gamepad 234 | 235 | bConfigurationValue = 0x01 236 | 237 | usbd_device dev_temp; 238 | dev_temp.address = 3; 239 | dev_temp.p_ohci = dev.p_ohci; 240 | 241 | devrequest dr = {0}; 242 | 243 | dr.requesttype = UT_WRITE_DEVICE #// 0x80 244 | dr.request = UR_SET_CONFIG #// 0x09 245 | dr.value = bConfigurationValue #// 0x01 246 | dr.index = 0 247 | dr.length = 0 248 | 249 | usbd_do_request_big_packet(&dev_temp, &dr, 0 ) 250 | 251 | return 0 252 | 253 | 254 | devrequest dr = {0}; 255 | 256 | def xGetPadInput(stXPAD * p, stXINPUT * xin, iWhichPad = 0): #// 0 is default pad 257 | usbd_device dev; 258 | dev.p_ohci = &(xin->my_ohci); 259 | dev.address = 3 #// This will choose the first gamepad it finds, only for debug 260 | 261 | stXPAD data_in = {0}; 262 | 263 | 264 | 265 | #// bug 266 | bConfigurationValue = 0x01 267 | 268 | #//devrequest dr = {0}; 269 | 270 | dr.requesttype = UT_WRITE_DEVICE #// 0x80 271 | dr.request = UR_SET_CONFIG #// 0x09 272 | dr.value = bConfigurationValue #// 0x01 273 | dr.index = 0 274 | dr.length = 0 275 | 276 | 277 | 278 | #// bug 279 | #//while(true) 280 | 281 | #//usbd_do_request_big_packet(&dev, &dr, 0 ); 282 | 283 | usb_bulk_msg_in(&dev, 20, &data_in) 284 | xmemcpy(p, &data_in, 20) 285 | 286 | 287 | #//DataDisplay( (void*) &data_in ); 288 | 289 | 290 | 291 | #//xSleep(100); 292 | 293 | usbd_do_request_big_packet(&dev, &dr, 0 ) 294 | 295 | 296 | #// Some error checking to be put here! 297 | #//if( data_in.structsize == 0 ) 298 | #// break; 299 | 300 | return 0 #// no error checking as of yet 301 | } 302 | 303 | 304 | def xSetPadInput(stXPAD * p, stXINPUT * xin, iWhichPad = 0): 305 | usbd_device dev; 306 | dev.p_ohci = &(xin->my_ohci); 307 | dev.address = 3; #// This will choose the first gamepad it finds, only for debug 308 | 309 | #// Rumbble...lets see if we can make our gamepad rumble :) 310 | 311 | data1 = bytes([0,6,0,120,0,120]) #// rumble data 312 | usb_bulk_msg(&dev, data1); #// simple bulk send function (in pad.c/.h) 313 | 314 | data2 = bytes([0,6,0,0, 0,0]); 315 | usb_bulk_msg(&dev, data2); 316 | 317 | return 0 318 | 319 | 320 | def xReleaseInput(stXINPUT * p): 321 | assert(False) 322 | return 0 323 | 324 | 325 | 326 | -------------------------------------------------------------------------------- /dump-xbox/dump-xbox.c: -------------------------------------------------------------------------------- 1 | //#include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "common/x86.h" 21 | #include "common/pe.h" 22 | 23 | #define printf debugPrint 24 | #define fflush(x) 25 | #define fclose(x) 26 | 27 | 28 | void resetFlash(volatile uint8_t* flashRom) { 29 | flashRom[0x555] = 0xAA; 30 | flashRom[0x2AA] = 0x55; 31 | flashRom[0x555] = 0xF0; 32 | KeStallExecutionProcessor(150000); 33 | return; 34 | } 35 | 36 | 37 | void setFlashCache(bool enable) { 38 | uint64_t mtrrType; 39 | 40 | uint32_t cr4 = getCr4(); 41 | disableCache(); 42 | flushCache(); 43 | flushTlb(); 44 | 45 | // Disable MTRR 46 | mtrrType = rdmsr(0x2FF,NULL,NULL); 47 | wrmsr(0x2FF, 0, 0); 48 | 49 | unsigned int i; 50 | for (i = 0; i < 8; i++) { 51 | uint64_t mtrr = rdmsr(0x200+i*2,NULL,NULL); 52 | uint32_t base = (mtrr >> 12) & 0xFFFFFF; 53 | uint8_t type = mtrr & 0xFF; 54 | 55 | if (base >= (0xFF000000 >> 12) && type != 0) { 56 | mtrr = rdmsr(0x201+i*2,NULL,NULL); 57 | mtrr = enable?(mtrr | 0x800):(mtrr & (~0x800)); 58 | wrmsr(0x201+i*2, mtrr >> 32, mtrr); 59 | } 60 | } 61 | 62 | flushCache(); 63 | flushTlb(); 64 | 65 | // Reenable MTRR 66 | wrmsr(0x2FF, mtrrType >> 32, mtrrType); 67 | 68 | enableCache(); 69 | setCr4(cr4); 70 | 71 | return; 72 | 73 | } 74 | 75 | bool dumpFile(const uint8_t* data, size_t size, const char* filename) { 76 | #if 0 77 | FILE* f = fopen(filename,"w"); 78 | if (f == NULL) { 79 | return false; 80 | } 81 | size_t offset = 0; 82 | while(size > 0) { 83 | size_t chunk = size>1024?1024:size; 84 | size_t written = fwrite(&data[offset],1,chunk,f); fflush(f); 85 | written = chunk; // Work around OpenXDK issue where it doesn't return the number of elements 86 | size -= written; 87 | offset += written; 88 | } 89 | fclose(f); 90 | #endif 91 | return true; 92 | } 93 | 94 | void dumpIoFile(volatile uint8_t* io, unsigned int offset, size_t size, const char* filename) { 95 | uint8_t* buffer = malloc(size+1); 96 | disableInterrupts(); 97 | setFlashCache(false); 98 | unsigned int i; 99 | for(i = 0; i < size; i += 2) { 100 | *(uint16_t*)&buffer[i] = *(uint16_t*)&io[(offset & (~1))+i]; 101 | } 102 | setFlashCache(true); 103 | enableInterrupts(); 104 | dumpFile(&buffer[offset & 1],size,filename); 105 | free(buffer); 106 | return; 107 | } 108 | 109 | int main() { 110 | 111 | 112 | pb_init(); 113 | pb_show_debug_screen(); 114 | 115 | debugPrint("Hello!"); 116 | 117 | // Open a log file 118 | #if 0 119 | const char* log = "log.txt"; 120 | FILE* f = fopen(log,"w"); 121 | fprintf(f,"Failed to log!\n"); fflush(stdout); 122 | fclose(f); 123 | freopen(log, "w", stdout); 124 | #endif 125 | 126 | // Workaround missing OpenXDK function 127 | void(*MmUnmapIoSpaceFix)(PVOID,SIZE_T) = (void*)&MmUnmapIoSpace; 128 | 129 | // Keep track how many errors we produce.. 130 | unsigned int errors = 0; 131 | 132 | // Dump the hardware information to log 133 | if (1) { 134 | 135 | // Dump generic details 136 | IoOutputDword(0xCF8, 0x80000084); 137 | size_t ramSize = IoInputDword(0xCFC); 138 | printf("Xbox: %i MB RAM\n",(ramSize+1) / (1024*1024)); fflush(stdout); 139 | 140 | // Dump MCPX details 141 | IoOutputDword(0xCF8, 0x80000808); 142 | uint32_t mcpxRevision = IoInputDword(0xCFC); 143 | IoOutputDword(0xCF8, 0x80000880); 144 | uint32_t mcpxRomEnable = IoInputDword(0xCFC); 145 | bool mcpxX2 = mcpxRomEnable & 0x1000; //FIXME: Might just indicate DVT4 146 | printf("MCPX: %s, Revision 0x%02X\n",mcpxX2?"X2":"X3",mcpxRevision & 0xFF); fflush(stdout); 147 | 148 | //FIXME: We should also dump NV2A details for completeness 149 | 150 | // Dump SMC details 151 | char smcVersion[3]; 152 | HalWriteSMBusValue(0x20 /* SMC Write */, 0x01 /* Version seek */, TRUE, 0); 153 | unsigned int i; 154 | for(i = 0; i < 3; i++) { 155 | HalReadSMBusValue(0x20 /* SMC Read */, 0x01 /* Version read */, TRUE, &smcVersion[i]); 156 | } 157 | printf("SMC: Version '%.3s'\n",smcVersion); fflush(stdout); 158 | uint8_t smcAvPack; 159 | HalReadSMBusValue(0x20 /* SMC Read */, 0x04 /* AV Pack read */, TRUE, &smcAvPack); 160 | printf("SMC: AV Pack 0x%02X\n",smcAvPack); fflush(stdout); 161 | 162 | } 163 | 164 | // Dump the flash 165 | if (1) { 166 | // 0xFF000000 - 0xFFFFFFFF is flash 167 | volatile uint8_t* flashRom = MmMapIoSpace(0xFF000000, 0x1000000, 0x4 /* READWRITE */ | 0x200 /* NOCACHE */); 168 | 169 | unsigned int flashSize = 0x100000; 170 | 171 | //FIXME: Get flash size if possible! Otherwise assume 1MB Flash 172 | if (0) { 173 | printf("Flash: preparing\n"); fflush(stdout); 174 | 175 | disableInterrupts(); 176 | setFlashCache(false); 177 | 178 | flashRom[0x555] = 0xAA; 179 | flashRom[0x2AA] = 0x55; 180 | flashRom[0x555] = 0x90; 181 | 182 | KeStallExecutionProcessor(1); 183 | 184 | uint8_t manufacturer = flashRom[0x0]; 185 | uint8_t code = flashRom[0x1]; 186 | 187 | resetFlash(flashRom); 188 | 189 | setFlashCache(true); 190 | enableInterrupts(); 191 | 192 | printf("Flash: Manufacturer 0x%02X, Code 0x%02X\n", manufacturer, code); fflush(stdout); 193 | } 194 | 195 | dumpIoFile(flashRom,0,flashSize,"flash.bin"); 196 | 197 | MmUnmapIoSpaceFix((PVOID)flashRom, 0x1000000); 198 | } 199 | 200 | // FIXME: Dump the mcpx internal rom [needs a way (bug/glitch/feature) to make this visible] 201 | if (0) { 202 | volatile uint8_t* mcpxRom = MmMapIoSpace(0xFFFFFE00, 0x200, 0x4 /* READWRITE */ | 0x200 /* NOCACHE */); 203 | MmUnmapIoSpaceFix((PVOID)mcpxRom, 0x200); 204 | } 205 | 206 | // Dump the eeprom 207 | if (1) { 208 | uint8_t buffer[0x100]; 209 | uint16_t* ptr = (uint16_t*)buffer; 210 | // Read eeprom words 211 | unsigned int i; 212 | for(i = 0; i < sizeof(buffer); i += 2) { 213 | ULONG value; 214 | if (!NT_SUCCESS(HalReadSMBusValue(0xA9 /* EEPROM Read */, (UCHAR)i, TRUE, (PCHAR)&value))) { 215 | printf("EEPROM: Couldn't read address 0x%02X\n",i); fflush(stdout); 216 | } 217 | *ptr++ = (uint16_t)value; 218 | } 219 | dumpFile(buffer,0x100,"eeprom.bin"); 220 | } 221 | 222 | // Dump xboxkrnl.exe and its arguments 223 | if (1) { 224 | // Dump the kernel image 225 | { 226 | void* kernelImage = (void*)0x80010000; 227 | IMAGE_DOS_HEADER* kernelImageDosHeader = kernelImage; 228 | IMAGE_NT_HEADERS* kernelImageHeader = (IMAGE_NT_HEADERS*)((uintptr_t)kernelImage+kernelImageDosHeader->e_lfanew); 229 | IMAGE_FILE_HEADER* kernelImageFileHeader = &kernelImageHeader->FileHeader; 230 | IMAGE_OPTIONAL_HEADER* kernelImageOptionalHeader = &kernelImageHeader->OptionalHeader; 231 | // Print some information about data sections 232 | uint32_t* kernelDataSectionInformation = (uint32_t*)kernelImageDosHeader->e_res2; 233 | printf("xboxkrnl.exe: Uninitalized data section is 0x%X bytes\n",kernelDataSectionInformation[0]); fflush(stdout); 234 | printf("xboxkrnl.exe: Initalized data section is 0x%X bytes at 0x%08X (Raw data at 0x%08X)\n",kernelDataSectionInformation[1],kernelDataSectionInformation[3],kernelDataSectionInformation[2]); fflush(stdout); 235 | // Calculate the kernel size 236 | size_t length = kernelImageOptionalHeader->SizeOfImage; 237 | if (0) { 238 | uint16_t sectionCount = kernelImageFileHeader->NumberOfSections; 239 | // Navigate to section header of the last section (which should be INIT) 240 | IMAGE_SECTION_HEADER* sectionHeaders = (IMAGE_SECTION_HEADER*)((uintptr_t)kernelImageOptionalHeader + kernelImageFileHeader->SizeOfOptionalHeader); 241 | IMAGE_SECTION_HEADER* sectionHeader = §ionHeaders[sectionCount-1]; 242 | // And calculate the actual length 243 | printf("xboxkrnl.exe: Found '%.8s', virtual size: 0x%X (at 0x%08X), raw size: 0x%X\n",sectionHeader->Name,sectionHeader->Misc.VirtualSize,sectionHeader->VirtualAddress,sectionHeader->SizeOfRawData); fflush(stdout); 244 | length = sectionHeader->VirtualAddress + sectionHeader->SizeOfRawData; 245 | printf("xboxkrnl.exe: Length of 0x%X bytes reported, 0x%X bytes calculated\n",kernelImageOptionalHeader->SizeOfImage,length); fflush(stdout); 246 | } 247 | dumpFile(kernelImage,length,"xboxkrnl.exe"); 248 | } 249 | // Dump keys which were passed to kernel 250 | { 251 | uint8_t keys[16+16]; 252 | // EEPROM Key 253 | { 254 | printf("keys.bin: EEPROM key: "); fflush(stdout); 255 | unsigned int i; 256 | for(i = 0; i < 16; i++) { 257 | printf("%02X",XboxEEPROMKey[i]); fflush(stdout); 258 | } 259 | printf("\n"); fflush(stdout); 260 | memcpy(&keys[0],XboxEEPROMKey,16); 261 | } 262 | // CERT key 263 | { 264 | uint8_t* XboxCERTKey = &XboxHDKey[-16]; // XboxCERTKey is just infront of XboxHDKey 265 | printf("keys.bin: CERT key: "); fflush(stdout); 266 | int i; 267 | for(i = 0; i < 16; i++) { 268 | printf("%02X",XboxCERTKey[i]); fflush(stdout); 269 | } 270 | printf("\n"); fflush(stdout); 271 | memcpy(&keys[16],XboxCERTKey,16); 272 | } 273 | // Now dump them to file 274 | dumpFile(keys,sizeof(keys),"keys.bin"); 275 | } 276 | } 277 | 278 | // Dump HDD stuff 279 | if (1) { 280 | 281 | const unsigned int sectorSize = 512; 282 | 283 | ANSI_STRING objectName; 284 | RtlInitAnsiString(&objectName, "\\Device\\Harddisk0\\partition0"); 285 | 286 | OBJECT_ATTRIBUTES objectAttributes = { 287 | .ObjectName = &objectName, 288 | .Attributes = OBJ_CASE_INSENSITIVE, 289 | .RootDirectory = NULL 290 | }; 291 | 292 | IO_STATUS_BLOCK ioStatusBlock; 293 | NTSTATUS status; 294 | 295 | HANDLE disk; 296 | 297 | status = NtOpenFile(&disk, GENERIC_ALL|SYNCHRONIZE, &objectAttributes, &ioStatusBlock, 0, FILE_SYNCHRONOUS_IO_ALERT); 298 | 299 | unsigned int sector = 0; 300 | unsigned int sectors = 0x400; // Space until first partition 301 | 302 | void* buffer = malloc(sectors * sectorSize); 303 | 304 | LARGE_INTEGER offset; 305 | offset.QuadPart = sector * sectorSize; 306 | 307 | status = NtReadFile(disk, 0, NULL, NULL, &ioStatusBlock, buffer, sectors * sectorSize, &offset); 308 | NtClose(disk); 309 | 310 | char filename[128]; 311 | sprintf(filename,"hdd_0x%x-0x%x.bin",sector,sector+sectors-1); 312 | dumpFile(buffer,sectors * sectorSize,filename); 313 | 314 | free(buffer); 315 | 316 | } 317 | 318 | printf("Done! %i error(s)\n",errors); fflush(stdout); 319 | 320 | HalReturnToFirmware(ReturnFirmwareQuickReboot); 321 | XReboot(); 322 | 323 | } 324 | --------------------------------------------------------------------------------