diff --git a/config/HAFE/splits.txt b/config/HAFE/splits.txt index 3c65b94..3b02b44 100644 --- a/config/HAFE/splits.txt +++ b/config/HAFE/splits.txt @@ -233,6 +233,26 @@ System.cpp: .sbss start:0x80330AD0 end:0x80330B78 .sdata2 start:0x80331F00 end:0x80331F78 +ChannelMathData.cpp: + .sdata2 start:0x80331F78 end:0x80331FC0 + +WiiConnect24.cpp: + .text start:0x80032610 end:0x80035BA8 + .data start:0x8019BD08 end:0x8019C189 + .bss start:0x801D99E0 end:0x801F6B28 + .sdata start:0x80330170 end:0x80330190 + .sbss start:0x80330B78 end:0x80330B98 + +DragScroll.cpp: + .text start:0x80035BA8 end:0x80035DC4 + +WeatherUtil.cpp: + .text start:0x80035DC4 end:0x800367F4 + .rodata start:0x8018F4D0 end:0x8018F4E6 + +MtxUtil.cpp: + .text start:0x800367F4 end:0x80036978 + Tween.cpp: .text start:0x80036994 end:0x800369F4 .sdata2 start:0x80331FC0 end:0x80331FC8 diff --git a/config/HAFE/symbols.txt b/config/HAFE/symbols.txt index 09f913c..873c3b2 100644 --- a/config/HAFE/symbols.txt +++ b/config/HAFE/symbols.txt @@ -772,23 +772,23 @@ __nw__FUll = .text:0x800325D4; // type:function size:0x14 scope:global __nwa__FUl = .text:0x800325E8; // type:function size:0x10 scope:global __dl__FPv = .text:0x800325F8; // type:function size:0xC scope:global __dla__FPv = .text:0x80032604; // type:function size:0xC scope:global -InitDownloader = .text:0x80032610; // type:function size:0xAC -UpdateDownloader = .text:0x800326BC; // type:function size:0x60 -ShutdownDownloader = .text:0x8003271C; // type:function size:0x90 +InitDownloader__Fv = .text:0x80032610; // type:function size:0xAC +UpdateDownloader__Fv = .text:0x800326BC; // type:function size:0x60 +ShutdownDownloader__Fl = .text:0x8003271C; // type:function size:0x90 getWeather__13CWiiConnect24FPvPxPUlPvPxPUlPCcPCcUlUlUl = .text:0x800327AC; // type:function size:0x444 downloadWeatherForecast__13CWiiConnect24FPvPxPUlUl = .text:0x80032BF0; // type:function size:0x420 downloadWeatherShort__13CWiiConnect24FPvPxPUlUl = .text:0x80033010; // type:function size:0x420 setWeather__13CWiiConnect24FPCcPCcUlUlUl = .text:0x80033430; // type:function size:0x448 deleteWeather__13CWiiConnect24Fv = .text:0x80033878; // type:function size:0x408 -DownloaderThread = .text:0x80033C80; // type:function size:0x8E4 -fn_80034564 = .text:0x80034564; // type:function size:0x614 -fn_80034B78 = .text:0x80034B78; // type:function size:0x9D4 -fn_8003554C = .text:0x8003554C; // type:function size:0x180 -fn_800356CC = .text:0x800356CC; // type:function size:0x1DC -fn_800358A8 = .text:0x800358A8; // type:function size:0xF0 -fn_80035998 = .text:0x80035998; // type:function size:0x1D8 -fn_80035B70 = .text:0x80035B70; // type:function size:0x1C -fn_80035B8C = .text:0x80035B8C; // type:function size:0x1C +DownloaderThread__FPv = .text:0x80033C80; // type:function size:0x8E4 +DownloadFiles__FP8WC24TaskUlUl = .text:0x80034564; // type:function size:0x614 +CreateDownloadTasks__FP8WC24TaskUlUlUcUsUsPUl = .text:0x80034B78; // type:function size:0x9D4 +ExecuteDownloadTask__FP8WC24TasklUlUs = .text:0x8003554C; // type:function size:0x180 +DeleteTaskFiles__FP8WC24TaskUlUl = .text:0x800356CC; // type:function size:0x1DC +MapDownloadError__F8NWC24Err = .text:0x800358A8; // type:function size:0xF0 +ReadCompressedFile__FP8WC24TaskPvPvUlPUl = .text:0x80035998; // type:function size:0x1D8 +WC24Alloc__FUll = .text:0x80035B70; // type:function size:0x1C +WC24Free__FPvPvl = .text:0x80035B8C; // type:function size:0x1C __ct__10DragScrollFv = .text:0x80035BA8; // type:function size:0x40 Reset__10DragScrollFv = .text:0x80035BE8; // type:function size:0x40 UpdateChan__10DragScrollFl = .text:0x80035C28; // type:function size:0x110 @@ -798,7 +798,7 @@ WrapHourTo24__FPl = .text:0x80035DF0; // type:function size:0x2C SmoothApproach__FPfffff = .text:0x80035E1C; // type:function size:0xB4 SmoothApproachAngle__FPfffff = .text:0x80035ED0; // type:function size:0x128 EaseCos__FUs = .text:0x80035FF8; // type:function size:0x3C -fn_80036034 = .text:0x80036034; // type:function size:0x2A4 +FormatDigits__FlPll = .text:0x80036034; // type:function size:0x2A4 scope:global FormatNumber__FlPwli = .text:0x800362D8; // type:function size:0xFC FormatInt__FlPw = .text:0x800363D4; // type:function size:0x104 MinutesToCalendarTime__FUlP14OSCalendarTime = .text:0x800364D8; // type:function size:0x18C @@ -1489,8 +1489,8 @@ VFi_nanddrv_init_drv_tbl = .text:0x800662B8; // type:function size:0x18 scope:gl nanddrv_physical_read = .text:0x800662D0; // type:function size:0x1A0 scope:local nanddrv_physical_write = .text:0x80066470; // type:function size:0x194 scope:local VFi_InitSDWrok = .text:0x80066604; // type:function size:0x4 -fn_80066608 = .text:0x80066608; // type:function size:0x1C8 -fn_800667D0 = .text:0x800667D0; // type:function size:0xC +SOInit = .text:0x80066608; // type:function size:0x1C8 +SOStartup = .text:0x800667D0; // type:function size:0xC fn_800667DC = .text:0x800667DC; // type:function size:0x428 fn_80066C04 = .text:0x80066C04; // type:function size:0x1B0 fn_80066DB4 = .text:0x80066DB4; // type:function size:0xE8 @@ -1509,7 +1509,7 @@ fn_80067B70 = .text:0x80067B70; // type:function size:0xF0 NETGetUniversalCalendar = .text:0x80067C60; // type:function size:0xE4 scope:global NETCalcCRC32 = .text:0x80067D44; // type:function size:0x134 fn_80067E78 = .text:0x80067E78; // type:function size:0x278 -fn_800680F0 = .text:0x800680F0; // type:function size:0x80 +SOGetLastError = .text:0x800680F0; // type:function size:0x80 fn_80068170 = .text:0x80068170; // type:function size:0x178 fn_800682E8 = .text:0x800682E8; // type:function size:0x8 STD_strnlen = .text:0x800682F0; // type:function size:0x30 scope:global @@ -1598,31 +1598,31 @@ NWC24iIoctlResourceManager = .text:0x8006DCBC; // type:function size:0x48 scope: NWC24iIoctlResourceManagerAsync = .text:0x8006DD04; // type:function size:0x5C scope:global NWC24iIsAsyncRequestPending = .text:0x8006DD60; // type:function size:0x8 scope:global CallbackAsyncIpc = .text:0x8006DD68; // type:function size:0x1C scope:local -fn_8006DD84 = .text:0x8006DD84; // type:function size:0x240 -fn_8006DFC4 = .text:0x8006DFC4; // type:function size:0xA0 -fn_8006E064 = .text:0x8006E064; // type:function size:0x9C -fn_8006E100 = .text:0x8006E100; // type:function size:0x108 -fn_8006E208 = .text:0x8006E208; // type:function size:0x24C -fn_8006E454 = .text:0x8006E454; // type:function size:0x8C -fn_8006E4E0 = .text:0x8006E4E0; // type:function size:0x154 -fn_8006E634 = .text:0x8006E634; // type:function size:0x1B4 -fn_8006E7E8 = .text:0x8006E7E8; // type:function size:0xC -fn_8006E7F4 = .text:0x8006E7F4; // type:function size:0x16C -fn_8006E960 = .text:0x8006E960; // type:function size:0x160 -fn_8006EAC0 = .text:0x8006EAC0; // type:function size:0x100 -fn_8006EBC0 = .text:0x8006EBC0; // type:function size:0x154 -fn_8006ED14 = .text:0x8006ED14; // type:function size:0x98 -fn_8006EDAC = .text:0x8006EDAC; // type:function size:0x70 +NWC24InitDlTask = .text:0x8006DD84; // type:function size:0x240 +NWC24SetDlId = .text:0x8006DFC4; // type:function size:0xA0 +NWC24GetDlId = .text:0x8006E064; // type:function size:0x9C +NWC24SetDlPriority = .text:0x8006E100; // type:function size:0x108 +NWC24SetDlInterval = .text:0x8006E208; // type:function size:0x24C +NWC24GetDlInterval = .text:0x8006E454; // type:function size:0x8C +NWC24SetDlRetryMargin = .text:0x8006E4E0; // type:function size:0x154 +NWC24SetDlUrl = .text:0x8006E634; // type:function size:0x1B4 +NWC24GetDlUrl = .text:0x8006E7E8; // type:function size:0xC +NWC24SetDlFlags = .text:0x8006E7F4; // type:function size:0x16C +NWC24SetDlFilename = .text:0x8006E960; // type:function size:0x160 +NWC24GetDlFilename = .text:0x8006EAC0; // type:function size:0x100 +NWC24SetDlCount = .text:0x8006EBC0; // type:function size:0x154 +NWC24GetDlLastUpdate = .text:0x8006ED14; // type:function size:0x98 +NWC24iCheckDlTask = .text:0x8006EDAC; // type:function size:0x70 fn_8006EE1C = .text:0x8006EE1C; // type:function size:0x13C fn_8006EF58 = .text:0x8006EF58; // type:function size:0x244 -fn_8006F19C = .text:0x8006F19C; // type:function size:0x3F4 -fn_8006F590 = .text:0x8006F590; // type:function size:0x1A4 -fn_8006F734 = .text:0x8006F734; // type:function size:0x23C +NWC24UpdateDlTask = .text:0x8006F19C; // type:function size:0x3F4 +NWC24DeleteDlTask = .text:0x8006F590; // type:function size:0x1A4 +NWC24AddDlTask = .text:0x8006F734; // type:function size:0x23C NWC24GetDlTask = .text:0x8006F970; // type:function size:0x170 scope:global -fn_8006FAE0 = .text:0x8006FAE0; // type:function size:0x16C +NWC24GetDlNextTime = .text:0x8006FAE0; // type:function size:0x16C fn_8006FC4C = .text:0x8006FC4C; // type:function size:0x2C0 -fn_8006FF0C = .text:0x8006FF0C; // type:function size:0x1A4 -fn_800700B0 = .text:0x800700B0; // type:function size:0x26C +NWC24GetDlVfName = .text:0x8006FF0C; // type:function size:0x1A4 +NWC24CreateDlVf = .text:0x800700B0; // type:function size:0x26C NWC24iOpenDlTaskList = .text:0x8007031C; // type:function size:0x5C scope:global NWC24iCloseDlTaskList = .text:0x80070378; // type:function size:0x8 scope:global fn_80070380 = .text:0x80070380; // type:function size:0x350 @@ -5976,7 +5976,7 @@ gGlobeDotPositions = .rodata:0x8017B508; // type:object size:0x8E20 gGlobeDotSizes = .rodata:0x80184328; // type:object size:0x2388 data:byte gGlobeDotColorIndices = .rodata:0x801866B0; // type:object size:0x2388 data:byte gGlobeDotColors = .rodata:0x80188A38; // type:object size:0x6A98 -lbl_8018F4D0 = .rodata:0x8018F4D0; // type:object size:0x16 data:wstring +sWeatherDigits = .rodata:0x8018F4D0; // type:object size:0x16 data:wstring sGlyphTextureIds = .rodata:0x8018F4E8; // type:object size:0x164 scope:local data:4byte sLanguageTextures = .rodata:0x8018F64C; // type:object size:0x1C scope:local sDateGlyphs = .rodata:0x8018F668; // type:object size:0x24 scope:local data:2byte @@ -6729,7 +6729,48 @@ lbl_8019BC98 = .data:0x8019BC98; // type:object size:0x1C data:wstring lbl_8019BCB4 = .data:0x8019BCB4; // type:object size:0x1A data:wstring lbl_8019BCD0 = .data:0x8019BCD0; // type:object size:0x1A data:wstring gLastUpdatedPrefixes = .data:0x8019BCEC; // type:object size:0x1C -lbl_8019BD08 = .data:0x8019BD08; // type:object size:0x488 +@4009 = .data:0x8019BD08; // type:object size:0x17 scope:local data:string +@4010 = .data:0x8019BD20; // type:object size:0x9 scope:local align:4 data:string +@4011 = .data:0x8019BD2C; // type:object size:0x15 scope:local align:4 data:string +@4012 = .data:0x8019BD48; // type:object size:0x18 scope:local align:8 data:string +@4194 = .data:0x8019BD60; // type:object size:0x19 scope:local data:string +@4195 = .data:0x8019BD80; // type:object size:0x18 scope:local align:8 data:string +@4196 = .data:0x8019BD98; // type:object size:0x11 scope:local data:string +@4197 = .data:0x8019BDAC; // type:object size:0x1B scope:local align:4 data:string +@4198 = .data:0x8019BDC8; // type:object size:0x20 scope:local align:4 data:string +@4199 = .data:0x8019BDE8; // type:object size:0x11 scope:local data:string +@4200 = .data:0x8019BE00; // type:object size:0x10 scope:local align:8 data:string +@4201 = .data:0x8019BE10; // type:object size:0x1D scope:local data:string +@4202 = .data:0x8019BE30; // type:object size:0x15 scope:local align:4 data:string +@4203 = .data:0x8019BE48; // type:object size:0x19 scope:local align:4 data:string +@4204 = .data:0x8019BE64; // type:object size:0x16 scope:local align:4 data:string +@4205 = .data:0x8019BE7C; // type:object size:0x1F scope:local align:4 data:string +@4206 = .data:0x8019BE9C; // type:object size:0x1D scope:local align:4 data:string +@4207 = .data:0x8019BEBC; // type:object size:0x19 scope:local align:4 data:string +@4495 = .data:0x8019BED8; // type:object size:0x1D scope:local align:4 data:string +@4496 = .data:0x8019BEF8; // type:object size:0x1A scope:local align:4 data:string +@4497 = .data:0x8019BF14; // type:object size:0x17 scope:local align:4 data:string +@4498 = .data:0x8019BF2C; // type:object size:0x15 scope:local align:4 data:string +@4499 = .data:0x8019BF44; // type:object size:0x1A scope:local align:4 data:string +@4500 = .data:0x8019BF60; // type:object size:0x18 scope:local align:4 data:string +@4501 = .data:0x8019BF78; // type:object size:0x1D scope:local data:string +@4502 = .data:0x8019BF98; // type:object size:0x1A scope:local align:4 data:string +@4503 = .data:0x8019BFB4; // type:object size:0x1D scope:local align:4 data:string +@4504 = .data:0x8019BFD8; // type:object size:0x20 scope:local align:8 data:string +@4505 = .data:0x8019BFF8; // type:object size:0x1D scope:local data:string +@4506 = .data:0x8019C018; // type:object size:0x1A scope:local align:4 data:string +@4507 = .data:0x8019C034; // type:object size:0x19 scope:local align:4 data:string +@4508 = .data:0x8019C050; // type:object size:0x1C scope:local align:4 data:string +@4509 = .data:0x8019C06C; // type:object size:0x17 scope:local data:string +@4541 = .data:0x8019C084; // type:object size:0x11 scope:local align:4 data:string +@4542 = .data:0x8019C098; // type:object size:0x14 scope:local align:4 data:string +@4543 = .data:0x8019C0B0; // type:object size:0x20 scope:local align:8 data:string +@4602 = .data:0x8019C0D0; // type:object size:0x16 scope:local data:string +@4603 = .data:0x8019C0E8; // type:object size:0x1C scope:local align:4 data:string +@4674 = .data:0x8019C104; // type:object size:0x15 scope:local data:string +@4675 = .data:0x8019C11C; // type:object size:0x27 scope:local align:4 data:string +@4676 = .data:0x8019C144; // type:object size:0x22 scope:local align:4 data:string +@4677 = .data:0x8019C168; // type:object size:0x21 scope:local align:4 data:string lbl_8019C190 = .data:0x8019C190; // type:object size:0xC data:4byte lbl_8019C19C = .data:0x8019C19C; // type:object size:0xC data:4byte lbl_8019C1A8 = .data:0x8019C1A8; // type:object size:0xC data:4byte @@ -8074,7 +8115,7 @@ lbl_801D2BD8 = .data:0x801D2BD8; // type:object size:0x25 scope:local data:strin lbl_801D2C00 = .data:0x801D2C00; // type:object size:0x21 scope:local data:string lbl_801D2C24 = .data:0x801D2C24; // type:object size:0x1C scope:local data:string @2473 = .data:0x801D2C40; // type:object size:0x15 scope:local data:string -@2610 = .data:0x801D2C58; // type:object size:0x27 scope:local data:string +@4011 = .data:0x801D2C58; // type:object size:0x27 scope:local data:string @2626 = .data:0x801D2C80; // type:object size:0x1D scope:local data:string @2627 = .data:0x801D2CA0; // type:object size:0x30 scope:local data:string gap_conn_trace_unexpected_addr = .data:0x801D2CD0; // type:object size:0x44 data:string @@ -8513,14 +8554,14 @@ gMotorCooldown = .bss:0x801D9840; // type:object size:0x10 scope:global data:4by gMotorPattern = .bss:0x801D9850; // type:object size:0x10 scope:global data:4byte gMotorPatternPos = .bss:0x801D9860; // type:object size:0x10 scope:global data:4byte gContentHandles = .bss:0x801D9870; // type:object size:0x168 scope:global -lbl_801D99E0 = .bss:0x801D99E0; // type:object size:0x4000 -lbl_801DD9E0 = .bss:0x801DD9E0; // type:object size:0x20 -lbl_801DDA00 = .bss:0x801DDA00; // type:object size:0x40 -lbl_801DDA40 = .bss:0x801DDA40; // type:object size:0x318 -lbl_801DDD58 = .bss:0x801DDD58; // type:object size:0x8000 -lbl_801E5D58 = .bss:0x801E5D58; // type:object size:0x10000 data:byte +gNWC24Work = .bss:0x801D99E0; // type:object size:0x4000 +gWC24Queue = .bss:0x801DD9E0; // type:object size:0x20 +gWC24QueueBuffer = .bss:0x801DDA00; // type:object size:0x40 +gWC24Thread = .bss:0x801DDA40; // type:object size:0x318 +gWC24ThreadStack = .bss:0x801DDD58; // type:object size:0x8000 +gWC24FileBuffer = .bss:0x801E5D58; // type:object size:0x10000 data:byte gWC24Tasks = .bss:0x801F5D58; // type:object size:0xDC0 -lbl_801F6B18 = .bss:0x801F6B18; // type:object size:0x10 +gWC24Spare = .bss:0x801F6B18; // type:object size:0x10 gModelMtx = .bss:0x801F6B28; // type:object size:0x30 data:4byte lbl_801F6B58 = .bss:0x801F6B58; // type:object size:0x10 lbl_801F6B68 = .bss:0x801F6B68; // type:object size:0xF0 data:byte @@ -9243,12 +9284,12 @@ lbl_80330158 = .sdata:0x80330158; // type:object size:0x2 data:string gUnblackNextFrame = .sdata:0x8033015A; // type:object size:0x1 scope:global data:byte lbl_80330160 = .sdata:0x80330160; // type:object size:0x8 data:wstring lbl_80330168 = .sdata:0x80330168; // type:object size:0x8 data:wstring -lbl_80330170 = .sdata:0x80330170; // type:object size:0x4 data:string -lbl_80330174 = .sdata:0x80330174; // type:object size:0x7 data:string -lbl_8033017C = .sdata:0x8033017C; // type:object size:0x2 data:string -lbl_80330180 = .sdata:0x80330180; // type:object size:0x7 data:string -lbl_80330188 = .sdata:0x80330188; // type:object size:0x4 data:string -lbl_8033018C = .sdata:0x8033018C; // type:object size:0x4 data:string +sVfDriveName = .sdata:0x80330170; // type:object size:0x4 data:string +sVfRootPath = .sdata:0x80330174; // type:object size:0x7 data:string +sVfReadMode = .sdata:0x8033017C; // type:object size:0x2 align:4 data:string +sBinNameFormat = .sdata:0x80330180; // type:object size:0x7 align:4 data:string +sForecastFileSuffix = .sdata:0x80330188; // type:object size:0x4 align:4 data:string +sShortFileSuffix = .sdata:0x8033018C; // type:object size:0x4 data:string lbl_80330190 = .sdata:0x80330190; // type:object size:0x4 data:string lbl_80330194 = .sdata:0x80330194; // type:object size:0x5 data:string lbl_8033019C = .sdata:0x8033019C; // type:object size:0x4 data:string @@ -9690,16 +9731,18 @@ gCursor = .sbss:0x80330B68; // type:object size:0x4 scope:global data:4byte gWeatherScene = .sbss:0x80330B6C; // type:object size:0x4 scope:global data:4byte gFatalScene = .sbss:0x80330B70; // type:object size:0x4 scope:global data:4byte gUnk80330B74 = .sbss:0x80330B74; // type:object size:0x4 scope:global data:4byte -lbl_80330B78 = .sbss:0x80330B78; // type:object size:0x1 data:byte -lbl_80330B79 = .sbss:0x80330B79; // type:object size:0x1 data:byte -lbl_80330B7A = .sbss:0x80330B7A; // type:object size:0x1 data:byte -lbl_80330B7B = .sbss:0x80330B7B; // type:object size:0x1 data:byte -lbl_80330B7C = .sbss:0x80330B7C; // type:object size:0x1 data:byte -lbl_80330B80 = .sbss:0x80330B80; // type:object size:0x4 data:4byte -lbl_80330B84 = .sbss:0x80330B84; // type:object size:0x4 data:4byte -lbl_80330B88 = .sbss:0x80330B88; // type:object size:0x4 data:4byte -lbl_80330B8C = .sbss:0x80330B8C; // type:object size:0x4 data:4byte -lbl_80330B90 = .sbss:0x80330B90; // type:object size:0x8 data:4byte +gNWC24LibOpened = .sbss:0x80330B78; // type:object size:0x1 data:byte +gWC24DownloadComplete = .sbss:0x80330B79; // type:object size:0x1 data:byte +gWC24ShutdownFlag1 = .sbss:0x80330B7A; // type:object size:0x1 data:byte +gWC24NetInitialized = .sbss:0x80330B7B; // type:object size:0x1 data:byte +gWC24ShutdownFlag2 = .sbss:0x80330B7C; // type:object size:0x1 data:byte +gWC24ShutdownPadding = .sbss:0x80330B7D; // type:object size:0x3 +gWC24DownloadIndex = .sbss:0x80330B80; // type:object size:0x4 data:4byte +gWC24QueueIndex = .sbss:0x80330B84; // type:object size:0x4 data:4byte +gWC24TaskCount = .sbss:0x80330B88; // type:object size:0x4 data:4byte +gWC24HeapBlock = .sbss:0x80330B8C; // type:object size:0x4 data:4byte +gWC24Heap = .sbss:0x80330B90; // type:object size:0x4 data:4byte +gWC24HeapPadding = .sbss:0x80330B94; // type:object size:0x4 lbl_80330B98 = .sbss:0x80330B98; // type:object size:0x4 data:byte gHomeButton = .sbss:0x80330BA0; // type:object size:0x4 data:4byte gUnkSceneFloat = .sbss:0x80330BA4; // type:object size:0x4 align:4 data:float @@ -10843,24 +10886,24 @@ lbl_80331F68 = .sdata2:0x80331F68; // type:object size:0x4 align:4 data:float lbl_80331F6C = .sdata2:0x80331F6C; // type:object size:0x4 align:4 data:float lbl_80331F70 = .sdata2:0x80331F70; // type:object size:0x4 align:4 data:float lbl_80331F74 = .sdata2:0x80331F74; // type:object size:0x4 data:byte -lbl_80331F78 = .sdata2:0x80331F78; // type:object size:0x4 align:4 data:float -lbl_80331F7C = .sdata2:0x80331F7C; // type:object size:0x4 align:4 data:float -lbl_80331F80 = .sdata2:0x80331F80; // type:object size:0x4 align:4 data:float -lbl_80331F84 = .sdata2:0x80331F84; // type:object size:0x4 align:4 data:float -lbl_80331F88 = .sdata2:0x80331F88; // type:object size:0x4 align:4 data:float -lbl_80331F8C = .sdata2:0x80331F8C; // type:object size:0x4 align:4 data:float -lbl_80331F90 = .sdata2:0x80331F90; // type:object size:0x4 align:4 data:float -lbl_80331F94 = .sdata2:0x80331F94; // type:object size:0x4 align:4 data:float -lbl_80331F98 = .sdata2:0x80331F98; // type:object size:0x4 align:4 data:float -lbl_80331F9C = .sdata2:0x80331F9C; // type:object size:0x4 align:4 data:float -lbl_80331FA0 = .sdata2:0x80331FA0; // type:object size:0x4 align:4 data:float -lbl_80331FA4 = .sdata2:0x80331FA4; // type:object size:0x4 align:4 data:float -lbl_80331FA8 = .sdata2:0x80331FA8; // type:object size:0x4 align:4 data:float -lbl_80331FAC = .sdata2:0x80331FAC; // type:object size:0x4 align:4 data:float -lbl_80331FB0 = .sdata2:0x80331FB0; // type:object size:0x4 align:4 data:float -lbl_80331FB4 = .sdata2:0x80331FB4; // type:object size:0x4 align:4 data:float -lbl_80331FB8 = .sdata2:0x80331FB8; // type:object size:0x4 align:4 data:float -lbl_80331FBC = .sdata2:0x80331FBC; // type:object size:0x4 align:4 data:float +sDragScrollZero = .sdata2:0x80331F78; // type:object size:0x4 align:4 data:float +sDragScrollOffset = .sdata2:0x80331F7C; // type:object size:0x4 align:4 data:float +sDragScrollThreshold = .sdata2:0x80331F80; // type:object size:0x4 align:4 data:float +sDragScrollNegThreshold = .sdata2:0x80331F84; // type:object size:0x4 align:4 data:float +sDragScrollMax = .sdata2:0x80331F88; // type:object size:0x4 align:4 data:float +sDragScrollAngleScale = .sdata2:0x80331F8C; // type:object size:0x4 align:4 data:float +sDragScrollHalfPi = .sdata2:0x80331F90; // type:object size:0x4 align:4 data:float +sDragScrollTimerScale = .sdata2:0x80331F94; // type:object size:0x4 align:4 data:float +sSmoothEpsilon = .sdata2:0x80331F98; // type:object size:0x4 align:4 data:float +sSmoothEpsilonNeg = .sdata2:0x80331F9C; // type:object size:0x4 align:4 data:float +sAngleZero = .sdata2:0x80331FA0; // type:object size:0x4 align:4 data:float +sAngleFull = .sdata2:0x80331FA4; // type:object size:0x4 align:4 data:float +sAngleHalfNeg = .sdata2:0x80331FA8; // type:object size:0x4 align:4 data:float +sAngleHalf = .sdata2:0x80331FAC; // type:object size:0x4 align:4 data:float +sIdxToFIdx = .sdata2:0x80331FB0; // type:object size:0x4 align:4 data:float +sHalf = .sdata2:0x80331FB4; // type:object size:0x4 align:4 data:float +sDegToRad = .sdata2:0x80331FB8; // type:object size:0x4 align:4 data:float +sDegToFIdx = .sdata2:0x80331FBC; // type:object size:0x4 align:4 data:float lbl_80331FC0 = .sdata2:0x80331FC0; // type:object size:0x8 align:4 data:float lbl_80331FC8 = .sdata2:0x80331FC8; // type:object size:0x4 align:4 data:float lbl_80331FCC = .sdata2:0x80331FCC; // type:object size:0x4 align:4 data:float diff --git a/configure.py b/configure.py index 6ae4df5..1268544 100755 --- a/configure.py +++ b/configure.py @@ -1099,6 +1099,11 @@ def MatchingFor(*versions): Object(Matching, "main.cpp"), Object(Matching, "DrawUtil.cpp"), Object(Matching, "GlobeDots.cpp"), + Object(Matching, "WiiConnect24.cpp"), + Object(Matching, "WeatherUtil.cpp"), + Object(Matching, "ChannelMathData.cpp"), + Object(Matching, "DragScroll.cpp"), + Object(Matching, "MtxUtil.cpp"), Object(Matching, "System.cpp"), Object(Matching, "Tween.cpp"), Object(NonMatching, "LayoutButton.cpp"), diff --git a/include/MSL_C/include/wchar.h b/include/MSL_C/include/wchar.h new file mode 100644 index 0000000..cfee240 --- /dev/null +++ b/include/MSL_C/include/wchar.h @@ -0,0 +1,18 @@ +#ifndef WCHAR_H +#define WCHAR_H + +#include "size_t.h" +#include "wchar_t.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int wcscmp(const wchar_t* lhs, const wchar_t* rhs); +wchar_t* wcsncpy(wchar_t* dst, const wchar_t* src, size_t count); + +#ifdef __cplusplus +} +#endif + +#endif // WCHAR_H diff --git a/include/channel/ChannelMathData.h b/include/channel/ChannelMathData.h new file mode 100644 index 0000000..ab88257 --- /dev/null +++ b/include/channel/ChannelMathData.h @@ -0,0 +1,30 @@ +#ifndef CHANNEL_CHANNEL_MATH_DATA_H +#define CHANNEL_CHANNEL_MATH_DATA_H + +#include + +// These constants are defined in ChannelMathData.cpp. The code units must see +// them as external objects: defining them alongside the functions lets MWCC +// fold them into anonymous local constants, which does not match the target. +extern const f32 sDragScrollZero; +extern const f32 sDragScrollOffset; +extern const f32 sDragScrollThreshold; +extern const f32 sDragScrollNegThreshold; +extern const f32 sDragScrollMax; +extern const f32 sDragScrollAngleScale; +extern const f32 sDragScrollHalfPi; +extern const f32 sDragScrollTimerScale; + +extern const f32 sSmoothEpsilon; +extern const f32 sSmoothEpsilonNeg; +extern const f32 sAngleZero; +extern const f32 sAngleFull; +extern const f32 sAngleHalfNeg; +extern const f32 sAngleHalf; +extern const f32 sIdxToFIdx; +extern const f32 sHalf; + +extern const f32 sDegToRad; +extern const f32 sDegToFIdx; + +#endif // CHANNEL_CHANNEL_MATH_DATA_H diff --git a/include/channel/WiiConnect24.h b/include/channel/WiiConnect24.h index 2896064..9f3090c 100644 --- a/include/channel/WiiConnect24.h +++ b/include/channel/WiiConnect24.h @@ -2,28 +2,84 @@ #define CHANNEL_WII_CONNECT24_H #include -// A WiiConnect24 request running on the worker thread (size 0x1B8) +// A pair of buffers/timestamps/sizes used by a WiiConnect24 download task. +// Each pair occupies 0x60 bytes; the remaining words are reserved and are +// only ever copied as part of the whole task. +struct WC24BufferSet { + void* forecast; // at 0x000 + void* shortForecast; // at 0x004 + u32 unk08[22]; // at 0x008 +}; + +struct WC24TimeSet { + s64* forecast; // at 0x000 + s64* shortForecast; // at 0x004 + u32 unk08[22]; // at 0x008 +}; + +struct WC24SizeSet { + u32* forecast; // at 0x000 + u32* shortForecast; // at 0x004 + u32 unk08[22]; // at 0x008 +}; + +// A request queued for the WiiConnect24 worker thread (size 0x1B8) struct WC24Task { - u8 unk0[0x164]; // at 0x0 - s32 mState; // at 0x164 - s32 mResult; // at 0x168 - s32 mErrorCode; // at 0x16C - s32 mNwc24Result; // at 0x170 - u8 unk174[0x1B8 - 0x174]; + u32 mType; // at 0x000 + u16 mTaskId; // at 0x004 + u16 mSubTaskId; // at 0x006 + u32 unk008; // at 0x008 + u32 unk00C; // at 0x00C + u32 unk010; // at 0x010 + WC24BufferSet mBuffers; // at 0x014 + WC24TimeSet mTimes; // at 0x074 + WC24SizeSet mSizes; // at 0x0D4 + s64 mNextDownloadTimes[2]; // at 0x138 + const char* mForecastUrl; // at 0x148 + const char* mShortUrl; // at 0x14C + u32 mForecastBufferSize; // at 0x150 + u32 mBufferSizes[2]; // at 0x154 + u16 mDownloadInterval; // at 0x15C + u16 mDownloadCount; // at 0x15E + u8 mRedownload; // at 0x160 + s32 mState; // at 0x164 + s32 mResult; // at 0x168 + s32 mErrorCode; // at 0x16C + s32 mNwc24Result; // at 0x170 + char mMessage[0x40]; // at 0x174 }; extern WC24Task gWC24Tasks[]; +enum WC24TaskType { + WC24_TASK_GET_WEATHER = 0, + WC24_TASK_DOWNLOAD_WEATHER = 1, + WC24_TASK_SET_WEATHER = 2, + WC24_TASK_DELETE_WEATHER = 3, +}; + +enum WC24TaskId { + WC24_TASK_ID_NONE = 0, + WC24_TASK_ID_FORECAST = 3, + WC24_TASK_ID_SHORT = 4, +}; + +void InitDownloader(void); +void UpdateDownloader(void); +void ShutdownDownloader(s32 event); + + // WiiConnect24.cpp: weather download tasks class CWiiConnect24 { public: - static s32 getWeather(void* forecastBuf, s64* forecastTime, u32* forecastSize, void* shortBuf, - s64* shortTime, u32* shortSize, const char* forecastUrl, const char* shortUrl, + static s32 getWeather(void* forecastBuf, s64* forecastTime, u32* forecastSize, + void* shortBuf, s64* shortTime, u32* shortSize, + const char* forecastUrl, const char* shortUrl, u32 forecastBufSize, u32 forecastMaxSize, u32 shortBufSize); static s32 downloadWeatherForecast(void* buf, s64* time, u32* size, u32 bufSize); static s32 downloadWeatherShort(void* buf, s64* time, u32* size, u32 bufSize); - static s32 setWeather(const char* forecastUrl, const char* shortUrl, u32 forecastBufSize, u32 arg3, - u32 arg4); + static s32 setWeather(const char* forecastUrl, const char* shortUrl, u32 forecastBufSize, + u32 redownload, u32 interval); static s32 deleteWeather(); }; diff --git a/include/channel/WiiConnect24Internal.h b/include/channel/WiiConnect24Internal.h new file mode 100644 index 0000000..2075211 --- /dev/null +++ b/include/channel/WiiConnect24Internal.h @@ -0,0 +1,40 @@ +#ifndef CHANNEL_WII_CONNECT24_INTERNAL_H +#define CHANNEL_WII_CONNECT24_INTERNAL_H +#include + +#include +#include + +#include + +extern OSMessageQueue gWC24Queue; +extern OSMessage gWC24QueueBuffer[16]; +extern OSThread gWC24Thread; +extern u8 gWC24ThreadStack[]; +extern u8 gWC24FileBuffer[]; +extern volatile u8 gWC24DownloadComplete; +extern volatile u8 gWC24NetInitialized; +extern volatile u8 gWC24ShutdownFlag1; +extern volatile u8 gWC24ShutdownFlag2; +extern volatile s32 gWC24DownloadIndex; +extern volatile s32 gWC24QueueIndex; +extern volatile s32 gWC24TaskCount; +extern void* gWC24HeapBlock; +extern MEMiHeapHead* gWC24Heap; + +// The SO block is not split yet; keep the dependency declarations together. +#ifdef __cplusplus +extern "C" { +#endif +s32 SOInit(void* init); +s32 SOStartup(void); +s32 SOGetLastError(void); +#ifdef __cplusplus +} +#endif + +void* DownloaderThread(void* pArg); +void* WC24Alloc(u32 unused, s32 size); +void WC24Free(void* unused, void* pBlock, s32 size); + +#endif diff --git a/include/revolution/NWC24/NWC24Download.h b/include/revolution/NWC24/NWC24Download.h index f39ffda..8528841 100644 --- a/include/revolution/NWC24/NWC24Download.h +++ b/include/revolution/NWC24/NWC24Download.h @@ -42,6 +42,26 @@ typedef struct NWC24DlTask { NWC24Err NWC24CheckDlTask(NWC24DlTask* pTask); NWC24Err NWC24DeleteDlTaskForced(NWC24DlTask* pTask); NWC24Err NWC24GetDlTask(NWC24DlTask* pTask, u16 id); +NWC24Err NWC24InitDlTask(NWC24DlTask* pTask, u32 type); +NWC24Err NWC24SetDlId(NWC24DlTask* pTask, u16 id); +NWC24Err NWC24GetDlId(NWC24DlTask* pTask, u32* pId); +NWC24Err NWC24SetDlPriority(NWC24DlTask* pTask, s32 priority); +NWC24Err NWC24SetDlInterval(NWC24DlTask* pTask, u16 interval); +NWC24Err NWC24GetDlInterval(NWC24DlTask* pTask, u16* pInterval); +NWC24Err NWC24SetDlRetryMargin(NWC24DlTask* pTask, s32 margin); +NWC24Err NWC24SetDlUrl(NWC24DlTask* pTask, const char* url); +NWC24Err NWC24GetDlUrl(NWC24DlTask* pTask, char* url, u32 len); +NWC24Err NWC24SetDlFlags(NWC24DlTask* pTask, u32 flags); +NWC24Err NWC24SetDlFilename(NWC24DlTask* pTask, const char* name); +NWC24Err NWC24GetDlFilename(NWC24DlTask* pTask, char* name, u32 len); +NWC24Err NWC24SetDlCount(NWC24DlTask* pTask, s16 count); +NWC24Err NWC24GetDlLastUpdate(NWC24DlTask* pTask, s64* time); +NWC24Err NWC24UpdateDlTask(NWC24DlTask* pTask); +NWC24Err NWC24DeleteDlTask(NWC24DlTask* pTask); +NWC24Err NWC24AddDlTask(NWC24DlTask* pTask); +NWC24Err NWC24GetDlNextTime(NWC24DlTask* pTask, s64* time); +NWC24Err NWC24GetDlVfName(NWC24DlTask* pTask, char* name, u32 len); +NWC24Err NWC24CreateDlVf(NWC24DlTask* pTask, u32 size); #ifdef __cplusplus } diff --git a/include/revolution/NWC24/NWC24Schedule.h b/include/revolution/NWC24/NWC24Schedule.h index 618b89b..98c73fb 100644 --- a/include/revolution/NWC24/NWC24Schedule.h +++ b/include/revolution/NWC24/NWC24Schedule.h @@ -10,6 +10,7 @@ extern "C" { s32 NWC24SuspendScheduler(void); s32 NWC24ResumeScheduler(void); +NWC24Err NWC24ExecDownloadTask(s32 mode, u16 taskId, s32 flags); #ifdef __cplusplus } diff --git a/include/revolution/NWC24/internal/NWC24iDownload.h b/include/revolution/NWC24/internal/NWC24iDownload.h index 1662067..b33dd21 100644 --- a/include/revolution/NWC24/internal/NWC24iDownload.h +++ b/include/revolution/NWC24/internal/NWC24iDownload.h @@ -71,6 +71,7 @@ NWC24Err NWC24iCloseDlTaskList(void); NWC24iDlHeader* NWC24iGetCachedDlHeader(void); NWC24Err NWC24iCheckHeaderConsistency(NWC24iDlHeader* pHeader, BOOL clear) DECOMP_DONT_INLINE; +NWC24Err NWC24iCheckDlTask(NWC24DlTask* pTask); NWC24Err NWC24iLoadDlHeader(void); #ifdef __cplusplus diff --git a/include/revolution/NWC24/internal/NWC24iSystem.h b/include/revolution/NWC24/internal/NWC24iSystem.h index 09f770b..9948ac7 100644 --- a/include/revolution/NWC24/internal/NWC24iSystem.h +++ b/include/revolution/NWC24/internal/NWC24iSystem.h @@ -8,7 +8,7 @@ extern "C" { #endif NWC24Err NWC24iPrepareShutdown(void); -NWC24Err NWC24iRequestShutdown(u32 event, NWC24Err* pResult); +NWC24Err NWC24iRequestShutdown(u32 event); #ifdef __cplusplus } diff --git a/include/revolution/SC/scapi.h b/include/revolution/SC/scapi.h index deda3be..2acc29b 100644 --- a/include/revolution/SC/scapi.h +++ b/include/revolution/SC/scapi.h @@ -116,7 +116,7 @@ typedef struct SCSimpleAddress { u8 SCGetAspectRatio(void); s8 SCGetDisplayOffsetH(void); u8 SCGetEuRgb60Mode(void); -void SCGetIdleMode(SCIdleModeInfo* mode); +BOOL SCGetIdleMode(SCIdleModeInfo* mode); u8 SCGetLanguage(void); BOOL SCSetLanguage(u8 language); BOOL SCGetParentalControl(SCParentalControlsInfo* pcInfo); diff --git a/src/ChannelMathData.cpp b/src/ChannelMathData.cpp new file mode 100644 index 0000000..01621ca --- /dev/null +++ b/src/ChannelMathData.cpp @@ -0,0 +1,25 @@ +#include +#include + +// Definitions deliberately live in a separate TU from the functions that use +// them. This forces named external constants; same-TU definitions are folded +// by MWCC into anonymous locals and break the target match. + +const f32 sDragScrollZero = 0.0f; +const f32 sDragScrollOffset = 228.0f; +const f32 sDragScrollThreshold = 20.0f; +const f32 sDragScrollNegThreshold = -20.0f; +const f32 sDragScrollMax = 185.2f; +const f32 sDragScrollAngleScale = 40.743664f; +const f32 sDragScrollHalfPi = 1.5707964f; +const f32 sDragScrollTimerScale = 23.0f; +const f32 sSmoothEpsilon = 0.0008f; +const f32 sSmoothEpsilonNeg = -0.0008f; +const f32 sAngleZero = 0.0f; +const f32 sAngleFull = 360.0f; +const f32 sAngleHalfNeg = -180.0f; +const f32 sAngleHalf = 180.0f; +const f32 sIdxToFIdx = 0.00390625f; +const f32 sHalf = 0.5f; +const f32 sDegToRad = 0.017453292f; +const f32 sDegToFIdx = 0.7111111f; diff --git a/src/DragScroll.cpp b/src/DragScroll.cpp new file mode 100644 index 0000000..f3c00dd --- /dev/null +++ b/src/DragScroll.cpp @@ -0,0 +1,84 @@ +#include +#include +#include + + +// Layout matches gDragScroll in SceneBase.cpp (size 0x34). +struct DragScroll { + DragScroll(); + void Reset(); + void Update(); + void UpdateChan(s32 chan); + + f32 mScroll[4]; // at 0x00 + s32 mTimer[4]; // at 0x10 + s32 mState[4]; // at 0x20 + u8 mActive; // at 0x30 + u8 unk31[3]; // at 0x31 +}; + +DragScroll::DragScroll() { + for (int i = 0; i < 4; i++) { + mScroll[i] = sDragScrollZero; + mTimer[i] = 0; + mState[i] = 0; + } + mActive = 0; +} + +void DragScroll::Reset() { + for (int i = 0; i < 4; i++) { + mScroll[i] = sDragScrollZero; + mTimer[i] = 0; + mState[i] = 0; + } + mActive = 0; +} + +void DragScroll::UpdateChan(s32 chan) { + f32* pScroll = &mScroll[chan]; + s32* pTimer = &mTimer[chan]; + s32* pState = &mState[chan]; + + f32 scroll = sDragScrollOffset - gPointerY[chan][0]; + *pScroll = scroll; + + BOOL zero = scroll < sDragScrollThreshold && scroll > sDragScrollNegThreshold; + if (zero) { + *pScroll = sDragScrollZero; + return; + } + + if (*pTimer > 0) + (*pTimer)--; + + if (*pTimer != 0) + return; + + *pState = *pScroll > sDragScrollZero ? 1 : 2; + + f32 max = sDragScrollMax; + f32 amount = __fabsf(*pScroll) - sDragScrollThreshold; + if (amount > max) + amount = max; + + f32 angle = sDragScrollAngleScale * (sDragScrollHalfPi * amount / max); + *pTimer = (s32)(sDragScrollTimerScale * nw4r::math::CosFIdx(angle)) + 1; +} + +void DragScroll::Update() { + s32* pTimer = mTimer; + s32* pState = mState; + int i; + + for (i = 0; i < 4; i++, pState++, pTimer++) { + *pState = 0; + + if (gHold[i] & 0x400) { // WPAD_BUTTON_B + mActive = TRUE; + UpdateChan(i); + } else { + *pTimer = 0; + } + } +} diff --git a/src/MtxUtil.cpp b/src/MtxUtil.cpp new file mode 100644 index 0000000..fd487da --- /dev/null +++ b/src/MtxUtil.cpp @@ -0,0 +1,38 @@ +#include +#include +#include + + +void TranslateMtx(nw4r::math::MTX34* m, f32 x, f32 y, f32 z) { + Mtx trans; + PSMTXTrans(trans, x, y, z); + PSMTXConcat(trans, m->mtx, m->mtx); +} + +void RotateX(Mtx m, f32 degrees) { + Mtx rot; + PSMTXRotRad(rot, 'x', sDegToRad * degrees); + PSMTXConcat(rot, m, m); +} + +void RotateY(Mtx m, f32 degrees) { + Mtx rot; + PSMTXRotRad(rot, 'y', sDegToRad * degrees); + PSMTXConcat(rot, m, m); +} + +void RotateZ(Mtx m, f32 degrees) { + Mtx rot; + PSMTXRotRad(rot, 'z', sDegToRad * degrees); + PSMTXConcat(rot, m, m); +} + +void RotateMtxDeg(nw4r::math::MTX34* m, f32 x, f32 y, f32 z) { + nw4r::math::MTX34 rot; + nw4r::math::MTX34RotXYZFIdx(&rot, sDegToFIdx * x, sDegToFIdx * y, sDegToFIdx * z); + PSMTXConcat(rot.mtx, m->mtx, m->mtx); +} + +void SetRotateZ(Mtx m, f32 degrees) { + PSMTXRotRad(m, 'z', sDegToRad * degrees); +} diff --git a/src/SceneBase.cpp b/src/SceneBase.cpp index eae3ffe..896a4ee 100644 --- a/src/SceneBase.cpp +++ b/src/SceneBase.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -24,7 +25,6 @@ #include #include -extern "C" void ShutdownDownloader(s32 event); wchar_t* FormatNumber(s32 value, wchar_t* pBuf, s32 digits, BOOL zeroPad); void WrapHour(s32* pHour); diff --git a/src/System.cpp b/src/System.cpp index 29fc773..8a3b2b2 100644 --- a/src/System.cpp +++ b/src/System.cpp @@ -1,5 +1,6 @@ #include "revolution/CNT/cnt.h" #include +#include #include #include @@ -9,10 +10,6 @@ #include #include -// Not yet decompiled (channel) -extern "C" void InitDownloader(void); -extern "C" void UpdateDownloader(void); - // Not yet decompiled (libraries) extern "C" void VFInit(void); extern "C" void OSRestart(u32 arg0); diff --git a/src/WeatherUtil.cpp b/src/WeatherUtil.cpp new file mode 100644 index 0000000..c333c9b --- /dev/null +++ b/src/WeatherUtil.cpp @@ -0,0 +1,280 @@ +#include +#include +#include +#include +#include + + +extern const wchar_t sWeatherDigits[] = L"0123456789"; + +void WrapHour(s32* pHour) { + *pHour %= 24; +} + +void WrapHourTo24(s32* pHour) { + *pHour %= 25; +} + +// Nintendo's usual addCalc idiom (cf. cLib_addCalc): step *value toward target +// by rate * distance, clamped to [minStep, maxStep]; returns the distance left. +f32 SmoothApproach(f32* value, f32 target, f32 rate, f32 maxStep, f32 minStep) { + if (*value != target) { + f32 diff = *value - target; + + if (__fabsf(diff) < minStep) { + *value = target; + } else { + f32 absDiff = __fabsf(diff); + f32 scaled = absDiff * rate; + diff *= rate; + + BOOL small = scaled < sSmoothEpsilon && scaled > sSmoothEpsilonNeg; + if (small) { + *value = target; + } else { + if (scaled > maxStep) { + if (diff < sAngleZero) + maxStep = -maxStep; + diff = maxStep; + } else if (scaled < minStep) { + if (diff < sAngleZero) + minStep = -minStep; + diff = minStep; + } + + *value -= diff; + } + } + } + + return __fabsf(*value - target); +} + +// addCalc for angles in degrees: like SmoothApproach, but takes the short way +// around, wraps into [0, 360), has no minimum step and snaps once the step <= minStep. +f32 SmoothApproachAngle(f32* value, f32 target, f32 rate, f32 maxStep, f32 minStep) { + f32 angle = *value; + if (angle < sAngleZero) + *value += sAngleFull; + else if (angle >= sAngleFull) + *value -= sAngleFull; + + if (target < sAngleZero) + target += sAngleFull; + else if (target >= sAngleFull) + target -= sAngleFull; + + if (*value != target) { + f32 diff = *value - target; + if (diff < sAngleHalfNeg) + diff += sAngleFull; + else if (diff > sAngleHalf) + diff -= sAngleFull; + + diff *= rate; + f32 scaled = __fabsf(diff); + + if (scaled <= minStep) { + *value = target; + } else { + if (scaled > maxStep) { + if (diff < sAngleZero) + maxStep = -maxStep; + diff = maxStep; + } + + *value -= diff; + + angle = *value; + if (angle < sAngleZero) + *value += sAngleFull; + else if (angle >= sAngleFull) + *value -= sAngleFull; + } + } + + return __fabsf(*value - target); +} + +f32 EaseCos(u16 t) { + return sHalf - sHalf * nw4r::math::CosFIdx(nw4r::math::U16ToF32(t) * sIdxToFIdx); +} + +s32 FormatDigits(s32 value, s32* digits, s32 maxDigits) { + s32 i; + s32 count = 0; + s32 v = value; + + while (v != 0) { + count++; + v /= 10; + } + + if (count > maxDigits) + count = maxDigits; + + i = 0; + for (; i < count; i++) { + digits[i] = value % 10; + value /= 10; + } + for (; i < maxDigits; i++) + digits[i] = 0; + + return count; +} + +wchar_t* FormatNumber(s32 value, wchar_t* pBuf, s32 digits, BOOL zeroPad) { + s32 digitBuf[10]; + s32* p; + s32 i; + + if (digits > 10) + digits = 10; + + if (value < 0) { + *pBuf++ = L'-'; + value -= value * 2; + } + + FormatDigits(value, digitBuf, digits); + + i = digits - 1; + p = &digitBuf[i]; + for (; i >= 0; p--, i--) { + if (!zeroPad && i != 0) { + if (*p != 0) { + zeroPad = TRUE; + *pBuf++ = sWeatherDigits[*p]; + } + } else { + zeroPad = TRUE; + *pBuf++ = sWeatherDigits[*p]; + } + } + + *pBuf = 0; + return pBuf; +} + +wchar_t* FormatInt(s32 value, wchar_t* pBuf) { + s32 hundreds; + s32 rest; + s32 tens; + s32 ones; + if (value < 0) { + *pBuf++ = L'-'; + value -= value * 2; + } + hundreds = value / 100; + rest = value % 100; + ones = rest % 10; + tens = rest / 10; + if (hundreds != 0) { + *pBuf++ = sWeatherDigits[hundreds]; + *pBuf++ = sWeatherDigits[tens]; + *pBuf++ = sWeatherDigits[ones]; + } else if (tens != 0) { + *pBuf++ = sWeatherDigits[tens]; + *pBuf++ = sWeatherDigits[ones]; + } else { + *pBuf++ = sWeatherDigits[ones]; + } + *pBuf = 0; + return pBuf; +} + +static inline BOOL IsLeapYear(s32 year) { + return year % 4 == 0; +} + +static inline s32 GetDaysInMonth(s32 month, s32 year) { + if (month == 4 || month == 6 || month == 9 || month == 11) + return 30; + if (month == 2) { + if (IsLeapYear(year)) + return 29; + return 28; + } + return 31; +} + +void MinutesToCalendarTime(u32 minutes, OSCalendarTime* time) { + s32 len; + s32 year; + s32 month; + s32 days; + s32 yday; + s32 totalDays; + s32 hours; + + hours = minutes / 60; + days = hours / 24; + totalDays = days; + year = 0; + month = 1; + for (;;) { + len = IsLeapYear(year) ? 366 : 365; + if (days < len) + break; + days -= len; + year++; + } + yday = days; + + for (;;) { + len = GetDaysInMonth(month, year); + if (days < len) + break; + days -= len; + month++; + } + + time->sec = 0; + time->min = minutes % 60; + time->hour = hours % 24; + time->mday = days + 1; + time->month = month - 1; + time->year = year + 2000; + time->wday = (totalDays + 6) % 7; + time->yday = yday; + time->msec = 0; + time->usec = 0; +} + +u32 GetUniversalMinutes() { + OSCalendarTime time; + s32 year; + s32 month; + s32 days; + s32 hour; + s32 min; + s32 i; + s32 j; + + NETGetUniversalCalendar(&time); + + year = time.year; + month = time.month + 1; + days = time.mday; + hour = time.hour; + min = time.min; + + days += (year - 2000) * 365; + for (j = 0; j < year - 2000; j += 4) + days++; + for (i = 1; i < month; i++) { + if (i == 4 || i == 6 || i == 9 || i == 11) { + days += 30; + } else if (i == 2) { + if (((year - 2000) & 3) == 0) + days += 29; + else + days += 28; + } else { + days += 31; + } + } + + return min + hour * 60 + (days - 1) * 1440; +} diff --git a/src/WiiConnect24.cpp b/src/WiiConnect24.cpp new file mode 100644 index 0000000..819a8a0 --- /dev/null +++ b/src/WiiConnect24.cpp @@ -0,0 +1,1108 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern NWC24Work gNWC24Work; +extern OSMessageQueue gWC24Queue; +extern OSMessage gWC24QueueBuffer[16]; +extern OSThread gWC24Thread; +extern u8 gWC24ThreadStack[0x8000]; +extern u8 gWC24FileBuffer[0x10000]; +extern WC24Task gWC24Tasks[8]; +extern u8 gWC24Spare[0x10]; +extern volatile u8 gNWC24LibOpened; +extern volatile u8 gWC24DownloadComplete; +extern volatile u8 gWC24ShutdownFlag1; +extern volatile u8 gWC24NetInitialized; +extern volatile u8 gWC24ShutdownFlag2; +extern u8 gWC24ShutdownPadding[3]; +extern volatile s32 gWC24DownloadIndex; +extern volatile s32 gWC24QueueIndex; +extern volatile s32 gWC24TaskCount; +extern void* gWC24HeapBlock; +extern MEMiHeapHead* gWC24Heap; +extern u8 gWC24HeapPadding[4]; + +void* DownloaderThread(void* unused); + +void InitDownloader(void) { + void* block = MEM2Alloc(0x10000, 0x20); + gWC24HeapBlock = block; + gWC24Heap = MEMCreateExpHeapEx(block, 0x10000, 4); + + OSThread* thread = &gWC24Thread; + OSInitMessageQueue(&gWC24Queue, gWC24QueueBuffer, 0x10); + OSCreateThread(thread, (OSThreadFunc)DownloaderThread, NULL, gWC24ThreadStack + 0x8000, + 0x8000, 8, 1); + OSResumeThread(thread); + + gWC24DownloadIndex = 0; + gWC24QueueIndex = 0; + gWC24TaskCount = 0; +} + +void UpdateDownloader(void) { + if (gWC24TaskCount <= 0) + return; + + if (gWC24Tasks[gWC24DownloadIndex].mState != 0) + return; + + gWC24DownloadIndex++; + gWC24TaskCount--; + + if (gWC24DownloadIndex >= gWC24TaskCount) + gWC24DownloadIndex = 0; +} + +void ShutdownDownloader(s32 event) { + int i = 0; + while (i < 10 && (gWC24ShutdownFlag1 || gWC24ShutdownFlag2)) { + NWC24iRequestShutdown(event); + OSSleepTicks(OS_TIME_SPEED); + i++; + } + + OSJoinThread(&gWC24Thread, NULL); +} + +s32 CWiiConnect24::getWeather(void* forecastBuf, s64* forecastTime, u32* forecastSize, + void* shortBuf, s64* shortTime, u32* shortSize, + const char* forecastUrl, const char* shortUrl, + u32 forecastBufSize, u32 forecastMaxSize, u32 shortBufSize) { + WC24Task task; + task.mType = WC24_TASK_GET_WEATHER; + task.mTaskId = WC24_TASK_ID_FORECAST; + task.mSubTaskId = WC24_TASK_ID_SHORT; + task.mBuffers.forecast = forecastBuf; + task.mBuffers.shortForecast = shortBuf; + task.mTimes.forecast = forecastTime; + task.mTimes.shortForecast = shortTime; + task.mSizes.forecast = forecastSize; + task.mSizes.shortForecast = shortSize; + task.mForecastUrl = forecastUrl; + task.mShortUrl = shortUrl; + task.mForecastBufferSize = forecastBufSize; + task.mBufferSizes[0] = forecastMaxSize; + task.mBufferSizes[1] = shortBufSize; + + s32 result = -1; + if (gWC24TaskCount < 8) { + gWC24Tasks[gWC24QueueIndex] = task; + gWC24Tasks[gWC24QueueIndex].mState = 1; + gWC24Tasks[gWC24QueueIndex].mErrorCode = 0; + gWC24Tasks[gWC24QueueIndex].mMessage[0] = 0; + + if (OSSendMessage(&gWC24Queue, &gWC24Tasks[gWC24QueueIndex], 0)) { + result = gWC24QueueIndex; + gWC24QueueIndex++; + gWC24TaskCount++; + + if (gWC24QueueIndex >= gWC24TaskCount) + gWC24QueueIndex = 0; + } + } + + return result; +} + +s32 CWiiConnect24::downloadWeatherForecast(void* buf, s64* time, u32* size, u32 bufSize) { + WC24Task task; + task.mType = WC24_TASK_DOWNLOAD_WEATHER; + task.mTaskId = WC24_TASK_ID_FORECAST; + task.mSubTaskId = WC24_TASK_ID_NONE; + task.mBuffers.forecast = buf; + task.mTimes.forecast = time; + task.mBufferSizes[0] = bufSize; + task.mSizes.forecast = size; + task.mForecastUrl = NULL; + + s32 result = -1; + if (gWC24TaskCount < 8) { + gWC24Tasks[gWC24QueueIndex] = task; + gWC24Tasks[gWC24QueueIndex].mState = 1; + gWC24Tasks[gWC24QueueIndex].mErrorCode = 0; + gWC24Tasks[gWC24QueueIndex].mMessage[0] = 0; + + if (OSSendMessage(&gWC24Queue, &gWC24Tasks[gWC24QueueIndex], 0)) { + result = gWC24QueueIndex; + gWC24QueueIndex++; + gWC24TaskCount++; + + if (gWC24QueueIndex >= gWC24TaskCount) + gWC24QueueIndex = 0; + } + } + + return result; +} + +s32 CWiiConnect24::downloadWeatherShort(void* buf, s64* time, u32* size, u32 bufSize) { + WC24Task task; + task.mType = WC24_TASK_DOWNLOAD_WEATHER; + task.mTaskId = WC24_TASK_ID_SHORT; + task.mSubTaskId = WC24_TASK_ID_NONE; + task.mBuffers.forecast = buf; + task.mTimes.forecast = time; + task.mBufferSizes[0] = bufSize; + task.mSizes.forecast = size; + task.mForecastUrl = NULL; + + s32 result = -1; + if (gWC24TaskCount < 8) { + gWC24Tasks[gWC24QueueIndex] = task; + gWC24Tasks[gWC24QueueIndex].mState = 1; + gWC24Tasks[gWC24QueueIndex].mErrorCode = 0; + gWC24Tasks[gWC24QueueIndex].mMessage[0] = 0; + + if (OSSendMessage(&gWC24Queue, &gWC24Tasks[gWC24QueueIndex], 0)) { + result = gWC24QueueIndex; + gWC24QueueIndex++; + gWC24TaskCount++; + + if (gWC24QueueIndex >= gWC24TaskCount) + gWC24QueueIndex = 0; + } + } + + return result; +} + +s32 CWiiConnect24::setWeather(const char* forecastUrl, const char* shortUrl, u32 forecastBufSize, + u32 redownload, u32 interval) { + WC24Task task; + WC24Task* pTask = &task; + pTask->mType = WC24_TASK_SET_WEATHER; + pTask->mTaskId = WC24_TASK_ID_FORECAST; + pTask->mSubTaskId = WC24_TASK_ID_SHORT; + pTask->mForecastUrl = forecastUrl; + pTask->mShortUrl = shortUrl; + pTask->mForecastBufferSize = forecastBufSize; + pTask->mRedownload = redownload; + if (interval == 0) + pTask->mDownloadInterval = 30; + else + pTask->mDownloadInterval = interval; + pTask->mDownloadCount = 60; + + s32 result = -1; + if (gWC24TaskCount < 8) { + gWC24Tasks[gWC24QueueIndex] = *pTask; + gWC24Tasks[gWC24QueueIndex].mState = 1; + gWC24Tasks[gWC24QueueIndex].mErrorCode = 0; + gWC24Tasks[gWC24QueueIndex].mMessage[0] = 0; + + if (OSSendMessage(&gWC24Queue, &gWC24Tasks[gWC24QueueIndex], 0)) { + result = gWC24QueueIndex; + gWC24QueueIndex++; + gWC24TaskCount++; + + if (gWC24QueueIndex >= gWC24TaskCount) + gWC24QueueIndex = 0; + } + } + + return result; +} + +s32 CWiiConnect24::deleteWeather() { + WC24Task task; + WC24Task* pTask = &task; + pTask->mType = WC24_TASK_DELETE_WEATHER; + pTask->mTaskId = WC24_TASK_ID_FORECAST; + pTask->mSubTaskId = WC24_TASK_ID_SHORT; + + s32 result = -1; + if (gWC24TaskCount < 8) { + gWC24Tasks[gWC24QueueIndex] = *pTask; + gWC24Tasks[gWC24QueueIndex].mState = 1; + gWC24Tasks[gWC24QueueIndex].mErrorCode = 0; + gWC24Tasks[gWC24QueueIndex].mMessage[0] = 0; + + if (OSSendMessage(&gWC24Queue, &gWC24Tasks[gWC24QueueIndex], 0)) { + result = gWC24QueueIndex; + gWC24QueueIndex++; + gWC24TaskCount++; + + if (gWC24QueueIndex >= gWC24TaskCount) + gWC24QueueIndex = 0; + } + } + + return result; +} + +char sVfDriveName[] = "@24"; +char sVfRootPath[] = "@24:/*"; +char sVfReadMode[] = "r"; +char sBinNameFormat[] = "%d.bin"; +char sForecastFileSuffix[] = "%sB"; +char sShortFileSuffix[] = "%sR"; + +NWC24Err MapDownloadError(NWC24Err error); +NWC24Err DownloadFiles(WC24Task* task, u32 doForecast, u32 doShort); +NWC24Err CreateDownloadTasks(WC24Task* task, u32 doForecast, u32 doShort, u8 redownload, + u16 newInterval, u16 count, u32* idOut); +NWC24Err ExecuteDownloadTask(WC24Task* task, s32 index, u32 unused, u16 unused2); +NWC24Err DeleteTaskFiles(WC24Task* task, u32 doForecast, u32 doShort); +NWC24Err ReadCompressedFile(WC24Task* task, void* fd, void* dest, u32 destSize, + u32* outSize); + +static inline void SetTaskMessage(WC24Task* task, const char* msg, s32 code, s32 result) { + task->mErrorCode = code; + task->mNwc24Result = result; + sprintf(task->mMessage, "%s %d %d", msg, code, result); +} + +// A macro, not an inline: the message literal must reach SetTaskMessage directly. +// Passing it through another inline parameter keeps it live across +// NWC24GetErrorCode() in a callee-saved register, which the original does not do. +#define SetTaskError(task, msg, result) (SetTaskMessage(task, msg, NWC24GetErrorCode(), result), MapDownloadError(result)) + +static inline NWC24Err OpenLib(WC24Task* task, s64 retry) { + NWC24Err error; + + for (;;) { + error = NWC24OpenLib(&gNWC24Work); + + if (error != NWC24_ERR_MUTEX && error != NWC24_ERR_BUSY && + error != NWC24_ERR_INPROGRESS) + break; + + OSSleepTicks(OS_MSEC_TO_TICKS(100 * (retry + 1))); + } + + if (error != NWC24_OK) + return SetTaskError(task, "NWC24OpenLib() failed.", error); + + gNWC24LibOpened = 1; + return NWC24_OK; +} + +static inline NWC24Err CheckLib(WC24Task* task) { + NWC24Err error = NWC24Check(2); + + if (error != NWC24_OK) + return SetTaskError(task, "NWC24Check() failed.", error); + + SCIdleModeInfo idle; + if (SCGetIdleMode(&idle) == 0 || idle.wc24 != 1) + return NWC24_ERR_INVALID_VALUE; + + return NWC24_OK; +} + +static inline NWC24Err CloseLib(WC24Task* task) { + NWC24Err error = NWC24CloseLib(); + + if (error != NWC24_OK) + return SetTaskError(task, "NWC24CloseLib() failed.", error); + + gNWC24LibOpened = 0; + return NWC24_OK; +} + +void* DownloaderThread(void* unused) { + WC24Task* task; + void* message; + s64 retry = 0; + s32 result; + u8 opened; + s32 attempts; + u8 done; + + for (;;) { + done = FALSE; + OSReceiveMessage(&gWC24Queue, &message, 1); + + task = (WC24Task*)message; + if (task == NULL) + break; + + switch (task->mType) { + case 0: + opened = FALSE; + attempts = 0; + + for (;;) { + task->mState = 2; + task->mResult = OpenLib(task, retry); + if (task->mResult != NWC24_OK) + break; + + opened = TRUE; + + task->mResult = CheckLib(task); + if (task->mResult != NWC24_OK) + break; + + task->mState = 3; + if ((task->mResult = DownloadFiles(task, 1, 1)) == NWC24_OK) + break; + if (task->mResult == NWC24_ERR_FATAL && attempts != 1) { + task->mState = 6; + task->mResult = CreateDownloadTasks(task, 1, 1, 1, 0x1E, 0x3C, 0); + if (task->mResult == NWC24_OK) { + task->mState = 2; + task->mResult = CloseLib(task); + opened = FALSE; + if (task->mResult == NWC24_OK) { + task->mState = 5; + task->mResult = ExecuteDownloadTask(task, 0, 0xFFFFFF, 0xFFFF); + if (task->mResult == NWC24_OK) { + task->mResult = ExecuteDownloadTask(task, 1, 0xFFFFFF, 0xFFFF); + if (task->mResult == NWC24_OK) { + attempts++; + continue; + } + } + } + } + } + break; + } + + if (opened) { + task->mState = 2; + result = CloseLib(task); + if (task->mResult == NWC24_OK) + task->mResult = result; + } + task->mState = 0; + break; + + case 1: + task->mState = 5; + task->mResult = ExecuteDownloadTask(task, 0, 0xFFFFFF, 0xFFFF); + if (task->mResult == NWC24_OK) { + task->mState = 2; + task->mResult = OpenLib(task, retry); + if (task->mResult == NWC24_OK) { + task->mResult = CheckLib(task); + if (task->mResult == NWC24_OK) { + task->mState = 3; + switch (task->mTaskId) { + case 3: + task->mResult = DownloadFiles(task, 1, 0); + break; + case 4: + task->mResult = DownloadFiles(task, 1, 0); + break; + } + } + task->mState = 2; + result = CloseLib(task); + if (task->mResult == NWC24_OK) + task->mResult = result; + } + } + task->mState = 0; + break; + + case 2: + task->mState = 2; + task->mResult = OpenLib(task, retry); + if (task->mResult == NWC24_OK) { + task->mResult = CheckLib(task); + if (task->mResult == NWC24_OK) { + task->mState = 6; + task->mResult = CreateDownloadTasks(task, 1, 1, task->mRedownload, task->mDownloadInterval, + task->mDownloadCount, 0); + } + task->mState = 2; + result = CloseLib(task); + if (task->mResult == NWC24_OK) + task->mResult = result; + } + task->mState = 0; + break; + + case 3: + task->mState = 2; + task->mResult = OpenLib(task, retry); + if (task->mResult == NWC24_OK) { + task->mResult = CheckLib(task); + if (task->mResult == NWC24_OK) { + task->mState = 7; + task->mResult = DeleteTaskFiles(task, 1, 1); + } + task->mState = 2; + result = CloseLib(task); + if (task->mResult == NWC24_OK) + task->mResult = result; + } + task->mState = 0; + break; + + case 4: + done = TRUE; + break; + } + + if (!done) + continue; + break; + } + + return NULL; +} + +NWC24Err DownloadFiles(WC24Task* task, u32 doForecast, u32 doShort) { + NWC24DlTask dlTasks[2]; + u8 dta[0x448]; + char url[0x100]; + char vfName[0x50]; + char fileName[0x10]; + void* buffers[2]; + s64* times[2]; + u32* sizes[2]; + u16 ids[2]; + u32 outSize; + + NWC24Err error; + s32 vfError; + BOOL found = FALSE; + + if (doForecast != 0) { + ids[0] = task->mTaskId; + buffers[0] = task->mBuffers.forecast; + times[0] = task->mTimes.forecast; + sizes[0] = task->mSizes.forecast; + } else { + ids[0] = 0; + buffers[0] = NULL; + times[0] = NULL; + sizes[0] = NULL; + } + + if (doShort != 0) { + ids[1] = task->mSubTaskId; + buffers[1] = task->mBuffers.shortForecast; + times[1] = task->mTimes.shortForecast; + sizes[1] = task->mSizes.shortForecast; + } else { + ids[1] = 0; + buffers[1] = NULL; + times[1] = NULL; + sizes[1] = NULL; + } + + // The loops below address the same task through dlTasks and through a + // second pointer to it; the original keeps one induction pointer for each. + NWC24DlTask* urlTask = dlTasks; + for (s32 i = 0; i < 2; i++) { + if (buffers[i] == NULL) + continue; + + error = NWC24GetDlTask(&dlTasks[i], ids[i]); + if (error == NWC24_ERR_NOT_FOUND) { + SetTaskMessage(task, "NWC24GetDlTask() failed.", NWC24GetErrorCode(), error); + return NWC24_ERR_FATAL; + } + + if (error != NWC24_OK) { + return SetTaskError(task, "NWC24GetDlTask() failed.", error); + } + + error = NWC24GetDlUrl(&urlTask[i], url, 0xFF); + if (error != NWC24_OK) { + return SetTaskError(task, "NWC24GetDlUrl() failed.", error); + } + + if ((&task->mForecastUrl)[i] != NULL) { + u32 j = 0; + while (j < 0xFF && url[j] != 0) { + if (url[j] != (&task->mForecastUrl)[i][j]) { + SetTaskMessage(task, "URL is not same.", 0, 0); + return NWC24_ERR_FATAL; + } + + j++; + } + } + } + + for (s32 i = 0; i < 2; i++) { + if (buffers[i] == NULL) + continue; + + error = NWC24GetDlVfName(&dlTasks[i], vfName, 0x50); + if (error != NWC24_OK) { + return SetTaskError(task, "NWC24GetDlVfName() failed.", error); + } + + vfError = VFMountDriveNANDFlashEx(sVfDriveName, vfName); + if (vfError != 0) { + SetTaskMessage(task, "VFMountDriveNANDFlash() failed.", 0, vfError); + return NWC24_ERR_FATAL; + } + + s32 searchResult = VFFileSearchFirst(dta, sVfRootPath, 0x7F); + while (searchResult == 0) + searchResult = VFFileSearchNext(dta); + + found = TRUE; + break; + } + + if (!found) + OSPanic("WiiConnect24.cpp", 0x4B1, "VF Load Failed."); + + NWC24DlTask* pTask = dlTasks; + for (s32 i = 0; i < 2; i++) { + if (buffers[i] == NULL) + continue; + + error = NWC24GetDlFilename(&dlTasks[i], fileName, 0x10); + if (error != NWC24_OK) { + return SetTaskError(task, "NWC24GetDlFilename() failed.", error); + } + + void* file = VFOpenFile(fileName, sVfReadMode, 0); + if (file == NULL) { + SetTaskMessage(task, "VFOpenFile() failed.", NWC24GetErrorCode(), 0); + + vfError = VFUnmountDrive(sVfDriveName); + if (vfError != 0) { + SetTaskMessage(task, "VFUnmountDrive() failed.", 0, vfError); + return NWC24_ERR_PROTECTED; + } + return NWC24_ERR_FATAL; + } + + NWC24Err result = ReadCompressedFile(task, file, buffers[i], task->mBufferSizes[i], &outSize); + if (result != NWC24_OK) + return result; + + *sizes[i] = outSize; + + // Pads the size to a multiple of 4, but writes into the task itself + // rather than into the downloaded buffer. + char* p; + u32 size = outSize; + p = (char*)&task->mBuffers + size; + while (size < ((outSize + 3) & ~3)) { + *p++ = 0; + size++; + } + + vfError = VFCloseFile(file); + if (vfError != 0) { + SetTaskMessage(task, "VFCloseFile() failed.", 0, vfError); + VFUnmountDrive(sVfDriveName); + return NWC24_ERR_PROTECTED; + } + + error = NWC24GetDlLastUpdate(&dlTasks[i], times[i]); + if (error != NWC24_OK) { + return SetTaskError(task, "NWC24GetDlLastUpdate() failed.", error); + } + + error = NWC24GetDlNextTime(&pTask[i], &task->mNextDownloadTimes[i]); + if (error != NWC24_OK) { + return SetTaskError(task, "NWC24GetDlNextTime() failed.", error); + } + } + + vfError = VFUnmountDrive(sVfDriveName); + if (vfError != 0) { + SetTaskMessage(task, "VFUnmoundDrive() failed.", 0, vfError); + return NWC24_ERR_PROTECTED; + } + + return NWC24_OK; +} + +NWC24Err CreateDownloadTasks(WC24Task* task, u32 doForecast, u32 doShort, u8 redownload, + u16 newInterval, u16 count, u32* idOut) { + NWC24DlTask dlTasks[2]; + u8 search[0x448]; + char url[0x100]; + char vfName[0x50]; + char vfName2[0x50]; + char fileName[0x10]; + char nameBuf[0x10]; + const char* urls[2]; + u16 ids[2]; + u16 interval; + + NWC24Err error; + s32 vfError; + BOOL redo = redownload; + BOOL init = redownload; + BOOL mounted = FALSE; + + if (doForecast != 0) { + ids[0] = task->mTaskId; + urls[0] = task->mForecastUrl; + } else { + ids[0] = 0; + urls[0] = NULL; + } + + if (doShort != 0) { + ids[1] = task->mSubTaskId; + urls[1] = task->mShortUrl; + } else { + ids[1] = 0; + urls[1] = NULL; + } + + NWC24DlTask* pTask = dlTasks; + for (s32 i = 0; i < 2; i++) { + if (ids[i] == 0) + continue; + + if (redo != 0) + goto firstDone; + + error = NWC24GetDlTask(&dlTasks[i], ids[i]); + if (error == NWC24_ERR_NOT_FOUND) { + redo = TRUE; + init = TRUE; + goto firstDone; + } + + if (error != NWC24_OK) + return SetTaskError(task, "NWC24GetDlTask() failed.", error); + + error = NWC24GetDlVfName(&pTask[i], vfName, 0x50); + if (error != NWC24_OK) + return SetTaskError(task, "NWC24GetDlVfName() failed.", error); + + if (!mounted) { + vfError = VFMountDriveNANDFlashEx(sVfDriveName, vfName); + if (vfError == 2 || vfError == 0xB001) { + redo = TRUE; + init = TRUE; + goto firstDone; + } + + if (vfError != 0) { + SetTaskMessage(task, "VFMountDriveNANDFlash() failed.", 0, vfError); + return NWC24_ERR_PROTECTED; + } + + mounted = TRUE; + s32 searchResult = VFFileSearchFirst(search, sVfRootPath, 0x7F); + while (searchResult == 0) + searchResult = VFFileSearchNext(search); + } + + if (!mounted) + continue; + + switch (ids[i]) { + case 3: + case 4: { + error = NWC24GetDlFilename(&pTask[i], fileName, 0x10); + if (error != NWC24_OK) + return SetTaskError(task, "NWC24GetDlFilename() failed.", error); + + void* file = VFOpenFile(fileName, sVfReadMode, 0); + if (file == NULL) { + redo = TRUE; + init = TRUE; + continue; + } + + vfError = VFCloseFile(file); + if (vfError != 0) { + SetTaskMessage(task, "VFCloseFile() failed.", 0, vfError); + VFUnmountDrive(sVfDriveName); + return NWC24_ERR_PROTECTED; + } + break; + } + } + } + +firstDone: + if (mounted) { + vfError = VFUnmountDrive(sVfDriveName); + if (vfError != 0) { + SetTaskMessage(task, "VFUnmountDrive() failed.", 0, vfError); + return NWC24_ERR_PROTECTED; + } + } + + if (!redo) { + for (s32 i = 0; i < 2; i++) { + if (ids[i] == 0) + continue; + + error = NWC24GetDlUrl(&dlTasks[i], url, 0xFF); + if (error != NWC24_OK) + return SetTaskError(task, "NWC24GetDlUrl() failed.", error); + + const char* p = url; + u32 j = 0; + while (j < 0xFF && *p != 0) { + if (*p != urls[i][j]) { + redo = TRUE; + init = TRUE; + break; + } + p++; + j++; + } + + error = NWC24GetDlInterval(&dlTasks[i], &interval); + if (error != NWC24_OK) + return SetTaskError(task, "NWC24GetDlInterval() failed.", error); + + if (newInterval != interval) + redo = TRUE; + } + } + + if (redo) { + BOOL created = FALSE; + NWC24DlTask* pTask2 = dlTasks; + + for (s32 i = 0; i < 2; i++) { + if (ids[i] == 0) + continue; + + if (init) { + error = NWC24InitDlTask(&dlTasks[i], 1); + if (error != NWC24_OK) + return SetTaskError(task, "NWC24InitDlTask() failed.", error); + + error = NWC24SetDlId(&dlTasks[i], ids[i]); + if (error != NWC24_OK) + return SetTaskError(task, "NWC24SetDlId() failed.", error); + + error = NWC24GetDlVfName(&pTask2[i], vfName2, 0x50); + if (error != NWC24_OK) + return SetTaskError(task, "NWC24GetDlVfName() failed.", error); + + if (!created) { + s32 nandResult = NANDDelete(vfName2); + if (nandResult != NAND_RESULT_NOEXISTS && nandResult != NAND_RESULT_OK) { + SetTaskMessage(task, "NANDDelete() failed.", 0, nandResult); + return NWC24_ERR_OVERFLOW; + } + + error = NWC24CreateDlVf(&pTask2[i], task->mForecastBufferSize); + if (error != NWC24_OK) + return SetTaskError(task, "NWC24CreateDlVf() failed.", error); + + created = TRUE; + } + } + + error = NWC24SetDlUrl(&dlTasks[i], urls[i]); + if (error != NWC24_OK) + return SetTaskError(task, "NWC24SetDlUrl() failed.", error); + + s32 priority; + s32 margin; + switch (ids[i]) { + case 3: + priority = 100; + margin = 0x2D0; + break; + case 4: + priority = 100; + margin = 0x2D0; + break; + } + + error = NWC24SetDlPriority(&dlTasks[i], priority); + if (error != NWC24_OK) + return SetTaskError(task, "NWC24SetDlPriority() failed.", error); + + error = NWC24SetDlFlags(&dlTasks[i], 0x40000000); + if (error != NWC24_OK) + return SetTaskError(task, "NWC24SetDlFlags() failed.", error); + + error = NWC24SetDlInterval(&dlTasks[i], newInterval); + if (error != NWC24_OK) + return SetTaskError(task, "NWC24SetDlInterval() failed.", error); + + error = NWC24SetDlRetryMargin(&dlTasks[i], margin); + if (error != NWC24_OK) + return SetTaskError(task, "NWC24SetDlRetryMargin() failed.", error); + + sprintf(nameBuf, sBinNameFormat, ids[i]); + error = NWC24SetDlFilename(&dlTasks[i], nameBuf); + if (error != NWC24_OK) + return SetTaskError(task, "NWC24SetDlFilename() failed.", error); + } + } + + NWC24DlTask* pTask3 = dlTasks; + for (s32 i = 0; i < 2; i++) { + if (ids[i] == 0) + continue; + + error = NWC24SetDlCount(&dlTasks[i], (s16)count); + if (error != NWC24_OK) + return SetTaskError(task, "NWC24SetDlCount() failed.", error); + + if (redo) { + error = NWC24AddDlTask(&dlTasks[i]); + if (error != NWC24_OK) + return SetTaskError(task, "NWC24AddDlTask() failed.", error); + } else { + error = NWC24UpdateDlTask(&dlTasks[i]); + if (error != NWC24_OK) + return SetTaskError(task, "NWC24UpdateDlTask() failed.", error); + } + + if (idOut != NULL) { + error = NWC24GetDlId(&pTask3[i], idOut); + if (error != NWC24_OK) + return SetTaskError(task, "NWC24GetDlId() failed.", error); + } + + NWC24iCheckDlTask(&pTask3[i]); + } + + gWC24DownloadComplete = 1; + return NWC24_OK; +} + +struct WC24SOConfig { + void* alloc; + void* free; +}; + +NWC24Err ExecuteDownloadTask(WC24Task* task, s32 index, u32 unused, u16 unused2) { + s32 initResult; + s32 startupResult; + NWC24Err error; + s32 taskId; + WC24SOConfig config; + + taskId = (&task->mTaskId)[index]; + + if (!gWC24NetInitialized) { + gWC24ShutdownFlag2 = 1; + config.alloc = (void*)WC24Alloc; + config.free = (void*)WC24Free; + + initResult = SOInit(&config); + if (initResult < 0) { + SetTaskMessage(task, "SOInit() failed.", 0, initResult); + gWC24ShutdownFlag2 = 0; + return NWC24_ERR_LIB_NOT_OPENED; + } + + startupResult = SOStartup(); + if (startupResult < 0) { + SetTaskMessage(task, "SOStartup() failed.", SOGetLastError(), startupResult); + gWC24ShutdownFlag2 = 0; + return NWC24_ERR_LIB_NOT_OPENED; + } + + gWC24NetInitialized = 1; + gWC24ShutdownFlag2 = 0; + } + + switch (taskId) { + case 3: + case 4: + gWC24ShutdownFlag1 = 1; + error = NWC24ExecDownloadTask(2, taskId, 0); + gWC24ShutdownFlag1 = 0; + + if (error != NWC24_OK) { + SetTaskMessage(task, "NWC24ExecDownloadTask() failed.", NWC24GetErrorCode(), error); + return MapDownloadError(error); + } + break; + } + return NWC24_OK; +} + +NWC24Err DeleteTaskFiles(WC24Task* task, u32 doForecast, u32 doShort) { + NWC24DlTask dlTask; + char vfName[0x50]; + u16 ids[2]; + u8 found; + + found = FALSE; + + if (doForecast != 0) + ids[0] = task->mTaskId; + else + ids[0] = 0; + + if (doShort != 0) + ids[1] = task->mSubTaskId; + else + ids[1] = 0; + + for (s32 i = 0; i < 2; i++) { + if (ids[i] == 0) + continue; + + NWC24Err error = NWC24GetDlTask(&dlTask, ids[i]); + if (error == NWC24_ERR_NOT_FOUND) + continue; + + if (error != NWC24_OK) { + return SetTaskError(task, "NWC24GetDl*() failed.", error); + } + + if (!found) { + found = TRUE; + error = NWC24GetDlVfName(&dlTask, vfName, 0x50); + if (error != NWC24_OK) { + return SetTaskError(task, "NWC24GetDlVfName() failed.", error); + } + } + + error = NWC24DeleteDlTask(&dlTask); + if (error != NWC24_OK) { + return SetTaskError(task, "NWC24DeleteDlTask() failed.", error); + } + } + + if (found) { + s32 nandResult = NANDDelete(vfName); + if (nandResult != NAND_RESULT_NOEXISTS && nandResult != NAND_RESULT_OK) { + SetTaskMessage(task, "NANDDelete() failed.", 0, nandResult); + return NWC24_ERR_OVERFLOW; + } + } + + return NWC24_OK; +} + +NWC24Err MapDownloadError(NWC24Err error) { + NWC24Err mapped = NWC24_ERR_FULL; + + if ((error <= NWC24_ERR_FILE_OPEN && error >= NWC24_ERR_FILE_OTHER) || + error == NWC24_ERR_NAND_CORRUPT || error == NWC24_ERR_FILE_EXISTS || + error == NWC24_ERR_INTERNAL_VF || error == NWC24_ERR_FILE_BROKEN) { + mapped = NWC24_ERR_NOMEM; + } else if (error == NWC24_ERR_OLD_SYSTEM) { + mapped = NWC24_ERR_CONFIG; + } else if (error == NWC24_ERR_FATAL || error == NWC24_ERR_NOMEM || + error == NWC24_ERR_INTERNAL_IPC) { + mapped = NWC24_ERR_FAILED; + } else if ((u32)(NWC24GetErrorCode() + 0x20000) == 0x55CE) { + mapped = NWC24_ERR_NULL; + } else { + switch (error) { + case NWC24_ERR_DISABLED: + mapped = NWC24_ERR_INVALID_VALUE; + break; + + case NWC24_ERR_NETWORK: + mapped = NWC24_ERR_NOT_SUPPORTED; + break; + + case NWC24_ERR_SERVER: + mapped = NWC24_ERR_NULL; + break; + } + } + + return mapped; +} + +NWC24Err ReadCompressedFile(WC24Task* task, void* fd, void* dest, u32 destSize, + u32* outSize) { + u32 fileSize = VFGetFileSizeByFd(fd); + u32 uncompSize = 0; + CXUncompContextLZ ctx; + + CXInitUncompContextLZ(&ctx, dest); + + u32 offset = 0; + while (offset < fileSize) { + u32 chunk = fileSize - offset; + if (chunk > 0x10000) + chunk = 0x10000; + + s32 vfError = VFReadFile(fd, gWC24FileBuffer, chunk, NULL); + if (vfError != 0) { + SetTaskMessage(task, "VFReadFile() failed.", 0, vfError); + return NWC24_ERR_PROTECTED; + } + + if (offset == 0) { + if ((gWC24FileBuffer[0] & 0xF0) != 0x10) { + SetTaskMessage(task, "CXGetCompressionType() data is not LZ.", 0, 0); + return NWC24_ERR_NULL; + } + + uncompSize = CXGetUncompressedSize(gWC24FileBuffer); + if (uncompSize > destSize) { + SetTaskMessage(task, "CXGetUncompressedSize() overflow.", 0, 0); + return NWC24_ERR_NULL; + } + } + + CXReadUncompLZ(&ctx, gWC24FileBuffer, chunk); + offset += 0x10000; + } + + // Equivalent to !CXIsFinishedUncompLZ(&ctx). + BOOL notFinished = ctx.destCount > 0 || ctx.headerSize > 0; + if (notFinished) { + SetTaskMessage(task, "CXIsFinisiedUncompLZ() is false.", 0, 0); + return NWC24_ERR_LIB_OPENED; + } + + *outSize = uncompSize; + return NWC24_OK; +} + +void* WC24Alloc(u32 unused, s32 size) { + void* block = NULL; + + if (size > 0) + block = MEMAllocFromExpHeapEx(gWC24Heap, size, 0x20); + + return block; +} + +void WC24Free(void* unused, void* block, s32 size) { + if (block == NULL) + return; + + if (size <= 0) + return; + + MEMFreeToExpHeap(gWC24Heap, block); +} + +NWC24Work gNWC24Work; +OSMessageQueue gWC24Queue; +OSMessage gWC24QueueBuffer[16]; +OSThread gWC24Thread; +u8 gWC24ThreadStack[0x8000]; +u8 gWC24FileBuffer[0x10000]; +WC24Task gWC24Tasks[8]; +u8 gWC24Spare[0x10]; + +volatile u8 gNWC24LibOpened; +volatile u8 gWC24DownloadComplete; +volatile u8 gWC24ShutdownFlag1; +volatile u8 gWC24NetInitialized; +volatile u8 gWC24ShutdownFlag2; +u8 gWC24ShutdownPadding[3]; +volatile s32 gWC24DownloadIndex; +volatile s32 gWC24QueueIndex; +volatile s32 gWC24TaskCount; +void* gWC24HeapBlock; +MEMiHeapHead* gWC24Heap; +u8 gWC24HeapPadding[4]; diff --git a/src/revolution/NWC24/NWC24System.c b/src/revolution/NWC24/NWC24System.c index 2890414..954cba2 100644 --- a/src/revolution/NWC24/NWC24System.c +++ b/src/revolution/NWC24/NWC24System.c @@ -56,15 +56,30 @@ NWC24Err NWC24iPrepareShutdown(void) { return result; } -NWC24Err NWC24iRequestShutdown(u32 event, NWC24Err* pResult) { +NWC24Err NWC24iRequestShutdown(u32 event) { static u8 shtBuffer[32] ALIGN(32); static u8 shtResult[32] ALIGN(32); + NWC24Err result; *(u32*)shtBuffer = event; - return NWC24_IOCTL_DEVICE_ASYNC(nwc24ShtFd, NWC24_IOCTL_SHUTDOWN, // - shtBuffer, sizeof(shtBuffer), // - shtResult, sizeof(shtResult), // - pResult); + result = NWC24_IOCTL_DEVICE_ASYNC(nwc24ShtFd, NWC24_IOCTL_SHUTDOWN, // + shtBuffer, sizeof(shtBuffer), // + shtResult, sizeof(shtResult), // + &result); + + if (result < 0) { + return result; + } + + while (NWC24iIsAsyncRequestPending()) { + OSSleepTicks(OS_TIME_SPEED); + } + + if (*(NWC24Err*)shtResult < 0) { + return NWC24_ERR_FAILED; + } + + return NWC24_OK; } BOOL NWC24Shutdown(BOOL final, u32 event) { @@ -90,7 +105,7 @@ BOOL NWC24Shutdown(BOOL final, u32 event) { } else { return TRUE; } - } else if (NWC24iRequestShutdown(event, &result) >= 0) { + } else if ((result = NWC24iRequestShutdown(event)) >= 0) { shuttingdown = TRUE; } diff --git a/src/revolution/SC/scapi.c b/src/revolution/SC/scapi.c index 183ca89..71c3103 100644 --- a/src/revolution/SC/scapi.c +++ b/src/revolution/SC/scapi.c @@ -47,8 +47,8 @@ u8 SCGetEuRgb60Mode(void) { return item; } -void SCGetIdleMode(SCIdleModeInfo* mode) { - SCFindByteArrayItem(mode, sizeof(SCIdleModeInfo), SC_ITEM_IPL_IDL); +BOOL SCGetIdleMode(SCIdleModeInfo* mode) { + return SCFindByteArrayItem(mode, sizeof(SCIdleModeInfo), SC_ITEM_IPL_IDL); } u8 SCGetLanguage(void) {