From 2543c555a4cb99a7ec320500a9695a731888a3f1 Mon Sep 17 00:00:00 2001 From: "e.bovendeur" Date: Fri, 4 Dec 2009 15:05:20 +0000 Subject: [PATCH] Lots of changes in this revision: * Added MEM2 support by Hibern * Better partition support (by oggzee) * Support for subdirectories in FAT32 (by oggzee) * Added support for cios 223 and 250 * Added BCA support (go to Settings->Custom Paths) to change the path of the BCA files (by Hermes) * Fixed issue with hairless mode * Fixed issue with IOS_ReloadIOSsafe (by giantpune) * Added setting to save games in a subdirectory * Fixed slow startup when loading from FAT (WiiTDB required!) * Changed handling of new titles a bit (speed improvement) Known issue: * FAT rename and re-id broken again due to subdirectory support (yes, I'm lazy) --- HBC/META.XML | 4 +- Makefile | 5 +- data/ehcmodule_fat.bin | Bin 61053 -> 69185 bytes gui.pnps | 2 +- source/libwiigui/gui_gamebrowser.cpp | 6 +- source/main.cpp | 82 +++++--- source/memory/mem2.cpp | 213 ++++++++++++++++++++ source/memory/mem2.h | 26 +++ source/memory/mem2alloc.cpp | 198 +++++++++++++++++++ source/memory/mem2alloc.h | 41 ++++ source/menu.cpp | 11 +- source/menu/menu_check.cpp | 27 ++- source/menu/menu_disclist.cpp | 3 + source/mload/dip_plugin.c | 215 ++++++++++---------- source/mload/dip_plugin.h | 4 +- source/mload/mload.c | 1 - source/patches/fst.c | 60 ++++++ source/patches/fst.h | 1 + source/patches/patchcode.c | 91 --------- source/prompts/PromptWindows.cpp | 31 +-- source/settings/Settings.cpp | 55 +++++- source/settings/cfg.c | 13 ++ source/settings/cfg.h | 5 + source/settings/newtitles.cpp | 48 +++-- source/settings/newtitles.h | 1 + source/sys.cpp | 34 +++- source/sys.h | 4 + source/usbloader/apploader.c | 5 +- source/usbloader/disc.h | 3 +- source/usbloader/getentries.cpp | 13 +- source/usbloader/partition_usbloader.c | 259 +++++++++++++++++++----- source/usbloader/partition_usbloader.h | 108 +++++----- source/usbloader/usbstorage.c | 47 ++--- source/usbloader/wbfs.c | 2 +- source/usbloader/wbfs_fat.c | 261 +++++++++++++++++++++---- source/usbloader/wdvd.c | 18 ++ source/usbloader/wdvd.h | 1 + 37 files changed, 1444 insertions(+), 454 deletions(-) create mode 100644 source/memory/mem2.cpp create mode 100644 source/memory/mem2.h create mode 100644 source/memory/mem2alloc.cpp create mode 100644 source/memory/mem2alloc.h diff --git a/HBC/META.XML b/HBC/META.XML index c9d5ef06..96736917 100644 --- a/HBC/META.XML +++ b/HBC/META.XML @@ -2,8 +2,8 @@ USB Loader GX USB Loader GX Team - 1.0 r846 - 200912030606 + 1.0 r847 + 200912031841 Loads games from USB-devices USB Loader GX is a libwiigui based USB iso loader with a wii-like GUI. You can install games to your HDDs and boot them with shorter loading times. The interactive GUI is completely controllable with WiiMote, Classic Controller or GC Controller. diff --git a/Makefile b/Makefile index 8a6cdde8..5de09a86 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ SOURCES := source source/libwiigui source/images source/fonts source/sounds \ source/libwbfs source/unzip source/language source/mload source/patches \ source/usbloader source/xml source/network source/settings source/prompts \ source/ramdisk source/wad source/banner source/cheats source/homebrewboot \ - source/themes source/menu source/libfat + source/themes source/menu source/libfat source/memory DATA := data INCLUDES := source @@ -30,8 +30,9 @@ INCLUDES := source CFLAGS = -g -O2 -save-temps -Wall $(MACHDEP) $(INCLUDE) CXXFLAGS = -Xassembler -aln=$@.lst $(CFLAGS) -LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map,--section-start,.init=0x80B00000 +LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map,--section-start,.init=0x80B00000,-wrap,malloc,-wrap,free,-wrap,memalign,-wrap,calloc,-wrap,realloc,-wrap,malloc_usable_size -include $(PROJECTDIR)/Make.config + #--------------------------------------------------------------------------------- # any extra libraries we wish to link with the project #--------------------------------------------------------------------------------- diff --git a/data/ehcmodule_fat.bin b/data/ehcmodule_fat.bin index c499a244e221feadfb202ca769c22c6d9799d30c..fd01feb7834f26c6a36811f360a6bbec983608bc 100644 GIT binary patch delta 26118 zcmc({3tUvy+CRSc%)kHxGB_v$Y-BE=2&gEM7qnmocf$;t7fKZEY}9H4T2N-D*5IY` z(gj(jz)*Rzi>dWe=;@;898cCcnbiTUj%jw#jIq#~Qx1k9g#7H< z`q3o$19BG$W52Qwx`sQEjz4_!%I}!5&jkHl zLI3HX|BB@M{qG0k)j@xC(Em%&KN$314*HKtzMUD{autBz|6jrQgV*>wg7LlzHc4^6 z|HEKM67V%sQ$1wj0T=%}{x*I(U$Eb0Yi({2 zwfY)B8}vGS?X`nlOgD#6x_%|T8J43BgKJd?-D&w-<6w?8GP4mWu)c5mtDdQi-m12| zzcps7W^3%$A$1XjN32Ikxa|nPc57Ijl7w2`EqvE{#C9Zwtqs{as4j{`)(?Iq&cFae zZYI+yT_kP(20q9Ta!$o-p)5`LNLS$o1I@89q zpXZB2V+IZVvp$UGwlQbhT?+pyup$Kh3ivGidmF+;m6Qv-g^vL9Qz@*oK0OpA_oOS^ z-gwwZzE@b}4owD`6p|=EgOEha%e`roHJr6?OG(CUvf%MQXnD*NAkP}S%N9k-Eb52hQ zKYKY{hN_Q{Lm^4_)Bvu&rZ{s!Vjk)U0f{%PHm+V2#J;ORCE-8E4KXKiX|Aa5-ZZ8y z`yt~)L5PQT(fo(badC~Y=46RMhm9bKWus&k4Uy~Rk2)KrgmYgU|SM_N1li;!jkc{34Ta_jUtGPoKzh3ykl zV{1ewoJk{5(wUGs z24u&*GPKLzGm+wQ#;l}r&p4yF$qhr%@r-}|aF$bk^L?XI)L0+NWp6W)FPM(u7%Z${ ztXJ#V#!v|TTXb-t%fhVp>uMBvVvH-&UE)w{6N$Phq+Tr=P3uugrDwLPJHl+sq~<)} z3hAB9Dbtu!O#92^jWDCVOk&G~AY1Y+rKo;)LkK@d2on=HC6%&{_M%&dv!YSZbXdU% z6>k+25tcZQxK`YP+<1OYc><`qol|y9;EP3LxdA!5XpT$Kn}O8Kh9M)FR)L=_YWQL< zOpF(Il@HDn6J1L8c}fy0+Bc#`b`!W4M-f0DB#vMNIpWb&ggP92ak-J7wNYEXi=Vxb zt59uB5>;eD_&9q{gT%0JB(Q2K5i8@x^&1n)bcOmK)T=Ej_~ka0P9bo%2r-d1L?D10bx3`Py_umn zLw-I>{aD(VK;tNG^~3{N(+67zi~5Y_wRsvL!xp2jp%a?-GM$oqvx)`@I*DE#(M4(# zy*|uwSrjK&q+mY6q~!;eOgJ(pGQ-|`sF^jH^Gg7OrX66K*IHyQmHTy+lIR$+^Z>)L z7KITxg4`A1wg%=*wEf=fl>=wTqT39g7re;S)yfMhxJZ60r*4oNLS2!*N!D4sZ3M%` zR6ob*_&QgV=TDpt-+$VQ3{z!XyjI8818g{7NR!=ODYMPWYjs=E;;)!Wb@% z({tlo;k~;d-x0>sM0hOLzBhzsymFGCO};?ZXBfi^j4nfsERj8a%XlKIW6|O1mNBoK zRgfD zkGM$^kvpaI2as+LWE>7szfH=hCqqn%FyHHSa3*zksRTF3za*q;kJm9Jz}CIypG}0 zx3XJhP$5d^X;cc?99Sq9-% z+^m)aBa2g5Lb)hTW1xZ~L*?nN2=6vY7$Io=FiHv`$gF^EUJn8&T)4lWSsoBb3P*NE zjk1UKv~zMxtX?6=yZj!(725NWfeNM?p|$!N*-;BtK%RQrAbpL1(cqatM}udZO(keu zY_FKcoW>&0)2m|w;wy3410kl1{cur4p$grn6Vm|x6jhyWLHg!M9W7}cfTsl3Ch9az^|;FivN$=LmvDG` z6jP%;+>8Z}m|m!24x{XCSK+%$!ZcC(MqLM=vHA)^NY7e~v3M@l73rUV1*j6^Fw%b; zsZqzn*1fM7tSATRc$NHE-e~t&8*wzYg4y8*2=#X*NHLam@VCxK{eElfLWemp5*ZB9BPl zN2Tv$(s!fu-9*3qOiu5L>}|EqFh=mjGk580QWzt{Y$A$nAtZF0vZ#A7*a4th!^HJgf=Q|oQ?^^9EzrpkW&$h%^Itg zpEYxfBeY2wj1QT)#rYx?mG`N}kc1Vc?O!n5(5Me(wt5BDf@o#; z&%D(kmo1mg($1?_P=V_1Hf?BoJ^oexm1)yXy~)q#XO$e(E;ThBf7$sSHxZ)sc&!2) z*;yzndDz*?FcE#N{&^!=sd36x-rSZ&vC(X_w4BMoeqSf2t9ep0q+E_Pa&gEg$>)YX zBl(AhPK~I}!RS)wX;#c8^0?7{ua0V*j8{FMq8FQ64vmY{;iq7-W59~R92&aJ%K4l=gxTT%Dl^yenu|{0PI!pwm@;q6>k3?)BFP7K@Tbo=BDqyEW-KF~gdw8%NiLP^b0cly-KS?W9T< zf!ICd^I^BJcc zboB^Hd_g}|-s4}?a$cXp+I#=*+#pVY(SbSp2e2yw6a5T*1QsVcgdXEfW3yJw;?ksa z6Veq}heUW+^Jb*S2jOq%hrfp}7@Kwf*&uv+Km1}o59w=z@WcAy=kn#K;Qqb}2KN`R z(F*SGt01($z)iG*<-rO(-hTLOTEX(Z3Nk6#%drTgWP6+v@dn~D-e&L3>iCATd`J_< znogX^uf_U`IjfRDCi509QYz9J_qFzX-U2RUd=@`*MFFaztxm_9ERcUX?yPo6UJ*Zg zeAbFsN*r<%`*S}USo;3=f~8f+w+D(Jx>Ef9ZGqyR?*8JhUR69;x#wbk{xbtg2P^lS z>Cb&~VCm(-$~_+k^2dQ44__(1ysz><#U0Zg&6~#;gKcGj-t$yQoOr!uMXHUJK`|W} z{>x24=>q>;nraTxHFN>o2DmUdH5><*0*5`y(D88C)(tIyD}pN~?h!X7MfBWi%oFw+ zYZRr6mkJp|rSq%6Ab@Oklj5{7@`#=>*a6oqMw<~U)?Amo=O0kglc8Kn$>-^-LnY|hWNyX`d4%uTmfacO zvhwhr=gGEFg@J*Ks?U#_${r<#(PP-x$?Va$$Q3A*`h;CFd15Z>gwr!ncR?7%5EifS*dbG_gL~y)+BZ?xtwK=>-O&o zK#JLBgXqvjV6)oe&n7d*#+juis3$Uv1~V$g>WW#A&hgy{$L8hbF|=veN!P$fSUp8k zr5i%WFyabb#CR5XLdov2%QSx%$B9HtG#T4|EU(4#x!b>zq>Y<4B>dMk_coAq~wPbgk*Lqd3Zu{)F>{i ztJk+kw*>Gb$eszQY%FQTmnFYUxG9G!-ioUfZv_-@kM|kdLMYx!gz9NY-^UUbnH2sT znK3DjterSKr|LIKFR|qXOaD$Pv5l7Y9+kpRN~M27=?TA4I>VL{EInK*Q7V`dlVI<3+J<4(y1z03d;_zq?VyF&&GMH^+KCz7;J&62r)B7<*CW!uTb8%Ju6 zSq#{z_IjSnWKO?DyWRaRS$^X}%tTWdc~o9-+SFnEHw7ej=wXB}XsYAXEthY6m1Xym z=kp#?hP%(s8)v(dgqeqvqWm%PaCaPeINy1PBh9e^J~SEQ9x<{Vhf{XW%c4_4$3i`0NOpz!T|{9nm52FLTNasD%jO0( zAT8?mpZpH35CSB`tsF=K^1kiUdhn?Rq?(Xw#7sDbTvg@FTd3ge!bZ9@hOFd8u#|{Kv@Wh4)B2jy8e6S`eoNvD&DBNht?(%t0kQ?TV%qX&@6%+}J zF<5-PybYro+RN9QZ%c*tY80UxJa)bYqUGzIKsL|Go-6ZKNj1h>1_8%LF(ZYA)leZ2 z=eEF@kd~GZVr>({jKw*jI)t`wKxcfQsTwi~+Ck1R@o;JuuwRnc;-^@SyimMN=4m67 zZ`GxF4mU=MngDLw)P^WQ<-Eifx|AM+V@RzA$3idHhjsZpuab4QZj^aM5_#L$WH3EA zrO*m#CtzlkIv|TyN*3Ix<{;SyQ00#&BD-pmDCZ z4DRx|)#Ok~QigPtAx%|9L3{%mL>o-&!l*_aEsA-1`CUlJjB2MXyDSk=9k09o@Xo>b zq0;d`TXg^y<1QYJjNb>>M4p_V(DKrPr{vky@k@|!$nW>vqNz>*`_)r0aGh|_DeD=PHIl8^6OTbg^CDo_k5`9kXsJ zv}xj}NRsRAG23D!$@TW!h|XK=lnnHgw~{oXiL~<)>jMc)**yqf(nZ;w z9YDB*+FN0En#=S14|7p7ta_$;t(e{X2)~ZsNDp^mQHd>@b_*}0(*0L!s-=LR1%r8L zPYlqEX~quhP$bMFjX{G~E>5wYX&rmZWD!SYt#TLZ)nn4m6|)f92mO`Z<@FqraM_u* zFU0XcnIvZMvyh!6&>{*;X0U=NafgP_x5gS_ zfe|!(Etv8Mz%Rp3g730M3$f0#;$S{sR9T|x7)-Lv(`=p0#rnRbD4b_f8Ruu|RGfS&O4!*!+A__LGrWlx<8ARtq;a z6^IjsLycx`3a2v95h{7}cy(E>Q7tS+Q)J?UAmIt|Eh%g-N93_`+UGJ6t@EwrCQ4L4 z#AWGI5Py)*)2rd%U;Y#NOl}(nwy|xQwj{k0Cu}k!yMsaQ-CU?7Y60YXgUBiF7YE>e zu3;LEGD92rboEI&q$J-JYWMxKIhV^CD7`i%H%`^h#sOHd?`aF4Ref-g5MD z5r*GmMnAg)w>Dlscukk&EqXxwpY~uU@C--36WeH|tg%K`%7x@5fvf}85~vpqUzxpzggcamWWCNzv@E|2%-z;N-J`>Ny&dKn!u$Xh6;)3f)l zj~RRN5^%=;wNYkHD1%{FuMMCnyAx2ud6S4!emc;$GutuHy!le8W|YW)r2 z1WU75b~P%`*N z!qN`o4BOFVo<~H9Z)fKllqO9Z^EGGL12aLzwYcm-WY!=uo;UGx(2Ft>>_?yyf~ax= zsLpj+b^z71Yf%L;r3NwSuY)N!h)Es9blhUct*36?Z=hlVsNCJxK{Yghs=afB(v*t} zOs`t*#i_h6l`)Kg`G@k$AbcI*7$uCaqYQWdBZ%An<1`L2ZU5zFmB zc!HlDB33A| zEJJ`QAIfQrFitRn^^1h(g|9LFE`qdU#JiJ&xDu~}D@U}CL+ezgbfEggf&(re*RG)I zS1mBi2(=?TB{@}R-W7klVay1|$nr!OhoSUSa40<>yjk`D-`M5$U!qDYFpj7d1l5K) zl>B^@Tma=WpXBce z+8IQp2%=&v4M6osKdNgnEeT>0euZgu08{%v4N5yz4(+Ecj{%kNo2YIIqRRahs@sC7 z-U$%Z{s+r)V9HHJXN?#m$rc@iBdFq%^n=?^4+^y4@eoOE+0{Je?0&uF_KyxCtP3K1 z(jow%2E9nf^fkzOe+nS${Q(-H^eVOG_QRYC*}9wJX|1%}f-3~BUt_UDmAZRdgJ?eb zHJaWI35`ucW3fyHnvZ{tM#A!H5X&pq!E!{xk|tqEwPXUz9tn&6*KOJyMDyr%(CifH zcnL8oaatv_;2xC+$3B>X+`Yf6kI+MOdv{=?p`~enqwzV7D(Q}uCUD?_=<6B>cTtnF(d~&|G^)o!;ub4y~6svZali=7KA!=IA!%( zZQtoelE}tLjDC%tH4H*p9-YQI47!nsap9~lSbAvrNow|F1GSG7!8R(H@;1u)C!5}K5r5UJ> zK^sUDO#oFtroBP3UgpjGe5fVdj0#T6?w-E{iP~<;ZDrd17W=b6^fImqWd6F1XyKzl zROl-ME{k4;sws%7IzUwW7nUu+wKgc6!S?kNRTjjw@M@{DW3k}wc_4_YD2QsmGLvk}~MgTuqfk)TkgT8Aa7rX(2uHUlmpbT`s{Og8w2W8+qWT4H?oZn0bg>@KOC$0U?Z_$ko3?ldM>6kdbJ_UEz z=;oWQ|F7&^+;2#^%dt^Y7(aV$j4li?N~SabvmJA3u>4}j7=u4gld-3jIlr(yf_SfhdNYjQ-w!gD;Jobs|52CdpaZ9}@X zC=Gt0BRQfIhuGcS>V8{M7H-QaI6P9|(mA~jy0e>_zk=5?IXTiJbfH{4E$#a`PzqNw z1L+~b^lyXdxWNWoxP6EFG74R|WXW**PD%J+WurITXnsi$K)Rj>v>)~ZP+|x75$pRC z(y@OISl4th{poL9mCp93ztj?0egmuZEfw!$b|-Mtb{-$U^k2M58_v&kh4{+I{EEDE zUmlWX@+KtYI-6`mtV5T&aEW;^;@Qskm;Q@01i>@Odli#LRI9Pk$$?EYr~YOg2vLhN zPO(gr%if_8^Saz#52wTT7m`vb$h^;yM=Mj~+}=O(>tMhbj=?!pc-m;0?*d2*dABmv z?)Ju6q7aL6;L@H$?i%oQI{Q9|+)JQ{rHp)z$=vSSIIi zL#RzHqEoyTOWX0iYbkDFwqlC&;_fKEzYAQ_uqe9Ro-@+-ltn%mk$*UU3?i(h2+7K& zNilA(g$o(Ow3X>Lw$Coe6v$h>b5*V50T-JUedA{0!Oz6SE?vUFMZwz|88gzm*+ zg@WNUkxXvnW4lfDUoemDJFfS;LOcgnr90oibtShaLC|&l%jkDTTVhduizN=5&i(~(9JW^mjj;IG{0Uu zh||_Z64h38kNY^;a8H5EGp*&rd)CWwxY;iQEhYDT%*wlZE|awTAG|4~`vQz~%s1gh zGq|*@8*pt=?h5Jtil&|SBV~3l1$Tpy^2_jl?$a?V;$OjKpgfnN=ULKz|KEV?CbDNm za&lLX!8wXIZ-xEH`91EWgLvp6oN*_?xSteBF09xn^PeX7K46zu=j!}an&mt>x+*rh%QqCF?Qs4;#W-^TxwtAdu@gvt##0YA zp;J~GpZFT9J<4S@hPf0NLz$~@mwEq69$!6%-A)dzPLO$bl9Q|T^6Dft>0Z4suFG2@ zwv3O(8U4@YSzTS;S&*eJ?+jA4=1$IgD0Q@PV(~YauNZ?4==LL|GpG>yFY@D>g|~!w zI-d=N^2vgTb>N;{+$g4Xh=VOSC?@dp_9}KOaq;3q#Q#FyOYnueL-*p=K+j+Jnla3& ze>|f^*))_q8pQBEdG?`1oJDUShaR%Z{Av>R@JxHJ6H57WEHCHtwa%0LZs(^42B9{r z6E_^F66IW!n8*zkG^^wp^48*qk3GB@Z+jAidslrI+WKi>VWurp+4>p7se~3IyS8QR zPe$3gY%yEMl-*~qu~nd_ko5AA)p zT*jES4`&23_Ac7LsA~0v)qB@S>1+DZpQHA{#rKuJVN)-1X^+R9h&rux3@%+fesRm< z%S*A5=sdGF1?~@2P#GpPACde&kmV0I z$+|yo8EhZRvX+*D8jXzI*;2Buhc#BgCTye2??n-dwr~-gYFuWv?pW2kRWQ`E0h=Q! z-f*j2;d_nj+mOS)NzQG^$7PlgWcVYGWh=VRnciwuD7H)(%lFR)dUCm)@VtMJJnd`!nb1EsxprLrAgtol0i)Mx+X za1j@aZXOuE20!@=EC%c@d#lP z?g1;h)ui~bsqF7a-D8eX3U4bajg`(C{JsdP2%L}W5Azmg6A0X9dbDjXHxYOE$6@~d zjLhG-8syF(CpV7JD14J_2RMbP{^W(i{q+%mzL^Z(lppm;ZB&~7oXpF_hEvBst zWvqqlB#){+H)gT5s)mUAa6^G*@9EjV=414q@#emSJQL9sa~ZV=Ln@V0l{nA$>A*po$wN7SXf? zU&DN_hEd*N(=7NJemrJ|!(*o~b)a2_>k!dg>c=p9;y|j`H(Jz5{9mgR`beY1~1@d!v{ekM)EZvLM_g~P+)PnFGRrJwlzOkG0q1EkU@vT@IEs5dq4=@jI@qe z<-?dv=F6qq2(KCc&NwihWiwqzSwJXkF$T8SMY&r1BbR0WHKi~(Yjg}Kg>hX9T{6mO z$oZ`i`zk5rJS8~Dx&CLiT%0(V3&9FCjCb%`anh1r*;yHnR~&y{mVq%IkJmzM@7uNn zIwrs5tnu3QOsUr1x$#-_-w-S*^O1c)ksXU?nx}_ywuJa;o6dxD#_U+!P45&-`T4o5 z7BB9ENm#uOygUvyI}l1+D!@1)-@DXj^au(lgIkDEae^MWlI0 z25!U>^3@J~9OM7&{@oGG{TJ~1@A!(e2xf(d(8pwG{a96xe^TYwVo6s|?wc4ChuQKQj62yxel|`>?vWm2E~gJC+n2BM8r6BYTzpnb zHZ+XPj^-vU?x3aI34(Ns+PWocrc37a>1ry+$`9k#j=;~kLl;u|nH;Ug*l82VrwyrW z9q~38aRF;sW465t$9PQpc5HEqiqBC>om=?XA$6ZJ?JpRM7vP`}xogpqF)q2Mldl29 zavY~l;?j9+f4jAa%OG=Kd^#8+V}(#_Ak*)PWxz_XUSN9OM}I69j5sg>e&;5X%);%( z$N7AI{aDsI&AxjO5ZjClkqf!^t(Fb|6$H;Gh!T$s=ic{L1aqGoq2lEeBbenYX>oo& zL^%=_3qhpW#-V&6s#c-uM~qg8_&Hj2_vd12+0M$bSbVVE=WQ7gg?SleXBQczCk+CZ zRyh{w_LH>z&UqC-Ez*~L?YtFyuoRy&78aj1T8qzhoLgGOjpWxm_byrNEH)0~r=uFG zMP~CBV~ik|o+K3+qxxIUBV7|r&okngN02PsF*eGA>EnT?k5i+E7`c9=Y0?X__i){Z z{L?YS-uy@{gV}KyM<0!1Z8IhGAItJCT8EmUILk5xfGdfj|A)eMgw1F{(3o81m zC9VzE2*S9StA+|*d5Mo3844R8o?kMG`4SIcp}+A+8@vof>7jk4o&OYbdHa@W4m@Pu zEUuyJ;PyuwSuU(H1{0Nco4gh`M8&tJt;d`1C-%xQ3D4VuBh;S6MwTz3Fx|s#vmy%1 zCh_y~RAsB1WSPvFB@Oa=*2VZXQPtWu$RWd1nE5#Wl!>|1N?T_%(&{w<;+;FKW>c-b z^-`0pRz3yitCc?7$T(B^1;24x-pMa2hIAz)8hM{8T4f4m!O74q0bQ$s7;By+wBF6VIB zlFIRk&`!rp&$Ru-nQI6#pDUk?cfhYUuocJ3R+Z&C6w2YXzb zGif60bkY@KH1=cxm8LG1)Y^dkSLG3YOJzKMC}B3I=-si2_`@umO5U1>MUvv$&TsQ1epT z!c2Z1uEIQD>4S!4zqnNSm8tpUcjz80XT0Ao%k5MA-mh)LxDa&Cm$Y-NN%Ie0=^WFu z!Ol4tm<4<)YpixJ?Hqew@3j2|Jrl!a^!Lm$Q&ZcUv}2t6gUX-w*GJA*`(T6x0fstX zpzG|wM1ae4#RJ!#%}>>a@|)^H=yRdgHIjN=9~fg0_J@s@1$GQK1Rt#YkW=BJfKuFq zJKtN6FA+;opsZe@HA_DQ@I}46Nx@so2lKYNDDgC3NL1y8#+t2>Vjkm2D6I`MHR1IU zXZxe6=_GTurcqHB-VlZA<}}Ld!z(qW`V%wokiL=AG=@*BJIS=Kst=DSz$G4;9n~;r zpQsPV=%jZN;u};WS)lj~E2)TzBa>z3`4wjEaYs~rxb}E0(|~tUxa8O4yR$K}A>7Dl zH)BU0Uax2fcSI$acuSeQK_&df5m{yy*J~ZZdQROguMdT=$#E|#`r58-2sPU2I*F=O zSA)DUs^Wu&@PrMr#ioXnOhggr(>1EnDLD>KlPlvuOuZcBw1J#3Y)cIeTpRY`%%0AQ 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 M=SwYXF0t(Y2eDDvF#rGn delta 18002 zcmd6O33OCN_V;`Jx|2>iAxj6+NgmKk0$ErB0S$}NbdpD>J4hfXVbf+2UJ@Jw0wSm# zzy(2_kV5S&BBQ7%>STL0w4#l40B?cy0V?2z%VIsjNO=LazZ=!7AB+kF22}wX0)c_~>dJ^m&g@_q3PSUgHQzGBo(QG&q4Y5+ z4FnE`$`zq>RVaNolp|n;?1A#x0i?YY;mF>BE7>kN$MRntP%|4sj8X;;r1A*!3OslMs-7A}-G%HZP zL%Z0;beo-Cp;=w&dN!2dtAb8!Y#s7A^#1RwXLyM)aa_vdbCc9Uwd`Di}BIi zb-Fe9z21DJHom$aa!bsI>f(9Uz}$jDk>!B(V0Dc7ppMxZyESgBdTYYg{x#8s`z`xP zl(62q-@JdTswR>|R4WVjS@zL#D!VnjCX6Vm<&OB567>wYj2p(Z4X=%ENHj1u6%phG zc2{qmY8cbn=7{MGbf4{Mf!4?BqC|BT)5;1~8{ZHI4AXvIFpFFk&3#>~5>+&J=BP{F zy$jQ!K)Mqti*!ewiqfL?Jx5I8gW&vhD(iG)hJo_s%<#6??>CT0h1u>vJ zuG5{hiR)DBEJ6BpTPXFqBYDZzMW#9k@S>^i+}7lO1i&7p0k=h1BCr&tfz$SoPBQ3QTp!nKNT^!evsiiX00| zT5vHl`2=%DnHkl}IG2y0Lwz&I#JP+bsAQbqF^(szV5}~H2Dm7s4eI6Qqv}eZO}%_t zDh#*_xCE4q>g96V%~MNcfGU8T_`(e%%Z2HpvUY$=5qPn>e>&~uP6I0x*$gC(itilC zr`Lw-BV8)*d#K$=R)$Zv4`MTU!-QGV+_BDYU7SauWvek=9oLzkF>Ppl+Oz?K>##m; zs%vFBz7l4N21k^bGL*4#Xmb*@lfzQ_rDQ2i&FAkjIiROe8B|*%hKc71>?Z5UWYu7VA*f#)_P23+M*vnVQ&+6zgh9(9JG|e;ps5&V0_a zzpiD4LYuNWt~PN9yH>R}WSMKJWs;Z(rG2F|2iJ$@U@6Rm=_ofZgoGWnN}piqIpAD(O^?|%)wl%1dEu0{0yO3co z4A_a(0y+)}Gs@JPG-X7&i7!`g8YrsCXAz_AzF&v1Xp%Dxx~g@@D~m@ZEg*-4yS{^aD{67!es|+pmlJqLYM(bi*s0;N+b<( zF-6oz)@$l*29Yb1iQw(%FQ&j|9-`CLW@kFYp;fX;U~nRp*%s%VI+Uf>e&MM2C%t^v z0GG=1c#oNNUr)^pX7NWoJCv=7liIySCuRt{29vQynu@Y|7vn$8hhNw|ODJJstm_#j zyqg*}@+@#N0B2=wnkkh+vm6#ajO>bBKJ)`IORO_oZ_KfkIL`~s(N)YutP1RD-c;n_ z&b`9`>BOi?vcG7J*E3>DQJh$BtFy+5T3d2wz`I6-Gxy$XU`=x|+ALUKxNU@14%|{P zeQzb*EUe+sV3zZWv`u7#e{8UicC0clR-89|PpmbDm%{>-2DUI7v%dW9OY7U z&)3D8RW3zt_f)G&E4T61NK7w;wh=TOyEk?(JB4o9v5H^L&@_Px9vlc|-kwilqXras z8M^&=rxx|IX(j1WMV+3Jazn*TLlw+4*_J7lIc#y+lqu#z&K$*_6#yH|MLVg8%Ch_G z8d+1`&-G(ME74nB?)ptz9)O?0G?}nkdM-b@k!KMx{C}(!kFU<<9WS7 zj@Fs50=brHR0O#cMe`ZsB6+Qg@vuCLx}Pi&*2vOl#T0Wy4a4iVvRh@ao)4TIYV6K> zb>#)9@PZN!N@SQxCxZ?wZuuUDXU%d0M6z)%rGHm&ug0`5&$sP~MV+E?Ln>pFH^{d! zE@hy&QHI%*Pl=W4vw68WjF0BkdP*uF8hfgeQV3#B+;PWy7x5 zJtv*{0%rI1XUYWxju zk9DkV5#(TbonC1k4@M?)uGD&$^Kj8{^GNG;zoBR_53`Q`4X(zVVI9#2H<(JY$vTL# zc7GzV9tzg|%|j&C>&UU#0Y&aR%>%51`%qD$+s$fg%5QLUtxAc}ixQ*$uE@Yevsz+w zlM^RF%+f@X9XG@7HWx(}s`QLaE2e|hbh=XAnbL2#q*lqs(L6i2E4GipS{$<{o99?# za`V(^rb^S?xZc2snT0B*8MNAK_+?0(Wc0V|>)+uAOYLRIM>nqLW3d<#Fzg?3rU{3! ztK9wgER8mo&B?Cpv*`A?cvX&usn;Cj!^Ab9i-v8`dKX=AMG<^9ugBr;6KgnZRFfxa zva=th$Gz^G`MlOkwl&UgBuA5LXPB^=eGj9JkML2JQXL~~z?6MNJ(e(CU$w-#WXqC| zm#UUpmu^}5aq$-Y1AtA%TZCd>2VrY1Qw@>A%;M)#83UtROyt`XBy3xFkvV_5@QAP} zjln*^Y~utXOcs?8bSVaxQ5R-vv2cGAG;nN-;}jMV>uC6iRC zN7)C}%Ig&QCAK8noZ7IuaBGT|36|>R4GP8Qk&E)wmPBEC@s|3qhVW21p?Hh)O$;uh z4w3|TOI$6-$L5Lo#lczhKaE+G3;8xJ2E#Zv45!D62b_oDXSx9N;FODRr2z65F~gJa zsWj;%;FEuv?T7hrVY5(8WSPvD(KlT;dt*V}36+*@4FB!$w2bpg;mA@Ggo>+CxFX#} zmSH?w`mV|Gxl_m^aA?9zp*D>Xn%etW(|B#Emro+Jojup04>dd?Fk5E8WE~S`PEpK#YlH9Nm##NgOVvrxTx+(t9;GL{@rn2yHJ?uWBRvG$ZVh zzFlIL&Q`8*$pa6Mv5Q&y!Fk&j&J(kQt>vY9n~rfs`ks{5fJv_S=T-VdVB6JT14vBt>R8IvM(%XR~Kt)i1>!o@szShLc`58bCf?8yb5&(I(USuuy#m>M_0^23`*-ruE(*LiNY4?ZA7MR3-Q4 zBE6pxN3sFwKJtEYEz(tFPRbUfBgn;+hq*}4H)I7rH@?#Ag97noAf1h_U{fMr^P^Gi zAPEC$vl6m>pf@~N-PN>X(9P_~Hw{(dLbl$N+{zf|7)IpP*%oRQo!x=mR)tmp2i_fc zA$WL^Ty{Q5)!sbtRWZwwrX6im+p^$QUl1p@vjx_ZDs3c6_mL;H)7ZO7hjy@JG)mom znLC3V)A2HQGWkxI#yXqg^&?rswO02Q1m6p|0)VN~!wf8OvEc@;yK8*YPD3hd54`An zK%7WFcF+73(-nz*e~>PcSHMR{xMvBbQQ51f^XU>k9(Xy9G!bsAkOzEHsC`&(`$i|%tuL$ z-%UKHId7cDE5>9C#jEqt4MyGN;nPF)yA#i778{F%8Dp|nCs5%~d#Sg!p)d8S{UPcv zP;U>ifAlK(s%;_iqrK$!T|?f}`QG08JNi)fbpC2@?X7*OSN3%Nbg+IjXFL*nq8xCqZ5n#AGXu$T-uM~-Aa zAo1DLIL>#aX>oQ6tLA*Grmirswk+#%^9nJk)9=4PYDV20^NE-v){Ds|ed}f5){$RF z&1IhzMx&8I`UcA|RY-Tj0h!g6gKkD1Gwt1_jU2;*PXZBh?ofXzjuwfS zVluS-TvmUzwpmJcfUwXV?F=}c!Y535nch%No*hqV?Rh!`s;-Z+-0Y#ysCvX0j-w&h^(Q>^0zlPyO~n3~(pns%Rz zg4goUhhq zalGYlynfGa>wGxg3glj(l-@5SE;1?qDd{&MkxU*xWbFO_f_jD37@|hp6V%UAYR_RQ z-yl&pfx6&dP>-{whN#C#6lD^1wUl23>g(AlM3^u^1tVn`4I2n_E+s!q(1jn=jz(Xx z#CSsjIW!@f4Uo?!Ok%&5%9#_R*<&Pk;$-$!sr>jv75g-)L-}5*>_UA7=|yrcO$=!fU?rfT=?&wnEc-TDXu2;vJn)ytwT}ZoRuGX&`X&TZr&oPy{DF%C9;^)P2+A^pH{qcnHu8 zppFrXO>&gfVZw1b)5(=7&ky}~ooKUpdjD{*Zv-OG^MV_?YDNS;W z33_uvIwz!c9&OSTEoJ+KyFbF2fb}y%;hN2B$cIzaWXIHvT(~EO47q7G7w#?~8*aJ} z>7R&f+6`QI*9Bsl21U9>vT|Al%1@B9)80mUJ=tDd$gw_q)3?P$HlfmcC=l?)pko+D zNW-hzAb|HYI={#aN!!6l0$q$nJ2jwxP%!UN@>WU7G}iMyR+6{7-NkxNOSvzQJ16Bn zL+-egYeVjolsk&tAt`rI>tT$b+~+)q2he+Pb+da*i2JclT!Z*0>LKSSw+g(5` zH|NY^J?T=9L(Tob183+|7Evs@jX0rxx+thMQ!Boh&M>2JDHW?gnOeCyH1?V5O7%VP z4jjWI0b*IS70D+z|B-c(#kXved7dYpTeRt(ZS)c&$lH2DZM040{9Y(T_~_^Q{?+t? z;zftD)9YDJOtUu0JQK;$S)&G{lb{fxj}R(MPywINP6=Q7vdsNHIaHd)DX`nPN>|I= ze<0#)Z<4E4DXM~<)kRoiY-;C5b9|?_>pZ!4&cG}w$}VkHt`NUI1EK?_3a86jeNWT= zIXZ4B5W}L|>By>rGjj12ERcJHGjnxup^U{ut>fQ2b)fA@@C3~_xqx(X)4b%SCAa>O z%c;ER0>T^!c&8z)rqhj;>jY5zJZgS{z+c)agPjYZ9?>qkfn&V+O}0h%upHxAOHMB?n7}|!jI$O@TLH%abXw?CoD5A9M)vJq zKm!1hUxI7-_Fmc%nRok*GVe^%aQiH%{mSjr*h3_L$qnoovSG<+t>>T=(1lgiGor>x z1L#V)<9B$uHlfqwuOgo;nLcfnRh=|ZvRsdUx;0L+T#vs9%3I__bQn@#o+N8T&s2Gr zQiBqD@g>`1nd*Ch-wD`DRxZ^>b>EHv4NC*DJ~`R3G{N4Ds7;^SLU(rttl#682X6~~ zQbSOmrUBGWcef=-E;NVArr(su_m*RMd8ga|Gq1(R6Ai!y%isQ{Y(LwJV02Z8(T2++ z&`pdr%%;Jqkh?a_s9trvol1^q(JVoDqgl_~G;a9@F{klCp^A?dYUqt1Zd?+J#@N!t zF}OJ#H#J8bhIFWTv@ls8=2G|&`)NyQ#3$3Dodj{@i0~j(Ck4kvoYO*$=E2@Ct5?rxmpWKI<2==~c0Co! zz<|jZQn|Lj-R)2B)pIst(Q+O)(0nGZ)5fArGU`G>cZAp1^O0JF*Cp!cAQj@@LA)Y_ zr=q|pX}lP`5Zgv5O0JEdvF%Q`?`Mf$pZd~J11b2824{O4P+{YHg!O#}JWMZWtlGF< z{E=($Y%l)(rg`hGV>O-!#JiYX$$Y_%<71ZnB$zZ2La|HXxre-buQAh83RJOR0;0s( zVC`=iu*`*vqj;39&X1P;#Op)N^GU*eIF1c?=8y!;OJ1on2dXoTo- zycWOx$jbX{GWVP0v-{E#-QG`y4KS)9Fu(z}Ck*B}u1>f2V-kOVn%(Wa-W-k67;_YU zM+wFFeHL-II_D8{81m`3@OOLDq+h+Wnc58%;F3>W`5kQMVpLFwJ2+krtBVz(QtyUk z?fA7Xv!PEGxwTXz@7=+ehZ&35)J{*Za*f_Yq>We;VWVn9U18SRnnHNNQE4%=-!{uCgV2*NNqFhF3>16QUm8WpbQi z9^TEvggkeWBUR((y4`K&j83=vn0W|(Kjza>GZ6=Jw`Uwy#b<-oXqrvyj2e`0cr0_V zs64Izdz`ykR4UkA+?%XxjB)3(Iu{qR5(u@0ld;{*|6!FGLjG#HVNDQbf-og!Uc9oeuyr zBZN68VSXL*^($Itb<#@+t{Gi&-(N}grh{Bp_blSsG)jp(SCj3Mdw(U3 ze;i=(ov&x}Kctu2LgB8k-S~p`eIZnhWIq4L)8S~Ej)tc~1fM2r|2g(ggX7Y2n@3`+ zEiyMx=2aKVx-OH~suS@IH;*)KZIJo%nr^BY#j<;ws)$;~7B_8s!q1XDPtD}G?tBvE z7|R|eCdXus3%o}jay*>F1=g6}YK=u~e}DrwUHHzQBUCx7g;IR&kHp?!7ZHpP<#CZ&hNksGaX+CsXgDPu^p$@o(uc@)313Y<>xsH4yQu3 zUujuq7_W1%z@H?xx{NH&FRx}go`L=B=xgjnvxN_ z=KO!R?&0Mo$H|L@Z#W_m8v2r~XqX)RhwA9`z{zHc@ogu~4LJz-Y$U%n=nR~1Id%yd zt???Q`m)q!W@oZKJ6Ff#7Xy1U8UJ*OYD(c+My^89m2>{fE!b7R@nWZ82oWLtB*{bdXRvBl&-f1D z19Xu!uS}WH-Swqth)lja>+bTX1HXuXR%A6TaG_UDK#$pU*IB((W z)qfAq4zfW?w&C_B zo8DzUggxLkp+qtFQ_fI0?;MTpHv9AFyyn@5|uW063NL?e$ zP}F?FwC^;`oOhW%VrpLxxktkEii9diz7l82aeStb*V*mEzSSyQ>kT{gNxUIJu;49| z+GKR@6BmHzHaK6Vdj`(Zi);qOufW~88I+K)>q}v>uyGV?$=}t_j_G*?PtiErguB)e zmdJ>l;AWSEgZTViZ$&b9bs=|VWo{(1at$RH=5%&@)^QL=kCReoHrW=2~*Kc7P=`B%!W7{C%uiBYKZO~`4r&Q zA^c0)*h~P*+Ad5hcAFX(l3C<2XokJ zR@h{tkMm*;)_EU8MNt)p>-q^>GTBy*^ro;4@3%6rVWhVwI#@%5jnlKH8HsK9DpK-s z*DNIY6nm^-6dy1!w0Tm*kiuHVPIUC3p=D>ZI$wa|w)85F%VnLH;Qn0Hu3~WZ#Z&9xn+$vCg|JXn(cHMcLN07c zisyCM|6_%%=x7Ze-ZN$171Xiwxxn7h7IgKEQhF0=!c zD1*BY4(^zKx*P3r*{?p*ZY0%_!seO?qJ(rz`>XVb-u^~#jU)GGgPE>&7mdzz`4RMAlrd~9rq%

E7=;Ec0Y#mmY+9xp=| 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 \ No newline at end of file + \ No newline at end of file diff --git a/source/libwiigui/gui_gamebrowser.cpp b/source/libwiigui/gui_gamebrowser.cpp index dfeb4ba3..e07df37b 100644 --- a/source/libwiigui/gui_gamebrowser.cpp +++ b/source/libwiigui/gui_gamebrowser.cpp @@ -13,6 +13,7 @@ #include "gui_gamebrowser.h" #include "../settings/cfg.h" #include "../main.h" +#include "settings/newtitles.h" #include #include @@ -341,14 +342,15 @@ void GuiGameBrowser::UpdateListEntries() gameTxtOver[i]->SetPosition(24, 0); if (Settings.marknewtitles) { - if (gameList[next].isNew) { + bool isNew = NewTitles::Instance()->IsNew(gameList[next].id); + if (isNew) { gameTxt[i]->SetMaxWidth(maxTextWidth - (newGames->GetWidth() + 1), GuiText::DOTTED); gameTxtOver[i]->SetMaxWidth(maxTextWidth - (newGames->GetWidth() + 1), GuiText::SCROLL); } else { gameTxt[i]->SetMaxWidth(maxTextWidth, GuiText::DOTTED); gameTxtOver[i]->SetMaxWidth(maxTextWidth, GuiText::SCROLL); } - newImg[i]->SetVisible(gameList[next].isNew != 0); + newImg[i]->SetVisible(isNew); } gameIndex[i] = next; diff --git a/source/main.cpp b/source/main.cpp index 4b3d116f..399378fd 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -50,6 +50,7 @@ #include "wad/title.h" #include "usbloader/partition_usbloader.h" #include "usbloader/usbstorage.h" +#include "memory/mem2.h" extern bool geckoinit; extern bool textVideoInit; @@ -177,7 +178,7 @@ void InitTextVideo () { int main(int argc, char *argv[]) { -// InitTextVideo(); + InitTextVideo(); //DEBUG_Init(GDBSTUB_DEVICE_USB, 1); //_break(); @@ -195,8 +196,17 @@ main(int argc, char *argv[]) { gprintf(", %s",argv[i]?argv[i]:""); gprintf(")"); + // This part is added, because we need a identify patched ios + printf("\n\tReloading into ios 236"); + if (IOS_ReloadIOSsafe(236) < 0) { + printf("\n\tIOS 236 not found, reloading into 36"); + IOS_ReloadIOSsafe(36); + } printf("\n\tStarting up"); + + MEM2_init(36); // Initialize 36 MB + MEM2_takeBigOnes(true); s32 ret; bool startupproblem = false; @@ -228,15 +238,25 @@ main(int argc, char *argv[]) { { InitTextVideo(); printf("\x1b[2J"); - printf("\n\n\n\tERROR!"); - printf("\n\tUSB Loader GX needs unstubbed cIOS 222 v4 or 249 v9+"); - printf("\n\n\tI found \n\t\t222 = %d%s",ios222rev,ios222rev==65535?" (Stubbed by 4.2 update)":""); - printf("\n\t\t249 = %d%s",ios249rev,ios249rev==65535?" (Stubbed by 4.2 update)":""); - printf("\n\n\tGo figure out how to get some cIOS action going on\n\tin your Wii and come back and see me."); - - sleep(15); - printf("\n\n\tBye"); - exit(0); + if ((ios222rev < 0 && ios222rev != WII_EINSTALL) || (ios249rev < 0 && ios249rev != WII_EINSTALL)) { + printf("\n\n\n\tWARNING!"); + printf("\n\tUSB Loader GX needs unstubbed cIOS 222 v4 or 249 v9+"); + printf("\n\n\tWe cannot determine the versions on your system,\n\tsince you have no patched ios 36 or 236 installed."); + printf("\n\tTherefor, if loading of USB Loader GX fails, you\n\tprobably have installed the 4.2 update,"); + printf("\n\tand you should go figure out how to get some cios action going on\n\tin your Wii."); + printf("\n\n\tThis message will show every time."); + sleep(5); + } else { + printf("\n\n\n\tERROR!"); + printf("\n\tUSB Loader GX needs unstubbed cIOS 222 v4 or 249 v9+"); + printf("\n\n\tI found \n\t\t222 = %d%s",ios222rev,ios222rev==65535?" (Stubbed by 4.2 update)":""); + printf("\n\t\t249 = %d%s",ios249rev,ios249rev==65535?" (Stubbed by 4.2 update)":""); + printf("\n\n\tGo figure out how to get some cIOS action going on\n\tin your Wii and come back and see me."); + + sleep(15); + printf("\n\n\tBye"); + exit(0); + } } printf("\n\tReloading ios 249..."); @@ -248,11 +268,24 @@ main(int argc, char *argv[]) { printf("\n\tIOS 249 failed, reloading ios 222..."); ret = IOS_ReloadIOSsafe(222); printf("%d", ret); - if(ret < 0) { - printf("\n\tERROR: cIOS could not be loaded!\n"); - sleep(5); - SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - } + + if (ret < 0) { + printf("\n\tIOS 222 failed, reloading ios 250..."); + ret = IOS_ReloadIOSsafe(250); + printf("%d", ret); + + if(ret < 0) { + printf("\n\tIOS 250 failed, reloading ios 223..."); + ret = IOS_ReloadIOSsafe(223); + printf("%d", ret); + + if (ret < 0) { + printf("\n\tERROR: cIOS could not be loaded!\n"); + sleep(5); + SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); + } + } + } printf("\n\tInitialize sd card"); SDCard_Init(); printf("\n\tLoad ehc module"); @@ -327,12 +360,13 @@ main(int argc, char *argv[]) { // gprintf("\n\tbootDevice = %s",bootDevice); /* Load Custom IOS */ - if (Settings.cios == ios222 && IOS_GetVersion() != 222) { - printf("\n\tReloading IOS to config setting (222)..."); + if ((Settings.cios == ios222 && IOS_GetVersion() != 222) || + (Settings.cios == ios223 && IOS_GetVersion() != 223)) { + printf("\n\tReloading IOS to config setting (%d)...", ios222 ? 222 : 223); SDCard_deInit();// unmount SD for reloading IOS USBDevice_deInit();// unmount USB for reloading IOS USBStorage_Deinit(); - ret = IOS_ReloadIOSsafe(222); + ret = IOS_ReloadIOSsafe(ios222 ? 222 : 223); printf("%d", ret); SDCard_Init(); load_ehc_module(); @@ -344,12 +378,14 @@ main(int argc, char *argv[]) { SDCard_Init(); // now mount SD:/ USBDevice_Init(); // and mount USB:/ WBFS_Init(WBFS_DEVICE_USB); - } else if (Settings.cios == ios249 && IOS_GetVersion() != 249) { - printf("\n\tReloading IOS to config setting (249)..."); + } else if ((Settings.cios == ios249 && IOS_GetVersion() != 249) || + (Settings.cios == ios250 && IOS_GetVersion() != 250)) { + + printf("\n\tReloading IOS to config setting (%d)...", ios249 ? 249 : 250); SDCard_deInit();// unmount SD for reloading IOS USBDevice_deInit();// unmount USB for reloading IOS USBStorage_Deinit(); - ret = IOS_ReloadIOSsafe(249); + ret = IOS_ReloadIOSsafe(ios249 ? 249 : 250); printf("%d", ret); if (ret < 0) { Settings.cios = ios222; @@ -377,9 +413,9 @@ main(int argc, char *argv[]) { //if a ID was passed via args copy it and try to boot it after the partition is mounted //its not really a headless mode. more like hairless. - if (argc>0&&argv[1]) + if (argc > 1 && argv[1]) { - strcpy(headlessID,argv[1]); + strncpy(headlessID, argv[1], sizeof(headlessID)); } //! Init the rest of the System diff --git a/source/memory/mem2.cpp b/source/memory/mem2.cpp new file mode 100644 index 00000000..c57a2af7 --- /dev/null +++ b/source/memory/mem2.cpp @@ -0,0 +1,213 @@ + +#include "mem2.h" +#include "mem2alloc.h" +#include "gecko.h" + +#include +#include + +#define MEM2_PRIORITY_SIZE 0x40 + +// Forbid the use of MEM2 through malloc +u32 MALLOC_MEM2 = 0; + +static CMEM2Alloc g_mem2gp; + +void MEM2_init(unsigned int mem2Size) +{ + g_mem2gp.init(mem2Size); +} + +void MEM2_cleanup(void) +{ + g_mem2gp.cleanup(); +} + +extern "C" void *MEM2_alloc(unsigned int s) +{ + return g_mem2gp.allocate(s); +} + +extern "C" void MEM2_free(void *p) +{ + g_mem2gp.release(p); +} + +extern "C" void *MEM2_realloc(void *p, unsigned int s) +{ + return g_mem2gp.reallocate(p, s); +} + +extern "C" unsigned int MEM2_usableSize(void *p) +{ + return CMEM2Alloc::usableSize(p); +} + +// Give priority to MEM2 for big allocations +// Used for saving some space in malloc, which is required for 2 reasons : +// - decent speed on small and frequent allocations +// - newlib uses its malloc internally (for *printf for example) so it should always have some memory left +bool g_bigGoesToMem2 = false; + +void MEM2_takeBigOnes(bool b) +{ + g_bigGoesToMem2 = b; +} + + +extern "C" +{ + +extern __typeof(malloc) __real_malloc; +extern __typeof(calloc) __real_calloc; +extern __typeof(realloc) __real_realloc; +extern __typeof(memalign) __real_memalign; +extern __typeof(free) __real_free; +extern __typeof(malloc_usable_size) __real_malloc_usable_size; + +void *__wrap_malloc(size_t size) +{ + void *p; + if (g_bigGoesToMem2 && size > MEM2_PRIORITY_SIZE) + { + p = MEM2_alloc(size); + if (p != 0) { + gprintf("Malloc of size %d returns address in MEM2\n", size); + return p; + } + gprintf("Malloc of size %d returns address in MEM1\n", size); + return __real_malloc(size); + } + p = __real_malloc(size); + if (p != 0) { + gprintf("Malloc of size %d returns address in MEM1\n", size); + return p; + } + gprintf("Malloc of size %d returns address in MEM2\n", size); + return MEM2_alloc(size); +} + +void *__wrap_calloc(size_t n, size_t size) +{ + void *p; + if (g_bigGoesToMem2 && size > MEM2_PRIORITY_SIZE) + { + p = MEM2_alloc(n * size); + if (p != 0) + { + gprintf("Calloc of amount %d, size %d returns address in MEM2\n", n, size); + memset(p, 0, n * size); + return p; + } + gprintf("Calloc of amount %d, size %d returns address in MEM1\n", n, size); + return __real_calloc(n, size); + } + p = __real_calloc(n, size); + if (p != 0) { + gprintf("Calloc of amount %d, size %d returns address in MEM1\n", n, size); + return p; + } + p = MEM2_alloc(n * size); + if (p != 0) { + gprintf("Calloc of amount %d, size %d returns address in MEM2\n", n, size); + memset(p, 0, n * size); + } else { + gprintf("Calloc of amount %d, size %d returns NULL\n", n, size); + } + return p; +} + +void *__wrap_memalign(size_t a, size_t size) +{ + void *p; + if (g_bigGoesToMem2 && size > MEM2_PRIORITY_SIZE) + { + if (a <= 32 && 32 % a == 0) + { + p = MEM2_alloc(size); + if (p != 0) { + gprintf("Memalign in blocks of %d, size %d returns address in MEM2\n", a, size); + return p; + } + } + gprintf("Memalign in blocks of %d, size %d returns address in MEM1\n", a, size); + return __real_memalign(a, size); + } + p = __real_memalign(a, size); + if (p != 0 || a > 32 || 32 % a != 0) { + gprintf("Memalign in blocks of %d, size %d returns address in MEM1\n", a, size); + return p; + } + + p = MEM2_alloc(size); + if (p != 0) { + gprintf("Memalign in blocks of %d, size %d returns address in MEM2\n", a, size); + } else { + gprintf("Memalign in blocks of %d, size %d returns NULL\n", a, size); + } + return p; +} + +void __wrap_free(void *p) +{ + if (((u32)p & 0x10000000) != 0) { + gprintf("Free pointer in address in MEM2\n"); + MEM2_free(p); + } else { + gprintf("Free pointer in address in MEM1\n"); + __real_free(p); + } +} + +void *__wrap_realloc(void *p, size_t size) +{ + void *n; + // ptr from mem2 + if (((u32)p & 0x10000000) != 0 || (p == 0 && g_bigGoesToMem2 && size > MEM2_PRIORITY_SIZE)) + { + n = MEM2_realloc(p, size); + if (n != 0) { + gprintf("Realloc of size %d returns memory in MEM2\n", size); + return n; + } + n = __real_malloc(size); + if (n == 0) { + gprintf("Realloc of size %d returns NULL\n", size); + return 0; + } + if (p != 0) + { + memcpy(n, p, MEM2_usableSize(p) < size ? MEM2_usableSize(p) : size); + MEM2_free(p); + } + gprintf("Realloc of size %d returns memory in MEM1\n", size); + return n; + } + // ptr from malloc + n = __real_realloc(p, size); + if (n != 0) { + gprintf("Realloc of size %d returns memory in MEM1\n", size); + return n; + } + n = MEM2_alloc(size); + if (n == 0) { + gprintf("Realloc of size %d returns memory in MEM2\n", size); + return 0; + } + if (p != 0) + { + memcpy(n, p, __real_malloc_usable_size(p) < size ? __real_malloc_usable_size(p) : size); + __real_free(p); + } + gprintf("Realloc of size %d returns memory in MEM2\n", size); + return n; +} + +size_t __wrap_malloc_usable_size(void *p) +{ + if (((u32)p & 0x10000000) != 0) + return MEM2_usableSize(p); + return __real_malloc_usable_size(p); +} + +} diff --git a/source/memory/mem2.h b/source/memory/mem2.h new file mode 100644 index 00000000..410705f6 --- /dev/null +++ b/source/memory/mem2.h @@ -0,0 +1,26 @@ +// 2 MEM2 allocators, one for general purpose, one for covers +// Aligned and padded to 32 bytes, as required by many functions + +#ifndef __MEM2_HPP +#define __MEM2_HPP + +#ifdef __cplusplus +extern "C" +{ +#endif + +void *MEM2_alloc(unsigned int s); +void *MEM2_realloc(void *p, unsigned int s); +void MEM2_free(void *p); +unsigned int MEM2_usableSize(void *p); + +#ifdef __cplusplus +} + +void MEM2_init(unsigned int mem2Size); +void MEM2_cleanup(void); +void MEM2_takeBigOnes(bool b); + +#endif + +#endif // !defined(__MEM2_HPP) diff --git a/source/memory/mem2alloc.cpp b/source/memory/mem2alloc.cpp new file mode 100644 index 00000000..83d2cb7a --- /dev/null +++ b/source/memory/mem2alloc.cpp @@ -0,0 +1,198 @@ + +#include "mem2alloc.h" + +#include +#include +#include + + +class LockMutex +{ + mutex_t &m_mutex; +public: + LockMutex(mutex_t &m) : m_mutex(m) { LWP_MutexLock(m_mutex); } + ~LockMutex(void) { LWP_MutexUnlock(m_mutex); } +}; + +void CMEM2Alloc::init(unsigned int size) +{ + m_baseAddress = (SBlock *)(((u32)SYS_GetArena2Lo() + 31) & ~31); + m_endAddress = (SBlock *)((char *)m_baseAddress + std::min(size * 0x100000, SYS_GetArena2Size() & ~31)); + if (m_endAddress > (SBlock *)0x93000000) // See loader/disc.c + m_endAddress = (SBlock *)0x93000000; + SYS_SetArena2Lo(m_endAddress); + LWP_MutexInit(&m_mutex, 0); +} + +void CMEM2Alloc::init(void *addr, void *end) +{ + m_baseAddress = (SBlock *)(((u32)addr + 31) & ~31); + m_endAddress = (SBlock *)((u32)end & ~31); + LWP_MutexInit(&m_mutex, 0); +} + +void CMEM2Alloc::cleanup(void) +{ + LWP_MutexDestroy(m_mutex); + m_mutex = 0; + m_first = 0; +// // Try to release the range we took through SYS functions +// if (SYS_GetArena2Lo() == m_endAdress) +// SYS_SetArena2Lo(m_baseAddress); + m_baseAddress = 0; + m_endAddress = 0; +} + +void CMEM2Alloc::clear(void) +{ + m_first = 0; + memset(m_baseAddress, 0, (u8 *)m_endAddress - (u8 *)m_endAddress); +} + +unsigned int CMEM2Alloc::usableSize(void *p) +{ + return p == 0 ? 0 : ((SBlock *)p - 1)->s * sizeof (SBlock); +} + +void *CMEM2Alloc::allocate(unsigned int s) +{ + if (s == 0) + s = 1; + // + LockMutex lock(m_mutex); + // + s = (s - 1) / sizeof (SBlock) + 1; + // First block + if (m_first == 0) + { + if (m_baseAddress + s + 1 >= m_endAddress) + return 0; + m_first = m_baseAddress; + m_first->next = 0; + m_first->prev = 0; + m_first->s = s; + m_first->f = false; + return (void *)(m_first + 1); + } + // Search for a free block + SBlock *i; + SBlock *j; + for (i = m_first; i != 0; i = i->next) + { + if (i->f && i->s >= s) + break; + j = i; + } + // Create a new block + if (i == 0) + { + i = j + j->s + 1; + if (i + s + 1 >= m_endAddress) + return 0; + j->next = i; + i->prev = j; + i->next = 0; + i->s = s; + i->f = false; + return (void *)(i + 1); + } + // Reuse a free block + i->f = false; + // Split it + if (i->s > s + 1) + { + j = i + s + 1; + j->f = true; + j->s = i->s - s - 1; + i->s = s; + j->next = i->next; + j->prev = i; + i->next = j; + if (j->next != 0) + j->next->prev = j; + } + return (void *)(i + 1); +} + +void CMEM2Alloc::release(void *p) +{ + if (p == 0) + return; + LockMutex lock(m_mutex); + SBlock *i = (SBlock *)p - 1; + i->f = true; + // Merge with previous block + if (i->prev != 0 && i->prev->f) + { + i = i->prev; + i->s += i->next->s + 1; + i->next = i->next->next; + if (i->next != 0) + i->next->prev = i; + } + // Merge with next block + if (i->next != 0 && i->next->f) + { + i->s += i->next->s + 1; + i->next = i->next->next; + if (i->next != 0) + i->next->prev = i; + } +} + +void *CMEM2Alloc::reallocate(void *p, unsigned int s) +{ + SBlock *i; + SBlock *j; + void *n; + + if (s == 0) + s = 1; + if (p == 0) + return allocate(s); + i = (SBlock *)p - 1; + s = (s - 1) / sizeof (SBlock) + 1; + { + LockMutex lock(m_mutex); + // Last block + if (i->next == 0 && i + s + 1 < m_endAddress) + { + i->s = s; + return p; + } + // Size <= current size + next block + if (i->s < s && i->next->f && i->s + i->next->s + 1 >= s) + { + // Merge + i->s += i->next->s + 1; + i->next = i->next->next; + if (i->next != 0) + i->next->prev = i; + } + // Size <= current size + if (i->s >= s) + { + // Split + if (i->s > s + 1) + { + j = i + s + 1; + j->f = true; + j->s = i->s - s - 1; + i->s = s; + j->next = i->next; + j->prev = i; + i->next = j; + if (j->next != 0) + j->next->prev = j; + } + return p; + } + } + // Size > current size + n = allocate(s * sizeof (SBlock)); + if (n == 0) + return 0; + memcpy(n, p, i->s * sizeof (SBlock)); + release(p); + return n; +} diff --git a/source/memory/mem2alloc.h b/source/memory/mem2alloc.h new file mode 100644 index 00000000..6d681b09 --- /dev/null +++ b/source/memory/mem2alloc.h @@ -0,0 +1,41 @@ +// MEM2 allocator +// Made as a class so i can have 2 sections, one being dedicated to the covers + +#ifndef __MEM2ALLOC_HPP +#define __MEM2ALLOC_HPP + +#include + +class CMEM2Alloc +{ +public: + void *allocate(unsigned int s); + void release(void *p); + void *reallocate(void *p, unsigned int s); + void init(unsigned int size); + void init(void *addr, void *end); + void cleanup(void); + void clear(void); + static unsigned int usableSize(void *p); + void forceEndAddress(void *newAddr) { m_endAddress = (SBlock *)newAddr; } + void *getEndAddress(void) const { return m_endAddress; } + void info(void *&address, unsigned int &size) const { address = m_baseAddress; size = (const char *)m_endAddress - (const char *)m_baseAddress; } + // + CMEM2Alloc(void) : m_baseAddress(0), m_endAddress(0), m_first(0), m_mutex(0) { } +private: + struct SBlock + { + unsigned int s; + SBlock *next; + SBlock *prev; + bool f; + } __attribute__((aligned(32))); + SBlock *m_baseAddress; + SBlock *m_endAddress; + SBlock *m_first; + mutex_t m_mutex; +private: + CMEM2Alloc(const CMEM2Alloc &); +}; + +#endif // !defined(__MEM2ALLOC_HPP) diff --git a/source/menu.cpp b/source/menu.cpp index d640c3c4..283032db 100644 --- a/source/menu.cpp +++ b/source/menu.cpp @@ -30,6 +30,7 @@ #include "sys.h" #include "wpad.h" #include "settings/newtitles.h" +#include "patches/fst.h" /*** Variables that are also used extern ***/ GuiWindow * mainWindow = NULL; @@ -275,14 +276,14 @@ int MainMenu(int menu) { if (strcmp(headlessID,"")==0) ResumeGui(); - bgMusic = new GuiSound(bg_music_ogg, bg_music_ogg_size, Settings.volume); + bgMusic = new GuiSound(bg_music_ogg, bg_music_ogg_size, Settings.volume); bgMusic->SetLoop(1); //loop music // startup music if (strcmp("", Settings.oggload_path) && strcmp("notset", Settings.ogg_path)) { bgMusic->Load(Settings.ogg_path); } bgMusic->Play(); - + while (currentMenu != MENU_EXIT) { bgMusic->SetVolume(Settings.volume); @@ -490,13 +491,17 @@ int MainMenu(int menu) { if(dvdheader) delete dvdheader; + gprintf("Loading BCA data..."); + ret = do_bca_code(header->id); + gprintf("%d\n", ret); + if (reloadblock == on && Sys_IsHermes()) { patch_cios_data(); if (!load_from_fat) { mload_close(); } } - + u8 errorfixer002 = 0; switch (fix002) { case on: diff --git a/source/menu/menu_check.cpp b/source/menu/menu_check.cpp index f1d50ae8..7a68c38c 100644 --- a/source/menu/menu_check.cpp +++ b/source/menu/menu_check.cpp @@ -16,7 +16,6 @@ extern char headlessID[8]; ***************************************************************************/ int MenuCheck() { gprintf("\nMenuCheck()"); - //WindowPrompt("test",0,"ok"); int menu = MENU_NONE; int i = 0; int choice; @@ -59,12 +58,12 @@ int MenuCheck() { extern PartList partitions; // Added for slow HDD for (int runs = 0; runs < 10; runs++) { - if (Partition_GetList(&partitions) != 0) { + if (Partition_GetList(WBFS_DEVICE_USB, &partitions) != 0) { sleep(1); continue; } - if (Settings.partition != -1 && partitions.num > Settings.partition) { + if (Settings.partition != -1 && partitions.num > Settings.partition) { PartInfo pinfo = partitions.pinfo[Settings.partition]; ret2 = WBFS_OpenPart(pinfo.fs_type == FS_TYPE_FAT32, pinfo.fat_i, partitions.pentry[Settings.partition].sector, partitions.pentry[Settings.partition].size, (char *) &game_partition); @@ -103,14 +102,14 @@ int MenuCheck() { } } } - + if (ret2 >= 0 || load_from_fat) { cfg_save_global(); break; } sleep(1); } - + if (ret2 < 0 && !load_from_fat) { choice = WindowPrompt(tr("No WBFS or FAT game partition found"),tr("You need to select or format a partition"), tr("Select"), tr("Format"), tr("Return")); if (choice == 0) { @@ -120,7 +119,7 @@ int MenuCheck() { menu = MENU_FORMAT; } } - + ret2 = Disc_Init(); if (ret2 < 0) { WindowPrompt (tr("Error !"),tr("Could not initialize DIP module!"),tr("OK")); @@ -136,6 +135,13 @@ int MenuCheck() { sleep(1); } + // open database if needed, load titles if needed + OpenXMLDatabase(Settings.titlestxt_path,Settings.db_language, Settings.db_JPtoEN, true, Settings.titlesOverride==1?true:false, true); + + // titles.txt loaded after database to override database titles with custom titles + //snprintf(pathname, sizeof(pathname), "%stitles.txt", Settings.titlestxt_path); + //cfg_parsefile(pathname, &title_set); + //Spieleliste laden __Menu_GetEntries(0); @@ -151,13 +157,6 @@ int MenuCheck() { USBDevice_Init(); SDCard_Init(); } - - // open database if needed, load titles if needed - OpenXMLDatabase(Settings.titlestxt_path,Settings.db_language, Settings.db_JPtoEN, true, Settings.titlesOverride==1?true:false, true); - - // titles.txt loaded after database to override database titles with custom titles - //snprintf(pathname, sizeof(pathname), "%stitles.txt", Settings.titlestxt_path); - //cfg_parsefile(pathname, &title_set); - + return menu; } diff --git a/source/menu/menu_disclist.cpp b/source/menu/menu_disclist.cpp index 58b6c203..dce03421 100644 --- a/source/menu/menu_disclist.cpp +++ b/source/menu/menu_disclist.cpp @@ -9,6 +9,7 @@ #include "prompts/DiscBrowser.h" #include "settings/Settings.h" #include "wpad.h" +#include "sys.h" #include "libwiigui/gui_gamebrowser.h" #include "libwiigui/gui_gamegrid.h" @@ -574,6 +575,8 @@ int MenuDiscList() { } ResumeGui(); + +// ShowMemInfo(); while (menu == MENU_NONE) { diff --git a/source/mload/dip_plugin.c b/source/mload/dip_plugin.c index 746388b0..b154cf50 100644 --- a/source/mload/dip_plugin.c +++ b/source/mload/dip_plugin.c @@ -1,117 +1,122 @@ -#define size_dip_plugin 3080 +#define size_dip_plugin 3224 -unsigned char dip_plugin[3080] __attribute__((aligned (32)))={ +unsigned char dip_plugin[3224] __attribute__((aligned (32)))={ 19, 119, 228, 85, 18, 52, 0, 1, 32, 34, 205, 172, 32, 32, 13, 57, 32, 32, 8, 197, 32, 32, 8, 153, 32, 32, 91, 129, 32, - 32, 0, 73, 32, 32, 40, 117, 32, 32, 54, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 19, 119, 233, 53, 70, 192, 70, 192, 70, 192, 70, 192, 70, 192, 70, 192, 70, 192, 71, 120, 234, 0, 2, 7, 70, 192, 71, - 120, 234, 0, 2, 23, 70, 192, 71, 120, 234, 0, 2, 39, 70, 192, 71, 120, 234, 0, 2, 31, 70, 192, 71, 120, 234, 0, 2, 5, - 70, 192, 71, 120, 234, 0, 1, 247, 70, 192, 71, 120, 234, 0, 2, 29, 70, 192, 71, 120, 234, 0, 2, 25, 70, 192, 71, 120, - 234, 0, 2, 33, 181, 0, 75, 7, 176, 137, 147, 0, 70, 104, 35, 0, 33, 0, 34, 0, 147, 1, 147, 2, 240, 0, 252, 85, 176, - 9, 188, 2, 71, 8, 70, 192, 227, 0, 0, 0, 181, 16, 28, 3, 34, 32, 28, 12, 72, 13, 28, 25, 247, 255, 255, 210, 33, 32, - 72, 11, 247, 255, 255, 202, 73, 10, 34, 1, 104, 11, 66, 19, 209, 253, 34, 32, 28, 32, 73, 6, 247, 255, 255, 196, 28, - 32, 33, 32, 247, 255, 255, 188, 75, 4, 104, 24, 188, 16, 188, 2, 71, 8, 70, 192, 13, 0, 96, 0, 13, 0, 96, 28, 13, 0, - 96, 32, 181, 240, 70, 95, 70, 86, 70, 77, 70, 68, 180, 240, 10, 203, 70, 154, 75, 20, 176, 137, 37, 0, 28, 7, 28, 14, - 70, 145, 70, 108, 70, 155, 70, 168, 70, 91, 96, 35, 70, 67, 96, 99, 96, 163, 70, 83, 96, 227, 28, 56, 70, 75, 28, 49, - 97, 35, 247, 255, 255, 162, 70, 104, 28, 57, 28, 50, 240, 0, 252, 6, 53, 1, 40, 0, 209, 8, 176, 9, 188, 60, 70, 144, - 70, 153, 70, 162, 70, 171, 188, 240, 188, 2, 71, 8, 45, 15, 217, 223, 231, 243, 208, 0, 0, 0, 181, 112, 176, 136, 75, - 8, 28, 6, 28, 13, 70, 108, 147, 0, 96, 97, 146, 2, 247, 255, 255, 129, 28, 49, 28, 42, 70, 104, 240, 0, 251, 229, 176, - 8, 188, 112, 188, 2, 71, 8, 168, 0, 0, 0, 181, 240, 70, 95, 70, 86, 70, 77, 70, 68, 180, 240, 176, 143, 144, 4, 145, - 3, 146, 2, 41, 0, 209, 0, 224, 126, 10, 82, 70, 145, 35, 128, 34, 0, 1, 27, 70, 147, 34, 255, 147, 1, 3, 210, 171, 6, - 146, 0, 70, 152, 224, 7, 154, 3, 68, 179, 69, 90, 217, 62, 154, 5, 24, 179, 10, 219, 68, 153, 154, 3, 70, 91, 26, 214, - 70, 74, 2, 83, 154, 2, 66, 154, 216, 87, 35, 0, 147, 5, 36, 0, 159, 4, 34, 128, 68, 95, 28, 56, 28, 49, 1, 18, 240, - 0, 248, 103, 44, 0, 209, 47, 40, 0, 208, 45, 155, 0, 28, 6, 66, 152, 217, 1, 38, 255, 3, 246, 10, 242, 70, 146, 36, - 0, 75, 43, 70, 66, 96, 19, 35, 0, 96, 83, 96, 147, 70, 83, 96, 211, 70, 75, 97, 19, 28, 56, 28, 49, 247, 255, 255, 38, - 70, 64, 28, 57, 28, 50, 240, 0, 251, 138, 52, 1, 28, 5, 40, 0, 208, 192, 44, 15, 217, 230, 45, 0, 208, 188, 176, 15, - 28, 40, 188, 60, 70, 144, 70, 153, 70, 162, 70, 171, 188, 240, 188, 2, 71, 8, 154, 5, 25, 147, 154, 1, 66, 147, 217, - 1, 155, 5, 26, 214, 32, 128, 1, 0, 33, 32, 247, 255, 254, 251, 28, 4, 40, 0, 208, 18, 33, 128, 1, 9, 70, 74, 247, 255, - 255, 59, 28, 5, 40, 0, 208, 15, 28, 32, 247, 255, 254, 217, 231, 216, 26, 211, 0, 155, 28, 28, 147, 5, 30, 99, 65, 156, - 231, 163, 37, 1, 66, 109, 231, 208, 37, 0, 231, 206, 154, 5, 28, 56, 24, 161, 28, 50, 247, 255, 254, 214, 28, 56, 28, - 49, 247, 255, 254, 206, 231, 228, 70, 192, 208, 0, 0, 0, 181, 48, 28, 13, 28, 20, 6, 195, 209, 18, 75, 15, 66, 152, - 217, 19, 33, 0, 75, 14, 24, 194, 75, 14, 66, 154, 216, 1, 75, 13, 26, 25, 66, 161, 211, 5, 28, 8, 66, 169, 216, 10, - 30, 99, 67, 152, 224, 0, 32, 0, 188, 48, 188, 2, 71, 8, 35, 192, 4, 91, 26, 25, 231, 232, 28, 40, 30, 99, 67, 152, 231, - 244, 1, 127, 255, 255, 240, 0, 0, 0, 3, 97, 127, 255, 19, 97, 128, 0, 71, 112, 70, 192, 181, 48, 28, 4, 72, 15, 28, - 13, 104, 131, 104, 193, 176, 129, 24, 91, 24, 154, 105, 3, 43, 0, 209, 15, 104, 67, 43, 0, 209, 7, 28, 32, 28, 41, 247, - 255, 255, 13, 176, 1, 188, 48, 188, 2, 71, 8, 28, 32, 28, 41, 247, 255, 255, 27, 231, 246, 28, 32, 28, 41, 240, 0, 249, - 220, 231, 241, 70, 192, 19, 119, 240, 0, 181, 240, 79, 27, 35, 1, 28, 4, 96, 59, 176, 129, 33, 32, 247, 255, 254, 127, - 104, 227, 43, 8, 208, 8, 28, 32, 240, 0, 250, 214, 35, 0, 96, 59, 176, 1, 188, 240, 188, 2, 71, 8, 105, 163, 37, 197, - 104, 24, 104, 89, 247, 255, 254, 109, 105, 163, 1, 173, 104, 27, 28, 40, 33, 8, 104, 92, 104, 30, 247, 255, 254, 100, - 75, 10, 4, 36, 64, 30, 67, 52, 78, 9, 96, 44, 28, 40, 33, 4, 96, 52, 247, 255, 254, 73, 28, 48, 33, 4, 247, 255, 254, - 69, 35, 0, 96, 59, 32, 0, 231, 217, 70, 192, 19, 119, 240, 40, 0, 0, 255, 255, 0, 0, 49, 136, 181, 16, 73, 20, 28, 4, - 104, 139, 104, 202, 24, 154, 105, 11, 43, 0, 209, 18, 104, 75, 43, 0, 209, 11, 33, 32, 247, 255, 254, 175, 40, 0, 219, - 3, 105, 162, 75, 12, 66, 154, 208, 10, 188, 16, 188, 2, 71, 8, 33, 32, 247, 255, 254, 185, 231, 242, 33, 32, 240, 0, - 249, 123, 231, 238, 75, 6, 34, 1, 112, 26, 120, 91, 43, 0, 209, 238, 247, 255, 254, 10, 231, 235, 70, 192, 19, 119, - 240, 0, 93, 28, 158, 163, 32, 34, 205, 172, 181, 240, 70, 87, 70, 70, 180, 192, 28, 4, 120, 0, 176, 129, 28, 14, 28, - 23, 40, 224, 208, 24, 77, 133, 35, 0, 98, 43, 28, 3, 59, 112, 43, 143, 217, 13, 28, 32, 28, 49, 28, 58, 240, 0, 250, - 108, 28, 4, 176, 1, 28, 32, 188, 12, 70, 144, 70, 154, 188, 240, 188, 2, 71, 8, 74, 123, 0, 155, 88, 211, 70, 159, 77, - 120, 106, 43, 43, 0, 209, 2, 105, 43, 43, 0, 208, 230, 28, 48, 33, 0, 28, 58, 240, 0, 248, 240, 106, 43, 224, 143, 104, - 107, 43, 0, 209, 2, 105, 43, 43, 0, 208, 217, 36, 0, 231, 221, 35, 1, 34, 37, 84, 171, 104, 43, 43, 0, 208, 0, 224, - 173, 28, 32, 28, 58, 240, 0, 250, 62, 28, 4, 34, 0, 35, 37, 84, 234, 44, 0, 209, 204, 224, 46, 104, 235, 104, 169, 70, - 154, 105, 43, 70, 136, 43, 0, 209, 0, 224, 176, 32, 0, 34, 0, 70, 67, 70, 81, 67, 11, 209, 2, 42, 0, 209, 0, 224, 176, - 28, 3, 30, 90, 65, 147, 96, 107, 28, 48, 28, 57, 247, 255, 255, 107, 28, 4, 231, 176, 105, 43, 43, 0, 208, 167, 35, - 2, 224, 85, 104, 97, 104, 162, 40, 208, 209, 0, 224, 144, 28, 48, 247, 255, 254, 250, 28, 4, 40, 0, 209, 160, 35, 37, - 92, 235, 43, 0, 209, 156, 28, 48, 28, 57, 247, 255, 254, 237, 231, 151, 35, 36, 92, 234, 42, 0, 209, 179, 105, 41, 84, - 234, 35, 37, 96, 42, 96, 106, 96, 170, 96, 234, 98, 42, 84, 234, 70, 136, 41, 0, 209, 0, 231, 128, 105, 108, 247, 255, - 253, 142, 70, 64, 28, 41, 28, 34, 56, 1, 49, 24, 240, 0, 248, 249, 28, 4, 231, 122, 104, 107, 43, 0, 208, 93, 75, 60, - 36, 160, 98, 43, 2, 36, 231, 114, 104, 107, 43, 0, 209, 3, 105, 43, 43, 0, 209, 0, 231, 101, 104, 99, 104, 162, 7, 155, - 67, 19, 74, 53, 36, 0, 64, 19, 96, 235, 231, 98, 104, 99, 72, 51, 64, 24, 247, 255, 253, 122, 28, 4, 231, 91, 104, 98, - 35, 36, 84, 234, 36, 0, 231, 86, 105, 43, 96, 51, 28, 48, 28, 57, 247, 255, 253, 67, 36, 0, 231, 78, 104, 99, 97, 43, - 43, 0, 209, 40, 105, 99, 36, 0, 97, 107, 231, 70, 104, 43, 231, 238, 104, 99, 36, 0, 96, 43, 231, 64, 104, 171, 231, - 232, 104, 99, 36, 0, 96, 171, 231, 58, 104, 107, 43, 0, 209, 3, 105, 43, 43, 0, 209, 0, 231, 45, 28, 48, 33, 0, 28, - 58, 240, 0, 248, 55, 28, 48, 28, 57, 247, 255, 253, 29, 36, 0, 231, 40, 104, 97, 104, 162, 28, 48, 247, 255, 254, 122, - 28, 4, 231, 79, 28, 40, 28, 33, 49, 8, 34, 6, 48, 24, 247, 255, 253, 17, 231, 206, 105, 43, 43, 0, 209, 158, 231, 15, - 2, 201, 2, 82, 231, 107, 28, 58, 28, 32, 28, 49, 240, 0, 249, 121, 28, 2, 30, 83, 65, 154, 231, 71, 105, 43, 43, 0, - 208, 0, 231, 74, 96, 106, 36, 0, 231, 2, 70, 192, 19, 119, 240, 0, 19, 119, 233, 144, 0, 5, 49, 0, 255, 255, 128, 0, - 127, 255, 255, 255, 181, 240, 70, 87, 70, 78, 70, 69, 180, 224, 70, 128, 28, 14, 70, 148, 42, 0, 208, 51, 33, 3, 28, - 2, 64, 10, 35, 4, 26, 155, 28, 24, 64, 8, 69, 96, 216, 49, 40, 0, 208, 49, 36, 0, 70, 67, 85, 30, 52, 1, 66, 160, 216, - 250, 69, 132, 208, 32, 70, 99, 26, 27, 8, 159, 70, 153, 0, 187, 70, 154, 43, 0, 208, 17, 4, 51, 6, 50, 2, 49, 67, 26, - 67, 10, 28, 21, 70, 67, 67, 53, 24, 26, 33, 0, 0, 139, 49, 1, 80, 213, 66, 185, 211, 250, 68, 84, 69, 209, 208, 6, 70, - 67, 25, 24, 52, 1, 112, 6, 48, 1, 69, 164, 216, 250, 188, 28, 70, 144, 70, 153, 70, 162, 188, 240, 188, 1, 71, 0, 70, - 96, 231, 203, 36, 0, 231, 211, 70, 192, 181, 112, 76, 17, 28, 6, 104, 32, 176, 130, 28, 13, 98, 2, 97, 6, 100, 1, 97, - 65, 33, 68, 247, 255, 252, 147, 28, 48, 28, 41, 247, 255, 252, 143, 75, 10, 34, 2, 104, 24, 104, 35, 73, 9, 147, 0, - 35, 1, 247, 255, 252, 154, 28, 41, 28, 4, 28, 48, 247, 255, 252, 145, 176, 2, 28, 32, 188, 112, 188, 2, 71, 8, 19, 119, - 240, 44, 19, 119, 240, 48, 87, 70, 83, 2, 181, 240, 176, 133, 28, 4, 28, 15, 146, 3, 40, 2, 216, 75, 77, 47, 104, 43, - 43, 0, 208, 74, 78, 46, 104, 48, 40, 0, 219, 1, 247, 255, 252, 97, 74, 44, 0, 163, 88, 152, 33, 1, 247, 255, 252, 107, - 96, 48, 40, 0, 219, 66, 104, 48, 40, 0, 219, 49, 104, 40, 28, 57, 34, 6, 48, 32, 247, 255, 252, 87, 104, 40, 169, 3, - 34, 4, 48, 64, 247, 255, 252, 81, 104, 42, 36, 4, 28, 19, 51, 32, 96, 19, 104, 42, 35, 6, 96, 83, 104, 42, 33, 68, 28, - 19, 51, 64, 96, 147, 104, 43, 96, 220, 104, 40, 247, 255, 252, 59, 104, 43, 104, 48, 34, 2, 147, 0, 73, 22, 35, 0, 247, - 255, 252, 71, 104, 42, 28, 19, 51, 32, 96, 19, 104, 43, 96, 92, 104, 42, 28, 19, 51, 64, 96, 147, 104, 43, 96, 220, - 176, 5, 188, 240, 188, 2, 71, 8, 32, 1, 66, 64, 231, 248, 75, 11, 78, 8, 96, 43, 35, 1, 66, 91, 96, 51, 231, 179, 44, - 0, 209, 186, 72, 8, 33, 1, 247, 255, 252, 31, 96, 48, 231, 180, 70, 192, 19, 119, 240, 44, 19, 119, 240, 48, 19, 119, - 235, 252, 87, 70, 83, 1, 19, 119, 240, 64, 19, 119, 235, 208, 233, 45, 64, 128, 229, 159, 113, 0, 229, 151, 112, 0, - 235, 0, 0, 45, 232, 189, 64, 128, 225, 47, 255, 30, 233, 45, 64, 128, 229, 159, 112, 236, 229, 151, 112, 0, 235, 0, - 0, 39, 232, 189, 64, 128, 225, 47, 255, 30, 233, 45, 64, 128, 229, 159, 112, 216, 229, 151, 112, 0, 235, 0, 0, 33, 232, - 189, 64, 128, 225, 47, 255, 30, 233, 45, 64, 128, 229, 159, 112, 196, 229, 151, 112, 0, 235, 0, 0, 27, 232, 189, 64, - 128, 225, 47, 255, 30, 233, 45, 64, 128, 229, 159, 112, 176, 229, 151, 112, 0, 235, 0, 0, 21, 232, 189, 64, 128, 225, - 47, 255, 30, 233, 45, 64, 128, 229, 159, 112, 156, 229, 151, 112, 0, 235, 0, 0, 15, 232, 189, 64, 128, 225, 47, 255, - 30, 230, 0, 8, 16, 225, 47, 255, 30, 230, 0, 7, 240, 225, 47, 255, 30, 230, 0, 3, 144, 225, 47, 255, 30, 230, 0, 3, - 176, 225, 47, 255, 30, 230, 0, 3, 208, 225, 47, 255, 30, 230, 0, 3, 240, 225, 47, 255, 30, 230, 0, 4, 80, 225, 47, 255, - 30, 225, 47, 255, 23, 239, 0, 0, 204, 225, 47, 255, 30, 180, 124, 181, 0, 247, 255, 253, 30, 188, 2, 188, 124, 71, 8, - 181, 112, 176, 136, 104, 133, 28, 1, 75, 14, 71, 24, 70, 192, 70, 114, 28, 1, 32, 4, 223, 171, 71, 16, 181, 240, 70, - 95, 70, 86, 70, 77, 70, 68, 180, 240, 75, 8, 104, 27, 71, 24, 19, 119, 224, 16, 19, 119, 224, 20, 19, 119, 224, 24, - 19, 119, 224, 28, 19, 119, 224, 32, 19, 119, 224, 36, 32, 16, 0, 213, 19, 119, 224, 12, 70, 192, 70, 192, 19, 119, 228, - 236, 19, 119, 228, 200, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, - 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 190, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, + 32, 0, 73, 32, 32, 40, 117, 32, 32, 54, 93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 119, 233, + 160, 19, 119, 233, 117, 70, 192, 70, 192, 70, 192, 70, 192, 70, 192, 70, 192, 70, 192, 71, 120, 234, 0, 2, 23, 70, 192, + 71, 120, 234, 0, 2, 39, 70, 192, 71, 120, 234, 0, 2, 55, 70, 192, 71, 120, 234, 0, 2, 47, 70, 192, 71, 120, 234, 0, + 2, 21, 70, 192, 71, 120, 234, 0, 2, 7, 70, 192, 71, 120, 234, 0, 2, 45, 70, 192, 71, 120, 234, 0, 2, 41, 70, 192, 71, + 120, 234, 0, 2, 49, 181, 0, 75, 7, 176, 137, 147, 0, 70, 104, 35, 0, 33, 0, 34, 0, 147, 1, 147, 2, 240, 0, 252, 157, + 176, 9, 188, 2, 71, 8, 70, 192, 227, 0, 0, 0, 181, 16, 28, 3, 34, 32, 28, 12, 72, 13, 28, 25, 247, 255, 255, 210, 33, + 32, 72, 11, 247, 255, 255, 202, 73, 10, 34, 1, 104, 11, 66, 19, 209, 253, 34, 32, 28, 32, 73, 6, 247, 255, 255, 196, + 28, 32, 33, 32, 247, 255, 255, 188, 75, 4, 104, 24, 188, 16, 188, 2, 71, 8, 70, 192, 13, 0, 96, 0, 13, 0, 96, 28, 13, + 0, 96, 32, 181, 240, 70, 95, 70, 86, 70, 77, 70, 68, 180, 240, 10, 203, 70, 154, 75, 20, 176, 137, 37, 0, 28, 7, 28, + 14, 70, 145, 70, 108, 70, 155, 70, 168, 70, 91, 96, 35, 70, 67, 96, 99, 96, 163, 70, 83, 96, 227, 28, 56, 70, 75, 28, + 49, 97, 35, 247, 255, 255, 162, 70, 104, 28, 57, 28, 50, 240, 0, 252, 78, 53, 1, 40, 0, 209, 8, 176, 9, 188, 60, 70, + 144, 70, 153, 70, 162, 70, 171, 188, 240, 188, 2, 71, 8, 45, 15, 217, 223, 231, 243, 208, 0, 0, 0, 181, 112, 176, 136, + 75, 8, 28, 6, 28, 13, 70, 108, 147, 0, 96, 97, 146, 2, 247, 255, 255, 129, 28, 49, 28, 42, 70, 104, 240, 0, 252, 45, + 176, 8, 188, 112, 188, 2, 71, 8, 168, 0, 0, 0, 181, 240, 70, 95, 70, 86, 70, 77, 70, 68, 180, 240, 176, 143, 144, 4, + 145, 3, 146, 2, 41, 0, 209, 0, 224, 126, 10, 82, 70, 145, 35, 128, 34, 0, 1, 27, 70, 147, 34, 255, 147, 1, 3, 210, 171, + 6, 146, 0, 70, 152, 224, 7, 154, 3, 68, 179, 69, 90, 217, 62, 154, 5, 24, 179, 10, 219, 68, 153, 154, 3, 70, 91, 26, + 214, 70, 74, 2, 83, 154, 2, 66, 154, 216, 87, 35, 0, 147, 5, 36, 0, 159, 4, 34, 128, 68, 95, 28, 56, 28, 49, 1, 18, + 240, 0, 248, 103, 44, 0, 209, 47, 40, 0, 208, 45, 155, 0, 28, 6, 66, 152, 217, 1, 38, 255, 3, 246, 10, 242, 70, 146, + 36, 0, 75, 43, 70, 66, 96, 19, 35, 0, 96, 83, 96, 147, 70, 83, 96, 211, 70, 75, 97, 19, 28, 56, 28, 49, 247, 255, 255, + 38, 70, 64, 28, 57, 28, 50, 240, 0, 251, 210, 52, 1, 28, 5, 40, 0, 208, 192, 44, 15, 217, 230, 45, 0, 208, 188, 176, + 15, 28, 40, 188, 60, 70, 144, 70, 153, 70, 162, 70, 171, 188, 240, 188, 2, 71, 8, 154, 5, 25, 147, 154, 1, 66, 147, + 217, 1, 155, 5, 26, 214, 32, 128, 1, 0, 33, 32, 247, 255, 254, 251, 28, 4, 40, 0, 208, 18, 33, 128, 1, 9, 70, 74, 247, + 255, 255, 59, 28, 5, 40, 0, 208, 15, 28, 32, 247, 255, 254, 217, 231, 216, 26, 211, 0, 155, 28, 28, 147, 5, 30, 99, + 65, 156, 231, 163, 37, 1, 66, 109, 231, 208, 37, 0, 231, 206, 154, 5, 28, 56, 24, 161, 28, 50, 247, 255, 254, 214, 28, + 56, 28, 49, 247, 255, 254, 206, 231, 228, 70, 192, 208, 0, 0, 0, 181, 48, 28, 13, 28, 20, 6, 195, 209, 18, 75, 15, 66, + 152, 217, 19, 33, 0, 75, 14, 24, 194, 75, 14, 66, 154, 216, 1, 75, 13, 26, 25, 66, 161, 211, 5, 28, 8, 66, 169, 216, + 10, 30, 99, 67, 152, 224, 0, 32, 0, 188, 48, 188, 2, 71, 8, 35, 192, 4, 91, 26, 25, 231, 232, 28, 40, 30, 99, 67, 152, + 231, 244, 1, 127, 255, 255, 240, 0, 0, 0, 3, 97, 127, 255, 19, 97, 128, 0, 71, 112, 70, 192, 181, 48, 28, 4, 72, 15, + 28, 13, 104, 131, 104, 193, 176, 129, 24, 91, 24, 154, 105, 3, 43, 0, 209, 15, 104, 67, 43, 0, 209, 7, 28, 32, 28, 41, + 247, 255, 255, 13, 176, 1, 188, 48, 188, 2, 71, 8, 28, 32, 28, 41, 247, 255, 255, 27, 231, 246, 28, 32, 28, 41, 240, + 0, 249, 246, 231, 241, 70, 192, 19, 119, 240, 0, 181, 240, 79, 27, 35, 1, 28, 4, 96, 59, 176, 129, 33, 32, 247, 255, + 254, 127, 104, 227, 43, 8, 208, 8, 28, 32, 240, 0, 250, 246, 35, 0, 96, 59, 176, 1, 188, 240, 188, 2, 71, 8, 105, 163, + 37, 197, 104, 24, 104, 89, 247, 255, 254, 109, 105, 163, 1, 173, 104, 27, 28, 40, 33, 8, 104, 92, 104, 30, 247, 255, + 254, 100, 75, 10, 4, 36, 64, 30, 67, 52, 78, 9, 96, 44, 28, 40, 33, 4, 96, 52, 247, 255, 254, 73, 28, 48, 33, 4, 247, + 255, 254, 69, 35, 0, 96, 59, 32, 0, 231, 217, 70, 192, 19, 119, 240, 40, 0, 0, 255, 255, 0, 0, 49, 136, 181, 16, 73, + 20, 28, 4, 104, 139, 104, 202, 24, 154, 105, 11, 43, 0, 209, 18, 104, 75, 43, 0, 209, 11, 33, 32, 247, 255, 254, 175, + 40, 0, 219, 3, 105, 162, 75, 12, 66, 154, 208, 10, 188, 16, 188, 2, 71, 8, 33, 32, 247, 255, 254, 185, 231, 242, 33, + 32, 240, 0, 249, 149, 231, 238, 75, 6, 34, 1, 112, 26, 120, 91, 43, 0, 209, 238, 247, 255, 254, 10, 231, 235, 70, 192, + 19, 119, 240, 0, 93, 28, 158, 163, 32, 34, 205, 172, 181, 240, 70, 87, 70, 70, 180, 192, 28, 6, 120, 0, 176, 129, 28, + 15, 70, 144, 40, 224, 208, 24, 77, 145, 35, 0, 98, 43, 28, 3, 59, 112, 43, 143, 217, 13, 28, 48, 28, 57, 70, 66, 240, + 0, 250, 180, 28, 4, 176, 1, 28, 32, 188, 12, 70, 144, 70, 154, 188, 240, 188, 2, 71, 8, 74, 135, 0, 155, 88, 211, 70, + 159, 77, 132, 106, 43, 43, 0, 209, 2, 105, 43, 43, 0, 208, 230, 28, 56, 33, 0, 70, 66, 240, 0, 249, 10, 106, 43, 96, + 59, 28, 56, 70, 65, 247, 255, 253, 212, 36, 0, 231, 223, 104, 107, 43, 0, 209, 2, 105, 43, 43, 0, 208, 211, 36, 0, 231, + 215, 104, 107, 43, 0, 209, 2, 105, 43, 43, 0, 208, 203, 28, 56, 33, 0, 70, 66, 240, 0, 248, 239, 28, 56, 70, 65, 247, + 255, 253, 187, 36, 0, 231, 198, 104, 115, 36, 0, 96, 171, 231, 194, 104, 171, 231, 217, 104, 115, 36, 0, 96, 43, 231, + 188, 104, 43, 231, 211, 104, 115, 97, 43, 43, 0, 208, 6, 28, 40, 28, 49, 48, 24, 49, 8, 34, 6, 247, 255, 253, 166, 105, + 115, 36, 0, 97, 107, 231, 171, 105, 43, 231, 194, 104, 114, 35, 36, 84, 234, 36, 0, 231, 164, 104, 235, 104, 172, 70, + 154, 105, 43, 43, 0, 209, 0, 224, 170, 32, 0, 34, 0, 70, 83, 67, 35, 209, 2, 42, 0, 209, 0, 224, 142, 28, 3, 30, 90, + 65, 147, 96, 107, 28, 56, 70, 65, 247, 255, 255, 70, 28, 4, 231, 139, 35, 1, 34, 37, 84, 171, 104, 43, 43, 0, 209, 0, + 224, 132, 104, 113, 104, 178, 28, 56, 247, 255, 254, 214, 28, 4, 34, 0, 35, 37, 84, 234, 44, 0, 208, 0, 231, 120, 28, + 56, 70, 65, 247, 255, 254, 201, 231, 115, 104, 115, 72, 70, 64, 24, 247, 255, 253, 139, 28, 4, 231, 108, 76, 68, 33, + 64, 28, 32, 247, 255, 253, 106, 34, 0, 28, 33, 224, 3, 50, 1, 42, 64, 209, 0, 231, 90, 92, 163, 43, 0, 208, 248, 34, + 64, 28, 56, 247, 255, 253, 80, 28, 56, 33, 64, 247, 255, 253, 72, 36, 0, 231, 83, 35, 36, 92, 234, 42, 0, 208, 0, 231, + 116, 84, 234, 35, 37, 84, 234, 105, 43, 96, 42, 96, 106, 96, 170, 96, 234, 98, 42, 70, 154, 43, 0, 209, 0, 231, 59, + 105, 108, 247, 255, 253, 73, 70, 80, 28, 41, 28, 34, 56, 1, 49, 24, 240, 0, 248, 206, 28, 4, 231, 53, 104, 113, 104, + 178, 40, 208, 208, 59, 28, 56, 247, 255, 254, 133, 28, 4, 40, 0, 208, 0, 231, 42, 35, 37, 92, 235, 43, 0, 208, 0, 231, + 37, 231, 171, 105, 43, 43, 0, 209, 0, 231, 26, 35, 2, 231, 55, 104, 107, 43, 0, 209, 3, 105, 43, 43, 0, 209, 0, 231, + 17, 104, 115, 104, 178, 7, 155, 67, 19, 74, 24, 36, 0, 64, 19, 96, 235, 231, 14, 104, 107, 43, 0, 208, 17, 75, 21, 36, + 160, 98, 43, 2, 36, 231, 6, 105, 43, 43, 0, 208, 0, 231, 108, 96, 106, 36, 0, 230, 255, 28, 48, 70, 66, 240, 0, 249, + 175, 28, 4, 231, 122, 105, 43, 43, 0, 209, 234, 230, 239, 2, 201, 2, 82, 231, 192, 70, 66, 28, 48, 240, 0, 249, 162, + 28, 2, 30, 83, 65, 154, 231, 78, 70, 192, 19, 119, 240, 0, 19, 119, 234, 32, 127, 255, 255, 255, 19, 119, 233, 160, + 255, 255, 128, 0, 0, 5, 49, 0, 181, 240, 70, 87, 70, 78, 70, 69, 180, 224, 70, 128, 28, 14, 70, 148, 42, 0, 208, 51, + 33, 3, 28, 2, 64, 10, 35, 4, 26, 155, 28, 24, 64, 8, 69, 96, 216, 49, 40, 0, 208, 49, 36, 0, 70, 67, 85, 30, 52, 1, + 66, 160, 216, 250, 69, 132, 208, 32, 70, 99, 26, 27, 8, 159, 70, 153, 0, 187, 70, 154, 43, 0, 208, 17, 4, 51, 6, 50, + 2, 49, 67, 26, 67, 10, 28, 21, 70, 67, 67, 53, 24, 26, 33, 0, 0, 139, 49, 1, 80, 213, 66, 185, 211, 250, 68, 84, 69, + 209, 208, 6, 70, 67, 25, 24, 52, 1, 112, 6, 48, 1, 69, 164, 216, 250, 188, 28, 70, 144, 70, 153, 70, 162, 188, 240, + 188, 1, 71, 0, 70, 96, 231, 203, 36, 0, 231, 211, 70, 192, 181, 112, 76, 17, 28, 6, 104, 32, 176, 130, 28, 13, 98, 2, + 97, 6, 100, 1, 97, 65, 33, 68, 247, 255, 252, 121, 28, 48, 28, 41, 247, 255, 252, 117, 75, 10, 34, 2, 104, 24, 104, + 35, 73, 9, 147, 0, 35, 1, 247, 255, 252, 128, 28, 41, 28, 4, 28, 48, 247, 255, 252, 119, 176, 2, 28, 32, 188, 112, 188, + 2, 71, 8, 19, 119, 240, 44, 19, 119, 240, 48, 87, 70, 83, 2, 181, 240, 176, 133, 28, 4, 28, 15, 146, 3, 40, 2, 216, + 75, 77, 47, 104, 43, 43, 0, 208, 74, 78, 46, 104, 48, 40, 0, 219, 1, 247, 255, 252, 71, 74, 44, 0, 163, 88, 152, 33, + 1, 247, 255, 252, 81, 96, 48, 40, 0, 219, 66, 104, 48, 40, 0, 219, 49, 104, 40, 28, 57, 34, 6, 48, 32, 247, 255, 252, + 61, 104, 40, 169, 3, 34, 4, 48, 64, 247, 255, 252, 55, 104, 42, 36, 4, 28, 19, 51, 32, 96, 19, 104, 42, 35, 6, 96, 83, + 104, 42, 33, 68, 28, 19, 51, 64, 96, 147, 104, 43, 96, 220, 104, 40, 247, 255, 252, 33, 104, 43, 104, 48, 34, 2, 147, + 0, 73, 22, 35, 0, 247, 255, 252, 45, 104, 42, 28, 19, 51, 32, 96, 19, 104, 43, 96, 92, 104, 42, 28, 19, 51, 64, 96, + 147, 104, 43, 96, 220, 176, 5, 188, 240, 188, 2, 71, 8, 32, 1, 66, 64, 231, 248, 75, 11, 78, 8, 96, 43, 35, 1, 66, 91, + 96, 51, 231, 179, 44, 0, 209, 186, 72, 8, 33, 1, 247, 255, 252, 5, 96, 48, 231, 180, 70, 192, 19, 119, 240, 44, 19, + 119, 240, 48, 19, 119, 236, 140, 87, 70, 83, 1, 19, 119, 240, 64, 19, 119, 236, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 233, 45, 64, 128, 229, 159, 113, 80, 229, 151, 112, 0, 235, 0, 0, 45, 232, 189, 64, 128, 225, 47, 255, 30, 233, 45, + 64, 128, 229, 159, 113, 60, 229, 151, 112, 0, 235, 0, 0, 39, 232, 189, 64, 128, 225, 47, 255, 30, 233, 45, 64, 128, + 229, 159, 113, 40, 229, 151, 112, 0, 235, 0, 0, 33, 232, 189, 64, 128, 225, 47, 255, 30, 233, 45, 64, 128, 229, 159, + 113, 20, 229, 151, 112, 0, 235, 0, 0, 27, 232, 189, 64, 128, 225, 47, 255, 30, 233, 45, 64, 128, 229, 159, 113, 0, 229, + 151, 112, 0, 235, 0, 0, 21, 232, 189, 64, 128, 225, 47, 255, 30, 233, 45, 64, 128, 229, 159, 112, 236, 229, 151, 112, + 0, 235, 0, 0, 15, 232, 189, 64, 128, 225, 47, 255, 30, 230, 0, 8, 16, 225, 47, 255, 30, 230, 0, 7, 240, 225, 47, 255, + 30, 230, 0, 3, 144, 225, 47, 255, 30, 230, 0, 3, 176, 225, 47, 255, 30, 230, 0, 3, 208, 225, 47, 255, 30, 230, 0, 3, + 240, 225, 47, 255, 30, 230, 0, 4, 80, 225, 47, 255, 30, 225, 47, 255, 23, 239, 0, 0, 204, 225, 47, 255, 30, 180, 124, + 181, 0, 247, 255, 252, 254, 188, 2, 188, 124, 71, 8, 181, 112, 176, 136, 104, 133, 28, 1, 75, 34, 71, 24, 70, 192, 70, + 192, 70, 192, 70, 192, 70, 192, 70, 192, 70, 192, 70, 192, 70, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 114, 28, 1, 32, 4, 223, 171, 71, 16, 181, 240, 70, 95, 70, 86, 70, 77, 70, 68, 180, 240, + 75, 8, 104, 27, 71, 24, 19, 119, 224, 16, 19, 119, 224, 20, 19, 119, 224, 24, 19, 119, 224, 28, 19, 119, 224, 32, 19, + 119, 224, 36, 32, 16, 0, 213, 19, 119, 224, 12, 70, 192, 70, 192, 19, 119, 229, 58, 19, 119, 229, 108, 19, 119, 228, + 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, + 19, 119, 228, 202, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, - 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 229, 34, 19, 119, 228, - 120, 19, 119, 229, 84, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 229, 44, 19, 119, 228, 120, 19, 119, 228, 120, + 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 230, 58, 19, 119, 228, 120, 19, 119, 229, 220, 19, 119, 228, + 120, 19, 119, 228, 120, 19, 119, 230, 24, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, - 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, - 120, 19, 119, 228, 120, 19, 119, 229, 142, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 229, 44, - 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 184, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, + 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 230, + 102, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 230, 24, 19, 119, 228, 120, 19, 119, 228, 120, + 19, 119, 228, 196, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, - 228, 120, 19, 119, 228, 120, 19, 119, 229, 44, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, - 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 229, 158, 19, 119, 228, 120, - 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 158, 19, - 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 230, 14, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, + 230, 24, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, + 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 230, 70, 19, 119, 229, 170, 19, 119, 228, 120, 19, 119, 228, 120, + 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 158, 19, 119, 228, 120, 19, 119, 228, 120, 19, + 119, 228, 120, 19, 119, 228, 212, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, - 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 230, 6, 19, 119, 230, 2, 19, 119, 229, 250, 19, 119, 229, 246, 19, - 119, 229, 230, 19, 119, 229, 214, 19, 119, 229, 204, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, - 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 229, 190, 47, 100, 101, - 118, 47, 117, 115, 98, 47, 101, 104, 99, 0, 0, 0, 0, 47, 100, 101, 118, 47, 117, 115, 98, 50, 0, 0, 0, 47, 100, 101, - 118, 47, 115, 100, 105, 111, 47, 115, 100, 104, 99, 0, 0, 19, 119, 235, 224, 19, 119, 235, 236, 19, 119, 235, 208 + 120, 19, 119, 228, 246, 19, 119, 228, 254, 19, 119, 229, 2, 19, 119, 229, 10, 19, 119, 229, 14, 19, 119, 229, 44, 19, + 119, 229, 48, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, + 228, 120, 19, 119, 228, 120, 19, 119, 228, 120, 19, 119, 229, 156, 47, 100, 101, 118, 47, 117, 115, 98, 47, 101, 104, + 99, 0, 0, 0, 0, 47, 100, 101, 118, 47, 117, 115, 98, 50, 0, 0, 0, 47, 100, 101, 118, 47, 115, 100, 105, 111, 47, 115, + 100, 104, 99, 0, 0, 19, 119, 236, 112, 19, 119, 236, 124, 19, 119, 236, 96 }; diff --git a/source/mload/dip_plugin.h b/source/mload/dip_plugin.h index 434dccf3..900078e7 100644 --- a/source/mload/dip_plugin.h +++ b/source/mload/dip_plugin.h @@ -1,3 +1,3 @@ -#define size_dip_plugin 3080 +#define size_dip_plugin 3224 -extern unsigned char dip_plugin[3080]; +extern unsigned char dip_plugin[3224]; diff --git a/source/mload/mload.c b/source/mload/mload.c index 93c9872d..a475ab30 100644 --- a/source/mload/mload.c +++ b/source/mload/mload.c @@ -612,7 +612,6 @@ return 0; int patch_cios_data() { - patch_datas[0]=*((u32 *) (dip_plugin+16*4)); mload_set_ES_ioctlv_vector((void *) patch_datas[0]); return 1; diff --git a/source/patches/fst.c b/source/patches/fst.c index 56b610c8..5724f112 100644 --- a/source/patches/fst.c +++ b/source/patches/fst.c @@ -34,6 +34,9 @@ #include "dvd_broadway.h" #include "wpad.h" #include "fatmounter.h" +#include "sys.h" +#include "mload/mload.h" +#include "mload/dip_plugin.h" extern struct SSettings Settings; @@ -95,4 +98,61 @@ u32 do_sd_code(char *filename) return 1; } +u32 do_bca_code(u8 *gameid) +{ + if (IOS_GetVersion() == 222 || IOS_GetVersion() == 223) + { + FILE *fp; + u32 filesize; + char filepath[150]; + memset(filepath, 0, 150); + u8 bcaCode[64] ATTRIBUTE_ALIGN(32); + sprintf(filepath, "%s%6s", Settings.BcaCodepath, gameid); + filepath[strlen(Settings.BcaCodepath)+6] = '.'; + filepath[strlen(Settings.BcaCodepath)+7] = 'b'; + filepath[strlen(Settings.BcaCodepath)+8] = 'c'; + filepath[strlen(Settings.BcaCodepath)+9] = 'a'; + + fp = fopen(filepath, "rb"); + if (!fp) { + memset(filepath, 0, 150); + sprintf(filepath, "%s%3s", Settings.BcaCodepath, gameid + 1); + filepath[strlen(Settings.BcaCodepath)+3] = '.'; + filepath[strlen(Settings.BcaCodepath)+4] = 'b'; + filepath[strlen(Settings.BcaCodepath)+5] = 'c'; + filepath[strlen(Settings.BcaCodepath)+6] = 'a'; + fp = fopen(filepath, "rb"); + + if (!fp) { + // Set default bcaCode + memset(bcaCode, 0, 64); + bcaCode[0x33] = 1; + } + } + + if (fp) { + u32 ret = 0; + + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + + if (filesize == 64) { + fseek(fp, 0, SEEK_SET); + ret = fread(bcaCode, 1, 64, fp); + } + fclose(fp); + + if (ret != 64) { + // Set default bcaCode + memset(bcaCode, 0, 64); + bcaCode[0x33] = 1; + } + } + + mload_seek(*((u32 *) (dip_plugin+15*4)), SEEK_SET); // offset 15 (bca_data area) + mload_write(bcaCode, 64); + mload_close(); + } + return 0; +} diff --git a/source/patches/fst.h b/source/patches/fst.h index 7ef9ed28..254a6d2a 100644 --- a/source/patches/fst.h +++ b/source/patches/fst.h @@ -31,6 +31,7 @@ extern "C" //u32 do_fst(u32 fstlocation); u32 do_sd_code(char *filename); +u32 do_bca_code(u8 *gameid); #ifdef __cplusplus } diff --git a/source/patches/patchcode.c b/source/patches/patchcode.c index 3b075bee..20aa9c91 100644 --- a/source/patches/patchcode.c +++ b/source/patches/patchcode.c @@ -172,32 +172,6 @@ void dogamehooks(void *addr, u32 len) } break; -/* - case 2: - if(memcmp(addr_start, kpadhooks, sizeof(kpadhooks))==0){ - patchhook((u32)addr_start, len); - patched = 1; - } - - if(memcmp(addr_start, kpadoldhooks, sizeof(kpadoldhooks))==0){ - patchhook((u32)addr_start, len); - patched = 1; - } - break; - - case 3: - if(memcmp(addr_start, joypadhooks, sizeof(joypadhooks))==0){ - patchhook((u32)addr_start, len); - patched = 1; - } - break; - - case 4: - if(memcmp(addr_start, recoveryhooks, sizeof(recoveryhooks))==0){ - patchhook3((u32)addr_start, len); - } - break; -*/ case 2: if(memcmp(addr_start, viwiihooks, sizeof(viwiihooks))==0){ @@ -206,69 +180,6 @@ void dogamehooks(void *addr, u32 len) break; -/* - case 6: - // jap region free - if(memcmp(addr_start, regionfreehooks, sizeof(regionfreehooks))==0){ - regionfreejap((u32)addr_start, len); - } - - // usa region free - if(memcmp(addr_start, regionfreehooks, sizeof(regionfreehooks))==0){ - regionfreeusa((u32)addr_start, len); - } - - // pal region free - if(memcmp(addr_start, regionfreehooks, sizeof(regionfreehooks))==0){ - regionfreepal((u32)addr_start, len); - } - - // skip disc update - if(memcmp(addr_start, updatecheckhook, sizeof(updatecheckhook))==0){ - patchupdatecheck((u32)addr_start, len); - } - break; - - - case 7: - if(memcmp(addr_start, healthcheckhook, sizeof(healthcheckhook))==0){ - removehealthcheck((u32)addr_start, len); - } - break; - - // no copy flags - case 8: - // Remove the actual flag so can copy back - if(memcmp(addr_start, nocopyflag5, sizeof(nocopyflag5))==0){ - copyflagcheck5((u32)addr_start, len); - } - - - if(memcmp(addr_start, nocopyflag1, sizeof(nocopyflag1))==0){ - copyflagcheck1((u32)addr_start, len); - } - - if(memcmp(addr_start, nocopyflag2, sizeof(nocopyflag2))==0){ - copyflagcheck2((u32)addr_start, len); - } - - // no VC and GH3 save - if(memcmp(addr_start, nocopyflag3, sizeof(nocopyflag2))==0){ - copyflagcheck3((u32)addr_start, len); - } - // no VC and GH3 save display remove - if(memcmp(addr_start, nocopyflag4, sizeof(nocopyflag4))==0){ - copyflagcheck4((u32)addr_start, len); - } - - break; - - case 9: - if(memcmp(addr_start, movedvdpatch, sizeof(movedvdpatch))==0){ - movedvdhooks((u32)addr_start, len); - } - break; -*/ // multidol case 3: @@ -355,5 +266,3 @@ void vidolpatcher(void *addr, u32 len) addr_start += 4; } } - - diff --git a/source/prompts/PromptWindows.cpp b/source/prompts/PromptWindows.cpp index 6696545c..b1db8f91 100644 --- a/source/prompts/PromptWindows.cpp +++ b/source/prompts/PromptWindows.cpp @@ -181,7 +181,7 @@ void WindowCredits() { starImg.SetAlignment(ALIGN_LEFT, ALIGN_TOP); starImg.SetPosition(505,350); - int numEntries = 20; + int numEntries = 21; GuiText * txt[numEntries]; txt[i] = new GuiText(tr("Credits"), 26, (GXColor) {255, 255, 255, 255}); @@ -216,16 +216,16 @@ void WindowCredits() { i++; y+=26; - txt[i] = new GuiText(" http://code.google.com/p/usbloader-gui/", 20, (GXColor) {255, 255, 255, 255}); - txt[i]->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - txt[i]->SetPosition(50,y); - i++; //y+=28; - txt[i] = new GuiText(tr("Official Site:"), 20, (GXColor) {255, 255, 255, 255}); - txt[i]->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - txt[i]->SetPosition(-180,y); + txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); + txt[i]->SetPosition(10,y); i++; - y+=28; + + txt[i] = new GuiText("http://code.google.com/p/usbloader-gui/", 20, (GXColor) {255, 255, 255, 255}); + txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); + txt[i]->SetPosition(160,y); + i++; + y+=26; GuiText::SetPresets(22, (GXColor) {255, 255, 255, 255}, 0, GuiText::WRAP,FTGX_JUSTIFY_LEFT | FTGX_ALIGN_TOP, ALIGN_LEFT, ALIGN_TOP); @@ -244,7 +244,7 @@ void WindowCredits() { txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(160,y); i++; - y+=34; + y+=26; char text[100]; @@ -257,7 +257,7 @@ void WindowCredits() { txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(160,y); i++; - y+=34; + y+=26; txt[i] = new GuiText(tr("Big thanks to:")); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); @@ -290,7 +290,7 @@ void WindowCredits() { txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(160,y); i++; - y+=22; + y+=26; txt[i] = new GuiText(tr("Special thanks to:")); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); @@ -326,6 +326,13 @@ void WindowCredits() { i++; y+=22; + sprintf(text, "Oggzee %s", tr("for FAT support")); + txt[i] = new GuiText(text); + txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); + txt[i]->SetPosition(60,y); + i++; + y+=22; + for (i=0; i < numEntries; i++) creditsWindowBox.Append(txt[i]); diff --git a/source/settings/Settings.cpp b/source/settings/Settings.cpp index 17c7bd2f..3d065f89 100644 --- a/source/settings/Settings.cpp +++ b/source/settings/Settings.cpp @@ -46,12 +46,12 @@ static const char *opts_no_yes[settings_off_on_max] = {trNOOP("No"),trNOOP("Yes" static const char *opts_off_on[settings_off_on_max] = {trNOOP("OFF"),trNOOP("ON") }; static const char *opts_videomode[settings_language_max][2] = {{"",trNOOP("Disc Default")},{trNOOP("System Default"),""},{trNOOP("AutoPatch"),""},{trNOOP("Force"), " PAL50"},{trNOOP("Force")," PAL60"},{trNOOP("Force")," NTSC"}}; static const char *opts_language[settings_language_max] = {trNOOP("Console Default"),trNOOP("Japanese"),trNOOP("English"),trNOOP("German"),trNOOP("French"),trNOOP("Spanish"),trNOOP("Italian"),trNOOP("Dutch"),trNOOP("SChinese"),trNOOP("TChinese"),trNOOP("Korean")}; -static const char *opts_cios[settings_ios_max] = {"IOS 249","IOS 222", "IOS 223"}; +static const char *opts_cios[settings_ios_max] = {"IOS 249","IOS 222", "IOS 223", "IOS 250"}; static const char *opts_parentalcontrol[5] = {trNOOP("0 (Everyone)"),trNOOP("1 (Child 7+)"),trNOOP("2 (Teen 12+)"),trNOOP("3 (Mature 16+)"),trNOOP("4 (Adults Only 18+)")}; static const char *opts_error002[settings_error002_max] = {trNOOP("No"),trNOOP("Yes"),trNOOP("Anti")}; bool IsValidPartition(int fs_type, int cios) { - if (cios == 249) { + if (cios == 249 || cios == 250) { return fs_type == FS_TYPE_WBFS; } else { return fs_type == FS_TYPE_WBFS || fs_type == FS_TYPE_FAT32; @@ -992,7 +992,7 @@ int MenuSettings() if (++Settings.cios >= settings_cios_max) { Settings.cios = 0; } - if (Settings.cios != 0 && ios222rev!=4) { + if ((Settings.cios == 1 && ios222rev!=4) || (Settings.cios == 2 && ios223rev != 4)) { WindowPrompt(tr("Hermes CIOS"),tr("USB Loader GX will only run with Hermes CIOS rev 4! Please make sure you have revision 4 installed!"),tr("OK")); } } @@ -1022,6 +1022,15 @@ int MenuSettings() pInfo.fs_type == FS_TYPE_FAT32 ? pInfo.fat_i : pInfo.wbfs_i, partition_size); } + + if (ret == ++Idx || firstRun) + { + if (firstRun) options2.SetName(Idx, "%s", tr("FAT: Use directories")); + if (ret == Idx) { + Settings.FatInstallToDir = Settings.FatInstallToDir == 0 ? 1 : 0; + } + options2.SetValue(Idx, "%s", tr(opts_no_yes[Settings.FatInstallToDir])); + } if(ret == ++Idx || firstRun) { @@ -1824,6 +1833,34 @@ int MenuSettings() options2.SetValue(Idx, "%s", Settings.theme_downloadpath); } + if(ret == ++Idx || firstRun) + { + if(firstRun) options2.SetName(Idx, "%s", tr("BCA Codes Path")); + if(ret == Idx) + { + w.Remove(&optionBrowser2); + w.Remove(&backBtn); + char entered[100] = ""; + strlcpy(entered, Settings.BcaCodepath, sizeof(entered)); + titleTxt.SetText(tr("BCA Codes Path")); + int result = BrowseDevice(entered, sizeof(entered), FB_DEFAULT, noFILES); + titleTxt.SetText(tr("Custom Paths")); + w.Append(&optionBrowser2); + w.Append(&backBtn); + if ( result == 1 ) + { + int len = (strlen(entered)-1); + if (entered[len] !='/') + strncat (entered, "/", 1); + strlcpy(Settings.BcaCodepath, entered, sizeof(Settings.BcaCodepath)); + WindowPrompt(tr("BCA Codes Path changed"),0,tr("OK")); + if (!isInserted(bootDevice)) + WindowPrompt(tr("No SD-Card inserted!"),tr("Insert an SD-Card to save."),tr("OK")); + } + } + options2.SetValue(Idx, "%s", Settings.BcaCodepath); + } + firstRun = false; } } @@ -2330,8 +2367,12 @@ int GameSettings(struct discHdr * header) viChoice = Settings.vpatch; if (Settings.cios == ios222) iosChoice = i222; - else - iosChoice = i249; + else if (Settings.cios == 250) + iosChoice = i250; + else if (Settings.cios == ios223) + iosChoice = i223; + else + iosChoice = 249; parentalcontrolChoice = 0; fix002 = Settings.error002; countrystrings = Settings.patchcountrystrings; @@ -2847,6 +2888,10 @@ int GameSettings(struct discHdr * header) reloadblock = off; if (Settings.cios == ios222) iosChoice = i222; + else if (Settings.cios == ios250) + iosChoice = i250; + else if (Settings.cios == ios223) + iosChoice = i223; else iosChoice = i249; parentalcontrolChoice = 0; diff --git a/source/settings/cfg.c b/source/settings/cfg.c index 0ac5c8d0..5c4cc6db 100644 --- a/source/settings/cfg.c +++ b/source/settings/cfg.c @@ -201,6 +201,7 @@ void CFG_Default(int widescreen) { // -1 = non forced Mode snprintf(Settings.homebrewapps_path, sizeof(Settings.homebrewapps_path), "%s/apps/", bootDevice); snprintf(Settings.Cheatcodespath, sizeof(Settings.Cheatcodespath), "%s/codes/", bootDevice); snprintf(Settings.TxtCheatcodespath, sizeof(Settings.TxtCheatcodespath), "%s/txtcodes/", bootDevice); + snprintf(Settings.BcaCodepath, sizeof(Settings.BcaCodepath), "%s/bca/", bootDevice); snprintf(Settings.dolpath, sizeof(Settings.dolpath), "%s/", bootDevice); sprintf(Settings.ogg_path, "notset"); } @@ -355,6 +356,7 @@ void Global_Default(void) { Settings.screensaver = 3; Settings.partition = -1; Settings.marknewtitles = 1; + Settings.FatInstallToDir = 0; } @@ -532,6 +534,10 @@ void path_set(char *name, char *val) { strlcpy(Settings.ogg_path, val, sizeof(Settings.ogg_path)); return; } + if (strcmp(name, "BcaCodepath") == 0) { + strlcpy(Settings.BcaCodepath, val, sizeof(Settings.BcaCodepath)); + return; + } return; @@ -1049,6 +1055,11 @@ void global_cfg_set(char *name, char *val) { Settings.marknewtitles = i; } return; + } else if (strcmp(name, "fatInstallToDir") == 0) { + int i; + if (sscanf(val, "%d", &i) == 1) { + Settings.FatInstallToDir = i; + } } cfg_bool("godmode", &Settings.godmode); @@ -1283,6 +1294,7 @@ bool cfg_save_global() { // save global settings fprintf(f, "theme_downloadpath = %s\n ", Settings.theme_downloadpath); fprintf(f, "homebrewapps_path = %s\n ", Settings.homebrewapps_path); fprintf(f, "Cheatcodespath = %s\n ", Settings.Cheatcodespath); + fprintf(f, "BcaCodepath = %s\n", Settings.BcaCodepath); fprintf(f, "titlesOverride = %d\n ", Settings.titlesOverride); //fprintf(f, "db_url = %s\n ", Settings.db_url); //fprintf(f, "db_JPtoEN = %d\n ", Settings.db_JPtoEN); @@ -1294,6 +1306,7 @@ bool cfg_save_global() { // save global settings fprintf(f, "discart = %d\n ", Settings.discart); fprintf(f, "partition = %d\n", Settings.partition); fprintf(f, "marknewtitles = %d\n", Settings.marknewtitles); + fprintf(f, "fatInstallToDir = %d\n", Settings.FatInstallToDir); fclose(f); return true; } diff --git a/source/settings/cfg.h b/source/settings/cfg.h index 0f858448..67e9652d 100644 --- a/source/settings/cfg.h +++ b/source/settings/cfg.h @@ -298,12 +298,15 @@ extern "C" { i249=0, i222, i223, + i250, settings_ios_max // always the last entry }; enum { ios249=0, ios222, + ios223, + ios250, settings_cios_max // always the last entry }; @@ -422,6 +425,8 @@ extern "C" { u8 discart; short gamesound; u8 marknewtitles; + char BcaCodepath[100]; + u8 FatInstallToDir; }; extern struct SSettings Settings; diff --git a/source/settings/newtitles.cpp b/source/settings/newtitles.cpp index 48896940..ec4f4dc8 100644 --- a/source/settings/newtitles.cpp +++ b/source/settings/newtitles.cpp @@ -78,6 +78,35 @@ NewTitles::~NewTitles() firstTitle = lastTitle = NULL; } +void NewTitles::CheckGame(u8 *titleid) +{ + Title *t = firstTitle; + while (t != NULL) { + // Loop all titles, search for the correct titleid + if (strcmp((const char *) titleid, (const char *) t->titleId) == 0) { + return; // Game found, which is excellent + } + t = (Title *) t->next; + } + + // Not found, add it + t = new Title(); + strncpy((char *) t->titleId, (char *) titleid, 6); + t->timestamp = time(NULL); + if (isNewFile) { + t->timestamp -= (NEW_SECONDS + 1); // Mark all games as not new if this is a new file + } + + if (firstTitle == NULL) { + firstTitle = t; + lastTitle = t; + } else { + lastTitle -> next = t; + lastTitle = t; + } + isDirty = true; +} + bool NewTitles::IsNew(u8 *titleid) { Title *t = firstTitle; @@ -95,23 +124,8 @@ bool NewTitles::IsNew(u8 *titleid) } t = (Title *) t->next; } - - // Not found, add it - t = new Title(); - strncpy((char *) t->titleId, (char *) titleid, 6); - t->timestamp = time(NULL); - if (isNewFile) { - t->timestamp -= (NEW_SECONDS + 1); // Mark all games as not new if this is a new file - } - - if (firstTitle == NULL) { - firstTitle = t; - lastTitle = t; - } else { - lastTitle -> next = t; - lastTitle = t; - } - isDirty = true; + // We should never get here, since all files should be added by now! + CheckGame(titleid); return !isNewFile; // If this is a new file, return false } diff --git a/source/settings/newtitles.h b/source/settings/newtitles.h index 4bc46295..c6e2538a 100644 --- a/source/settings/newtitles.h +++ b/source/settings/newtitles.h @@ -10,6 +10,7 @@ public: static void DestroyInstance(); void Save(); + void CheckGame(u8 *titleid); bool IsNew(u8 *titleid); void Remove(u8 *titleid); private: diff --git a/source/sys.cpp b/source/sys.cpp index 9d4e4662..1e3324ac 100644 --- a/source/sys.cpp +++ b/source/sys.cpp @@ -207,11 +207,22 @@ bool Sys_IsHermes() { return IOS_GetVersion() == 222 || IOS_GetVersion() == 223; } +#include "Prompts/PromptWindows.h" + +void ShowMemInfo() { + char buf[255]; + struct mallinfo mymallinfo = mallinfo(); + sprintf((char *) &buf,"Total: %d, Used: %d, Can be freed: %d", mymallinfo.arena/1024, mymallinfo.uordblks/1024, mymallinfo.keepcost/1024); + WindowPrompt("Mem info", (char *) &buf, "OK"); +} + #include "wad/title.h" s32 ios222rev = -69; +s32 ios223rev = -69; s32 ios249rev = -69; +s32 ios250rev = -69; s32 IOS_ReloadIOSsafe(int ios) { @@ -219,20 +230,31 @@ s32 IOS_ReloadIOSsafe(int ios) { if (ios222rev == -69) ios222rev = getIOSrev(0x00000001000000dell); - - if (ios222rev != 4)return -2; + if (ios222rev >= 0 && ios222rev != 4)return -2; } + else if (ios==223) + { + if (ios223rev == -69) + ios223rev = getIOSrev(0x00000001000000dfll); + if (ios223rev >= 0 && ios223rev != 4)return -2; + } else if (ios==249) { if (ios249rev == -69) - ios249rev = getIOSrev(0x00000001000000f9ll); - + ios249rev = getIOSrev(0x00000001000000f9ll); - if (!(ios249rev>=9 && ios249rev<65535))return -2; + if (ios249rev >= 0 && !(ios249rev>=9 && ios249rev<65280))return -2; + } + else if (ios==250) + { + if (ios250rev == -69) + ios250rev = getIOSrev(0x00000001000000fall); + + if (ios250rev >= 0 && !(ios250rev>=9 && ios250rev<65280))return -2; } return IOS_ReloadIOS(ios); - } + diff --git a/source/sys.h b/source/sys.h index 6e72019d..c8f46954 100644 --- a/source/sys.h +++ b/source/sys.h @@ -16,7 +16,11 @@ int Sys_IosReload(int IOS); bool Sys_IsHermes(); s32 IOS_ReloadIOSsafe(int ios); +void ShowMemInfo(); extern s32 ios222rev; +extern s32 ios223rev; extern s32 ios249rev; +extern s32 ios250rev; #endif + diff --git a/source/usbloader/apploader.c b/source/usbloader/apploader.c index 394f0d40..9823226e 100644 --- a/source/usbloader/apploader.c +++ b/source/usbloader/apploader.c @@ -256,6 +256,8 @@ void PretendThereIsADiscInTheDrive(void *buffer, u32 len) /** Thanks to WiiPower **/ bool NewSuperMarioBrosPatch(void *Address, int Size) { + if (IOS_GetVersion() == 222 || IOS_GetVersion() == 223) return false; // Don't use this when using Hermes, it'll use the BCA fix instead... + if (memcmp("SMNE", (char *)0x80000000, 4) == 0) { u8 SearchPattern[32] = { 0x94, 0x21, 0xFF, 0xD0, 0x7C, 0x08, 0x02, 0xA6, 0x90, 0x01, 0x00, 0x34, 0x39, 0x61, 0x00, 0x30, 0x48, 0x12, 0xD7, 0x89, 0x7C, 0x7B, 0x1B, 0x78, 0x7C, 0x9C, 0x23, 0x78, 0x7C, 0xBD, 0x2B, 0x78 }; @@ -387,7 +389,8 @@ s32 Apploader_Run(entry_point *entry, u8 cheat, u8 videoSelected, u8 vipatch, u8 if (error002fix!=0) { /* ERROR 002 fix (thanks to WiiPower for sharing this)*/ - *(u32 *)0x80003140 = *(u32 *)0x80003188; + *(u32 *)0x80003188 = *(u32 *)0x80003140; +// *(u32 *)0x80003140 = *(u32 *)0x80003188; } if (cheat || geckoinit) { diff --git a/source/usbloader/disc.h b/source/usbloader/disc.h index ea9ac179..a06827e9 100644 --- a/source/usbloader/disc.h +++ b/source/usbloader/disc.h @@ -19,8 +19,7 @@ extern "C" { u8 bufsize; /* Padding */ - u8 isNew; // Use space from the padding below - u8 unused1[13]; + u8 unused1[14]; /* Magic word */ u32 magic; diff --git a/source/usbloader/getentries.cpp b/source/usbloader/getentries.cpp index d520a9ce..0b6ab246 100644 --- a/source/usbloader/getentries.cpp +++ b/source/usbloader/getentries.cpp @@ -16,7 +16,6 @@ #include #include - #include "listfiles.h" #define typei 0x00010001 @@ -163,8 +162,8 @@ int __Menu_GetPrevFilter(int t, wchar_t* gameFilter, u32 gameFiltered, wchar_t * { struct discHdr *header = &buffer[i]; - /* Is new? */ - header->isNew = NewTitles::Instance()->IsNew(header->id); + /* Register game */ + NewTitles::Instance()->CheckGame(header->id); /* Filter Favorite */ if (Settings.fave && t==0) @@ -479,8 +478,8 @@ int __Menu_GetGameList(int t, wchar_t* gameFilter, discHdr ** PgameList, u32 *Pg for (u32 i = 0; i < cnt; i++) { struct discHdr *header = &buffer[i]; - /* Is new? */ - header->isNew = NewTitles::Instance()->IsNew(header->id); + /* Register game */ + NewTitles::Instance()->CheckGame(header->id); /* Filters */ if (Settings.fave && t==0) { @@ -515,7 +514,7 @@ int __Menu_GetGameList(int t, wchar_t* gameFilter, discHdr ** PgameList, u32 *Pg } if (!buffer) return -1; - + if (Settings.sort==pcount) { qsort(buffer, cnt, sizeof(struct discHdr), __Menu_EntryCmpCount); } else if (Settings.fave) { @@ -547,6 +546,7 @@ int __Menu_GetEntries(int t, const wchar_t* Filter) { new_gameFilter = wcsdup_new(Filter ? Filter : (gameFilter ? gameFilter : L"") ); if(new_gameFilter == NULL) return -1; + for(;;) { if (mountMethod==3) @@ -562,6 +562,7 @@ int __Menu_GetEntries(int t, const wchar_t* Filter) { break; new_gameFilter[wcslen(new_gameFilter)-1] = 0; } + /* init GameFilterNextList */ if(__Menu_GetGameFilter_NextList(new_gameList, new_gameCnt, &new_gameFilter, &new_gameFilterNextList) < 0) goto error; diff --git a/source/usbloader/partition_usbloader.c b/source/usbloader/partition_usbloader.c index 8ccccd2b..895f9280 100644 --- a/source/usbloader/partition_usbloader.c +++ b/source/usbloader/partition_usbloader.c @@ -1,57 +1,123 @@ +// Modified by oggzee + #include #include #include +#include #include "partition_usbloader.h" -#include "usbstorage.h" #include "sdhc.h" +#include "usbstorage.h" #include "utils.h" -#include "libwbfs/libwbfs.h" #include "wbfs.h" +#include "libwbfs/libwbfs.h" /* 'partition table' structure */ typedef struct { - /* Zero bytes */ - u8 padding[446]; + /* Zero bytes */ + u8 padding[446]; - /* Partition table entries */ - partitionEntry entries[MAX_PARTITIONS]; + /* Partition table entries */ + partitionEntry entries[MAX_PARTITIONS]; } ATTRIBUTE_PACKED partitionTable; -s32 Partition_GetEntries(partitionEntry *outbuf, u32 *outval) { - static partitionTable table ATTRIBUTE_ALIGN(32); - u32 cnt, sector_size; - s32 ret; +s32 Partition_GetEntries(u32 device, partitionEntry *outbuf, u32 *outval) +{ + static partitionTable table ATTRIBUTE_ALIGN(32); - /* Get sector size */ - ret = USBStorage_GetCapacity(§or_size); - if (ret < 0) - return ret; + u32 cnt, sector_size; + s32 ret; - /* Read partition table */ - ret = USBStorage_ReadSectors(0, 1, &table); - if (ret < 0) - return ret; + /* Read from specified device */ + switch (device) { + case WBFS_DEVICE_USB: { + /* Get sector size */ + ret = USBStorage_GetCapacity(§or_size); + if (ret == 0) + return -1; - /* Swap endianess */ - for (cnt = 0; cnt < 4; cnt++) { - partitionEntry *entry = &table.entries[cnt]; + /* Read partition table */ + ret = USBStorage_ReadSectors(0, 1, &table); + if (ret < 0) + return ret; - entry->sector = swap32(entry->sector); - entry->size = swap32(entry->size); - } + break; + } - /* Set partition entries */ - memcpy(outbuf, table.entries, sizeof(table.entries)); + case WBFS_DEVICE_SDHC: { + /* SDHC sector size */ + sector_size = SDHC_SECTOR_SIZE; - /* Set sector size */ - *outval = sector_size; + /* Read partition table */ + ret = SDHC_ReadSectors(0, 1, &table); + if (!ret) + return -1; - return 0; + break; + } + + default: + return -1; + } + + + /* Swap endianess */ + for (cnt = 0; cnt < 4; cnt++) { + partitionEntry *entry = &table.entries[cnt]; + + entry->sector = swap32(entry->sector); + entry->size = swap32(entry->size); + } + + /* Set partition entries */ + memcpy(outbuf, table.entries, sizeof(table.entries)); + + /* Set sector size */ + *outval = sector_size; + + return 0; } -s32 Partition_GetEntriesEx(partitionEntry *outbuf, u32 *outval, int *num) +bool Device_ReadSectors(u32 device, u32 sector, u32 count, void *buffer) +{ + s32 ret; + + /* Read from specified device */ + switch (device) { + case WBFS_DEVICE_USB: + ret = USBStorage_ReadSectors(sector, count, buffer); + if (ret < 0) + return false; + return true; + + case WBFS_DEVICE_SDHC: + return SDHC_ReadSectors(sector, count, buffer); + } + + return false; +} + +bool Device_WriteSectors(u32 device, u32 sector, u32 count, void *buffer) +{ + s32 ret; + + /* Read from specified device */ + switch (device) { + case WBFS_DEVICE_USB: + ret = USBStorage_WriteSectors(sector, count, buffer); + if (ret < 0) + return false; + return true; + + case WBFS_DEVICE_SDHC: + return SDHC_WriteSectors(sector, count, buffer); + } + + return false; +} + +s32 Partition_GetEntriesEx(u32 device, partitionEntry *outbuf, u32 *psect_size, int *num) { static partitionTable table ATTRIBUTE_ALIGN(32); partitionEntry *entry; @@ -61,34 +127,60 @@ s32 Partition_GetEntriesEx(partitionEntry *outbuf, u32 *outval, int *num) int maxpart = *num; // Get sector size - ret = USBStorage_GetCapacity(§or_size); - if (ret == 0) return -1; + switch (device) { + case WBFS_DEVICE_USB: + ret = USBStorage_GetCapacity(§or_size); + if (ret == 0) return -1; + break; + case WBFS_DEVICE_SDHC: + sector_size = SDHC_SECTOR_SIZE; + break; + default: + return -1; + } + /* Set sector size */ + *psect_size = sector_size; u32 ext = 0; u32 next = 0; // Read partition table - ret = USBStorage_ReadSectors(0, 1, &table); + ret = Device_ReadSectors(device, 0, 1, &table); if (!ret) return -1; + // Check if it's a RAW WBFS disc, without partition table + if (get_fs_type(&table) == FS_TYPE_WBFS) { + memset(outbuf, 0, sizeof(table.entries)); + wbfs_head_t *head = (wbfs_head_t*)&table; + outbuf->size = wbfs_ntohl(head->n_hd_sec); + *num = 1; + return 0; + } /* Swap endianess */ for (i = 0; i < 4; i++) { entry = &table.entries[i]; entry->sector = swap32(entry->sector); entry->size = swap32(entry->size); - if (!ext && entry->type == 0x0f) ext = entry->sector; + if (!ext && part_is_extended(entry->type)) { + ext = entry->sector; + } } /* Set partition entries */ memcpy(outbuf, table.entries, sizeof(table.entries)); - /* Set sector size */ - *outval = sector_size; // num primary *num = 4; + if (!ext) return 0; + next = ext; // scan extended partition for logical - if (ext) for(i=0; isector = swap32(entry->sector); entry->size = swap32(entry->size); @@ -106,12 +198,32 @@ s32 Partition_GetEntriesEx(partitionEntry *outbuf, u32 *outval, int *num) break; } } - } return 0; } +bool part_is_extended(int type) +{ + if (type == 0x05) return true; + if (type == 0x0f) return true; + return false; +} + +bool part_is_data(int type) +{ + if (type && !part_is_extended(type)) return true; + return false; +} + +bool part_valid_data(partitionEntry *entry) +{ + if (entry->size && entry->type && entry->sector) { + return part_is_data(entry->type); + } + return false; +} + char* part_type_data(int type) { switch (type) { @@ -133,8 +245,20 @@ char* part_type_data(int type) return NULL; } -int get_fs_type(char *buf) +char *part_type_name(int type) { + static char unk[8]; + if (type == 0) return "UNUSED"; + if (part_is_extended(type)) return "EXTEND"; + char *p = part_type_data(type); + if (p) return p; + sprintf(unk, "UNK-%02x", type); + return unk; +} + +int get_fs_type(void *buff) +{ + char *buf = buff; // WBFS wbfs_head_t *head = (wbfs_head_t *)buf; if (head->magic == wbfs_htonl(WBFS_MAGIC)) return FS_TYPE_WBFS; @@ -154,7 +278,19 @@ bool is_type_fat(int type) return (type == FS_TYPE_FAT16 || type == FS_TYPE_FAT32); } -s32 Partition_GetList(PartList *plist) + +char *get_fs_name(int i) +{ + switch (i) { + case FS_TYPE_FAT16: return "FAT16"; + case FS_TYPE_FAT32: return "FAT32"; + case FS_TYPE_NTFS: return "NTFS"; + case FS_TYPE_WBFS: return "WBFS"; + } + return ""; +} + +s32 Partition_GetList(u32 device, PartList *plist) { partitionEntry *entry = NULL; PartInfo *pinfo = NULL; @@ -164,10 +300,19 @@ s32 Partition_GetList(PartList *plist) // Get partition entries plist->num = MAX_PARTITIONS_EX; - ret = Partition_GetEntriesEx(plist->pentry, &plist->sector_size, &plist->num); + ret = Partition_GetEntriesEx(device, plist->pentry, &plist->sector_size, &plist->num); if (ret < 0) { return -1; } + // check for RAW WBFS disc + if (plist->num == 1) { + pinfo = &plist->pinfo[0]; + entry = &plist->pentry[0]; + plist->wbfs_n = 1; + pinfo->wbfs_i = 1; + return 0; + } + char buf[plist->sector_size]; // scan partitions for filesystem type @@ -175,10 +320,15 @@ s32 Partition_GetList(PartList *plist) pinfo = &plist->pinfo[i]; entry = &plist->pentry[i]; if (!entry->size) continue; - if (!part_type_data(entry->type)) continue; - if (!USBStorage_ReadSectors(entry->sector, 1, buf)) continue; + if (!entry->type) continue; + if (!entry->sector) continue; + // even though wrong, it's possible WBFS is on an extended part. + //if (!part_is_data(entry->type)) continue; + if (!Device_ReadSectors(device, entry->sector, 1, buf)) continue; pinfo->fs_type = get_fs_type(buf); if (pinfo->fs_type == FS_TYPE_WBFS) { + // multiple wbfs on sdhc not supported + if (device == WBFS_DEVICE_SDHC && (plist->wbfs_n > 1 || i > 4)) continue; plist->wbfs_n++; pinfo->wbfs_i = plist->wbfs_n; } else if (is_type_fat(pinfo->fs_type)) { @@ -188,3 +338,24 @@ s32 Partition_GetList(PartList *plist) } return 0; } + + +int Partition_FixEXT(u32 device, int part) +{ + static partitionTable table ATTRIBUTE_ALIGN(32); + int ret; + + if (part < 0 || part > 3) return -1; + // Read partition table + ret = Device_ReadSectors(device, 0, 1, &table); + if (!ret) return -1; + if (part_is_extended(table.entries[part].type)) { + table.entries[part].type = 0x0b; // FAT32 + ret = Device_WriteSectors(device, 0, 1, &table); + if (!ret) return -1; + return 0; + } + return -1; +} + + diff --git a/source/usbloader/partition_usbloader.h b/source/usbloader/partition_usbloader.h index 6aa881d1..6ebcc1a5 100644 --- a/source/usbloader/partition_usbloader.h +++ b/source/usbloader/partition_usbloader.h @@ -1,62 +1,74 @@ -#ifndef _PARTITION_H_ +#ifndef _PARTITION_H_ #define _PARTITION_H_ - -#ifdef __cplusplus -extern "C" { -#endif - - /* 'partition entry' structure */ - typedef struct { - /* Boot indicator */ - u8 boot; - /* Starting CHS */ - u8 start[3]; +#ifdef __cplusplus +extern "C" { +#endif - /* Partition type */ - u8 type; +/* 'partition entry' structure */ +typedef struct { + /* Boot indicator */ + u8 boot; - /* Ending CHS */ - u8 end[3]; + /* Starting CHS */ + u8 start[3]; - /* Partition sector */ - u32 sector; + /* Partition type */ + u8 type; - /* Partition size */ - u32 size; - } ATTRIBUTE_PACKED partitionEntry; + /* Ending CHS */ + u8 end[3]; - /* Constants */ - #define MAX_PARTITIONS 4 - #define MAX_PARTITIONS_EX 10 + /* Partition sector */ + u32 sector; - #define FS_TYPE_UNK 0 - #define FS_TYPE_FAT16 1 - #define FS_TYPE_FAT32 2 - #define FS_TYPE_NTFS 3 - #define FS_TYPE_WBFS 4 + /* Partition size */ + u32 size; +} ATTRIBUTE_PACKED partitionEntry; - typedef struct - { - int fs_type; - int wbfs_i; // seq wbfs part index - int fat_i; // seq fat part index - } PartInfo; +/* Constants */ +#define MAX_PARTITIONS 4 +#define MAX_PARTITIONS_EX 10 - typedef struct - { - int num; - u32 sector_size; - partitionEntry pentry[MAX_PARTITIONS_EX]; - int wbfs_n; - int fat_n; - PartInfo pinfo[MAX_PARTITIONS_EX]; - } PartList; +#define FS_TYPE_UNK 0 +#define FS_TYPE_FAT16 1 +#define FS_TYPE_FAT32 2 +#define FS_TYPE_NTFS 3 +#define FS_TYPE_WBFS 4 - /* Prototypes */ - s32 Partition_GetEntries(partitionEntry *, u32 *); - s32 Partition_GetEntriesEx(partitionEntry *, u32 *, int *); - s32 Partition_GetList(PartList *plist); +typedef struct +{ + int fs_type; + int wbfs_i; // seq wbfs part index + int fat_i; // seq fat part index +} PartInfo; + +typedef struct +{ + int num; + u32 sector_size; + partitionEntry pentry[MAX_PARTITIONS_EX]; + int wbfs_n; + int fat_n; + PartInfo pinfo[MAX_PARTITIONS_EX]; +} PartList; + +/* Prototypes */ +s32 Partition_GetEntries(u32 device, partitionEntry *outbuf, u32 *outval); +s32 Partition_GetEntriesEx(u32 device, partitionEntry *outbuf, u32 *outval, int *num); +bool Device_ReadSectors(u32 device, u32 sector, u32 count, void *buffer); +bool Device_WriteSectors(u32 device, u32 sector, u32 count, void *buffer); +s32 Partition_GetList(u32 device, PartList *plist); +int Partition_FixEXT(u32 device, int part); + +bool part_is_extended(int type); +bool part_is_data(int type); +char* part_type_data(int type); +char* part_type_name(int type); +bool part_valid_data(partitionEntry *entry); +int get_fs_type(void *buf); +bool is_type_fat(int type); +char* get_fs_name(int i); #ifdef __cplusplus } diff --git a/source/usbloader/usbstorage.c b/source/usbloader/usbstorage.c index 07edbfd6..657a1153 100644 --- a/source/usbloader/usbstorage.c +++ b/source/usbloader/usbstorage.c @@ -52,7 +52,7 @@ static s32 hid = -1, fd = -1; static u32 sector_size; extern void* SYS_AllocArena2MemLo(u32 size,u32 align); -static void *mem2_ptr=NULL; +//static void *mem2_ptr=NULL; inline s32 __USBStorage_isMEM2Buffer(const void *buffer) { u32 high_addr = ((u32)buffer) >> 24; @@ -151,7 +151,7 @@ void USBStorage_Deinit(void) { s32 USBStorage_ReadSectors(u32 sector, u32 numSectors, void *buffer) { - void *buf = (void *)buffer; +// void *buf = (void *)buffer; u32 len = (sector_size * numSectors); s32 ret; @@ -159,30 +159,31 @@ s32 USBStorage_ReadSectors(u32 sector, u32 numSectors, void *buffer) { /* Device not opened */ if (fd < 0) return fd; - if(!mem2_ptr) mem2_ptr=SYS_AllocArena2MemLo(2048*256,32); + +// if(!mem2_ptr) mem2_ptr=SYS_AllocArena2MemLo(2048*256,32); /* MEM1 buffer */ - if (!__USBStorage_isMEM2Buffer(buffer)) { +// if (!__USBStorage_isMEM2Buffer(buffer)) { /* Allocate memory */ - buf = mem2_ptr; //iosAlloc(hid, len); - if (!buf) - return IPC_ENOMEM; - } +// buf = mem2_ptr; //iosAlloc(hid, len); +// if (!buf) +// return IPC_ENOMEM; +// } /* Read data */ - ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_UMS_READ_SECTORS, "ii:d", sector, numSectors, buf, len); + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_UMS_READ_SECTORS, "ii:d", sector, numSectors, buffer, len); /* Copy data */ - if (buf != buffer) { - memcpy(buffer, buf, len); +// if (buf != buffer) { +// memcpy(buffer, buf, len); //iosFree(hid, buf); - } +// } return ret; } s32 USBStorage_WriteSectors(u32 sector, u32 numSectors, const void *buffer) { - void *buf = (void *)buffer; +// void *buf = (void *)buffer; u32 len = (sector_size * numSectors); s32 ret; @@ -190,26 +191,26 @@ s32 USBStorage_WriteSectors(u32 sector, u32 numSectors, const void *buffer) { /* Device not opened */ if (fd < 0) return fd; - if(!mem2_ptr) mem2_ptr = SYS_AllocArena2MemLo(2048*256,32); +// if(!mem2_ptr) mem2_ptr = SYS_AllocArena2MemLo(2048*256,32); /* MEM1 buffer */ - if (!__USBStorage_isMEM2Buffer(buffer)) { +// if (!__USBStorage_isMEM2Buffer(buffer)) { /* Allocate memory */ - buf = mem2_ptr; //buf = iosAlloc(hid, len); - if (!buf) - return IPC_ENOMEM; +// buf = mem2_ptr; //buf = iosAlloc(hid, len); +// if (!buf) +// return IPC_ENOMEM; /* Copy data */ - memcpy(buf, buffer, len); - } +// memcpy(buf, buffer, len); +// } /* Write data */ - ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_UMS_WRITE_SECTORS, "ii:d", sector, numSectors, buf, len); + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_UMS_WRITE_SECTORS, "ii:d", sector, numSectors, buffer, len); /* Free memory */ - if (buf != buffer) - iosFree(hid, buf); +// if (buf != buffer) +// iosFree(hid, buf); return ret; } diff --git a/source/usbloader/wbfs.c b/source/usbloader/wbfs.c index 7bd3f884..2dbecc80 100644 --- a/source/usbloader/wbfs.c +++ b/source/usbloader/wbfs.c @@ -338,7 +338,7 @@ s32 WBFS_OpenNamed(char *partition) } // Get partition entries - ret = Partition_GetList(&plist); + ret = Partition_GetList(wbfsDev, &plist); if (ret || plist.num == 0) return -1; if (part_fat) { diff --git a/source/usbloader/wbfs_fat.c b/source/usbloader/wbfs_fat.c index a87ac11b..8e638fb1 100644 --- a/source/usbloader/wbfs_fat.c +++ b/source/usbloader/wbfs_fat.c @@ -24,8 +24,11 @@ #include "disc.h" #include "settings/cfg.h" + // WBFS FAT by oggzee +// max fat fname = 256 +#define MAX_FAT_PATH 1024 #define D_S(A) A, sizeof(A) char wbfs_fat_drive[16]; @@ -42,36 +45,38 @@ static u32 fat_sector_size = 512; void WBFS_Spinner(s32 x, s32 max); s32 __WBFS_ReadDVD(void *fp, u32 lba, u32 len, void *iobuf); + s32 _WBFS_FAT_GetHeadersCount(void *outbuf, u32 *count, u32 len) { DIR *dir; struct dirent *dent; char *p; int ret, cnt = 0; - char path[100]; + char path[MAX_FAT_PATH]; wbfs_t *part = NULL; u32 size; u8 *ptr; struct discHdr tmpHdr; int hdrsize; struct stat st; + u8 id[8]; + int is_dir; //dbg_time1(); strcpy(path, wbfs_fat_drive); strcat(path, wbfs_fat_dir); dir = opendir(path); + //printf("opendir: %s %p\n", path, dir); Wpad_WaitButtons(); if (!dir) { *count = 0; return 0; } while ((dent = readdir(dir)) != NULL) { + //printf("found: %s\n", dent->d_name); if (outbuf && cnt >= *count) break; if ((char)dent->d_name[0] == '.') continue; - p = strrchr(dent->d_name, '.'); - if (!p) continue; - if (strcasecmp(p, ".wbfs") != 0) continue; - if (strlen(dent->d_name) != 11) continue; // GAMEID.wbfs - u8 id[8]; + if (strlen(dent->d_name) < 8) continue; // "GAMEID_x" + memcpy(id, dent->d_name, 6); id[6] = 0; @@ -80,6 +85,26 @@ s32 _WBFS_FAT_GetHeadersCount(void *outbuf, u32 *count, u32 len) strcat(path, "/"); strcat(path, dent->d_name); stat(path, &st); + + is_dir = S_ISDIR(st.st_mode); + //printf("path: %s %d\n", path, is_dir); + if (is_dir) { + // usb:/wbfs/GAMEID_TITLE/GAMEID.wbfs + if (dent->d_name[6] != '_') continue; + strcat(path, "/"); + strcat(path, (char*)id); + strcat(path, ".wbfs"); + //printf("path2: %s\n", path); + if (stat(path, &st) == -1) continue; + } else { + // usb:/wbfs/GAMEID.wbfs + p = strrchr(dent->d_name, '.'); + if (!p) continue; + if (strcasecmp(p, ".wbfs") != 0) continue; + if (strlen(dent->d_name) != 11) continue; // GAMEID.wbfs + } + + //printf("found: %s %d MB\n", path, (int)(st.st_size/1024/1024)); // size must be at least 1MB to be considered a valid wbfs file if (st.st_size < 1024*1024) continue; if (!outbuf) { @@ -89,40 +114,43 @@ s32 _WBFS_FAT_GetHeadersCount(void *outbuf, u32 *count, u32 len) } ptr = ((u8 *)outbuf) + (cnt * len); hdrsize = len; - + // if we have titles.txt entry use that char *title = cfg_get_title(id); + // if directory, and no titles.txt get title from dir name + if (!title && is_dir) { + title = &dent->d_name[7]; + } if (title) { memset(&tmpHdr, 0, sizeof(tmpHdr)); memcpy(tmpHdr.id, id, 6); - strncpy(tmpHdr.title, title, sizeof(tmpHdr.title)); + strncpy(tmpHdr.title, title, sizeof(tmpHdr.title)-1); tmpHdr.magic = 0x5D1C9EA3; memcpy(ptr, &tmpHdr, hdrsize); cnt++; continue; } - // no title found, read it from wbfs file directly + // else read it from wbfs file directly FILE *fp = fopen(path, "rb"); if (fp != NULL) { fseek(fp, 512, SEEK_SET); fread(&tmpHdr, sizeof(struct discHdr), 1, fp); fclose(fp); - - if (tmpHdr.magic == 0x5D1C9EA3 && (memcmp(tmpHdr.id, id, 6) == 0)) { + if ((tmpHdr.magic == 0x5D1C9EA3) && (memcmp(tmpHdr.id, id, 6) == 0)) { memcpy(ptr, &tmpHdr, hdrsize); cnt++; continue; } } - // no title found, read it from wbfs file - // but this is a little bit slower + // but this is a little bit slower // open 'partition' file part = WBFS_FAT_OpenPart(id); if (!part) { + printf("bad wbfs file: %s\n", dent->d_name); + sleep(2); continue; } - /* Get header */ ret = wbfs_get_disc_info(part, 0, ptr, hdrsize, &size); if (ret == 0) cnt++; @@ -131,7 +159,7 @@ s32 _WBFS_FAT_GetHeadersCount(void *outbuf, u32 *count, u32 len) *count = cnt; closedir(dir); //dbg_time2("\nFAT HDRS"); - //Wpad_WaitButtons(); + //Wpad_WaitButtonsCommon(); return 0; } @@ -175,7 +203,6 @@ s32 WBFS_FAT_DiskSpace(f32 *used, f32 *free) // statvfs is slow, so cache values if (!wbfs_fat_vfs_have || wbfs_fat_vfs_lba != wbfs_part_lba) { ret = statvfs(wbfs_fat_drive, &wbfs_fat_vfs); - if (ret) return 0; wbfs_fat_vfs_have = 1; wbfs_fat_vfs_lba = wbfs_part_lba; @@ -192,32 +219,122 @@ s32 WBFS_FAT_DiskSpace(f32 *used, f32 *free) static int nop_read_sector(void *_fp,u32 lba,u32 count,void*buf) { + //printf("read %d %d\n", lba, count); //Wpad_WaitButtons(); return 0; } static int nop_write_sector(void *_fp,u32 lba,u32 count,void*buf) { + //printf("write %d %d\n", lba, count); //Wpad_WaitButtons(); return 0; } -void WBFS_FAT_fname(u8 *id, char *fname, int len) +void WBFS_FAT_fname(u8 *id, char *fname, int len, char *path) { - snprintf(fname, len, "%s%s/%.6s.wbfs", wbfs_fat_drive, wbfs_fat_dir, id); + if (path == NULL) { + snprintf(fname, len, "%s%s/%.6s.wbfs", wbfs_fat_drive, wbfs_fat_dir, id); + } else { + snprintf(fname, len, "%s/%.6s.wbfs", path, id); + } +} + +void mk_gameid_title(struct discHdr *header, char *name, int re_space) +{ + int i, len; + + memcpy(name, header->id, 6); + name[6] = 0; + strcat(name, "_"); + strcat(name, get_title(header)); + + // replace silly chars with '_' + len = strlen(name); + for (i = 0; i < len; i++) { + if(strchr("\\/:<>|\"", name[i]) || iscntrl(name[i])) { + name[i] = '_'; + } + if(re_space && name[i]==' ') { + name[i] = '_'; + } + } +} + +void WBFS_FAT_get_dir(struct discHdr *header, char *path) +{ + strcpy(path, wbfs_fat_drive); + strcat(path, wbfs_fat_dir); + if (Settings.FatInstallToDir) { + strcat(path, "/"); + mk_gameid_title(header, path + strlen(path), 0); + } +} + +void mk_title_txt(struct discHdr *header, char *path) +{ + char fname[MAX_FAT_PATH]; + FILE *f; + + strcpy(fname, path); + strcat(fname, "/"); + mk_gameid_title(header, fname+strlen(fname), 1); + strcat(fname, ".txt"); + + f = fopen(fname, "wb"); + if (!f) return; + fprintf(f, "%.6s = %.64s\n", header->id, get_title(header)); + fclose(f); + printf("Info file: %s\n", fname); +} + + +int WBFS_FAT_find_fname(u8 *id, char *fname, int len) +{ + struct stat st; + WBFS_FAT_fname(id, fname, len, NULL); + if (stat(fname, &st) == 0) return 1; + // direct file not found, check subdirs + DIR *dir; + struct dirent *dent; + char path[MAX_FAT_PATH]; + strcpy(path, wbfs_fat_drive); + strcat(path, wbfs_fat_dir); + dir = opendir(path); + //printf("opendir: %s %p\n", path, dir); Wpad_WaitButtons(); + if (!dir) { + goto err; + } + while ((dent = readdir(dir)) != NULL) { + char *name = (char*)dent->d_name; + if (name[0] == '.') continue; + if (name[6] != '_') continue; + if (strncmp(name, (char*)id, 6) != 0) continue; + if (strlen(name) < 8) continue; + snprintf(fname, len, "%s/%s/%.6s.wbfs", path, name, id); + if (stat(fname, &st) == 0) { + closedir(dir); + return 2; + } + } + closedir(dir); + // not found + err: + *fname = 0; + return 0; } wbfs_t* WBFS_FAT_OpenPart(u8 *id) { - char fname[100]; + char fname[MAX_FAT_PATH]; wbfs_t *part = NULL; int ret; // wbfs 'partition' file - WBFS_FAT_fname(id, fname, sizeof(fname)); + if ( !WBFS_FAT_find_fname(id, fname, sizeof(fname)) ) return NULL; ret = split_open(&split, fname); if (ret) return NULL; part = wbfs_open_partition( split_read_sector, - split_write_sector, //readonly //split_write_sector, + nop_write_sector, //readonly //split_write_sector, &split, fat_sector_size, split.total_sec, 0, 0); if (!part) { split_close(&split); @@ -225,17 +342,20 @@ wbfs_t* WBFS_FAT_OpenPart(u8 *id) return part; } -wbfs_t* WBFS_FAT_CreatePart(u8 *id) +wbfs_t* WBFS_FAT_CreatePart(u8 *id, char *path) { - char fname[100]; + char fname[MAX_FAT_PATH]; wbfs_t *part = NULL; u64 size = (u64)143432*2*0x8000ULL; u32 n_sector = size / 512; int ret; + //printf("CREATE PART %s %lld %d\n", id, size, n_sector); snprintf(D_S(fname), "%s%s", wbfs_fat_drive, wbfs_fat_dir); - mkdir(fname, 0777); - WBFS_FAT_fname(id, fname, sizeof(fname)); + mkdir(fname, 0777); // base usb:/wbfs + mkdir(path, 0777); // game subdir + WBFS_FAT_fname(id, fname, sizeof(fname), path); + printf("Writing to %s\n", fname); ret = split_create(&split, fname, OPT_split_size, size, true); if (ret) return NULL; @@ -243,6 +363,8 @@ wbfs_t* WBFS_FAT_CreatePart(u8 *id) u32 scnt = 0; int fd = split_get_file(&split, 0, &scnt, 0); if (fd<0) { + printf("ERROR creating file\n"); + sleep(2); split_close(&split); return NULL; } @@ -267,42 +389,87 @@ void WBFS_FAT_ClosePart(wbfs_t* part) s32 WBFS_FAT_RemoveGame(u8 *discid) { - char fname[100]; + char fname[MAX_FAT_PATH]; + int loc; // wbfs 'partition' file - WBFS_FAT_fname(discid, fname, sizeof(fname)); + loc = WBFS_FAT_find_fname(discid, fname, sizeof(fname)); + if ( !loc ) return -1; split_create(&split, fname, 0, 0, true); split_close(&split); + if (loc == 1) return 0; + + // game is in subdir + // remove optional .txt file + DIR *dir; + struct dirent *dent; + char path[MAX_FAT_PATH]; + strncpy(path, fname, sizeof(path)); + char *p= strrchr(path, '/'); + if (p) *p = 0; + dir = opendir(path); + if (!dir) return 0; + while ((dent = readdir(dir)) != NULL) { + char *name = (char*)dent->d_name; + if (name[0] == '.') continue; + if (name[6] != '_') continue; + if (strncmp(name, (char*)discid, 6) != 0) continue; + p = strrchr(name, '.'); + if (!p) continue; + if (strcasecmp(p, ".txt") != 0) continue; + snprintf(fname, sizeof(fname), "%s/%s", path, name); + remove(fname); + break; + } + closedir(dir); + // remove game subdir + //rmdir(path); + if (unlink(path) == -1) { + } - // Reset FAT stats - wbfs_fat_vfs_have = 0; return 0; } + s32 WBFS_FAT_AddGame(void) { static struct discHdr header ATTRIBUTE_ALIGN(32); - s32 ret; + char path[MAX_FAT_PATH]; wbfs_t *part = NULL; - - //write_test(); return -1; + s32 ret; // read ID from DVD Disc_ReadHeader(&header); + // path + WBFS_FAT_get_dir(&header, path); // create wbfs 'partition' file - part = WBFS_FAT_CreatePart(header.id); + part = WBFS_FAT_CreatePart(header.id, path); if (!part) return -1; /* Add game to device */ + partition_selector_t part_sel = ONLY_GAME_PARTITION; + int copy_1_1 = 0; +/* + switch (CFG.install_partitions) { + case CFG_INSTALL_GAME: + part_sel = ONLY_GAME_PARTITION; + break; + case CFG_INSTALL_ALL: + part_sel = ALL_PARTITIONS; + break; + case CFG_INSTALL_1_1: + part_sel = ALL_PARTITIONS; + copy_1_1 = 1; + break; + } +*/ extern wbfs_t *hdd; wbfs_t *old_hdd = hdd; hdd = part; // used by spinner - ret = wbfs_add_disc(part, __WBFS_ReadDVD, NULL, WBFS_Spinner, ONLY_GAME_PARTITION, 0); + ret = wbfs_add_disc(part, __WBFS_ReadDVD, NULL, WBFS_Spinner, part_sel, copy_1_1); hdd = old_hdd; wbfs_trim(part); WBFS_FAT_ClosePart(part); - - // Reset FAT stats - wbfs_fat_vfs_have = 0; if (ret < 0) return ret; + mk_title_txt(&header, path); return 0; } @@ -315,7 +482,7 @@ s32 WBFS_FAT_DVD_Size(u64 *comp_size, u64 *real_size) wbfs_t *part = NULL; u64 size = (u64)143432*2*0x8000ULL; u32 n_sector = size / fat_sector_size; - u32 wii_sec_sz; + u32 wii_sec_sz; // init a temporary dummy part // as a placeholder for wbfs_size_disc @@ -326,13 +493,21 @@ s32 WBFS_FAT_DVD_Size(u64 *comp_size, u64 *real_size) wii_sec_sz = part->wii_sec_sz; /* Add game to device */ - ret = wbfs_size_disc(part, __WBFS_ReadDVD, NULL, ONLY_GAME_PARTITION, &comp_sec, &last_sec); + partition_selector_t part_sel = ONLY_GAME_PARTITION; +/* + if (CFG.install_partitions) { + part_sel = ALL_PARTITIONS; + } else { + part_sel = ONLY_GAME_PARTITION; + } +*/ + ret = wbfs_size_disc(part, __WBFS_ReadDVD, NULL, part_sel, &comp_sec, &last_sec); wbfs_close(part); if (ret < 0) return ret; - if (comp_size != NULL) *comp_size = (u64)wii_sec_sz * comp_sec; - if (real_size != NULL) *real_size = (u64)wii_sec_sz * last_sec; + *comp_size = (u64)wii_sec_sz * comp_sec; + *real_size = (u64)wii_sec_sz * last_sec; return 0; } @@ -366,8 +541,8 @@ s32 WBFS_FAT_ReIDGame(u8 *discid, const void *newID) char fnamenew[100]; s32 cnt = 0x31; - WBFS_FAT_fname(discid, fname, sizeof(fname)); - WBFS_FAT_fname((u8*) newID, fnamenew, sizeof(fnamenew)); + WBFS_FAT_fname(discid, fname, sizeof(fname), NULL); + WBFS_FAT_fname((u8*) newID, fnamenew, sizeof(fnamenew), NULL); int stringlength = strlen(fname); diff --git a/source/usbloader/wdvd.c b/source/usbloader/wdvd.c index e588b9a1..2143ad79 100644 --- a/source/usbloader/wdvd.c +++ b/source/usbloader/wdvd.c @@ -15,6 +15,7 @@ #define IOCTL_DI_SEEK 0xAB #define IOCTL_DI_STOPLASER 0xD2 #define IOCTL_DI_OFFSET 0xD9 +#define IOCTL_DI_DISC_BCA 0xDA #define IOCTL_DI_STOPMOTOR 0xE3 #define IOCTL_DI_SETUSBMODE 0xF4 #define IOCTL_DI_DISABLERESET 0xF6 @@ -336,3 +337,20 @@ s32 WDVD_SetUSBMode(const u8 *id, s32 partition) { return (ret == 1) ? 0 : -ret; } + +s32 WDVD_Read_Disc_BCA(void *buf) +{ + s32 ret; + + memset(inbuf, 0, sizeof(inbuf)); + + /* Disc read */ + inbuf[0] = IOCTL_DI_DISC_BCA << 24; + //inbuf[1] = 64; + + ret = IOS_Ioctl(di_fd, IOCTL_DI_DISC_BCA, inbuf, sizeof(inbuf), buf, 64); + if (ret < 0) + return ret; + + return (ret == 1) ? 0 : -ret; +} diff --git a/source/usbloader/wdvd.h b/source/usbloader/wdvd.h index 83fcf4cb..0f7848ff 100644 --- a/source/usbloader/wdvd.h +++ b/source/usbloader/wdvd.h @@ -23,6 +23,7 @@ extern "C" { s32 WDVD_GetCoverStatus(u32 *); s32 WDVD_DisableReset(u8); s32 WDVD_SetUSBMode(const u8 *, s32 partition); + s32 WDVD_Read_Disc_BCA(void *buf); #ifdef __cplusplus }