Screenshot
#1
Screenshot

This code will allow you to take a screenshot, anytime, at the press of a button. The steps below illustrate the process of converting the file to a viewable .png file:

Wii:
1) Press the button activator to take a screenshot.
2) Navigate to /screenshots directory on your NAND (I recommend using the homebrew application "WiiXplorer" for this).
3) Find the screenshot file (The file naming convention is YYMMDDHHMMSS according to the date and time the screenshot was taken at).
4) Copy the file onto your SD Card / USB.

Computer:
5) Insert the SD Card / USB into your computer.
6) Move the file into the same directory as the "Screenshot Converter" program (Which can be downloaded here: https://mkwii.org/downloads/ScreenshotCodeConverter.zip).
7) Run the program.

Following this, a viewable .png file of the screenshot will be created in the same directory as the executable.

(NTSC-U)
04009644 818C0020
2834XXXX YYYYZZZZ
C2009644 0000004A
818C0020 9421FF30
BC410008 48000015
2F736372 65656E73
686F7473 00000000
7C6802A6 38800000
38A00003 38C00003
38E00003 3FE08016
63EC9DD4 7D8903A6
4E800421 2C03FF97
41A2000C 2C030000
408201F0 3D80801A
618CACBC 7D8903A6
4E800421 38A10080
3D80801A 618CAF08
7D8903A6 4E800421
386100A8 4800002D
2F736372 65656E73
686F7473 2F253032
64253032 64253032
64253032 64253032
64253032 64000000
7C8802A6 80A10094
38C00064 7CE533D6
7CC731D6 7CA62850
80C10090 38C60001
80E1008C 81010088
81210084 81410080
4CC63182 3D808001
618C0ECC 7D8903A6
4E800421 2C030019
40820150 386100A8
38800000 38A00003
38C00003 38E00003
63ECABD4 7D8903A6
4E800421 2C030000
40820128 386100A8
38800002 63ECADBC
7D8903A6 4E800421
2C030000 4180010C
7C7E1B78 3FA0CD80
807D00C0 60630020
907D00C0 38600080
38800020 80ADA358
80A50024 7CBC2B78
3D808022 618C9490
7D8903A6 4E800421
2C030000 418200B0
7C7B1B78 3C80CC00
60842000 38A00080
3D808000 618C5F34
7D8903A6 4E800421
7FC3F378 7F64DB78
38A00080 63ECB220
7D8903A6 4E800421
2C030080 40820058
7FC3F378 809B001C
74851000 41820008
54842834 3C848000
88BB0000 54A52834
88DB0001 54C6E8FE
7CA53378 70A507FE
88DB0049 54C61838
7CA629D6 54A5083C
7CA52214 7CA42850
63ECB220 7D8903A6
4E800421 7F63DB78
7F84E378 3D808022
618C9800 7D8903A6
4E800421 807D00C0
546306F2 907D00C0
7FC3F378 63ECB2E4
7D8903A6 4E800421
B8410008 382100D0
60000000 00000000
E0000000 80008000

(PAL)
04009684 818C0020
2834XXXX YYYYZZZZ
C2009684 0000004A
818C0020 9421FF30
BC410008 48000015
2F736372 65656E73
686F7473 00000000
7C6802A6 38800000
38A00003 38C00003
38E00003 3FE08016
63EC9E74 7D8903A6
4E800421 2C03FF97
41A2000C 2C030000
408201F0 3D80801A
618CAD5C 7D8903A6
4E800421 38A10080
3D80801A 618CAFA8
7D8903A6 4E800421
386100A8 4800002D
2F736372 65656E73
686F7473 2F253032
64253032 64253032
64253032 64253032
64253032 64000000
7C8802A6 80A10094
38C00064 7CE533D6
7CC731D6 7CA62850
80C10090 38C60001
80E1008C 81010088
81210084 81410080
4CC63182 3D808001
618C1A2C 7D8903A6
4E800421 2C030019
40820150 386100A8
38800000 38A00003
38C00003 38E00003
63ECAC74 7D8903A6
4E800421 2C030000
40820128 386100A8
38800002 63ECAE5C
7D8903A6 4E800421
2C030000 4180010C
7C7E1B78 3FA0CD80
807D00C0 60630020
907D00C0 38600080
38800020 80ADA360
80A50024 7CBC2B78
3D808022 618C9814
7D8903A6 4E800421
2C030000 418200B0
7C7B1B78 3C80CC00
60842000 38A00080
3D808000 618C5F34
7D8903A6 4E800421
7FC3F378 7F64DB78
38A00080 63ECB2C0
7D8903A6 4E800421
2C030080 40820058
7FC3F378 809B001C
74851000 41820008
54842834 3C848000
88BB0000 54A52834
88DB0001 54C6E8FE
7CA53378 70A507FE
88DB0049 54C61838
7CA629D6 54A5083C
7CA52214 7CA42850
63ECB2C0 7D8903A6
4E800421 7F63DB78
7F84E378 3D808022
618C9B84 7D8903A6
4E800421 807D00C0
546306F2 907D00C0
7FC3F378 63ECB384
7D8903A6 4E800421
B8410008 382100D0
60000000 00000000
E0000000 80008000

(NTSC-J)
040095E0 818C0020
2834XXXX YYYYZZZZ
C20095E0 0000004A
818C0020 9421FF30
BC410008 48000015
2F736372 65656E73
686F7473 00000000
7C6802A6 38800000
38A00003 38C00003
38E00003 3FE08016
63EC9D94 7D8903A6
4E800421 2C03FF97
41A2000C 2C030000
408201F0 3D80801A
618CAC7C 7D8903A6
4E800421 38A10080
3D80801A 618CAEC8
7D8903A6 4E800421
386100A8 4800002D
2F736372 65656E73
686F7473 2F253032
64253032 64253032
64253032 64253032
64253032 64000000
7C8802A6 80A10094
38C00064 7CE533D6
7CC731D6 7CA62850
80C10090 38C60001
80E1008C 81010088
81210084 81410080
4CC63182 3D808001
618C1950 7D8903A6
4E800421 2C030019
40820150 386100A8
38800000 38A00003
38C00003 38E00003
63ECAB94 7D8903A6
4E800421 2C030000
40820128 386100A8
38800002 63ECAD7C
7D8903A6 4E800421
2C030000 4180010C
7C7E1B78 3FA0CD80
807D00C0 60630020
907D00C0 38600080
38800020 80ADA360
80A50024 7CBC2B78
3D808022 618C9734
7D8903A6 4E800421
2C030000 418200B0
7C7B1B78 3C80CC00
60842000 38A00080
3D808000 618C5F34
7D8903A6 4E800421
7FC3F378 7F64DB78
38A00080 63ECB1E0
7D8903A6 4E800421
2C030080 40820058
7FC3F378 809B001C
74851000 41820008
54842834 3C848000
88BB0000 54A52834
88DB0001 54C6E8FE
7CA53378 70A507FE
88DB0049 54C61838
7CA629D6 54A5083C
7CA52214 7CA42850
63ECB1E0 7D8903A6
4E800421 7F63DB78
7F84E378 3D808022
618C9AA4 7D8903A6
4E800421 807D00C0
546306F2 907D00C0
7FC3F378 63ECB2A4
7D8903A6 4E800421
B8410008 382100D0
60000000 00000000
E0000000 80008000

(NTSC-K)
0400978C 818C0020
2833XXXX YYYYZZZZ
C200978C 0000004A
818C0020 9421FF30
BC410008 48000015
2F736372 65656E73
686F7473 00000000
7C6802A6 38800000
38A00003 38C00003
38E00003 3FE08016
63EC9F10 7D8903A6
4E800421 2C03FF97
41A2000C 2C030000
408201F0 3D80801A
618CB0B8 7D8903A6
4E800421 38A10080
3D80801A 618CB304
7D8903A6 4E800421
386100A8 4800002D
2F736372 65656E73
686F7473 2F253032
64253032 64253032
64253032 64253032
64253032 64000000
7C8802A6 80A10094
38C00064 7CE533D6
7CC731D6 7CA62850
80C10090 38C60001
80E1008C 81010088
81210084 81410080
4CC63182 3D808001
618C1A94 7D8903A6
4E800421 2C030019
40820150 386100A8
38800000 38A00003
38C00003 38E00003
63ECAD10 7D8903A6
4E800421 2C030000
40820128 386100A8
38800002 63ECAEF8
7D8903A6 4E800421
2C030000 4180010C
7C7E1B78 3FA0CD80
807D00C0 60630020
907D00C0 38600080
38800020 80ADA380
80A50024 7CBC2B78
3D808022 618C9B88
7D8903A6 4E800421
2C030000 418200B0
7C7B1B78 3C80CC00
60842000 38A00080
3D808000 618C5F34
7D8903A6 4E800421
7FC3F378 7F64DB78
38A00080 63ECB35C
7D8903A6 4E800421
2C030080 40820058
7FC3F378 809B001C
74851000 41820008
54842834 3C848000
88BB0000 54A52834
88DB0001 54C6E8FE
7CA53378 70A507FE
88DB0049 54C61838
7CA629D6 54A5083C
7CA52214 7CA42850
63ECB35C 7D8903A6
4E800421 7F63DB78
7F84E378 3D808022
618C9EF8 7D8903A6
4E800421 807D00C0
546306F2 907D00C0
7FC3F378 63ECB420
7D8903A6 4E800421
B8410008 382100D0
60000000 00000000
E0000000 80008000

Code:
#============================================================#
#                           Source                           #
#------------------------------------------------------------#
# Draw Debug Bar Call Address Ports:                         #
# RMCE - 0x80009644                                          #
# RMCP - 0x80009684                                          #
# RMCJ - 0x800095E0                                          #
# RMCK - 0x8000978C                                          #
#============================================================#

#============================================================#
#                    Assembler Directives                    #
#============================================================#

.macro push_stack length, registers
       stwu r1, -\length(r1)
       stmw r2, 8(r1)
.endm

.macro absolute_ISFS_bl register, address
       ori \register, r31, \address@l
       mtctr \register
       bctrl
.endm

.macro absolute_bl register, address
       lis \register, \address@h
       ori \register, \register, \address@l
       mtctr \register
       bctrl
.endm

.macro pop_stack registers, length
       lmw \registers, 8(r1)
       addi r1, r1, \length
.endm

.set region, ''

.if     (region == 'E' || region == 'e') # RMCE
        .set memcpy, 0x80005F34
        .set sprintf, 0x80010ECC
        .set ISFS_CreateDir, 0x80169DD4
        .set ISFS_CreateFile, 0x8016ABD4
        .set ISFS_Open, 0x8016ADBC
        .set ISFS_Write, 0x8016B220
        .set ISFS_Close, 0x8016B2E4
        .set OSGetTime, 0x801AACBC
        .set OSTicksToCalendarTime, 0x801AAF08
        .set EGG_Heap_alloc, 0x80229490
        .set EGG_Heap_free, 0x80229800
.elseif (region == 'P' || region == 'p') # RMCP
        .set memcpy, 0x80005F34
        .set sprintf, 0x80011A2C
        .set ISFS_CreateDir, 0x80169E74
        .set ISFS_CreateFile, 0x8016AC74
        .set ISFS_Open, 0x8016AE5C
        .set ISFS_Write, 0x8016B2C0
        .set ISFS_Close, 0x8016B384
        .set OSGetTime, 0x801AAD5C
        .set OSTicksToCalendarTime, 0x801AAFA8
        .set EGG_Heap_alloc, 0x80229814
        .set EGG_Heap_free, 0x80229B84
.elseif (region == 'J' || region == 'j') # RMCJ
        .set memcpy, 0x80005F34
        .set sprintf, 0x80011950
        .set ISFS_CreateDir, 0x80169D94
        .set ISFS_CreateFile, 0x8016AB94
        .set ISFS_Open, 0x8016AD7C
        .set ISFS_Write, 0x8016B1E0
        .set ISFS_Close, 0x8016B2A4
        .set OSGetTime, 0x801AAC7C
        .set OSTicksToCalendarTime, 0x801AAEC8
        .set EGG_Heap_alloc, 0x80229734
        .set EGG_Heap_free, 0x80229AA4
.elseif (region == 'K' || region == 'k') # RMCK
        .set memcpy, 0x80005F34
        .set sprintf, 0x80011A94
        .set ISFS_CreateDir, 0x80169F10
        .set ISFS_CreateFile, 0x8016AD10
        .set ISFS_Open, 0x8016AEF8
        .set ISFS_Write, 0x8016B35C
        .set ISFS_Close, 0x8016B420
        .set OSGetTime, 0x801AB0B8
        .set OSTicksToCalendarTime, 0x801AB304
        .set EGG_Heap_alloc, 0x80229B88
        .set EGG_Heap_free, 0x80229EF8
.else # Invalid Region
        .abort
.endif

.set NULL, 0

.set STACK_FRAME_LENGTH, 0xD0
.set STACK_FRAME_PRESERVED_REGISTERS_OFFSET, 0
.set STACK_FRAME_OSTICKSTOCALENDARTIME_STRUCT_OFFSET, 0x80
.set STACK_FRAME_SCREENSHOT_FILEPATH_BUFFER_OFFSET, 0xA8

.set OSTICKSTOCALENDARTIME_STRUCT_SECONDS_OFFSET, 0x0
.set OSTICKSTOCALENDARTIME_STRUCT_MINUTES_OFFSET, 0x4
.set OSTICKSTOCALENDARTIME_STRUCT_HOURS_OFFSET, 0x8
.set OSTICKSTOCALENDARTIME_STRUCT_DAY_OFFSET, 0xC
.set OSTICKSTOCALENDARTIME_STRUCT_MONTH_OFFSET, 0x10
.set OSTICKSTOCALENDARTIME_STRUCT_YEAR_OFFSET, 0x14

.set ISFS_EOK, 0
.set ISFS_EEXIST, -105

.set ISFS_MAX_PATHNAMELEN, 64
.set ISFS_MAX_FILENAMELEN, 12

.set SCREENSHOT_FILE_PATHNAMELEN, 25

.set ISFS_OPEN_READ, 0x1
.set ISFS_OPEN_WRITE, 0x02
.set ISFS_OPEN_RW, (ISFS_OPEN_READ | ISFS_OPEN_WRITE)

.set SLOT_LED_ON_BITS, 0x20

.set HOLLYWOOD_REGISTERS_BASE, 0xCD800000
.set HOLLYWOOD_GPIOB_OUT_OFFSET, 0xC0

.set VI_REGISTERS_BASE, 0xCC002000
.set VI_REGISTERS_LENGTH, 0x80

#============================================================#
#                            Start                           #
#============================================================#

.globl screenshot
screenshot:

# Original Instruction
lwz r12, 0x20(r12)

# Function Prologue...
push_stack STACK_FRAME_LENGTH, r2

# ISFS_CreateDir arguments
bl branch_link_write_screenshot_directory
.string "/screenshots\0\0\0"
branch_link_write_screenshot_directory:
mflr r3 # Pointer to the filepath
li r4, 0 # attributes
li r5, 3 # owner_perm
li r6, 3 # group_perm
li r7, 3 # other_perm

lis r31, 0x8016

absolute_ISFS_bl r12, ISFS_CreateDir
cmpwi r3, ISFS_EEXIST
beq+ branch_screenshot_directory_already_exists
cmpwi r3, ISFS_EOK
bne- branch_isfs_createdir_failed

branch_screenshot_directory_already_exists:
# OSTicksToCalendarTime arguments
absolute_bl r12, OSGetTime
addi r5, r1, STACK_FRAME_OSTICKSTOCALENDARTIME_STRUCT_OFFSET

absolute_bl r12, OSTicksToCalendarTime

# sprintf arguments
addi r3, r1, STACK_FRAME_SCREENSHOT_FILEPATH_BUFFER_OFFSET

bl branch_link_write_screenshot_filepath
#                       YY  MM  DD  HH  MM  SS
.string "/screenshots/%02d%02d%02d%02d%02d%02d\0\0"
branch_link_write_screenshot_filepath:
mflr r4 # Pointer to format String

lwz r5, STACK_FRAME_OSTICKSTOCALENDARTIME_STRUCT_OFFSET + OSTICKSTOCALENDARTIME_STRUCT_YEAR_OFFSET(r1)

# Derive the last two digits from the year
li r6, 100
divw r7, r5, r6
mullw r6, r7, r6
subf r5, r6, r5

lwz r6, STACK_FRAME_OSTICKSTOCALENDARTIME_STRUCT_OFFSET + OSTICKSTOCALENDARTIME_STRUCT_MONTH_OFFSET(r1)
addi r6, r6, 1
lwz r7, STACK_FRAME_OSTICKSTOCALENDARTIME_STRUCT_OFFSET + OSTICKSTOCALENDARTIME_STRUCT_DAY_OFFSET(r1)
lwz r8, STACK_FRAME_OSTICKSTOCALENDARTIME_STRUCT_OFFSET + OSTICKSTOCALENDARTIME_STRUCT_HOURS_OFFSET(r1)
lwz r9, STACK_FRAME_OSTICKSTOCALENDARTIME_STRUCT_OFFSET + OSTICKSTOCALENDARTIME_STRUCT_MINUTES_OFFSET(r1)
lwz r10, STACK_FRAME_OSTICKSTOCALENDARTIME_STRUCT_OFFSET + OSTICKSTOCALENDARTIME_STRUCT_SECONDS_OFFSET(r1)

crclr 4*cr1+eq
absolute_bl r12, sprintf
cmpwi r3, SCREENSHOT_FILE_PATHNAMELEN
bne- branch_sprintf_failed

# ISFS_CreateFile arguments
addi r3, r1, STACK_FRAME_SCREENSHOT_FILEPATH_BUFFER_OFFSET # Pointer to the filepath
li r4, 0 # attributes
li r5, 3 # owner_perm
li r6, 3 # group_perm
li r7, 3 # other_perm

absolute_ISFS_bl r12, ISFS_CreateFile
cmpwi r3, ISFS_EOK
bne- branch_isfs_createfile_failed

# ISFS_Open arguments
addi r3, r1, STACK_FRAME_SCREENSHOT_FILEPATH_BUFFER_OFFSET # Pointer to the filepath
li r4, ISFS_OPEN_WRITE # Mode

absolute_ISFS_bl r12, ISFS_Open
cmpwi r3, 0 # if (fd < 0)
blt- branch_isfs_open_failed
mr r30, r3 # Preserve the fd in r30

lis r29, HOLLYWOOD_REGISTERS_BASE@h

# Turn on the Wii's Disc Slot LED
lwz r3, HOLLYWOOD_GPIOB_OUT_OFFSET(r29)
ori r3, r3, SLOT_LED_ON_BITS
stw r3, HOLLYWOOD_GPIOB_OUT_OFFSET(r29)

# EGG::Heap::alloc arguments
li r3, VI_REGISTERS_LENGTH # Length of the memory block
li r4, 32 # Alignment of the memory block

# Get the pointer to the System Heap (RKSystem->mpSystemHeap)
.if     (region == 'E' || region == 'e') # RMCE
        lwz r5, -0x5CA8(r13)
.elseif (region == 'P' || region == 'p') # RMCP
        lwz r5, -0x5CA0(r13)
.elseif (region == 'J' || region == 'j') # RMCJ
        lwz r5, -0x5CA0(r13)
.elseif (region == 'K' || region == 'k') # RMCK
        lwz r5, -0x5C80(r13)
.endif

lwz r5, 0x24(r5) # Pointer to the heap to allocate the memory block in
mr r28, r5 # Preserve the pointer to the System Heap in r28

absolute_bl r12, EGG_Heap_alloc
cmpwi r3, NULL
beq- branch_isfs_write_egg_heap_alloc_failed

# memcpy arguments
mr r27, r3 # Preserve the pointer to the allocated memory block in r27
lis r4, VI_REGISTERS_BASE@h # Pointer to the buffer to copy from
ori r4, r4, VI_REGISTERS_BASE@l
li r5, VI_REGISTERS_LENGTH # Number of bytes to copy

absolute_bl r12, memcpy

# ISFS_Write arguments
mr r3, r30 # fd
mr r4, r27 # Pointer to the buffer to write from
li r5, VI_REGISTERS_LENGTH # Number of bytes to write

absolute_ISFS_bl r12, ISFS_Write
cmpwi r3, VI_REGISTERS_LENGTH
bne- branch_isfs_write_failed

# ISFS_Write arguments
mr r3, r30 # fd

# Calculate the address of XFB1
lwz r4, 0x1C(r27)
andis. r5, r4, 0x1000
beq- branch_page_offset_bit_not_set
slwi r4, r4, 5
branch_page_offset_bit_not_set:
addis r4, r4, 0x8000

# Calculate the Screen Height
# uint32 sheight = (uint32)(((viregs[0] << 5) | (viregs[1] >> 3)) & 0x07FE);
lbz r5, 0(r27)
slwi r5, r5, 5
lbz r6, 1(r27)
srwi r6, r6, 3
or r5, r5, r6
andi. r5, r5, 0x7FE

# Calculate the Screen Width
# uint32 swidth = (uint32)(viregs[0x49] << 3);
lbz r6, 0x49(r27)
slwi r6, r6, 3

# Calculate the length of XFB1
# (XFB1 Address + Screen Height * Screen Width * 2) - XFB1 Address
mullw r5, r6, r5
slwi r5, r5, 1
add r5, r5, r4

subf r5, r4, r5 # Number of bytes to write

absolute_ISFS_bl r12, ISFS_Write

branch_isfs_write_failed:
# EGG::Heap::free arguments
mr r3, r27 # Pointer to the block of memory to free
mr r4, r28 # Pointer to the heap where the block of memory to free is located

absolute_bl r12, EGG_Heap_free

branch_isfs_write_egg_heap_alloc_failed:
# Turn off the Wii's Disc Slot LED
lwz r3, HOLLYWOOD_GPIOB_OUT_OFFSET(r29)
rlwinm r3, r3, 0, 27, 25
stw r3, HOLLYWOOD_GPIOB_OUT_OFFSET(r29)

# ISFS_Close argument
mr r3, r30 # fd

absolute_ISFS_bl r12, ISFS_Close

branch_isfs_createdir_failed:
branch_sprintf_failed:
branch_isfs_createfile_failed:
branch_isfs_open_failed:
# Function Epilogue...
pop_stack r2, STACK_FRAME_LENGTH

Notes:
- Extreme overuse of this code may cause wear to the Wii's NAND memory. As per the NAND Manual:

"Wear on Wii Console NAND Memory

Write operations will wear out Wii console NAND memory, so please avoid frequent writing. (Use as virtual memory is prohibited.)
Please limit its average frequency to less than once per minute."

- Screenshot files are not automatically deleted, you are responsible for maintaining them.
- The Wii's Disc Slot LED will turn on when the code is attempting to write the file.

Code Creator: Star
Code Credits: Link and dcx2 (Gecko dotNet Screenshot Code), Chadderz (wii-ct-code), Riidefi (RKSystem Structure and EGG Methods)
[Image: M8Vn0Cu.png]
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)