From dd3a7523e3c3f21c8b7a3693c6df93c262e67d01 Mon Sep 17 00:00:00 2001 From: miya Date: Thu, 16 Oct 2008 01:58:27 +0000 Subject: [PATCH] git-svn-id: file:///Users/lillianskinner/Downloads/platinum/twl/TwlToolsRED@2 7061adef-622a-194b-ae81-725974e89856 --- build/tools/sctools/Makefile | 30 + build/tools/sctools/common/src/ap_info.h | 52 + build/tools/sctools/common/src/ecdl.cpp | 169 ++ build/tools/sctools/common/src/ecdl.h | 43 + build/tools/sctools/common/src/font.c | 827 ++++++++ build/tools/sctools/common/src/font.h | 21 + build/tools/sctools/common/src/gfx.c | 160 ++ build/tools/sctools/common/src/gfx.h | 25 + .../tools/sctools/common/src/hatamotolib.cpp | 717 +++++++ build/tools/sctools/common/src/hatamotolib.h | 28 + build/tools/sctools/common/src/hwi.c | 397 ++++ build/tools/sctools/common/src/hwi.h | 51 + build/tools/sctools/common/src/key.c | 42 + build/tools/sctools/common/src/key.h | 34 + build/tools/sctools/common/src/kpsc.cpp | 43 + build/tools/sctools/common/src/logprintf.c | 795 +++++++ build/tools/sctools/common/src/logprintf.h | 7 + build/tools/sctools/common/src/mprintf.c | 817 ++++++++ build/tools/sctools/common/src/mprintf.h | 20 + build/tools/sctools/common/src/my_fs_util.c | 1828 +++++++++++++++++ build/tools/sctools/common/src/my_fs_util.h | 48 + build/tools/sctools/common/src/mynvram.c | 246 +++ build/tools/sctools/common/src/mynvram.h | 16 + build/tools/sctools/common/src/mywlan.c | 311 +++ build/tools/sctools/common/src/mywlan.h | 22 + build/tools/sctools/common/src/netconnect.c | 376 ++++ build/tools/sctools/common/src/netconnect.h | 39 + build/tools/sctools/common/src/nuc.c | 503 +++++ build/tools/sctools/common/src/nuc.h | 14 + .../tools/sctools/common/src/nuc_error_msg.c | 277 +++ .../tools/sctools/common/src/nuc_error_msg.h | 52 + build/tools/sctools/common/src/sitedefs.c | 237 +++ build/tools/sctools/common/src/sitedefs.h | 38 + build/tools/sctools/common/src/stream.c | 398 ++++ build/tools/sctools/common/src/stream.h | 18 + .../common/src/string_map_dwc_apinfo_type.inc | 9 + .../common/src/string_map_ec_error.inc | 81 + .../sctools/common/src/string_map_ec_op.inc | 31 + .../common/src/string_map_ec_phase.inc | 32 + build/tools/sctools/common/src/text.c | 412 ++++ build/tools/sctools/common/src/text.h | 74 + build/tools/sctools/common/src/wcm_control.c | 917 +++++++++ build/tools/sctools/common/src/wcm_control.h | 171 ++ build/tools/sctools/copy_dst/Makefile | 90 + build/tools/sctools/copy_dst/banner/Makefile | 59 + .../sctools/copy_dst/banner/banner_v3.bsf | Bin 0 -> 788 bytes .../sctools/copy_dst/banner/icon/gameIcon.bmp | Bin 0 -> 630 bytes .../sctools/copy_dst/banner/icon/subIcon.bmp | Bin 0 -> 630 bytes .../sctools/copy_dst/banner/sub_banner_v3.bsf | Bin 0 -> 864 bytes build/tools/sctools/copy_dst/copy_dst.rsf | 325 +++ build/tools/sctools/copy_dst/src/main.c | 548 +++++ build/tools/sctools/copy_org/Makefile | 90 + build/tools/sctools/copy_org/banner/Makefile | 59 + .../sctools/copy_org/banner/banner_v3.bsf | Bin 0 -> 788 bytes .../sctools/copy_org/banner/icon/gameIcon.bmp | Bin 0 -> 630 bytes .../sctools/copy_org/banner/icon/subIcon.bmp | Bin 0 -> 630 bytes .../sctools/copy_org/banner/sub_banner_v3.bsf | Bin 0 -> 864 bytes build/tools/sctools/copy_org/copy_org.rsf | 325 +++ build/tools/sctools/copy_org/src/main.c | 548 +++++ build/tools/sctools/files/fanfare.32.wav | Bin 0 -> 229586 bytes 60 files changed, 12472 insertions(+) create mode 100644 build/tools/sctools/Makefile create mode 100644 build/tools/sctools/common/src/ap_info.h create mode 100644 build/tools/sctools/common/src/ecdl.cpp create mode 100644 build/tools/sctools/common/src/ecdl.h create mode 100644 build/tools/sctools/common/src/font.c create mode 100644 build/tools/sctools/common/src/font.h create mode 100644 build/tools/sctools/common/src/gfx.c create mode 100644 build/tools/sctools/common/src/gfx.h create mode 100644 build/tools/sctools/common/src/hatamotolib.cpp create mode 100644 build/tools/sctools/common/src/hatamotolib.h create mode 100644 build/tools/sctools/common/src/hwi.c create mode 100644 build/tools/sctools/common/src/hwi.h create mode 100644 build/tools/sctools/common/src/key.c create mode 100644 build/tools/sctools/common/src/key.h create mode 100644 build/tools/sctools/common/src/kpsc.cpp create mode 100644 build/tools/sctools/common/src/logprintf.c create mode 100644 build/tools/sctools/common/src/logprintf.h create mode 100644 build/tools/sctools/common/src/mprintf.c create mode 100644 build/tools/sctools/common/src/mprintf.h create mode 100644 build/tools/sctools/common/src/my_fs_util.c create mode 100644 build/tools/sctools/common/src/my_fs_util.h create mode 100644 build/tools/sctools/common/src/mynvram.c create mode 100644 build/tools/sctools/common/src/mynvram.h create mode 100644 build/tools/sctools/common/src/mywlan.c create mode 100644 build/tools/sctools/common/src/mywlan.h create mode 100644 build/tools/sctools/common/src/netconnect.c create mode 100644 build/tools/sctools/common/src/netconnect.h create mode 100644 build/tools/sctools/common/src/nuc.c create mode 100644 build/tools/sctools/common/src/nuc.h create mode 100644 build/tools/sctools/common/src/nuc_error_msg.c create mode 100644 build/tools/sctools/common/src/nuc_error_msg.h create mode 100644 build/tools/sctools/common/src/sitedefs.c create mode 100644 build/tools/sctools/common/src/sitedefs.h create mode 100644 build/tools/sctools/common/src/stream.c create mode 100644 build/tools/sctools/common/src/stream.h create mode 100644 build/tools/sctools/common/src/string_map_dwc_apinfo_type.inc create mode 100644 build/tools/sctools/common/src/string_map_ec_error.inc create mode 100644 build/tools/sctools/common/src/string_map_ec_op.inc create mode 100644 build/tools/sctools/common/src/string_map_ec_phase.inc create mode 100644 build/tools/sctools/common/src/text.c create mode 100644 build/tools/sctools/common/src/text.h create mode 100644 build/tools/sctools/common/src/wcm_control.c create mode 100644 build/tools/sctools/common/src/wcm_control.h create mode 100644 build/tools/sctools/copy_dst/Makefile create mode 100644 build/tools/sctools/copy_dst/banner/Makefile create mode 100644 build/tools/sctools/copy_dst/banner/banner_v3.bsf create mode 100644 build/tools/sctools/copy_dst/banner/icon/gameIcon.bmp create mode 100644 build/tools/sctools/copy_dst/banner/icon/subIcon.bmp create mode 100644 build/tools/sctools/copy_dst/banner/sub_banner_v3.bsf create mode 100644 build/tools/sctools/copy_dst/copy_dst.rsf create mode 100644 build/tools/sctools/copy_dst/src/main.c create mode 100644 build/tools/sctools/copy_org/Makefile create mode 100644 build/tools/sctools/copy_org/banner/Makefile create mode 100644 build/tools/sctools/copy_org/banner/banner_v3.bsf create mode 100644 build/tools/sctools/copy_org/banner/icon/gameIcon.bmp create mode 100644 build/tools/sctools/copy_org/banner/icon/subIcon.bmp create mode 100644 build/tools/sctools/copy_org/banner/sub_banner_v3.bsf create mode 100644 build/tools/sctools/copy_org/copy_org.rsf create mode 100644 build/tools/sctools/copy_org/src/main.c create mode 100644 build/tools/sctools/files/fanfare.32.wav diff --git a/build/tools/sctools/Makefile b/build/tools/sctools/Makefile new file mode 100644 index 0000000..ffdb626 --- /dev/null +++ b/build/tools/sctools/Makefile @@ -0,0 +1,30 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Date:: +# $Rev: +# $Author: +#---------------------------------------------------------------------------- + + + +SUBDIRS = copy_org copy_dst + + +include $(TWLSDK_ROOT)/build/buildtools/commondefs + +include $(TWLSDK_ROOT)/build/buildtools/modulerules + + + +#===== End of Makefile ===== diff --git a/build/tools/sctools/common/src/ap_info.h b/build/tools/sctools/common/src/ap_info.h new file mode 100644 index 0000000..7987379 --- /dev/null +++ b/build/tools/sctools/common/src/ap_info.h @@ -0,0 +1,52 @@ +/*---------------------------------------------------------------------------* + Project: TwlWiFi - demos - netconnect + File: ap_info.h + + Copyright 2005,2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: 2007-10-30#$ + $Rev: 74 $ + $Author: adachi_hiroaki $ + *---------------------------------------------------------------------------*/ + +#ifndef SHARED_AP_INFO_H_ +#define SHARED_AP_INFO_H_ + +#ifdef __cplusplus + +extern "C" { +#endif + +/*===========================================================================*/ +#include + +// AP 情報構造体 型定義 +typedef struct WcmControlApInfo +{ + u8 bssId[WCM_BSSID_SIZE]; + WCMWepDesc wepDesc; + u8 essId[WCM_ESSID_SIZE]; + u32 auth_option; + +} WcmControlApInfo; + +// 固定 AP 情報の参照 +extern const WcmControlApInfo constApInfo; + +/*===========================================================================*/ +#ifdef __cplusplus + +} /* extern "C" */ +#endif + +#endif /* SHARED_AP_INFO_H_ */ + +/*---------------------------------------------------------------------------* + End of file + *---------------------------------------------------------------------------*/ diff --git a/build/tools/sctools/common/src/ecdl.cpp b/build/tools/sctools/common/src/ecdl.cpp new file mode 100644 index 0000000..69c62d0 --- /dev/null +++ b/build/tools/sctools/common/src/ecdl.cpp @@ -0,0 +1,169 @@ +/*---------------------------------------------------------------------------* + Project: TwlSDK - tools - ecdl + File: main.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: 2008-09-24#$ + $Rev: 8618 $ + $Author: hatamoto_minoru $ + *---------------------------------------------------------------------------*/ + +#include "ecdl.h" + +#ifdef SDK_DEBUG +#define ECDL_LOG(msg) OS_TPrintf("----\nECDL-LOG: %s\n----\n", msg); +#endif +#ifdef SDK_RELEASE +#define ECDL_LOG(msg) OS_TPrintf("ECDL-LOG: %s\n", msg); +#endif + +namespace +{ + char CheckRegistration() + { + s32 progress; + ECError ecError; + ECDeviceInfo di; + + ECDL_LOG("check registeration"); + progress = EC_CheckRegistration(); + WaitEC(progress); + + ecError = EC_GetDeviceInfo(&di); + SDK_ASSERT( ecError == EC_ERROR_OK ); + +#ifdef SDK_DEBUG +#define ECDL_DI_FMT "%-30s" + OS_TPrintf(ECDL_DI_FMT " %d\n", "isKeyPairConfirmed", di.isKeyPairConfirmed); + OS_TPrintf(ECDL_DI_FMT " %d\n", "deviceId", di.deviceId); + OS_TPrintf(ECDL_DI_FMT " %s\n", "serial", di.serial); + OS_TPrintf(ECDL_DI_FMT " %s\n", "originalSerial", di.originalSerial); + OS_TPrintf(ECDL_DI_FMT " %s\n", "accountId", di.accountId); + OS_TPrintf(ECDL_DI_FMT " %s\n", "registrationStatus", di.registrationStatus); + OS_TPrintf(ECDL_DI_FMT " %s\n", "extAccountId", di.extAccountId); + OS_TPrintf(ECDL_DI_FMT " %s\n", "country", di.country); + OS_TPrintf(ECDL_DI_FMT " %s\n", "accountCountry", di.accountCountry); + OS_TPrintf(ECDL_DI_FMT " %s\n", "region", di.region); + OS_TPrintf(ECDL_DI_FMT " %s\n", "language", di.language); + OS_TPrintf(ECDL_DI_FMT " %d\n", "blockSize", di.blockSize); + OS_TPrintf(ECDL_DI_FMT " %d\n", "usedBlocks", di.usedBlocks); + OS_TPrintf(ECDL_DI_FMT " %d\n", "totalBlocks", di.totalBlocks); + OS_TPrintf(ECDL_DI_FMT " %d\n", "netContentRestrictions", di.netContentRestrictions); + OS_TPrintf(ECDL_DI_FMT " %d\n", "userAge", di.userAge); + OS_TPrintf(ECDL_DI_FMT " %d\n", "parentalControlFlags", di.parentalControlFlags); + OS_TPrintf(ECDL_DI_FMT " %d\n", "parentalControlOgn", di.parentalControlOgn); + OS_TPrintf(ECDL_DI_FMT " %d\n", "isParentalControlEnabled", di.isParentalControlEnabled); + OS_TPrintf(ECDL_DI_FMT " %d\n", "isNeedTicketSync", di.isNeedTicketSync); + OS_TPrintf(ECDL_DI_FMT " %d\n", "lastTicketSyncTime", di.lastTicketSyncTime); + OS_TPrintf(ECDL_DI_FMT " %d\n", "wirelessMACAddr", di.wirelessMACAddr); + OS_TPrintf(ECDL_DI_FMT " %d\n", "bluetoothMACAddr", di.bluetoothMACAddr); + OS_TPrintf(ECDL_DI_FMT " %d\n", "titleId", di.titleId); + OS_TPrintf(ECDL_DI_FMT " %d\n", "freeChannelAppCount", di.freeChannelAppCount); + OS_TPrintf(ECDL_DI_FMT " %d\n", "usedUserInodes", di.usedUserInodes); + OS_TPrintf(ECDL_DI_FMT " %d\n", "maxUserInodes", di.maxUserInodes); + OS_TPrintf(ECDL_DI_FMT " %s\n", "deviceCode", di.deviceCode); + OS_TPrintf(ECDL_DI_FMT " %s\n", "accountDeviceCode", di.accountDeviceCode); + OS_TPrintf(ECDL_DI_FMT " %d\n", "isNeedTicketSyncImportAll", di.isNeedTicketSyncImportAll); +#endif + + return di.registrationStatus[0]; + } + + void GetChallenge(char* challenge) + { + s32 progress; + ECError ecError; + + ECDL_LOG("get challenge"); + progress = EC_SendChallengeReq(); + WaitEC(progress); + + ecError = EC_GetChallengeResp(challenge); + SDK_ASSERT( ecError == EC_ERROR_OK ); + } + + void SyncRegistration(const char* challenge) + { + s32 progress; + + ECDL_LOG("sync registration"); + progress = EC_SyncRegistration(challenge); + WaitEC(progress); + } + + void Register(const char* challenge) + { + s32 progress; + + ECDL_LOG("register"); + progress = EC_Register(challenge, NULL, NULL); + WaitEC(progress); + } + + void Transfer(const char* challenge) + { + s32 progress; + + ECDL_LOG("transfer"); + progress = EC_Transfer(challenge); + WaitEC(progress); + } + + void SyncTickets() + { + s32 progress; + + ECDL_LOG("sync tickets"); + progress = EC_SyncTickets(EC_SYNC_TYPE_IMPORT_ALL); + WaitEC(progress); + } + + void DownloadTitles(const NAMTitleId* pTitleIds, u32 numTitleIds) + { + s32 progress; + + ECDL_LOG("download"); + for( u32 i = 0; i < numTitleIds; ++i ) + { + NAMTitleId tid = pTitleIds[i]; + + OS_TPrintf("download %08X %08X\n", (u32)(tid >> 32), (u32)tid); + progress = EC_DownloadTitle(tid, EC_DT_UPDATE_REQUIRED_CONTENTS); + WaitEC(progress); + } + } +} + + +void +ECDownload(const NAMTitleId* pTitleIds, u32 numTitleIds) +{ + char challenge[EC_CHALLENGE_BUF_SIZE]; + char status; + + status = CheckRegistration(); + // U unregistered + // R registered + // P pending + // T transfered + SDK_ASSERTMSG(status != 'U', "acount not transfered yet."); + SDK_ASSERTMSG(status != 'R', "already registered. please delete acount."); + SDK_ASSERTMSG( (status == 'P') || (status == 'T'), "invalid registration status '%c'", status ); + + GetChallenge(challenge); + Transfer(challenge); + + GetChallenge(challenge); + SyncRegistration(challenge); + + SyncTickets(); + DownloadTitles(pTitleIds, numTitleIds); +} + diff --git a/build/tools/sctools/common/src/ecdl.h b/build/tools/sctools/common/src/ecdl.h new file mode 100644 index 0000000..d4f7a47 --- /dev/null +++ b/build/tools/sctools/common/src/ecdl.h @@ -0,0 +1,43 @@ +/*---------------------------------------------------------------------------* + Project: TwlSDK - tools - ecdl + File: ecdl.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: 2008-09-19#$ + $Rev: 8593 $ + $Author: hatamoto_minoru $ + *---------------------------------------------------------------------------*/ +#ifndef ECDL_H_ +#define ECDL_H_ + +#define EC_NO_JS + +#include +#include +#include +#include "es.h" +#include "ecx.h" + +#define PRINT_LINE OS_TPrintf("%4d\n", __LINE__) + +#ifdef __cplusplus +extern "C" { +#endif + +void ECDownload(const NAMTitleId* pTitleIds, u32 numTitleIds); +void KPSClient(); +void WaitEC(ECOpId opId); + + +#ifdef __cplusplus +} +#endif + +#endif // ECDL_H_ diff --git a/build/tools/sctools/common/src/font.c b/build/tools/sctools/common/src/font.c new file mode 100644 index 0000000..cea8cdc --- /dev/null +++ b/build/tools/sctools/common/src/font.c @@ -0,0 +1,827 @@ +/*---------------------------------------------------------------------------* + Project: NitroSDK - demos - sio + File: font.c + + Copyright 2003,2004 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Log: font.c,v $ + Revision 1.1 2004/08/14 11:13:01 Miya + initial check in + + Revision 1.1 2004/06/19 10:36:03 Miya + initial check-in + + Revision 1.1 2004/06/19 05:30:25 Miya + initial check in + + Revision 1.1 2004/03/17 07:25:48 terui + initial upload + + $NoKeywords: $ + *---------------------------------------------------------------------------*/ + +#include +#include "font.h" + +const u32 d_CharData[0xe0 * 8] = { + 0x00000000,0x00000000,0x00000000,0x00000000, //0000 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x11111111,0x11111111,0x11111111,0x11111111, //0001 + 0x11111111,0x11111111,0x11111111,0x11111111, + 0x22222222,0x22222222,0x22222222,0x22222222, //0002 + 0x22222222,0x22222222,0x22222222,0x22222222, + 0x33333333,0x33333333,0x33333333,0x33333333, //0003 + 0x33333333,0x33333333,0x33333333,0x33333333, + 0x00000000,0x03333330,0x03333330,0x03333330, //0004 + 0x03333330,0x03333330,0x03333330,0x00000000, + 0x33333333,0x33333333,0x33333333,0x33333333, //0005 + 0x22222222,0x22222222,0x22222222,0x22222222, + 0x00000000,0x00003330,0x00033330,0x00333330, //0006 + 0x03333330,0x00333330,0x00033330,0x00003330, + 0x00000000,0x00000000,0x00003300,0x00033300, //0007 + 0x00333300,0x00033300,0x00003300,0x00000000, + 0x03333333,0x03333333,0x03333333,0x03333333, //0008 + 0x03333333,0x03333333,0x03333333,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0009 + 0x00000000,0x00000000,0x00000000,0x33333333, + 0x00000000,0x00000000,0x00000000,0x00000000, //000A + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //000B + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //000C + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //000D + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //000E + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //000F + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0010 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0011 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0012 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0013 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0014 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0015 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0016 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0017 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0018 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0019 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //001A + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //001B + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //001C + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //001D + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //001E + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //001F + 0x00000000,0x00000000,0x00000000,0x00000000, + + 0x00000000,0x00000000,0x00000000,0x00000000, //0020 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00011000,0x00011000,0x00011000,0x00011000, //0021 ! + 0x00000000,0x00011000,0x00011000,0x00000000, + 0x00011011,0x00011011,0x00010010,0x00000000, //0022 " + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00010100,0x00111110,0x00010100, //0023 # + 0x00010100,0x00111110,0x00010100,0x00000000, + 0x00001000,0x00111110,0x00001011,0x00111110, //0024 $ + 0x01101000,0x00111110,0x00001000,0x00000000, + 0x01100111,0x00110101,0x00011111,0x00001100, //0025 % + 0x01110110,0x01010011,0x01110001,0x00000000, + 0x00011100,0x00110110,0x00011100,0x00001110, //0026 & + 0x00011011,0x01110011,0x00111110,0x00000000, + 0x00000011,0x00000011,0x00000010,0x00000000, //0027 ' + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00011100,0x00000110,0x00000011,0x00000011, //0028 ( + 0x00000011,0x00000110,0x00011100,0x00000000, + 0x00011100,0x00110000,0x01100000,0x01100000, //0029 ) + 0x01100000,0x00110000,0x00011100,0x00000000, + 0x00000000,0x00000000,0x00001000,0x00101010, //002A * + 0x00011100,0x00101010,0x00001000,0x00000000, + 0x00000000,0x00000000,0x00001000,0x00001000, //002B + + 0x00111110,0x00001000,0x00001000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //002C , + 0x00000000,0x00000011,0x00000011,0x00000010, + 0x00000000,0x00000000,0x00000000,0x00000000, //002D - + 0x00111110,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //002E . + 0x00000000,0x00000011,0x00000011,0x00000000, + 0x01100000,0x00110000,0x00011000,0x00001100, //002F / + 0x00000110,0x00000011,0x00000001,0x00000000, + 0x00011100,0x00110010,0x01100011,0x01100011, //0030 0 + 0x01100011,0x00100110,0x00011100,0x00000000, + 0x00011000,0x00011100,0x00011110,0x00011000, //0031 1 + 0x00011000,0x00011000,0x00011000,0x00000000, + 0x00111110,0x01100011,0x01100011,0x00111000, //0032 2 + 0x00001110,0x00000011,0x01111111,0x00000000, + 0x01111110,0x00110000,0x00011000,0x00111100, //0033 3 + 0x01100000,0x01100011,0x00111110,0x00000000, + 0x00110000,0x00111000,0x00111100,0x00110110, //0034 4 + 0x00110011,0x01111111,0x00110000,0x00000000, + 0x00111111,0x00000011,0x00000011,0x00111111, //0035 5 + 0x01100000,0x01100011,0x00111110,0x00000000, + 0x00111100,0x00000110,0x00000011,0x00111111, //0036 6 + 0x01100011,0x01100011,0x00111110,0x00000000, + 0x01111111,0x01110000,0x00111000,0x00011100, //0037 7 + 0x00001100,0x00001100,0x00001100,0x00000000, + 0x00111110,0x01100011,0x01100011,0x00111110, //0038 8 + 0x01100011,0x01100011,0x00111110,0x00000000, + 0x00111110,0x01100011,0x01100011,0x01111110, //0039 9 + 0x01100000,0x00110000,0x00011110,0x00000000, + 0x00000000,0x00011000,0x00011000,0x00000000, //003A : + 0x00000000,0x00011000,0x00011000,0x00000000, + 0x00000000,0x00011000,0x00011000,0x00000000, //003B ; + 0x00000000,0x00011000,0x00011000,0x00010000, + 0x01100000,0x00111000,0x00001110,0x00000011, //003C < + 0x00001110,0x00111000,0x01100000,0x00000000, + 0x00000000,0x00000000,0x00111110,0x00000000, //003D = + 0x00000000,0x00111110,0x00000000,0x00000000, + 0x00000011,0x00001110,0x00111000,0x01100000, //003E > + 0x00111000,0x00001110,0x00000011,0x00000000, + 0x01111110,0x11000011,0x11000011,0x01111000, //003F ? + 0x00011000,0x00000000,0x00011000,0x00000000, + 0x00111110,0x01100011,0x01011001,0x01010101, //0040 @ + 0x01011101,0x01110011,0x00011110,0x00000000, + 0x00111110,0x01100011,0x01100011,0x01111111, //0041 A + 0x01100011,0x01100011,0x01100011,0x00000000, + 0x00111111,0x01100011,0x01100011,0x00111111, //0042 B + 0x01100011,0x01100011,0x00111111,0x00000000, + 0x00111110,0x01100011,0x01100011,0x00000011, //0043 C + 0x01100011,0x01100011,0x00111110,0x00000000, + 0x00111111,0x01100011,0x01100011,0x01100011, //0044 D + 0x01100011,0x01100011,0x00111111,0x00000000, + 0x01111111,0x00000011,0x00000011,0x00111111, //0045 E + 0x00000011,0x00000011,0x01111111,0x00000000, + 0x01111111,0x00000011,0x00000011,0x00111111, //0046 F + 0x00000011,0x00000011,0x00000011,0x00000000, + 0x00111110,0x01100011,0x00000011,0x01111011, //0047 G + 0x01100011,0x01100011,0x00111110,0x00000000, + 0x01100011,0x01100011,0x01100011,0x01111111, //0048 H + 0x01100011,0x01100011,0x01100011,0x00000000, + 0x00011000,0x00011000,0x00011000,0x00011000, //0049 I + 0x00011000,0x00011000,0x00011000,0x00000000, + 0x01100000,0x01100000,0x01100000,0x01100000, //004A J + 0x01100000,0x01100011,0x00111110,0x00000000, + 0x01100011,0x01110011,0x00111011,0x00011111, //004B K + 0x00111011,0x01110011,0x01100011,0x00000000, + 0x00000011,0x00000011,0x00000011,0x00000011, //004C L + 0x00000011,0x00000011,0x01111111,0x00000000, + 0x01100011,0x01100011,0x01110111,0x01110111, //004D M + 0x01111111,0x01101011,0x01101011,0x00000000, + 0x01100011,0x01100111,0x01101111,0x01111111, //004E N + 0x01111011,0x01110011,0x01100011,0x00000000, + 0x00111110,0x01100011,0x01100011,0x01100011, //004F O + 0x01100011,0x01100011,0x00111110,0x00000000, + 0x00111111,0x01100011,0x01100011,0x00111111, //0050 P + 0x00000011,0x00000011,0x00000011,0x00000000, + 0x00111110,0x01100011,0x01100011,0x01100011, //0051 Q + 0x01100011,0x00111110,0x01110000,0x00000000, + 0x00111111,0x01100011,0x01100011,0x00111111, //0052 R + 0x01100011,0x01100011,0x01100011,0x00000000, + 0x00111110,0x01100011,0x00000011,0x00111110, //0053 S + 0x01100000,0x01100011,0x00111110,0x00000000, + 0x11111111,0x00011000,0x00011000,0x00011000, //0054 T + 0x00011000,0x00011000,0x00011000,0x00000000, + 0x01100011,0x01100011,0x01100011,0x01100011, //0055 U + 0x01100011,0x01100011,0x00111110,0x00000000, + 0x01100011,0x01100011,0x00110110,0x00110110, //0056 V + 0x00011100,0x00011100,0x00001000,0x00000000, + 0x11011011,0x11011011,0x11011011,0x11011011, //0057 W + 0x11011011,0x01111110,0x01100110,0x00000000, + 0x01000001,0x01100011,0x00110110,0x00011100, //0058 X + 0x00011100,0x00110110,0x01100011,0x00000000, + 0x11000011,0x11000011,0x11100111,0x01111110, //0059 Y + 0x00011000,0x00011000,0x00011000,0x00000000, + 0x01111111,0x00110000,0x00011000,0x00001100, //005A Z + 0x00000110,0x00000011,0x01111111,0x00000000, + 0x00001111,0x00000011,0x00000011,0x00000011, //005B [ + 0x00000011,0x00000011,0x00001111,0x00000000, + 0x01100110,0x01100110,0x11111111,0x00011000, //005C \. + 0x11111111,0x00011000,0x00011000,0x00000000, + 0x01111000,0x01100000,0x01100000,0x01100000, //005D ] + 0x01100000,0x01100000,0x01111000,0x00000000, + 0x00011100,0x00110110,0x00100010,0x00000000, //005E ^ + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //005F _ + 0x00000000,0x00000000,0x01111111,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0060 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x01111000,0x01100100, //0061 a + 0x01100110,0x01110110,0x01101110,0x00000000, + 0x00000110,0x00000110,0x00111110,0x01100110, //0062 b + 0x01100110,0x01100110,0x00111110,0x00000000, + 0x00000000,0x00000000,0x00111100,0x01100110, //0063 c + 0x00000110,0x01100110,0x00111100,0x00000000, + 0x01100000,0x01100000,0x01111100,0x01100110, //0064 d + 0x01100110,0x01100110,0x01111100,0x00000000, + 0x00000000,0x00000000,0x00111100,0x01100110, //0065 e + 0x01111110,0x00000110,0x00111100,0x00000000, + 0x01110000,0x00011000,0x01111110,0x00011000, //0066 f + 0x00011000,0x00011000,0x00011000,0x00000000, + 0x00000000,0x00000000,0x00111100,0x01100110, //0067 g + 0x01100110,0x01111100,0x01100000,0x00111110, + 0x00000110,0x00000110,0x00000110,0x00111110, //0068 h + 0x01100110,0x01100110,0x01100110,0x00000000, + 0x00011000,0x00011000,0x00000000,0x00011000, //0069 i + 0x00011000,0x00011000,0x00011000,0x00000000, + 0x00110000,0x00110000,0x00000000,0x00110000, //006A j + 0x00110000,0x00110000,0x00110011,0x00011110, + 0x00000011,0x01100011,0x00110011,0x00011111, //006B k + 0x00011111,0x00110011,0x01100011,0x00000000, + 0x00011000,0x00011000,0x00011000,0x00011000, //006C l + 0x00011000,0x00011000,0x00011000,0x00000000, + 0x00000000,0x00000000,0x01111111,0x11011011, //006D m + 0x11011011,0x11011011,0x11011011,0x00000000, + 0x00000000,0x00000000,0x00111110,0x01100110, //006E n + 0x01100110,0x01100110,0x01100110,0x00000000, + 0x00000000,0x00000000,0x00111100,0x01100110, //006F o + 0x01100110,0x01100110,0x00111100,0x00000000, + 0x00000000,0x00000000,0x00111110,0x01100110, //0070 p + 0x01100110,0x00111110,0x00000110,0x00000110, + 0x00000000,0x00000000,0x01111100,0x01100110, //0071 q + 0x01100110,0x01111100,0x01100000,0x01100000, + 0x00000000,0x00000000,0x00101100,0x00011100, //0072 r + 0x00001100,0x00001100,0x00001100,0x00000000, + 0x00000000,0x00000000,0x00111100,0x00000110, //0073 s + 0x00111100,0x01100000,0x00111100,0x00000000, + 0x00000000,0x00011000,0x00111100,0x00011000, //0074 t + 0x00011000,0x00011000,0x00110000,0x00000000, + 0x00000000,0x00000000,0x01100110,0x01100110, //0075 u + 0x01100110,0x01100110,0x01111100,0x00000000, + 0x00000000,0x00000000,0x01100110,0x01100110, //0076 v + 0x01100110,0x00111100,0x00011000,0x00000000, + 0x00000000,0x00000000,0x11011011,0x11011011, //0077 w + 0x11011011,0x11011011,0x01111110,0x00000000, + 0x00000000,0x00000000,0x01100110,0x00111100, //0078 x + 0x00011000,0x00111100,0x01100110,0x00000000, + 0x00000000,0x00000000,0x01100110,0x01100110, //0079 y + 0x01100110,0x01111100,0x01100000,0x00111110, + 0x00000000,0x00000000,0x01111110,0x00110000, //007A z + 0x00011000,0x00001100,0x01111110,0x00000000, + 0x00011100,0x00000110,0x00000110,0x00000111, //007B { + 0x00000110,0x00000110,0x00011100,0x00000000, + 0x00001000,0x00001000,0x00001000,0x00001000, //007C | + 0x00001000,0x00001000,0x00001000,0x00000000, + 0x00011100,0x00110000,0x00110000,0x01110000, //007D } + 0x00110000,0x00110000,0x00011100,0x00000000, + 0x01101100,0x00111110,0x00011011,0x00000000, //007E ~ + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //007F + 0x00000000,0x00000000,0x00000000,0x00000000, + + 0x00000000,0x00000000,0x00000000,0x00000000, //0080 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0081 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0082 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0083 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0084 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0085 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0086 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0087 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0088 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0089 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //008A + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //008B + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //008C + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //008D + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //008E + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //008F + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0090 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0091 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0092 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0093 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0094 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0095 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0096 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0097 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0098 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //0099 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //009A + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //009B + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //009C + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //009D + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //009E + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //009F + 0x00000000,0x00000000,0x00000000,0x00000000, + + 0x00000000,0x00000000,0x00000000,0x00000000, //00A0 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //00A1 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //00A2 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //00A3 + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, //00A4 + 0x00000000,0x00000000,0x00000000,0x00000000, + + 0x00000000,0x00000000,0x00000000,0x00001100, //00A5 ・ + 0x00001100,0x00000000,0x00000000,0x00000000, + 0x01111110,0x01000000,0x01111110,0x01000000, //00A6 ヲ + 0x01100000,0x00110000,0x00011100,0x00000000, + 0x00000000,0x00000000,0x00111110,0x00101000, //00A7 ァ + 0x00011000,0x00001000,0x00000100,0x00000000, + 0x00000000,0x00000000,0x00100000,0x00011000, //00A8 ィ + 0x00001110,0x00001000,0x00001000,0x00000000, + 0x00000000,0x00000000,0x00001000,0x00111110, //00A9 ゥ + 0x00100010,0x00110000,0x00011100,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00011100, //00AA ェ + 0x00001000,0x00001000,0x00111110,0x00000000, + 0x00000000,0x00000000,0x00010000,0x00111110, //00AB ォ + 0x00011000,0x00010100,0x00010010,0x00000000, + 0x00000000,0x00000000,0x00000100,0x00111110, //00AC ャ + 0x00100100,0x00010100,0x00000100,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00011100, //00AD ュ + 0x00010000,0x00010000,0x01111110,0x00000000, + 0x00000000,0x00000000,0x00111100,0x00100000, //00AE ョ + 0x00111100,0x00100000,0x00111100,0x00000000, + 0x00000000,0x00000000,0x00001010,0x00101010, //00AF ッ + 0x00100000,0x00110000,0x00011100,0x00000000, + 0x00000000,0x00000000,0x00000000,0x01111110, //00B0 ー + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x01111111,0x01000000,0x01100100,0x00111100, //00B1 ア + 0x00000100,0x00000110,0x00000011,0x00000000, + 0x01100000,0x00110000,0x00011100,0x00010111, //00B2 ィ + 0x00010000,0x00010000,0x00010000,0x00000000, + 0x00001000,0x01111111,0x01000001,0x01000001, //00B3 ウ + 0x01100000,0x00110000,0x00011110,0x00000000, + 0x00000000,0x00111110,0x00001000,0x00001000, //00B4 エ + 0x00001000,0x00001000,0x01111111,0x00000000, + 0x00100000,0x01111111,0x00101000,0x00101100, //00B5 オ + 0x00100110,0x00100011,0x00110000,0x00000000, + 0x00000100,0x01111111,0x01000100,0x01000100, //00B6 カ + 0x01000100,0x01100110,0x00110011,0x00000000, + 0x00000100,0x01111111,0x00001000,0x00001000, //00B7 キ + 0x01111111,0x00010000,0x00010000,0x00000000, + 0x01111110,0x01000010,0x01000011,0x01000000, //00B8 ク + 0x01100000,0x00110000,0x00011110,0x00000000, + 0x00000010,0x01111110,0x00010010,0x00010001, //00B9 ケ + 0x00010000,0x00011000,0x00001110,0x00000000, + 0x01111111,0x01000000,0x01000000,0x01000000, //00BA コ + 0x01000000,0x01000000,0x01111111,0x00000000, + 0x00100010,0x01111111,0x00100010,0x00100010, //00BB サ + 0x00100000,0x00110000,0x00011110,0x00000000, + 0x00000011,0x01000000,0x01000011,0x01000000, //00BC シ + 0x01000000,0x01100000,0x00111111,0x00000000, + 0x00111111,0x00100000,0x00100000,0x00010000, //00BD ス + 0x00001000,0x00010100,0x01100011,0x00000000, + 0x00000010,0x01111111,0x01000010,0x01100010, //00BE セ + 0x00100010,0x00000110,0x01111100,0x00000000, + 0x01000001,0x01000011,0x01000010,0x01100000, //00BF ソ. + 0x00110000,0x00011000,0x00001100,0x00000000, + 0x01111110,0x01000010,0x01000011,0x01111000, //00C0 タ + 0x01000000,0x01100000,0x00111110,0x00000000, + 0x00110000,0x00011110,0x00010000,0x01111111, //00C1 チ + 0x00010000,0x00011000,0x00001110,0x00000000, + 0x01000101,0x01000101,0x01000101,0x01000000, //00C2 ツ + 0x01100000,0x00110000,0x00011110,0x00000000, + 0x00111110,0x00000000,0x01111111,0x00001000, //00C3 テ + 0x00001000,0x00001100,0x00000110,0x00000000, + 0x00000010,0x00000010,0x00000010,0x00011110, //00C4 ト + 0x00100010,0x00000010,0x00000010,0x00000000, + 0x00001000,0x00001000,0x01111111,0x00001000, //00C5 ナ + 0x00001000,0x00001100,0x00000111,0x00000000, + 0x00111110,0x00000000,0x00000000,0x00000000, //00C6 ニ + 0x00000000,0x00000000,0x01111111,0x00000000, + 0x00111111,0x00100000,0x00110000,0x00011010, //00C7 ヌ + 0x00001100,0x00010110,0x00100011,0x00000000, + 0x00001000,0x01111111,0x01100000,0x00110000, //00C8 ネ + 0x00011100,0x01101010,0x01001001,0x00000000, + 0x00100000,0x00100000,0x00100000,0x00100000, //00C9 ノ + 0x00110000,0x00011000,0x00001110,0x00000000, + 0x00011000,0x00110000,0x01100001,0x01000001, //00CA ハ + 0x01000001,0x01000001,0x01000001,0x00000000, + 0x00000001,0x00000001,0x00111111,0x00000001, //00CB ヒ + 0x00000001,0x00000011,0x00111110,0x00000000, + 0x01111111,0x01000000,0x01000000,0x01000000, //00CC フ + 0x01100000,0x00110000,0x00011110,0x00000000, + 0x00000000,0x00000000,0x00001110,0x00011001, //00CD ヘ + 0x00110001,0x01100000,0x01000000,0x00000000, + 0x00001000,0x01111111,0x00001000,0x00001000, //00CE ホ + 0x01001001,0x01001001,0x01001001,0x00000000, + 0x01111111,0x01000000,0x01000000,0x01100011, //00CF マ + 0x00111110,0x00001100,0x00011000,0x00000000, + 0x00011111,0x01110000,0x00000110,0x00011100, //00D0 ミ + 0x00110000,0x00000111,0x01111100,0x00000000, + 0x00001100,0x00000110,0x00000010,0x01000011, //00D1 ム + 0x01000001,0x01000001,0x01111111,0x00000000, + 0x01000000,0x01100010,0x00110100,0x00011000, //00D2 メ + 0x00001100,0x00010110,0x00100011,0x00000000, + 0x01111111,0x00000100,0x01111111,0x00000100, //00D3 モ + 0x00000100,0x00001100,0x01111000,0x00000000, + 0x00000100,0x01111111,0x01000100,0x01100100, //00D4 ヤ + 0x00110100,0x00000100,0x00000100,0x00000000, + 0x00011110,0x00010000,0x00010000,0x00010000, //00D5 ユ + 0x00010000,0x00010000,0x01111111,0x00000000, + 0x01111110,0x01000000,0x01000000,0x01111110, //00D6 ヨ + 0x01000000,0x01000000,0x01111110,0x00000000, + 0x01111111,0x00000000,0x01111111,0x01000000, //00D7 ラ + 0x01000000,0x01100000,0x00111110,0x00000000, + 0x01000010,0x01000010,0x01000010,0x01000010, //00D8 リ + 0x01000000,0x01100000,0x00111100,0x00000000, + 0x00001010,0x00001010,0x00001010,0x00001010, //00D9 ル + 0x01001010,0x01101010,0x00111011,0x00000000, + 0x00000001,0x00000001,0x01000001,0x01100001, //00DA レ + 0x00110001,0x00011001,0x00001111,0x00000000, + 0x01111111,0x01000001,0x01000001,0x01000001, //00DB ロ + 0x01000001,0x01000001,0x01111111,0x00000000, + 0x01111111,0x01000001,0x01000001,0x01000000, //00DC ワ + 0x01100000,0x00110000,0x00011110,0x00000000, + 0x00000111,0x01000000,0x01000000,0x01000000, //00DD ン + 0x01100000,0x00110000,0x00011111,0x00000000, + 0x00001001,0x00010010,0x00000000,0x00000000, //00DE ゛ + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00001110,0x00001010,0x00001110,0x00000000, //00DF ゜ + 0x00000000,0x00000000,0x00000000,0x00000000, +}; + +#if 0 +const u32 d_PaletteData[16][16] = { + {0x0000,0x5294,0x0000,0x0000,}, // Black + {0x0000,0x001f,0x7c00,0x001f, + 0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x7c00}, + {0x0000,0x001f,0x7c00,0x001f, + 0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x7fff}, + {0x7fff,0x7c00,0x7c00,0x7c00,}, // Blue + {0x7fff,0x7c1f,0x0000,0x0000,}, // + {0x7fff,0x7fe0,0x0000,0x0000,}, // + {0x7fff,0x03ff,0x0000,0x0000,}, // + { 0x7c00,0x7fff,0x739c,0x6b5a,0x6318,0x5ad6,0x5294,0x4a52, // 0000h + 0x4210,0x39ce,0x318c,0x294a,0x2108,0x18c6,0x1084,0x0842}, + {0x7fff,0x0000,0x0000,0x0000,}, // + {0x7fff,0x0000,0x0000,0x0000,}, // + {0x7fff,0x0000,0x0000,0x0000,}, // + {0x7fff,0x0000,0x0000,0x0000,}, // + {0x7fff,0x0000,0x0000,0x0000,}, // + {0x7fff,0x0000,0x0000,0x0000,}, // + {0x7fff,0x0000,0x0000,0x0000,}, // + {0x7fff,0x001f,0x0000,0x0000,} // +}; + +#else +const u32 d_PaletteData[8 * 16] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, // black + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x001f0000, 0x00000000, 0x00000000, 0x00000000, // red + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x03e00000, 0x00000000, 0x00000000, 0x00000000, // green + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x7c000000, 0x00000000, 0x00000000, 0x00000000, // blue + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x03ff0000, 0x00000000, 0x00000000, 0x00000000, // yellow + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x7c1f0000, 0x00000000, 0x00000000, 0x00000000, // purple + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x7fe00000, 0x00000000, 0x00000000, 0x00000000, // light blue + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00180000, 0x00000000, 0x00000000, 0x00000000, // dark red + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x03000000, 0x00000000, 0x00000000, 0x00000000, // dark green + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x60000000, 0x00000000, 0x00000000, 0x00000000, // dark blue + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x03180000, 0x00000000, 0x00000000, 0x00000000, // dark yellow + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x60180000, 0x00000000, 0x00000000, 0x00000000, // dark purple + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x63000000, 0x00000000, 0x00000000, 0x00000000, // dark light blue + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x56b50000, 0x00000000, 0x00000000, 0x00000000, // gray + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x2d6b0000, 0x00000000, 0x00000000, 0x00000000, // dark gray + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x7fff0000, 0x00000000, 0x00000000, 0x00000000, // white + 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + +#endif + + +const u32 d_IconCharData[0x80 * 8] = { + 0x00000000,0x0003d300,0x32111100,0x111cb100, // 0000h + 0x11b91100,0xb99b1200,0x999c1300,0x99a11000, + 0x00000000,0x00000000,0x00000000,0x00000032, // 0001h + 0x11112211,0x1111111c,0x999abcb9,0x999999a9, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0002h + 0x04321111,0x21111111,0x11cba999,0xba999999, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0003h + 0x00000000,0x00000003,0x00000031,0x00003211, + 0x00000000,0x0003d300,0x32111100,0x111cb100, // 0004h + 0x11b91100,0xb99b1200,0x999c1300,0x99a11000, + 0x00000000,0x00000000,0x00000000,0x00000032, // 0005h + 0x11112211,0x1111111c,0x999abcb9,0x999999a9, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0006h + 0x04321111,0x21111111,0x11cba999,0xba999999, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0007h + 0x00000000,0x00000003,0x00000031,0x00003211, + 0x00000000,0x0003d300,0x32111100,0x111cb100, // 0008h + 0x11b91100,0xb99b1200,0x999c1300,0x99a11000, + 0x00000000,0x00000000,0x00000000,0x00000032, // 0009h + 0x11112211,0x1111111c,0x999abcb9,0x999999a9, + 0x00000000,0x00000000,0x00000000,0x00000000, // 000ah + 0x04321111,0x21111111,0x11cba999,0xba999999, + 0x00000000,0x00000000,0x00000000,0x00000000, // 000bh + 0x00000000,0x00000003,0x00000031,0x00003211, + 0x00000000,0x0003d300,0x32111100,0x111cb100, // 000ch + 0x11b91100,0xb99b1200,0x999c1300,0x99a11000, + 0x00000000,0x00000000,0x00000000,0x00000032, // 000dh + 0x11112211,0x1111111c,0x999abcb9,0x999999a9, + 0x00000000,0x00000000,0x00000000,0x00000000, // 000eh + 0x04321111,0x21111111,0x11cba999,0xba999999, + 0x00000000,0x00000000,0x00000000,0x00000000, // 000fh + 0x00000000,0x00000003,0x00000031,0x00003211, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0010h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0011h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0012h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0013h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0014h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0015h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0016h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0017h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0018h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0019h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 001ah + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 001bh + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 001ch + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 001dh + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 001eh + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 001fh + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x99b12000,0x9ab11300,0x999b1130,0x999a1114, // 0020h + 0x9999b113,0x9999a112,0x99999c11,0x99999b11, + 0x55557999,0x55555999,0x99755999,0x99855999, // 0021h + 0x99955999,0x99955999,0x99955999,0x99955999, + 0x99975555,0x99955555,0x99955799,0x99955899, // 0022h + 0x99955999,0x99955899,0x99955799,0x99955799, + 0x0003111b,0x00311ba9,0x0311b999,0x4111a999, // 0023h + 0x311b9999,0x211a9999,0x11c99999,0x11b99999, + 0x99b12000,0x9ab11300,0x999b1130,0x999a1114, // 0024h + 0x9999b113,0x9999a112,0x99999c11,0x99999b11, + 0x79999999,0x59999999,0x59999999,0x59999999, // 0025h + 0x59999999,0x59999999,0x59999999,0x59999999, + 0x99999997,0x99999995,0x99999995,0x99999995, // 0026h + 0x99999995,0x99999985,0x99999975,0x99999965, + 0x0003111b,0x00311ba9,0x0311b999,0x4111a999, // 0027h + 0x311b9999,0x211a9999,0x11c99999,0x11b99999, + 0x99b12000,0x9ab11300,0x999b1130,0x999a1114, // 0028h + 0x9999b113,0x9999a112,0x99999c11,0x99999b11, + 0x55557999,0x55557999,0x89999999,0x99999999, // 0029h + 0x99999999,0x99999999,0x89999999,0x55557999, + 0x99997555,0x99995555,0x99995567,0x99995579, // 002ah + 0x99995589,0x99995579,0x99995567,0x99995555, + 0x0003111b,0x00311ba9,0x0311b999,0x4111a999, // 002bh + 0x311b9999,0x211a9999,0x11c99999,0x11b99999, + 0x99b12000,0x9ab11300,0x999b1130,0x999a1114, // 002ch + 0x9999b113,0x9999a112,0x99999c11,0x99999b11, + 0x55557999,0x55557999,0x99999999,0x99999999, // 002dh + 0x99999999,0x99999999,0x99999999,0x55799999, + 0x99975555,0x99955555,0x99955678,0x99955799, // 002eh + 0x99955899,0x99955799,0x99955678,0x99955555, + 0x0003111b,0x00311ba9,0x0311b999,0x4111a999, // 002fh + 0x311b9999,0x211a9999,0x11c99999,0x11b99999, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0030h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0031h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0032h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0033h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0034h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0035h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0036h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0037h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0038h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0039h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 003ah + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 003bh + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 003ch + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 003dh + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 003eh + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 003fh + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x99999b11,0x99999c11,0x9999a112,0x9999b113, // 0040h + 0x999a1114,0x999b1130,0x9ab11300,0xb1113000, + 0x99955999,0x99955999,0x99955999,0x99955999, // 0041h + 0x99855999,0x99755999,0x55555999,0x55557999, + 0x99955579,0x99955559,0x99955559,0x99955559, // 0042h + 0x99955558,0x99955557,0x99955555,0x99975555, + 0x11b99999,0x11c99999,0x211a9999,0x311b9999, // 0043h + 0x4111a999,0x0311b999,0x00311ba9,0x0003111b, + 0x99999b11,0x99999c11,0x9999a112,0x9999b113, // 0044h + 0x999a1114,0x999b1130,0x9ab11300,0xb1113000, + 0x59999999,0x59999999,0x59999999,0x59999999, // 0045h + 0x59999999,0x59999999,0x59999999,0x79999999, + 0x99999755,0x99999555,0x99999555,0x99999555, // 0046h + 0x99999555,0x99999555,0x99999555,0x99999755, + 0x11b99999,0x11c99999,0x211a9999,0x311b9999, // 0047h + 0x4111a999,0x0311b999,0x00311ba9,0x0003111b, + 0x99999b11,0x99999c11,0x9999a112,0x9999b113, // 0048h + 0x999a1114,0x999b1130,0x9ab11300,0xb1113000, + 0x55555999,0x65555999,0x75555999,0x85555999, // 0049h + 0x75555999,0x65555999,0x55555999,0x55557999, + 0x99997555,0x99999987,0x99999999,0x99999999, // 004ah + 0x99999999,0x99999987,0x99975555,0x99975555, + 0x11b99999,0x11c99999,0x211a9999,0x311b9999, // 004bh + 0x4111a999,0x0311b999,0x00311ba9,0x0003111b, + 0x99999b11,0x99999c11,0x9999a112,0x9999b113, // 004ch + 0x999a1114,0x999b1130,0x9ab11300,0xb1113000, + 0x55799999,0x78999999,0x99999999,0x99999999, // 004dh + 0x99999999,0x78999999,0x55557999,0x55557999, + 0x99955555,0x99955556,0x99955557,0x99955558, // 004eh + 0x99955557,0x99955556,0x99955555,0x99975555, + 0x11b99999,0x11c99999,0x211a9999,0x311b9999, // 004fh + 0x4111a999,0x0311b999,0x00311ba9,0x0003111b, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0050h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0051h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0052h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0053h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0054h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0055h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0056h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0057h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0058h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0059h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 005ah + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 005bh + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 005ch + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 005dh + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 005eh + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 005fh + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x11230000,0x13000000,0x30000000,0x00000000, // 0060h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x999999ab,0x999abc11,0x11111112,0x11112340, // 0061h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0xba999999,0x11cba999,0x21111111,0x04321111, // 0062h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00003211,0x00000031,0x00000003,0x00000000, // 0063h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x11230000,0x13000000,0x30000000,0x00000000, // 0064h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x999999ab,0x999abc11,0x11111112,0x11112340, // 0065h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0xba999999,0x11cba999,0x21111111,0x04321111, // 0066h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00003211,0x00000031,0x00000003,0x00000000, // 0067h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x11230000,0x13000000,0x30000000,0x00000000, // 0068h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x999999ab,0x999abc11,0x11111112,0x11112340, // 0069h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0xba999999,0x11cba999,0x21111111,0x04321111, // 006ah + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00003211,0x00000031,0x00000003,0x00000000, // 006bh + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x11230000,0x13000000,0x30000000,0x00000000, // 006ch + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x999999ab,0x999abc11,0x11111112,0x11112340, // 006dh + 0x00000000,0x00000000,0x00000000,0x00000000, + 0xba999999,0x11cba999,0x21111111,0x04321111, // 006eh + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00003211,0x00000031,0x00000003,0x00000000, // 006fh + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0070h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0071h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0072h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0073h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0074h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0075h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0076h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0077h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0078h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 0079h + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 007ah + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 007bh + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 007ch + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 007dh + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 007eh + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, // 007fh + 0x00000000,0x00000000,0x00000000,0x00000000 +}; + + +const u16 d_IconPaletteData[16 * 4] = { + 0x7c1f,0x7fff,0x5ef7,0x3def,0x1ce7,0x0000,0x0007,0x000f, // 0000h + 0x0017,0x001f,0x1cff,0x3dff,0x5eff,0x7fff,0x0000,0x0000, + 0x7c1f,0x7fff,0x5ef7,0x3def,0x1ce7,0x0000,0x00e0,0x01e0, // 0001h + 0x02e0,0x03e0,0x1fe7,0x3fef,0x5ff7,0x7fff,0x0000,0x0000, + 0x7c1f,0x7fff,0x5ef7,0x3def,0x1ce7,0x0000,0x1ce0,0x3de0, // 0002h + 0x5ee0,0x7fe0,0x7fe7,0x7fef,0x7ff7,0x7fff,0x0000,0x0000, + 0x7c1f,0x7fff,0x5ef7,0x3def,0x1ce7,0x0000,0x00e7,0x01ef, // 0003h + 0x02f7,0x03ff,0x1fff,0x3fff,0x5fff,0x7fff,0x0000,0x0000 + +}; + + + + diff --git a/build/tools/sctools/common/src/font.h b/build/tools/sctools/common/src/font.h new file mode 100644 index 0000000..046f655 --- /dev/null +++ b/build/tools/sctools/common/src/font.h @@ -0,0 +1,21 @@ +#ifndef _FONT_H_ +#define _FONT_H_ + +extern const u32 d_CharData[0xe0 * 8]; +extern const u32 d_PaletteData[8*16]; +extern const u32 d_IconCharData[0x80 * 8]; +extern const u16 d_IconPaletteData[16 * 4]; + +extern GXOamAttr g_oam[128]; + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/build/tools/sctools/common/src/gfx.c b/build/tools/sctools/common/src/gfx.c new file mode 100644 index 0000000..0d7dcd6 --- /dev/null +++ b/build/tools/sctools/common/src/gfx.c @@ -0,0 +1,160 @@ +#include "font.h" +#include "text.h" +#include "mprintf.h" +#include "gfx.h" + +static GXOamAttr g_oam[128]; +#define VRAM_SIZE 2*32*24 + +/* DISPLAY Control */ + +// #define TEXT_HEAPBUF_SIZE 0x16000 +#define TEXT_HEAPBUF_SIZE 0x80000 /* 512KByte */ +static u8 text_heap_buffer[TEXT_HEAPBUF_SIZE]; + +static TEXT_CTRL textctrl[NUM_OF_SCREEN]; +TEXT_CTRL *tc[NUM_OF_SCREEN]; +static int vram_num_main = 1; +static int vram_num_sub = 0; +static void VBlankIntr( void ); +static u32 v_blank_intr_counter = 0; + +static u32 g_screen[MAX_VRAM_NUM][VRAM_SIZE/sizeof(u32)]; + + +void Gfx_Init(void) +{ + int i; + u16 palette_no = 0x0f; + int line_buf_count; + + FX_Init(); + GX_Init(); + GX_DispOff(); + GXS_DispOff(); + + + + line_buf_count = init_text_buf_sys((void *)&(text_heap_buffer[0]), + (void *)&(text_heap_buffer[TEXT_HEAPBUF_SIZE])); + + // OS_TPrintf("Init start 1\n"); + for( i = 0 ; i < NUM_OF_SCREEN ; i++) { + tc[i] = &(textctrl[i]); + init_text(tc[i], (u16 *)&(g_screen[i]), palette_no); + } + + + + + // VRAM初期化 + GX_SetBankForLCDC(GX_VRAM_LCDC_ALL); + MI_CpuClearFast((void*)HW_LCDC_VRAM,HW_LCDC_VRAM_SIZE); + (void)GX_DisableBankForLCDC(); + MI_CpuFillFast((void*)HW_OAM,192,HW_OAM_SIZE); + MI_CpuClearFast((void*)HW_PLTT,HW_PLTT_SIZE); + MI_CpuFillFast((void*)HW_DB_OAM,192,HW_DB_OAM_SIZE); + MI_CpuClearFast((void*)HW_DB_PLTT,HW_DB_PLTT_SIZE); + + + + /* BG0表示設定 */ + GX_SetBankForBG(GX_VRAM_BG_128_A); + G2_SetBG0Control(GX_BG_SCRSIZE_TEXT_256x256, + GX_BG_COLORMODE_16, + GX_BG_SCRBASE_0xf000, // SCR base block 31 + GX_BG_CHARBASE_0x00000, // CHR base block 0 + GX_BG_EXTPLTT_01); + G2_SetBG0Priority(0); + G2_BG0Mosaic(FALSE); + // 2D表示設定 + GX_SetGraphicsMode(GX_DISPMODE_GRAPHICS,GX_BGMODE_0,GX_BG0_AS_2D); + GX_LoadBG0Char(d_CharData,0,sizeof(d_CharData)); + GX_LoadBGPltt(d_PaletteData,0,sizeof(d_PaletteData)); + // GX_SetBankForARM7(GX_VRAM_ARM7_128_D); + // OBJ表示設定 + GX_SetBankForOBJ(GX_VRAM_OBJ_128_B); + GX_SetOBJVRamModeChar(GX_OBJVRAMMODE_CHAR_2D); + GX_LoadOBJ(d_IconCharData,0,sizeof(d_IconCharData)); + GX_LoadOBJPltt(d_IconPaletteData,0,sizeof(d_IconPaletteData)); + + GX_SetVisiblePlane(GX_PLANEMASK_BG0 | GX_PLANEMASK_OBJ ); + + + /* BG1表示設定 */ + GX_SetBankForSubBG( GX_VRAM_SUB_BG_48_HI ); + G2S_SetBG0Control( + GX_BG_SCRSIZE_TEXT_256x256 , + GX_BG_COLORMODE_16 , + GX_BG_SCRBASE_0xf800 , // SCR ベースブロック 31 + GX_BG_CHARBASE_0x10000 , // CHR ベースブロック 0 + GX_BG_EXTPLTT_01 + ); + G2S_SetBG0Priority( 0 ); + G2S_BG0Mosaic( FALSE ); + GXS_SetGraphicsMode(GX_BGMODE_0); + GXS_LoadBG0Char(d_CharData,0,sizeof(d_CharData)); + GXS_LoadBGPltt(d_PaletteData,0,sizeof(d_PaletteData)); + + GX_SetBankForSubOBJ(GX_VRAM_SUB_OBJ_128_D); + GXS_SetOBJVRamModeChar(GX_OBJVRAMMODE_CHAR_2D); + + + GXS_LoadOBJ(d_IconCharData,0,sizeof(d_IconCharData)); + GXS_LoadOBJPltt(d_IconPaletteData,0,sizeof(d_IconPaletteData)); + GXS_SetVisiblePlane( GX_PLANEMASK_BG0 | GX_PLANEMASK_OBJ ); + + + // LCD表示開始 + GX_DispOn(); + GXS_DispOn(); + + for(i=0;i<128;i++){ + G2_SetOBJPosition(&(g_oam[i]),256,192); + } + + G2_SetBG0Offset( 0, 0 ); + G2S_SetBG0Offset( 0, 0 ); + + + // for V Blank + (void)OS_SetIrqFunction( OS_IE_V_BLANK, VBlankIntr ); + (void)OS_EnableIrqMask( OS_IE_V_BLANK ); + (void)GX_VBlankIntr( TRUE ); + +} + +static void VBlankIntr(void) +{ + if( vram_num_main < NUM_OF_SCREEN ) { + DC_FlushRange( (void *)&(g_screen[vram_num_main]),VRAM_SIZE); + } + GX_LoadBG0Scr( &(g_screen[vram_num_main]),0,VRAM_SIZE); + + if( vram_num_sub < NUM_OF_SCREEN ) { + DC_FlushRange( (void *)&(g_screen[vram_num_sub]),VRAM_SIZE); + } + GXS_LoadBG0Scr( (void *)&(g_screen[vram_num_sub]) , 0 , VRAM_SIZE ); + + // 仮想OAMをVRAMに反映 + DC_FlushRange(g_oam,sizeof(g_oam)); + GX_LoadOAM(g_oam,0,sizeof(g_oam)); + //---- 割り込みチェックフラグ + OS_SetIrqCheckFlag( OS_IE_V_BLANK ); + v_blank_intr_counter++; +} + + + +void Gfx_Render(int main_no, int sub_no) +{ + vram_num_main = main_no; + vram_num_sub = sub_no; + + if( -1 < vram_num_main && vram_num_main < NUM_OF_SCREEN ) { + text_buf_to_vram(tc[vram_num_main]); + } + if( -1 < vram_num_sub && vram_num_sub < NUM_OF_SCREEN ) { + text_buf_to_vram(tc[vram_num_sub]); + } +} diff --git a/build/tools/sctools/common/src/gfx.h b/build/tools/sctools/common/src/gfx.h new file mode 100644 index 0000000..cc8338e --- /dev/null +++ b/build/tools/sctools/common/src/gfx.h @@ -0,0 +1,25 @@ +#ifndef _GFX_H_ +#define _GFX_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +void Gfx_Init(void); +void Gfx_Render(int main_no, int sub_no); + +#ifdef __cplusplus +} +#endif + + + +#define MAX_VRAM_NUM (NUM_OF_SCREEN) + +extern TEXT_CTRL *tc[NUM_OF_SCREEN]; +extern int vram_num_main; +extern int vram_num_sub; + + +#endif /* _GFX_H_ */ diff --git a/build/tools/sctools/common/src/hatamotolib.cpp b/build/tools/sctools/common/src/hatamotolib.cpp new file mode 100644 index 0000000..045feeb --- /dev/null +++ b/build/tools/sctools/common/src/hatamotolib.cpp @@ -0,0 +1,717 @@ +#include "ecdl.h" +#include +#include +#include +#include + +#ifdef USE_DWC +#include +#include +#endif + +#include +#include + +#include "netconnect.h" +#include "sitedefs.h" +#include "wcm_control.h" + +#include "hatamotolib.h" + +static void *Alloc(size_t size) +{ + OSIntrMode old = OS_DisableInterrupts(); + void* p = OS_Alloc(size); + OS_RestoreInterrupts(old); + return p; +} + +static void Free(void* ptr) +{ + if( ptr != NULL ) + { + OSIntrMode old = OS_DisableInterrupts(); + OS_Free(ptr); + OS_RestoreInterrupts(old); + } +} + +#ifdef USE_DWC +static void* AllocForDWC(DWCAllocType name, u32 size, int align) +{ + SDK_ASSERT(align <= 32);(void)name;(void)align; return Alloc(static_cast(size)); +} + +static void FreeForDWC(DWCAllocType name, void* ptr, u32 size) +{ + (void)name;(void)size; Free(ptr); +} +#endif + +static void* AllocForNHTTP(u32 size, int align) { SDK_ASSERT(align <= 32);(void)align; return Alloc(size); } +static void* AllocForEC (u32 size, int align) { SDK_ASSERT(align <= 32);(void)align; return Alloc(size); } +static void* AllocForNSSL (u32 size) { return Alloc(size); } +static void* AllocForNAM (u32 size) { return Alloc(size); } +static void FreeForNHTTP (void* p) { Free(p); } +static void FreeForEC (void* p) { Free(p); } +static void FreeForNSSL (void* p) { Free(p); } +static void FreeForNAM (void* p) { Free(p); } +static void* ReallocForNSSL(void* p, u32 size) +{ + if( p != NULL ) + { + void* newp = Alloc(size); + MI_CpuCopy8(p, newp, size); + Free(p); + return newp; + } + else + { + return Alloc(size); + } +} + + +static void PrintBytes(const void* pv, u32 size) +{ + const u8* p = reinterpret_cast(pv); + + for( u32 i = 0; i < size; ++i ) + { + OS_TPrintf("%02X", p[i]); + } +} + + +struct StringMap +{ + int value; + const char* string; +}; + +static const char* +FindString(const StringMap* pMap, int value) +{ + while( pMap->string != NULL ) + { + if( pMap->value == value ) + { + return pMap->string; + } + + pMap++; + } + + return "unknwon value"; +} + + +static const char* GetECErrorString(ECError err) +{ + static const StringMap STRING_MAP[] = + { +#include "string_map_ec_error.inc" + { 0, NULL } + }; + + return FindString(STRING_MAP, err); +} + +static const char* +GetECOperationString(ECOperation op) +{ + static const StringMap STRING_MAP[] = + { +#include "string_map_ec_op.inc" + { 0, NULL } + }; + + return FindString(STRING_MAP, op); +} + +static const char* +GetECOpPhaseString(ECOpPhase phase) +{ + static const StringMap STRING_MAP[] = + { +#include "string_map_ec_phase.inc" + { 0, NULL } + }; + + return FindString(STRING_MAP, phase); +} + + +#ifdef USE_DWC +static const char* +GetDWCApInfoTypeString(DWCApInfoType type) +{ + static const StringMap STRING_MAP[] = + { +#include "string_map_dwc_apinfo_type.inc" + { 0, NULL } + }; + + return FindString(STRING_MAP, type); +} +#endif + + +static const char* +GetOSTWLRegionString(u8 x) +{ + static const StringMap STRING_MAP[] = + { + { OS_TWL_REGION_JAPAN, "japan" }, + { OS_TWL_REGION_AMERICA, "america" }, + { OS_TWL_REGION_EUROPE, "europe" }, + { OS_TWL_REGION_AUSTRALIA, "australia" }, + { OS_TWL_REGION_CHINA, "china" }, + { OS_TWL_REGION_KOREA, "korea" }, + { 0, NULL } + }; + + return FindString(STRING_MAP, x); +} + + +void SetupUserInfo(void) +{ + BOOL bSuccess; + BOOL bModified = FALSE; + void* pWork; + + // LCFG_ReadTWLSettings を行う前に LCFG_ReadHWSecureInfo が必要 + { + bSuccess = LCFG_ReadHWSecureInfo(); + SDK_ASSERT(bSuccess); + } + + // 設定の読み込み + { + pWork = Alloc(LCFG_READ_TEMP); + SDK_POINTER_ASSERT(pWork); + + bSuccess = LCFG_ReadTWLSettings(reinterpret_cast(pWork)); + SDK_ASSERT(bSuccess); + + Free(pWork); + } + + { + // ニックネームが空なら適当に設定 + if( *LCFG_TSD_GetNicknamePtr() == L'\0' ) + { + LCFG_TSD_SetNickname(reinterpret_cast(L"ecdl")); + bModified = TRUE; + } + + // 国が選択されていないなら適当に設定 + if( LCFG_TSD_GetCountry() == LCFG_TWL_COUNTRY_UNDEFINED ) + { + LCFG_TSD_SetCountry(LCFG_TWL_COUNTRY_JAPAN); + bModified = TRUE; + } + } + + // 設定が変更されているなら書き出す + if( bModified ) + { + pWork = Alloc(LCFG_WRITE_TEMP); + SDK_POINTER_ASSERT(pWork); + + bSuccess = LCFG_WriteTWLSettings(reinterpret_cast(pWork)); + SDK_ASSERT(bSuccess); + + Free(pWork); + } +} + +void SetupVerData(void) +{ + BOOL bSuccess; + + void* pWork = Alloc(NA_VERSION_DATA_WORK_SIZE); + SDK_POINTER_ASSERT(pWork); + + bSuccess = NA_LoadVersionDataArchive(pWork, NA_VERSION_DATA_WORK_SIZE); + + SDK_ASSERT(bSuccess); +} + + + +void PrintDeviceInfo(void) +{ + ESId deviceId; + u8 region; + u32 launcherInitialCode; + const u8* pSerial; + const u8* pMovable; + u32 movableLen1; + u32 movableLen2; + u64 fuseId; + char bmsDeviceId[32]; + + LCFG_ReadHWNormalInfo(); + LCFG_ReadHWSecureInfo(); + + // region + region = LCFG_THW_GetRegion(); + + // launcher initial code + LCFG_THW_GetLauncherTitleID_Lo(reinterpret_cast(&launcherInitialCode)); + + // ES Device ID + ES_GetDeviceId(&deviceId); + snprintf(bmsDeviceId, sizeof(bmsDeviceId), "%lld", ((0x3ull << 32) | deviceId)); + + // serial + pSerial = LCFG_THW_GetSerialNoPtr(); + + // movable id + pMovable = LCFG_THW_GetMovableUniqueIDPtr(); + movableLen1 = LCFG_TWL_HWINFO_MOVABLE_UNIQUE_ID_LEN / 2; + movableLen2 = LCFG_TWL_HWINFO_MOVABLE_UNIQUE_ID_LEN - movableLen1; + + // fuse id + fuseId = SCFG_ReadFuseData(); + + OS_TPrintf("Region: %-10s %08X\n", GetOSTWLRegionString(region), launcherInitialCode); + OS_TPrintf("DeviceID: %08X %u\n", deviceId, deviceId); + OS_TPrintf(" %s\n", bmsDeviceId); + OS_TPrintf("Serial: %s\n", pSerial); + OS_TPrintf("MovableID: "); PrintBytes(pMovable, movableLen1); OS_TPrintf("\n"); + OS_TPrintf(" "); PrintBytes(pMovable + movableLen1, movableLen2); OS_TPrintf("\n"); + OS_TPrintf("FuseID: %08X%08X\n", static_cast(fuseId >> 32), static_cast(fuseId)); +} + +void SetupTitlesDataFile(const NAMTitleId* pTitleIds, u32 numTitleIds) +{ + for( u32 i = 0; i < numTitleIds; ++i ) + { + NAMTitleId tid = pTitleIds[i]; + NAM_SetupTitleDataFile(tid); + } +} + +void DeleteECDirectory(void) +{ + char buf[64]; + OSTitleId tid = OS_GetTitleId(); + + STD_TSNPrintf(buf, sizeof(buf), "nand:/title/%08x/%08x", + (u32)(tid >> 32), (u32)(tid >> 0) ); + + FS_DeleteDirectoryAuto(buf); +} + +void SetupShopTitleId(void) +{ + *(u64 *)(HW_TWL_ROM_HEADER_BUF + 0x230) = 0x00030015484E4641ull; +} + + +#ifdef USE_DWC +static u8 sDwcWork[ DWC_INIT_WORK_SIZE ] ATTRIBUTE_ALIGN(32); +static DWCInetControl sDwcInetCtrl; + +static void PollConnection_DWC() +{ + int errCode; + int counter = 0; + while( ! DWC_CheckInet() ) + { + DWC_ProcessInet(); + OS_Sleep(16); + OS_TPrintf("*** %s %d %d\n",__FUNCTION__, __LINE__, counter); + counter++; + } + + OS_TPrintf("*** %s %d\n",__FUNCTION__, __LINE__); + + switch ( DWC_GetInetStatus() ) + { + case DWC_CONNECTINET_STATE_NOT_INITIALIZED: + OS_TPrintf(" DWC_CONNECTINET_STATE_NOT_INITIALIZED\n" ); + break; + case DWC_CONNECTINET_STATE_IDLE: + OS_TPrintf(" DWC_CONNECTINET_STATE_IDLE \n" ); + break; + case DWC_CONNECTINET_STATE_OPERATING: + OS_TPrintf(" DWC_CONNECTINET_STATE_OPERATING \n" ); + break; + case DWC_CONNECTINET_STATE_OPERATED: + OS_TPrintf(" DWC_CONNECTINET_STATE_OPERATED \n" ); + break; + case DWC_CONNECTINET_STATE_CONNECTED: + OS_TPrintf(" DWC_CONNECTINET_STATE_CONNECTED \n" ); + break; + case DWC_CONNECTINET_STATE_DISCONNECTING: + OS_TPrintf(" DWC_CONNECTINET_STATE_DISCONNECTING \n" ); + break; + case DWC_CONNECTINET_STATE_DISCONNECTED: + OS_TPrintf(" DWC_CONNECTINET_STATE_DISCONNECTED %d\n"); + break; + case DWC_CONNECTINET_STATE_ERROR: + DWC_GetLastError(&errCode); + OS_Panic(" DWC_CONNECTINET_STATE_ERROR %d\n", errCode ); + break; + case DWC_CONNECTINET_STATE_FATAL_ERROR: + DWC_GetLastError(&errCode); + OS_Panic(" DWC_CONNECTINET_STATE_FATAL_ERROR %d\n", errCode ); + break; + default: + DWC_GetLastError(&errCode); + OS_Panic(" DWC_CONNECTINET_STATE_UNKNOWN_ERROR %d\n", errCode ); + } +} + +static bool ConnectionResult_DWC() +{ + DWCApInfo apinfo; + + if ( DWC_GetApInfo( &apinfo ) == TRUE ) + { + OS_TPrintf(" AP type: %s\n", GetDWCApInfoTypeString(apinfo.aptype)); + OS_TPrintf(" ESSID : %s\n", &apinfo.essid); + return true; + } + else + { + DWCError error; + int errorCode; + DWCErrorType errorType; + // 接続失敗のエラーコード表示 + error = DWC_GetLastErrorEx( &errorCode, &errorType ); + OS_TPrintf(" error point : %d\n", error ); + OS_TPrintf(" error no : %d\n", -errorCode ); + OS_TPrintf(" error type : %d\n", errorType ); + + return false; + } +} + + + +void NetworkAutoConnect_DWC(void) +{ + + DWC_SetReportLevel(DWC_REPORTFLAG_ALL); + + int result = DWC_Init(sDwcWork); + + if ( result == DWC_INIT_RESULT_DESTROY_OTHER_SETTING ) + { + OS_TPrintf( "Wi-Fi setting might be broken.\n" ); + } + + DWC_SetMemFunc( &AllocForDWC, &FreeForDWC ); + + DWC_InitInet( &sDwcInetCtrl ); + + DWC_SetDisableEulaCheck(); + + DWC_ConnectInetAsync(); + + + PollConnection_DWC(); + + if( ! ConnectionResult_DWC() ) + { + OS_Panic("auto connect failed"); + } +} + +void NetworkShutdown_DWC(void) +{ + DWC_CleanupInet(); +} +#endif + +void SetupNSSL(void) +{ + NSSLConfig conf; + + conf.maxId = 8; + conf.alloc = AllocForNSSL; + conf.free = FreeForNSSL; + conf.realloc = ReallocForNSSL; + conf.fixedHeapSize = 0; + NSSL_Init(&conf); +} + +void SetupNHTTP(void) +{ + int rv; + + rv = NHTTPStartup(AllocForNHTTP, FreeForNHTTP, 18); + NHTTP_SetBuiltinRootCAAsDefault(NSSL_ROOTCA_NINTENDO_2); + + if (rv != NHTTP_ERROR_NONE) + { + OS_Panic("Failed to start NHTTP, rv=%d\n", rv); + } +} + + + +static void +GetDeviceCode(char *deviceCode, u32 bufSize) +{ + u64 eFuseData; + u32 i; + u8 digit; + + /* Fake the device code, seeded from the eFuse data */ + SCFG_Init(); + eFuseData = SCFG_ReadFuseData(); + + for (i = 0; i < (bufSize - 1); i++) { + digit = (u8) (eFuseData % 10); + deviceCode[i] = (char) (digit + 48); + eFuseData /= 10; + } + + deviceCode[bufSize - 1] = '\0'; +} + +static void +Dummy_WWW_AddJSPlugin() +{ +} + +static void +LoadCert(void** ppCert, u32* pSize, const char* name) +{ + FSFile f; + BOOL bSuccess; + s32 readSize; + s32 result; + char path[64]; + + void* pCert; + u32 certSize; + + FS_InitFile(&f); + + STD_TSNPrintf(path, sizeof(path), "verdata:/%s", name); + + bSuccess = FS_OpenFile(&f, path); + if( ! bSuccess ) + { + OS_Panic("Cannot open %s\n", path); + } + + certSize = FS_GetFileLength(&f); + pCert = OS_Alloc(certSize); + if ( pCert == NULL ) + { + OS_Panic("Cannot allocate work memroy\n"); + } + + readSize = FS_ReadFile(&f, pCert, static_cast(certSize)); + if( readSize != certSize ) + { + OS_Panic("fail to read file\n"); + } + + FS_CloseFile(&f); + + result = NA_DecodeVersionData(pCert, certSize, pCert, certSize); + if( result <= 0 ) + { + OS_Panic("fail to decode version info %d\n", result); + } + + *ppCert = pCert; + *pSize = certSize; +} + + +void SetupEC(void) +{ + static u32 logLevel, clientCertSize, clientKeySize; + static char deviceCode[17]; + + ECError rv = EC_ERROR_OK; + void* pClientCert; + void* pClientKey; + + // Initialize the EC library + + logLevel = EC_LOG_FINE; + // logLevel = EC_LOG_NONE; + + LoadCert(&pClientCert, &clientCertSize, ".twl-nup-cert.der"); + LoadCert(&pClientKey, &clientKeySize, ".twl-nup-prvkey.der"); + GetDeviceCode(deviceCode, sizeof(deviceCode)); + + ECNameValue initArgs[] = + { + { EC_ALLOC, &AllocForEC }, + { EC_FREE, &FreeForEC }, + { EC_LOG_LEVEL, &logLevel }, + { EC_CLIENT_CERT, pClientCert }, + { EC_CLIENT_CERT_SIZE, &clientCertSize }, + { EC_CLIENT_KEY, pClientKey }, + { EC_CLIENT_KEY_SIZE, &clientKeySize }, + { EC_DEVICE_CODE, deviceCode }, + { EC_ADD_JS_PLUGIN_CALLBACK, &Dummy_WWW_AddJSPlugin }, + }; + const u32 nInitArgs = sizeof(initArgs) / sizeof(initArgs[0]); + + rv = EC_Init(initArgs, nInitArgs); + SDK_ASSERTMSG(rv == EC_ERROR_OK, "Failed to initialize EC, rv=%d\n", rv); + + rv = EC_SetWebSvcUrls( "https://ecs.t.shop.nintendowifi.net/ecs/services/ECommerceSOAP", + "https://ias.t.shop.nintendowifi.net/ias/services/IdentityAuthenticationSOAP", + "https://cas.t.shop.nintendowifi.net/cas/services/CatalogingSOAP" ); + SDK_ASSERTMSG(rv == EC_ERROR_OK, "Failed to EC_SetWebSvcUrls, rv=%d\n", rv); + + rv = EC_SetContentUrls( "http://ccs.t.shop.nintendowifi.net/ccs/download", + "http://ccs.t.shop.nintendowifi.net/ccs/download" ); + SDK_ASSERTMSG(rv == EC_ERROR_OK, "Failed to EC_SetContentUrls, rv=%d\n", rv); +} + + +void WaitEC(ECOpId opId) +{ + ECError result; + ECProgress progress; + ECProgress progress_prev; + + if( opId < 0 ) + { + OS_TPanic("error %d %s\n", opId, GetECErrorString(opId)); + } + + MI_CpuClear(&progress_prev, sizeof(progress_prev)); + + for(;;) + { + result = EC_GetProgress(opId, &progress); + + if( (result != EC_ERROR_OK) && (result != EC_ERROR_NOT_DONE) ) + { + if( result == EC_ERROR_NOT_ACTIVE ) + { + OS_TPrintf("opId=%d\n", opId); + } + OS_TPanic("Failed to EC_GetProgress, result=%d %s\n", result, GetECErrorString(result)); + } + + //#ifdef SDK_DEBUG + if( MI_CpuComp8(&progress_prev, &progress, sizeof(progress)) != 0 ) + { + OS_TPrintf("---------\n"); + OS_TPrintf("progress report\n"); + OS_TPrintf(" status %5d %s\n", progress.status, GetECErrorString(progress.status)); + OS_TPrintf(" operation %5d %s\n", progress.operation, GetECOperationString(progress.operation)); + OS_TPrintf(" phase %5d %s\n", progress.phase, GetECOpPhaseString(progress.phase)); + OS_TPrintf(" isCancelRequested %5d\n", progress.isCancelRequested); + OS_TPrintf(" totalSize %5d\n", progress.totalSize); + OS_TPrintf(" downloadedSize %5d\n", progress.downloadedSize); + OS_TPrintf(" errCode %5d\n", progress.errCode); + OS_TPrintf(" errInfo %s\n", progress.errInfo); + progress_prev = progress; + } + //#endif + if( progress.phase == EC_PHASE_Done ) + { + break; + } + + OS_Sleep(300); + } +} + + + +void hatamotolib_main(void) +{ + // 不要:デバイス情報の表示 + PrintDeviceInfo(); + + OS_TPrintf("--------------------------------\n"); + + // setup + { + // 必須:タイトル ID の偽装 + SetupShopTitleId(); + + // ?:ユーザ設定がされていないと接続できないので適当に設定 + SetupUserInfo(); + + // 必須:バージョンデータのマウント + SetupVerData(); + + // 必須:ネットワークへの接続 + OS_TPrintf("connecting to AP....\n"); +#ifdef USE_DWC + NetworkAutoConnect_DWC(); +#endif + OS_TPrintf("connected\n"); + + // 必須:HTTP と SSL の初期化 + OS_TPrintf("start NHTTP\n"); + SetupNSSL(); + SetupNHTTP(); + + // 必須:EC の初期化 + OS_TPrintf("start EC\n"); + SetupEC(); + } + + // body + { + // ダウンロードすべきタイトルの指定 + NAMTitleId tids[] = + { + 0x00030004346b6141ull, +// 0x0003000434616141ull, +// 0x0003000434616241ull, +// 0x000300043461644aull, +// 0x0003000434616741ull, +// 0x0003000434616a41ull, +// 0x0003000434617441ull, +// 0x0003000434626141ull, +// 0x0003000434626341ull, +// 0x0003000434626641ull, +// 0x0003000434626841ull, +// 0x0003000434626941ull, +// 0x0003000434636341ull, +// 0x00030004346b6141ull, +// 0x00030004346b6241ull, +// 0x00030004346b6341ull, + }; + + // 必須:デバイス証明書の発行 + KPSClient(); + + // 必須:指定タイトルをダウンロード + ECDownload(tids, sizeof(tids)/sizeof(*tids)); + + // 不要:セーブデータ領域を作成 + SetupTitlesDataFile(tids, sizeof(tids)/sizeof(*tids)); + } + + // cleanup + { + ECError rv = EC_ERROR_OK; + + // EC の終了処理 + rv = EC_Shutdown(); + SDK_WARNING(rv == EC_ERROR_OK, "Failed to shutdown EC, rv=%d\n", rv); + + // ネットワークからの切断 +#ifdef USE_DWC + NetworkShutdown_DWC(); +#endif + } + + // EC が自分の Title ID のディレクトリを作成してしまうため、削除する + DeleteECDirectory(); + +} + diff --git a/build/tools/sctools/common/src/hatamotolib.h b/build/tools/sctools/common/src/hatamotolib.h new file mode 100644 index 0000000..756bde9 --- /dev/null +++ b/build/tools/sctools/common/src/hatamotolib.h @@ -0,0 +1,28 @@ +#ifndef __HATAMOTO_LIB__ +#define __HATAMOTO_LIB__ + +#ifdef __cplusplus +extern "C" { +#endif + +void PrintDeviceInfo(void); +void SetupShopTitleId(void); +void SetupUserInfo(void); +void SetupVerData(void); +void NetworkAutoConnect_DWC(void); +void NetworkShutdown_DWC(void); +void SetupNSSL(void); +void SetupNHTTP(void); +void SetupEC(void); +void WaitEC(ECOpId opId); +void DeleteECDirectory(void); +void SetupTitlesDataFile(const NAMTitleId* pTitleIds, u32 numTitleIds); + +void hatamotolib_main(void); + + +#ifdef __cplusplus +} +#endif + +#endif // __HATAMOTO_LIB__ diff --git a/build/tools/sctools/common/src/hwi.c b/build/tools/sctools/common/src/hwi.c new file mode 100644 index 0000000..1d3006e --- /dev/null +++ b/build/tools/sctools/common/src/hwi.c @@ -0,0 +1,397 @@ +/*---------------------------------------------------------------------------* + Project: TwlIPL + File: HWInfoWriterLib.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: 2008-05-09#$ + $Rev: 1309 $ + $Author: yosiokat $ + *---------------------------------------------------------------------------*/ + +#include +// #include +#include "hwi.h" + +#include "font.h" +#include "text.h" +#include "mprintf.h" + + +/* +// TWL本体設定データリード(関数内でNTR本体設定データのリードも行う。) +extern BOOL LCFG_ReadTWLSettings( u8 (*pTempBuffer)[ LCFG_READ_TEMP ] ); + +// TWL本体設定データライト(関数内でNTR本体設定データへのライトも行う。) +extern BOOL LCFG_WriteTWLSettings( u8 (*pTempBuffer)[ LCFG_WRITE_TEMP ] ); + +*/ + +// TWL設定データのリード +static BOOL ReadTWLSettings( LCFGTWLSettingsData *cfg_data ) +{ + BOOL isReadTSD = FALSE; + u8 *pBuffer; + if( cfg_data == NULL ) { + return FALSE; + } + pBuffer = OS_Alloc( LCFG_TEMP_BUFFER_SIZE * 2 ); + if( pBuffer ) { + isReadTSD = LCFG_ReadTWLSettings( (u8 (*)[ LCFG_TEMP_BUFFER_SIZE * 2 ] )pBuffer ); + if( isReadTSD == TRUE ) { + /* 内部バッファーからダイレクトにコピーする */ + STD_CopyMemory( (void *)cfg_data, (void *)LCFGi_GetTSD(), sizeof(LCFGTWLSettingsData) ); + OS_TPrintf( "TSD read succeeded.\n" ); + }else { + OS_TPrintf( "TSD read failed.\n" ); + } + OS_Free( pBuffer ); + } + return isReadTSD; +} + + +static BOOL WriteTWLSettings( LCFGTWLSettingsData *cfg_data ) +{ + BOOL isWriteTSD = FALSE; + u8 *pBuffer; + if( cfg_data == NULL ) { + return FALSE; + } + pBuffer = OS_Alloc( LCFG_TEMP_BUFFER_SIZE ); + if( pBuffer ) { + /* 内部バッファーにダイレクトにコピーする */ + STD_CopyMemory( (void *)LCFGi_GetTSD(), (void *)cfg_data, sizeof(LCFGTWLSettingsData) ); + isWriteTSD = LCFG_WriteTWLSettings( (u8 (*)[ LCFG_TEMP_BUFFER_SIZE ] )pBuffer ); + if( isWriteTSD == FALSE ) { + OS_TPrintf( "TSD write failed.\n" ); + mprintf( "TSD write failed.\n" ); + } + OS_Free( pBuffer ); + } + return isWriteTSD; +} + +BOOL MiyaBackupTWLSettings(const char *path) +{ + FSFile f; + BOOL bSuccess; + FSResult fsResult; + s32 writtenSize; + LCFGTWLSettingsData cfg_data; + LCFGReadResult retval; + + retval = LCFGi_THW_ReadSecureInfo(); + if( retval != LCFG_TSF_READ_RESULT_SUCCEEDED ) { + OS_TPrintf( "HW Normal Info read failed.\n" ); + mprintf( "HW Normal Info read failed.\n" ); + return FALSE; + } + + if( FALSE == ReadTWLSettings( &cfg_data ) ) { + mprintf("Failed read cfg file 1.\n" ); + return FALSE; + } + + FS_InitFile(&f); + + /* delete file ? */ + + FS_CreateFileAuto(path, (FS_PERMIT_R|FS_PERMIT_W)); + bSuccess = FS_OpenFileEx(&f, path, (FS_FILEMODE_R|FS_FILEMODE_W)); + if (bSuccess == FALSE) { + fsResult = FS_GetArchiveResultCode(path); + mprintf("Failed open file 1 - HWNormal Info.:%d\n", fsResult ); + return FALSE; + } + + writtenSize = FS_WriteFile(&f, (void *)&cfg_data, (s32)sizeof(LCFGTWLSettingsData) ); + if( writtenSize != sizeof(LCFGTWLSettingsData) ) { + fsResult = FS_GetArchiveResultCode(path); + mprintf("Failed write file 1 - HWNormal Info.:%d\n", fsResult ); + return FALSE; + } + + bSuccess = FS_CloseFile(&f); + if (bSuccess == FALSE) { + fsResult = FS_GetArchiveResultCode(path); + mprintf("Failed close file 1 - HWNormal Info.:%d\n", fsResult ); + return FALSE; + } + + return TRUE; +} + +BOOL MiyaRestoreTWLSettings(const char *path) +{ + FSFile f; + BOOL bSuccess; + FSResult fsResult; + s32 readSize; + LCFGTWLSettingsData cfg_data; + LCFGReadResult retval; + + retval = LCFGi_THW_ReadSecureInfo(); + if( retval != LCFG_TSF_READ_RESULT_SUCCEEDED ) { + OS_TPrintf( "HW Normal Info read failed.\n" ); + mprintf( "HW Normal Info read failed.\n" ); + return FALSE; + } + + if( FALSE == ReadTWLSettings( &cfg_data ) ) { + mprintf("Failed read cfg file 2.\n" ); + } + + + FS_InitFile(&f); + bSuccess = FS_OpenFileEx(&f, path, FS_FILEMODE_R); + if (bSuccess == FALSE) { + fsResult = FS_GetArchiveResultCode(path); + mprintf("Failed open file 2 - HWNormal Info.:%d\n", fsResult ); + return FALSE; + } + readSize = FS_ReadFile(&f, (void *)&cfg_data, (s32)sizeof(LCFGTWLSettingsData) ); + if( readSize != sizeof(LCFGTWLSettingsData) ) { + fsResult = FS_GetArchiveResultCode(path); + mprintf("Failed read file 2 - HWNormal Info.:%d\n", fsResult ); + return FALSE; + } + + bSuccess = FS_CloseFile(&f); + if (bSuccess == FALSE) { + fsResult = FS_GetArchiveResultCode(path); + mprintf("Failed close file 2 - HWNormal Info.:%d\n", fsResult ); + return FALSE; + } + + + + + + /* 実際に書き出し */ + if( FALSE == WriteTWLSettings( &cfg_data ) ) { + return FALSE; + } + + + return TRUE; +} + + +BOOL MiyaReadHWSecureInfo( LCFGTWLHWSecureInfo *Info ) +{ + LCFGReadResult retval; + + if( Info == NULL ) { + return FALSE; + } + + retval = LCFGi_THW_ReadSecureInfo(); + if( retval != LCFG_TSF_READ_RESULT_SUCCEEDED ) { + OS_TPrintf( "HW Normal Info read failed.\n" ); + mprintf( "HW Normal Info read failed.\n" ); + return FALSE; + } + + /* + c:/twlsdk/include/twl/lcfg/common/TWLHWInfo.h + #define LCFGi_GetHWN() ( &s_hwInfoN ) + */ + + STD_CopyMemory( (void *)Info, (void *)LCFGi_GetHWS() , sizeof(LCFGTWLHWSecureInfo) ); + + OS_TPrintf( "HW Secure Info read succeeded.\n" ); + return TRUE; +} + + +BOOL MiyaReadHWNormalInfo( LCFGTWLHWNormalInfo *Info ) +{ + LCFGReadResult retval; + + if( Info == NULL ) { + return FALSE; + } + + retval = LCFGi_THW_ReadNormalInfo(); + if( retval != LCFG_TSF_READ_RESULT_SUCCEEDED ) { + OS_TPrintf( "HW Normal Info read failed.\n" ); + mprintf( "HW Normal Info read failed.\n" ); + return FALSE; + } + + /* + c:/twlsdk/include/twl/lcfg/common/TWLHWInfo.h + #define LCFGi_GetHWN() ( &s_hwInfoN ) + */ + + STD_CopyMemory( (void *)Info, (void *)LCFGi_GetHWN() , sizeof(LCFGTWLHWNormalInfo) ); + + OS_TPrintf( "HW Normal Info read succeeded.\n" ); + return TRUE; +} + + + +/*---------------------------------------------------------------------------* + Name: HWI_WriteHWNormalInfoFile + + Description: HWノーマルInfoファイルのライト + + Arguments: + + Returns: None. + *---------------------------------------------------------------------------*/ +static BOOL MiyaWriteHWNormalInfoFile( LCFGTWLHWNormalInfo *Info ) +{ + /* + + typedef struct LCFGTWLHWNormalInfo { + u8 rtcAdjust; // RTC調整値 + u8 rsv[ 3 ]; + u8 movableUniqueID[ LCFG_TWL_HWINFO_MOVABLE_UNIQUE_ID_LEN ]; // 移行可能なユニークID + } LCFGTWLHWNormalInfo; // 20byte + */ + + if (!LCFGi_THW_WriteNormalInfoDirect( Info )) { + OS_TPrintf( "HW Normal Info Write failed.\n" ); + mprintf( "HW Normal Info Write failed.\n" ); + return FALSE; + } + return TRUE; +} + + + + +BOOL MiyaBackupHWNormalInfo(const char *path) +{ + FSFile f; + BOOL bSuccess; + FSResult fsResult; + s32 writtenSize; + + LCFGTWLHWNormalInfo info; + + if( FALSE == MiyaReadHWNormalInfo( &info ) ) { + return FALSE; + } + + FS_InitFile(&f); + + FS_CreateFileAuto(path, (FS_PERMIT_R|FS_PERMIT_W)); + bSuccess = FS_OpenFileEx(&f, path, (FS_FILEMODE_R|FS_FILEMODE_W)); + if (bSuccess == FALSE) { + fsResult = FS_GetArchiveResultCode(path); + mprintf("Failed open file 1 - HWNormal Info.:%d\n", fsResult ); + return FALSE; + } + + + writtenSize = FS_WriteFile(&f, (void *)&info, (s32)sizeof(LCFGTWLHWNormalInfo) ); + if( writtenSize != sizeof(LCFGTWLHWNormalInfo) ) { + fsResult = FS_GetArchiveResultCode(path); + mprintf("Failed write file 1 - HWNormal Info.:%d\n", fsResult ); + return FALSE; + } + + bSuccess = FS_CloseFile(&f); + if (bSuccess == FALSE) { + fsResult = FS_GetArchiveResultCode(path); + mprintf("Failed close file 1 - HWNormal Info.:%d\n", fsResult ); + return FALSE; + } + + return TRUE; +} + +BOOL MiyaReadHWNormalInfo_From_SD(const char *path, LCFGTWLHWNormalInfo *info) +{ + FSFile f; + BOOL bSuccess; + FSResult fsResult; + s32 readSize; + + if( info == NULL ) { + return FALSE; + } + + FS_InitFile(&f); + bSuccess = FS_OpenFileEx(&f, path, FS_FILEMODE_R); + if (bSuccess == FALSE) { + fsResult = FS_GetArchiveResultCode(path); + mprintf("Failed open file 2 - HWNormal Info.:%d\n", fsResult ); + return FALSE; + } + readSize = FS_ReadFile(&f, (void *)info, (s32)sizeof(LCFGTWLHWNormalInfo) ); + if( readSize != sizeof(LCFGTWLHWNormalInfo) ) { + fsResult = FS_GetArchiveResultCode(path); + mprintf("Failed read file 2 - HWNormal Info.:%d\n", fsResult ); + return FALSE; + } + + bSuccess = FS_CloseFile(&f); + if (bSuccess == FALSE) { + fsResult = FS_GetArchiveResultCode(path); + mprintf("Failed close file 2 - HWNormal Info.:%d\n", fsResult ); + return FALSE; + } + + return TRUE; +} + + +BOOL MiyaRestoreHWNormalInfo(const char *path) +{ + FSFile f; + BOOL bSuccess; + FSResult fsResult; + s32 readSize; + + LCFGTWLHWNormalInfo info; + LCFGTWLHWNormalInfo info_temp; + + FS_InitFile(&f); + bSuccess = FS_OpenFileEx(&f, path, FS_FILEMODE_R); + if (bSuccess == FALSE) { + fsResult = FS_GetArchiveResultCode(path); + mprintf("Failed open file 2 - HWNormal Info.:%d\n", fsResult ); + return FALSE; + } + readSize = FS_ReadFile(&f, (void *)&info, (s32)sizeof(LCFGTWLHWNormalInfo) ); + if( readSize != sizeof(LCFGTWLHWNormalInfo) ) { + fsResult = FS_GetArchiveResultCode(path); + mprintf("Failed read file 2 - HWNormal Info.:%d\n", fsResult ); + return FALSE; + } + + bSuccess = FS_CloseFile(&f); + if (bSuccess == FALSE) { + fsResult = FS_GetArchiveResultCode(path); + mprintf("Failed close file 2 - HWNormal Info.:%d\n", fsResult ); + return FALSE; + } + + + /* RTCの補正値だけはそのまま使う */ + if( FALSE == MiyaReadHWNormalInfo( &info_temp ) ) { + return FALSE; + } + info.rtcAdjust = info_temp.rtcAdjust; + + /* 実際に書き出し */ + if( FALSE == MiyaWriteHWNormalInfoFile( &info ) ) { + return FALSE; + } + + return TRUE; +} + + + diff --git a/build/tools/sctools/common/src/hwi.h b/build/tools/sctools/common/src/hwi.h new file mode 100644 index 0000000..7960c74 --- /dev/null +++ b/build/tools/sctools/common/src/hwi.h @@ -0,0 +1,51 @@ +/*---------------------------------------------------------------------------* + Project: TwlIPL + File: DS_Chat.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: 2008-03-31#$ + $Rev: 1019 $ + $Author: kamikawa $ + *---------------------------------------------------------------------------*/ + +#ifndef __HWI_LIB__ +#define __HWI_LIB__ + +#ifdef __cplusplus +extern "C" { +#endif + + +#include + +#include +#include + +#include "TWLHWInfo_api.h" +#include "TWLSettings_api.h" + + +// define data---------------------------------------------------------- + +BOOL MiyaBackupHWNormalInfo(const char *path); +BOOL MiyaRestoreHWNormalInfo(const char *path); +BOOL MiyaBackupTWLSettings(const char *path); +BOOL MiyaRestoreTWLSettings(const char *path); +BOOL MiyaReadHWNormalInfo( LCFGTWLHWNormalInfo *Info ); +BOOL MiyaReadHWSecureInfo( LCFGTWLHWSecureInfo *Info ); +BOOL MiyaReadHWNormalInfo_From_SD(const char *path, LCFGTWLHWNormalInfo *info); + + + +#ifdef __cplusplus +} +#endif + +#endif // __HWI_LIB__ diff --git a/build/tools/sctools/common/src/key.c b/build/tools/sctools/common/src/key.c new file mode 100644 index 0000000..f76b0d8 --- /dev/null +++ b/build/tools/sctools/common/src/key.c @@ -0,0 +1,42 @@ +#include +#include "key.h" + +static u16 old_keydata = 0; +#define REPEAT_ON 1 +#define REPEAT_COUNT 60 +static int repeat_counter[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + +u16 m_get_key_trigger(void) +{ + u16 keydata; + u16 trigger; + int i; + u16 r; + + keydata = (u16)PAD_Read(); + trigger = (u16)(keydata & (keydata ^ old_keydata)); + +#if REPEAT_ON + r = 1; + for( i = 0 ; i < 15 ; i++ ) { + if( r & old_keydata & keydata ) { + repeat_counter[i]++; + if( repeat_counter[i] > REPEAT_COUNT ) { + trigger |= r; + } + } + else { + repeat_counter[i] = 0; + } + r <<= 1; + } +#endif + + old_keydata = keydata; + return trigger; +} + +u16 m_get_key_code(void) +{ + return old_keydata; +} diff --git a/build/tools/sctools/common/src/key.h b/build/tools/sctools/common/src/key.h new file mode 100644 index 0000000..9704195 --- /dev/null +++ b/build/tools/sctools/common/src/key.h @@ -0,0 +1,34 @@ +#ifndef _KEY_H_ +#define _KEY_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +u16 m_get_key_trigger(void); +u16 m_get_key_code(void); + +/* + u16 keyData; + keyData = m_get_key_code(void); + if ( keyData & PAD_KEY_DOWN ) { + } if ( keyData & PAD_KEY_UP ) { + } if ( keyData & PAD_KEY_LEFT ) { + } if ( keyData & PAD_KEY_RIGHT ) { + } if ( keyData & PAD_BUTTON_R ) { + } if ( keyData & PAD_BUTTON_L ) { + } if( keyData & PAD_BUTTON_A ) { + } if( keyData & PAD_BUTTON_B ) { + } if( keyData & PAD_BUTTON_SELECT ) { + } if( keyData & PAD_BUTTON_START ) { + } + +*/ + +#ifdef __cplusplus +} +#endif + + +#endif /* _KEY_H_ */ diff --git a/build/tools/sctools/common/src/kpsc.cpp b/build/tools/sctools/common/src/kpsc.cpp new file mode 100644 index 0000000..c2b25c7 --- /dev/null +++ b/build/tools/sctools/common/src/kpsc.cpp @@ -0,0 +1,43 @@ +/*---------------------------------------------------------------------------* + Project: TwlSDK - tools - ecdl + File: kpsc.cpp + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: 2008-09-22#$ + $Rev: 8604 $ + $Author: hatamoto_minoru $ + *---------------------------------------------------------------------------*/ + +#include "ecdl.h" + + +void +KPSClient() +{ + s32 progress; + + OS_TPrintf("generate key pair\n"); + progress = EC_GenerateKeyPair(); + + OS_TPrintf("%s %d\n",__FUNCTION__,__LINE__); + + WaitEC(progress); + OS_TPrintf("%s %d\n",__FUNCTION__,__LINE__); + + OS_TPrintf("confirm key pair\n"); + progress = EC_ConfirmKeyPair(); + OS_TPrintf("%s %d\n",__FUNCTION__,__LINE__); + + WaitEC(progress); + + OS_TPrintf("%s %d\n",__FUNCTION__,__LINE__); + +} + diff --git a/build/tools/sctools/common/src/logprintf.c b/build/tools/sctools/common/src/logprintf.c new file mode 100644 index 0000000..96ee18b --- /dev/null +++ b/build/tools/sctools/common/src/logprintf.c @@ -0,0 +1,795 @@ +/************************************************************************** + * Copyright (C) 1997, Nintendo Co.,Ltd. * + *************************************************************************/ + + +#include +#include "text.h" +#include "mprintf.h" +#include "logprintf.h" + +#define __std(ref) ref +// #define __std(ref) ::std::ref + +#include +#include "text.h" +#include "mprintf.h" +#include "logprintf.h" + +#define __fourbytealign(n) ((((unsigned long) (n)) + 3U) & ~3U) +#define __va_start(parm) ((__std(va_list)) ((char*) ((unsigned long)(&parm) & ~3U) + __fourbytealign(sizeof(parm)))) + +#define va_start(ap, parm) ((ap) = __va_start(parm)) + +#define va_arg(ap, type) (*(type *) ((ap += __fourbytealign(sizeof(type))) - __fourbytealign(sizeof(type)))) +#define va_end(ap) ((void) 0) + + +static void *my_memcpy(void *dest, const void *src, size_t n) +{ + unsigned char *s = (unsigned char *)dest; + unsigned char *ss = (unsigned char *)src; + + while(n--) { + *s++ = *ss++ ; + } + return dest; +} + +static const char *strchr(const char *s,int c) +{ + int a; + while( (a = (int)*s) != NULL ) { + if(a == c) + return s; + s++; + } + return (const char *)NULL; +} + +static size_t strlen(const char *s) +{ + size_t n=0; + while( *s != NULL) { + n++; + s++; + } + return n; +} + + +static void *proutPrintf(void *fd, const char *buf, size_t n) +{ + /* write to file */ + /* return (fwrite(buf, 1, n, str) == n ? str : NULL);*/ + int i; + + for (i = 0; i < n; i++) { + OS_PutChar( buf[i] ); + m_putchar( (void *)tc[0], buf[i] ); + } + + if( fd != NULL ) { + (void)FS_WriteFile(fd, (void *)buf, (long)n ); + } + + /* return a fake pointer so that it's not NULL */ + return ((void *)fd); +} + +static int _Printf(void *(*pfn)(void *, const char *, size_t), + void *arg, int len, const char *fmt, va_list ap); + + +void miya_log_fprintf(FSFile *fd, const char *fmt, ...) +{ + int ans; + va_list ap; + va_start(ap, fmt); + ans = _Printf(&proutPrintf, (void *)fd, 0x200, fmt, ap); + va_end(ap); +} + +/**************************************************************/ + + +/*******************************************************/ + +/* _Printf function */ + +#define _YVALS + +/* float properties */ +#define _D0 3 +#define _DBIAS (0x400-1) +#define _DLONG 0 +#define _DOFF 4 +#define _LBIAS (0x400-1) +#define _LONG_DOUBLE 0 + +/* pointer properties */ +#define _NULL (void *) 0 +typedef unsigned int _Sizet; + +/***********************************************************/ +#define _FSP 0x01 +#define _FPL 0x02 +#define _FMI 0x04 +#define _FNO 0x08 +#define _FZE 0x10 +#define _WMAX 999 +#define _WANT (EOF-1) + +#if _LONG_DOUBLE +typedef long double ldouble; +#else +typedef double ldouble; +#endif + +typedef struct { + union { + long long ll; + ldouble ld; + } v; + char *s; + int n0, nz0, n1, nz1, n2, nz2, prec, width; + size_t nchar; + unsigned int flags; + char qual; +} _Pft; + +/* declarations */ +static void miya_Ldtob(_Pft *, char); +static void miya_Litob(_Pft *, char); + + + +/* macros */ + +#if _DLONG +#define LDSIGN(x) (((unsigned short *) &(x))[_D0 ? 4 : 0] & 0x8000) +#else +#define LDSIGN(x) (((unsigned short *) &(x))[_D0] & 0x8000) +#endif + +#define ISDIGIT(c) ((c >= '0') && (c <= '9')) +#define MAX_PAD (sizeof(spaces) - 1) +#define PAD(s, n) \ + if (0 < (n)) { \ + int i, j = (n); \ + for (; 0 < j; j -= i) { \ + i = MAX_PAD < j ? MAX_PAD : j; \ + PUT(s, i); \ + } \ + } +#if 0 // miyamoto +#define PUT(s, n) \ + if( len > n ) {\ + if (0 < (n)) { \ + len -= n; \ + if ((arg = (*pfn)(arg, s, n)) != NULL) \ + x.nchar += (n); \ + else \ + return (x.nchar); \ + } \ + } \ + else { \ + return (x.nchar); \ + } +#else +#define PUT(s, n) \ + if( len > n ) {\ + if (0 < (n)) { \ + len -= n; \ + arg = (*pfn)(arg, s, n); \ + x.nchar += (n); \ + } \ + } \ + else { \ + return (x.nchar); \ + } +#endif + +static char spaces[] = " "; +static char zeroes[] = "00000000000000000000000000000000"; + +static void _Putfld(_Pft *, va_list *, char, char *); + + +typedef struct { + long quot; + long rem; +} miya_ldiv_t; + +typedef struct { + long long quot; + long long rem; +} miya_lldiv_t; + +static miya_lldiv_t miya_lldiv(long long a, long long b) +{ + miya_lldiv_t t; + t.quot = a / b; + t.rem = a % b; + return t; +} + +static miya_ldiv_t miya_ldiv(long a, long b) +{ + miya_ldiv_t t; + t.quot = a / b; + t.rem = a % b; + return t; +} + + +static int _Printf(void *(*pfn)(void *, const char *, size_t), + void *arg, int len, const char *fmt, va_list ap) +{ /* print formatted */ + _Pft x; + + x.nchar = 0; + while (1) { /* scan format string */ + const char *s = fmt; + int c; + const char *t; + static const char fchar[] = {" +-#0"}; + static const unsigned int fbit[] = { + _FSP, _FPL, _FMI, _FNO, _FZE, 0}; + char ac[32]; + + /* copy any literal text */ + while ( (0 < (c = (int)(*s++)) ) && (c != '%')) { + } + --s; +#if 1 + PUT(fmt, s - fmt); +#else + if (0 < (s-fmt)) { + if ((arg = (*pfn)(arg, fmt, s-fmt)) != NULL) + x.nchar += (s-fmt); + else + return (x.nchar); + } + } + else { + return (x.nchar); + } + +#endif + if (c == '\0') + return (x.nchar); + fmt = ++s; + + /* parse a conversion specifier */ + for (x.flags = 0; (t = strchr(fchar, *s)) != NULL; ++s) + x.flags |= fbit[t - fchar]; + if (*s == '*') { /* get width argument */ + x.width = va_arg(ap, int); + if (x.width < 0) { /* same as '-' flag */ + x.width = -x.width; + x.flags |= _FMI; + } + ++s; + } else /* accumulate width digits */ + for (x.width = 0; ISDIGIT((int)*s); ++s) + if (x.width < _WMAX) + x.width = x.width * 10 + *s - '0'; + if (*s != '.') + x.prec = -1; + else if ( *++s == '*') { /* get precision argument */ + x.prec = va_arg(ap, int); + ++s; + } else /* accumulate precision digits */ + for (x.prec = 0; ISDIGIT(*s); ++s) + if (x.prec < _WMAX) + x.prec = x.prec * 10 + *s - '0'; + x.qual = (char)(strchr("hlL", *s) ? *s++ : '\0'); + if (( x.qual == 'l') && ( *s == 'l')) { + x.qual= 'L'; /* the %ll qualifier */ + s++; + } + + /* do the conversion */ + _Putfld(&x, &ap, *s, ac); + x.width -= x.n0 + x.nz0 + x.n1 + x.nz1 + x.n2 + x.nz2; + if (!(x.flags & _FMI)) + PAD(spaces, x.width); + PUT(ac, x.n0); + PAD(zeroes, x.nz0); + PUT(x.s, x.n1); + PAD(zeroes, x.nz1); + PUT(x.s + x.n1, x.n2); + PAD(zeroes, x.nz2); + if (x.flags & _FMI) + PAD(spaces, x.width); + fmt = s + 1; + } + /* shouldn't reach here, only used to eliminate the compiler warning */ + // return 0; +} + +static void _Putfld(_Pft *px, va_list *pap, char code, char *ac) +{ /* convert a field for _Printf */ + + px->n0 = px->nz0 = px->n1 = px->nz1 = px->n2 = px->nz2 = 0; + switch (code) { /* switch on conversion specifier */ + case 'c': + ac[px->n0++] = (char)va_arg(*pap, int); + break; + case 'd': + case 'i': /* convert a signed decimal integer */ + if (px->qual == 'l') + px->v.ll = va_arg(*pap, long); + else if (px->qual == 'L') + px->v.ll = va_arg(*pap, long long); + else + px->v.ll = va_arg(*pap, int); + if ( px->qual == 'h') + px->v.ll = (short) px->v.ll; + if (px->v.ll < 0) /* negate safely in miya_Litob */ + ac[px->n0++] = '-'; + else if (px->flags & _FPL) + ac[px->n0++] = '+'; + else if (px->flags & _FSP) + ac[px->n0++] = ' '; + px->s = &ac[px->n0]; + miya_Litob(px, code); + break; + case 'o': + case 'u': + case 'x': + case 'X': /* convert unsigned */ + if (px->qual == 'l') + px->v.ll = va_arg(*pap, long); + else if (px->qual == 'L') + px->v.ll = va_arg(*pap, long long); + else + px->v.ll = va_arg(*pap, int); + if (px->qual == 'h') + px->v.ll = (unsigned short) px->v.ll; + else if (px->qual == '\0') + px->v.ll = (unsigned int) px->v.ll; + if (px->flags & _FNO) { /* indicate base with prefix */ + ac[px->n0++] = '0'; + if (code == 'x' || code == 'X') + ac[px->n0++] = code; + } + px->s = &ac[px->n0]; + miya_Litob(px, code); + break; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': /* convert floating */ + px->v.ld = px->qual == 'L' ? + va_arg(*pap, ldouble) : va_arg(*pap, double); + if (LDSIGN(px->v.ld)) + ac[px->n0++] = '-'; + else if (px->flags & _FPL) + ac[px->n0++] = '+'; + else if (px->flags & _FSP) + ac[px->n0++] = ' '; + px->s = &ac[px->n0]; + miya_Ldtob(px, code); + break; + case 'n': /* return output count */ + if (px->qual == 'h') + *va_arg(*pap, short *) = (short)(px->nchar); + else if (px->qual == 'l') + *va_arg(*pap, long *) = px->nchar; + else if (px->qual == 'L') + *va_arg(*pap, long long *) = px->nchar; + else + *va_arg(*pap, int *) = px->nchar; + break; + case 'p': /* convert a pointer, hex long version */ + px->v.ll = (long) va_arg(*pap, void *); + px->s = &ac[px->n0]; + miya_Litob(px, 'x'); + break; + case 's': /* convert a string */ + px->s = va_arg(*pap, char *); + px->n1 = strlen(px->s); + if (0 <= px->prec && px->prec < px->n1) + px->n1 = px->prec; + break; + case '%': /* put a '%' */ + ac[px->n0++] = '%'; + break; + default: /* undefined specifier, print it out */ + ac[px->n0++] = code; + break; + } +} + +/*****************************************************************/ + +/* miya_Litob function */ + + +/* IEEE 754 properties */ +#define _DFRAC ((1<<_DOFF)-1) +#define _DMASK (0x7fff&~_DFRAC) +#define _DMAX ((1<<(15-_DOFF))-1) +#define _DNAN (0x8000|_DMAX<<_DOFF|1<<(_DOFF-1)) +#define _DSIGN 0x8000 +#define DSIGN(x) (((unsigned short *)&(x))[_D0] & _DSIGN) +#define HUGE_EXP (int)(_DMAX * 900L / 1000) +#define HUGE_RAD 3.14e30 +#define SAFE_EXP (_DMAX>>1) + +/* word offsets within double */ +#if _D0==3 +#define _D1 2 /* little-endian order */ +#define _D2 1 +#define _D3 0 +#else +#define _D1 1 /* big-endian order */ +#define _D2 2 +#define _D3 3 +#endif + +/* return values for _D functions */ +#define FINITE -1 +#define INF 1 +#define _NAN 2 + +static char ldigs[] = "0123456789abcdef"; +static char udigs[] = "0123456789ABCDEF"; + +static void miya_Litob(_Pft *px, char code) +{ /* convert unsigned long to text */ + char ac[24]; /* safe for 64-bit integers */ + char *digs = code == 'X' ? udigs : ldigs; + int base = code == 'o' ? 8 : code != 'x' && code != 'X' ? 10 : 16; + int i = sizeof(ac); + unsigned long long ullval = px->v.ll; + + if ((code == 'd' || code == 'i') && px->v.ll < 0) + ullval = -ullval; /* safe against overflow */ + if (ullval || px->prec) + ac[--i] = digs[ullval % base]; + px->v.ll = ullval / base; + while (0 < px->v.ll && 0 < i) { /* convert digits */ + miya_lldiv_t qr = miya_lldiv(px->v.ll, (long long) base); + + px->v.ll = qr.quot; + ac[--i] = digs[qr.rem]; + } + px->n1 = sizeof(ac) - i; + (void)my_memcpy(px->s, &ac[i], px->n1); + if (px->n1 < px->prec) + px->nz0 = px->prec - px->n1; + if (px->prec < 0 && (px->flags & (_FMI | _FZE)) == _FZE + && 0 < (i = px->width - px->n0 - px->nz0 - px->n1)) + px->nz0 += i; + +} + + +/*************************************************/ +/* miya_Ldtob function */ + +#include "float.h" + +/* macros */ +#define NDIG 8 + +/* static data */ +static const ldouble pows[] = { + 1e1L, 1e2L, 1e4L, 1e8L, 1e16L, 1e32L, +#if 0x100 < _LBIAS /* assume IEEE 754 8- or 10-byte */ + 1e64L, 1e128L, 1e256L, +#if _DLONG /* assume IEEE 754 10-byte */ + 1e512L, 1e1024L, 1e2048L, 1e4096L, +#endif +#endif +}; + +static short _Ldunscale(short *, ldouble *); +static void _Genld(_Pft *, char, char *, short, short); + +static void miya_Ldtob(_Pft *px, char code) +{ /* convert long double to text */ + char ac[32]; + char *p = ac; + ldouble ldval = px->v.ld; + short errx, nsig, xexp; + + if (px->prec < 0) + px->prec = 6; + else if (px->prec == 0 && (code == 'g' || code == 'G')) + px->prec = 1; + if (0 < (errx = _Ldunscale(&xexp, &px->v.ld))) { + /* x == Nan, x == INF */ + (void)my_memcpy(px->s, errx == _NAN ? "NaN" : "Inf", px->n1 = 3); + return; + } else if (0 == errx) /*x == 0 */ + nsig = 0, xexp = 0; + else { /* 0 < |x|, convert it */ + { /* scale ldval to ~~10^(NDIG/2) */ + int i, n; + + if (ldval < 0.0) + ldval = -ldval; + if ((xexp = (short)(xexp * 30103L / 100000L - NDIG/2)) < 0) { + /* scale up */ + n = (-xexp + (NDIG/2-1)) & ~(NDIG/2-1), xexp = (short)(-n); + for (i = 0; 0 < n; n >>= 1, ++i) + if (n & 1) + ldval *= pows[i]; + } else if (0 < xexp) { /* scale down */ + ldouble factor = 1.0; + + xexp &= ~(NDIG/2-1); + for (n = xexp, i = 0; 0 < n; n >>= 1, ++i) + if (n & 1) + factor *= pows[i]; + ldval /= factor; + } + } + { /* convert significant digits */ + int gen = px->prec + + (code == 'f' ? xexp + 2 + NDIG : 2 + NDIG / 2); + + if (LDBL_DIG + NDIG / 2 < gen) + gen = LDBL_DIG + NDIG / 2; + for ( *p++ = '0' ; 0 < gen && 0.0 < ldval; p += NDIG) { + /* convert NDIG at a time */ + int j; + long lo = (long) ldval; + + if (0 < (gen -= NDIG)) + ldval = (ldval - (ldouble) lo) * 1e8L; + for (p += NDIG, j = NDIG; 0 < lo && 0 <= --j; ) { + /* convert NDIG digits */ + miya_ldiv_t qr; + + qr = miya_ldiv(lo, 10); + *--p = (char)(qr.rem + '0'), lo = qr.quot; + } + while (0 <= --j) + *--p = '0'; + } + gen = p - &ac[1]; + for (p = &ac[1], xexp += NDIG - 1; *p == '0'; ++p) + --gen, --xexp; /* correct xexp */ + + /* miya + char code + int prec + short nsig, xexp; + */ + nsig = (short)(px->prec + (code == 'f' ? xexp + 1 : + code == 'e' || code == 'E' ? 1 : 0)); + if (gen < nsig) + nsig = (short)gen; + if (0 < nsig) { /* round and strip trailing zeros */ + /* const char drop; */ + char drop; + int n; + drop = (char)(nsig < gen && '5' <= p[nsig] ? '9' : '0'); + + for (n = nsig; p[--n] == drop; ) + --nsig; + + if ( drop == '9') { + p[n] = *(&(p[n])+1); + + // ++p[n]; + } + if (n < 0) + --p, ++nsig, ++xexp; + } + } + } + _Genld(px, code, p, nsig, xexp); +} + +#if _DLONG /* 10-byte IEEE format */ +#define _LMASK 0x7fff +#define _LMAX 0x7fff +#define _LSIGN 0x8000 +#if _D0==3 /* little-endian order */ +#define _L0 4 +#define _L1 3 +#define _L2 2 +#define _L3 1 +#define _L4 0 +#else +#define _L0 0 +#define _L1 1 +#define _L2 2 +#define _L3 3 +#define _L4 4 +#endif + +static short dnorm(unsigned short *ps) +{ /* normalize long double fraction */ + short xchar; + + for (xchar = 0; ps[_L1] == 0; xchar -= 16) { /* shift left by 16 */ + ps[_L1] = ps[_L2], ps[_L2] = ps[_L3]; + ps[_L3] = ps[_L4], ps[_L4] = 0; + } + for (; ps[_L1] < 1U<<_LOFF; --xchar) { /* shift left by 1 */ + ps[_L1] = ps[_L1] << 1 | ps[_L2] >> 15; + ps[_L2] = ps[_L2] << 1 | ps[_L3] >> 15; + ps[_L3] = ps[_L3] << 1 | ps[_L4] >> 15; + ps[_L4] <<= 1; + } + return (xchar); +} + +static short _Ldunscale(short *pex, ldouble *px) +{ /* separate *px to |frac| < 1/2 and 2^*pex */ + unsigned short *ps = (unsigned short *) px; + short xchar = ps[_L0] & _LMAX; + + if (xchar == _LMAX) { /* NaN or INF */ + *pex = 0; + return (ps[_L1] & 0x7fff || ps[_L2] || ps[_L3] || ps[_L4] ? + _NAN : INF); + } else if (ps[_L1] == 0 && ps[_L2] == 0 && ps[_L3] == 0 && ps[_L4] == 0) { + /* zero */ + *pex = 0; + return (0); + } else { /* finite, reduce to [1/2, 1) */ + xchar += dnorm(ps); + ps[_L0] = ps[_L0] & _LSIGN | _LBIAS; + *pex = xchar - _LBIAS; + return (FINITE); + } +} + +#else /* long double same as double */ +/* + static short _Dnorm(unsigned short *ps) + { + short xchar; + unsigned short sign = ps[_D0] & _DSIGN; + + xchar = 0; + if ((ps[_D0] &= _DFRAC) != 0 || ps[_D1] || ps[_D2] || ps[_D3]) { + for (; ps[_D0] == 0; xchar -= 16) { + ps[_D0] = ps[_D1], ps[_D1] = ps[_D2]; + ps[_D2] = ps[_D3], ps[_D3] = 0; + } + for (; ps[_D0] < 1<<_DOFF; --xchar) { + ps[_D0] = ps[_D0] << 1 | ps[_D1] >> 15; + ps[_D1] = ps[_D1] << 1 | ps[_D2] >> 15; + ps[_D2] = ps[_D2] << 1 | ps[_D3] >> 15; + ps[_D3] <<= 1; + } + for (; 1<<_DOFF+1 <= ps[_D0]; ++xchar) { + ps[_D3] = ps[_D3] >> 1 | ps[_D2] << 15; + ps[_D2] = ps[_D2] >> 1 | ps[_D1] << 15; + ps[_D1] = ps[_D1] >> 1 | ps[_D0] << 15; + ps[_D0] >>= 1; + } + ps[_D0] &= _DFRAC; + } + ps[_D0] |= sign; + return (xchar); + } + */ +static short _Ldunscale(short *pex, ldouble *px) +{ /* separate *px to |frac| < 1/2 and 2^*pex */ + unsigned short *ps = (unsigned short *) px; + short xchar = (short)((ps[_D0] & _DMASK) >> _DOFF); + + if (xchar == _DMAX) { /* NaN or INF */ + *pex = 0; + return (short)(ps[_D0] & _DFRAC || ps[_D1] || ps[_D2] || ps[_D3] ? + _NAN : INF); + } else if (0 < xchar /* || (xchar = _Dnorm(ps)) != 0 */) { + /* finite, reduce to [1/2, 1) */ + ps[_D0] = (short)(ps[_D0] & ~_DMASK | _DBIAS << _DOFF); +#if _LONG_DOUBLE + *pex = (short)(xchar - _DBIAS); +#else + *pex = (short)(xchar - _DBIAS + 1); /* for SGI */ +#endif + return (FINITE); + } else if (xchar < 0) { /* error! */ + return (_NAN); + } else { /* zero */ + *pex = 0; + return (0); + } +} + +#endif + +static void _Genld(_Pft *px, char code, char *p, short nsig, short xexp) +{ /* generate long double text */ + const char point = '.'; + + if (nsig <= 0) + nsig = 1, p = "0"; + if (code == 'f' || (code == 'g' || code == 'G') && + -4 <= xexp && xexp < px->prec) { /* 'f' format */ + ++xexp; /* change to leading digit count */ + if (code != 'f') { /* fixup for 'g' */ + if (!(px->flags & _FNO) && nsig < px->prec) + px->prec = nsig; + if ((px->prec -= xexp) < 0) + px->prec = 0; + } + if (xexp <= 0) { /* digits only to right of point */ + px->s[px->n1++] = '0'; + + if (0 < px->prec || px->flags & _FNO) + px->s[px->n1++] = point; + if (px->prec < -xexp) + xexp = (short)(-px->prec); + px->nz1 = -xexp; + px->prec += xexp; + if (px->prec < nsig) + nsig = (short)(px->prec); + (void)my_memcpy(&px->s[px->n1], p, px->n2 = nsig); + px->nz2 = px->prec - nsig; + } else if (nsig < xexp) { /* zeros before point */ + (void)my_memcpy(&px->s[px->n1], p, nsig); + px->n1 += nsig; + px->nz1 = xexp - nsig; + if (0 < px->prec || px->flags & _FNO) + px->s[px->n1] = point, ++px->n2; + px->nz2 = px->prec; + } else { /* enough digits before point */ + (void)my_memcpy(&px->s[px->n1], p, xexp); + px->n1 += xexp; + nsig -= xexp; + if (0 < px->prec || px->flags & _FNO) + px->s[px->n1++] = point; + if (px->prec < nsig) + nsig = (short)(px->prec); + (void)my_memcpy(&px->s[px->n1], p + xexp, nsig); + px->n1 += nsig; + px->nz1 = px->prec - nsig; + } + } else { /* 'e' format */ + if (code == 'g' || code == 'G') { /* fixup for 'g' */ + if (nsig < px->prec) + px->prec = nsig; + if (--px->prec < 0) + px->prec = 0; + code = (char)(code == 'g' ? 'e' : 'E'); + } + px->s[px->n1++] = *p++; + if (0 < px->prec || px->flags & _FNO) + px->s[px->n1++] = point; + if (0 < px->prec) { /* put fraction digits */ + if (px->prec < --nsig) + nsig = (short)(px->prec); + (void)my_memcpy(&px->s[px->n1], p, nsig); + px->n1 += nsig; + px->nz1 = px->prec - nsig; + } + p = &px->s[px->n1]; /* put exponent */ + *p++ = code; + if (0 <= xexp) + *p++ = '+'; + else { /* negative exponent */ + *p++ = '-'; + xexp = (short)(-xexp); + } + if (100 <= xexp) { /* put oversize exponent */ + if (1000 <= xexp) + *p = (char)(xexp / 1000 + '0'), p++, xexp %= 1000; + *p = (char)(xexp / 100 + '0'), p++, xexp %= 100; + } + *p = (char)(xexp / 10 + '0'),p++, xexp %= 10; + *p = (char)(xexp + '0'); p++; + px->n2 = p - &px->s[px->n1]; + } + if ((px->flags & (_FMI | _FZE)) == _FZE) { /* pad with leading zeros */ + int n = px->n0 + px->n1 + px->nz1 + px->n2 + px->nz2; + + if (n < px->width) + px->nz0 = px->width - n; + } +} + diff --git a/build/tools/sctools/common/src/logprintf.h b/build/tools/sctools/common/src/logprintf.h new file mode 100644 index 0000000..84983ee --- /dev/null +++ b/build/tools/sctools/common/src/logprintf.h @@ -0,0 +1,7 @@ +#ifndef _LOGPRINT_ +#define _LOGPRINT_ + +void miya_log_fprintf(FSFile *fd, const char *fmt, ...); + + +#endif /* _LOGPRINT_ */ diff --git a/build/tools/sctools/common/src/mprintf.c b/build/tools/sctools/common/src/mprintf.c new file mode 100644 index 0000000..720810e --- /dev/null +++ b/build/tools/sctools/common/src/mprintf.c @@ -0,0 +1,817 @@ +/************************************************************************** + * Copyright (C) 1997, Nintendo Co.,Ltd. * + *************************************************************************/ + +/* c:/program files/Metrowerks/CodeWarrior for NITRO V0.4/ARM_EABI_Support/msl/MSL_C/MSL_ARM/Include/ */ + +//#pragma avoid_byte none +//#pragma warn_byte none + +#define __std(ref) ref +// #define __std(ref) ::std::ref + + +#include + + + +#define __fourbytealign(n) ((((unsigned long) (n)) + 3U) & ~3U) +#define __va_start(parm) ((__std(va_list)) ((char*) ((unsigned long)(&parm) & ~3U) + __fourbytealign(sizeof(parm)))) + +#define va_start(ap, parm) ((ap) = __va_start(parm)) + +#define va_arg(ap, type) (*(type *) ((ap += __fourbytealign(sizeof(type))) - __fourbytealign(sizeof(type)))) +#define va_end(ap) ((void) 0) + + +#include "text.h" +#include "mprintf.h" + +#define CODE32 + + +static inline u8 My_ReadByte( const void* address ) +{ + if ( (u32)address & 1 ) + { + return (u8)( *(u16*)((u32)address & ~1) >> 8 ); + } + else + { + return (u8)( *(u16*)address & 0xff ); + } +} + + + +static inline void My_WriteByte( void* address, u8 value ) +{ + u16 val = *(u16*)((u32)address & ~1); + + if ( (u32)address & 1 ) + { + *(u16*)((u32)address & ~1) = (u16)( ((value & 0xff)<< 8) | (val & 0xff) ); + } + else + { + *(u16*)((u32)address & ~1) = (u16)( (val & 0xff00) | (value & 0xff) ); + } +} + + + +void mprintf(const char *fmt, ...); + +static void *memcpy(void *dest, const void *src, size_t n) +{ + unsigned char *s = (unsigned char *)dest; + unsigned char *ss = (unsigned char *)src; + + while(n--) { + My_WriteByte( s++,My_ReadByte(ss++)); + } + return dest; +} + + + +static const char *strchr(const char *s,int c) +{ + int a; + while( (a = (int)My_ReadByte(s)) != NULL ) { + if(a == c) + return s; + s++; + } + return (const char *)NULL; +} + +static size_t strlen(const char *s) +{ + size_t n=0; + while( My_ReadByte(s) != NULL) { + n++; + s++; + } + return n; +} + + + +#define MIYA_PUTCHAR m_putchar + +static int _Printf(void *(*pfn)(void *, const char *, size_t), + void *arg, const char *fmt, va_list ap); + +static void *proutPrintf(void *txb, const char *buf, size_t n) +{ + /* write to file */ + /* return (fwrite(buf, 1, n, str) == n ? str : NULL);*/ + int i; + + for (i = 0; i < n; i++) + MIYA_PUTCHAR( txb, My_ReadByte(&(buf[i]))); + /* return a fake pointer so that it's not NULL */ + return ((void *)txb); +} + + +void mprintf(const char *fmt, ...) +{ + void *tb = (void *)tc[0]; + int ans; + va_list ap; + va_start(ap, fmt); + ans = _Printf(&proutPrintf, tb, fmt, ap); + va_end(ap); +} + +void mfprintf(TEXT_CTRL *tc, const char *fmt, ...) +{ + int ans; + va_list ap; + va_start(ap, fmt); + ans = _Printf(&proutPrintf, (void *)tc, fmt, ap); + va_end(ap); +} + +/*******************************************************/ + +/* _Printf function */ + +#define _YVALS + +/* float properties */ +#define _D0 3 +#define _DBIAS (0x400-1) +#define _DLONG 0 +#define _DOFF 4 +#define _LBIAS (0x400-1) +#define _LONG_DOUBLE 0 + +/* pointer properties */ +#define _NULL (void *) 0 +typedef unsigned int _Sizet; + +/***********************************************************/ +#define _FSP 0x01 +#define _FPL 0x02 +#define _FMI 0x04 +#define _FNO 0x08 +#define _FZE 0x10 +#define _WMAX 999 +#define _WANT (EOF-1) + +#if _LONG_DOUBLE +typedef long double ldouble; +#else +typedef double ldouble; +#endif + +typedef struct { + union { + long long ll; + ldouble ld; + } v; + char *s; + int n0, nz0, n1, nz1, n2, nz2, prec, width; + size_t nchar; + unsigned int flags; + char qual; + char padding0[3]; +} _Pft; + +/* declarations */ +static void miya_Ldtob(_Pft *, char); +static void miya_Litob(_Pft *, char); + + + +/* macros */ + +#if _DLONG +#define LDSIGN(x) (((unsigned short *) &(x))[_D0 ? 4 : 0] & 0x8000) +#else +#define LDSIGN(x) (((unsigned short *) &(x))[_D0] & 0x8000) +#endif + +#define ISDIGIT(c) ((c >= '0') && (c <= '9')) +#define MAX_PAD (sizeof(spaces) - 1) +#define PAD(s, n) \ + if (0 < (n)) { \ + int i, j = (n); \ + for (; 0 < j; j -= i) { \ + i = MAX_PAD < j ? MAX_PAD : j; \ + PUT(s, i); \ + } \ + } +#define PUT(s, n) \ + if (0 < (n)) { \ + if ((arg = (*pfn)(arg, s, n)) != NULL) \ + x.nchar += (n); \ + else \ + return (x.nchar); \ + } + +static char spaces[] = " "; +static char zeroes[] = "00000000000000000000000000000000"; + +static void _Putfld(_Pft *, va_list *, char, char *); + + +typedef struct { + long quot; + long rem; +} miya_ldiv_t; + +typedef struct { + long long quot; + long long rem; +} miya_lldiv_t; + +static miya_lldiv_t miya_lldiv(long long a, long long b) +{ + miya_lldiv_t t; + t.quot = a / b; + t.rem = a % b; + return t; +} + +static miya_ldiv_t miya_ldiv(long a, long b) +{ + miya_ldiv_t t; + t.quot = a / b; + t.rem = a % b; + return t; +} + +// static void *proutPrintf(void *txb, const char *buf, size_t n) +// ans = _Printf(&proutPrintf, tb, (const char *)tc->buffer, ap); + +static int _Printf(void *(*pfn)(void *, const char *, size_t), + void *arg, const char *fmt, va_list ap) +{ /* print formatted */ + _Pft x; + + x.nchar = 0; + while (1) { /* scan format string */ + const char *s = fmt; + int c; + const char *t; + static const char fchar[] = {" +-#0"}; + static const unsigned int fbit[] = { + _FSP, _FPL, _FMI, _FNO, _FZE, 0}; + char ac[32]; + + /* copy any literal text */ + + while ( (0 < (c = (int)My_ReadByte(s++)) ) && (c != '%')) { + } + --s; +#if 1 + PUT(fmt, s - fmt); +#else + if (0 < (s-fmt)) { + if ((arg = (*pfn)(arg, fmt, s-fmt)) != NULL) + x.nchar += (s-fmt); + else + return (x.nchar); + } + +#endif + if (c == '\0') + return (x.nchar); + fmt = ++s; + + /* parse a conversion specifier */ + for (x.flags = 0; (t = strchr(fchar, *s)) != NULL; ++s) + x.flags |= fbit[t - fchar]; + if (My_ReadByte(s) == '*') { /* get width argument */ + x.width = va_arg(ap, int); + if (x.width < 0) { /* same as '-' flag */ + x.width = -x.width; + x.flags |= _FMI; + } + ++s; + } else /* accumulate width digits */ + for (x.width = 0; ISDIGIT((int)*s); ++s) + if (x.width < _WMAX) + x.width = x.width * 10 + *s - '0'; + if (My_ReadByte(s) != '.') + x.prec = -1; + else if ( My_ReadByte(++s) == '*') { /* get precision argument */ + x.prec = va_arg(ap, int); + ++s; + } else /* accumulate precision digits */ + for (x.prec = 0; ISDIGIT(*s); ++s) + if (x.prec < _WMAX) + x.prec = x.prec * 10 + *s - '0'; + My_WriteByte(&(x.qual), + (char)(strchr("hlL", My_ReadByte(s)) ? My_ReadByte(s++) : '\0')); + if ((My_ReadByte(&(x.qual)) == 'l') && (My_ReadByte(s) == 'l')) { + My_WriteByte(&(x.qual), 'L'); /* the %ll qualifier */ + s++; + } + + /* do the conversion */ + _Putfld(&x, &ap, My_ReadByte(s), ac); + x.width -= x.n0 + x.nz0 + x.n1 + x.nz1 + x.n2 + x.nz2; + if (!(x.flags & _FMI)) + PAD(spaces, x.width); + PUT(ac, x.n0); + PAD(zeroes, x.nz0); + PUT(x.s, x.n1); + PAD(zeroes, x.nz1); + PUT(x.s + x.n1, x.n2); + PAD(zeroes, x.nz2); + if (x.flags & _FMI) + PAD(spaces, x.width); + fmt = s + 1; + } + /* shouldn't reach here, only used to eliminate the compiler warning */ + return 0; +} + +static void _Putfld(_Pft *px, va_list *pap, char code, char *ac) +{ /* convert a field for _Printf */ + + px->n0 = px->nz0 = px->n1 = px->nz1 = px->n2 = px->nz2 = 0; + switch (code) { /* switch on conversion specifier */ + case 'c': + My_WriteByte(&(ac[px->n0++]), (char)va_arg(*pap, int)); + break; + case 'd': + case 'i': /* convert a signed decimal integer */ + if (px->qual == 'l') + px->v.ll = va_arg(*pap, long); + else if (px->qual == 'L') + px->v.ll = va_arg(*pap, long long); + else + px->v.ll = va_arg(*pap, int); + if (My_ReadByte(&(px->qual)) == 'h') + px->v.ll = (short) px->v.ll; + if (px->v.ll < 0) /* negate safely in miya_Litob */ + My_WriteByte(&(ac[px->n0++]), '-'); + else if (px->flags & _FPL) + My_WriteByte(&(ac[px->n0++]),'+'); + else if (px->flags & _FSP) + My_WriteByte(&(ac[px->n0++]),' '); + px->s = &ac[px->n0]; + miya_Litob(px, code); + break; + case 'o': + case 'u': + case 'x': + case 'X': /* convert unsigned */ + if (px->qual == 'l') + px->v.ll = va_arg(*pap, long); + else if (px->qual == 'L') + px->v.ll = va_arg(*pap, long long); + else + px->v.ll = va_arg(*pap, int); + if (px->qual == 'h') + px->v.ll = (unsigned short) px->v.ll; + else if (px->qual == '\0') + px->v.ll = (unsigned int) px->v.ll; + if (px->flags & _FNO) { /* indicate base with prefix */ + My_WriteByte(&(ac[px->n0++]),'0'); + if (code == 'x' || code == 'X') + My_WriteByte(&(ac[px->n0++]), code); + } + px->s = &ac[px->n0]; + miya_Litob(px, code); + break; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': /* convert floating */ + px->v.ld = px->qual == 'L' ? + va_arg(*pap, ldouble) : va_arg(*pap, double); + if (LDSIGN(px->v.ld)) + My_WriteByte(&(ac[px->n0++]), '-'); + else if (px->flags & _FPL) + My_WriteByte(&(ac[px->n0++]), '+'); + else if (px->flags & _FSP) + My_WriteByte(&(ac[px->n0++]), ' '); + px->s = &ac[px->n0]; + miya_Ldtob(px, code); + break; + case 'n': /* return output count */ + if (px->qual == 'h') + *va_arg(*pap, short *) = (short)(px->nchar); + else if (px->qual == 'l') + *va_arg(*pap, long *) = px->nchar; + else if (px->qual == 'L') + *va_arg(*pap, long long *) = px->nchar; + else + *va_arg(*pap, int *) = px->nchar; + break; + case 'p': /* convert a pointer, hex long version */ + px->v.ll = (long) va_arg(*pap, void *); + px->s = &ac[px->n0]; + miya_Litob(px, 'x'); + break; + case 's': /* convert a string */ + px->s = va_arg(*pap, char *); + px->n1 = strlen(px->s); + if (0 <= px->prec && px->prec < px->n1) + px->n1 = px->prec; + break; + case '%': /* put a '%' */ + My_WriteByte(&(ac[px->n0++]),'%'); + break; + default: /* undefined specifier, print it out */ + My_WriteByte(&(ac[px->n0++]), code); + break; + } +} + +/*****************************************************************/ + +/* miya_Litob function */ + + +/* IEEE 754 properties */ +#define _DFRAC ((1<<_DOFF)-1) +#define _DMASK (0x7fff&~_DFRAC) +#define _DMAX ((1<<(15-_DOFF))-1) +#define _DNAN (0x8000|_DMAX<<_DOFF|1<<(_DOFF-1)) +#define _DSIGN 0x8000 +#define DSIGN(x) (((unsigned short *)&(x))[_D0] & _DSIGN) +#define HUGE_EXP (int)(_DMAX * 900L / 1000) +#define HUGE_RAD 3.14e30 +#define SAFE_EXP (_DMAX>>1) + +/* word offsets within double */ +#if _D0==3 +#define _D1 2 /* little-endian order */ +#define _D2 1 +#define _D3 0 +#else +#define _D1 1 /* big-endian order */ +#define _D2 2 +#define _D3 3 +#endif + +/* return values for _D functions */ +#define FINITE -1 +#define INF 1 +#define NAN 2 + +static char ldigs[] = "0123456789abcdef"; +static char udigs[] = "0123456789ABCDEF"; + +static void miya_Litob(_Pft *px, char code) +{ /* convert unsigned long to text */ + char ac[24]; /* safe for 64-bit integers */ + char *digs = code == 'X' ? udigs : ldigs; + int base = code == 'o' ? 8 : code != 'x' && code != 'X' ? 10 : 16; + int i = sizeof(ac); + unsigned long long ullval = px->v.ll; + + if ((code == 'd' || code == 'i') && px->v.ll < 0) + ullval = -ullval; /* safe against overflow */ + if (ullval || px->prec) + My_WriteByte(&(ac[--i]), My_ReadByte(&(digs[ullval % base]))); + px->v.ll = ullval / base; + while (0 < px->v.ll && 0 < i) { /* convert digits */ + miya_lldiv_t qr = miya_lldiv(px->v.ll, (long long) base); + + px->v.ll = qr.quot; + My_WriteByte(&(ac[--i]), My_ReadByte(&(digs[qr.rem]))); + } + px->n1 = sizeof(ac) - i; + (void)memcpy(px->s, &ac[i], px->n1); + if (px->n1 < px->prec) + px->nz0 = px->prec - px->n1; + if (px->prec < 0 && (px->flags & (_FMI | _FZE)) == _FZE + && 0 < (i = px->width - px->n0 - px->nz0 - px->n1)) + px->nz0 += i; + +} + + +/*************************************************/ +/* miya_Ldtob function */ + +#include "float.h" + +/* macros */ +#define NDIG 8 + +/* static data */ +static const ldouble pows[] = { + 1e1L, 1e2L, 1e4L, 1e8L, 1e16L, 1e32L, +#if 0x100 < _LBIAS /* assume IEEE 754 8- or 10-byte */ + 1e64L, 1e128L, 1e256L, +#if _DLONG /* assume IEEE 754 10-byte */ + 1e512L, 1e1024L, 1e2048L, 1e4096L, +#endif +#endif +}; + +static short _Ldunscale(short *, ldouble *); +static void _Genld(_Pft *, char, char *, short, short); + +static void miya_Ldtob(_Pft *px, char code) +{ /* convert long double to text */ + char ac[32]; + char *p = ac; + ldouble ldval = px->v.ld; + short errx, nsig, xexp; + + if (px->prec < 0) + px->prec = 6; + else if (px->prec == 0 && (code == 'g' || code == 'G')) + px->prec = 1; + if (0 < (errx = _Ldunscale(&xexp, &px->v.ld))) { + /* x == Nan, x == INF */ + (void)memcpy(px->s, errx == NAN ? "NaN" : "Inf", px->n1 = 3); + return; + } else if (0 == errx) /*x == 0 */ + nsig = 0, xexp = 0; + else { /* 0 < |x|, convert it */ + { /* scale ldval to ~~10^(NDIG/2) */ + int i, n; + + if (ldval < 0.0) + ldval = -ldval; + if ((xexp = (short)(xexp * 30103L / 100000L - NDIG/2)) < 0) { + /* scale up */ + n = (-xexp + (NDIG/2-1)) & ~(NDIG/2-1), xexp = (short)(-n); + for (i = 0; 0 < n; n >>= 1, ++i) + if (n & 1) + ldval *= pows[i]; + } else if (0 < xexp) { /* scale down */ + ldouble factor = 1.0; + + xexp &= ~(NDIG/2-1); + for (n = xexp, i = 0; 0 < n; n >>= 1, ++i) + if (n & 1) + factor *= pows[i]; + ldval /= factor; + } + } + { /* convert significant digits */ + int gen = px->prec + + (code == 'f' ? xexp + 2 + NDIG : 2 + NDIG / 2); + + if (LDBL_DIG + NDIG / 2 < gen) + gen = LDBL_DIG + NDIG / 2; + for (My_WriteByte(p++, '0') ; 0 < gen && 0.0 < ldval; p += NDIG) { + /* convert NDIG at a time */ + int j; + long lo = (long) ldval; + + if (0 < (gen -= NDIG)) + ldval = (ldval - (ldouble) lo) * 1e8L; + for (p += NDIG, j = NDIG; 0 < lo && 0 <= --j; ) { + /* convert NDIG digits */ + miya_ldiv_t qr; + + qr = miya_ldiv(lo, 10); + My_WriteByte(--p , (char)(qr.rem + '0')), lo = qr.quot; + } + while (0 <= --j) + My_WriteByte(--p , '0'); + } + gen = p - &ac[1]; + for (p = &ac[1], xexp += NDIG - 1; My_ReadByte(p) == '0'; ++p) + --gen, --xexp; /* correct xexp */ + + /* miya + char code + int prec + short nsig, xexp; + */ + nsig = (short)(px->prec + (code == 'f' ? xexp + 1 : + code == 'e' || code == 'E' ? 1 : 0)); + if (gen < nsig) + nsig = (short)gen; + if (0 < nsig) { /* round and strip trailing zeros */ + const char drop; + int n; + My_WriteByte((void *)&drop, (char)(nsig < gen && '5' <= My_ReadByte(&(p[nsig])) ? '9' : '0') ); + + for (n = nsig; My_ReadByte(&(p[--n])) == My_ReadByte(&drop); ) + --nsig; + + if (My_ReadByte(&drop) == '9') { + My_WriteByte(&(p[n]), My_ReadByte(&(p[n])+1)); + + // ++p[n]; + } + if (n < 0) + --p, ++nsig, ++xexp; + } + } + } + _Genld(px, code, p, nsig, xexp); +} + +#if _DLONG /* 10-byte IEEE format */ +#define _LMASK 0x7fff +#define _LMAX 0x7fff +#define _LSIGN 0x8000 +#if _D0==3 /* little-endian order */ +#define _L0 4 +#define _L1 3 +#define _L2 2 +#define _L3 1 +#define _L4 0 +#else +#define _L0 0 +#define _L1 1 +#define _L2 2 +#define _L3 3 +#define _L4 4 +#endif + +static short dnorm(unsigned short *ps) +{ /* normalize long double fraction */ + short xchar; + + for (xchar = 0; ps[_L1] == 0; xchar -= 16) { /* shift left by 16 */ + ps[_L1] = ps[_L2], ps[_L2] = ps[_L3]; + ps[_L3] = ps[_L4], ps[_L4] = 0; + } + for (; ps[_L1] < 1U<<_LOFF; --xchar) { /* shift left by 1 */ + ps[_L1] = ps[_L1] << 1 | ps[_L2] >> 15; + ps[_L2] = ps[_L2] << 1 | ps[_L3] >> 15; + ps[_L3] = ps[_L3] << 1 | ps[_L4] >> 15; + ps[_L4] <<= 1; + } + return (xchar); +} + +static short _Ldunscale(short *pex, ldouble *px) +{ /* separate *px to |frac| < 1/2 and 2^*pex */ + unsigned short *ps = (unsigned short *) px; + short xchar = ps[_L0] & _LMAX; + + if (xchar == _LMAX) { /* NaN or INF */ + *pex = 0; + return (ps[_L1] & 0x7fff || ps[_L2] || ps[_L3] || ps[_L4] ? + NAN : INF); + } else if (ps[_L1] == 0 && ps[_L2] == 0 && ps[_L3] == 0 && ps[_L4] == 0) { + /* zero */ + *pex = 0; + return (0); + } else { /* finite, reduce to [1/2, 1) */ + xchar += dnorm(ps); + ps[_L0] = ps[_L0] & _LSIGN | _LBIAS; + *pex = xchar - _LBIAS; + return (FINITE); + } +} + +#else /* long double same as double */ +/* + static short _Dnorm(unsigned short *ps) + { + short xchar; + unsigned short sign = ps[_D0] & _DSIGN; + + xchar = 0; + if ((ps[_D0] &= _DFRAC) != 0 || ps[_D1] || ps[_D2] || ps[_D3]) { + for (; ps[_D0] == 0; xchar -= 16) { + ps[_D0] = ps[_D1], ps[_D1] = ps[_D2]; + ps[_D2] = ps[_D3], ps[_D3] = 0; + } + for (; ps[_D0] < 1<<_DOFF; --xchar) { + ps[_D0] = ps[_D0] << 1 | ps[_D1] >> 15; + ps[_D1] = ps[_D1] << 1 | ps[_D2] >> 15; + ps[_D2] = ps[_D2] << 1 | ps[_D3] >> 15; + ps[_D3] <<= 1; + } + for (; 1<<_DOFF+1 <= ps[_D0]; ++xchar) { + ps[_D3] = ps[_D3] >> 1 | ps[_D2] << 15; + ps[_D2] = ps[_D2] >> 1 | ps[_D1] << 15; + ps[_D1] = ps[_D1] >> 1 | ps[_D0] << 15; + ps[_D0] >>= 1; + } + ps[_D0] &= _DFRAC; + } + ps[_D0] |= sign; + return (xchar); + } + */ +static short _Ldunscale(short *pex, ldouble *px) +{ /* separate *px to |frac| < 1/2 and 2^*pex */ + unsigned short *ps = (unsigned short *) px; + short xchar = (short)((ps[_D0] & _DMASK) >> _DOFF); + + if (xchar == _DMAX) { /* NaN or INF */ + *pex = 0; + return (short)(ps[_D0] & _DFRAC || ps[_D1] || ps[_D2] || ps[_D3] ? + NAN : INF); + } else if (0 < xchar /* || (xchar = _Dnorm(ps)) != 0 */) { + /* finite, reduce to [1/2, 1) */ + ps[_D0] = (short)(ps[_D0] & ~_DMASK | _DBIAS << _DOFF); +#if _LONG_DOUBLE + *pex = (short)(xchar - _DBIAS); +#else + *pex = (short)(xchar - _DBIAS + 1); /* for SGI */ +#endif + return (FINITE); + } else if (xchar < 0) { /* error! */ + return (NAN); + } else { /* zero */ + *pex = 0; + return (0); + } +} + +#endif + +static void _Genld(_Pft *px, char code, char *p, short nsig, short xexp) +{ /* generate long double text */ + const char point = '.'; + + if (nsig <= 0) + nsig = 1, p = "0"; + if (code == 'f' || (code == 'g' || code == 'G') && + -4 <= xexp && xexp < px->prec) { /* 'f' format */ + ++xexp; /* change to leading digit count */ + if (code != 'f') { /* fixup for 'g' */ + if (!(px->flags & _FNO) && nsig < px->prec) + px->prec = nsig; + if ((px->prec -= xexp) < 0) + px->prec = 0; + } + if (xexp <= 0) { /* digits only to right of point */ + My_WriteByte(&(px->s[px->n1++]), '0'); + + if (0 < px->prec || px->flags & _FNO) + My_WriteByte(&(px->s[px->n1++]), point); + if (px->prec < -xexp) + xexp = (short)(-px->prec); + px->nz1 = -xexp; + px->prec += xexp; + if (px->prec < nsig) + nsig = (short)(px->prec); + (void)memcpy(&px->s[px->n1], p, px->n2 = nsig); + px->nz2 = px->prec - nsig; + } else if (nsig < xexp) { /* zeros before point */ + (void)memcpy(&px->s[px->n1], p, nsig); + px->n1 += nsig; + px->nz1 = xexp - nsig; + if (0 < px->prec || px->flags & _FNO) + My_WriteByte(&(px->s[px->n1]), point), ++px->n2; + px->nz2 = px->prec; + } else { /* enough digits before point */ + (void)memcpy(&px->s[px->n1], p, xexp); + px->n1 += xexp; + nsig -= xexp; + if (0 < px->prec || px->flags & _FNO) + My_WriteByte(&(px->s[px->n1++]),point); + if (px->prec < nsig) + nsig = (short)(px->prec); + (void)memcpy(&px->s[px->n1], p + xexp, nsig); + px->n1 += nsig; + px->nz1 = px->prec - nsig; + } + } else { /* 'e' format */ + if (code == 'g' || code == 'G') { /* fixup for 'g' */ + if (nsig < px->prec) + px->prec = nsig; + if (--px->prec < 0) + px->prec = 0; + code = (char)(code == 'g' ? 'e' : 'E'); + } + My_WriteByte(&(px->s[px->n1++]), My_ReadByte(p++)); + if (0 < px->prec || px->flags & _FNO) + My_WriteByte(&(px->s[px->n1++]), point); + if (0 < px->prec) { /* put fraction digits */ + if (px->prec < --nsig) + nsig = (short)(px->prec); + (void)memcpy(&px->s[px->n1], p, nsig); + px->n1 += nsig; + px->nz1 = px->prec - nsig; + } + p = &px->s[px->n1]; /* put exponent */ + My_WriteByte(&p, code); + p++; + if (0 <= xexp) + My_WriteByte(p++, '+'); + else { /* negative exponent */ + My_WriteByte(p++, '-'); + xexp = (short)(-xexp); + } + if (100 <= xexp) { /* put oversize exponent */ + if (1000 <= xexp) + My_WriteByte(p, (char)(xexp / 1000 + '0')),p++, xexp %= 1000; + My_WriteByte(p, (char)(xexp / 100 + '0')), p++, xexp %= 100; + } + My_WriteByte(p, (char)(xexp / 10 + '0')),p++, xexp %= 10; + My_WriteByte(p, (char)(xexp + '0')); p++; + px->n2 = p - &px->s[px->n1]; + } + if ((px->flags & (_FMI | _FZE)) == _FZE) { /* pad with leading zeros */ + int n = px->n0 + px->n1 + px->nz1 + px->n2 + px->nz2; + + if (n < px->width) + px->nz0 = px->width - n; + } +} diff --git a/build/tools/sctools/common/src/mprintf.h b/build/tools/sctools/common/src/mprintf.h new file mode 100644 index 0000000..0480313 --- /dev/null +++ b/build/tools/sctools/common/src/mprintf.h @@ -0,0 +1,20 @@ +#ifndef _MPRINT_ +#define _MPRINT_ + +#define NUM_OF_SCREEN 4 + +extern TEXT_CTRL *tc[NUM_OF_SCREEN]; + +#ifdef __cplusplus +extern "C" { +#endif + +void mfprintf(TEXT_CTRL *tc, const char *fmt, ...); +void mprintf(const char *fmt, ...); + +#ifdef __cplusplus +} +#endif + + +#endif /* _MPRINT_ */ diff --git a/build/tools/sctools/common/src/my_fs_util.c b/build/tools/sctools/common/src/my_fs_util.c new file mode 100644 index 0000000..9878ed7 --- /dev/null +++ b/build/tools/sctools/common/src/my_fs_util.c @@ -0,0 +1,1828 @@ +#include +#include "text.h" +#include "mprintf.h" +#include "my_fs_util.h" +#include "logprintf.h" + +/* + NAND -> SDコピーの時、アトリビュートと時間とパーミッションを合わせる必要あり? + + +// ファイル名の最大長 (旧仕様のプロシージャに限定) +#define FS_FILE_NAME_MAX 127 + +// エントリ名の最大長 (旧仕様のプロシージャに限定) +#define FS_ENTRY_SHORTNAME_MAX 16 +#define FS_ENTRY_LONGNAME_MAX 260 + +// ReadDirectoryで使用するエントリ属性 +#define FS_ATTRIBUTE_IS_DIRECTORY 0x00000100UL +#define FS_ATTRIBUTE_IS_PROTECTED 0x00000200UL +// MS-DOS FATベースのアーカイブに限り意味のある属性 +#define FS_ATTRIBUTE_DOS_MASK 0x000000FFUL +#define FS_ATTRIBUTE_DOS_READONLY 0x00000001UL +#define FS_ATTRIBUTE_DOS_HIDDEN 0x00000002UL +#define FS_ATTRIBUTE_DOS_SYSTEM 0x00000004UL +#define FS_ATTRIBUTE_DOS_VOLUME 0x00000008UL +#define FS_ATTRIBUTE_DOS_DIRECTORY 0x00000010UL +#define FS_ATTRIBUTE_DOS_ARCHIVE 0x00000020UL + + +// OpenFileで使用するアクセスモード +#define FS_FILEMODE_R 0x00000001UL +#define FS_FILEMODE_W 0x00000002UL +#define FS_FILEMODE_L 0x00000004UL +#define FS_FILEMODE_RW (FS_FILEMODE_R | FS_FILEMODE_W) +#define FS_FILEMODE_RWL (FS_FILEMODE_R | FS_FILEMODE_W | FS_FILEMODE_L) + +// OpenDirectoryで使用するアクセスモード +#define FS_DIRMODE_SHORTNAME_ONLY 0x00001000UL + +// CreateFileで使用する権限フラグ +#define FS_PERMIT_R 0x00000001UL +#define FS_PERMIT_W 0x00000002UL +#define FS_PERMIT_RW (FS_PERMIT_R | FS_PERMIT_W) + +// ReadDirectoryで使用するエントリ情報 +typedef struct FSDirectoryEntryInfo +{ + char shortname[FS_ENTRY_SHORTNAME_MAX]; + u32 shortname_length; + char longname[FS_ENTRY_LONGNAME_MAX]; + u32 longname_length; + u32 attributes; + FSDateTime atime; + FSDateTime mtime; + FSDateTime ctime; + u32 filesize; + u32 id; +} FSDirectoryEntryInfo; + +typedef struct FSDirectoryEntryInfoW +{ + u32 attributes; + FSDateTime atime; + FSDateTime mtime; + FSDateTime ctime; + u32 filesize; + u32 id; + u32 shortname_length; + u32 longname_length; + char shortname[FS_ENTRY_SHORTNAME_MAX]; + u16 longname[FS_ENTRY_LONGNAME_MAX]; +} FSDirectoryEntryInfoW; + +// GetPathInfo,SetPathInfoで使用するエントリ情報 +typedef struct FSPathInfo +{ + u32 attributes; + FSDateTime ctime; + FSDateTime mtime; + FSDateTime atime; + u32 filesize; + u32 id; +} FSPathInfo; +*/ + + +static const u32 BUF_SIZE = 256; + +typedef struct { + FSResult result; + char string[32]; +} FS_RESUTL_WORD; + +/* c:/twlsdk/include/nitro/fs/types.h */ + +static FS_RESUTL_WORD my_word[] = { + { FS_RESULT_SUCCESS, "FS_RESULT_SUCCESS" }, + { FS_RESULT_FAILURE, "FS_RESULT_FAILURE" }, + { FS_RESULT_BUSY, "FS_RESULT_BUSY" }, + { FS_RESULT_CANCELED, "FS_RESULT_CANCELED" }, + { FS_RESULT_CANCELLED, "FS_RESULT_CANCELLED" }, + { FS_RESULT_UNSUPPORTED, "FS_RESULT_UNSUPPORTED" }, + { FS_RESULT_ERROR, "FS_RESULT_ERROR" }, + { FS_RESULT_INVALID_PARAMETER, "FS_RESULT_INVALID_PARAMETER" }, + { FS_RESULT_NO_MORE_RESOUCE, "FS_RESULT_NO_MORE_RESOUCE" }, + { FS_RESULT_ALREADY_DONE, "FS_RESULT_ALREADY_DONE" }, + { FS_RESULT_PERMISSION_DENIED, "FS_RESULT_PERMISSION_DENIED" }, + { FS_RESULT_MEDIA_FATAL, "FS_RESULT_MEDIA_FATAL" }, + { FS_RESULT_NO_ENTRY, "FS_RESULT_NO_ENTRY" }, + { FS_RESULT_MEDIA_NOTHING, "FS_RESULT_MEDIA_NOTHING" }, + { FS_RESULT_MEDIA_UNKNOWN, "FS_RESULT_MEDIA_UNKNOWN" }, + { FS_RESULT_BAD_FORMAT, "FS_RESULT_BAD_FORMAT" }, + { FS_RESULT_MAX, "FS_RESULT_MAX" }, + // プロシージャ内で使用する一時的な結果値 + { FS_RESULT_PROC_ASYNC, "FS_RESULT_PROC_ASYNC" }, + { FS_RESULT_PROC_DEFAULT, "FS_RESULT_PROC_DEFAULT" }, + { FS_RESULT_PROC_UNKNOWN, "FS_RESULT_PROC_UNKNOW" }, +}; + + +char *my_fs_util_get_fs_result_word( FSResult res ) +{ + int i; + for( i = 0 ; i < 19 ; i++ ) { + if( my_word[i].result == res ) { + return my_word[i].string; + } + } + return my_word[0].string; +} + + +static void PrintAttributes(u32 attributes, FSFile *log_fd) +{ + char buf[7]; + buf[0] = (char)(( attributes & FS_ATTRIBUTE_DOS_DIRECTORY )? 'd' : '-'); + buf[1] = (char)(( attributes & FS_ATTRIBUTE_DOS_VOLUME )? 'v' : '-'); + buf[2] = (char)(( attributes & FS_ATTRIBUTE_DOS_SYSTEM )? 's' : '-'); + buf[3] = (char)(( attributes & FS_ATTRIBUTE_DOS_ARCHIVE )? 'a' : '-'); + buf[4] = (char)(( attributes & FS_ATTRIBUTE_DOS_HIDDEN )? 'h' : '-'); + buf[5] = (char)(( attributes & FS_ATTRIBUTE_DOS_READONLY )? 'R' : '-'); + buf[6] = '\0'; + miya_log_fprintf(log_fd, "%s", buf ); + +} + + +static BOOL Log_File_Open(FSFile *log_fd, const char *log_file_name) +{ + BOOL bSuccess; + FSResult res; + + if( log_file_name == NULL ) { + return FALSE; + } + + FS_InitFile(log_fd); + + bSuccess = FS_OpenFileEx(log_fd, log_file_name, (FS_FILEMODE_W)); + + if( ! bSuccess ) { + FS_CreateFileAuto( log_file_name, FS_PERMIT_W); + bSuccess = FS_OpenFileEx(log_fd, log_file_name, (FS_FILEMODE_W)); + if( ! bSuccess ) { + res = FS_GetArchiveResultCode( log_file_name ); + miya_log_fprintf(NULL, "log file open error %s\n", log_file_name ); + miya_log_fprintf(NULL, " Failed open file:%s\n", my_fs_util_get_fs_result_word( res )); + return FALSE; + } + } + return TRUE; +} + +static void Log_File_Close(FSFile *log_fd) +{ + FS_FlushFile(log_fd); + FS_CloseFile(log_fd); +} + +/*---------------------------------------------------------------------------* + Name: LoadFile + + Description: 内部でメモリを確保しファイルを読み込みます。 + + Arguments: path: 読み込むファイルのパス。 + + Returns: ファイルが存在するならファイルの内容が読み込まれた + 内部で確保したバッファへのポインタを返します。 + このポインタは FS_Free で解放する必要があります。 + *---------------------------------------------------------------------------*/ +static BOOL LoadFile(const char* path, char **alloc_ptr, int *alloc_size, FSFile *log_fd) +{ + FSFile f; + BOOL bSuccess; + char* pBuffer; + u32 fileSize; + s32 readSize = 0; + + if( alloc_ptr == 0 || alloc_size == NULL ) { + miya_log_fprintf(log_fd, "Failed LoadFile argument error\n"); + return FALSE; + } + + FS_InitFile(&f); + + bSuccess = FS_OpenFileEx(&f, path, FS_FILEMODE_R); + if( ! bSuccess ) { + miya_log_fprintf(log_fd, "Failed Open File %s\n",__FUNCTION__); + miya_log_fprintf(log_fd, " path=%s\n", path ); + miya_log_fprintf(log_fd, " res=%s\n", my_fs_util_get_fs_result_word( FS_GetArchiveResultCode(path) )); + return FALSE; + } + + fileSize = FS_GetFileLength(&f); + pBuffer = (char*)OS_Alloc(fileSize + 1); + if( pBuffer == NULL ) { + miya_log_fprintf(log_fd, "Mem alloc error: %s\n", __FUNCTION__); + return FALSE; + } + + readSize = FS_ReadFile(&f, pBuffer, (s32)fileSize); + if( readSize != fileSize ) { + miya_log_fprintf(log_fd, "Failed Read File: %s\n",path); + } + + *alloc_size = (int)readSize; + + bSuccess = FS_CloseFile(&f); + if( ! bSuccess ) { + miya_log_fprintf(log_fd, "Failed Close File\n"); + miya_log_fprintf(log_fd, " %s\n", my_fs_util_get_fs_result_word( FS_GetArchiveResultCode(path))); + } + + pBuffer[fileSize] = '\0'; + *alloc_ptr = pBuffer; + return TRUE; +} + +static BOOL SaveFile(const char* path, void* pData, u32 size, FSFile *log_fd) +{ + FSFile f; + BOOL bSuccess; + FSResult fsResult; + s32 writtenSize; + + FS_InitFile(&f); + + bSuccess = FS_OpenFileEx(&f, path, FS_FILEMODE_W); + if (bSuccess == FALSE) { + FSResult res = FS_GetArchiveResultCode(path); + if( res == FS_RESULT_NO_ENTRY ) { + /* 本来ここで問題なし */ + } + else { + miya_log_fprintf(log_fd, "Failed open file:%s %d %s %s\n", + __FUNCTION__,__LINE__, path, my_fs_util_get_fs_result_word(res) ); + } + } + else { + FS_CloseFile(&f); + /* backup バックアップを取っておくべき?? */ + FS_DeleteFile(path); + } + + FS_CreateFile(path, (FS_PERMIT_R|FS_PERMIT_W)); + bSuccess = FS_OpenFileEx(&f, path, FS_FILEMODE_W); + if (bSuccess == FALSE) { + FSResult res = FS_GetArchiveResultCode(path); + miya_log_fprintf(log_fd,"Failed open file:%s %d %s %s\n", + __FUNCTION__,__LINE__, path, my_fs_util_get_fs_result_word(res) ); + return FALSE; + } + + fsResult = FS_SetFileLength(&f, 0); + if( fsResult != FS_RESULT_SUCCESS ) { + } + + writtenSize = FS_WriteFile(&f, pData, (s32)size); + if( writtenSize != size ) { + } + + FS_FlushFile(&f); + bSuccess = FS_CloseFile(&f); + if( bSuccess ) { + + } + return TRUE; +} + +static BOOL CopyFile(const char *dst_path, const char *src_path, FSFile *log_fd ) +{ + char *alloc_ptr = NULL; + int alloc_size = 0; + + if( TRUE == LoadFile(src_path, &alloc_ptr, &alloc_size, log_fd) ) { + if( TRUE == SaveFile(dst_path, alloc_ptr, (u32)alloc_size, log_fd) ) { + if( alloc_ptr ) { + OS_Free(alloc_ptr); + } + return TRUE; + } + if( alloc_ptr ) { + OS_Free(alloc_ptr); + } + return FALSE; + } + return FALSE; +} + +static BOOL CompareFsDateTime( FSDateTime *dt1, FSDateTime *dt2) +{ + if( dt1->year != dt2->year ) { + return FALSE; + } + if( dt1->month != dt2->month ) { + return FALSE; + } + if( dt1->day != dt2->day ) { + return FALSE; + } + if( dt1->hour != dt2->hour ) { + return FALSE; + } + if( dt1->minute != dt2->minute ) { + return FALSE; + } + if( dt1->second != dt2->second ) { + return FALSE; + } + return TRUE; +} + +static BOOL restore_entry_list(MY_DIR_ENTRY_LIST **headp, FSFile *log_fd) +{ + MY_DIR_ENTRY_LIST *list_temp; + MY_DIR_ENTRY_LIST *list_temp2; + FSPathInfo path_info; + int count = 0; + /* トップディレクトリの方から順にリストされているので */ + /* リストの後ろ(下位ディレクトリの方)から戻さないといけない */ + /* バックワードで戻していく */ + + if( *headp == NULL ) { + } + else { + for( list_temp = *headp ; list_temp->next != NULL ; list_temp = list_temp->next ) { + ; + } + while( list_temp != NULL ) { + path_info.attributes = list_temp->content.attributes; + path_info.ctime = list_temp->content.ctime; + path_info.mtime = list_temp->content.mtime; + path_info.atime = list_temp->content.atime; + path_info.id = list_temp->content.id; + path_info.filesize = list_temp->content.filesize; + if( FALSE == FS_SetPathInfo( list_temp->src_path, &path_info) ) { + FSResult fsResult = FS_GetArchiveResultCode(list_temp->src_path); + miya_log_fprintf(log_fd, "%s %d: Failed SetPathInfo\n", __FUNCTION__,__LINE__ ); + miya_log_fprintf(log_fd, " %s\n", list_temp->src_path ); + miya_log_fprintf(log_fd, " %s\n", my_fs_util_get_fs_result_word( fsResult ) ); + } + count++; + list_temp2 = list_temp; + list_temp = list_temp->prev; + OS_Free( list_temp2 ); + } + } + return TRUE; +} + + +static BOOL add_entry_list( MY_DIR_ENTRY_LIST **headp, MY_DIR_ENTRY_LIST *entry_list ) +{ + MY_DIR_ENTRY_LIST *list_temp; + MY_DIR_ENTRY_LIST *list_prev_temp; + + if( entry_list == NULL ) { + return FALSE; + } + if( headp == NULL ) { + return FALSE; + } + + if( *headp == NULL ) { + *headp = (MY_DIR_ENTRY_LIST *)OS_Alloc( sizeof(MY_DIR_ENTRY_LIST) ); + STD_CopyMemory( (void *)(*headp), (void *)entry_list , sizeof(MY_DIR_ENTRY_LIST) ); + (*headp)->prev = NULL; + (*headp)->next = NULL; + } + else { + for( list_temp = *headp ; list_temp->next != NULL ; list_temp = list_temp->next ) { + ; + } + list_temp->next = (MY_DIR_ENTRY_LIST *)OS_Alloc( sizeof(MY_DIR_ENTRY_LIST) ); + + list_prev_temp = list_temp; + list_temp = list_temp->next; + STD_CopyMemory( (void *)list_temp, (void *)entry_list , sizeof(MY_DIR_ENTRY_LIST) ); + list_temp->prev = list_prev_temp; + list_temp->next = NULL; + } + return TRUE; +} + +static BOOL my_fs_add_list( MY_DIR_ENTRY_LIST **headp, FSDirectoryEntryInfo *entry, + const char *src_path, const char *dst_path, FSFile *log_fd) +{ + MY_DIR_ENTRY_LIST *list_temp; + MY_DIR_ENTRY_LIST *list_prev_temp; + FSPathInfo path_info; + if( entry == NULL ) { + return FALSE; + } + + /* ファイルの場合 srcがNAND, dstがSD */ + if( !STD_StrCmp( src_path, "nand:" ) ) { + /* nandのルートディレクトリはスルーする。 */ + return TRUE; + } + + + if( FALSE == FS_GetPathInfo(src_path,&path_info) ) { + miya_log_fprintf(log_fd, "%s %d: Failed GetPathInfo\n", __FUNCTION__,__LINE__ ); + miya_log_fprintf(log_fd, " %s\n", src_path ); + } + else { + if( FALSE == CompareFsDateTime( &(entry->atime), &(path_info.atime)) ) { + miya_log_fprintf(log_fd, "warning atime\n"); + miya_log_fprintf(log_fd," entry %d/%02d/%02d ", entry->atime.year,entry->atime.month,entry->atime.day); + miya_log_fprintf(log_fd,"%02d:%02d:%02d\n", entry->atime.hour,entry->atime.minute,entry->atime.second); + miya_log_fprintf(log_fd," path_info %d/%02d/%02d ", path_info.atime.year,path_info.atime.month,path_info.atime.day); + miya_log_fprintf(log_fd,"%02d:%02d:%02d\n", path_info.atime.hour,path_info.atime.minute,path_info.atime.second); + + entry->atime = path_info.atime; + + } + + if( FALSE == CompareFsDateTime( &(entry->mtime), &(path_info.mtime)) ) { + miya_log_fprintf(log_fd, "warning mtime\n"); + miya_log_fprintf(log_fd," entry %d/%02d/%02d ", entry->mtime.year,entry->mtime.month,entry->mtime.day); + miya_log_fprintf(log_fd,"%02d:%02d:%02d\n", entry->mtime.hour,entry->mtime.minute,entry->mtime.second); + miya_log_fprintf(log_fd," path_info %d/%02d/%02d ", path_info.mtime.year,path_info.mtime.month,path_info.mtime.day); + miya_log_fprintf(log_fd,"%02d:%02d:%02d\n", path_info.mtime.hour,path_info.mtime.minute,path_info.mtime.second); + + entry->mtime = path_info.mtime; + } + +#if 0 + /* なぜがWarningが出る */ +OS_TPrintf("%s %d\n",__FUNCTION__,__LINE__); + if( FALSE == CompareFsDateTime( &(entry->ctime), &(path_info.ctime)) ) { + miya_log_fprintf(log_fd, "warning ctime\n"); + miya_log_fprintf(log_fd," entry %d/%02d/%02d ", entry->ctime.year,entry->ctime.month,entry->ctime.day); + miya_log_fprintf(log_fd,"%02d:%02d:%02d\n", entry->ctime.hour,entry->ctime.minute,entry->ctime.second); + miya_log_fprintf(log_fd," path_info %d/%02d/%02d ", path_info.ctime.year,path_info.ctime.month,path_info.ctime.day); + miya_log_fprintf(log_fd,"%02d:%02d:%02d\n", path_info.ctime.hour,path_info.ctime.minute,path_info.ctime.second); + } +OS_TPrintf("%s %d\n",__FUNCTION__,__LINE__); +#endif + // OS_TPrintf("path info att=0x%08x\n",path_info.attributes); + if( entry->attributes != path_info.attributes ) { + miya_log_fprintf(log_fd, "Warning: path att = 0x%08x\n",path_info.attributes); + miya_log_fprintf(log_fd, " entry att = 0x%08x\n",entry->attributes); + } + entry->attributes = path_info.attributes; + } + + + if( *headp == NULL ) { + *headp = (MY_DIR_ENTRY_LIST *)OS_Alloc( sizeof(MY_DIR_ENTRY_LIST) ); + STD_MemSet((void *)*headp, 0, sizeof(MY_DIR_ENTRY_LIST) ); + + (*headp)->prev = NULL; + (*headp)->next = NULL; + STD_CopyMemory( (void *)&((*headp)->content), (void *)entry ,sizeof(FSDirectoryEntryInfo) ); + STD_StrCpy((*headp)->src_path, src_path); + if( dst_path ) { + STD_StrCpy((*headp)->dst_path, dst_path); + } + } + else { + for( list_temp = *headp ; list_temp->next != NULL ; list_temp = list_temp->next ) { + ; + } + list_temp->next = (MY_DIR_ENTRY_LIST *)OS_Alloc( sizeof(MY_DIR_ENTRY_LIST) ); + STD_MemSet((void *)list_temp->next, 0, sizeof(MY_DIR_ENTRY_LIST) ); + + list_prev_temp = list_temp; + list_temp = list_temp->next; + list_temp->prev = list_prev_temp; + list_temp->next = NULL; + STD_CopyMemory( (void *)&(list_temp->content), (void *)entry ,sizeof(FSDirectoryEntryInfo) ); + STD_StrCpy(list_temp->src_path, src_path); + if( dst_path ) { + STD_StrCpy(list_temp->dst_path, dst_path); + } + } + + return TRUE; +} + +BOOL ClearDirEntryList( MY_DIR_ENTRY_LIST **headp ) +{ + MY_DIR_ENTRY_LIST *list_temp1 = *headp; + MY_DIR_ENTRY_LIST *list_temp2; + + *headp = NULL; + + while( list_temp1 ) { + list_temp2 = list_temp1->next; + OS_Free( list_temp1 ); + list_temp1 = list_temp2; + } + return TRUE; +} + +void PrintDirEntryListForward( MY_DIR_ENTRY_LIST *head, FSFile *log_fd ) +{ + MY_DIR_ENTRY_LIST *list_temp; + // char *log_file_name = "sdmc:/miya/save_dir_entry_log.txt"; + miya_log_fprintf(log_fd, "PrintDirEntryListForward-----Start\n"); + if( head == NULL ) { + } + else { + for( list_temp = head ; list_temp != NULL ; list_temp = list_temp->next ) { + if( list_temp->src_path ) { + miya_log_fprintf(log_fd, "src name = %s\n", list_temp->src_path ); + } + if( list_temp->dst_path ) { + miya_log_fprintf(log_fd, "dst name = %s\n", list_temp->dst_path ); + } + } + } + miya_log_fprintf(log_fd, "PrintDirEntryListForward-----End\n"); +} + +void PrintDirEntryListBackward( MY_DIR_ENTRY_LIST *head, FSFile *log_fd) +{ + MY_DIR_ENTRY_LIST *list_temp; + // MY_DIR_ENTRY_LIST *list_prev; + miya_log_fprintf(log_fd, "PrintDirEntryListBackword-----Start\n"); + if( head == NULL ) { + } + else { + for( list_temp = head ; list_temp->next != NULL ; list_temp = list_temp->next ) { + ; + } + for( ; list_temp != NULL ; list_temp = list_temp->prev ) { + if( list_temp->src_path ) { + miya_log_fprintf(log_fd, "src name = %s\n", list_temp->src_path ); + } + if( list_temp->dst_path ) { + miya_log_fprintf(log_fd, "dst name = %s\n", list_temp->dst_path ); + } + } + } + miya_log_fprintf(log_fd, "PrintDirEntryListBackward-----End\n"); +} + + +static BOOL CheckSystemApp(char path[]) +{ + char c; + int num; + /* + No. 0 0003000f484e4c41 + No. 1 0003000f484e4841 + No. 2 0003000f484e4341 + No. 3 00030015484e4241 + No. 4 00030017484e4141 launcher + ^ + | ここの最下位ビットが1のやつがシステムアプリ + | + システムアプリはダウンロード対象外 + */ + c = path[19]; + if( ('a' <= c) && (c <= 'f') ) { + num = (int)( c - 'a' + 10 ); + } + else if( ('0' <= c) && (c <= '9') ) { + num = (int)( c - '0' ); + } + else { + num = 0; + } + + if( num & 1 ) { + /* System App. */ + return TRUE; + } + else { + /* User App. */ + return FALSE; + } +} + +void GetDirEntryList( MY_DIR_ENTRY_LIST *head, void **pBuffer, int *size) +{ + int i; + int count = 0; + MY_DIR_ENTRY_LIST *list_temp; + char *buf; + + + if( head == NULL ) { + } + else { + for( list_temp = head ; list_temp->next != NULL ; list_temp = list_temp->next ) { + if( list_temp->src_path ) { + if( FALSE == CheckSystemApp( list_temp->src_path) ) { + count++; + } + } + } + if( count ) { + buf = (char *)OS_Alloc( (u32)(count * 16) ); + STD_MemSet((void *)buf, 0, sizeof(count * 16)); + } + else { + buf = NULL; + } + *pBuffer = (void *)buf; + *size = count; + + + count = 0; + for( ; list_temp != NULL ; list_temp = list_temp->prev ) { + if( list_temp->src_path ) { + /* + No. 0 0003000f484e4c41 + No. 1 0003000f484e4841 + No. 2 0003000f484e4341 + No. 3 00030015484e4241 + No. 4 00030017484e4141 launcher + ^ + | ここの最下位ビットが1のやつがシステムアプリ + | + システムアプリはダウンロード対象外 + */ + if( FALSE == CheckSystemApp( list_temp->src_path ) ) { + count++; + /* User App. */ + for( i = 0 ; i < 8 ; i++ ) { + *buf++ = list_temp->src_path[12 + i]; + } + for( i = 0 ; i < 8 ; i++ ) { + *buf++ = list_temp->src_path[21 + i]; + } + } + OS_TPrintf("User App. count = %d\n", count); + /* + nand:/title/00030017/484e4141 は ランチャー + nand:/title/00030015/484e4641 は shop + nand:/title/00030015/484e4241 は 本体設定 + */ + } + } + } +} + + +void PrintSrcDirEntryListBackward( MY_DIR_ENTRY_LIST *head, FSFile *log_fd) +{ + MY_DIR_ENTRY_LIST *list_temp; + // MY_DIR_ENTRY_LIST *list_prev; + miya_log_fprintf(log_fd, "PrintSrcDirEntryListBackword-----Start\n"); + if( head == NULL ) { + } + else { + for( list_temp = head ; list_temp->next != NULL ; list_temp = list_temp->next ) { + ; + } + for( ; list_temp != NULL ; list_temp = list_temp->prev ) { + if( list_temp->src_path ) { + miya_log_fprintf(log_fd, "%s\n", list_temp->src_path ); + /* + nand:/title/00030017/484e4141 は ランチャー + nand:/title/00030015/484e4641 は shop + nand:/title/00030015/484e4241 は 本体設定 + */ + } + } + } + miya_log_fprintf(log_fd, "PrintSrcDirEntryListBackward-----End\n"); +} + + + + + +BOOL SaveDirEntryList( MY_DIR_ENTRY_LIST *head , char *path ) +{ + FSFile f; + FSFile f_src; + FSFile f_dst; + BOOL bSuccess; + FSResult fsResult; + s32 writtenSize; + MY_DIR_ENTRY_LIST *list_temp; + int list_count = 0; + + FSFile log_fd; + BOOL log_active = FALSE; + // char *log_file_name = "sdmc:/miya/save_dir_entry_log.txt"; + char *log_file_name = NULL; + + /* ここでSDカードがあるかどうか調べる */ + + log_active = Log_File_Open( &log_fd, log_file_name ); + + /* 最初にSD側のルートディレクトリのデータを消しとくべきか? + せっかくファイルリストに記録してるのでもったいない→必要ない */ + FS_InitFile(&f); + FS_InitFile(&f_src); + FS_InitFile(&f_dst); + + if( path == NULL ) { + miya_log_fprintf(&log_fd, "%s %d not specify entry save file\n",__FUNCTION__,__LINE__ ); + return FALSE; + } + FS_CreateFileAuto(path, (FS_PERMIT_R|FS_PERMIT_W)); + bSuccess = FS_OpenFileEx(&f, path, FS_FILEMODE_W); + if (bSuccess == FALSE) { + fsResult = FS_GetArchiveResultCode(path); + miya_log_fprintf(&log_fd, "Failed create file - dir entry list file:%d\n", fsResult ); + return FALSE; + } + + fsResult = FS_SetFileLength(&f, 0); + if( fsResult != FS_RESULT_SUCCESS ) { + miya_log_fprintf(&log_fd, "Failed set file len - dir entry list file:%d\n", fsResult ); + } + + /* バックワードでファイルに保存 */ + if( head == NULL ) { + } + else { + for( list_temp = head ; list_temp->next != NULL ; list_temp = list_temp->next ) { + ; + } + for( ; list_temp != NULL ; list_temp = list_temp->prev ) { + // OS_TPrintf( "name = %s\n", list_temp->src_path ); + /* SDにログを残す場合 */ + if( log_active ) { + miya_log_fprintf(&log_fd, "%s\n", list_temp->src_path); + } + + writtenSize = FS_WriteFile(&f, (void *)list_temp, (s32)sizeof(MY_DIR_ENTRY_LIST) ); + if( writtenSize != sizeof(MY_DIR_ENTRY_LIST) ) { + miya_log_fprintf(&log_fd, "%s %d: Failed write file\n", __FUNCTION__ , __LINE__ ); + miya_log_fprintf(&log_fd, " %s\n", path); + miya_log_fprintf(&log_fd, " entry count %d\n", list_count ); + } + + /* SD側にディレクトリの作成とファイルのコピー */ + if( (list_temp->content.attributes & FS_ATTRIBUTE_IS_DIRECTORY) != 0 ) { + /* ディレクトリの場合 */ + bSuccess = FS_CreateDirectoryAuto(list_temp->dst_path, FS_PERMIT_RW); + // bSuccess = FS_CreateDirectory(list_temp->dst_path, FS_PERMIT_RW); + if(!bSuccess) { + fsResult = FS_GetArchiveResultCode(list_temp->dst_path); + if( fsResult != FS_RESULT_ALREADY_DONE ) { + miya_log_fprintf(&log_fd, "%s %d: Failed Create DST Directory\n", __FUNCTION__ , __LINE__ ); + miya_log_fprintf(&log_fd, " %s\n", list_temp->dst_path); + miya_log_fprintf(&log_fd, " %s\n", my_fs_util_get_fs_result_word( fsResult ) ); + return FALSE; + } + } + } + else { + /* ファイルの場合 srcがNAND, dstがSD */ + if( !STD_StrCmp( list_temp->src_path, "nand:" ) ) { + /* nandのルートディレクトリはスルーする。 */ + } + else { + /* NANDからSDにコピーする */ + // CopyFile( dst <= src ); + CopyFile(list_temp->dst_path, list_temp->src_path, &log_fd ); + } + } + list_count++; + } + } + + FS_FlushFile(&f); + + if( FS_CloseFile(&f) == FALSE) { + miya_log_fprintf(&log_fd, "%s %d: Failed Close file\n", __FUNCTION__ , __LINE__ ); + miya_log_fprintf(&log_fd, " %s\n", path); + miya_log_fprintf(&log_fd, " %s\n", my_fs_util_get_fs_result_word( FS_GetArchiveResultCode(path) ) ); + } + miya_log_fprintf(NULL, "write etnry list count %d\n", list_count); + + if( log_active ) { + Log_File_Close(&log_fd); + } + return TRUE; +} + +/******************************************** + * NANDにディレクトリエントリとファイルを復活させる。 +*********************************************/ +BOOL RestoreDirEntryList( char *path ) +{ + FSFile f; + FSFile f_dir; + BOOL bSuccess; + FSResult fsResult; + s32 readSize; + MY_DIR_ENTRY_LIST list_temp; + FSPathInfo path_info; + int list_count = 0; + MY_DIR_ENTRY_LIST *readonly_list_head = NULL; + + FSFile log_fd; + BOOL log_active = FALSE; + char *log_file_name = "sdmc:/miya/restore_dir_entry_log.txt"; + + /* ここでSDカードがあるかどうか調べる */ + + log_active = Log_File_Open( &log_fd, log_file_name ); + + FS_InitFile(&f); + FS_InitFile(&f_dir); + + if( FS_OpenFileEx(&f, path, FS_FILEMODE_R) == FALSE) { + fsResult = FS_GetArchiveResultCode(path); + miya_log_fprintf(&log_fd, "%s %d: Failed Open file\n", __FUNCTION__ , __LINE__ ); + miya_log_fprintf(&log_fd, " %s\n", path); + miya_log_fprintf(&log_fd, " %s\n", my_fs_util_get_fs_result_word( fsResult ) ); + return FALSE; + } + + while( 1 ) { + /* リストはルートディレクトリに近い順から入っている */ + readSize = FS_ReadFile(&f, (void *)&list_temp, (s32)sizeof(MY_DIR_ENTRY_LIST) ); + if( readSize == 0 ) { + miya_log_fprintf(&log_fd, "Read entry count %d\n", list_count ); + break; + } + else if( readSize != (s32)sizeof(MY_DIR_ENTRY_LIST) ) { + miya_log_fprintf(&log_fd, "%s %d: Failed Read file\n", __FUNCTION__ , __LINE__ ); + miya_log_fprintf(&log_fd, " %s\n", path); + miya_log_fprintf(&log_fd, " read entry count %d\n", list_count ); + break; + } + + list_count++; + + /* NAND側にディレクトリの作成とファイルのコピー */ + if( (list_temp.content.attributes & FS_ATTRIBUTE_IS_DIRECTORY) != 0 ) { + /* ディレクトリの場合 */ + if( TRUE == FS_GetPathInfo(list_temp.src_path, &path_info) ) { + /* 復元される側にすでにディレクトリエントリがある場合 */ + if( (path_info.attributes & FS_ATTRIBUTE_IS_DIRECTORY) == 0 ) { + /* ディレクトリでない場合 エラー */ + /* SDにログを残す場合 */ + if( log_active ) { + miya_log_fprintf(&log_fd, "%s %d: NOT a directory\n", __FUNCTION__ , __LINE__ ); + miya_log_fprintf(&log_fd, " %s\n", list_temp.src_path ); + } + /* require backup file */ + /* パニック?? */ + /* それとも一度デリートする?? */ + } + } + else { + /* 復元される側にディレクトリエントリがない場合 */ + bSuccess = FS_CreateDirectory(list_temp.src_path, FS_PERMIT_RW); + if(!bSuccess) { + fsResult = FS_GetArchiveResultCode(list_temp.src_path); + if( fsResult != FS_RESULT_ALREADY_DONE ) { + if( log_active ) { + miya_log_fprintf(&log_fd, "%s %d: Failed Create NAND Directory\n", __FUNCTION__,__LINE__); + miya_log_fprintf(&log_fd, " %s\n", my_fs_util_get_fs_result_word( fsResult ) ); + miya_log_fprintf(&log_fd, " %s\n", list_temp.src_path); + } + } + } + if( FALSE == FS_GetPathInfo(list_temp.src_path, &path_info) ) { + if( log_active ) { + miya_log_fprintf(&log_fd, "%s %d: Failed GetPathInfo\n", __FUNCTION__,__LINE__ ); + miya_log_fprintf(&log_fd, " %s\n", list_temp.src_path ); + } + // return FALSE; + } + } + + /* このディレクトリを記憶しておき、あとで属性をまとめて戻す */ + if( FALSE == add_entry_list( &readonly_list_head, &list_temp ) ) { + miya_log_fprintf(&log_fd, "%s %d: ERROR: add_entry_list\n", __FUNCTION__,__LINE__ ); + miya_log_fprintf(&log_fd, " %s\n", list_temp.src_path ); + } + + if( (path_info.attributes & FS_ATTRIBUTE_DOS_READONLY) != 0 ) { + /* リードオンリーの場合,一度リードライト可能にする */ + path_info.attributes &= ~FS_ATTRIBUTE_DOS_READONLY; + if( FALSE == FS_SetPathInfo( list_temp.src_path, &path_info) ) { + fsResult = FS_GetArchiveResultCode(list_temp.src_path); + miya_log_fprintf(&log_fd, "%s %d: Failed SetPathInfo\n", __FUNCTION__,__LINE__ ); + miya_log_fprintf(&log_fd, " %s\n", list_temp.src_path ); + miya_log_fprintf(&log_fd, " %s\n", my_fs_util_get_fs_result_word( fsResult ) ); + } + } + } + else { + /* ファイルの場合 srcがSD, dstがNAND */ + if( !STD_StrCmp( list_temp.src_path, "nand:" ) ) { + /* nandのルートディレクトリはスルーする。 */ + } + else { + + /* とりあえずデスティネーションファイルがあるかどうか確認して + あればSDにバックアップする */ + // CopyFile( dst <= src ); + CopyFile( list_temp.src_path, list_temp.dst_path, &log_fd ); + + path_info.attributes = list_temp.content.attributes; + path_info.ctime = list_temp.content.ctime; + path_info.mtime = list_temp.content.mtime; + path_info.atime = list_temp.content.atime; + path_info.id = list_temp.content.id; + path_info.filesize = list_temp.content.filesize; + if( FALSE == FS_SetPathInfo( list_temp.src_path, &path_info) ) { + fsResult = FS_GetArchiveResultCode(list_temp.src_path); + miya_log_fprintf(&log_fd, "%s %d: Failed SetPathInfo\n", __FUNCTION__,__LINE__ ); + miya_log_fprintf(&log_fd, " %s\n", list_temp.src_path ); + miya_log_fprintf(&log_fd, " %s\n", my_fs_util_get_fs_result_word( fsResult ) ); + } + } + } + } + + /* add_entry_list( &readonly_list_head, &list_temp ); + でリストにしたエントリーのアトリビュートを逆順で元に戻す。*/ + if( FALSE == restore_entry_list(&readonly_list_head, &log_fd) ) { + miya_log_fprintf(&log_fd, "%s %d: ERROR: restore_entry_list\n", __FUNCTION__,__LINE__ ); + } + + exit_label: + + // FS_FlushFile(&f); //リードだからいらない + if( FS_CloseFile(&f) == FALSE) { + fsResult = FS_GetArchiveResultCode(path); + miya_log_fprintf(&log_fd, "%s %d: Failed Close file\n", __FUNCTION__ , __LINE__ ); + miya_log_fprintf(&log_fd, " %s\n", path); + miya_log_fprintf(&log_fd, " %s\n", my_fs_util_get_fs_result_word( fsResult ) ); + } + + if( log_active ) { + Log_File_Close(&log_fd); + } + + return TRUE; +} + +// = "nand:/title"; + +static BOOL my_fs_is_Title_Hi_dir_name(const char *name) +{ + char *str; + int i; + str = (char *)name; + if( STD_StrLen(name) != 8 /* 9 ? */) { + return FALSE; + } + + for( i = 0 ; i < 8 ; i++ ) { + if( (( '0' <= *str ) && (* str <= '9')) || + (( 'a' <= *str ) && (* str <= 'f')) || + (( 'A' <= *str ) && (* str <= 'F')) ) { + } + else { + return FALSE; + } + str++; + } + return TRUE; +} + +static BOOL my_fs_is_Title_Lo_dir_name(const char *name) +{ + char *str; + int i; + str = (char *)name; + if( STD_StrLen(name) != 8 /* 9 ? */) { + return FALSE; + } + + for( i = 0 ; i < 8 ; i++ ) { + if( (( '0' <= *str ) && (* str <= '9')) || + (( 'a' <= *str ) && (* str <= 'f')) || + (( 'A' <= *str ) && (* str <= 'F')) ) { + } + else { + return FALSE; + } + str++; + } + return TRUE; +} + +static BOOL Path_Buffers_Init(const char *path_src, const char *path_dst, + char **path_src_dir, char **path_src_full, + char **path_dst_dir, char **path_dst_full, + FSFile *log_fd) +{ + *path_src_dir = (char *)OS_Alloc( FILE_PATH_LEN ); + if( *path_src_dir == NULL ) { + miya_log_fprintf(log_fd, "Error: alloc error src_dir\n"); + return FALSE; + } + *path_src_full = (char *)OS_Alloc( FILE_PATH_LEN ); + if( *path_src_full == NULL ) { + miya_log_fprintf(log_fd, "Error: alloc error src_full\n"); + return FALSE; + } + *path_dst_dir = (char *)OS_Alloc( FILE_PATH_LEN ); + if( *path_dst_dir == NULL ) { + miya_log_fprintf(log_fd, "Error: alloc error dst_dir\n"); + return FALSE; + } + *path_dst_full = (char *)OS_Alloc( FILE_PATH_LEN ); + if( *path_dst_full == NULL ) { + miya_log_fprintf(log_fd, "Error: alloc error dst_full\n"); + return FALSE; + } + + STD_MemSet((void *)*path_src_dir, 0, FILE_PATH_LEN); + STD_MemSet((void *)*path_src_full, 0, FILE_PATH_LEN); + STD_MemSet((void *)*path_dst_dir, 0, FILE_PATH_LEN); + STD_MemSet((void *)*path_dst_full, 0, FILE_PATH_LEN); + + STD_StrCpy(*path_src_dir, path_src); + STD_StrCat(*path_src_dir, "/"); + STD_StrCpy(*path_dst_dir, path_dst); + STD_StrCat(*path_dst_dir, "/"); + + return TRUE; +} + +static void Path_Buffers_Clean( char *path_src_dir, char *path_src_full, + char *path_dst_dir,char *path_dst_full) +{ + if( path_src_dir != NULL ) { + OS_Free(path_src_dir); + } + if( path_src_full != NULL ) { + OS_Free(path_src_full); + } + if( path_dst_dir != NULL ) { + OS_Free(path_dst_dir); + } + if( path_dst_full != NULL ) { + OS_Free(path_dst_full); + } +} + +BOOL MydataLoad(const char *path, void *pBuffer, int size, FSFile *log_fd) +{ + FSFile f; + BOOL bSuccess; + // u32 fileSize; + s32 readSize = 0; + + FS_InitFile(&f); + + bSuccess = FS_OpenFileEx(&f, path, FS_FILEMODE_R); + if( ! bSuccess ) { + miya_log_fprintf(log_fd, "Failed Open File %s\n",__FUNCTION__); + miya_log_fprintf(log_fd, " path=%s\n", path ); + miya_log_fprintf(log_fd, " res=%s\n", my_fs_util_get_fs_result_word( FS_GetArchiveResultCode(path) )); + return FALSE; + } + readSize = FS_ReadFile(&f, pBuffer, (s32)size); + if( readSize != size ) { + miya_log_fprintf(log_fd, "Failed Read File: %s\n",path); + } + bSuccess = FS_CloseFile(&f); + if( ! bSuccess ) { + miya_log_fprintf(log_fd, "Failed Close File\n"); + miya_log_fprintf(log_fd, " %s\n", my_fs_util_get_fs_result_word( FS_GetArchiveResultCode(path))); + } + + return TRUE; +} + + +BOOL MydataSave(const char *path, void *pData, int size, FSFile *log_fd) +{ +#pragma unused(log_fd) + + FSFile f; + // BOOL flag; + BOOL bSuccess; + FSResult res; + FSResult fsResult; + s32 writtenSize; + + FS_InitFile(&f); + + bSuccess = FS_OpenFileEx(&f, path, FS_FILEMODE_W); + if( ! bSuccess ) { + FS_CreateFileAuto( path, FS_PERMIT_W); + bSuccess = FS_OpenFileEx(&f, path , FS_FILEMODE_W ); + if( ! bSuccess ) { + res = FS_GetArchiveResultCode( path ); + miya_log_fprintf(NULL, "log file open error %s\n", path ); + miya_log_fprintf(NULL, " Failed open file:%s\n", my_fs_util_get_fs_result_word( res )); + return FALSE; + } + } + + fsResult = FS_SetFileLength(&f, 0); + if( fsResult != FS_RESULT_SUCCESS ) { + } + + writtenSize = FS_WriteFile(&f, pData, (s32)size); + if( writtenSize != size ) { + } + + FS_FlushFile(&f); + bSuccess = FS_CloseFile(&f); + if( bSuccess ) { + + } + return TRUE; +} + +/* SDカードがあるかどうか */ +BOOL SDCardValidation(void) +{ + FSFile f; + BOOL flag; + FS_InitFile(&f); + if(FS_OpenDirectory(&f, "sdmc:/", FS_PERMIT_R) == FALSE ) { + flag = FALSE; + } + else { + flag = TRUE; + } + (void)FS_CloseDirectory(&f); + return flag; +} + + +/* 過去にショップに接続したかどうか */ +BOOL CheckShopRecord(FSFile *log_fd) +{ +#pragma unused(log_fd) + + FSFile f; + BOOL bSuccess; + char path[256]; + + FS_InitFile(&f); + + STD_StrCpy(path, "nand:/sys/dev.kp"); + bSuccess = FS_OpenFileEx(&f, path, (FS_FILEMODE_R)); + if( ! bSuccess ) { + if( FS_RESULT_NO_ENTRY == FS_GetArchiveResultCode(path) ) { + } + return FALSE; + } + (void)FS_CloseFile(&f); + +#if 0 + STD_StrCpy(path, "nand:/sys/log/shop.log"); + bSuccess = FS_OpenFileEx(&f, path, (FS_FILEMODE_R)); + if( ! bSuccess ) { + if( FS_RESULT_NO_ENTRY == FS_GetArchiveResultCode(path) ) { + } + return FALSE; + } + (void)FS_CloseFile(&f); +#endif + + // STD_StrCpy(path, "nand:/title/00030015/484e4641/data/ec.cfg"); /* ショップアカウント情報 */ + STD_StrCpy(path, "nand:/title/00030015/484e464a/data/ec.cfg"); /* ショップアカウント情報 */ + bSuccess = FS_OpenFileEx(&f, path, (FS_FILEMODE_R)); + if( ! bSuccess ) { + if( FS_RESULT_NO_ENTRY == FS_GetArchiveResultCode(path) ) { + } + return FALSE; + } + (void)FS_CloseFile(&f); + + + return TRUE; + +} + + +int get_title_id(MY_DIR_ENTRY_LIST **headp, const char *path_src, int *save_parent_dir_info_flag, char *log_file_name ) +{ + static int recursive_count = 0; + static FSFile log_fd; + static BOOL log_active = FALSE; + // static char *log_file_name = "sdmc:/miya/nandinfo_find_title_save_data.txt"; + + FSFile f_src; + FSDirectoryEntryInfo entry_src; + FSDirectoryEntryInfo entry_current_dir; + int ret_value = 0; + // FSResult fs_ret_value; + int save_parent_dir_info_flag_temp = 0; + + char *path_src_dir = NULL; + char *path_src_full = NULL; + + /* ここでSDカードがあるかどうか調べる */ + + if( recursive_count == 0 ) { + log_active = Log_File_Open( &log_fd, log_file_name ); + } + + recursive_count++; + + // recursive_count 1 2 3 4 + // nand:/title + // nand:/title/00000000/00000000/data/*.sav + if( recursive_count > 4 ) { + ret_value = 0; + goto end_process; + } + + /* ソースディレクトリオープン */ + FS_InitFile(&f_src); + + if(FS_OpenDirectory(&f_src, path_src, FS_PERMIT_R) == FALSE ) { + if( log_active ) { + miya_log_fprintf(&log_fd, "%s %d: Failed Open Directory\n", __FUNCTION__ , __LINE__ ); + miya_log_fprintf(&log_fd, " %s\n", path_src); + miya_log_fprintf(&log_fd, " %s\n", my_fs_util_get_fs_result_word( FS_GetArchiveResultCode(path_src) ) ); + } + ret_value = -1; + goto end_process; + } + + /* ファイル名バッファ初期化 */ + path_src_dir = (char *)OS_Alloc( FILE_PATH_LEN ); + if( path_src_dir == NULL ) { + miya_log_fprintf(&log_fd, "Error: alloc error src_dir\n"); + ret_value = -1; + goto end_process; + } + path_src_full = (char *)OS_Alloc( FILE_PATH_LEN ); + if( path_src_full == NULL ) { + miya_log_fprintf(&log_fd, "Error: alloc error src_full\n"); + ret_value = -1; + goto end_process; + } + STD_MemSet((void *)path_src_dir, 0, FILE_PATH_LEN); + STD_MemSet((void *)path_src_full, 0, FILE_PATH_LEN); + STD_StrCpy(path_src_dir, path_src); + STD_StrCat(path_src_dir, "/"); + + while( FS_ReadDirectory(&f_src, &entry_src) ) { + + if( STD_StrCmp(entry_src.longname, ".") == 0 ) { + /* とりあえずカレントディレクトリエントリを残しておく */ + STD_CopyMemory( (void *)&entry_current_dir, (void *)&entry_src ,sizeof(FSDirectoryEntryInfo) ); + } + else if( STD_StrCmp(entry_src.longname, "..") == 0 ) { + } + else { + STD_StrCpy( path_src_full , path_src_dir ); + STD_StrCat( path_src_full , entry_src.longname ); + + /* SDにログを残す場合 */ + if( log_active ) { + // OS_TPrintf("path = %s len=%d att=0x%08x\n",path_src_full,entry_src.filesize, entry_src.attributes); + PrintAttributes(entry_src.attributes, &log_fd); + miya_log_fprintf(&log_fd, " %8d %s\n", entry_src.filesize, path_src_full ); + } + + if( (entry_src.attributes & FS_ATTRIBUTE_IS_DIRECTORY) != 0 ) { + /* ディレクトリの場合 */ + // recursive_count 1 2 3 4 + // nand:/title + // nand:/title/00000000/00000000/data/*.sav + if( recursive_count == 1 ) { + if( my_fs_is_Title_Hi_dir_name( entry_src.longname ) == TRUE ) { + get_title_id( headp, path_src_full , &save_parent_dir_info_flag_temp, log_file_name ); + } + } + if( recursive_count == 2 ) { + if( my_fs_is_Title_Lo_dir_name( entry_src.longname ) == TRUE ) { + get_title_id( headp, path_src_full , &save_parent_dir_info_flag_temp, log_file_name ); + } + } + else if( (recursive_count == 3) ) { + if( !STD_StrCmp( entry_src.longname, "content" ) ) { + get_title_id( headp, path_src_full , &save_parent_dir_info_flag_temp, log_file_name ); + } + } + } + else { + // recursive_count 1 2 3 4 + // nand:/title + // nand:/title/00000000/00000000/content/title.tmd + if( (recursive_count == 4) ) { + if( !STD_StrCmp( entry_src.longname, "title.tmd" ) ) { + /* 目的のファイルを見つけた。 */ +#if 0 + my_fs_add_list(headp, &entry_src, path_src_full, NULL, &log_fd); +#endif + save_parent_dir_info_flag_temp = 1; + } + } + } + } + } + + if( save_parent_dir_info_flag_temp == 1 ) { + // OS_TPrintf("save dir info = %s\n\n",path_src); + if( recursive_count == 3 ) { + my_fs_add_list( headp, &entry_current_dir, path_src, NULL, &log_fd); + } + else { + /* contentディレクトリなど */ + + } + *save_parent_dir_info_flag = 1; + } + + /* ソースディレクトリクローズ */ + if( FS_CloseDirectory(&f_src) == FALSE) { + miya_log_fprintf(&log_fd, "%s %d: Failed Close Directory\n", __FUNCTION__ , __LINE__ ); + miya_log_fprintf(&log_fd, " %s\n", path_src); + miya_log_fprintf(&log_fd, " %s\n", my_fs_util_get_fs_result_word( FS_GetArchiveResultCode(path_src))); + ret_value = -1; + } + + end_process: + if( path_src_dir != NULL ) { + OS_Free(path_src_dir); + } + if( path_src_full != NULL ) { + OS_Free(path_src_full); + } + + recursive_count--; + + if( recursive_count == 0 ) { + if( log_active ) { + Log_File_Close(&log_fd); + } + } + + return ret_value; +} + + + +int find_title_save_data(MY_DIR_ENTRY_LIST **headp, const char *path_dst, const char *path_src, int *save_parent_dir_info_flag, char *log_file_name ) +{ + static int recursive_count = 0; + static FSFile log_fd; + static BOOL log_active = FALSE; + // static char *log_file_name = "sdmc:/miya/nandinfo_find_title_save_data.txt"; + + FSFile f_src; + FSDirectoryEntryInfo entry_src; + FSDirectoryEntryInfo entry_current_dir; + int ret_value = 0; + // FSResult fs_ret_value; + int save_parent_dir_info_flag_temp = 0; + + char *path_src_dir = NULL; + char *path_src_full = NULL; + char *path_dst_dir = NULL; + char *path_dst_full = NULL; + + /* ここでSDカードがあるかどうか調べる */ + + if( recursive_count == 0 ) { + log_active = Log_File_Open( &log_fd, log_file_name ); + } + + recursive_count++; + + // recursive_count 1 2 3 4 + // nand:/title + // nand:/title/00000000/00000000/data/*.sav + if( recursive_count > 4 ) { + ret_value = 0; + goto end_process; + } + + /* ソースディレクトリオープン */ + FS_InitFile(&f_src); + + if(FS_OpenDirectory(&f_src, path_src, FS_PERMIT_R) == FALSE ) { + if( log_active ) { + miya_log_fprintf(&log_fd, "%s %d: Failed Open Directory\n", __FUNCTION__ , __LINE__ ); + miya_log_fprintf(&log_fd, " %s\n", path_src); + miya_log_fprintf(&log_fd, " %s\n", my_fs_util_get_fs_result_word( FS_GetArchiveResultCode(path_src) ) ); + } + ret_value = -1; + goto end_process; + } + + /* ファイル名バッファ初期化 */ + if( FALSE == Path_Buffers_Init(path_src, path_dst, + &path_src_dir, &path_src_full, &path_dst_dir, &path_dst_full, &log_fd ) ) + { + ret_value = -1; + goto end_process; + } + + while( FS_ReadDirectory(&f_src, &entry_src) ) { + + if( STD_StrCmp(entry_src.longname, ".") == 0 ) { + /* とりあえずカレントディレクトリエントリを残しておく */ + STD_CopyMemory( (void *)&entry_current_dir, (void *)&entry_src ,sizeof(FSDirectoryEntryInfo) ); + } + else if( STD_StrCmp(entry_src.longname, "..") == 0 ) { + } + else { + STD_StrCpy( path_src_full , path_src_dir ); + STD_StrCat( path_src_full , entry_src.longname ); + STD_StrCpy( path_dst_full , path_dst_dir ); + STD_StrCat( path_dst_full , entry_src.longname ); + + /* SDにログを残す場合 */ + if( log_active ) { + // OS_TPrintf("path = %s len=%d att=0x%08x\n",path_src_full,entry_src.filesize, entry_src.attributes); + PrintAttributes(entry_src.attributes, &log_fd); + miya_log_fprintf(&log_fd, " %8d %s\n", entry_src.filesize, path_src_full ); + } + + if( (entry_src.attributes & FS_ATTRIBUTE_IS_DIRECTORY) != 0 ) { + /* ディレクトリの場合 */ + // recursive_count 1 2 3 4 + // nand:/title + // nand:/title/00000000/00000000/data/*.sav + if( recursive_count == 1 ) { + if( my_fs_is_Title_Hi_dir_name( entry_src.longname ) == TRUE ) { + find_title_save_data( headp, path_dst_full, path_src_full , &save_parent_dir_info_flag_temp, log_file_name ); + } + } + if( recursive_count == 2 ) { + if( my_fs_is_Title_Lo_dir_name( entry_src.longname ) == TRUE ) { + find_title_save_data( headp, path_dst_full, path_src_full , &save_parent_dir_info_flag_temp, log_file_name ); + } + } + else if( (recursive_count == 3) ) { + if( !STD_StrCmp( entry_src.longname, "data" ) ) { + find_title_save_data( headp, path_dst_full, path_src_full , &save_parent_dir_info_flag_temp, log_file_name ); + } + } + } + else { + // recursive_count 1 2 3 4 + // nand:/title + // nand:/title/00000000/00000000/data/*.sav + if( (recursive_count == 4) ) { + if( !STD_StrCmp( entry_src.longname, "public.sav" ) ) { + /* 目的のファイルを見つけた。 */ + my_fs_add_list(headp, &entry_src, path_src_full, path_dst_full, &log_fd); + save_parent_dir_info_flag_temp = 1; + } + else if( !STD_StrCmp( entry_src.longname, "private.sav" ) ) { + my_fs_add_list(headp, &entry_src, path_src_full, path_dst_full, &log_fd); + save_parent_dir_info_flag_temp = 1; + } +#if 1 + /* いるのか? */ + else if( !STD_StrCmp( entry_src.longname, "banner.sav" ) ) { + my_fs_add_list(headp, &entry_src, path_src_full, path_dst_full, &log_fd); + save_parent_dir_info_flag_temp = 1; + } +#endif + } + } + } + } + + if( save_parent_dir_info_flag_temp == 1 ) { + // OS_TPrintf("save dir info = %s\n\n",path_src); + my_fs_add_list( headp, &entry_current_dir, path_src, path_dst, &log_fd); + *save_parent_dir_info_flag = 1; + } + + /* ソースディレクトリクローズ */ + if( FS_CloseDirectory(&f_src) == FALSE) { + miya_log_fprintf(&log_fd, "%s %d: Failed Close Directory\n", __FUNCTION__ , __LINE__ ); + miya_log_fprintf(&log_fd, " %s\n", path_src); + miya_log_fprintf(&log_fd, " %s\n", my_fs_util_get_fs_result_word( FS_GetArchiveResultCode(path_src))); + ret_value = -1; + } + + end_process: + + Path_Buffers_Clean( path_src_dir, path_src_full, path_dst_dir, path_dst_full ); + + recursive_count--; + + if( recursive_count == 0 ) { + if( log_active ) { + Log_File_Close(&log_fd); + } + } + + return ret_value; +} + + +int find_copy(MY_DIR_ENTRY_LIST **headp, const char *path_dst, const char *path_src, + char *extension, int max_recursive_count, int *save_parent_dir_info_flag, char *log_file_name ) +{ + static int recursive_count = 0; + static FSFile log_fd; + static BOOL log_active = FALSE; + // static char *log_file_name = "sdmc:/miya/nandinfo_find_copy.txt"; + + FSFile f_src; + FSDirectoryEntryInfo entry_src; + FSDirectoryEntryInfo entry_current_dir; + int ret_value = 0; + // FSResult fs_ret_value; + int save_parent_dir_info_flag_temp = 0; + + char *path_src_dir = NULL; + char *path_src_full = NULL; + char *path_dst_dir = NULL; + char *path_dst_full = NULL; + + /* ここでSDカードがあるかどうか調べる */ + + if( recursive_count == 0 ) { + log_active = Log_File_Open( &log_fd, log_file_name ); + } + + if( recursive_count > max_recursive_count ) { + ret_value = 0; + goto end_process; + } + + recursive_count++; + + /* ソースディレクトリオープン */ + FS_InitFile(&f_src); + + if( FS_OpenDirectory(&f_src, path_src, FS_PERMIT_R) == FALSE ) { + miya_log_fprintf(&log_fd, "%s %d: Failed Open Directory\n", __FUNCTION__ , __LINE__ ); + miya_log_fprintf(&log_fd, " %s\n", path_src); + miya_log_fprintf(&log_fd, " %s\n", my_fs_util_get_fs_result_word( FS_GetArchiveResultCode(path_src) ) ); + ret_value = -1; + goto end_process; + } + + /* ファイル名バッファ初期化 */ + if( FALSE == Path_Buffers_Init(path_src, path_dst, + &path_src_dir, &path_src_full, &path_dst_dir, &path_dst_full, &log_fd ) ) + { + ret_value = -1; + goto end_process; + } + + while( FS_ReadDirectory(&f_src, &entry_src) ) { + + if( STD_StrCmp(entry_src.longname, ".") == 0 ) { + /* とりあえずカレントディレクトリエントリを残しておく */ + STD_CopyMemory( (void *)&entry_current_dir, (void *)&entry_src ,sizeof(FSDirectoryEntryInfo) ); + } + else if( STD_StrCmp(entry_src.longname, "..") == 0 ) { + } + else { + STD_StrCpy( path_src_full , path_src_dir ); + STD_StrCat( path_src_full , entry_src.longname ); + STD_StrCpy( path_dst_full , path_dst_dir ); + STD_StrCat( path_dst_full , entry_src.longname ); + + /* SDにログを残す場合 */ + if( log_active ) { + PrintAttributes(entry_src.attributes, &log_fd); + miya_log_fprintf(&log_fd, " %8d %s\n", entry_src.filesize, path_src_full ); + } + + if( (entry_src.attributes & FS_ATTRIBUTE_IS_DIRECTORY) != 0 ) { + find_copy( headp, path_dst_full, path_src_full , extension, max_recursive_count, &save_parent_dir_info_flag_temp , log_file_name); + } + else { + /* ここで拡張子比較 */ + { + int len; + int extension_len = STD_StrLen( extension ); + len = STD_StrLen( path_src_full ); + + if( len > (extension_len - 1) ) { + // 123456789012345678901234567890123456789012345678901234567890123 + // ../backup_sample/ARM7.TWL/bin/ARM7-TS.HYB/Release/ltdmain.c.cpp + if( !STD_StrNCmp( &(path_src_full[len - (extension_len )]), extension , extension_len+1 ) ) { + /* 目的のファイルを見つけた。 */ + // OS_TPrintf("%s len=%d att=0x%08x\n", path_src_full, entry_src.filesize, entry_src.attributes); + // OS_TPrintf("%s\n", path_dst_full ); + + my_fs_add_list(headp, &entry_src, path_src_full, path_dst_full, &log_fd); + + save_parent_dir_info_flag_temp = 1; + } + } + } + } + } + } + + if( save_parent_dir_info_flag_temp == 1 ) { + // OS_TPrintf("save dir info = %s\n\n",path_src); + my_fs_add_list( headp, &entry_current_dir, path_src, path_dst, &log_fd ); + *save_parent_dir_info_flag = 1; + } + + /* ソースディレクトリクローズ */ + if( FS_CloseDirectory(&f_src) == FALSE) { + miya_log_fprintf(&log_fd, "%s %d: Failed Close Directory\n", __FUNCTION__ , __LINE__ ); + miya_log_fprintf(&log_fd, " %s\n", path_src); + miya_log_fprintf(&log_fd, " %s\n", my_fs_util_get_fs_result_word( FS_GetArchiveResultCode(path_src))); + ret_value = -1; + } + + end_process: + + Path_Buffers_Clean( path_src_dir, path_src_full, path_dst_dir, path_dst_full ); + + recursive_count--; + if( recursive_count == 0 ) { + if( log_active ) { + Log_File_Close(&log_fd); + } + } + + return ret_value; +} + + +int copy_r( MY_DIR_ENTRY_LIST **headp, const char *path_dst, const char *path_src, char *log_file_name ) +{ + static int recursive_count = 0; + static FSFile log_fd; + static BOOL log_active = FALSE; + // static char *log_file_name = "sdmc:/nandinfo_copy_r.txt"; + + FSFile f_src; + FSDirectoryEntryInfo entry_src; + FSDirectoryEntryInfo entry_current_dir; + int ret_value = 0; + + char *path_src_dir = NULL; + char *path_src_full = NULL; + char *path_dst_dir = NULL; + char *path_dst_full = NULL; + + /* ここでSDカードがあるかどうか調べる */ + if( recursive_count == 0 ) { + log_active = Log_File_Open( &log_fd, log_file_name ); + } + + recursive_count++; + + /* ソースディレクトリオープン */ + FS_InitFile(&f_src); + + if( FS_OpenDirectory(&f_src, path_src, FS_PERMIT_R) == FALSE ) { + if( log_active ) { + miya_log_fprintf(&log_fd, "%s %d: Failed Open Directory\n", __FUNCTION__ , __LINE__ ); + miya_log_fprintf(&log_fd, " %s\n", path_src); + miya_log_fprintf(&log_fd, " %s\n", my_fs_util_get_fs_result_word( FS_GetArchiveResultCode(path_src) ) ); + } + + ret_value = -1; + goto end_process; + } + + /* ファイル名バッファ初期化 */ + if( FALSE == Path_Buffers_Init(path_src, path_dst, + &path_src_dir, &path_src_full, &path_dst_dir, &path_dst_full, &log_fd ) ) + { + ret_value = -1; + goto end_process; + } + + while( FS_ReadDirectory(&f_src, &entry_src) ) { + + if( STD_StrCmp(entry_src.longname, ".") == 0 ) { + /* とりあえずカレントディレクトリエントリを残しておく */ + STD_CopyMemory( (void *)&entry_current_dir, (void *)&entry_src ,sizeof(FSDirectoryEntryInfo) ); + } + else if( STD_StrCmp(entry_src.longname, "..") == 0 ) { + + } + else { + STD_StrCpy( path_src_full , path_src_dir ); + STD_StrCat( path_src_full , entry_src.longname ); + STD_StrCpy( path_dst_full , path_dst_dir ); + STD_StrCat( path_dst_full , entry_src.longname ); + /* SDにログを残す場合 */ + if( log_active ) { + miya_log_fprintf(&log_fd, "path = %s len=%d\n",path_src_full,entry_src.filesize); + } + + if( (entry_src.attributes & FS_ATTRIBUTE_IS_DIRECTORY) != 0 ) { + copy_r( headp, path_dst_full, path_src_full, log_file_name ); + } + else { + my_fs_add_list(headp, &entry_src, path_src_full, path_dst_full, &log_fd); + } + } + } + + /* ディレクトリエントリをセーブ */ + + my_fs_add_list( headp, &entry_current_dir, path_src, path_dst, &log_fd ); + + /* ディレクトリクローズ */ + if( FS_CloseDirectory(&f_src) == FALSE) { + if( log_active ) { + miya_log_fprintf(&log_fd, "%s %d: Failed Close Directory\n", __FUNCTION__ , __LINE__ ); + miya_log_fprintf(&log_fd, " %s\n", path_src); + miya_log_fprintf(&log_fd, " %s\n", my_fs_util_get_fs_result_word( FS_GetArchiveResultCode(path_src))); + } + ret_value = -1; + } + + end_process: + + Path_Buffers_Clean( path_src_dir, path_src_full, path_dst_dir, path_dst_full ); + + recursive_count--; + if( recursive_count == 0 ) { + if( log_active ) { + Log_File_Close(&log_fd); + } + } + + return ret_value; +} + +void write_debug_data(void) +{ + // CopyFile( dst <= src ); + CopyFile("nand:/tmp/m00.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m01.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m02.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m03.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m04.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m05.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m06.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m07.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m08.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m09.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m10.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m11.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m12.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m13.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m14.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m15.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m16.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m17.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m18.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m19.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m20.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m22.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m23.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m24.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m25.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m26.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m27.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m28.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m29.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m30.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m31.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m32.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m33.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/tmp/m34.sav" , "nand:/sys/log/sysmenu.log", NULL); + CopyFile("nand:/sys/miya.log", "sdmc:/miya.log", NULL); + CopyFile("nand:/shared1/miya.log", "sdmc:/miya.log", NULL); + + + + /* + PrintDirEntryListBackword-----Start + src name = nand:/title + dst name = sdmc:/miya_find_title_save + src name = nand:/title/0003000f + dst name = sdmc:/miya_find_title_save/0003000f + src name = nand:/title/0003000f/484e4341 + dst name = sdmc:/miya_find_title_save/0003000f/484e4341 + src name = nand:/title/0003000f/484e4341/data + dst name = sdmc:/miya_find_title_save/0003000f/484e4341/data +-- src name = nand:/title/0003000f/484e4341/data/private.sav + dst name = sdmc:/miya_find_title_save/0003000f/484e4341/data/private.sav + src name = nand:/title/00030017 + dst name = sdmc:/miya_find_title_save/00030017 + src name = nand:/title/00030017/484e4141 + dst name = sdmc:/miya_find_title_save/00030017/484e4141 + src name = nand:/title/00030017/484e4141/data + dst name = sdmc:/miya_find_title_save/00030017/484e4141/data +-- src name = nand:/title/00030017/484e4141/data/public.sav + dst name = sdmc:/miya_find_title_save/00030017/484e4141/data/public.sav + PrintDirEntryListBackward-----End + */ + +#if 0 + +nand:/title/0003000f +nand:/title/0003000f/484e4341 +nand:/title/0003000f/484e4341/content +nand:/title/0003000f/484e4841 +nand:/title/0003000f/484e4841/data +nand:/title/0003000f/484e4841/content +nand:/title/0003000f/484e4c41 +nand:/title/0003000f/484e4c41/data + +nand:/title/00030015 +nand:/title/00030015/484e4241 +nand:/title/00030015/484e4241/data +nand:/title/00030015/484e4241/content + CopyFile("nand:/title/00030017/ + CopyFile("nand:/ticket/public.sav" , "nand:/sys/log/sysmenu.log"); + CopyFile("nand:/ticket/00030015 + CopyFile("nand:/ticket/00030015/miya.sav" , "nand:/sys/log/sysmenu.log"); +#endif +} + diff --git a/build/tools/sctools/common/src/my_fs_util.h b/build/tools/sctools/common/src/my_fs_util.h new file mode 100644 index 0000000..0b1e45a --- /dev/null +++ b/build/tools/sctools/common/src/my_fs_util.h @@ -0,0 +1,48 @@ +#ifndef _MY_FS_UTIL_H_ +#define _MY_FS_UTIL_H_ + +#define FILE_PATH_LEN 512 + +typedef struct _MY_DIR_ENTRY_LIST { + struct _MY_DIR_ENTRY_LIST *prev; + struct _MY_DIR_ENTRY_LIST *next; + FSDirectoryEntryInfo content; + char src_path[ FILE_PATH_LEN ]; + char dst_path[ FILE_PATH_LEN ]; +} MY_DIR_ENTRY_LIST; + + +#ifdef __cplusplus +extern "C" { +#endif + +char *my_fs_util_get_fs_result_word( FSResult res ); + +int find_title_save_data(MY_DIR_ENTRY_LIST **headp, const char *path_dst, + const char *path_src, int *save_dir_info, char *log_file_name ); +int find_copy( MY_DIR_ENTRY_LIST **headp, const char *path_dst, const char *path_src, + char *extension, int level, int *save_info, char *log_file_name ); +int copy_r( MY_DIR_ENTRY_LIST **headp, const char *path_dst, const char *path_src, char *log_file_name ); +int get_title_id(MY_DIR_ENTRY_LIST **headp, const char *path_src, + int *save_parent_dir_info_flag, char *log_file_name ); + +void PrintDirEntryListForward( MY_DIR_ENTRY_LIST *head, FSFile *log_fd ); +void PrintDirEntryListBackward( MY_DIR_ENTRY_LIST *head, FSFile *log_fd ); +void PrintSrcDirEntryListBackward( MY_DIR_ENTRY_LIST *head, FSFile *log_fd); + +BOOL SaveDirEntryList( MY_DIR_ENTRY_LIST *head , char *path ); +BOOL RestoreDirEntryList( char *path ); +BOOL ClearDirEntryList( MY_DIR_ENTRY_LIST **headp ); +void write_debug_data(void); +BOOL SDCardValidation(void); +BOOL CheckShopRecord(FSFile *log_fd); +BOOL MydataSave(const char *path, void *pData, int size, FSFile *log_fd); +BOOL MydataLoad(const char *path, void *pBuffer, int size, FSFile *log_fd); +void GetDirEntryList( MY_DIR_ENTRY_LIST *head, void **pBuffer, int *size); + +#ifdef __cplusplus +} +#endif + + +#endif /* _MY_FS_UTIL_H_ */ diff --git a/build/tools/sctools/common/src/mynvram.c b/build/tools/sctools/common/src/mynvram.c new file mode 100644 index 0000000..fd02c79 --- /dev/null +++ b/build/tools/sctools/common/src/mynvram.c @@ -0,0 +1,246 @@ + +#include +#include + +#include "font.h" +#include "text.h" +#include "mprintf.h" +#include "mynvram.h" + + +#define NVRAM_PERSONAL_DATA_OFFSET 0x20 + +/* Wifi設定だけなら0x0A00 */ +//#define NVRAM_PERSONAL_DATA_SIZE 0x0C00 +#define NVRAM_PERSONAL_DATA_SIZE 0x0A00 + + +#define NVRAM_INTERNAL_BUF_SIZE 0x100 +static u8 nvram_buffer[NVRAM_INTERNAL_BUF_SIZE] ATTRIBUTE_ALIGN(32); + +static BOOL my_nvram_read( u32 offset, u32 size, void *buf) +{ + u32 internal_size = size; + u32 internal_offset = offset; + void *temp_buf = buf; + u32 temp_size; + int i; + + while( internal_size ) { + if( internal_size > NVRAM_INTERNAL_BUF_SIZE ) { + temp_size = NVRAM_INTERNAL_BUF_SIZE; + } + else { + temp_size = internal_size; + } + + DC_InvalidateRange(nvram_buffer, NVRAM_INTERNAL_BUF_SIZE); + if( NVRAM_RESULT_SUCCESS != NVRAMi_Read( internal_offset , temp_size, (void* )nvram_buffer) ) { + mprintf("nvram read error\n"); + OS_TPrintf( "nvram error: %s %s %d\n",__FILE__,__FUNCTION__,__LINE__); + return FALSE; + } + for( i = 0 ; i < temp_size ; i++ ) { + *(u8 *)temp_buf = nvram_buffer[i]; + ((u8 *)temp_buf)++; + } + + internal_offset += temp_size; + internal_size -= temp_size; + } + // OS_TPrintf( "nvram success: offset = 0x%02x\n", offset); + return TRUE; +} + +static BOOL my_nvram_write( u32 offset, u32 size, void *buf) +{ + u32 internal_size = size; + u32 internal_offset = offset; + void *temp_buf = buf; + u32 temp_size; + int i; + + while( internal_size ) { + + if( internal_size > NVRAM_INTERNAL_BUF_SIZE ) { + temp_size = NVRAM_INTERNAL_BUF_SIZE; + } + else { + temp_size = internal_size; + } + + for( i = 0 ; i < temp_size ; i++ ) { + nvram_buffer[i] = *(u8 *)temp_buf; + ((u8 *)temp_buf)++; + } + + DC_FlushRange(nvram_buffer, NVRAM_INTERNAL_BUF_SIZE); + if( NVRAM_RESULT_SUCCESS != NVRAMi_Write( internal_offset , temp_size, (void* )nvram_buffer) ) { + OS_TPrintf( "nvram write error: %s %s %d\n",__FILE__,__FUNCTION__,__LINE__); + return FALSE; + } + + internal_offset += temp_size; + internal_size -= temp_size; + } + // OS_TPrintf( "nvram success: offset = 0x%02x\n", offset); + return TRUE; +} + + +/* *INDENT-OFF* */ +static const char * const fs_result_strings[] = +{ + "FS_RESULT_SUCCESS", + "FS_RESULT_FAILURE", + "FS_RESULT_BUSY", + "FS_RESULT_CANCELED", + "FS_RESULT_UNSUPPORTED", + "FS_RESULT_ERROR", + "FS_RESULT_INVALID_PARAMETER", + "FS_RESULT_NO_MORE_RESOUCE", + "FS_RESULT_ALREADY_DONE", + "FS_RESULT_PERMISSION_DENIED", + "FS_RESULT_MEDIA_FATAL", +}; +static const size_t fs_result_string_max = sizeof(fs_result_strings) / sizeof(*fs_result_strings); + +static void ReportLastErrorPath(const char *path) +{ + FSResult result = FS_GetArchiveResultCode(path); + + SDK_ASSERT((result >= 0) && (result < fs_result_string_max)); + mprintf("FS error:\n \"%s\"\n %s\n", + path, fs_result_strings[result]); +} + +BOOL nvram_backup(char *path) +{ + BOOL bSuccess; + FSFile nor_fd; + u16 offset; + u32 vol; + int len; + char nor_file_path[FS_FILE_NAME_MAX]; +#define BUF_SIZE 0x100 + u8 nor_buf[BUF_SIZE]; + + + FS_InitFile(&nor_fd); + // STD_TSNPrintf(nor_file_path, sizeof(nor_file_path), "sdmc:/twl_nor.bin"); + STD_TSNPrintf(nor_file_path, sizeof(nor_file_path), path ); + bSuccess = FS_OpenFileEx(&nor_fd, nor_file_path, (FS_FILEMODE_R|FS_FILEMODE_W)); + if( ! bSuccess ) + { + if( !FS_CreateFile(nor_file_path, FS_PERMIT_R | FS_PERMIT_W)) { + ReportLastErrorPath(nor_file_path); + OS_TWarning("2 FS_CreateFile(%s) failed.", nor_file_path); + return FALSE; + } + bSuccess = FS_OpenFileEx(&nor_fd, nor_file_path, (FS_FILEMODE_R|FS_FILEMODE_W)); + if( ! bSuccess ) { + OS_TPrintf("error %s %s %d\n",__FILE__,__FUNCTION__,__LINE__); + return FALSE; + } + } + + if( TRUE != my_nvram_read( NVRAM_PERSONAL_DATA_OFFSET , sizeof(u16), (void* )&offset) ) { + OS_TPrintf( "nvram error: %s %s %d\n",__FILE__,__FUNCTION__,__LINE__); + } + else { + OS_TPrintf( "nvram success: offset = 0x%02x\n", offset); + } + + if( offset == 0 ) { + OS_TPrintf( "nvram error: offset = 0x%02x\n", offset); + return FALSE; + } + + offset *= 8; + offset -= 0xA00; + + for( vol = 0 ; vol < NVRAM_PERSONAL_DATA_SIZE ; vol += BUF_SIZE ) { + OS_TPrintf("."); + if( TRUE != my_nvram_read( offset+vol , BUF_SIZE, (void* )nor_buf) ) { + OS_TPrintf( "nvram error: %s %s %d\n",__FILE__,__FUNCTION__,__LINE__); + } + else { + len = FS_WriteFile(&nor_fd, nor_buf, BUF_SIZE); + if (len != BUF_SIZE) + { + OS_TWarning("FS_WriteFile() failed."); + break; + } + } + } + OS_TPrintf("\n"); + + FS_FlushFile(&nor_fd); + + bSuccess = FS_CloseFile(&nor_fd); + + // OS_TPrintf( "nvram read completed.\n"); + return TRUE; +} + +BOOL nvram_restore(char *path) +{ + BOOL bSuccess; + FSFile nor_fd; + u16 offset; + u32 vol; + int len; + char nor_file_path[FS_FILE_NAME_MAX]; +#define BUF_SIZE 0x100 + u8 nor_buf[BUF_SIZE]; + + FS_InitFile(&nor_fd); + STD_TSNPrintf(nor_file_path, sizeof(nor_file_path), path ); + bSuccess = FS_OpenFileEx(&nor_fd, nor_file_path, FS_FILEMODE_R); + if( ! bSuccess ) { + OS_TPrintf("error %s %s %d\n",__FILE__,__FUNCTION__,__LINE__); + return FALSE; + } + + /* offsetアドレスの取得 */ + if( TRUE != my_nvram_read( NVRAM_PERSONAL_DATA_OFFSET , sizeof(u16), (void* )&offset) ) { + OS_TPrintf( "nvram error: %s %s %d\n",__FILE__,__FUNCTION__,__LINE__); + } + else { + OS_TPrintf( "nvram success: offset = 0x%02x\n", offset); + } + + if( offset == 0 ) { + OS_TPrintf( "nvram error: offset = 0x%02x\n", offset); + return FALSE; + } + + offset *= 8; + offset -= 0xA00; + + for( vol = 0 ; vol < NVRAM_PERSONAL_DATA_SIZE ; vol += BUF_SIZE ) { + OS_TPrintf("."); + + len = FS_ReadFile(&nor_fd, nor_buf, BUF_SIZE); + if (len != BUF_SIZE) { + OS_TWarning("FS_ReadFile() failed."); + break; + } + + if( TRUE != my_nvram_write( offset+vol , BUF_SIZE, (void* )nor_buf) ) { + OS_TPrintf( "nvram write error: %s %s %d\n",__FILE__,__FUNCTION__,__LINE__); + } + else { + } + } + + OS_TPrintf("\n"); + bSuccess = FS_CloseFile(&nor_fd); + + mprintf("nvram write completed.\n"); + OS_TPrintf( "nvram write completed.\n"); + + return TRUE; +} + + diff --git a/build/tools/sctools/common/src/mynvram.h b/build/tools/sctools/common/src/mynvram.h new file mode 100644 index 0000000..514c0ae --- /dev/null +++ b/build/tools/sctools/common/src/mynvram.h @@ -0,0 +1,16 @@ +#ifndef _MY_NVRAM_H_ +#define _MY_NVRAM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// BOOL my_nvram_read( u32 offset, u32 size, void *buf); +BOOL nvram_backup(char *path); +BOOL nvram_restore(char *path); + +#ifdef __cplusplus +} +#endif + +#endif /* _MY_NVRAM_H_ */ diff --git a/build/tools/sctools/common/src/mywlan.c b/build/tools/sctools/common/src/mywlan.c new file mode 100644 index 0000000..0b0f3fd --- /dev/null +++ b/build/tools/sctools/common/src/mywlan.c @@ -0,0 +1,311 @@ +#include +#include + +#include "text.h" +#include "mprintf.h" +#include "logprintf.h" + +#include "mywlan.h" + +static char SSID_STR[256]; +static int SSID_LEN = 0; +static char KEY_STR[256]; +static u8 KEY_BIN[256]; +static int KEY_STR_LEN = 0; +static int KEY_BIN_LEN = 0; +static int MODE = 0; + +char *GetWlanSSID(void) +{ + return SSID_STR; +} + +char *GetWlanKEYSTR(void) +{ + return KEY_STR; +} + +int GetWlanKEYBIN(u8 *buf) +{ + if( KEY_BIN_LEN ) { + STD_CopyMemory((void *)buf, (void *)KEY_BIN, (unsigned long)KEY_BIN_LEN); + } + return KEY_BIN_LEN; +} + +int GetWlanMode(void) +{ + return MODE; +} + +/* +SSID:"001D731A8202" +MODE:"WEP128" +;MODE:"OPEN" +;MODE:"WPA-TKIP" +;MODE:"WPA2-TKIP" +;MODE:"WPA-AES" +;MODE:"WPA2-AES" +KEY-STR:"0123456789red" +KEY-BIN:"0123456789red" +*/ + +static int ReadLine(FSFile *f, char *buf, int buf_size) +{ + char c; + s32 readSize; + int count = 0; + while( 1 ) { + readSize = FS_ReadFile(f, (void *)&c, (s32)1 ); + if( readSize == 0 ) { + /* EOF */ + break; + } + count++; + if( c == '\r' ) { + *buf = '\0'; + break; + } + else if(c == '\n' ) { + *buf = '\0'; + break; + } + + *buf = c; + buf++; + + if( count > buf_size ) { + break; + } + } + return count; +} + + +BOOL LoadWlanConfigFile(char *path) +{ + FSFile f; + FSResult res; + s32 readSize; + BOOL bSuccess; + int count = 0; + int count2; + int count3; + BOOL ssid_flag; + BOOL key_str_flag; + BOOL key_bin_flag; + BOOL mode_flag; + BOOL ret_flag = FALSE; + int lo_hi; + u8 hex; + char c; + +#define LINE_BUF_SIZE 256 + char line_buf[LINE_BUF_SIZE]; + + FS_InitFile(&f); + bSuccess = FS_OpenFileEx(&f, path, FS_FILEMODE_R); + if (bSuccess == FALSE) { + res = FS_GetArchiveResultCode(path); + return FALSE; + } + + + ssid_flag = FALSE; + key_str_flag = FALSE; + key_bin_flag = FALSE; + mode_flag = FALSE; + + + while( 1 ) { + readSize = ReadLine(&f, line_buf, LINE_BUF_SIZE ); + if( readSize == 0 ) { + /* EOF */ + break; + } + + + if( readSize > 5 ) { + if( !ssid_flag && !STD_StrNCmp( line_buf, "SSID:" , STD_StrLen("SSID:")) ) { + count = STD_StrLen("SSID:"); + if( line_buf[count] == '\"' /* 1個目 */) { + count++; + count2 = count; + while( readSize > count ) { + if( line_buf[count] == '\"' /* 2個目 */) { + SSID_LEN = count - 1; + SSID_STR[count - count2] = '\0'; + ssid_flag = TRUE; + break; + } + SSID_STR[count - count2] = line_buf[count]; + count++; + } + } + } + else if( !mode_flag && !STD_StrNCmp( line_buf, "MODE:" , STD_StrLen("MODE:")) ) { + count = STD_StrLen("MODE:"); + if( line_buf[count] == '\"' /* 1個目 */) { + count++; + count2 = count; + if( !STD_StrNCmp( &(line_buf[count2]), "NONE" , STD_StrLen("NONE")) ) { + // ;MODE:"NONE" + if( line_buf[ count2 + STD_StrLen("NONE") ] == '\"' /* 2個目 */) { + MODE = WCM_WEPMODE_NONE; + mode_flag = TRUE; + } + } + else if( !STD_StrNCmp( &(line_buf[count2]), "WEP40" , STD_StrLen("WEP40")) ) { + if( line_buf[ count2 + STD_StrLen("WEP40") ] == '\"' /* 2個目 */) { + MODE = WCM_WEPMODE_40; + mode_flag = TRUE; + } + } + else if( !STD_StrNCmp( &(line_buf[count2]), "WEP104" , STD_StrLen("WEP104")) ) { + if( line_buf[ count2 + STD_StrLen("WEP104") ] == '\"' /* 2個目 */) { + MODE = WCM_WEPMODE_104; + mode_flag = TRUE; + } + } + + else if( !STD_StrNCmp( &(line_buf[count2]), "WEP128" , STD_StrLen("WEP128")) ) { + if( line_buf[ count2 + STD_StrLen("WEP128") ] == '\"' /* 2個目 */) { + MODE = WCM_WEPMODE_128; + mode_flag = TRUE; + } + } + else if( !STD_StrNCmp( &(line_buf[count2]), "WPA-TKIP", STD_StrLen("WPA-TKIP")) ) { + if( line_buf[ count2 + STD_StrLen("WPA-TKIP") ] == '\"' /* 2個目 */) { + MODE = WCM_WEPMODE_WPA_TKIP; + mode_flag = TRUE; + } + } + else if( !STD_StrNCmp( &(line_buf[count2]), "WPA2-TKIP", STD_StrLen("WPA2-TKIP")) ) { + if( line_buf[ count2 + STD_StrLen("WPA2-TKIP") ] == '\"' /* 2個目 */) { + MODE = WCM_WEPMODE_WPA2_TKIP; + mode_flag = TRUE; + } + } + else if( !STD_StrNCmp( &(line_buf[count2]), "WPA-AES", STD_StrLen("WPA-AES")) ) { + if( line_buf[ count2 + STD_StrLen("WPA-AES") ] == '\"' /* 2個目 */) { + MODE = WCM_WEPMODE_WPA_AES; + mode_flag = TRUE; + } + } + else if( !STD_StrNCmp( &(line_buf[count2]), "WPA2-AES" , STD_StrLen("WPA2-AES")) ) { + if( line_buf[ count2 + STD_StrLen("WPA2-AES") ] == '\"' /* 2個目 */) { + MODE = WCM_WEPMODE_WPA2_AES; + mode_flag = TRUE; + } + } + } + + } + else if( !key_str_flag && !STD_StrNCmp( line_buf, "KEY-STR:" , STD_StrLen("KEY-STR:")) ) { + count = STD_StrLen("KEY-STR:"); + if( line_buf[count] == '\"' /* 1個目 */) { + count++; + count2 = count; + while( readSize > count ) { + if( line_buf[count] == '\"' /* 2個目 */) { + KEY_STR_LEN = count - 1; + KEY_STR[count - count2] = '\0'; + /* このときwep128ならcountは(13文字なので) */ + // 0123456789012345678901 + // KEY-STR:"0123456789red" + key_str_flag = TRUE; + break; + } + KEY_STR[count - count2] = line_buf[count]; + count++; + } + } + } + else if( !key_bin_flag && !STD_StrNCmp( line_buf, "KEY-BIN:" , STD_StrLen("KEY-BIN:")) ) { + count = STD_StrLen("KEY-BIN:"); + lo_hi = 0; + hex = 0; + if( line_buf[count] == '\"' /* 1個目 */) { + count++; + count2 = count; + count3 = 0; + while( readSize > count ) { + if( line_buf[count] == '\"' /* 2個目 */) { + if( lo_hi == 0 ) { + KEY_BIN_LEN = count3; + key_bin_flag = TRUE; + } + break; + } + c = line_buf[count]; + + if( ('a' <= c) && (c <= 'f') ) { + if( lo_hi == 0 ) { + hex = (u8)(( c - 'a' + 10 ) * 16); + } + else { + hex += (u8)( c - 'a' + 10 ); + } + } + else if( ('A' <= c) && (c <= 'F') ) { + if( lo_hi == 0 ) { + hex = (u8)(( c - 'a' + 10 ) * 16); + } + else { + hex += (u8)( c - 'a' + 10 ); + } + } + else if( ('0' <= c) && (c <= '9') ) { + if( lo_hi == 0 ) { + hex = (u8)( (c - '0' ) * 16 ); + } + else { + hex += (u8)(c - '0'); + } + } + else { + /* error! */ + break; + } + if( lo_hi == 1 ) { + KEY_BIN[count3] = hex; + count3++; + } + lo_hi ^= 1; + count++; + } + } + } + } + + if( ssid_flag == TRUE && (key_str_flag == TRUE || key_bin_flag == TRUE) && mode_flag == TRUE ) { + ret_flag = TRUE; + break; + } + + /*123456789012345678 + + SSID:"001D731A8202" + MODE:"WEP128" + ;MODE:"NONE" + ;MODE:"WPA-TKIP" + ;MODE:"WPA2-TKIP" + ;MODE:"WPA-AES" + ;MODE:"WPA2-AES" + KEY-STR:"0123456789red" + KEY-BIN:"0123456789red" + */ + + + } + + if( FS_CloseFile(&f) == FALSE) { + res = FS_GetArchiveResultCode(path); + return FALSE; + } + + return ret_flag; +} + + + diff --git a/build/tools/sctools/common/src/mywlan.h b/build/tools/sctools/common/src/mywlan.h new file mode 100644 index 0000000..0563b5a --- /dev/null +++ b/build/tools/sctools/common/src/mywlan.h @@ -0,0 +1,22 @@ +#ifndef _MY_WLAN_H_ +#define _MY_WLAN_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + + +BOOL LoadWlanConfigFile(char *path); + +char *GetWlanSSID(void); +int GetWlanKEYBIN(u8 *buf); +char *GetWlanKEYSTR(void); +int GetWlanMode(void); + +#ifdef __cplusplus +} +#endif + + +#endif /* _MY_WLAN_H_ */ diff --git a/build/tools/sctools/common/src/netconnect.c b/build/tools/sctools/common/src/netconnect.c new file mode 100644 index 0000000..2c8c4be --- /dev/null +++ b/build/tools/sctools/common/src/netconnect.c @@ -0,0 +1,376 @@ +/*---------------------------------------------------------------------------* + Project: TwlWiFi - demos - netconnect + File: netconnect.c + + Copyright 2005,2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: 2008-08-06#$ + $Rev: 880 $ + $Author: adachi_hiroaki $ + *---------------------------------------------------------------------------*/ + +#include +#include + +#include "sitedefs.h" +#include "wcm_control.h" +#include "netconnect.h" +#include "mywlan.h" + +/*---------------------------------------------------------------------------* + 初期化 + *---------------------------------------------------------------------------*/ +static void Heap_Setup(void); +#ifdef SDK_TWL +static void* myAlloc_SOCL(u32 size); +static void myFree_SOCL(void* ptr); +#endif // SDK_TWL + +static void ncStartWiFi(void); +static void ncFinishWiFi(void); +static void ncStartInet(void); +static void ncFinishInet(void); + +static WcmControlApInfo apInfo; +static s32 previousAddr = 0; +static u8 g_deviceId = WCM_DEVICEID_DEFAULT; +static BOOL g_started = FALSE; + +/*---------------------------------------------------------------------------* + Name : NcGlobalInit + Description : + Arguments : なし。 + Returns : + *---------------------------------------------------------------------------*/ +void NcGlobalInit() +{ + OS_Init(); + OS_InitTick(); + OS_InitAlarm(); + RTC_Init(); + Heap_Setup(); + (void)OS_EnableIrq(); + (void)OS_EnableInterrupts(); +} + +#ifndef SDK_WIFI_INET +static void ncStartWiFi(void) +{ + int result; + if (previousAddr != 0) + { + /* + DHCPサーバに前回取得したアドレスを要求するように設定する。 + アドレスプールを使い切らないようにするために必要。 + */ + SOCL_SetRequestedIP(SOC_NtoHl(previousAddr)); + } + +#ifdef SDK_TWL + if (OS_IsRunOnTwl()) + { + static SOCLConfig socl_config; + MI_CpuClear8(&socl_config, sizeof(socl_config)); + + socl_config.alloc = myAlloc_SOCL; + socl_config.free = myFree_SOCL; + socl_config.use_dhcp = TRUE; + socl_config.cmd_packet_max = SOCL_CMDPACKET_MAX; + socl_config.lan_buffer_size = SOCL_LAN_BUFFER_SIZE_DEFAULT * 8; + socl_config.mtu = 1400; + socl_config.rwin = 65535; + + OS_TPrintf("SOCL_Startup....\n"); + result = SOCL_Startup(&socl_config); + } + else +#endif // SDK_TWL + { + SOCConfig soc_config; + soc_config.alloc = NcAlloc; + soc_config.free = NcFree; + soc_config.flag = SOC_FLAG_DHCP; + soc_config.mtu = 0; + soc_config.rwin = 0; + + OS_TPrintf("SOC_Startup....\n"); + result = SOC_Startup(&soc_config); + } + if (result < 0) + { + OS_Panic("SOC_Startup failed (%d)", result); + } + g_started = TRUE; + OS_TPrintf("DHCP....\n"); + + while (SOC_GetHostID() == 0) + { + OS_Sleep(100); + } + + OS_TPrintf("IP addr = %3d.%3d.%3d.%3d\n", CPS_CV_IPv4(CPSMyIp)); + OS_TPrintf("NetMask = %3d.%3d.%3d.%3d\n", CPS_CV_IPv4(CPSNetMask)); + OS_TPrintf("GW addr = %3d.%3d.%3d.%3d\n", CPS_CV_IPv4(CPSGatewayIp)); + OS_TPrintf("DNS[0] = %3d.%3d.%3d.%3d\n", CPS_CV_IPv4(CPSDnsIp[0])); + OS_TPrintf("DNS[1] = %3d.%3d.%3d.%3d\n", CPS_CV_IPv4(CPSDnsIp[1])); +} + +static void ncFinishWiFi(void) +{ + previousAddr = SOC_GetHostID(); + if (g_started) + { + (void)SOC_Cleanup(); + g_started = FALSE; + } +} + +#else // SDK_WIFI_INET + +static void ncStartInet(void) +{ + int result; + SOConfig soConfig = + { + SO_VENDOR_NINTENDO, // vendor + SO_VERSION, // version + + NcAlloc, // alloc + NcFree, // free + + SO_FLAG_DHCP, // flag + SO_HtoNl(SO_INADDR_ANY),// addr + SO_HtoNl(SO_INADDR_ANY),// netmask + SO_HtoNl(SO_INADDR_ANY),// router + SO_HtoNl(SO_INADDR_ANY),// dns1 + SO_HtoNl(SO_INADDR_ANY),// dns1 + 4096, // timeWaitBuffer + 4096, // reassemblyBuffer + 0, // maximum transmission unit size + + // TCP + 16384, // default TCP receive window size (default 2 x MSS) + 0, // default TCP total retransmit timeout value (default 100 sec) + + // PPP PPP関連機能は未実装 + NULL, + NULL, + + // PPPoE PPP関連機能は未実装 + NULL, + + // DHCP + "NINTENDO-DS", // DHCP host name + 4, // TCP total retransmit times (default 4) + + // UDP + 0, // default UDP send buffer size (default 1472) + 0 // defualt UDP receive buffer size (default 4416) + }; + +#ifdef SDK_TWL + if (OS_IsRunOnTwl()) + { + soConfig.rwin = 65535; + } +#endif // SDK_TWL + + OS_TPrintf("SO_Startup....\n"); + result = SO_Startup(&soConfig); + if (result < 0) + { + OS_Panic("SO_Startup failed (%d)", result); + } + g_started = TRUE; + + OS_TPrintf("DHCP....\n"); + + while (SO_GetHostID() == 0 && IP_GetConfigError(NULL) == 0) + { + OS_Sleep(100); + } + + if (SO_GetHostID() != 0) + { + int retry = 0; + u8 ip[ IP_ALEN ]; + + IP_GetAddr( NULL, ip ); + OS_Printf("IP addr = %3d.%3d.%3d.%3d\n", ip[0], ip[1], ip[2], ip[3] ); + IP_GetNetmask ( NULL, ip ); + OS_Printf("NetMask = %3d.%3d.%3d.%3d\n", ip[0], ip[1], ip[2], ip[3] ); + IP_GetGateway ( NULL, ip ); + OS_Printf("GW addr = %3d.%3d.%3d.%3d\n", ip[0], ip[1], ip[2], ip[3] ); + } + else + { + OS_TPrintf("NO DHCP SERVER or NO LINK....\n"); + + switch (IP_GetConfigError(NULL)) + { + case IP_ERR_DHCP_TIMEOUT: + OS_Panic("IP_ERR_DHCP_TIMEOUT\n"); + break; + + case IP_ERR_LINK_DOWN: + OS_Panic("IP_ERR_LINK_DOWN\n"); + break; + + default: + OS_Panic("Default???\n"); + break; + } + } + +} + +static void ncFinishInet(void) +{ + previousAddr = SO_GetHostID(); + if (g_started) + { + (void)SO_Cleanup(); + g_started = FALSE; + } +} + +#endif // SDK_WIFI_INET + +/*---------------------------------------------------------------------------* + Name : NcStart + Description : + Arguments : apClass - SitDefsに定義されているアクセスポイントのクラス名 + Returns : + *---------------------------------------------------------------------------*/ +void NcStart(const char* apClass) +{ + + SiteDefs_Init(); + + + if( FALSE == ENV_SetBinary("WiFi.LAN.1.AP.1.WEP.KEY", (void *)GetWlanKEYSTR()) ) { + OS_TPrintf("Error %s %d\n", __FUNCTION__,__LINE__); + } + if( FALSE == ENV_SetString("WiFi.LAN.1.AP.1.ESSID", GetWlanSSID()) ) { + OS_TPrintf("Error %s %d\n", __FUNCTION__,__LINE__); + } + + if( FALSE == ENV_SetU8("WiFi.LAN.1.AP.1.WEP.MODE", (u8)GetWlanMode() ) ) { + OS_TPrintf("Error %s %d\n", __FUNCTION__,__LINE__); + } + + if (!InitWcmApInfo(&apInfo, apClass)) + { + OS_Panic("Invalid AP Class...."); + } + + while (WCM_GetPhase() != WCM_PHASE_NULL) + { + OS_Sleep(100); + } + + InitWcmControlByApInfoEx(&apInfo, g_deviceId); + + OS_TPrintf("LINK UP....\n"); + + while (WCM_GetPhase() != WCM_PHASE_DCF) + { + OS_Sleep(100); + } + + +#ifndef SDK_WIFI_INET +OS_TPrintf("%s %d\n",__FUNCTION__,__LINE__); + + ncStartWiFi(); + +#else // SDK_WIFI_INET +OS_TPrintf("%s %d\n",__FUNCTION__,__LINE__); + + ncStartInet(); +#endif // SDK_WIFI_INET +} + +void NcFinish() +{ +#ifndef SDK_WIFI_INET + ncFinishWiFi(); +#else // SDK_WIFI_INET + ncFinishInet(); +#endif // SDK_WIFI_INET +} + +void NcSetDevice(u8 deviceId) +{ + g_deviceId = deviceId; +} + +SDK_WEAK_SYMBOL void* NcAlloc(u32 name, s32 size) +{ +#pragma unused(name) + OSIntrMode enable = OS_DisableInterrupts(); + void* ptr = OS_Alloc((u32) size); + (void)OS_RestoreInterrupts(enable); + return ptr; +} + +SDK_WEAK_SYMBOL void NcFree(u32 name, void* ptr, s32 size) +{ +#pragma unused(name, size) + OSIntrMode enable = OS_DisableInterrupts(); + OS_Free(ptr); + (void)OS_RestoreInterrupts(enable); + return; +} + +/*---------------------------------------------------------------------------* + ヒープ関連 + *---------------------------------------------------------------------------*/ + +// MainRAM アリーナに対して メモリ割り当てシステム初期化 +static void Heap_Setup(void) +{ + void* nstart; + OSHeapHandle handle; + + nstart = OS_InitAlloc(OS_ARENA_MAIN, OS_GetMainArenaLo(), OS_GetMainArenaHi(), 1); + OS_SetMainArenaLo(nstart); + handle = OS_CreateHeap(OS_ARENA_MAIN, OS_GetMainArenaLo(), OS_GetMainArenaHi()); + (void)OS_SetCurrentHeap(OS_ARENA_MAIN, handle); +} + + +#ifdef SDK_TWL +static void* myAlloc_SOCL(u32 size) +{ + s32 msize = (s32) (size + sizeof(s32)); + s32* m = NcAlloc(0U, msize); + + // 呼び出しに必要なパラメータを領域の先頭に隠しておく + if (m) + { + m[0] = (s32) msize; + m++; + } + + return (void*)m; +} + +static void myFree_SOCL(void* ptr) +{ + s32* m = (s32*)ptr; + + // 呼び出しに必要なパラメータを領域の先頭から取り出す + if (m) + { + m--; + NcFree(0U, (void*)m, m[0]); + } +} +#endif // SDK_TWL diff --git a/build/tools/sctools/common/src/netconnect.h b/build/tools/sctools/common/src/netconnect.h new file mode 100644 index 0000000..1b495fb --- /dev/null +++ b/build/tools/sctools/common/src/netconnect.h @@ -0,0 +1,39 @@ +/*---------------------------------------------------------------------------* + Project: TwlWiFi - demos - netconnect + File: netconnect.h + + Copyright 2005,2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: 2008-01-11#$ + $Rev: 212 $ + $Author: adachi_hiroaki $ + *---------------------------------------------------------------------------*/ + +#ifndef NITROWIFI_DEMOS_NETCONNECT_H_ +#define NITROWIFI_DEMOS_NETCONNECT_H_ + +#ifdef __cplusplus + +extern "C" +{ +#endif + +void NcGlobalInit(void); +void NcStart(const char* apClass); +void NcFinish(void); +void NcSetDevice(u8 deviceId); +void* NcAlloc(u32 name, s32 size); +void NcFree(u32 name, void* ptr, s32 size); + +#ifdef __cplusplus + +} /* extern "C" */ +#endif + +#endif /* NITROWIFI_DEMOS_NETCOONECT_H_ */ diff --git a/build/tools/sctools/common/src/nuc.c b/build/tools/sctools/common/src/nuc.c new file mode 100644 index 0000000..253117b --- /dev/null +++ b/build/tools/sctools/common/src/nuc.c @@ -0,0 +1,503 @@ +#include +#include +#include +#include + +#include "netconnect.h" +#include "sitedefs.h" +#include "nuc.h" +#include "nuc_error_msg.h" + +// Network Updateのフェーズを表す +typedef enum{ + + PHASE_INIT, // 初期状態 + PHASE_READY, // 開始 + PHASE_CONNECTING_NETWORK, // ネットワーク接続中 + PHASE_TEST_READY, // 接続テスト開始 + PHASE_TEST_PROCESS, // 接続テスト中 + PHASE_TEST_GETTING_WII_ID, // Wii ID取得処理 + PHASE_TEST_FINISHED, // 接続テスト完了 + PHASE_TEST_CLEANUP, // 接続テスト後処理 + PHASE_NUP_BREAK, // <キー入力待ち>アップデート前確認待ち + PHASE_NUP_READY, // ネットワークアップデート開始 + PHASE_NUP_CHECK, // ネットワークアップデート更新情報取得中 + PHASE_NUP_DOWNLOAD, // ネットワークアップデート ダウンロード開始 + PHASE_NUP_PROCESS, // ネットワークアップデート中 + PHASE_NUP_FINISHED, // ネットワークアップデート完了 + PHASE_NUP_CLEANUP, // ネットワークアップデート後処理 + PHASE_NUP_SKIPPED, // ネットワークアップデートがスキップされた + PHASE_CLEANING_UP, // 後処理 + PHASE_FINISHED, // 完了 + PHASE_ERROR_OCCURRED // エラー発生 +} NucPhaseState; + + +// ネットワーク接続状態 +typedef enum{ + NET_CONNECT_NONE, + NET_CONNECT_OK, + NET_CONNECT_ERROR +} NetConnectState; + +static volatile NetConnectState NetConnect = NET_CONNECT_NONE; + +// state管理 +static struct +{ + NucPhaseState state; + u32 count; +} TestState; + +static inline void ChangeState(NucPhaseState state) +{ + TestState.state = state; + TestState.count = 0; +} + +// titleID 取得用 +static NUCTitleId TitleIds[NUC_MAX_TITLE_UPDATE_COUNT]; +static u32 TitleIdNum; + +static u8 WorkForNA[NA_VERSION_DATA_WORK_SIZE]; + +static BOOL AllocFailTest = FALSE; + +static void *alloc(u32 size, int align) +{ + u32 *ptr = NULL, *realPtr = NULL; + u32 realSize; + OSIntrMode old; + + old = OS_DisableInterrupts(); + + /* realSize is size plus alignment and header */ + if (align < 4) + { + align = 4; + } + realSize = size + align + 4; + + realPtr = (u32 *) OS_Alloc(realSize); + ptr = (u32 *)((((u32) realPtr) + 4 + align - 1) & ~(align - 1)); + + *((u32 *)(((u32)ptr) - 4)) = (u32) realPtr; + (void)OS_RestoreInterrupts( old ); + + end: + SDK_ASSERT(((u32)ptr & (align - 1)) == 0); + return (void *) ptr; +} + +static void free(void *p) +{ + u32 realPtr = *((u32 *)(((u32)p) - 4)); + OS_Free((void *) realPtr); +} + +/*---------------------------------------------------------------------------* + Name: InitNupLib + Description: NUCライブラリを開始します。 + *---------------------------------------------------------------------------*/ +static BOOL InitNupLib() +{ + BOOL ret; + + ret = NUC_Init(alloc, free); + + if (ret == FALSE) { + OS_TPrintf("NUC_Init() failed, error code=%d\n", NUC_GetLastError()); + } + return ret; +} + +/*---------------------------------------------------------------------------* + Name: StartNupCheck + Description: ダウンロードリスト一覧の取得を開始します。 + *---------------------------------------------------------------------------*/ +static BOOL StartNupCheck(void) +{ + BOOL ret; + TitleIdNum = sizeof(TitleIds) / sizeof(TitleIds[0]); + + ret = NUC_CheckAsync(TitleIds, &TitleIdNum); + if (ret == FALSE) + { + OS_TPrintf("NUC_CheckAsync() failed, error code=%d\n", NUC_GetLastError()); + } + + return ret; +} + + +/*---------------------------------------------------------------------------* + Name: ProgressNupCheck + Description: ダウンロードリスト一覧の取得状況を確認します + *---------------------------------------------------------------------------*/ +static NucStatus ProgressNupCheck(void) +{ + u64 CurrentSize, TotalSize; + NucStatus status; + + NUC_GetProgress(&CurrentSize, &TotalSize, &status); + if (status == NUC_STATUS_ERROR) + { + OS_TPrintf("NUC_GetProgress() failed in checking, error code=%d\n", NUC_GetLastError()); + } +#if 0 + if (TestState.count++ % STRING_ANIM_CNT == 0) + { + u32 num = (TestState.count / STRING_ANIM_CNT) % 3; + const char* msg[] = { + "Now checking list. ", + "Now checking list.. ", + "Now checking list... "}; + + PrintString(TEXT_X, TEXT_Y, COLOR_WHITE, msg[num]); + } +#endif + return status; +} + +/*---------------------------------------------------------------------------* + Name: StartNupDownload + Description: ダウンロードを開始します。 + *---------------------------------------------------------------------------*/ +static BOOL StartNupDownload(void) +{ + /* こいつが呼ばれたらFSが切り離されてしまうのでリブートが必要 */ + BOOL ret = NUC_DownloadAsync(TitleIds, TitleIdNum); + + if (ret == FALSE) + { + OS_TPrintf("NUP_DownloadAsync() failed, error code=%d\n", NUC_GetLastError()); + } + + return ret; +} + +/*---------------------------------------------------------------------------* + Name: ProgressNupDownload + Description: ダウンロードの進行状況を表示します。 + *---------------------------------------------------------------------------*/ +static NucStatus ProgressNupDownload(void) +{ + u64 CurrentSize, TotalSize; + NucStatus status; + + NUC_GetProgress(&CurrentSize, &TotalSize, &status); + if (status == NUC_STATUS_ERROR) + { // エラー発生 + OS_TPrintf("NUC_GetProgress() failed in download, error code=%d\n", NUC_GetLastError()); + } + else { +#if 0 + // ダウンロード状況を描画 + int dw = (int)((CurrentSize * BAR_W)/TotalSize); + FillRect(BAR_X, BAR_Y, dw, BAR_H, GX_RGBA(31, 16, 16, 1)); +#endif + } + +#if 0 + if (TestState.count++ % STRING_ANIM_CNT == 0) + { + u32 num = (TestState.count / STRING_ANIM_CNT) % 3; + const char* msg[] = { + "Now downloading. ", + "Now downloading.. ", + "Now downloading... "}; + PrintString(TEXT_X, TEXT_Y, COLOR_WHITE, msg[num]); + } +#endif + return status; +} + +/*---------------------------------------------------------------------------* + Name: CleanNupLib + Description: NUCライブラリを終了します。 + *---------------------------------------------------------------------------*/ +static BOOL CleanNupLib() +{ + BOOL ret = NUC_Cleanup(TitleIds, TitleIdNum); + if (ret == FALSE) + { + OS_TPrintf("NUP_CleanUp() failed, error code=%d\n", NUC_GetLastError()); + } + return ret; +} + +/*---------------------------------------------------------------------------* + Name: ProgressNetConnect + Description: ネットワーク接続を待ちます。 + *---------------------------------------------------------------------------*/ +static void ProgressNetConnect(void) +{ +#if 0 + if (TestState.count++ % STRING_ANIM_CNT == 0) + { + u32 num = (TestState.count / STRING_ANIM_CNT) % 3; + const char* msg[] = { + "Connecting network. ", + "Connecting network.. ", + "Connecting network..."}; + PrintString(TEXT_X, TEXT_Y, COLOR_WHITE, msg[num]); + } +#endif +} + + + +static void ShowErrorMsg(int error_code) +{ +#if 0 + PrintString(TEXT_X, TEXT_Y, COLOR_WHITE, "Error Occurred "); +#endif + if (error_code > 0) + { +#if 0 + PrintString(TEXT_X, TEXT_Y + 10, COLOR_WHITE, "Error Code:%d", error_code); + PrintString(TEXT_X, TEXT_Y + 12, COLOR_WHITE, "%s", GetPublicMsg(error_code)); + PrintString(TEXT_X, TEXT_Y + 15, COLOR_WHITE, "%s", GetPrivateMsg(error_code)); +#endif + OS_TPrintf( "Error Code:%d\n", error_code); + OS_TPrintf( "%s\n", GetPublicMsg(error_code)); + OS_TPrintf( "%s\n", GetPrivateMsg(error_code)); + } + else + { + OS_TPrintf( "%s\n", "Network Error occurred.\nTry again later."); +#if 0 + // ネットワークエラー時の表示メッセージ(暫定) + PrintString(TEXT_X, TEXT_Y + 10, COLOR_WHITE, "%s", "Network Error occurred.\nTry again later."); +#endif + } +} + +// ネットワーク接続関連 +#define STACK_SIZE (1024*4) +static OSThread NetThread; +static u64 NetStack[STACK_SIZE / sizeof(u64)]; +#define THREAD1_PRIO 24 +static volatile BOOL NetHTTPEnd = FALSE; + + +static void NetConnectProc(void *arg) +{ +#pragma unused(arg) + BOOL ret; + NucStatus status; + int error_code = 0; + + + /* Start networking */ + NcStart(SITEDEFS_DEFAULTCLASS); + + NetConnect = NET_CONNECT_OK; + //OS_TPrintf("%s %d\n",__FUNCTION__,__LINE__); + // 終了の呼び出しを待つ +// OS_SleepThread(NULL); + + while(1) { + OS_Sleep( 16 ); /* OS_WaitVBlankIntrの代わり */ + + switch ( TestState.state ) { + case PHASE_CONNECTING_NETWORK: + ProgressNetConnect(); + if (NetConnect == NET_CONNECT_OK) + { + ChangeState(PHASE_NUP_BREAK); + } + else if (NetConnect == NET_CONNECT_ERROR) + { // ネットワーク接続エラー + error_code = -1; + ChangeState(PHASE_ERROR_OCCURRED); + } + break; + case PHASE_NUP_BREAK: // AボタンでNUPライブラリを初期化します。 + ret = InitNupLib(); + if (ret == FALSE) + { // エラー発生 + ChangeState(PHASE_ERROR_OCCURRED); + error_code = NUC_GetLastError(); + } + else + { + ChangeState(PHASE_NUP_READY); + } + break; + + case PHASE_NUP_READY: // 更新情報の取得を開始します。 + ret = StartNupCheck(); + if (ret == FALSE) + { // エラー発生 + error_code = NUC_GetLastError(); + ChangeState(PHASE_NUP_CLEANUP); + } + else + { + ChangeState(PHASE_NUP_CHECK); + } + break; + + case PHASE_NUP_CHECK: // 更新情報の取得状況を表示します。 + status = ProgressNupCheck(); + if (status == NUC_STATUS_ERROR) + { // エラー発生 + ChangeState(PHASE_NUP_CLEANUP); + error_code = NUC_GetLastError(); + } + else if (status == NUC_STATUS_COMPLETED) + { // 更新リスト 取得終了 + if (TitleIdNum > 0 ) + { // ダウンロードへ + int i; + for (i = 0; i < TitleIdNum; i++) + { + OS_TPrintf("DL list:%3d:0x%llx", i, TitleIds[i]); + } + ChangeState(PHASE_NUP_DOWNLOAD); + } + else + { // 更新すべきものがない + OS_TPrintf("No title to update\n"); + ChangeState(PHASE_NUP_CLEANUP); + } + } + break; + + case PHASE_NUP_DOWNLOAD: // ダウンロードを開始します。 + ret = StartNupDownload(); + if (ret == FALSE) + { // エラー発生 + ChangeState(PHASE_NUP_CLEANUP); + error_code = NUC_GetLastError(); + } + else + { + ChangeState(PHASE_NUP_PROCESS); + } + break; + + case PHASE_NUP_PROCESS: // ダウンロードの進行状況を表示します。 + status = ProgressNupDownload(); + if (status == NUC_STATUS_ERROR) + { // エラー発生 + ChangeState(PHASE_NUP_CLEANUP); + error_code = NUC_GetLastError(); + } + else if (status == NUC_STATUS_COMPLETED) + { // ダウンロード完了 + ChangeState(PHASE_NUP_CLEANUP); + } + break; + + case PHASE_NUP_CLEANUP: // NUPライブラリのクリーンアップ + ret = CleanNupLib(); + if (ret == FALSE && error_code == 0) + { // 他でエラーが起こっていない場合のみ上書きします。 + error_code = NUC_GetLastError(); + } + // ネットの切断と後始末 + OS_WakeupThreadDirect(&NetThread); + ChangeState(PHASE_CLEANING_UP); + break; + + case PHASE_CLEANING_UP: // ネットの切断終了を待ちます + if (NetConnect == NET_CONNECT_NONE) + { // 切断終了 + if (error_code == 0) + { // 正常終了 + ChangeState(PHASE_FINISHED); + } + else + { + ChangeState(PHASE_ERROR_OCCURRED); + } + } + break; + + case PHASE_FINISHED: // ネットワークアップデートが正常終了しました。 + OS_TPrintf("Network Update Completed\n"); + if (TitleIdNum > 0) + { + OS_TPrintf("%d file is updated\n", TitleIdNum); + } + else + { + OS_TPrintf("Nothing is updated\n"); + } + + if (TitleIdNum > 0) + { // 再起動する + OS_TPrintf("再起動します\n"); + } + else + { // メッセージを表示して戻る + OS_TPrintf("アップデートするものがなかったので戻ります\n"); + } + goto end; + + break; + + case PHASE_ERROR_OCCURRED: // エラーが発生しています。 + + ShowErrorMsg(error_code); + goto end; +#if 0 + if (Trg == PAD_BUTTON_A) + { + OS_TPrintf("エラーが発生したので戻ります\n"); + goto end; + } +#endif + break; + + default: +OS_TPrintf("%s %d\n",__FUNCTION__,__LINE__); + + break; + } + } + + end: + NcFinish(); + NetConnect = NET_CONNECT_NONE; + + OS_TPrintf("Network Connection ended\n"); +} + + +/* ********************** */ +void nuc_main(void) +{ + + ChangeState(PHASE_INIT); + // NcGlobalInit(); in netconnect.c + + /* クライアント証明書の初期化 */ + SEA_Init(); + // あらかじめWRAMにロードしておきます + if (!NA_LoadVersionDataArchive(WorkForNA, NA_VERSION_DATA_WORK_SIZE)) { + OS_TPrintf("NA load error\n"); + goto end; + } + + if (!NUC_LoadCert()) { + // WRAMにロード + OS_TPrintf("Client cert load error\n"); + goto end; + } + + (void)NA_UnloadVersionDataArchive(); + + + /* 接続設定スレッドの作成 */ + OS_CreateThread(&NetThread, NetConnectProc, + NULL, NetStack + STACK_SIZE / sizeof(u64), + STACK_SIZE, THREAD1_PRIO); + OS_WakeupThreadDirect(&NetThread); + ChangeState(PHASE_CONNECTING_NETWORK); + + end: + return; +} diff --git a/build/tools/sctools/common/src/nuc.h b/build/tools/sctools/common/src/nuc.h new file mode 100644 index 0000000..afb2b50 --- /dev/null +++ b/build/tools/sctools/common/src/nuc.h @@ -0,0 +1,14 @@ +#ifndef _NUC_H_ +#define _NUC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +void nuc_main(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _NUC_H_ */ diff --git a/build/tools/sctools/common/src/nuc_error_msg.c b/build/tools/sctools/common/src/nuc_error_msg.c new file mode 100644 index 0000000..66bec8a --- /dev/null +++ b/build/tools/sctools/common/src/nuc_error_msg.c @@ -0,0 +1,277 @@ +/*---------------------------------------------------------------------------* + Project: TwlWiFi - build - demos.TWL - nuc + File: error_msg.c + + Copyright 2007-2008 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: 2008-09-18#$ + $Rev: 1024 $ + $Author: okubata_ryoma $ + *---------------------------------------------------------------------------*/ + +#include +#include +#include "nuc_error_msg.h" + +/*---------------------------------------------------------------------------* + 内部使用の定数定義 + *---------------------------------------------------------------------------*/ +static const char* PrivateNupError[] = +{ + "[NUP] Unknown Error", + "[NUP] Another NUP operation is in progress", + "[NUP] Unexpected internal error in the NUP library", + "[NUP] Invalid input parameters", + "[NUP] Country code is undefined", + "[NUP] Unable to get version data", + "[NUP] Unable to create data file", + "[NUP] Out of memory" +}; + +static const char* PrivateCheckError[] = +{ + "[CHK] Unknown Error", + "[CHK] No operation started since NUP_Init()", + "[CHK] The check operation is in progress", + "[CHK] Unexpected internal error in the NUP library", + "[CHK] Exceeded memory restriction", + "[CHK] Required system titles not on system", + "[CHK] Failed to parse SOAP response", + "[CHK] Server returned error in SOAP response", + "[CHK] Version of a title is newer on the system than on the server", + "[CHK] Server returned a HTTP status error when downloading content, ticket, or TMD" +}; + +static const char* PrivateDownloadError[] = +{ + "[DL] Unknown Error", + "[DL] No operation started since NUP_Init()", + "[DL] The download operation is in progress", + "[DL] Unexpected internal error in the NUP library", + "[DL] Exceeded memory restriction", + "[DL] Required system titles not on system", + "[DL] Failed to parse SOAP response", + "[DL] Server returned error in SOAP response", + "[DL] Version of a title is newer on the system than on the server", + "[DL] Server returned a HTTP status error when downloading content, ticket, or TMD", + "[DL] One or more specified titles not found on the update list", + "[DL] Invalid TMD downloaded", + "[DL] Invalid ticket downloaded", + "[DL] Failed to import ticket", + "[DL] Failed to initialize title import", + "[DL] Failed to complete title import", + "[DL] Failed to begin content import", + "[DL] Failed to import content data", + "[DL] Failed to end content import", + "[DL] Insufficient space on the file system to download title", + "[DL] Insufficient space on the file system to create backup data" +}; + +static const char* PrivateHTTPError[] = +{ + "[HTTP] Unknown Error", + "[HTTP] Failed to resolve DNS", + "[HTTP] Failed to connect to server", + "[HTTP] Failed to resolve DNS in proxy server", + "[HTTP] Failed to connect to proxy server", + "[HTTP] Invalid handle error", + "[HTTP] Failed to alloc", + "[HTTP] Socket Error", + "[HTTP] Receive buffer is full", + "[HTTP] Failed to parse HTTP header", + "[HTTP] Request is canceled by the caller", + "[HTTP] Failed to create HTTP thread", + "[HTTP] Error in sending, recieving or closing socket", + "[HTTP] Invalid parameter is set in NHTTPCreateConnection", + "[HTTP] request is in progress", + "[HTTP] Failed to set Root CA", + "[HTTP] Failed to set client cert" +}; + +static const char* PrivateSSLError[] = +{ + "[SSL] Unknown Error", + "[SSL] SSL protocol failure", + "[SSL] Read attempted but without waiting for blocking from the caller", + "[SSL] Read attempted but without waiting for blocking from the caller", + "[SSL] System call error", + "[SSL] Read/write system returned 0", + "[SSL] BIO not yet connected", + "[SSL] SSL Descriptor is out of range", + "[SSL] the server name and common name do not match", + "[SSL] verification failed for the root CA", + "[SSL] the certificate chain is incorrect", + "[SSL] the time limit for the certificate has expired", + "[SSL] Cert size is too large to get", + "[SSL] Invalid root CA", + "[SSL] Invalid client cert", + "[SSL] the certificate has been denied", + "[SSL] NSSL library was not initialized" +}; + +static const char* PrivateFSError[] = +{ + "[FS] Unknown Error", + "[FS] Failed to correctly process results", + "[FS] Current command is busy", + "[FS] Command was canceled", + "[FS] Archive is not supported by the specified command", + "[FS] Some arguments are invalid to process the specified command", + "[FS] Some internal resource is not enough to process the specified command", + "[FS] Situation which the specified command will make is already made", + "[FS] Specified command has failed by the problem of access permission.", + "[FS] Detected file system corruption", + "[FS] Failed in the results where a detectable error occurred during processing." +}; + +static const char* PrivateESError[] = +{ + "[ES] Unknown Error", + "[ES] Incorrect cert type", + "[ES] Failed to generate key", + "[ES] Failed to verify signature", + "[ES] Cannot open file", + "[ES] Incorrect public key type", + "[ES] Cert issurer mismatch", + "[ES] Failed to encrypt", + "[ES] Failed to open file", + "[ES] Failed to read file", + "[ES] Failed to write file", + "[ES] TMD contents number error", + "[ES] Incorrect signature length", + "[ES] Incorrect cert length", + "[ES] Device error", + "[ES] File descripter is maximum", + "[ES] Invalid operation", + "[ES] FS connection failure", + "[ES] Unsupported transfer source", + "[ES] Device ID mismatch", + "[ES] Incorrect content size", + "[ES] Hash mismatch", + "[ES] Incorrent content count", + "[ES] Out of memory", + "[ES] No TMD file exists", + "[ES] No right", + "[ES] Issuer not found", + "[ES] No ticket exists", + "[ES] Incorrect ticket", + "[ES] Not enough space", + "[ES] Incorrect boot version", + "[ES] Unknown error", + "[ES] Expired error", + "[ES] Unused error", + "[ES] Incorrect title version", + "[ES] OS Ticket does not exist", + "[ES] OS Content does not exist", + "[ES] Not empty", + "[ES] Disc NAND have no TMD" +}; + +static const char* PublicError[] = +{ + /* エラーが発生しましたため、 + TWL本体の更新ができません。*/ + "Error occurred.\nUnable to update TWL system.", + /* TWL本体保存メモリの空き容量が + 不足しています。 + TWL本体の更新はできませんでした。*/ + "Insufficient memory in TWL\nUnable to update TWL system.", + /* サーバーに接続できません。 + しばらく待ってやり直してください。*/ + "Unable to connect to server\nTry again later.", + /* インターネットのエラーにより、 + TWL本体の更新ができません。 + しばらく待ってやり直してください。*/ + "Network Error occurred.\nTry again later." +}; + +/*---------------------------------------------------------------------------* + 内部使用のワーク領域型定義 + *---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------* + グローバル変数 + *---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------* + 内部使用の関数定義(ローカル) + *---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------* + 外部公開の関数定義 + *---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------* + Name: GetPrivateMsg + + Description: 開発者向けのエラーのメッセージを取得します。 + + Arguments: error_code + + Returns: 文字列へのポインタ + *---------------------------------------------------------------------------*/ +const char* GetPrivateMsg(int error_code) +{ + if (34000 <= error_code && error_code <= 34007) + { // NUPの一般的なエラー + return PrivateNupError[error_code - 34000]; + } + else if (34100 <= error_code && error_code <= 34109) + { // 更新情報取得中のエラー + return PrivateCheckError[error_code - 34100]; + } + else if (34200 <= error_code && error_code <= 34220) + { // ダウンロード中のエラー + return PrivateDownloadError[error_code - 34200]; + } + else if (34300 <= error_code && error_code <= 34316) + { // NHTTPのエラー + return PrivateHTTPError[error_code - 34300]; + } + else if (34400 <= error_code && error_code <= 34417) + { // NSSLのエラー + return PrivateSSLError[error_code - 34400]; + } + else if (34500 <= error_code && error_code <= 34510) + { // FSのエラー + return PrivateFSError[error_code - 34500]; + } + else if (34600 <= error_code && error_code <= 34639) + { // ESのエラー + return PrivateESError[error_code - 34600]; + } + return NULL; +} + +/*---------------------------------------------------------------------------* + Name: GetPublicMsg + + Description: ユーザー向けのエラーのメッセージを取得します。 + + Arguments: error_code + + Returns: 文字列へのポインタ + *---------------------------------------------------------------------------*/ +const char* GetPublicMsg(int error_code) +{ + NucError type = NUC_GetErrorType(error_code); + switch(type) + { + case NUC_ERROR_NO_SPACE: + return PublicError[1]; + case NUC_ERROR_CONNECT: + return PublicError[2]; + case NUC_ERROR_INTERNET: + return PublicError[3]; + case NUC_ERROR_UPDATE: + default: + return PublicError[0]; + } +} + diff --git a/build/tools/sctools/common/src/nuc_error_msg.h b/build/tools/sctools/common/src/nuc_error_msg.h new file mode 100644 index 0000000..9e90d5d --- /dev/null +++ b/build/tools/sctools/common/src/nuc_error_msg.h @@ -0,0 +1,52 @@ +#ifndef _NUC_ERROR_MSG_H_ +#define _NUC_ERROR_MSG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/*---------------------------------------------------------------------------* + 定数 + *---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------* + 型 + *---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------* + 関数 + *---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------* + Name: GetPrivateMsg + + Description: 開発者向けのエラーのメッセージを取得します。 + + Arguments: error_code + + Returns: 文字列へのポインタ + *---------------------------------------------------------------------------*/ +const char* GetPrivateMsg(int error_code); + +/*---------------------------------------------------------------------------* + Name: GetPublicMsg + + Description: ユーザー向けのエラーのメッセージを取得します。 + + Arguments: error_code + + Returns: 文字列へのポインタ + *---------------------------------------------------------------------------*/ +const char* GetPublicMsg(int error_code); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _NUC_ERROR_MSG_H_ */ + +/*---------------------------------------------------------------------------* + End of file + *---------------------------------------------------------------------------*/ diff --git a/build/tools/sctools/common/src/sitedefs.c b/build/tools/sctools/common/src/sitedefs.c new file mode 100644 index 0000000..348ac29 --- /dev/null +++ b/build/tools/sctools/common/src/sitedefs.c @@ -0,0 +1,237 @@ +/*---------------------------------------------------------------------------* + Project: TwlWiFi - demos - netconnect + File: sitedefs.c + + Copyright 2005,2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: 2007-10-30#$ + $Rev: 74 $ + $Author: adachi_hiroaki $ + *---------------------------------------------------------------------------*/ + +#include +#include +#include "sitedefs.h" + + +/* + c:/twlsdk/include/nitro/env/env_system.h +*/ + +BOOL ENV_SetBinary(const char *name, void *ptr) +{ + ENVResource *resSetPtr; + ENVResource *p = ENVi_Search(name, &resSetPtr); + u16 len = 1; + + if (!p) { + return FALSE; + } + + if (p->type != ENV_RESTYPE_BINARY) { + return FALSE; + } + + len = (u16)(p->len - ((p->type == ENV_RESTYPE_STRING) ? 1 : 0)); + OS_TPrintf("env bin len = %d\n", len); + + + if (p->type & ENV_RESTYPE_OFFSET_MASK) + { + // return (void *)((u32)resSetPtr + (u32)(p->ptr)); + // (char *)((u32)resSetPtr + (u32)(p->ptr)) = val; + STD_CopyMemory( (void *)((u32)resSetPtr + (u32)(p->ptr)) , ptr, (u32)STD_StrLen(ptr) ); + } + else + { + // return p->ptr; + // (char *)p->ptr = val; + // STD_CopyMemory( (void *)(p->ptr) , ptr , (u32)len ); + STD_CopyMemory( (void *)(p->ptr) , ptr , (u32)STD_StrLen(ptr) ); + } + return TRUE; +} + +BOOL ENV_SetString(const char *name, char *str) +{ + ENVResource *resSetPtr; + ENVResource *p = ENVi_Search(name, &resSetPtr); + u16 len = 1; + + if (!p) { + return FALSE; + } + + if (p->type != ENV_RESTYPE_STRING) { + return FALSE; + } + + len = (u16)(p->len - ((p->type == ENV_RESTYPE_STRING) ? 1 : 0)); + OS_TPrintf("env str len = %d\n", len); + + + if (p->type & ENV_RESTYPE_OFFSET_MASK) + { + // return (void *)((u32)resSetPtr + (u32)(p->ptr)); + // (char *)((u32)resSetPtr + (u32)(p->ptr)) = val; + STD_CopyMemory( (void *)((u32)resSetPtr + (u32)(p->ptr)) , (void *)str, (u32)STD_StrLen(str) ); + } + else + { + // return p->ptr; + // (char *)p->ptr = val; + STD_CopyMemory( (void *)(p->ptr) , (void *)str , (u32)STD_StrLen(str) ); + } + return TRUE; +} + + + +BOOL ENV_SetU8(const char *name, u8 val) +{ + ENVResource *resSetPtr; + ENVResource *p = ENVi_Search(name, &resSetPtr); + u16 len; + + if (!p) { + return FALSE; + } + + len = (u16)(p->len - ((p->type == ENV_RESTYPE_STRING) ? 1 : 0)); + OS_TPrintf("env u8 len = %d\n", len); + + + // return p->ptr; + p->ptr = (u8 *)val; + return TRUE; +} + + + + +#if 0 +void SitedefsSetWepMode() +{ + ENVResource *ap_wepmode = ENV_Search("WiFi.LAN.1.AP.1.WEP.MODE"); /* U8 */ + "WiFi.LAN.1.AP.1.WEP.KEY" // BINARY -> ENV_GetBinary(const char *name, void **retPtr) + // BOOL ENV_GetBinaryAndSize(const char *name, void **retPtr, int *size) + + "WiFi.LAN.1.AP.1.ESSID" // STRING + static inline BOOL ENV_GetString(const char *name, char **retPtr) + static inline BOOL ENV_GetStringAndLength(const char *name, char **retPtr, int *len) +} +#endif + +/* *INDENT-OFF* */ +static ENVResource myResources[] = +{ + //----------------------------- + // LAN + //----------------------------- + + // LAN:Access Point 1 + // Demos in TwlWiFi use this settings by default. + { "WiFi.LAN.1.AP.1.ISVALID", ENV_BOOL ( TRUE ) }, + { "WiFi.LAN.1.AP.1.DESC", ENV_STRING( "Access Point for Demos" ) }, + { "WiFi.LAN.1.AP.1.ROUTERMODE", ENV_BOOL ( TRUE ) }, + { "WiFi.LAN.1.AP.1.ESSID", ENV_STRING( "\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" ) }, /* 最大32バイト */ + { "WiFi.LAN.1.AP.1.BSSID", ENV_BINARY( "\xff\xff\xff\xff\xff\xff" ) }, + { "WiFi.LAN.1.AP.1.AUTHMODE", ENV_U32 ( WCM_OPTION_AUTH_OPENSYSTEM ) }, + { "WiFi.LAN.1.AP.1.WEP.MODE", ENV_U8 ( WCM_WEPMODE_NONE ) }, + { "WiFi.LAN.1.AP.1.WEP.KEYID", ENV_U8 ( 0 ) }, + { "WiFi.LAN.1.AP.1.WEP.KEY", /* 最大80バイト */ + ENV_BINARY("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00") }, +#if 0 + // HOST with fixed address on the LAN.1.AP.1 + { "WiFi.LAN.1.AP.1.HOST.1.ISVALID", ENV_BOOL ( TRUE ) }, + { "WiFi.LAN.1.AP.1.HOST.1.NAME", ENV_STRING( "192.168.2.106" ) }, + { "WiFi.LAN.1.AP.1.HOST.1.NETMASK", ENV_STRING( "255.255.255.0" ) }, + { "WiFi.LAN.1.AP.1.HOST.1.GATEWAY", ENV_STRING( "192.168.2.1" ) }, + { "WiFi.LAN.1.AP.1.HOST.1.DNS1", ENV_STRING( "192.168.2.1" ) }, + { "WiFi.LAN.1.AP.1.HOST.1.DNS2", ENV_STRING( "0.0.0.0" ) }, +#else + // HOST with fixed address on the LAN.1.AP.1 + { "WiFi.LAN.1.AP.1.HOST.1.ISVALID", ENV_BOOL ( TRUE ) }, + { "WiFi.LAN.1.AP.1.HOST.1.NAME", ENV_STRING( "10.101.11.91" ) }, + { "WiFi.LAN.1.AP.1.HOST.1.NETMASK", ENV_STRING( "255.255.255.0" ) }, + { "WiFi.LAN.1.AP.1.HOST.1.GATEWAY", ENV_STRING( "10.101.11.230" ) }, + { "WiFi.LAN.1.AP.1.HOST.1.DNS1", ENV_STRING( "10.100.11.1" ) }, + { "WiFi.LAN.1.AP.1.HOST.1.DNS2", ENV_STRING( "10.100.11.5" ) }, + +#endif + // LAN:HTTP Server + { "WiFi.LAN.1.HTTP.1.ISVALID", ENV_BOOL ( TRUE ) }, + { "WiFi.LAN.1.HTTP.1.DESC", ENV_STRING( "linux server" ) }, + { "WiFi.LAN.1.HTTP.1.NAME", ENV_STRING( "192.168.2.2" ) }, + { "WiFi.LAN.1.HTTP.1.PORT.1", ENV_U16 ( 80 ) }, + + // LAN:ECHO Server + { "WiFi.LAN.1.ECHO.1.ISVALID", ENV_BOOL ( TRUE ) }, + { "WiFi.LAN.1.ECHO.1.DESC", ENV_STRING( "linux server" ) }, + { "WiFi.LAN.1.ECHO.1.NAME", ENV_STRING( "192.168.2.2" ) }, + { "WiFi.LAN.1.ECHO.1.PORT.1", ENV_U16 ( 7 ) }, + + // LAN:CHARGEN Server + { "WiFi.LAN.1.CHARGEN.1.ISVALID",ENV_BOOL ( TRUE ) }, + { "WiFi.LAN.1.CHARGEN.1.DESC", ENV_STRING( "linux server" ) }, + { "WiFi.LAN.1.CHARGEN.1.NAME", ENV_STRING( "192.168.2.2" ) }, + { "WiFi.LAN.1.CHARGEN.1.PORT.1", ENV_U16 ( 19 ) }, + + // LAN:Ping Taret + { "WiFi.LAN.1.PING.1.ISVALID", ENV_BOOL ( TRUE ) }, + { "WiFi.LAN.1.PING.1.DESC", ENV_STRING( "default gateway" ) }, + { "WiFi.LAN.1.PING.1.NAME", ENV_STRING( "192.168.2.1" ) }, + + // LAN:FTP Server + { "WiFi.LAN.1.FTP.1.ISVALID", ENV_BOOL ( TRUE ) }, + { "WiFi.LAN.1.FTP.1.DESC", ENV_STRING( "linux server" ) }, + { "WiFi.LAN.1.FTP.1.NAME", ENV_STRING( "192.168.2.2" ) }, + { "WiFi.LAN.1.FTP.1.PORT.1", ENV_U16 ( 21 ) }, + + //---- end mark + ENV_RESOURCE_END, +}; + +ENVResource* resourceArray[]= +{ + myResources, NULL +}; +/* *INDENT-ON* */ + +/*---------------------------------------------------------------------------* + Name: SiteDefs_Init + + Description: main + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void SiteDefs_Init(void) +{ + //---- declaration of using ENV system + //SDK と WIFI の両方に ENV ライブラリが入っているため、以下のように切り替える + //SDKがENVの旧versionを持っているなら、それに合わせて ENV_Init は昔版で。 +#if (SDK_VERSION_DATE <= 20050918) && (SDK_VERSION_DATE >= 20050908) + ENV_Init(myResources); +#else + //SDKにENVの旧versionは含まれていないので、それに合わせて ENV_Init は改良版で。 + ENV_Init(); +#endif +} + +/*====== End of sitedefs.c ======*/ diff --git a/build/tools/sctools/common/src/sitedefs.h b/build/tools/sctools/common/src/sitedefs.h new file mode 100644 index 0000000..fb5ffcd --- /dev/null +++ b/build/tools/sctools/common/src/sitedefs.h @@ -0,0 +1,38 @@ +/*---------------------------------------------------------------------------* + Project: TwlWiFi - demos - netconnect + File: sitedefs.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: 2008-07-14#$ + $Rev: 788 $ + $Author: adachi_hiroaki $ + *---------------------------------------------------------------------------*/ +#ifndef NITROWIFI_SITEDEFS_H_ +#define NITROWIFI_SITEDEFS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +#define SITEDEFS_DEFAULTCLASS "WiFi.LAN.1.AP.1" +#define SITEDEFS_DEFAULTCLASS_FOR_TEST "WiFi.LAN.1.AP.1" +void SiteDefs_Init(void); +BOOL ENV_SetBinary(const char *name, void *ptr); +BOOL ENV_SetString(const char *name, char *str); +BOOL ENV_SetU8(const char *name, u8 val); + + + +#ifdef __cplusplus +} +#endif + +#endif // NITROWIFI_SITEDEFS_H_ diff --git a/build/tools/sctools/common/src/stream.c b/build/tools/sctools/common/src/stream.c new file mode 100644 index 0000000..834c49d --- /dev/null +++ b/build/tools/sctools/common/src/stream.c @@ -0,0 +1,398 @@ +#include +#include "stream.h" + + +#define MAKE_FOURCC(cc1, cc2, cc3, cc4) (u32)((cc1) | (cc2 << 8) | (cc3 << 16) | (cc4 << 24)) + +#define FOURCC_RIFF MAKE_FOURCC('R', 'I', 'F', 'F') +#define FOURCC_WAVE MAKE_FOURCC('W', 'A', 'V', 'E') +#define FOURCC_fmt MAKE_FOURCC('f', 'm', 't', ' ') +#define FOURCC_data MAKE_FOURCC('d', 'a', 't', 'a') + +#define L_CHANNEL 4 +#define R_CHANNEL 5 +#define ALARM_NUM 0 +#define STREAM_THREAD_PRIO 12 +#define THREAD_STACK_SIZE 1024 +#define STRM_BUF_PAGESIZE 1024*32 +#define STRM_BUF_SIZE STRM_BUF_PAGESIZE*2 + +// WAVフォーマットヘッダ +typedef struct WaveFormat +{ + u16 format; + u16 channels; + s32 sampleRate; + u32 dataRate; + u16 blockAlign; + u16 bitPerSample; +} +WaveFormat; + +// ストリームオブジェクト +typedef struct StreamInfo +{ + FSFile file; + WaveFormat format; + u32 beginPos; + s32 dataSize; + u32 bufPage; + BOOL isPlay; +} +StreamInfo; + +static BOOL ReadWaveFormat(StreamInfo * strm); +static void ReadStrmData(StreamInfo * strm); +static void SoundAlarmHandler(void *arg); +static void StrmThread(void *arg); +static void VBlankIntr(void); +static void PlayStream(StreamInfo * strm, const char *filename); +static void StopStream(StreamInfo * strm); + +static u16 Cont; +static u16 Trg; +static u64 strmThreadStack[THREAD_STACK_SIZE / sizeof(u64)]; +static OSThread strmThread; +static OSMessageQueue msgQ; +static OSMessage msgBuf[1]; + +static u8 strm_lbuf[STRM_BUF_SIZE] ATTRIBUTE_ALIGN(32); +static u8 strm_rbuf[STRM_BUF_SIZE] ATTRIBUTE_ALIGN(32); +static u8 strm_tmp[STRM_BUF_PAGESIZE * 2] ATTRIBUTE_ALIGN(32); + +// ファイル名 +//const char filename2[] = "kart_title.32.wav"; +const char filename1[] = "fanfare.32.wav"; + +static StreamInfo strm; + +void stream_main(void) +{ + strm.isPlay = FALSE; + SND_LockChannel((1 << L_CHANNEL) | (1 << R_CHANNEL), 0); + + /* ストリームスレッドの起動 */ + OS_CreateThread(&strmThread, + StrmThread, + NULL, + strmThreadStack + THREAD_STACK_SIZE / sizeof(u64), + THREAD_STACK_SIZE, STREAM_THREAD_PRIO); + OS_WakeupThreadDirect(&strmThread); +} + +void stream_play1(void) +{ + PlayStream(&strm, filename1); +} + +BOOL stream_is_play1_end(void) +{ + if (strm.dataSize <= 0) { + return TRUE; + } + return FALSE; +} + + + +static void PlayStream(StreamInfo * strm, const char *filename) +{ + int timerValue; + u32 alarmPeriod; + + // 再生中であれば停止する +OS_TPrintf("%s %d\n",__FUNCTION__,__LINE__); + + + if (strm->isPlay) + { + u32 tag; + StopStream(strm); + tag = SND_GetCurrentCommandTag(); + (void)SND_FlushCommand(SND_COMMAND_NOBLOCK | SND_COMMAND_IMMEDIATE); + SND_WaitForCommandProc(tag); // 停止を待つ + } + + // ファイル走査 + if (FS_IsFile(&strm->file)) + (void)FS_CloseFile(&strm->file); + if ( ! FS_OpenFile(&strm->file, filename) ) { + OS_Panic("Error: failed to open file %s\n", filename); + } + if (!ReadWaveFormat(strm)) + { + OS_Panic("Error: failed to read wavefile\n"); + } + + strm->isPlay = TRUE; + + /* パラメータ設定 */ + timerValue = SND_TIMER_CLOCK / strm->format.sampleRate; + alarmPeriod = timerValue * STRM_BUF_PAGESIZE / 32U; + alarmPeriod /= (strm->format.bitPerSample == 16) ? sizeof(s16) : sizeof(s8); + + // 初期ストリームデータ読み込み + (void)FS_SeekFile(&strm->file, (s32)strm->beginPos, FS_SEEK_SET); + strm->bufPage = 0; + ReadStrmData(strm); + ReadStrmData(strm); + + // チャンネルとアラームをセットアップ + SND_SetupChannelPcm(L_CHANNEL, + (strm->format.bitPerSample == + 16) ? SND_WAVE_FORMAT_PCM16 : SND_WAVE_FORMAT_PCM8, strm_lbuf, + SND_CHANNEL_LOOP_REPEAT, 0, STRM_BUF_SIZE / sizeof(u32), 127, + SND_CHANNEL_DATASHIFT_NONE, timerValue, 0); + SND_SetupChannelPcm(R_CHANNEL, + (strm->format.bitPerSample == + 16) ? SND_WAVE_FORMAT_PCM16 : SND_WAVE_FORMAT_PCM8, + (strm->format.channels == 1) ? strm_lbuf : strm_rbuf, + SND_CHANNEL_LOOP_REPEAT, 0, STRM_BUF_SIZE / sizeof(u32), 127, + SND_CHANNEL_DATASHIFT_NONE, timerValue, 127); + SND_SetupAlarm(ALARM_NUM, alarmPeriod, alarmPeriod, SoundAlarmHandler, strm); + SND_StartTimer((1 << L_CHANNEL) | (1 << R_CHANNEL), 0, 1 << ALARM_NUM, 0); +} + +/*---------------------------------------------------------------------------* + Name: StopStream + + Description: ストリーム再生を停止 + + Arguments: strm - ストリームオブジェクト + + Returns: None. + *---------------------------------------------------------------------------*/ +static void StopStream(StreamInfo * strm) +{ + SND_StopTimer((1 << L_CHANNEL) | (1 << R_CHANNEL), 0, 1 << ALARM_NUM, 0); + if (FS_IsFile(&strm->file)) + (void)FS_CloseFile(&strm->file); + strm->isPlay = FALSE; +} + +/*---------------------------------------------------------------------------* + Name: StrmThread + + Description: ストリームスレッド + + Arguments: arg - ユーザーデータ(未使用) + + Returns: None. + *---------------------------------------------------------------------------*/ +static void StrmThread(void * /*arg */ ) +{ + OSMessage message; + + OS_InitMessageQueue(&msgQ, msgBuf, 1); + + while (1) + { + (void)OS_ReceiveMessage(&msgQ, &message, OS_MESSAGE_BLOCK); + (void)ReadStrmData((StreamInfo *) message); + } +} + +/*---------------------------------------------------------------------------* + Name: SoundAlarmHandler + + Description: アラームコールバック関数 + + Arguments: arg - ストリームオブジェクト + + Returns: None. + *---------------------------------------------------------------------------*/ +static void SoundAlarmHandler(void *arg) +{ + (void)OS_SendMessage(&msgQ, (OSMessage)arg, OS_MESSAGE_NOBLOCK); +} + +/*---------------------------------------------------------------------------* + Name: ReadStrmData + + Description: ストリームデータ読み込み関数 + ファイルからバッファの1ページ分のストリームデータを読み込む + + Arguments: strm - ストリームオブジェクト + + Returns: None. + *---------------------------------------------------------------------------*/ +static void ReadStrmData(StreamInfo * strm) +{ + int i; + s32 readSize; + u8 *lbuf, *rbuf; + + // ストリームが終端に達している + if (strm->dataSize <= 0) + { + StopStream(strm); + // OS_TPrintf("Stop stream\n"); + return; + } + + // バッファのページ設定 + if (strm->bufPage == 0) + { + lbuf = strm_lbuf; + rbuf = strm_rbuf; + strm->bufPage = 1; + } + else + { + lbuf = strm_lbuf + STRM_BUF_PAGESIZE; + rbuf = strm_rbuf + STRM_BUF_PAGESIZE; + strm->bufPage = 0; + } + + // データ読み込み + if (strm->format.channels == 1) + { + // モノラル + readSize = FS_ReadFile(&strm->file, + strm_tmp, + (strm->dataSize < + STRM_BUF_PAGESIZE) ? strm->dataSize : STRM_BUF_PAGESIZE); + if (readSize == -1) + OS_Panic("read file end\n"); + + if (strm->format.bitPerSample == 16) + { + // 16bitデータ + for (i = 0; i < readSize / sizeof(s16); i++) + { + ((s16 *)lbuf)[i] = ((s16 *)strm_tmp)[i]; + } + for (; i < STRM_BUF_PAGESIZE / sizeof(s16); i++) + { + ((s16 *)lbuf)[i] = 0; // ストリームの終端に達した場合、残りを0で埋める + } + } + else + { + // 8bitデータ + for (i = 0; i < readSize / sizeof(s8); i++) + { + ((s8 *)lbuf)[i] = (s8)((s16)strm_tmp[i] - 128); + } + for (; i < STRM_BUF_PAGESIZE / sizeof(s8); i++) + { + ((s8 *)lbuf)[i] = 0; + } + } + } + else + { + // ステレオ + readSize = FS_ReadFile(&strm->file, + strm_tmp, + (strm->dataSize < + STRM_BUF_PAGESIZE * 2) ? strm->dataSize : STRM_BUF_PAGESIZE * 2); + if (readSize == -1) + OS_Panic("read file end\n"); + + if (strm->format.bitPerSample == 16) + { + // 16bitデータ + for (i = 0; i < (readSize / 2) / sizeof(s16); i++) + { + ((s16 *)lbuf)[i] = ((s16 *)strm_tmp)[2 * i]; + ((s16 *)rbuf)[i] = ((s16 *)strm_tmp)[2 * i + 1]; + } + for (; i < STRM_BUF_PAGESIZE / sizeof(s16); i++) + { + ((s16 *)lbuf)[i] = 0; + ((s16 *)rbuf)[i] = 0; + } + } + else + { + // 8bitデータ + for (i = 0; i < (readSize / 2) / sizeof(s8); i++) + { + ((s8 *)lbuf)[i] = (s8)((s16)strm_tmp[2 * i] - 128); + ((s8 *)rbuf)[i] = (s8)((s16)strm_tmp[2 * i + 1] - 128); + } + for (; i < STRM_BUF_PAGESIZE / sizeof(s8); i++) + { + ((s8 *)lbuf)[i] = 0; + ((s8 *)rbuf)[i] = 0; + } + } + } + + strm->dataSize -= readSize; + + return; +} + + +/*---------------------------------------------------------------------------* + Name: ReadWaveFormat + + Description: WAVEフォーマットのデータのヘッダとデータ列の先頭位置、データサイズを取得 + + Arguments: strm - ストリームオブジェクト + + Returns: 読み取りに成功したらTRUE、失敗したらFALSE + *---------------------------------------------------------------------------*/ +static BOOL ReadWaveFormat(StreamInfo * strm) +{ + u32 tag; + s32 size; + BOOL fFmt = FALSE, fData = FALSE; + + (void)FS_SeekFileToBegin(&strm->file); + + (void)FS_ReadFile(&strm->file, &tag, 4); + if (tag != FOURCC_RIFF) + return FALSE; + + (void)FS_ReadFile(&strm->file, &size, 4); + + (void)FS_ReadFile(&strm->file, &tag, 4); + if (tag != FOURCC_WAVE) + return FALSE; + + while (size > 0) + { + s32 chunkSize; + if (FS_ReadFile(&strm->file, &tag, 4) == -1) + { + return FALSE; + } + if (FS_ReadFile(&strm->file, &chunkSize, 4) == -1) + { + return FALSE; + } + + switch (tag) + { + case FOURCC_fmt: + if (FS_ReadFile(&strm->file, (u8 *)&strm->format, chunkSize) == -1) + { + return FALSE; + } + fFmt = TRUE; + break; + case FOURCC_data: + strm->beginPos = FS_GetPosition(&strm->file); + strm->dataSize = chunkSize; + (void)FS_SeekFile(&strm->file, chunkSize, FS_SEEK_CUR); + fData = TRUE; + break; + default: + (void)FS_SeekFile(&strm->file, chunkSize, FS_SEEK_CUR); + break; + } + if (fFmt && fData) + { + return TRUE; // fmt と data を読み終えたら強制終了 + } + + size -= chunkSize; + } + + if (size != 0) + return FALSE; + return TRUE; +} diff --git a/build/tools/sctools/common/src/stream.h b/build/tools/sctools/common/src/stream.h new file mode 100644 index 0000000..50a7f86 --- /dev/null +++ b/build/tools/sctools/common/src/stream.h @@ -0,0 +1,18 @@ +#ifndef _STREAM_H_ +#define _STREAM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +extern void channel_play(void); +extern void stream_main(void); +extern void stream_play1(void); +extern BOOL stream_is_play1_end(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _STREAM_H_ */ diff --git a/build/tools/sctools/common/src/string_map_dwc_apinfo_type.inc b/build/tools/sctools/common/src/string_map_dwc_apinfo_type.inc new file mode 100644 index 0000000..f7d2576 --- /dev/null +++ b/build/tools/sctools/common/src/string_map_dwc_apinfo_type.inc @@ -0,0 +1,9 @@ +{ DWC_APINFO_TYPE_USER0, "USER 1" }, +{ DWC_APINFO_TYPE_USER1, "USER 2" }, +{ DWC_APINFO_TYPE_USER2, "USER 3" }, +{ DWC_APINFO_TYPE_USER3, "USER 4" }, +{ DWC_APINFO_TYPE_USER4, "USER 5" }, +{ DWC_APINFO_TYPE_USER5, "USER 6" }, +{ DWC_APINFO_TYPE_USB, "NINTENDO-USBAP" }, +{ DWC_APINFO_TYPE_SHOP, "Wi-Fi STATION" }, +{ DWC_APINFO_TYPE_FREESPOT, "FREESPOT" }, diff --git a/build/tools/sctools/common/src/string_map_ec_error.inc b/build/tools/sctools/common/src/string_map_ec_error.inc new file mode 100644 index 0000000..d90190e --- /dev/null +++ b/build/tools/sctools/common/src/string_map_ec_error.inc @@ -0,0 +1,81 @@ +{ EC_ERROR_AGE_RESTRICTED, "EC_ERROR_AGE_RESTRICTED" }, +{ EC_ERROR_ALREADY, "EC_ERROR_ALREADY" }, +{ EC_ERROR_ALREADY_OWN, "EC_ERROR_ALREADY_OWN" }, +{ EC_ERROR_BUSY, "EC_ERROR_BUSY" }, +{ EC_ERROR_CANCELED, "EC_ERROR_CANCELED" }, +{ EC_ERROR_CANCEL_FAILED, "EC_ERROR_CANCEL_FAILED" }, +{ EC_ERROR_CERT_CHAIN, "EC_ERROR_CERT_CHAIN" }, +{ EC_ERROR_CFG_HOME_INODE, "EC_ERROR_CFG_HOME_INODE" }, +{ EC_ERROR_CFG_HOME_SPACE, "EC_ERROR_CFG_HOME_SPACE" }, +{ EC_ERROR_CFG_USER_INODE, "EC_ERROR_CFG_USER_INODE" }, +{ EC_ERROR_CFG_USER_SPACE, "EC_ERROR_CFG_USER_SPACE" }, +{ EC_ERROR_CONFIG, "EC_ERROR_CONFIG" }, +{ EC_ERROR_CONNECT, "EC_ERROR_CONNECT" }, +{ EC_ERROR_CONTENT_SIZE, "EC_ERROR_CONTENT_SIZE" }, +{ EC_ERROR_DEVICE_KEY_PAIR, "EC_ERROR_DEVICE_KEY_PAIR" }, +{ EC_ERROR_ECARD, "EC_ERROR_ECARD" }, +{ EC_ERROR_EULA, "EC_ERROR_EULA" }, +{ EC_ERROR_FAIL, "EC_ERROR_FAIL" }, +{ EC_ERROR_FILE_READ, "EC_ERROR_FILE_READ" }, +{ EC_ERROR_FILE_WRITE, "EC_ERROR_FILE_WRITE" }, +{ EC_ERROR_FIRMWARE, "EC_ERROR_FIRMWARE" }, +{ EC_ERROR_HTTP_HDR_PARSE, "EC_ERROR_HTTP_HDR_PARSE" }, +{ EC_ERROR_INIT, "EC_ERROR_INIT" }, +{ EC_ERROR_INSUFFICIENT_FUNDS, "EC_ERROR_INSUFFICIENT_FUNDS" }, +{ EC_ERROR_INSUFICIENT_RESOURCE, "EC_ERROR_INSUFICIENT_RESOURCE" }, +{ EC_ERROR_INVALID, "EC_ERROR_INVALID" }, +{ EC_ERROR_INVALID_PCPW, "EC_ERROR_INVALID_PCPW" }, +{ EC_ERROR_NAND, "EC_ERROR_NAND" }, +{ EC_ERROR_NET_CONTENT, "EC_ERROR_NET_CONTENT" }, +{ EC_ERROR_NET_NA, "EC_ERROR_NET_NA" }, +{ EC_ERROR_NHTTP_AHF, "EC_ERROR_NHTTP_AHF" }, +{ EC_ERROR_NHTTP_CRX, "EC_ERROR_NHTTP_CRX" }, +{ EC_ERROR_NHTTP_PDE, "EC_ERROR_NHTTP_PDE" }, +{ EC_ERROR_NHTTP_PDR, "EC_ERROR_NHTTP_PDR" }, +{ EC_ERROR_NHTTP_SCA, "EC_ERROR_NHTTP_SCA" }, +{ EC_ERROR_NHTTP_SCCD, "EC_ERROR_NHTTP_SCCD" }, +{ EC_ERROR_NHTTP_SMO, "EC_ERROR_NHTTP_SMO" }, +{ EC_ERROR_NHTTP_SRA, "EC_ERROR_NHTTP_SRA" }, +{ EC_ERROR_NHTTP_SRCD, "EC_ERROR_NHTTP_SRCD" }, +{ EC_ERROR_NHTTP_SVO, "EC_ERROR_NHTTP_SVO" }, +{ EC_ERROR_NOMEM, "EC_ERROR_NOMEM" }, +{ EC_ERROR_NOT_ACTIVE, "EC_ERROR_NOT_ACTIVE" }, +{ EC_ERROR_NOT_BUSY, "EC_ERROR_NOT_BUSY" }, +{ EC_ERROR_NOT_DONE, "EC_ERROR_NOT_DONE" }, +{ EC_ERROR_NOT_FOUND, "EC_ERROR_NOT_FOUND" }, +{ EC_ERROR_NOT_OWNED, "EC_ERROR_NOT_OWNED" }, +{ EC_ERROR_NOT_SUPPORTED, "EC_ERROR_NOT_SUPPORTED" }, +{ EC_ERROR_NO_DEVICE_CODE, "EC_ERROR_NO_DEVICE_CODE" }, +{ EC_ERROR_NO_DEVICE_KEY_PAIR, "EC_ERROR_NO_DEVICE_KEY_PAIR" }, +{ EC_ERROR_NO_TMD, "EC_ERROR_NO_TMD" }, +{ EC_ERROR_OK, "EC_ERROR_OK" }, +{ EC_ERROR_OVERFLOW, "EC_ERROR_OVERFLOW" }, +{ EC_ERROR_PC_DISABLED, "EC_ERROR_PC_DISABLED" }, +{ EC_ERROR_POINTS_RESTRICTED, "EC_ERROR_POINTS_RESTRICTED" }, +{ EC_ERROR_RANGE_END, "EC_ERROR_RANGE_END" }, +{ EC_ERROR_RANGE_START, "EC_ERROR_RANGE_START" }, +{ EC_ERROR_REGISTER, "EC_ERROR_REGISTER" }, +{ EC_ERROR_SHOP_SETUP, "EC_ERROR_SHOP_SETUP" }, +{ EC_ERROR_SYNC, "EC_ERROR_SYNC" }, +{ EC_ERROR_SYS_BLOCKS, "EC_ERROR_SYS_BLOCKS" }, +{ EC_ERROR_SYS_INODES, "EC_ERROR_SYS_INODES" }, +{ EC_ERROR_TICKET, "EC_ERROR_TICKET" }, +{ EC_ERROR_TITLE, "EC_ERROR_TITLE" }, +{ EC_ERROR_TITLE_CONTENT, "EC_ERROR_TITLE_CONTENT" }, +{ EC_ERROR_USER_BLOCKS, "EC_ERROR_USER_BLOCKS" }, +{ EC_ERROR_USER_INODES, "EC_ERROR_USER_INODES" }, +{ EC_ERROR_WS_RECV, "EC_ERROR_WS_RECV" }, +{ EC_ERROR_WS_REPORT, "EC_ERROR_WS_REPORT" }, +{ EC_ERROR_WS_RESP, "EC_ERROR_WS_RESP" }, +{ EC_ES_ERROR_END, "EC_ES_ERROR_END" }, +{ EC_ES_ERROR_START, "EC_ES_ERROR_START" }, +{ EC_HTTP_STATUS_RANGE_END, "EC_HTTP_STATUS_RANGE_END" }, +{ EC_HTTP_STATUS_RANGE_START, "EC_HTTP_STATUS_RANGE_START" }, +{ EC_ISFS_ERROR_END, "EC_ISFS_ERROR_END" }, +{ EC_ISFS_ERROR_START, "EC_ISFS_ERROR_START" }, +{ EC_NHTTP_APIERR_RANGE_END, "EC_NHTTP_APIERR_RANGE_END" }, +{ EC_NHTTP_APIERR_RANGE_START, "EC_NHTTP_APIERR_RANGE_START" }, +{ EC_NHTTP_ERROR_RANGE_END, "EC_NHTTP_ERROR_RANGE_END" }, +{ EC_NHTTP_ERROR_RANGE_START, "EC_NHTTP_ERROR_RANGE_START" }, +{ EC_SSL_ERROR_RANGE_END, "EC_SSL_ERROR_RANGE_END" }, +{ EC_SSL_ERROR_RANGE_START, "EC_SSL_ERROR_RANGE_START" }, diff --git a/build/tools/sctools/common/src/string_map_ec_op.inc b/build/tools/sctools/common/src/string_map_ec_op.inc new file mode 100644 index 0000000..2142f2d --- /dev/null +++ b/build/tools/sctools/common/src/string_map_ec_op.inc @@ -0,0 +1,31 @@ +{ EC_OP_Invalid, "EC_OP_Invalid" }, +{ EC_OP_PurchaseTitle, "EC_OP_PurchaseTitle" }, +{ EC_OP_Unused1, "EC_OP_Unused1" }, +{ EC_OP_SyncTickets, "EC_OP_SyncTickets" }, +{ EC_OP_RefreshCachedBalance, "EC_OP_RefreshCachedBalance" }, +{ EC_OP_PurchasePoints, "EC_OP_PurchasePoints" }, +{ EC_OP_GetTitle, "EC_OP_GetTitle" }, +{ EC_OP_GenerateKeyPair, "EC_OP_GenerateKeyPair" }, +{ EC_OP_ConfirmKeyPair, "EC_OP_ConfirmKeyPair" }, +{ EC_OP_CheckRegistration, "EC_OP_CheckRegistration" }, +{ EC_OP_Register, "EC_OP_Register" }, +{ EC_OP_Unregister, "EC_OP_Unregister" }, +{ EC_OP_Transfer, "EC_OP_Transfer" }, +{ EC_OP_DeleteOwnership, "EC_OP_DeleteOwnership" }, +{ EC_OP_PurchaseGiftTitle, "EC_OP_PurchaseGiftTitle" }, +{ EC_OP_AcceptGiftTitle, "EC_OP_AcceptGiftTitle" }, +{ EC_OP_CheckDeviceStatus, "EC_OP_CheckDeviceStatus" }, +{ EC_OP_SyncRegistration, "EC_OP_SyncRegistration" }, +{ EC_OP_Connect, "EC_OP_Connect" }, +{ EC_OP_DownloadContents, "EC_OP_DownloadContents" }, +{ EC_OP_Subscribe, "EC_OP_Subscribe" }, +{ EC_OP_GetChallenge, "EC_OP_GetChallenge" }, +{ EC_OP_UpdateStatus, "EC_OP_UpdateStatus" }, +{ EC_OP_ListTitles, "EC_OP_ListTitles" }, +{ EC_OP_ListContentSets, "EC_OP_ListContentSets" }, +{ EC_OP_ListSubscriptionPricings, "EC_OP_ListSubscriptionPricings" }, +{ EC_OP_ListPointsPricings, "EC_OP_ListPointsPricings" }, +{ EC_OP_ListECardItems, "EC_OP_ListECardItems" }, +{ EC_OP_ListTitleContents, "EC_OP_ListTitleContents" }, +{ EC_OP_GetTitleResourceRequirement, "EC_OP_GetTitleResourceRequirement" }, +{ EC_OP_GetContentsResourceRequirement, "EC_OP_GetContentsResourceRequirement" }, diff --git a/build/tools/sctools/common/src/string_map_ec_phase.inc b/build/tools/sctools/common/src/string_map_ec_phase.inc new file mode 100644 index 0000000..14309f0 --- /dev/null +++ b/build/tools/sctools/common/src/string_map_ec_phase.inc @@ -0,0 +1,32 @@ +{ EC_PHASE_NoPhase, "EC_PHASE_NoPhase" }, +{ EC_PHASE_Starting, "EC_PHASE_Starting" }, +{ EC_PHASE_Done, "EC_PHASE_Done" }, +{ EC_PHASE_PurchasingTitle, "EC_PHASE_PurchasingTitle" }, +{ EC_PHASE_Unused1, "EC_PHASE_Unused1" }, +{ EC_PHASE_DownloadingContent, "EC_PHASE_DownloadingContent" }, +{ EC_PHASE_GettingTicketsFromServer, "EC_PHASE_GettingTicketsFromServer" }, +{ EC_PHASE_GettingPointsBalance, "EC_PHASE_GettingPointsBalance" }, +{ EC_PHASE_PurchasingPoints, "EC_PHASE_PurchasingPoints" }, +{ EC_PHASE_GeneratingKeyPair, "EC_PHASE_GeneratingKeyPair" }, +{ EC_PHASE_ConfirmingKeyPair, "EC_PHASE_ConfirmingKeyPair" }, +{ EC_PHASE_CheckingRegistrationStatus, "EC_PHASE_CheckingRegistrationStatus" }, +{ EC_PHASE_Registering, "EC_PHASE_Registering" }, +{ EC_PHASE_Unregistering, "EC_PHASE_Unregistering" }, +{ EC_PHASE_Transfer, "EC_PHASE_Transfer" }, +{ EC_PHASE_DeletingOwnership, "EC_PHASE_DeletingOwnership" }, +{ EC_PHASE_AcceptingGiftTitle, "EC_PHASE_AcceptingGiftTitle" }, +{ EC_PHASE_GettingDeviceStatus, "EC_PHASE_GettingDeviceStatus" }, +{ EC_PHASE_SyncingRegistration, "EC_PHASE_SyncingRegistration" }, +{ EC_PHASE_Connecting, "EC_PHASE_Connecting" }, +{ EC_PHASE_UpdatingStatus, "EC_PHASE_UpdatingStatus" }, +{ EC_PHASE_ListingTitles, "EC_PHASE_ListingTitles" }, +{ EC_PHASE_ListingContentSets, "EC_PHASE_ListingContentSets" }, +{ EC_PHASE_ListingSubscriptionPricings, "EC_PHASE_ListingSubscriptionPricings" }, +{ EC_PHASE_ListingPointsPricings, "EC_PHASE_ListingPointsPricings" }, +{ EC_PHASE_ListingECardItems, "EC_PHASE_ListingECardItems" }, +{ EC_PHASE_GettingECardBalance, "EC_PHASE_GettingECardBalance" }, +{ EC_PHASE_Subscribing, "EC_PHASE_Subscribing" }, +{ EC_PHASE_GettingChallenge, "EC_PHASE_GettingChallenge" }, +{ EC_PHASE_ListingTitleContents, "EC_PHASE_ListingTitleContents" }, +{ EC_PHASE_GettingTitleResourceRequirement, "EC_PHASE_GettingTitleResourceRequirement" }, +{ EC_PHASE_GettingContentsResourceRequirement, "EC_PHASE_GettingContentsResourceRequirement" }, diff --git a/build/tools/sctools/common/src/text.c b/build/tools/sctools/common/src/text.c new file mode 100644 index 0000000..0aeee17 --- /dev/null +++ b/build/tools/sctools/common/src/text.c @@ -0,0 +1,412 @@ + +#include +#include "text.h" + +#define TAB_SIZE 8 +/*****************************************************/ + +static LINE_BUF *lb_free_ptr; +static int x_size = X_LINE_MAX; +static int y_size = Y_LINE_MAX; + +void text_display_newline_on(TEXT_CTRL *tc) +{ + tc->text_buf.display_newline = 1; +} + +void text_display_newline_off(TEXT_CTRL *tc) +{ + tc->text_buf.display_newline = 0; +} + + +static void link_line_buf(LINE_BUF *m,LINE_BUF *s) +{ + if( m->next != NULL ) { + s->next = m->next; + (s->next)->prev = s; + } + else + s->next = NULL; + + m->next = s; + s->prev = m; +} + +static void unlink_line_buf(LINE_BUF *s) +{ + if( (s->prev != NULL ) && (s->next != NULL) ) { + (s->prev)->next = s->next; + (s->next)->prev = s->prev; + s->next = NULL; + s->prev = NULL; + } + else if( s->next != NULL) { + s->prev = NULL; + (s->next)->prev = NULL; + s->next = NULL; + } + else if( s->prev != NULL ) { + s->next = NULL; + (s->prev)->next = NULL; + s->prev = NULL; + } +} + +static LINE_BUF *alloc_line_buf(void) +{ + LINE_BUF *tmp; + if( lb_free_ptr == NULL ) { + OS_TPrintf("Error: %s %d\n",__FUNCTION__,__LINE__); + return NULL; + } + if( lb_free_ptr->next == NULL ) { + OS_TPrintf("Error: %s %d\n",__FUNCTION__,__LINE__); + return NULL; + } + tmp = lb_free_ptr; + lb_free_ptr = lb_free_ptr->next; + unlink_line_buf(tmp); + tmp->last_count = 0; + tmp->cur_count = 0; +#if 0 + for(i = 0 ; i < LINE_BUF_X_SIZE ; i++) + tmp->buf[i] = 0x20; +#endif + return(tmp); +} + +static void free_line_buf(LINE_BUF *lb) +{ + lb->next = lb_free_ptr; + lb->prev = NULL; + (lb->next)->prev = lb; + lb_free_ptr = lb; +} + +static int init_lb_heap(void *heap_start, void *heap_end) +{ + int i; + LINE_BUF *lb_heap=(LINE_BUF *)heap_start; + lb_free_ptr = (LINE_BUF *)heap_start; + lb_heap[0].prev = NULL; + + i = 0; + while( (u32)&(lb_heap[i+1]) < (u32)heap_end ) { + link_line_buf(&(lb_heap[i]), &(lb_heap[i+1])); + i++; + } + lb_heap[i].next = NULL; + return i; /* num of line buffer */ +#if 0 + OS_Printf("text_heap s %08x\n",(u32)heap_start); + OS_Printf("text_heap e %08x\n",(u32)heap_end); +#endif + +} + +static void init_text_buf(TEXT_BUF *tb) +{ + tb->num_y = &y_size; + tb->num_x = &x_size; + tb->virtual_x = LINE_BUF_X_SIZE - X_LINE_MAX - 1; + tb->virtual_y = Y_LINE_MAX * 4; + tb->start = tb->cur = alloc_line_buf(); + tb->display_offset_y = 0; + tb->display_offset_x = 0; +} + + +void init_text(TEXT_CTRL *tc, u16 *screen, u16 palette) +{ + tc->screen = screen; + tc->x_line = 0; + tc->y_line = 0; + tc->palette = palette; + init_text_buf(&(tc->text_buf)); +} + + + /* + 0 -> black + 1 -> red + 2 -> green + 3 -> blue + 4 -> yellow + 5 -> purple + 6 -> sky blue + 7 -> red + 8 -> green + 9 -> blue + 0xA -> yellow + 0xB -> purple + 0xC -> sky blue + 0xD -> white + 0xE -> white + 0xF -> white + + */ + +void m_set_palette(TEXT_CTRL *tc, u16 num) +{ + tc->palette = (u16)(0x0f & num); +} + +u16 m_get_palette(TEXT_CTRL *tc) +{ + return tc->palette; +} + +void m_set_display_offset_x(TEXT_CTRL *tc, int offset) +{ + TEXT_BUF *tb = &(tc->text_buf); + if( ( offset >= 0 ) && (offset < tb->virtual_x) ) { + tb->display_offset_x = offset; + } +} + +int m_get_display_offset_x(TEXT_CTRL *tc) +{ + TEXT_BUF *tb = &(tc->text_buf); + return tb->display_offset_x; +} + +void m_set_display_offset_y(TEXT_CTRL *tc, int offset) +{ + TEXT_BUF *tb = &(tc->text_buf); + if( ( offset >= 0 ) && (offset < tb->virtual_y) ) { + tb->display_offset_y = offset; + } +} + +int m_get_display_offset_y(TEXT_CTRL *tc) +{ + TEXT_BUF *tb = &(tc->text_buf); + return tb->display_offset_y; +} + +static void clear_line(TEXT_CTRL *tc) +{ + int i; + i = tc->x_line; + + for( ; i < X_LINE_MAX ; i++) + tc->screen[(tc->y_line * X_LINE_MAX ) + i] = 0; + +} + +static void add_y_line(TEXT_CTRL *tc,u16 num) +{ + tc->y_line += num; + if( tc->y_line > Y_LINE_MAX ) { + tc->y_line = 0; + } +} + +static void add_x_line(TEXT_CTRL *tc,u16 num) +{ + tc->x_line += num; + + if( tc->x_line > X_LINE_MAX ) { + tc->x_line = 0; + tc->y_line++; + if( tc->y_line > Y_LINE_MAX ) { + tc->y_line = 0; + } + } +} + +static void put_char_vram(TEXT_CTRL *tc, int c, u16 col) +{ + + switch( c ) { + case '\0': + break; + case '\n': + clear_line(tc); + tc->x_line = 0; + add_y_line(tc, 1 ); + break; + case '\r': + tc->x_line = 0; + break; + case '\t': + add_x_line(tc, TAB_SIZE ); + break; + case '\f': + tc->x_line = 0; + tc->y_line = 0; + clear_line(tc); + break; + case '\b': /* Back Space */ + case '\v': /* Vertical Tab */ + break; + default: + if( ( c < 0x20 ) || ( 0x7f < c ) ) + c = 0x20; /* white space */ + tc->screen[(tc->y_line * X_LINE_MAX ) + tc->x_line] = (u16)((col << 12) | (0x00ff & c)); + add_x_line(tc, 1 ); + } +} + +/**********************/ + +void m_putchar(TEXT_CTRL *tc, int c) +{ + int i; + LINE_BUF *tmp; + TEXT_BUF *tb = &(tc->text_buf); + + switch( c ) { + case '\0': + break; + case '\n': + if( tb->cur->next == NULL ) { + tmp = tb->cur; /* 表示開始バッファを再計算 */ + for(i = 0 ; (i < *(tb->num_y)-1) && (i < LINE_BUF_Y_SIZE) ; i++) { + if( tmp->prev == NULL ) + break; + tmp = tmp->prev; + } + tb->start = tmp; /* 表示開始バッファ */ + + /* いらないバッファを解放、ただし保存用バーチャル分は残す */ + i = 0; + while(tmp->prev != NULL) { + tmp = tmp->prev; + i++; + if( i > tb->virtual_y-1 ) { + unlink_line_buf( tmp ); + free_line_buf( tmp ); + } + } + + tmp = alloc_line_buf(); /* バッファを1つ取得 */ + if( tmp == NULL ) { + /* ラインバッファがない */ + OS_TPrintf("Free line buffer shortage!!\n"); + return; + } + link_line_buf(tb->cur, tmp); /* 取得したバッファをカレント + バッファの次にリンク */ + tb->cur = tmp; /* カレントバッファを更新 */ + tb->cur->cur_count = 0; + tb->cur->last_count = 0; + } + else { + tb->cur = tb->cur->next; + tb->cur->cur_count = 0; + tb->cur->last_count = 0; + } + break; + case '\r': + tb->cur->cur_count = 0; + break; + case '\t': + tb->cur->cur_count += TAB_SIZE; + if( tb->cur->cur_count > LINE_BUF_X_SIZE ) + tb->cur->cur_count = LINE_BUF_X_SIZE; + if( tb->cur->cur_count > tb->cur->last_count ) + tb->cur->last_count = tb->cur->cur_count; + break; + case '\f': + tb->cur = tb->start; + tb->cur->cur_count = 0; + tb->cur->last_count = 0; + break; + case '\a': /* BELL */ + break; + case '\b': /* Back Space */ + if( tb->cur->cur_count > 0 ) + tb->cur->cur_count--; + break; + case '\v': /* Vertical Tab */ + break; + default: + if( tb->cur->cur_count >= LINE_BUF_X_SIZE ) { + /* add 2008.08.14 */ + /* ラインバッファをオーバーした時の挙動 */ + if( tb->display_newline == 0 ) { + /* 表示しない */ + break; + } + else { + /* 改行して表示する */ + m_putchar(tc, '\n'); + } + } + tb->cur->buf[tb->cur->cur_count] = c; + tb->cur->col[tb->cur->cur_count] = tc->palette; + tb->cur->cur_count++; + if( tb->cur->cur_count > LINE_BUF_X_SIZE ) { + tb->cur->cur_count = LINE_BUF_X_SIZE; + break; + } + if( tb->cur->cur_count > tb->cur->last_count ) + tb->cur->last_count = tb->cur->cur_count; + break; + } +} + + +int init_text_buf_sys(void *heap_start, void *heap_end) +{ + return init_lb_heap(heap_start, heap_end); +} + + +void text_buf_to_vram(TEXT_CTRL *tc) +{ + int x_line,line_no; + int c; + u16 col; + int i; + LINE_BUF *lb; + TEXT_BUF *tb = &(tc->text_buf); + + lb = tb->start; + i = 0; + while( i < tb->display_offset_y ) { + if( lb->prev == NULL ) { + break; + } + lb = lb->prev; + i++; + } + + + put_char_vram(tc, '\f', (u16)0); + + for(line_no = 0 ; line_no < (*tb->num_y + 1) ; line_no++ ){ + //miya x_line = 0; + x_line = tb->display_offset_x; + + if( lb == NULL) + break; + while( x_line < lb->last_count ) { + //miya 2008.09.10 + // if( x_line >= (*tb->num_x + 1)) { + if( x_line >= (*tb->num_x + 1 + tb->display_offset_x)) { + break; + } + else { + c = lb->buf[x_line]; + col = lb->col[x_line]; + put_char_vram(tc, c, col); + } + x_line++; + } + //miya 2008.09.10 + // if( x_line <= *tb->num_x ) { + if( x_line <= *tb->num_x + tb->display_offset_x ) { + put_char_vram(tc, '\n', (u16)0); + } + lb = lb->next; + } + + for( ; line_no <= *tb->num_y ; line_no++ ) { + put_char_vram(tc, '\n', (u16)0); + } +} + + diff --git a/build/tools/sctools/common/src/text.h b/build/tools/sctools/common/src/text.h new file mode 100644 index 0000000..17a5bb0 --- /dev/null +++ b/build/tools/sctools/common/src/text.h @@ -0,0 +1,74 @@ +#ifndef _TEXT_H_ +#define _TEXT_H_ + +#define X_LINE_MAX (32) +#define Y_LINE_MAX (24) + +#define LINE_BUF_X_SIZE 256 +#define LINE_BUF_Y_SIZE 24 + +#define DEFAULT_TEXT_PALETTE_NO 0x0f + +typedef struct LINE_BUF_s { + int buf[LINE_BUF_X_SIZE]; + u16 col[LINE_BUF_X_SIZE]; + int last_count; + int cur_count; + struct LINE_BUF_s *prev; + struct LINE_BUF_s *next; +} LINE_BUF; + +typedef struct { + int *num_x,*num_y; + LINE_BUF *start; + LINE_BUF *cur; + int virtual_x; + int virtual_y; + int display_offset_x; + int display_offset_y; + int display_newline; +} TEXT_BUF; + + + + +// #define TEXT_BUFFER_SIZE 128 +// typedef char (MY_ARRAY)[TEXT_BUFFER_SIZE] ; +// MY_ARRAY buffer; + +typedef struct { + TEXT_BUF text_buf; + u16 *screen; + u16 x_line; + u16 y_line; + u16 palette; + u16 padding; +} TEXT_CTRL; + + + +#ifdef __cplusplus +extern "C" { +#endif + +int init_text_buf_sys(void *heap_start, void *heap_end); + + +void m_putchar(TEXT_CTRL *tc, int c); +void m_set_palette(TEXT_CTRL *tc, u16 num); +u16 m_get_palette(TEXT_CTRL *tc); +void init_text(TEXT_CTRL *tc, u16 *screen, u16 palette); +void m_set_display_offset_y(TEXT_CTRL *tc, int offset); +int m_get_display_offset_y(TEXT_CTRL *tc); +void m_set_display_offset_x(TEXT_CTRL *tc, int offset); +int m_get_display_offset_x(TEXT_CTRL *tc); + +void text_buf_to_vram(TEXT_CTRL *tc); +void text_display_newline_off(TEXT_CTRL *tc); +void text_display_newline_on(TEXT_CTRL *tc); + +#ifdef __cplusplus +} +#endif + +#endif /* _TEXT_H_ */ diff --git a/build/tools/sctools/common/src/wcm_control.c b/build/tools/sctools/common/src/wcm_control.c new file mode 100644 index 0000000..f638951 --- /dev/null +++ b/build/tools/sctools/common/src/wcm_control.c @@ -0,0 +1,917 @@ +/*---------------------------------------------------------------------------* + Project: TwlWiFi - demos - netconnect + File: wcm_control.c + + Copyright 2005,2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: 2008-08-19#$ + $Rev: 931 $ + $Author: adachi_hiroaki $ + *---------------------------------------------------------------------------*/ + +#include +#include +#include "wcm_control.h" +#include "ap_info.h" +#include "sitedefs.h" +#include "netconnect.h" + +/*---------------------------------------------------------------------------* + 定数 定義 + *---------------------------------------------------------------------------*/ + +#define LOCAL_WCM_THREAD_STACK_SIZE 4096 // ( 変更可 ) TWLではDSより大きなスタックを確保する +#define LOCAL_WCM_THREAD_PRIORITY 12 // ( 変更可 ) 低めの優先度で十分 +#define LOCAL_WCM_DMA_NO 3 // ( 変更可 ) 無線機能に使用する DMA 番号 +#define LOCAL_WCM_APLIST_COUNT_MAX 22 // WCM ライブラリ内で管理させる AP 情報リストの数 +#define LOCAL_WCM_APLIST_BUF_SIZE ((LOCAL_WCM_APLIST_COUNT_MAX * WCM_APLIST_BLOCK_SIZE) + 12) +#define LOCAL_WCM_THREAD_MSGQ_CAPACITY 3 // メッセージキューの段数 +#define LOCAL_WCM_MSG_TERMINATE ((void*)0) +#define LOCAL_WCM_MSG_FOUND_AP ((void*)1) +#define LOCAL_WCM_MSG_DISCONNECTED ((void*)2) +#define LOCAL_WCM_MSG_ERROR ((void*)3) +#define LOCAL_WCM_MSG_CONNECT_AP ((void*)4) +#define LOCAL_WCM_MSG_DISCONNECT ((void*)5) + +// WCM ライブラリ操作ログ表示用 文字列定数 +static const char* gWcmResultText[8] = +{ + "SUCCESS" , "FAILURE" , + "PROGRESS" , "ACCEPT" , + "REJECT" , "WMDISABLE" , + "NOT_ENOUGH_MEM" , "FATAL_ERROR" +}; +static const char* gWcmNotifyText[10] = +{ + "COMMON" , "STARTUP" , + "CLEANUP" , "BEGIN_SEARCH" , + "END_SEARCH" , "CONNECT" , + "DISCONNECT" , "FOUND_AP" , + "SEARCH_AROUND" , "TERMINATE" +}; + +// デバッグ文字列表示設定 +#define LOCAL_DEBUG_PRINT 1 +#if LOCAL_DEBUG_PRINT +#define DebugPrintf OS_TPrintf +#else +#define DebugPrintf(...) ((void)0) +#endif + +/*---------------------------------------------------------------------------* + 内部変数 定義 + *---------------------------------------------------------------------------*/ + +// スレッド制御用 +static OSThread gWcmThread; +static u64 gWcmStack[LOCAL_WCM_THREAD_STACK_SIZE / sizeof( u64 ) ]; +static OSMessageQueue gWcmMessageQ; +static OSMessage gWcmMsgArray[LOCAL_WCM_THREAD_MSGQ_CAPACITY]; +static OSThreadQueue gWcmThreadQ; + +// WCM ライブラリ制御用 +static volatile BOOL gWcmTerminateFlag; +static vs32 gWcmResult; +static void* gWcmBuf = NULL; +static WCMConfig gWcmConfig; +static WCMBssDesc gWcmBssDesc ATTRIBUTE_ALIGN(4); +static u32 gWcmScanType = WCM_OPTION_SCANTYPE_ACTIVE; +static u32 gWcmPowerMode = WCM_OPTION_POWER_ACTIVE; + +static BOOL gWcmManualConnect = FALSE; +static u32 gWcmSearchAroundCount = 0; + +static const WcmControlApInfo g_ApInfoAny = +{ + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, + { 0, 0, {0} }, + { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + } +}; + +static const WcmControlApInfo* constApInfoPtr = NULL; +//static u8 g_deviceId = WCM_DEVICEID_DEFAULT; +static u8 g_deviceId = WCM_DEVICEID_NWM; +/*---------------------------------------------------------------------------* + 内部関数 プロトタイプ + *---------------------------------------------------------------------------*/ +static void ControlThread(void* arg); +static void FreezeThread(void); +static s32 CallFunction(s16 notifyId); +static void ControlCallback(WCMNotice* arg); + +/*---------------------------------------------------------------------------* + Name: SetWcmScanType + + Description: AP を探索する際のスキャン方式を設定する。 + デフォルトではアクティブスキャン方式となっている。 + InitWcmControl() より先に呼び出す必要がある。 + + Arguments: type - WCM_OPTION_SCANTYPE_* の定数を指定する。 + + Returns: None. + *---------------------------------------------------------------------------*/ +void SetWcmScanType(u32 type) +{ + switch (type) + { + case WCM_OPTION_SCANTYPE_PASSIVE: + gWcmScanType = WCM_OPTION_SCANTYPE_PASSIVE; + break; + + default: + gWcmScanType = WCM_OPTION_SCANTYPE_ACTIVE; + } +} + +/*---------------------------------------------------------------------------* + Name: SetWcmPowerMode + + Description: AP と通信する際の電力管理方式を指定する。 + デフォルトでは常時アクティブ方式となっている。 + InitWcmControl() より先に呼び出す必要がある。 + + Arguments: mode - WCM_OPTION_POWER_* の定数を指定する。 + + Returns: None. + *---------------------------------------------------------------------------*/ +void SetWcmPowerMode(u32 mode) +{ + switch (mode) + { + case WCM_OPTION_POWER_SAVE: + gWcmPowerMode = WCM_OPTION_POWER_SAVE; + break; + + default: + gWcmPowerMode = WCM_OPTION_POWER_ACTIVE; + } +} + +/*---------------------------------------------------------------------------* + Name: SetWcmManualConnectMode + + Description: APのリストから手動で選択して接続するよう設定. + デフォルトでは FALSE (自動接続)となっている. + InitWcmControl() より先に呼び出す必要がある. + + Arguments: enable - 手動で接続する場合は TRUE。 + + Returns: None. + *---------------------------------------------------------------------------*/ +void SetWcmManualConnect(BOOL enable) +{ + gWcmManualConnect = enable; +} + +/*---------------------------------------------------------------------------* + Name: ConnectAP + + Description: 指定されたBssDescのAPへ接続する + + Arguments: bd - 接続するAPを示す構造体へのポインタ。 + + Returns: None. + *---------------------------------------------------------------------------*/ +void ConnectAP(const WCMBssDesc* pbd) +{ + SDK_NULL_ASSERT(pbd); + MI_CpuCopy8(pbd, &gWcmBssDesc, /*sizeof(WCMBssDesc)*/ WCM_APLIST_SIZE); + (void)OS_SendMessage(&gWcmMessageQ, LOCAL_WCM_MSG_CONNECT_AP, OS_MESSAGE_NOBLOCK); +} + +/*---------------------------------------------------------------------------* + Name: DisconnectAP + + Description: AP との接続の切断を試みる + + Arguments: None. + + Returns: None. + *---------------------------------------------------------------------------*/ +void DisconnectAP(void) +{ + OSIntrMode e = OS_DisableInterrupts(); + if (WCM_GetPhase() == WCM_PHASE_DCF) + { + (void)OS_SendMessage(&gWcmMessageQ, LOCAL_WCM_MSG_DISCONNECT, OS_MESSAGE_NOBLOCK); + } + (void)OS_RestoreInterrupts(e); +} + +/*---------------------------------------------------------------------------* + Name: InitWcmControl/InitWcmControlByApInfo + + Description: 内部変数を初期化し、WCM コントロールスレッドを起動する。 + 既にスレッドが起動している場合は何も行わない。 + + Arguments: None. + + Returns: None. + *---------------------------------------------------------------------------*/ +void InitWcmControl(void) +{ + // InitWcmControlEx(WCM_DEVICEID_DEFAULT); + InitWcmControlEx(WCM_DEVICEID_NWM); +} + +void InitWcmControlEx(u8 deviceId) +{ + static WcmControlApInfo apInfo; + InitWcmApInfo(&apInfo, SITEDEFS_DEFAULTCLASS); + InitWcmControlByApInfoEx(&apInfo, deviceId); +} + +void InitWcmControlByApInfo(const WcmControlApInfo* ptr) +{ + InitWcmControlByApInfoEx(ptr, WCM_DEVICEID_DEFAULT); +} + +void InitWcmControlByApInfoEx(const WcmControlApInfo* ptr, u8 deviceId) +{ + // スレッド変数が一回でも初期化されたか? + if (gWcmThread.id != 0) + { + // スレッドは終了状態にあるか? + if (OS_IsThreadTerminated(&gWcmThread) == FALSE) + { + return; + } + } + + if (ptr) + { + constApInfoPtr = ptr; + } + else + { + constApInfoPtr = &g_ApInfoAny; + } + g_deviceId = deviceId; + + // メッセージキューを初期化 + OS_InitMessageQueue(&gWcmMessageQ, gWcmMsgArray, LOCAL_WCM_THREAD_MSGQ_CAPACITY); + + // スレッドキューを初期化 + OS_InitThreadQueue(&gWcmThreadQ); + + // 変数初期化 + MI_CpuClearFast(&gWcmBssDesc, sizeof(WCMBssDesc)); + + // スレッド変数を使って新規にスレッドを作成 + OS_CreateThread(&gWcmThread, ControlThread, NULL, (void*)(gWcmStack + (LOCAL_WCM_THREAD_STACK_SIZE / sizeof(u64))), + LOCAL_WCM_THREAD_STACK_SIZE, LOCAL_WCM_THREAD_PRIORITY); + + // 作成したスレッドを起動 + gWcmTerminateFlag = FALSE; + + OS_WakeupThreadDirect(&gWcmThread); + +} + +/*---------------------------------------------------------------------------* + Name: TerminateWcmControl + + Description: WCM コントロールスレッドを終了する。 + スレッドが起動されていない場合は何も行わない。 + NOTICE: 当関数はスレッドを終了する為のトリガとなるだけなので、スレッド + 自体が終了するまで待たずに戻る点に注意。 + スレッド自体の終了を待ちたい場合は、OS_SendMessage 呼び出しに + 続いて OS_JoinThread を呼び出す必要がある。 + なお、WCM_GetPhase 関数が WCM_PHASE_NULL を返すようになれば + スレッドは終了している。 + + Arguments: None. + + Returns: None. + *---------------------------------------------------------------------------*/ +void TerminateWcmControl(void) +{ + // スレッド変数が一回でも初期化されたか? + if (gWcmThread.id != 0) + { + // スレッドは終了状態にあるか? + if (OS_IsThreadTerminated(&gWcmThread) == FALSE) + { + // 既に終了要求を受け付けている状態か? + if (gWcmTerminateFlag == FALSE) + { + gWcmTerminateFlag = TRUE; + (void)OS_SendMessage(&gWcmMessageQ, LOCAL_WCM_MSG_TERMINATE, OS_MESSAGE_NOBLOCK); + } + } + } +} + +/*---------------------------------------------------------------------------* + Name: SetWcmThreadPriority + + Description: WCM ライブラリの状態管理を行うスレッドの優先度を変更する。 + + Arguments: priority - 0 〜 31 のスレッド優先度を指定する。 + + Returns: u8 - 変更する前に設定されていた優先度を返す。 + なんらかの理由で優先度の変更に失敗した場合は 0xff を返す。 + *---------------------------------------------------------------------------*/ +u8 SetWcmThreadPriority(u8 priority) +{ + u8 beforePrio = 0xff; + + if (priority < 32) + { + if ((gWcmThread.id != 0) && (OS_IsThreadTerminated(&gWcmThread) == FALSE)) + { + beforePrio = (u8) OS_GetThreadPriority(&gWcmThread); + if (FALSE == OS_SetThreadPriority(&gWcmThread, (u32) priority)) + { + beforePrio = 0xff; + } + } + } + + return beforePrio; +} + +/*---------------------------------------------------------------------------* + Name: GetWcmSearchAroundCount + + Description: 全チャンネルを何週スキャンしたかを返す。 + StartUp直後は0になる。 + + Arguments: None. + + Returns: 何回スキャンしたか。 + *---------------------------------------------------------------------------*/ +u32 GetWcmSearchAroundCount(void) +{ + return gWcmSearchAroundCount; +} + +/*---------------------------------------------------------------------------* + Name: ControlThread + + Description: WCM ライブラリの状態管理を行うスレッド。 + gWcmTerminateFlag 変数が FALSE の間は指定 AP に接続しようとし、 + また接続完了後はその接続を維持しようとする。 + TerminateWcmControl 関数が呼ばれると gWcmTerminateFlag 変数が + TRUE に変化し、その後は WCM ライブラリを終了して自身のスレッド + も終了しようとする。 + なお、WCM ライブラリ内部で FATAL ERROR が発生した場合には、 + 当スレッドは内部で無限ループに入る。 + + Arguments: arg - 使用しない。 + + Returns: None. + *---------------------------------------------------------------------------*/ +static void ControlThread(void* arg) +{ +#pragma unused(arg) + + void* wcmBuf; + void* wcmAplistBuf; + s32 wcmResult; + OSMessage tempMessage; + s32 workSize = WCM_WORK_SIZE; + + WCM_GetWorkSize(&workSize, g_deviceId); + + // WCM ライブラリが使用するメモリを確保 + wcmBuf = NcAlloc(0, workSize + LOCAL_WCM_APLIST_BUF_SIZE); + + if (wcmBuf == NULL) + { + /* メモリが確保できなかった場合はその後の操作は不可能なので終了 */ + return; + } + + wcmAplistBuf = (void*)((u32) wcmBuf + WCM_WORK_SIZE); + + // WCM ライブラリを初期化 + wcmResult = WCM_InitEx(wcmBuf, workSize, g_deviceId); + DebugPrintf("WCM_InitEx(deviceId = %d) -> %s\n", g_deviceId, gWcmResultText[wcmResult]); + if (wcmResult != WCM_RESULT_SUCCESS) + { + /* + * ここでエラーが返される場合は、ライブラリの使い方が間違っている可能性が高い。 + * 他のスレッドから WCM ライブラリが既に初期化されていることが考えられる。 + */ + NcFree(0, wcmBuf, workSize + LOCAL_WCM_APLIST_BUF_SIZE); + return; + } + + // WCM ライブラリの動作設定 + gWcmConfig.dmano = LOCAL_WCM_DMA_NO; + gWcmConfig.pbdbuffer = wcmAplistBuf; + gWcmConfig.nbdbuffer = LOCAL_WCM_APLIST_BUF_SIZE; + gWcmConfig.nbdmode = WCM_APLIST_MODE_IGNORE; + + // 接続開始 + while (gWcmTerminateFlag == FALSE) + { + switch (WCM_GetPhase()) + { + case WCM_PHASE_WAIT: + // 無線機能の起動 + wcmResult = CallFunction(WCM_NOTIFY_STARTUP); + break; + + case WCM_PHASE_IDLE: + if (gWcmBssDesc.length == 0) + { + // AP の自動探索を開始 + WCM_ClearApList(); + wcmResult = CallFunction(WCM_NOTIFY_BEGIN_SEARCH); + } + else + { + // AP に接続 + wcmResult = CallFunction(WCM_NOTIFY_CONNECT); + if (wcmResult == WCM_RESULT_SUCCESS) + { + // 次に接続しようとする際にサーチからやり直す為に、ここでクリア + MI_CpuClearFast(&gWcmBssDesc, sizeof(WCMBssDesc)); + } + } + break; + + case WCM_PHASE_SEARCH: + // AP が発見されるまで待つ + (void)OS_ReceiveMessage(&gWcmMessageQ, &tempMessage, OS_MESSAGE_BLOCK); + + /* + * メッセージとしては、WCM の停止要求、AP 発見通知、及びエラー通知の3種類を + * 受け取る可能性がある。AP 発見通知の場合は、このサンプルでは発見した AP がすなわち + * 接続すべき AP なので、情報を退避する。 + */ + if (tempMessage == LOCAL_WCM_MSG_FOUND_AP) + { + /* 自動接続モードならここで検索終了 */ + if (!gWcmManualConnect) + { + (void)WCM_LockApList(WCM_APLIST_LOCK); + if (WCM_CountApList() >= 1) + { + // 最初に見つかった AP の情報のみを退避する。 + MI_CpuCopyFast(WCM_PointApList(0), &gWcmBssDesc, WCM_APLIST_SIZE); + DebugPrintf(" BSS-ID: %02x-%02x-%02x-%02x-%02x-%02x\n", gWcmBssDesc.bssid[0], + gWcmBssDesc.bssid[1], gWcmBssDesc.bssid[2], gWcmBssDesc.bssid[3], + gWcmBssDesc.bssid[4], gWcmBssDesc.bssid[5]); + DebugPrintf(" ESS-ID: %s\n", gWcmBssDesc.ssid); + } + (void)WCM_LockApList(WCM_APLIST_UNLOCK); + wcmResult = CallFunction(WCM_NOTIFY_END_SEARCH); + } + } + + /* 接続要求があればまず検索を終了 */ + else if (tempMessage == LOCAL_WCM_MSG_CONNECT_AP) + { + wcmResult = CallFunction(WCM_NOTIFY_END_SEARCH); + } + break; + + case WCM_PHASE_DCF: + // 切断通知まで待つ + (void)OS_ReceiveMessage(&gWcmMessageQ, &tempMessage, OS_MESSAGE_BLOCK); + /* + * メッセージとしては、WCM の停止要求、AP からの切断、及びエラー通知の3種類を + * 受け取る可能性がある。このどれの場合も受け取った時点で行うべき処理は無い。 + */ + if (tempMessage == LOCAL_WCM_MSG_DISCONNECT) + { + wcmResult = CallFunction(WCM_NOTIFY_DISCONNECT); + } + break; + + case WCM_PHASE_IRREGULAR: + // FIFO が溢れた可能性が高いので、少し待つ + OS_Sleep(1); + + // IRREGULAR フェーズ時に有効な処理は強制停止のみ + wcmResult = CallFunction(WCM_NOTIFY_TERMINATE); + break; + + case WCM_PHASE_FATAL_ERROR: + default: + FreezeThread(); + } + } + // 終了処理 + while (gWcmTerminateFlag != FALSE) + { + switch (WCM_GetPhase()) + { + case WCM_PHASE_WAIT: + // ループから抜ける + gWcmTerminateFlag = FALSE; + break; + + case WCM_PHASE_IDLE: + // 無線機能の停止 + wcmResult = CallFunction(WCM_NOTIFY_CLEANUP); + break; + + case WCM_PHASE_SEARCH: + // AP の自動探索を停止 + wcmResult = CallFunction(WCM_NOTIFY_END_SEARCH); + break; + + case WCM_PHASE_DCF: + // AP から切断 + wcmResult = CallFunction(WCM_NOTIFY_DISCONNECT); + break; + + case WCM_PHASE_IRREGULAR: + // 無線機能の強制終了 + wcmResult = CallFunction(WCM_NOTIFY_TERMINATE); + break; + + case WCM_PHASE_FATAL_ERROR: + default: + FreezeThread(); + } + } + + // WCM ライブラリを終了 + wcmResult = WCM_Finish(); + DebugPrintf("WCM_Finish() -> %s\n", gWcmResultText[wcmResult]); + if (wcmResult != WCM_RESULT_SUCCESS) + { + /* + * ここでエラーが返される場合は、ライブラリの使い方が間違っている可能性が高い。 + * 他のスレッドから WCM ライブラリが終了されてしまっていることが考えられる。 + */ + NcFree(0, wcmBuf, workSize + LOCAL_WCM_APLIST_BUF_SIZE); + return; + } + + // 確保していたメモリを解放 + NcFree(0, wcmBuf, workSize + LOCAL_WCM_APLIST_BUF_SIZE); + return; +} + +/*---------------------------------------------------------------------------* + Name: FreezeThread + + Description: スレッドから呼び出され、無限ループに入って固まる。 + + Arguments: None. + + Returns: 当関数からは返らない。 + *---------------------------------------------------------------------------*/ +static void FreezeThread(void) +{ + /* + * FATAL ERROR の場合はどうしようもないので、中で固まることにする。 + * 致命的なメモリが破壊された、ARM7の負荷が高すぎて処理しきれなく + * なった、スレッドのスタックが溢れた、等の原因が考えられる。 + * エンドユーザーに適切なエラー通知をしなければならないが、ソフト的に + * 復旧する方法は無いので、エンドユーザーに電源を切ってもらうしかない。 + */ + while (TRUE) + { + OS_Sleep(10000); + } +} + +/*---------------------------------------------------------------------------* + Name: CallFunction + + Description: WCM 非同期 API をコールし、非同期処理が完了するまで待つ。 + スレッドから呼び出されることを想定しており、非同期処理の完了を + 内部でブロックして待つ点に注意。 + + Arguments: WCM 非同期 API の種別を WCM_NOTIFY_* の形式で指定する。 + 指定可能なパラメータは以下の通り。 + WCM_NOTIFY_STARTUP : WCM_StartupAsync を実行する。 + WCM_NOTIFY_CLEANUP : WCM_CleanupAsync を実行する。 + WCM_NOTIFY_BEGIN_SEARCH : WCM_SearchAsycn(開始) を実行する。 + WCM_NOTIFY_END_SEARCH : WCM_SearchAsync(停止) を実行する。 + WCM_NOTIFY_CONNECT : WCM_ConnectAsync を実行する。 + WCM_NOTIFY_DISCONNECT : WCM_DisconnectAsync を実行する。 + WCM_NOTIFY_TERMINATE : WCM_TerminateAsync を実行する。 + + Returns: 同期的・非同期的な処理結果を返す。 + 返される値は以下の内のいずれかとなる。 + WCM_RESULT_SUCCESS : 処理が正常に完了。 + WCM_RESULT_FAILURE : 処理の途中でなんらかの要因で失敗。 + WCM_RESULT_FATAL_ERROR : パラメータが想定範囲外 + *---------------------------------------------------------------------------*/ +static s32 CallFunction(s16 notifyId) +{ + s32 wcmResult = WCM_RESULT_FATAL_ERROR; + OSIntrMode e = OS_DisableInterrupts(); + void* essId; + + while (TRUE) + { + // 実行すべき API を振り分け + switch (notifyId) + { + case WCM_NOTIFY_STARTUP: + wcmResult = WCM_StartupAsync(&gWcmConfig, ControlCallback); + DebugPrintf("WCM_StartupAsync() -> %s\n", gWcmResultText[wcmResult]); + break; + + case WCM_NOTIFY_CLEANUP: + wcmResult = WCM_CleanupAsync(); + DebugPrintf("WCM_CleanupAsync() -> %s\n", gWcmResultText[wcmResult]); + break; + + case WCM_NOTIFY_BEGIN_SEARCH: + /****************************************************************/ + /* デモでは特別に 0xff で埋められている SSID を WCM_Essid_Any として扱う */ + essId = (void*)constApInfoPtr->essId; + if (WCM_CompareEssID(essId, WCM_Essid_Any)) + { + essId = (void*)WCM_Essid_Any; + } + /* アクセスポイントを探索する際の重要な設定を行っている箇所 */ + wcmResult = WCM_BeginSearchAsync((void*)constApInfoPtr->bssId, essId, (u32) ( + WCM_OPTION_CHANNEL_ALL | gWcmScanType | + (gWcmManualConnect ? WCM_OPTION_ROUNDSCAN_NOTIFY : WCM_OPTION_ROUNDSCAN_IGNORE) + )); + + /****************************************************************/ + DebugPrintf("WCM_BeginSearchAsync -> %s\n", gWcmResultText[wcmResult]); + break; + + case WCM_NOTIFY_END_SEARCH: + wcmResult = WCM_EndSearchAsync(); + DebugPrintf("WCM_EndSearchAsync -> %s\n", gWcmResultText[wcmResult]); + break; + + case WCM_NOTIFY_CONNECT: + /****************************************************************/ + + /* アクセスポイントに接続する際の重要な設定を行っている箇所 */ + wcmResult = WCM_ConnectAsync(&gWcmBssDesc, (void*)(&(constApInfoPtr->wepDesc)), + gWcmPowerMode | constApInfoPtr->auth_option); + + /****************************************************************/ + DebugPrintf("WCM_ConnectAsync() -> %s\n", gWcmResultText[wcmResult]); + break; + + case WCM_NOTIFY_DISCONNECT: + wcmResult = WCM_DisconnectAsync(); + DebugPrintf("WCM_DisconnectAsync() -> %s\n", gWcmResultText[wcmResult]); + break; + + case WCM_NOTIFY_TERMINATE: + wcmResult = WCM_TerminateAsync(); + DebugPrintf("WCM_TerminateAsync() -> %s\n", gWcmResultText[wcmResult]); + break; + + default: + (void)OS_RestoreInterrupts(e); + return wcmResult; + } + + // 同期的な処理結果を確認 + switch (wcmResult) + { + case WCM_RESULT_REJECT: + // 少し待ってからリトライする + OS_Sleep(1); + continue; + + case WCM_RESULT_PROGRESS: + case WCM_RESULT_ACCEPT: + // 非同期処理応答がコールバックされるまで待つ + OS_SleepThread(&gWcmThreadQ); + break; + + case WCM_RESULT_SUCCESS: + case WCM_RESULT_FAILURE: + // 同期的に処理が完了し、非同期処理は行われない場合 + (void)OS_RestoreInterrupts(e); + return wcmResult; + + case WCM_RESULT_WMDISABLE: + /* + * ARM7 側で WM ライブラリが動作していない場合。 + * WCM を使い始める前に WVR ライブラリを使って ARM7 側無線ドライバを + * 起動させる必要があるが、それはアプリケーションの責任で行うべきこと。 + */ + OS_Panic("ARM7 WM library is not ready.\n"); + + case WCM_RESULT_NOT_ENOUGH_MEM: // 非同期関数にこの返り値が返されることは無い + case WCM_RESULT_FATAL_ERROR: + default: + (void)OS_RestoreInterrupts(e); + FreezeThread(); + } + + // 非同期的な処理結果を確認 + switch (gWcmResult) + { + case WCM_RESULT_SUCCESS: + case WCM_RESULT_FAILURE: + wcmResult = gWcmResult; + break; + + case WCM_RESULT_PROGRESS: // 非同期処理結果にこの処理結果が渡されることは無い + case WCM_RESULT_ACCEPT: // 非同期処理結果にこの処理結果が渡されることは無い + case WCM_RESULT_REJECT: // 非同期処理結果にこの処理結果が渡されることは無い + case WCM_RESULT_WMDISABLE: // 非同期処理結果にこの処理結果が渡されることは無い + case WCM_RESULT_NOT_ENOUGH_MEM: // 非同期処理結果にこの処理結果が渡されることは無い + case WCM_RESULT_FATAL_ERROR: + default: + (void)OS_RestoreInterrupts(e); + FreezeThread(); + } + + break; + } + + (void)OS_RestoreInterrupts(e); + return wcmResult; +} + +/*---------------------------------------------------------------------------* + Name: ControlCallback + + Description: WCM 非同期 API の処理結果応答、及び WCM ライブラリからの 不定期 + 通知を受け取るコールバック関数。 + + Arguments: arg - 通知種別や処理結果などを示す構造体へのポインタ。 + + Returns: None. + *---------------------------------------------------------------------------*/ +static void ControlCallback(WCMNotice* arg) +{ + static s16 lastNotify = -1; + static s16 lastResult = -1; + + //前回と結果が違ったら表示 + if (lastNotify != arg->notify || lastResult != arg->result) + { + DebugPrintf(" cb %s %s\n", gWcmNotifyText[arg->notify], gWcmResultText[arg->result]); + lastNotify = arg->notify; + lastResult = arg->result; + } + + switch (arg->notify) + { + case WCM_NOTIFY_STARTUP: + gWcmSearchAroundCount = 0; + + case WCM_NOTIFY_CLEANUP: + case WCM_NOTIFY_BEGIN_SEARCH: + case WCM_NOTIFY_END_SEARCH: + case WCM_NOTIFY_CONNECT: + case WCM_NOTIFY_TERMINATE: + // 要求と 1 対 1 に対応する非同期処理の結果応答 + gWcmResult = arg->result; + OS_WakeupThread(&gWcmThreadQ); + break; + + case WCM_NOTIFY_DISCONNECT: +#ifdef SDK_THREAD_INFINITY + if (gWcmThreadQ.head != NULL) +#else + if (gWcmThreadQ != 0) +#endif + { + // 要求と 1 対 1 に対応する非同期処理の結果応答 + gWcmResult = arg->result; + OS_WakeupThread(&gWcmThreadQ); + } + else + { + // スレッドが稼動中、もしくはメッセージ待ちで停止状態の場合 + if (arg->result == WCM_RESULT_SUCCESS) + { + // DCF 通信中に AP から切断されたことを示す通知 + (void)OS_SendMessage(&gWcmMessageQ, LOCAL_WCM_MSG_DISCONNECTED, OS_MESSAGE_NOBLOCK); + } + else + { + // DCF 通信中になんらかのエラーが発生したことを示す通知 + (void)OS_JamMessage(&gWcmMessageQ, LOCAL_WCM_MSG_ERROR, OS_MESSAGE_NOBLOCK); + } + } + break; + + case WCM_NOTIFY_FOUND_AP: + // 自動探索時の非同期通知 + { + if (arg->result == WCM_RESULT_SUCCESS) + { + OSMessage tempMessage; + + // 自動探索時に AP を発見したことを示す通知 + if (OS_ReadMessage(&gWcmMessageQ, &tempMessage, OS_MESSAGE_NOBLOCK) == FALSE) + { + // 何度も通知されるものなので、メッセージキューが空である場合のみに限定 + (void)OS_SendMessage(&gWcmMessageQ, LOCAL_WCM_MSG_FOUND_AP, OS_MESSAGE_NOBLOCK); + } + } + else + { + // 自動探索時になんらかのエラーが発生したことを示す通知 + (void)OS_JamMessage(&gWcmMessageQ, LOCAL_WCM_MSG_ERROR, OS_MESSAGE_NOBLOCK); + } + } + break; + + case WCM_NOTIFY_SEARCH_AROUND: + // 自動探索時に指定チャンネルを一巡したことを示す不定期通知 + ++gWcmSearchAroundCount; + break; + } +} + +/*---------------------------------------------------------------------------* + Name: InitWcmApInfo + + Description: アクセスポイント接続パラメータを環境設定値から取得して + 初期化する + + Arguments: apinfo 接続パラメータ + apclass アクセスポイントのクラス名 + + Returns: TRUE 成功 + FALSE 失敗 + *---------------------------------------------------------------------------*/ +static BOOL myEnvCopy(const char* name, void* dest, int dest_size); +static BOOL myEnvCopy(const char* name, void* dest, int dest_size) +{ + void* src; + int src_size; + + if (ENV_GetBinaryAndSize(name, &src, &src_size)) + { + MI_CpuCopy8(src, dest, (u32) MATH_MIN(src_size, dest_size)); + return TRUE; + } + + return FALSE; +} + +BOOL InitWcmApInfo(WcmControlApInfo* apinfo, const char* apclass) +{ + BOOL isvalid; + char buf[WCM_ESSID_SIZE + 1]; + + SiteDefs_Init(); + + OS_TPrintf("SiteDef: %s\n", apclass); + ENV_SetClass(apclass); + + if (ENV_GetBOOL(".ISVALID", &isvalid) && isvalid) + { + MI_CpuClear8(apinfo, sizeof(WcmControlApInfo)); + + if (myEnvCopy(".ESSID", apinfo->essId, sizeof(apinfo->essId)) && + myEnvCopy(".BSSID", apinfo->bssId, sizeof(apinfo->bssId)) && ENV_GetU32(".AUTHMODE", &apinfo->auth_option) && + ENV_GetU8(".WEP.MODE", &apinfo->wepDesc.mode) && ENV_GetU8(".WEP.KEYID", &apinfo->wepDesc.keyId) && + myEnvCopy(".WEP.KEY", apinfo->wepDesc.key, sizeof(apinfo->wepDesc.key))) + { +#ifdef SDK_TWL + /* 暗号化方式が WPA の場合はパスフレーズを PSK に変換 */ + if(apinfo->wepDesc.mode > WCM_WEPMODE_128 ) /* WPA */ + { + u8 psk[WCM_WPA_PSK_SIZE]; + if (WCM_GetWPAPSK(apinfo->wepDesc.key, apinfo->essId, (u8)STD_GetStringLength((const char*)apinfo->essId), psk) == FALSE) + { + OS_TWarning("PreSharedKey Calculation Error.\n"); + return FALSE; + } + MI_CpuClear8(apinfo->wepDesc.key, sizeof(apinfo->wepDesc.key)); + MI_CpuCopy8(psk, apinfo->wepDesc.key, WCM_WPA_PSK_SIZE); + + apinfo->wepDesc.keyId = 32; + } +#endif + MI_CpuClear8(buf, sizeof(buf)); + MI_CpuCopy8(apinfo->essId, buf, WCM_ESSID_SIZE); + OS_TPrintf(" essId: %s, bssId: %02x:%02x:%02x:%02x:%02x:%02x\n", buf, + apinfo->bssId[0], apinfo->bssId[1], apinfo->bssId[2], + apinfo->bssId[3], apinfo->bssId[4], apinfo->bssId[5]); + + OS_TPrintf(" wepmode: %d\n", apinfo->wepDesc.mode); + if( apinfo->wepDesc.keyId != 32) { + OS_TPrintf(" wepkey: %s\n", apinfo->wepDesc.key); + } + return TRUE; + } + } + OS_TWarning("This SiteDef name is invalid.\n"); + + return FALSE; +} + +/*---------------------------------------------------------------------------* + End of file + *---------------------------------------------------------------------------*/ diff --git a/build/tools/sctools/common/src/wcm_control.h b/build/tools/sctools/common/src/wcm_control.h new file mode 100644 index 0000000..5cee3ff --- /dev/null +++ b/build/tools/sctools/common/src/wcm_control.h @@ -0,0 +1,171 @@ +/*---------------------------------------------------------------------------* + Project: TwlWiFi - demos - netconnect + File: wcm_control.h + + Copyright 2005,2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: 2007-12-06#$ + $Rev: 156 $ + $Author: okajima_manabu $ + *---------------------------------------------------------------------------*/ +#ifndef SHARED_WCM_CONTROL_H_ +#define SHARED_WCM_CONTROL_H_ + +#include "ap_info.h" + +#ifdef __cplusplus + +extern "C" { +#endif + +/*===========================================================================*/ + +/*---------------------------------------------------------------------------* + Name: InitWcmControl + + Description: 内部変数を初期化し、WCM コントロールスレッドを起動する。 + 既にスレッドが起動している場合は何も行わない。 + + Arguments: None. + + Returns: None. + *---------------------------------------------------------------------------*/ +void InitWcmControl(void); +void InitWcmControlEx(u8 deviceId); +void InitWcmControlByApInfo(const WcmControlApInfo* ptr); +void InitWcmControlByApInfoEx(const WcmControlApInfo* ptr, u8 deviceId); + +/*---------------------------------------------------------------------------* + Name: InitWcmApInfo + + Description: アクセスポイント接続パラメータを環境設定値から取得して + 初期化する + + Arguments: apinfo 接続パラメータ + apclass アクセスポイントのクラス名 + + Returns: TRUE 成功 + FALSE 失敗 + *---------------------------------------------------------------------------*/ +BOOL InitWcmApInfo(WcmControlApInfo* apinfo, const char* apclass); + +/*---------------------------------------------------------------------------* + Name: TerminateWcmControl + + Description: WCM コントロールスレッドを終了する。 + スレッドが起動されていない場合は何も行わない。 + NOTICE: 当関数はスレッドを終了する為のトリガとなるだけなので、スレッド + 自体が終了するまで待たずに戻る点に注意。 + スレッド自体の終了を待ちたい場合は、OS_SendMessage 呼び出しに + 続いて OS_JoinThread を呼び出す必要がある。 + なお、WCM_GetPhase 関数が WCM_PHASE_NULL を返すようになれば + スレッドは終了している。 + + Arguments: None. + + Returns: None. + *---------------------------------------------------------------------------*/ +void TerminateWcmControl(void); + +/*---------------------------------------------------------------------------* + Name: SetWcmThreadPriority + + Description: WCM ライブラリの状態管理を行うスレッドの優先度を変更する。 + + Arguments: priority - 0 〜 31 のスレッド優先度を指定する。 + + Returns: u8 - 変更する前に設定されていた優先度を返す。 + なんらかの理由で優先度の変更に失敗した場合は 0xff を返す。 + *---------------------------------------------------------------------------*/ +u8 SetWcmThreadPriority(u8 priority); + +/*---------------------------------------------------------------------------* + Name: GetWcmSearchAroundCount + + Description: 全チャンネルを何週スキャンしたかを返す。 + StartUp直後は0になる。 + + Arguments: None. + + Returns: 何回スキャンしたか。 + *---------------------------------------------------------------------------*/ +u32 GetWcmSearchAroundCount(void); + +/*---------------------------------------------------------------------------* + Name: SetWcmManualConnectMode + + Description: APのリストから手動で選択して接続するよう設定. + デフォルトでは FALSE (自動接続)となっている. + InitWcmControl() より先に呼び出す必要がある. + + Arguments: enable - 手動で接続する場合は TRUE。 + + Returns: None. + *---------------------------------------------------------------------------*/ +void SetWcmManualConnect(BOOL enable); + +/*---------------------------------------------------------------------------* + Name: ConnectAP + + Description: 指定されたBssDescのAPへ接続する + + Arguments: bd - 接続するAPを示す構造体へのポインタ。 + + Returns: None. + *---------------------------------------------------------------------------*/ +void ConnectAP(const WCMBssDesc* pbd); + +/*---------------------------------------------------------------------------* + Name: DisconnectAP + + Description: AP との接続の切断を試みる + + Arguments: None. + + Returns: None. + *---------------------------------------------------------------------------*/ +void DisconnectAP(void); + +/*---------------------------------------------------------------------------* + Name: SetWcmScanType + + Description: AP を探索する際のスキャン方式を設定する。 + デフォルトではアクティブスキャン方式となっている。 + InitWcmControl() より先に呼び出す必要がある。 + + Arguments: type - WCM_OPTION_SCANTYPE_* の定数を指定する。 + + Returns: None. + *---------------------------------------------------------------------------*/ +void SetWcmScanType(u32 type); + +/*---------------------------------------------------------------------------* + Name: SetWcmPowerMode + + Description: AP と通信する際の電力管理方式を指定する。 + デフォルトでは常時アクティブ方式となっている。 + InitWcmControl() より先に呼び出す必要がある。 + + Arguments: mode - WCM_OPTION_POWER_* の定数を指定する。 + + Returns: None. + *---------------------------------------------------------------------------*/ +void SetWcmPowerMode(u32 mode); + +/*===========================================================================*/ +#ifdef __cplusplus + +} /* extern "C" */ +#endif + +#endif /* SHARED_WCM_CONTROL_H_ */ + +/*---------------------------------------------------------------------------* + End of file + *---------------------------------------------------------------------------*/ diff --git a/build/tools/sctools/copy_dst/Makefile b/build/tools/sctools/copy_dst/Makefile new file mode 100644 index 0000000..8a3f002 --- /dev/null +++ b/build/tools/sctools/copy_dst/Makefile @@ -0,0 +1,90 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: TwlSDK - demos - MI - ndma-1 +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Date:: 2008-01-17#$ +# $Rev: 3650 $ +# $Author: okubata_ryoma $ +#---------------------------------------------------------------------------- +SUBDIRS = ./banner + +TARGET_PLATFORM := TWL +TWL_ARCHGEN := LIMITED + +SRCDIR = ../common/src ./src + +SRCS = main.c key.c font.c text.c mprintf.c logprintf.c \ + gfx.c hwi.c mynvram.c my_fs_util.c \ + kpsc.cpp ecdl.cpp hatamotolib.cpp \ + sitedefs.c wcm_control.c netconnect.c mywlan.c \ + nuc.c nuc_error_msg.c stream.c + +TARGET_BIN = copy_dst.srl +ROM_SPEC = copy_dst.rsf + + +ES_DIR = $(ROOT)/add-ins/es +ES_LATEST = $(shell ls -d $(ES_DIR)/es-sdk-*|sort|tail -n 1) +ES_ROOT = $(ES_LATEST) + +LINCLUDES = $(TWLSDK_ROOT)/build/libraries/lcfg/ARM9.TWL/include ../common/src \ + $(ES_ROOT)/twl/include $(ES_ROOT)/common/lib/ec/include + +LLIBRARY_DIRS += $(ES_ROOT)/twl/lib/$(TWL_BUILDTYPE) + + +LLIBRARIES += libecx$(TWL_LIBSUFFIX).a \ + liblcfg$(TWL_LIBSUFFIX).a \ + libnhttp.nssl$(TWL_LIBSUFFIX).a \ + libshr$(TWL_LIBSUFFIX).a \ + libnuc$(TWL_LIBSUFFIX).a \ + libnup$(TWL_LIBSUFFIX).a \ + libnam$(TWL_LIBSUFFIX).a \ + libes$(TWL_LIBSUFFIX).a \ + libsea$(TWL_LIBSUFFIX).a \ + libboc$(TWL_LIBSUFFIX).a \ + libsfs$(TWL_LIBSUFFIX).a \ + libna$(TWL_LIBSUFFIX).a \ + + +# libnhttp$(TWL_LIBSUFFIX).a \ + + +MAKEROM_ROMROOT = ../files +MAKEROM_ROMFILES = fanfare.32.wav + + +COMPONENT_NAME = armadillo +MAKEROM_ARM7_BASE = $(TWL_COMPONENTSDIR)/$(COMPONENT_NAME)/$(TWL_BUILDTYPE_ARM7)/$(COMPONENT_NAME) +MAKEROM_ARM7 = $(MAKEROM_ARM7_BASE).$(TWL_ELF_EXT) + + +include $(TWLWIFI_ROOT)/build/buildtools/commondefs +include $(TWLSDK_ROOT)/build/buildtools/commondefs + + +#---------------------------------- +# セキュアアプリ指定 + +MAKEROM := $(TWL_TOOLSDIR)/bin/makerom.TWL.secure.exe + + +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- + +do-build: $(TARGETS) + +#---------------------------------------------------------------------------- + +include $(TWLWIFI_ROOT)/build/buildtools/modulerules +include $(TWLSDK_ROOT)/build/buildtools/modulerules diff --git a/build/tools/sctools/copy_dst/banner/Makefile b/build/tools/sctools/copy_dst/banner/Makefile new file mode 100644 index 0000000..4f2a5fc --- /dev/null +++ b/build/tools/sctools/copy_dst/banner/Makefile @@ -0,0 +1,59 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: TwlSDK - nandApp - demos - SubBanner +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Date:: 2008-06-27#$ +# $Rev: 6887 $ +# $Author: nakasima $ +#---------------------------------------------------------------------------- +TARGET_PLATFORM = TWL + +include $(TWLSDK_ROOT)/build/buildtools/commondefs +MAKEBANNER = $(TWL_TOOLSDIR)/bin/makebanner.TWL.exe + +ICON_DIR = ./icon + +BANNER_ICON = $(ICON_DIR)/gameIcon.bmp +BANNER_SPEC = banner_v3.bsf + +SUB_BANNER_ICON = $(ICON_DIR)/subIcon.bmp +SUB_BANNER_SPEC = sub_banner_v3.bsf + + +TARGETS = banner.bnr +SUB_TARGETS = sub_banner.bnr +INSTALL_DIR = ./ +INSTALL_TARGETS = $(TARGETS) $(SUB_TARGETS) + +BANNER_ICON_NAME = $(basename $(BANNER_ICON)) +BANNER_ICON_MIDDLE = $(addprefix $(BANNER_ICON_NAME), .nbfs .nbfc .nbfp) +SUB_BANNER_ICON_NAME = $(basename $(SUB_BANNER_ICON)) +SUB_BANNER_ICON_MIDDLE = $(addprefix $(SUB_BANNER_ICON_NAME), .nbfs .nbfc .nbfp) + +LDIRT_CLEAN = $(TARGETS) $(SUB_TARGETS) \ + $(BANNER_ICON_MIDDLE) \ + $(SUB_BANNER_ICON_MIDDLE) \ + $(TARGETS:.bnr=.srl) + +include $(TWLSDK_ROOT)/build/buildtools/modulerules + +#---------------------------------------------------------------------------- +# build +#---------------------------------------------------------------------------- +do-build: $(TARGETS) $(SUB_TARGETS) + +$(TARGETS): $(BANNER_SPEC) $(BANNER_ICON) $(BANNER_ICON_MIDDLE) + $(MAKEBANNER) -N $(BANNER_ICON_NAME) $(BANNER_SPEC) $(TARGETS) + +$(SUB_TARGETS): $(SUB_BANNER_SPEC) $(SUB_BANNER_ICON) $(SUB_BANNER_ICON_MIDDLE) + $(MAKEBANNER) -s -N $(SUB_BANNER_ICON_NAME) $(SUB_BANNER_SPEC) $(SUB_TARGETS) +# diff --git a/build/tools/sctools/copy_dst/banner/banner_v3.bsf b/build/tools/sctools/copy_dst/banner/banner_v3.bsf new file mode 100644 index 0000000000000000000000000000000000000000..8e54be0a3997f10f8ede1e2efe38741bca4761d4 GIT binary patch literal 788 zcmc(dOK-wJ420)OCH})ooXe|H4?Thi4^;>?ZSM;K5s8;bDB{mUKX2$YxfQLnu{~aA z#`f*nP*1TUIX#Et@V$1NrH({n9k3Qyky352>v~e-o?X28pS$?4)2G%j$zZg)GjiJM zL?kCRhoRFM?kRqbx5H1vz2Kz>$%tC+7qudkbo8os4IZk@)P2Lt*rnRT_YHP#GZ*<_?nVLZFNJ=K+{q@(nl#iM1NWRF}*6o;|hNL3Vx#syqNyq4&-RJEzQ_S tAEunptvP*h%-H6BfNhI&re8<05;e)(fO!4TVcQl@%=)<{&ngzxe*quvXXyX{ literal 0 HcmV?d00001 diff --git a/build/tools/sctools/copy_dst/banner/icon/gameIcon.bmp b/build/tools/sctools/copy_dst/banner/icon/gameIcon.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d094481d115037587ecd84f0e8e9677c74debf7d GIT binary patch literal 630 zcmb7>y^VuF5QX1aZeVkyz3?P*3OFuxKnZj}L_kgjlt34BKoRs{B;p&tnf)2v7#R!ynp#s6a~t%L{(L&>l#hdplw@pU58;9FpeXp zX~I0uSe6Crx?#P6xq*wko`#@i~GY2>`7B!11Y2|-nk?gGvLIrOC<5m z5h@sq*Co8p3{01-7YBA6s7*dH8`y>MIX!?Yx7f_7U7Z7{HxR-#{BlSotd9$)$wXKP0^U zDWUgE!r5;LQc3{*KfxTO6n83w5EXUf%4AJy=tNVMc~s%bVhkM>+{oBYyc${)#Jclf z+K{j{l~9;Dpz`NG6ox2FsVJC>QhLQzT+UULtlXhhe95Q>^Ie&U#}s%r6W%3j^x z8aJywy{I8~YwfzTHD=X5vR0Y|EnauG&sn2y6w9O(BiEwQx!Kqkf7z$>qs0hWE?0D{ zclln}v7U!Jw5;p#Nh{z!?bw@SK&!}NBk1}y&v`)x&8m@d;QOE5_nW&c<0$;XA9e0y z7Y&Ma?B$fdx#wb=DIZ^cIPLPbhix{RMm!W7{fmHe&vmTF%!osAJh}*i)bK3fpU2@i!h!2_6JTSkmlys02hFs?_L@`RVkPoEO}0ghB3+yDRo literal 0 HcmV?d00001 diff --git a/build/tools/sctools/copy_dst/banner/sub_banner_v3.bsf b/build/tools/sctools/copy_dst/banner/sub_banner_v3.bsf new file mode 100644 index 0000000000000000000000000000000000000000..2fd176b00d75decec4e62183a2f7fb8e5c0a4fec GIT binary patch literal 864 zcmc(e!AiqG5QhJ&g5V=8^sKH39(q(7Td^Wd^`2~68fdE|(c;tiKAyb$A|6EjcDDx= z3OOkQ!_LgV|Nb+Xe13FvqEv~D_JSGWOiOH{ifpPoPL5NeQghBb+E!_?Lk(p0VxR51eVY#LtLxj{{AuJ-Jzf4ljbZe2NtutsCRhqA?4Szsa f2iS`5Qh_S84{}Z=(*s}2PdFpzs4u{I#>oB! +#include +#include "ecdl.h" +#include +#include +#include +#include + +#include "font.h" +#include "text.h" +#include "mprintf.h" +#include "gfx.h" +#include "key.h" +#include "my_fs_util.h" +#include "mynvram.h" + +#include "stream.h" + +#include "hwi.h" + +#include "hatamotolib.h" +#include "ecdl.h" + +#include "mywlan.h" + +#include "nuc.h" + + + +//#define DEBUG_PRINT 1 + +//================================================================================ + +static FSEventHook sSDHook; +static BOOL sd_card_flag = FALSE; + + +static void SDEvents(void *userdata, FSEvent event, void *arg) +{ + (void)userdata; + (void)arg; + if (event == FS_EVENT_MEDIA_REMOVED) { + sd_card_flag = FALSE; + mprintf("sdmc:removed!\n"); + } + else if (event == FS_EVENT_MEDIA_INSERTED) { + sd_card_flag = TRUE; + mprintf("sdmc:inserted!\n"); + } +} + +typedef struct { + RTCDate rtc_date; + RTCTime rtc_time; + BOOL shop_record_flag; + u8 movableUniqueID[ LCFG_TWL_HWINFO_MOVABLE_UNIQUE_ID_LEN ]; // 移行可能なユニークID 16byte +} MyData; + +static MyData mydata; + +static int vram_num_main = 1; +static int vram_num_sub = 0; + +void TwlMain(void) +{ + void* newArenaLo; + OSHeapHandle hHeap; + u16 keyData; + int loop_counter = 0; + int save_dir_info = 0; + MY_DIR_ENTRY_LIST *dir_entry_list_head = NULL; + RTCDate rtc_date; + RTCTime rtc_time; + LCFGTWLHWNormalInfo hwn_info; + LCFGTWLHWSecureInfo hws_info; + int i; + int n; + char path_base[256]; + char path_log[256]; + char path[256]; + u8 macAddress[6]; + + OS_Init(); + OS_InitThread(); + + OS_InitTick(); + OS_InitAlarm(); + + + // マスター割り込みフラグを許可に + (void)OS_EnableIrq(); + + // IRQ 割り込みを許可します + (void)OS_EnableInterrupts(); + + + Gfx_Init(); + + /* + 0 -> black + 1 -> red + 2 -> green + 3 -> blue + 4 -> yellow + 5 -> purple + 6 -> sky blue + 7 -> red + 8 -> green + 9 -> blue + 0xA -> yellow + 0xB -> purple + 0xC -> sky blue + 0xD -> white + 0xE -> white + 0xF -> white + + */ + // m_set_palette(tc[0], 0xF); + + + // ARM7との通信FIFO割り込み許可 + (void)OS_EnableIrqMask(OS_IE_SPFIFO_RECV); + + // ファイルシステム初期化 + FS_Init( FS_DMA_NOT_USE ); + + + // メインアリーナのアロケートシステムを初期化 + newArenaLo = OS_InitAlloc(OS_ARENA_MAIN, OS_GetMainArenaLo(), OS_GetMainArenaHi(), 1); + OS_SetMainArenaLo(newArenaLo); + + // メインアリーナ上にヒープを作成 + hHeap = OS_CreateHeap(OS_ARENA_MAIN, OS_GetMainArenaLo(), OS_GetMainArenaHi()); + OS_SetCurrentHeap(OS_ARENA_MAIN, hHeap); + + RTC_Init(); + + SCFG_Init(); + + NVRAMi_Init(); + + SND_Init(); + stream_main(); + + // 必須;SEA の初期化 + SEA_Init(); + + // 不要:NAM の初期化 + // NAM_Init(&AllocForNAM, &FreeForNAM); + + // 必須:ES の初期化 + ES_InitLib(); + + + if( FALSE == MiyaReadHWNormalInfo( &hwn_info ) ) { + mprintf("HW Normal Info. read error\n"); + } + else { + mprintf("HW Normal Info. read succeeded.\n"); + mprintf("UniqueID\n 0x"); + for( i = 0; i < LCFG_TWL_HWINFO_MOVABLE_UNIQUE_ID_LEN/2 ; i++ ) { + mprintf("%02X:", hwn_info.movableUniqueID[i]); + } + mprintf("\n 0x"); + for( ; i < LCFG_TWL_HWINFO_MOVABLE_UNIQUE_ID_LEN ; i++ ) { + mprintf("%02X:", hwn_info.movableUniqueID[i]); + } + mprintf("\n"); + // mprintf(" RTC Adjust data = 0x%02x\n", hwn_info.rtcAdjust ); + } + + mprintf("\n"); + + if( FALSE == MiyaReadHWSecureInfo( &hws_info ) ) { + mprintf("HW Secure Info. - read failed\n"); + } + else { + mprintf("HW Secure Info. - read succeeded.\n"); + + mprintf(" Serial No.\n 0x"); + for( i = 0; i < LCFG_TWL_HWINFO_SERIALNO_LEN_MAX/2 ; i++ ) { + mprintf("%02X:", hws_info.serialNo[i]); + } + mprintf("\n 0x"); + for( ; i < LCFG_TWL_HWINFO_SERIALNO_LEN_MAX ; i++ ) { + if( hws_info.serialNo[i] ) { + mprintf("%02X:", hws_info.serialNo[i]); + } + else { + // #define LCFG_TWL_HWINFO_SERIALNO_LEN_MAX 15 // 本体シリアルNo.長Max(終端付きなので、14bytesまで拡張 + // 終端をみつけたらブレーク + break; + } + } + mprintf("\n"); + mprintf("%s\n", hws_info.serialNo); + + mprintf(" validLang.bmp = 0x%08x\n", hws_info.validLanguageBitmap ); + mprintf(" wifi disable flag = %d\n", hws_info.flags.forceDisableWireless ); + mprintf(" lchr-TitleIDLo = " ); + for( i = 0 ; i < 4 ; i++ ) { + mprintf("%02X:", hws_info.launcherTitleID_Lo[i]); + } + mprintf("\n Region data = 0x%02x\n\n", hws_info.region ); + } + + + + + OS_GetMacAddress( macAddress ); + mprintf("macAddress "); + for ( i = 0 ; i < 6 ; i++ ) { + mprintf("%02X:", macAddress[i]); + } + mprintf("\n"); + + + if( FALSE == SDCardValidation() ) { + sd_card_flag = FALSE; + mprintf("No SD Card\n"); + } + else { + sd_card_flag = TRUE; + mprintf("Detect SD Card\n"); + } + + FS_RegisterEventHook("sdmc", &sSDHook, SDEvents, NULL); + + + STD_StrCpy( path_base , "sdmc:/" ); + STD_StrCat( path_base , (const char *)hws_info.serialNo ); + STD_StrCat( path_base , "/" ); + + + + + while( 1 ) { + OS_WaitVBlankIntr(); + Gfx_Render( vram_num_main , vram_num_sub ); + (void)RTC_GetTime( &rtc_time ); + + keyData = m_get_key_trigger(); + + // ARM7コマンド応答受信 + while (SND_RecvCommandReply(SND_COMMAND_NOBLOCK) != NULL) + { + } + // コマンドフラッシュ(フラッシュして即座に実行を要求) + (void)SND_FlushCommand(SND_COMMAND_NOBLOCK | SND_COMMAND_IMMEDIATE); + + + if ( keyData & PAD_BUTTON_R ) { + } + else if ( keyData & PAD_BUTTON_L ) { + } + else if ( keyData & PAD_BUTTON_A ) { + /* ユーザーデータ吸出しモード */ + if( sd_card_flag == TRUE ) { + + mprintf("BACKUP to SD Card\n"); + /************************************/ + + /* ショップの履歴確認 */ + if( TRUE == CheckShopRecord(NULL) ) { + mydata.shop_record_flag = TRUE; + } + else { + mydata.shop_record_flag = FALSE; + mprintf("no shop record\n"); + } + + + + /* 日時の保存 */ + STD_StrCpy( path , path_base ); + STD_StrCat( path , "twl_rtc.dat" ); + if( RTC_RESULT_SUCCESS != RTC_GetDate( &rtc_date ) ) { + mprintf("rtc read date error.\n"); + } + if( RTC_RESULT_SUCCESS != RTC_GetTime( &rtc_time ) ) { + mprintf("rtc read time error.\n"); + } + + STD_CopyMemory( (void *)&(mydata.rtc_date), (void *)&rtc_date, sizeof(RTCDate) ); + STD_CopyMemory( (void *)&(mydata.rtc_time), (void *)&rtc_time, sizeof(RTCTime) ); + STD_CopyMemory( (void *)(mydata.movableUniqueID), (void *)hwn_info.movableUniqueID, LCFG_TWL_HWINFO_MOVABLE_UNIQUE_ID_LEN ); + if( TRUE == MydataSave(path, (void *)&mydata, sizeof(MyData), NULL) ) { + } + + + + /* nand:/sysディレクトリまわりの保存 */ + STD_StrCpy( path , path_base ); + STD_StrCat( path , "twl_ninfo.dat" ); + if( TRUE == MiyaBackupHWNormalInfo( path ) ) { + mprintf("HWInfo Normal backup completed.\n"); + } + + /* Wifi設定の保存 */ + STD_StrCpy( path , path_base ); + STD_StrCat( path , "twl_nor.bin" ); + if( TRUE == nvram_backup( path ) ) { + mprintf("nvram backup completed.\n"); + } + + /* nand:/shared1ディレクトリまわりの保存 */ + STD_StrCpy( path , path_base ); + STD_StrCat( path , "twl_cfg.dat" ); + if( TRUE == MiyaBackupTWLSettings( path ) ) { + mprintf("TWL CFG backup completed.\n"); + } + + + /* nand:/shared2ディレクトリまわりの保存 */ + STD_StrCpy( path , path_base ); + STD_StrCat( path , "shared2" ); + STD_StrCpy( path_log , path_base ); + STD_StrCat( path_log , "shared2.txt" ); + + if( 0 == copy_r( &dir_entry_list_head, path, "nand:/shared2" , path_log ) ) { + mprintf("copy_r shared2 completed.\n"); + PrintDirEntryListBackward( dir_entry_list_head, NULL ); + STD_StrCpy( path , path_base ); + STD_StrCat( path , "shared2.lst" ); + SaveDirEntryList( dir_entry_list_head, path ); + } + (void)ClearDirEntryList( &dir_entry_list_head ); + + + /* nand2:/photoディレクトリまわりの保存 */ + STD_StrCpy( path , path_base ); + STD_StrCat( path , "photo" ); + STD_StrCpy( path_log , path_base ); + STD_StrCat( path_log , "photolog.txt" ); + + if( 0 == copy_r( &dir_entry_list_head, path , "nand2:/photo" , path_log ) ) { + mprintf("copy_r photo completed.\n"); + PrintDirEntryListBackward( dir_entry_list_head, NULL ); + STD_StrCpy( path , path_base ); + STD_StrCat( path , "photo.lst" ); + SaveDirEntryList( dir_entry_list_head, path ); + } + (void)ClearDirEntryList( &dir_entry_list_head ); + + /* nand:/ticketはチケット同期?でうまいこと合わせてくれるんでバックアップ不要 */ + + /* nand:/titleディレクトリまわりの保存 */ + /* **.savファイルをすべてバックアップ */ + + STD_StrCpy( path , path_base ); + STD_StrCat( path , "title" ); + STD_StrCpy( path_log , path_base ); + STD_StrCat( path_log , "titlelog.txt" ); + + if( 0 == find_title_save_data( &dir_entry_list_head, path , "nand:/title", &save_dir_info, path_log ) ) { + mprintf("find_title_save_data completed.\n"); + PrintDirEntryListBackward( dir_entry_list_head, NULL ); + STD_StrCpy( path , path_base ); + STD_StrCat( path , "title.lst" ); + + SaveDirEntryList( dir_entry_list_head , path ); + } + (void)ClearDirEntryList( &dir_entry_list_head ); + + + + /* タイトルリストの生成 */ + /* + nand:/title/00030004/ + + nand:/title/00030004/34626241/content/title.tmd + nand:/title/00030004/34626241/content/00000000.app + nand:/title/00030004/34626241/data/ + + nand:/title/00030017/484e4141 は ランチャー + nand:/title/00030015/484e4641 は shop + nand:/title/00030015/484e4241 は 本体設定 + + + No. 0 0003000f484e4c41 + No. 1 0003000f484e4841 + No. 2 0003000f484e4341 + No. 3 00030015484e4241 + No. 4 00030017484e4141 launcher + ^ + | ここの最下位ビットが1のやつがシステムアプリ + | + システムアプリはダウンロード対象外 + */ + + STD_StrCpy( path_log , path_base ); + STD_StrCat( path_log , "title2log.txt" ); + if( 0 == get_title_id( &dir_entry_list_head, "nand:/title", &save_dir_info, path_log ) ) { + mprintf("get_title_id completed.\n"); + + // STD_StrCpy( path , path_base ); + // STD_StrCat( path , "titlelist.txt" ); + { + void *pBuffer; + int count; + int i,j; + char *ptr; + GetDirEntryList( dir_entry_list_head, &pBuffer, &count); + OS_TPrintf("count = %d\n", count ); + ptr = (char *)pBuffer; + + if( ptr != NULL && count != 0 ) { + for( j = 0 ; j < count ; j++ ) { + OS_TPrintf("No. %d ",j); + for( i = 0 ; i < 16 ; i++ ) { + OS_TPrintf("%c", *ptr); + ptr++; + } + OS_TPrintf("\n"); + } + OS_Free(pBuffer); + } + } + + + + PrintSrcDirEntryListBackward( dir_entry_list_head, NULL ); + } + (void)ClearDirEntryList( &dir_entry_list_head ); + + } + else { + mprintf("insert SD card\n"); + } + } + else if ( keyData & PAD_BUTTON_B ) { + /* ユーザーデータ書き込みモード */ + if( TRUE == LoadWlanConfigFile("sdmc:/wlan_cfg.txt") ) { + OS_TPrintf("SSID = %s\n", GetWlanSSID()); + OS_TPrintf("MODE = "); + switch( GetWlanMode() ) { + case 1: + OS_TPrintf("OPEN\n"); + break; + case 2: + OS_TPrintf("WEP128\n"); + break; + case 3: + OS_TPrintf("WPA-TKIP\n"); + break; + case 4: + OS_TPrintf("WPA2-TKIP\n"); + break; + case 5: + OS_TPrintf("WPA-AES\n"); + break; + case 6: + OS_TPrintf("WPA2-AES\n"); + break; + defalut: + OS_TPrintf("Unknow mode..\n"); + break; + } + OS_TPrintf("KEY STR = %s\n", GetWlanKEYSTR()); + { + u8 buf[256]; + int len; + int i; + len = GetWlanKEYBIN(buf); + if( len ) { + OS_TPrintf("KEY BIN = 0x"); + for( i = 0 ; i < len ; i++ ) { + OS_TPrintf("%02X",buf[i]); + } + OS_TPrintf("\n"); + } + } + } + else { + OS_TPrintf("Invalid wlan cfg file\n"); + } + nuc_main(); + + + } + else if ( keyData & PAD_BUTTON_START ) { + } + else if ( keyData & PAD_BUTTON_SELECT ) { + } + else if ( keyData & PAD_BUTTON_X ) { + OS_TPrintf("stream on\n"); + if( TRUE == stream_is_play1_end() ) { + stream_play1(); + } + } + else if ( keyData & PAD_BUTTON_Y ) { + } + else if ( keyData & PAD_KEY_UP ) { + n = m_get_display_offset_y(tc[0]); + n++; + m_set_display_offset_y(tc[0], n); + } + else if ( keyData & PAD_KEY_DOWN ) { + n = m_get_display_offset_y(tc[0]); + n--; + m_set_display_offset_y(tc[0], n); + } + else if ( keyData & PAD_KEY_RIGHT ) { + n = m_get_display_offset_x(tc[0]); + n++; + m_set_display_offset_x(tc[0], n); + } + else if ( keyData & PAD_KEY_LEFT ) { + n = m_get_display_offset_x(tc[0]); + n--; + m_set_display_offset_x(tc[0], n); + } + + + // mfprintf(tc[1], "\f\ncounter = %d\n\n", loop_counter); + mfprintf(tc[1], "\f\n%4d/%02d/%02d %02d:%02d:%02d\n\n", + rtc_date.year + 2000, rtc_date.month , rtc_date.day, + rtc_time.hour , rtc_time.minute , rtc_time.second ); + + mfprintf(tc[1], "press A -> Store to SD Card\n"); + mfprintf(tc[1], "press B -> Load to NAND Flash\n"); + mfprintf(tc[1], "\n"); + + + + + loop_counter++; + + } + OS_Terminate(); +} + + +/*====== End of main.c ======*/ + diff --git a/build/tools/sctools/copy_org/Makefile b/build/tools/sctools/copy_org/Makefile new file mode 100644 index 0000000..4d2fe34 --- /dev/null +++ b/build/tools/sctools/copy_org/Makefile @@ -0,0 +1,90 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: TwlSDK - demos - MI - ndma-1 +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Date:: 2008-01-17#$ +# $Rev: 3650 $ +# $Author: okubata_ryoma $ +#---------------------------------------------------------------------------- +SUBDIRS = ./banner + +TARGET_PLATFORM := TWL +TWL_ARCHGEN := LIMITED + +SRCDIR = ../common/src ./src + +SRCS = main.c key.c font.c text.c mprintf.c logprintf.c \ + gfx.c hwi.c mynvram.c my_fs_util.c \ + kpsc.cpp ecdl.cpp hatamotolib.cpp \ + sitedefs.c wcm_control.c netconnect.c mywlan.c \ + nuc.c nuc_error_msg.c stream.c + +TARGET_BIN = copy_org.srl +ROM_SPEC = copy_org.rsf + + +ES_DIR = $(ROOT)/add-ins/es +ES_LATEST = $(shell ls -d $(ES_DIR)/es-sdk-*|sort|tail -n 1) +ES_ROOT = $(ES_LATEST) + +LINCLUDES = $(TWLSDK_ROOT)/build/libraries/lcfg/ARM9.TWL/include ../common/src \ + $(ES_ROOT)/twl/include $(ES_ROOT)/common/lib/ec/include + +LLIBRARY_DIRS += $(ES_ROOT)/twl/lib/$(TWL_BUILDTYPE) + + +LLIBRARIES += libecx$(TWL_LIBSUFFIX).a \ + liblcfg$(TWL_LIBSUFFIX).a \ + libnhttp.nssl$(TWL_LIBSUFFIX).a \ + libshr$(TWL_LIBSUFFIX).a \ + libnuc$(TWL_LIBSUFFIX).a \ + libnup$(TWL_LIBSUFFIX).a \ + libnam$(TWL_LIBSUFFIX).a \ + libes$(TWL_LIBSUFFIX).a \ + libsea$(TWL_LIBSUFFIX).a \ + libboc$(TWL_LIBSUFFIX).a \ + libsfs$(TWL_LIBSUFFIX).a \ + libna$(TWL_LIBSUFFIX).a \ + + +# libnhttp$(TWL_LIBSUFFIX).a \ + + +MAKEROM_ROMROOT = ../files +MAKEROM_ROMFILES = fanfare.32.wav + + +COMPONENT_NAME = armadillo +MAKEROM_ARM7_BASE = $(TWL_COMPONENTSDIR)/$(COMPONENT_NAME)/$(TWL_BUILDTYPE_ARM7)/$(COMPONENT_NAME) +MAKEROM_ARM7 = $(MAKEROM_ARM7_BASE).$(TWL_ELF_EXT) + + +include $(TWLWIFI_ROOT)/build/buildtools/commondefs +include $(TWLSDK_ROOT)/build/buildtools/commondefs + + +#---------------------------------- +# セキュアアプリ指定 + +MAKEROM := $(TWL_TOOLSDIR)/bin/makerom.TWL.secure.exe + + +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- + +do-build: $(TARGETS) + +#---------------------------------------------------------------------------- + +include $(TWLWIFI_ROOT)/build/buildtools/modulerules +include $(TWLSDK_ROOT)/build/buildtools/modulerules diff --git a/build/tools/sctools/copy_org/banner/Makefile b/build/tools/sctools/copy_org/banner/Makefile new file mode 100644 index 0000000..4f2a5fc --- /dev/null +++ b/build/tools/sctools/copy_org/banner/Makefile @@ -0,0 +1,59 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: TwlSDK - nandApp - demos - SubBanner +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Date:: 2008-06-27#$ +# $Rev: 6887 $ +# $Author: nakasima $ +#---------------------------------------------------------------------------- +TARGET_PLATFORM = TWL + +include $(TWLSDK_ROOT)/build/buildtools/commondefs +MAKEBANNER = $(TWL_TOOLSDIR)/bin/makebanner.TWL.exe + +ICON_DIR = ./icon + +BANNER_ICON = $(ICON_DIR)/gameIcon.bmp +BANNER_SPEC = banner_v3.bsf + +SUB_BANNER_ICON = $(ICON_DIR)/subIcon.bmp +SUB_BANNER_SPEC = sub_banner_v3.bsf + + +TARGETS = banner.bnr +SUB_TARGETS = sub_banner.bnr +INSTALL_DIR = ./ +INSTALL_TARGETS = $(TARGETS) $(SUB_TARGETS) + +BANNER_ICON_NAME = $(basename $(BANNER_ICON)) +BANNER_ICON_MIDDLE = $(addprefix $(BANNER_ICON_NAME), .nbfs .nbfc .nbfp) +SUB_BANNER_ICON_NAME = $(basename $(SUB_BANNER_ICON)) +SUB_BANNER_ICON_MIDDLE = $(addprefix $(SUB_BANNER_ICON_NAME), .nbfs .nbfc .nbfp) + +LDIRT_CLEAN = $(TARGETS) $(SUB_TARGETS) \ + $(BANNER_ICON_MIDDLE) \ + $(SUB_BANNER_ICON_MIDDLE) \ + $(TARGETS:.bnr=.srl) + +include $(TWLSDK_ROOT)/build/buildtools/modulerules + +#---------------------------------------------------------------------------- +# build +#---------------------------------------------------------------------------- +do-build: $(TARGETS) $(SUB_TARGETS) + +$(TARGETS): $(BANNER_SPEC) $(BANNER_ICON) $(BANNER_ICON_MIDDLE) + $(MAKEBANNER) -N $(BANNER_ICON_NAME) $(BANNER_SPEC) $(TARGETS) + +$(SUB_TARGETS): $(SUB_BANNER_SPEC) $(SUB_BANNER_ICON) $(SUB_BANNER_ICON_MIDDLE) + $(MAKEBANNER) -s -N $(SUB_BANNER_ICON_NAME) $(SUB_BANNER_SPEC) $(SUB_TARGETS) +# diff --git a/build/tools/sctools/copy_org/banner/banner_v3.bsf b/build/tools/sctools/copy_org/banner/banner_v3.bsf new file mode 100644 index 0000000000000000000000000000000000000000..8e54be0a3997f10f8ede1e2efe38741bca4761d4 GIT binary patch literal 788 zcmc(dOK-wJ420)OCH})ooXe|H4?Thi4^;>?ZSM;K5s8;bDB{mUKX2$YxfQLnu{~aA z#`f*nP*1TUIX#Et@V$1NrH({n9k3Qyky352>v~e-o?X28pS$?4)2G%j$zZg)GjiJM zL?kCRhoRFM?kRqbx5H1vz2Kz>$%tC+7qudkbo8os4IZk@)P2Lt*rnRT_YHP#GZ*<_?nVLZFNJ=K+{q@(nl#iM1NWRF}*6o;|hNL3Vx#syqNyq4&-RJEzQ_S tAEunptvP*h%-H6BfNhI&re8<05;e)(fO!4TVcQl@%=)<{&ngzxe*quvXXyX{ literal 0 HcmV?d00001 diff --git a/build/tools/sctools/copy_org/banner/icon/gameIcon.bmp b/build/tools/sctools/copy_org/banner/icon/gameIcon.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d094481d115037587ecd84f0e8e9677c74debf7d GIT binary patch literal 630 zcmb7>y^VuF5QX1aZeVkyz3?P*3OFuxKnZj}L_kgjlt34BKoRs{B;p&tnf)2v7#R!ynp#s6a~t%L{(L&>l#hdplw@pU58;9FpeXp zX~I0uSe6Crx?#P6xq*wko`#@i~GY2>`7B!11Y2|-nk?gGvLIrOC<5m z5h@sq*Co8p3{01-7YBA6s7*dH8`y>MIX!?Yx7f_7U7Z7{HxR-#{BlSotd9$)$wXKP0^U zDWUgE!r5;LQc3{*KfxTO6n83w5EXUf%4AJy=tNVMc~s%bVhkM>+{oBYyc${)#Jclf z+K{j{l~9;Dpz`NG6ox2FsVJC>QhLQzT+UULtlXhhe95Q>^Ie&U#}s%r6W%3j^x z8aJywy{I8~YwfzTHD=X5vR0Y|EnauG&sn2y6w9O(BiEwQx!Kqkf7z$>qs0hWE?0D{ zclln}v7U!Jw5;p#Nh{z!?bw@SK&!}NBk1}y&v`)x&8m@d;QOE5_nW&c<0$;XA9e0y z7Y&Ma?B$fdx#wb=DIZ^cIPLPbhix{RMm!W7{fmHe&vmTF%!osAJh}*i)bK3fpU2@i!h!2_6JTSkmlys02hFs?_L@`RVkPoEO}0ghB3+yDRo literal 0 HcmV?d00001 diff --git a/build/tools/sctools/copy_org/banner/sub_banner_v3.bsf b/build/tools/sctools/copy_org/banner/sub_banner_v3.bsf new file mode 100644 index 0000000000000000000000000000000000000000..2fd176b00d75decec4e62183a2f7fb8e5c0a4fec GIT binary patch literal 864 zcmc(e!AiqG5QhJ&g5V=8^sKH39(q(7Td^Wd^`2~68fdE|(c;tiKAyb$A|6EjcDDx= z3OOkQ!_LgV|Nb+Xe13FvqEv~D_JSGWOiOH{ifpPoPL5NeQghBb+E!_?Lk(p0VxR51eVY#LtLxj{{AuJ-Jzf4ljbZe2NtutsCRhqA?4Szsa f2iS`5Qh_S84{}Z=(*s}2PdFpzs4u{I#>oB! +#include +#include "ecdl.h" +#include +#include +#include +#include + +#include "font.h" +#include "text.h" +#include "mprintf.h" +#include "gfx.h" +#include "key.h" +#include "my_fs_util.h" +#include "mynvram.h" + +#include "stream.h" + +#include "hwi.h" + +#include "hatamotolib.h" +#include "ecdl.h" + +#include "mywlan.h" + +#include "nuc.h" + + + +//#define DEBUG_PRINT 1 + +//================================================================================ + +static FSEventHook sSDHook; +static BOOL sd_card_flag = FALSE; + + +static void SDEvents(void *userdata, FSEvent event, void *arg) +{ + (void)userdata; + (void)arg; + if (event == FS_EVENT_MEDIA_REMOVED) { + sd_card_flag = FALSE; + mprintf("sdmc:removed!\n"); + } + else if (event == FS_EVENT_MEDIA_INSERTED) { + sd_card_flag = TRUE; + mprintf("sdmc:inserted!\n"); + } +} + +typedef struct { + RTCDate rtc_date; + RTCTime rtc_time; + BOOL shop_record_flag; + u8 movableUniqueID[ LCFG_TWL_HWINFO_MOVABLE_UNIQUE_ID_LEN ]; // 移行可能なユニークID 16byte +} MyData; + +static MyData mydata; + +static int vram_num_main = 1; +static int vram_num_sub = 0; + +void TwlMain(void) +{ + void* newArenaLo; + OSHeapHandle hHeap; + u16 keyData; + int loop_counter = 0; + int save_dir_info = 0; + MY_DIR_ENTRY_LIST *dir_entry_list_head = NULL; + RTCDate rtc_date; + RTCTime rtc_time; + LCFGTWLHWNormalInfo hwn_info; + LCFGTWLHWSecureInfo hws_info; + int i; + int n; + char path_base[256]; + char path_log[256]; + char path[256]; + u8 macAddress[6]; + + OS_Init(); + OS_InitThread(); + + OS_InitTick(); + OS_InitAlarm(); + + + // マスター割り込みフラグを許可に + (void)OS_EnableIrq(); + + // IRQ 割り込みを許可します + (void)OS_EnableInterrupts(); + + + Gfx_Init(); + + /* + 0 -> black + 1 -> red + 2 -> green + 3 -> blue + 4 -> yellow + 5 -> purple + 6 -> sky blue + 7 -> red + 8 -> green + 9 -> blue + 0xA -> yellow + 0xB -> purple + 0xC -> sky blue + 0xD -> white + 0xE -> white + 0xF -> white + + */ + // m_set_palette(tc[0], 0xF); + + + // ARM7との通信FIFO割り込み許可 + (void)OS_EnableIrqMask(OS_IE_SPFIFO_RECV); + + // ファイルシステム初期化 + FS_Init( FS_DMA_NOT_USE ); + + + // メインアリーナのアロケートシステムを初期化 + newArenaLo = OS_InitAlloc(OS_ARENA_MAIN, OS_GetMainArenaLo(), OS_GetMainArenaHi(), 1); + OS_SetMainArenaLo(newArenaLo); + + // メインアリーナ上にヒープを作成 + hHeap = OS_CreateHeap(OS_ARENA_MAIN, OS_GetMainArenaLo(), OS_GetMainArenaHi()); + OS_SetCurrentHeap(OS_ARENA_MAIN, hHeap); + + RTC_Init(); + + SCFG_Init(); + + NVRAMi_Init(); + + SND_Init(); + stream_main(); + + // 必須;SEA の初期化 + SEA_Init(); + + // 不要:NAM の初期化 + // NAM_Init(&AllocForNAM, &FreeForNAM); + + // 必須:ES の初期化 + ES_InitLib(); + + + if( FALSE == MiyaReadHWNormalInfo( &hwn_info ) ) { + mprintf("HW Normal Info. read error\n"); + } + else { + mprintf("HW Normal Info. read succeeded.\n"); + mprintf("UniqueID\n 0x"); + for( i = 0; i < LCFG_TWL_HWINFO_MOVABLE_UNIQUE_ID_LEN/2 ; i++ ) { + mprintf("%02X:", hwn_info.movableUniqueID[i]); + } + mprintf("\n 0x"); + for( ; i < LCFG_TWL_HWINFO_MOVABLE_UNIQUE_ID_LEN ; i++ ) { + mprintf("%02X:", hwn_info.movableUniqueID[i]); + } + mprintf("\n"); + // mprintf(" RTC Adjust data = 0x%02x\n", hwn_info.rtcAdjust ); + } + + mprintf("\n"); + + if( FALSE == MiyaReadHWSecureInfo( &hws_info ) ) { + mprintf("HW Secure Info. - read failed\n"); + } + else { + mprintf("HW Secure Info. - read succeeded.\n"); + + mprintf(" Serial No.\n 0x"); + for( i = 0; i < LCFG_TWL_HWINFO_SERIALNO_LEN_MAX/2 ; i++ ) { + mprintf("%02X:", hws_info.serialNo[i]); + } + mprintf("\n 0x"); + for( ; i < LCFG_TWL_HWINFO_SERIALNO_LEN_MAX ; i++ ) { + if( hws_info.serialNo[i] ) { + mprintf("%02X:", hws_info.serialNo[i]); + } + else { + // #define LCFG_TWL_HWINFO_SERIALNO_LEN_MAX 15 // 本体シリアルNo.長Max(終端付きなので、14bytesまで拡張 + // 終端をみつけたらブレーク + break; + } + } + mprintf("\n"); + mprintf("%s\n", hws_info.serialNo); + + mprintf(" validLang.bmp = 0x%08x\n", hws_info.validLanguageBitmap ); + mprintf(" wifi disable flag = %d\n", hws_info.flags.forceDisableWireless ); + mprintf(" lchr-TitleIDLo = " ); + for( i = 0 ; i < 4 ; i++ ) { + mprintf("%02X:", hws_info.launcherTitleID_Lo[i]); + } + mprintf("\n Region data = 0x%02x\n\n", hws_info.region ); + } + + + + + OS_GetMacAddress( macAddress ); + mprintf("macAddress "); + for ( i = 0 ; i < 6 ; i++ ) { + mprintf("%02X:", macAddress[i]); + } + mprintf("\n"); + + + if( FALSE == SDCardValidation() ) { + sd_card_flag = FALSE; + mprintf("No SD Card\n"); + } + else { + sd_card_flag = TRUE; + mprintf("Detect SD Card\n"); + } + + FS_RegisterEventHook("sdmc", &sSDHook, SDEvents, NULL); + + + STD_StrCpy( path_base , "sdmc:/" ); + STD_StrCat( path_base , (const char *)hws_info.serialNo ); + STD_StrCat( path_base , "/" ); + + + + + while( 1 ) { + OS_WaitVBlankIntr(); + Gfx_Render( vram_num_main , vram_num_sub ); + (void)RTC_GetTime( &rtc_time ); + + keyData = m_get_key_trigger(); + + // ARM7コマンド応答受信 + while (SND_RecvCommandReply(SND_COMMAND_NOBLOCK) != NULL) + { + } + // コマンドフラッシュ(フラッシュして即座に実行を要求) + (void)SND_FlushCommand(SND_COMMAND_NOBLOCK | SND_COMMAND_IMMEDIATE); + + + if ( keyData & PAD_BUTTON_R ) { + } + else if ( keyData & PAD_BUTTON_L ) { + } + else if ( keyData & PAD_BUTTON_A ) { + /* ユーザーデータ吸出しモード */ + if( sd_card_flag == TRUE ) { + + mprintf("BACKUP to SD Card\n"); + /************************************/ + + /* ショップの履歴確認 */ + if( TRUE == CheckShopRecord(NULL) ) { + mydata.shop_record_flag = TRUE; + } + else { + mydata.shop_record_flag = FALSE; + mprintf("no shop record\n"); + } + + + + /* 日時の保存 */ + STD_StrCpy( path , path_base ); + STD_StrCat( path , "twl_rtc.dat" ); + if( RTC_RESULT_SUCCESS != RTC_GetDate( &rtc_date ) ) { + mprintf("rtc read date error.\n"); + } + if( RTC_RESULT_SUCCESS != RTC_GetTime( &rtc_time ) ) { + mprintf("rtc read time error.\n"); + } + + STD_CopyMemory( (void *)&(mydata.rtc_date), (void *)&rtc_date, sizeof(RTCDate) ); + STD_CopyMemory( (void *)&(mydata.rtc_time), (void *)&rtc_time, sizeof(RTCTime) ); + STD_CopyMemory( (void *)(mydata.movableUniqueID), (void *)hwn_info.movableUniqueID, LCFG_TWL_HWINFO_MOVABLE_UNIQUE_ID_LEN ); + if( TRUE == MydataSave(path, (void *)&mydata, sizeof(MyData), NULL) ) { + } + + + + /* nand:/sysディレクトリまわりの保存 */ + STD_StrCpy( path , path_base ); + STD_StrCat( path , "twl_ninfo.dat" ); + if( TRUE == MiyaBackupHWNormalInfo( path ) ) { + mprintf("HWInfo Normal backup completed.\n"); + } + + /* Wifi設定の保存 */ + STD_StrCpy( path , path_base ); + STD_StrCat( path , "twl_nor.bin" ); + if( TRUE == nvram_backup( path ) ) { + mprintf("nvram backup completed.\n"); + } + + /* nand:/shared1ディレクトリまわりの保存 */ + STD_StrCpy( path , path_base ); + STD_StrCat( path , "twl_cfg.dat" ); + if( TRUE == MiyaBackupTWLSettings( path ) ) { + mprintf("TWL CFG backup completed.\n"); + } + + + /* nand:/shared2ディレクトリまわりの保存 */ + STD_StrCpy( path , path_base ); + STD_StrCat( path , "shared2" ); + STD_StrCpy( path_log , path_base ); + STD_StrCat( path_log , "shared2.txt" ); + + if( 0 == copy_r( &dir_entry_list_head, path, "nand:/shared2" , path_log ) ) { + mprintf("copy_r shared2 completed.\n"); + PrintDirEntryListBackward( dir_entry_list_head, NULL ); + STD_StrCpy( path , path_base ); + STD_StrCat( path , "shared2.lst" ); + SaveDirEntryList( dir_entry_list_head, path ); + } + (void)ClearDirEntryList( &dir_entry_list_head ); + + + /* nand2:/photoディレクトリまわりの保存 */ + STD_StrCpy( path , path_base ); + STD_StrCat( path , "photo" ); + STD_StrCpy( path_log , path_base ); + STD_StrCat( path_log , "photolog.txt" ); + + if( 0 == copy_r( &dir_entry_list_head, path , "nand2:/photo" , path_log ) ) { + mprintf("copy_r photo completed.\n"); + PrintDirEntryListBackward( dir_entry_list_head, NULL ); + STD_StrCpy( path , path_base ); + STD_StrCat( path , "photo.lst" ); + SaveDirEntryList( dir_entry_list_head, path ); + } + (void)ClearDirEntryList( &dir_entry_list_head ); + + /* nand:/ticketはチケット同期?でうまいこと合わせてくれるんでバックアップ不要 */ + + /* nand:/titleディレクトリまわりの保存 */ + /* **.savファイルをすべてバックアップ */ + + STD_StrCpy( path , path_base ); + STD_StrCat( path , "title" ); + STD_StrCpy( path_log , path_base ); + STD_StrCat( path_log , "titlelog.txt" ); + + if( 0 == find_title_save_data( &dir_entry_list_head, path , "nand:/title", &save_dir_info, path_log ) ) { + mprintf("find_title_save_data completed.\n"); + PrintDirEntryListBackward( dir_entry_list_head, NULL ); + STD_StrCpy( path , path_base ); + STD_StrCat( path , "title.lst" ); + + SaveDirEntryList( dir_entry_list_head , path ); + } + (void)ClearDirEntryList( &dir_entry_list_head ); + + + + /* タイトルリストの生成 */ + /* + nand:/title/00030004/ + + nand:/title/00030004/34626241/content/title.tmd + nand:/title/00030004/34626241/content/00000000.app + nand:/title/00030004/34626241/data/ + + nand:/title/00030017/484e4141 は ランチャー + nand:/title/00030015/484e4641 は shop + nand:/title/00030015/484e4241 は 本体設定 + + + No. 0 0003000f484e4c41 + No. 1 0003000f484e4841 + No. 2 0003000f484e4341 + No. 3 00030015484e4241 + No. 4 00030017484e4141 launcher + ^ + | ここの最下位ビットが1のやつがシステムアプリ + | + システムアプリはダウンロード対象外 + */ + + STD_StrCpy( path_log , path_base ); + STD_StrCat( path_log , "title2log.txt" ); + if( 0 == get_title_id( &dir_entry_list_head, "nand:/title", &save_dir_info, path_log ) ) { + mprintf("get_title_id completed.\n"); + + // STD_StrCpy( path , path_base ); + // STD_StrCat( path , "titlelist.txt" ); + { + void *pBuffer; + int count; + int i,j; + char *ptr; + GetDirEntryList( dir_entry_list_head, &pBuffer, &count); + OS_TPrintf("count = %d\n", count ); + ptr = (char *)pBuffer; + + if( ptr != NULL && count != 0 ) { + for( j = 0 ; j < count ; j++ ) { + OS_TPrintf("No. %d ",j); + for( i = 0 ; i < 16 ; i++ ) { + OS_TPrintf("%c", *ptr); + ptr++; + } + OS_TPrintf("\n"); + } + OS_Free(pBuffer); + } + } + + + + PrintSrcDirEntryListBackward( dir_entry_list_head, NULL ); + } + (void)ClearDirEntryList( &dir_entry_list_head ); + + } + else { + mprintf("insert SD card\n"); + } + } + else if ( keyData & PAD_BUTTON_B ) { + /* ユーザーデータ書き込みモード */ + if( TRUE == LoadWlanConfigFile("sdmc:/wlan_cfg.txt") ) { + OS_TPrintf("SSID = %s\n", GetWlanSSID()); + OS_TPrintf("MODE = "); + switch( GetWlanMode() ) { + case 1: + OS_TPrintf("OPEN\n"); + break; + case 2: + OS_TPrintf("WEP128\n"); + break; + case 3: + OS_TPrintf("WPA-TKIP\n"); + break; + case 4: + OS_TPrintf("WPA2-TKIP\n"); + break; + case 5: + OS_TPrintf("WPA-AES\n"); + break; + case 6: + OS_TPrintf("WPA2-AES\n"); + break; + defalut: + OS_TPrintf("Unknow mode..\n"); + break; + } + OS_TPrintf("KEY STR = %s\n", GetWlanKEYSTR()); + { + u8 buf[256]; + int len; + int i; + len = GetWlanKEYBIN(buf); + if( len ) { + OS_TPrintf("KEY BIN = 0x"); + for( i = 0 ; i < len ; i++ ) { + OS_TPrintf("%02X",buf[i]); + } + OS_TPrintf("\n"); + } + } + } + else { + OS_TPrintf("Invalid wlan cfg file\n"); + } + nuc_main(); + + + } + else if ( keyData & PAD_BUTTON_START ) { + } + else if ( keyData & PAD_BUTTON_SELECT ) { + } + else if ( keyData & PAD_BUTTON_X ) { + OS_TPrintf("stream on\n"); + if( TRUE == stream_is_play1_end() ) { + stream_play1(); + } + } + else if ( keyData & PAD_BUTTON_Y ) { + } + else if ( keyData & PAD_KEY_UP ) { + n = m_get_display_offset_y(tc[0]); + n++; + m_set_display_offset_y(tc[0], n); + } + else if ( keyData & PAD_KEY_DOWN ) { + n = m_get_display_offset_y(tc[0]); + n--; + m_set_display_offset_y(tc[0], n); + } + else if ( keyData & PAD_KEY_RIGHT ) { + n = m_get_display_offset_x(tc[0]); + n++; + m_set_display_offset_x(tc[0], n); + } + else if ( keyData & PAD_KEY_LEFT ) { + n = m_get_display_offset_x(tc[0]); + n--; + m_set_display_offset_x(tc[0], n); + } + + + // mfprintf(tc[1], "\f\ncounter = %d\n\n", loop_counter); + mfprintf(tc[1], "\f\n%4d/%02d/%02d %02d:%02d:%02d\n\n", + rtc_date.year + 2000, rtc_date.month , rtc_date.day, + rtc_time.hour , rtc_time.minute , rtc_time.second ); + + mfprintf(tc[1], "press A -> Store to SD Card\n"); + mfprintf(tc[1], "press B -> Load to NAND Flash\n"); + mfprintf(tc[1], "\n"); + + + + + loop_counter++; + + } + OS_Terminate(); +} + + +/*====== End of main.c ======*/ + diff --git a/build/tools/sctools/files/fanfare.32.wav b/build/tools/sctools/files/fanfare.32.wav new file mode 100644 index 0000000000000000000000000000000000000000..eb6cc56b11dfbbcb887c2a6cfade45870cccdd30 GIT binary patch literal 229586 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%0D|3$H?wWnp>cX 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 nKaf9H