diff --git a/CMakeLists.txt b/CMakeLists.txt index 53c1d51b..e88fd3a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -164,5 +164,6 @@ add_subdirectory(pt) # depends on ghttp add_subdirectory(sake) # depends on common and ghttp. test relies on GP and gt2 for UDPEngine add_subdirectory(webservices) add_subdirectory(sc) # depends on common webservices ghttp +add_subdirectory(Direct2Game) add_subdirectory(Voice2) add_subdirectory(sharedDll) diff --git a/Direct2Game/CMakeLists.txt b/Direct2Game/CMakeLists.txt new file mode 100644 index 00000000..9b4509d5 --- /dev/null +++ b/Direct2Game/CMakeLists.txt @@ -0,0 +1,21 @@ +add_library(usd2g + d2gDeserialize.c + d2gDeserialize.h + d2gDownloads.c + d2gDownloads.h + d2gMain.c + d2gMain.h + d2gServices.c + d2gServices.h + d2gUtil.c + d2gUtil.h + Direct2Game.h +) + +target_link_libraries(usd2g PUBLIC ushttp uswebservice) + +target_compile_definitions(usd2g PRIVATE -DGHTTP_EXTENDEDERROR) + +if (UNISPY_BUILD_TESTS) + add_subdirectory(Direct2GameTest) +endif() diff --git a/Direct2Game/Direct2Game.h b/Direct2Game/Direct2Game.h new file mode 100644 index 00000000..d2e8e742 --- /dev/null +++ b/Direct2Game/Direct2Game.h @@ -0,0 +1,1900 @@ +// GameSpy Direct2Game SDK +// This SDK is designed and developed by GameSpy Tech. +// Copyright (c) 2008, GameSpy Technology + +#ifndef __GS_DIRECT2GAME_H__ +#define __GS_DIRECT2GAME_H__ +/////////////////////////////////////////////////////////////////////////////// +// This header file should be used as a reference for the Direct2Game interface +// All structures and SDK API functions are defined in this file. + +#include "../ghttp/ghttpSoap.h" +#include "../common/gsCrypt.h" +#include "../common/gsLargeInt.h" +#include "../common/gsResultCodes.h" +#include "../webservices/AuthService.h" + +#if defined(__cplusplus) +extern "C" +{ +#endif + + // Defaults +#define GS_D2G_DEFAULT_CATALOG_REGION L"global" +#define GS_D2G_DEFAULT_CATALOG_VERSION 0 +#define GS_D2G_DEFAULT_CULTURECODE L"en-US" +#define GS_D2G_DEFAULT_CURRENCYCODE L"USD" + +// Summary +// D2GResultCode enum contains the error codes +// which might be returned by the D2G SDK API calls. +// +// Remarks +// To obtain these codes for use in implementation, Please use the +// macros associated with GSResult. They can be found in gsResultCodes.h +// Example +// To obtain the D2GResultCode from a GSResult, call: +// +// GSResult result; +// ... +// D2GResultCode d2gCode = GS_RESULT_CODE(result); +// +typedef enum D2GResultCode +{ + /// account problems + D2GResultCode_InsufficientFunds, + D2GResultCode_NoPaymentMethodFound, + + /// store availability + D2GResultCode_StoreOnline, + D2GResultCode_StoreOfflineForMaintenance, + D2GResultCode_StoreOfflineRetired, + D2GResultCode_StoreNotYetLaunched, + + /// Order + D2GResultCode_Order_Ok, + D2GResultCode_Order_DuplicateItemIds, + D2GResultCode_Order_OrderInitiateFailed, + D2GResultCode_Order_UserNotAuthorized, + D2GResultCode_Order_UserNotFound, + D2GResultCode_Order_UserAccountNotFound, + D2GResultCode_Order_OrderItemFailed, + D2GResultCode_Order_BillingFailed, + D2GResultCode_Order_BillingPending, + D2GResultCode_Order_BillingRequestNotFound, + D2GResultCode_Order_InsufficientFunds, + D2GResultCode_Order_RequiredFieldNotSet, + D2GResultCode_Order_UnexepectedError, + D2GResultCode_Order_UnknownResultCode, + + /// order item + D2GResultCode_OrderItem_Ok, + D2GResultCode_OrderItem_CountryRestriction, + D2GResultCode_OrderItem_AgeRestriction, + D2GResultCode_OrderItem_QuantityRestriction, + D2GResultCode_OrderItem_ItemNotAuthorized, + D2GResultCode_OrderItem_ItemDoesNotExist, + D2GResultCode_OrderItem_PriceChanged, + D2GResultCode_OrderItem_ItemRefunded, + D2GResultCode_OrderItem_VariantDoesNotExist, + D2GResultCode_OrderItem_UnexepectedError, + D2GResultCode_OrderItem_UnspecificedRestriction, + D2GResultCode_OrderItem_UnknownResultCode, + + /// download + D2GResultCode_File_NotOwnedByUser, + + /// catalog + D2GResultCode_Catalog_Empty, + D2GResultCode_Catalog_MaxCategoryNumReached, + + /// extra info + D2GResultCode_ExtraInfo_Empty, + D2GResultCode_ExtraInfo_KeyNotFound, + + D2GResultCode_Max +} D2GResultCode; + +// Summary +// Sort direction parameters for the d2g sort functions +typedef enum D2GSortDirection +{ + D2GSort_Ascending, + D2GSort_Descending +} D2GSortDirection; + +// Summary +// Used in d2gContentUpdatesCallback. +// Each enum indicates content update type. +typedef enum D2GContentUpdateType +{ + D2GContentNew = 10, + D2GContentUpdated = 20, + D2GContentRemoved = 30 +} D2GContentUpdateType; + +// Summary +// Used in d2gContentUpdatesCallback. +// Each enum indicates how the application should proceed +// when installing an update. +typedef enum D2GDownloadType +{ + D2GDownloadForced = 0, + D2GDownloadMandatory = 1, + D2GDownloadOptional = 2 +} D2GDownloadType; + + +///Type Definitions + +// Summary +// D2GInstancePtr is a reference to D2G SDK Instance. +// This pointer has to be maintained throughout the life time of the SDK. +typedef void* D2GInstancePtr; + +// Summary +// D2GCatalogPtr is used by the SDK to get a presence of a specific store. +// It is a reference to a unique identifier based on game id, region, version, +// and access token. +typedef void* D2GCatalogPtr; + +///Basic Types + +typedef gsi_u32 D2GItemId; // a 32-bit number to uniquely identify an item +typedef gsi_u32 D2GUrlId; // a 32-bit number to uniquely identify a downloadable item +typedef gsi_u32 D2GFileId; // a 32-bit number which uniquely identifies a file +typedef gsi_u32 D2GPackageId; // a 32-bit number used to identify an item in a DRM solution + +/// Base Structures + +// Summary +// D2GBasicItemInfo contains the basic information about an item +// that exists on the backend. +// It is part of D2GOrderItem and D2GCatalogItem structures. +typedef struct D2GBasicItemInfo +{ + D2GItemId mItemId; // unique item id. + UCS2String mExternalItemCode; // unique external item id. + UCS2String mName; // the name of the item. + UCS2String mPrice; // the unit price of the item in string format + UCS2String mTax; // the tax for the unit price for this particular item +} D2GBasicItemInfo; + +// Summary +// D2GGeoInfo contains the region information associated with an item. Used +// by D2GOrderInfo and D2GCatalogItem. +typedef struct D2GGeoInfo +{ + UCS2String mCurrencyCode; // The currency set up for the current store + UCS2String mCultureCode; // A culture code associated with the catalog, e.g. "en-us". +} D2GGeoInfo; + + +/// Extra Info + +// Summary +// D2GExtraInfo contains a pair. +// This can be used by the developer to specify meta data for the store. +// The data this structure contains is configured on the backend admin site. +typedef struct D2GExtraInfo +{ + UCS2String mKey; + UCS2String mValue; +} D2GExtraInfo; + +// Summary +// D2GExtraInfoList contains a list of D2GExtraInfo objects +typedef struct D2GExtraInfoList +{ + gsi_u32 mCount; // Number of D2GExtraInfo items that mExtraInfoElements points to + D2GExtraInfo *mExtraInfoElements; // Actual D2GExtraInfo objects that contain the pairs +} D2GExtraInfoList; + +/// D2G Catalog Types + +// Summary +// Names of Images associated with a catalog item +typedef struct D2GImageList +{ + gsi_u32 mCount; // Number of images + UCS2String *mImageName; // A pointer to an array of names of an item's images. +} D2GImageList; + +// Summary +// Categories associated with a catalog item +typedef struct D2GCategoryList +{ + gsi_u32 mCount; // Number of categories. + UCS2String *mCategoryNames; // A pointer to the array of category names. +} D2GCategoryList; + +// Summary +// Product information of a catalog item +typedef struct D2GProductInfo +{ + UCS2String mPublisher; // Name of the publisher + UCS2String mDeveloper; // Name of the developer + UCS2String mSummary; // Summary as set by the developer + time_t mReleaseDate; // Date when item was released. + gsi_u32 mFileSize; // Total size of the files associated with the item in bytes. +} D2GProductInfo; + +// Summary +// D2GCatalogItem contains catalog information for an item. +typedef struct D2GCatalogItem +{ + D2GBasicItemInfo mItem; // Basic information about this product + D2GGeoInfo mGeoInfo; // Geographical information about this catalog item + D2GProductInfo mProductInfo; // Information about the publisher/developer associated with this item + D2GCategoryList mCategories; // Categories list that contain organizational information based on category + D2GImageList mImages; // Image list for image URLs + D2GExtraInfoList mExtraItemInfoList; // Extra Info list related to this specific item +} D2GCatalogItem; + +// Summary +// List of catalog items +typedef struct D2GCatalogItemList +{ + gsi_u32 mCount; // Number of items in the list + D2GCatalogItem *mCatalogItems; // A pointer to an array of catalog items. +} D2GCatalogItemList; + +///Accounts + +// Summary +// D2GCreditCardInfo contains the credit card information for the +// authenticated user. +// A credit card must exist with the user's account on the back end in order +// to use +// the purchasing functionality. +typedef struct D2GCreditCardInfo +{ + gsi_u32 mAccountId; // The unique account id used when ordering. + UCS2String mCreditCardType; // String for the credit card type. + time_t mExpirationDate; // the expiration date for the credit card. + gsi_bool mIsDefault; // true if this is the default card on the user's account. + int mLastFourDigits; // the last for digits of the credit card. +} D2GCreditCardInfo; + +// Summary +// D2GCreditCardInfoList is a container for a list of D2GCreditCardInfo +// associated with the user's account. It is part of the +// D2GGetUserCreditCardsResponse +typedef struct D2GCreditCardInfoList +{ + gsi_u32 mCount; // The number of credit card + D2GCreditCardInfo *mCreditCardInfos; // A pointer to the array which contains the credit cards. +} D2GCreditCardInfoList; + +/// D2G Order Item Types + +// Summary +// Order Validation used as part of an order to determine if the total order +// has been processed +// appropriately. It is part of D2GOrderItem and D2GOrderInfo structures. +typedef struct D2GOrderValidation +{ + gsi_bool mIsValid; // gsi_true, if order valid. gsi_false indicates that the order is invalid, and the items below need to be checked. + UCS2String mMessage; // The text message for validation sent from the back end if invalid. + GSResult mResult; // Set to a result with a D2GResultCode +} D2GOrderValidation; + +// Summary +// Top level order information that is contained by D2GOrderTotal and +// D2GOrderPurchase. +typedef struct D2GOrderInfo +{ + gsi_u32 mAccountId; // account id from Credit card that was used to make the purchase. + UCS2String mRootOrderGuid; // Root order ID that uniquely identifies an order + D2GGeoInfo mGeoInfo; // Culture and currency information for the order + D2GOrderValidation mValidation; // Validation for the entire order + UCS2String mSubTotal; // Subtotal for the entire order + UCS2String mTax; // Tax for the entire order + UCS2String mTotal; // Total price for the entire order +} D2GOrderInfo; + +// Summary +// D2GOrderItemTotal contains information about the quantity, subtotal, and +// total +// for a given item. It is contained by D2GOrderItem. +typedef struct D2GOrderItemTotal +{ + gsi_u32 mQuantity; + UCS2String mSubTotal; + UCS2String mTotal; +} D2GOrderItemTotal; + +// Summary +// D2GOrderItem is contained by D2GOrderPurchase, and D2GOrderTotal as a list. +// It contains some catalog information, pricing, and validation. +typedef struct D2GOrderItem +{ + D2GBasicItemInfo mItem; // Item information similar to the catalog + D2GOrderValidation mValidation; // Validation information for the given item. + D2GOrderItemTotal mItemTotal; // Pricing information for the given item. +} D2GOrderItem; + +// Summary +// List of order items of an Order Total. They are used to show +// the order total for each item. +typedef struct D2GOrderItemList +{ + gsi_u32 mCount; // Count of mOrderItems. + D2GOrderItem *mOrderItems; // Order item that contains per item total price information. +} D2GOrderItemList; + +// Summary +// D2GOrderTotal contains the order information contained in the +// D2GOrderTotalResponse. +// It is an input to the d2gStartOrder. +typedef struct D2GOrderTotal +{ + time_t mQuoteDate; // The date that the quote was given for an order. + D2GOrderInfo mOrder; // Top level order information + D2GOrderItemList mOrderItemList; // Items which are part of the order. See D2GOrderItemList. +} D2GOrderTotal; + +// Summary +// A License associated with a purchased item +typedef struct D2GLicenseItem +{ + UCS2String mLicenseKey; // the license key for the purchased item + UCS2String mLicenseName; // the name for the license. +} D2GLicenseItem; + +// Summary +// D2GLicenseItemList contains the List of licenses for a purchased item. +// If an item has more than one downloadable items, +// there may be more than one license associated with it. +// It is contained by the D2GOrderItemPurchase structure. +typedef struct D2GLicenseItemList +{ + gsi_u32 mCount; // The number of licenses in the list + D2GLicenseItem *mLicenses; // a pointer to the array which keeps the list of licenses. +} D2GLicenseItemList; + +// Summary +// D2GDownloadItem contains the download information of a purchased item. +//It is also used to start the download process. +typedef struct D2GDownloadItem +{ + D2GUrlId mUrlId; // the UrlId of this particular download + D2GFileId mFileId; // the File Id of this particular download + gsi_u32 mSequence; // Some downloads may require one file to be installed before another. This number indicates the order. + UCS2String mName; // A string which contains the name of the download. + UCS2String mAssetType; // The asset type indicates how the game will organize files, e.g. map, avitar. + float mVersion; // The version number for this download. +} D2GDownloadItem; + +// Summary +// D2GDownloadItemList contains a list of downloads for an item. +// This is contained by the D2GOrderItemPurchase. +typedef struct D2GDownloadItemList +{ + gsi_u32 mCount; // The number of download items in the mDownload array. + D2GDownloadItem *mDownloads; // The pointer to the array which contains a list of download items. +} D2GDownloadItemList; + +// Summary +// D2GOrderItemPurchaseList contains a list of item purchases that are available +// during the purchase process, and purchase history. +typedef struct D2GOrderItemPurchaseList +{ + gsi_u32 mCount; // Number of items in the list + struct D2GOrderItemPurchase *mOrderItemPurchases; // Pointer to the array of type D2GOrderItemPurchase +} D2GOrderItemPurchaseList; + +/// NOTE: D2GOrderItemPurchaseList needs to be defined first because its +// nested in D2GOrderItemPurchase + +// Summary +// D2GOrderItemPurchase contains the top level purchase information which in +// turn +// can be nested. mPurchaseList will be filled in with a count if there are +// nested items. +typedef struct D2GOrderItemPurchase +{ + D2GOrderItem mOrderItem; + D2GLicenseItemList mLicenseList; + D2GDownloadItemList mDownloadList; + D2GOrderItemPurchaseList mPurchaseList; +} D2GOrderItemPurchase; + +// Summary +// D2GOrderPurchase contains the overall purchase information. It is part of +// the D2GPurchaseHistory, D2GStartOrderResponse and D2GIsOrderCompleteResponse. +typedef struct D2GOrderPurchase +{ + time_t mPurchaseDate; // The date that the purchase was made. + D2GOrderInfo mOrder; // Top level order information + D2GOrderItemPurchaseList mItemPurchases; // List of items purchased +} D2GOrderPurchase; + +// Summary +// D2GPurchaseHistory contains the list of purchases made by the user. +typedef struct D2GPurchaseHistory +{ + gsi_u32 mCount; // Number of purchases + D2GOrderPurchase *mPurchases; // A pointer to an array of past purchases +} D2GPurchaseHistory; + +/// Check content updates + +// Summary +// D2GContentUpdate contains the content update information for an item. +typedef struct D2GContentUpdate +{ + D2GItemId mItemId; // Unique item id for the content update + D2GDownloadItem mDownloadItem; // Download information, see D2GDownloadItem + gsi_u32 mChangeType; // See D2GContentUpdateType + D2GDownloadType mDownloadType; // See D2GDownloadType +} D2GContentUpdate; + +// Summary +// D2GContentUpdateList contains a list of content updates. +// It is part of the D2GCheckContentUpdatesResponse. +typedef struct D2GContentUpdateList +{ + gsi_u32 mCount; // Number of downloaded items + D2GContentUpdate *mContentUpdates; // A pointer to an array of downloaded items. +}D2GContentUpdateList; + + +/// Responses to the services requests. + +// Summary +// The response message for an Extra Info Request +typedef struct D2GLoadExtraCatalogInfoResponse +{ + GHTTPResult mHttpResult; // HTTP Result + D2GExtraInfoList *mExtraCatalogInfoList; // See D2GExtraInfoList. This must be freed using d2gFreeExtraInfoList +} D2GLoadExtraCatalogInfoResponse; + +// Summary +// The response message for Get Store Availability Request +typedef struct D2GGetStoreAvailabilityResponse +{ + GHTTPResult mHttpResult; // HTTP Result + GSResult mAvailabilityCode; // A code returned by the back end. See D2GResultCode_Store... codes +} D2GGetStoreAvailabilityResponse; + +// Summary +// The response message for Load Catalog Items Request +typedef struct D2GLoadCatalogItemsResponse +{ + GHTTPResult mHttpResult; // HTTP Result + D2GCatalogItemList *mItemList; // See D2GCatalogItems. This must be freed using d2gFreeCatalogItems. +} D2GLoadCatalogItemsResponse; + +// Summary +// The response message for Load Catalog Items by Category Request +typedef struct D2GLoadCatalogItemsByCategoryResponse +{ + GHTTPResult mHttpResult; // HTTP Result + D2GCatalogItemList *mItemList; // See D2GCatalogItems. This must be freed using d2gFreeCatalogItems. +} D2GLoadCatalogItemsByCategoryResponse; + +// Summary +// The response message for Get User Credit Cards Request +#ifndef _WIN32 +#warning "WARNING: D2GGetUserCreditCardsResponse Not supported" +#endif +typedef struct D2GGetUserCreditCardsResponse +{ + GHTTPResult mHttpResult; // HTTP Result + D2GCreditCardInfoList *mListOfCreditCards; // See D2GCreditCardInfoList. This must be free using d2gFreeCreditCardInfoList. +} D2GGetUserCreditCardsResponse; + +// Summary +// The response message for Get Order Total Request +#ifndef _WIN32 +#warning "WARNING: D2GGetOrderTotalResponse Not supported" +#endif +typedef struct D2GGetOrderTotalResponse +{ + GHTTPResult mHttpResult; // HTTP Result + D2GOrderTotal *mOrderTotal; // See D2GOrderTotal. This must be freed using d2gFreeOrderTotal. +} D2GGetOrderTotalResponse; + +// Summary +// The response message for Start Order Request +#ifndef _WIN32 +#warning "WARNING: D2GStartOrderResponse Not supported" +#endif +typedef struct D2GStartOrderResponse +{ + GHTTPResult mHttpResult; // HTTP Result + D2GOrderPurchase *mOrderPurchase; // See D2GOrderPurchase. This must be freed using d2gFreeOrderPurchase. +} D2GStartOrderResponse; + +// Summary +// The response message for Is Order Complete Request +#ifndef _WIN32 +#warning "WARNING: D2GIsOrderCompleteResponse Not supported" +#endif +typedef struct D2GIsOrderCompleteResponse +{ + GHTTPResult mHttpResult; // HTTP Result + D2GOrderPurchase *mOrderPurchase; // See D2GOrderPurchase. This must be freed using d2gFreeOrderPurchase. +} D2GIsOrderCompleteResponse; + +// Summary +// The response message for Get Purchase History Request. +#ifndef _WIN32 +#warning "WARNING: D2GIsOrderCompleteResponse Not supported" +#endif +typedef struct D2GGetPurchaseHistoryResponse +{ + GHTTPResult mHttpResult; // HTTP Result + D2GPurchaseHistory *mPurchaseHistory; // See D2GPurchaseHistory. This must freed using d2gFreePurchaseHistory. +} D2GGetPurchaseHistoryResponse; + +// Summary +// The response message for Get Item Activation Request +// Used as part of an DRM solution. +#ifndef _WIN32 +#warning message("WARNING: D2GGetItemActivationResponse Not supported" +#endif +typedef struct D2GGetItemActivationResponse +{ + GHTTPResult mHttpResult; // HTTP Result + UCS2String mStatusMessage; // Status message from the back end. This must be freed using gsifree. + D2GItemId mItemId; // The unique item id. + UCS2String mActivationCode; // Activation code for the item. This must be freed using gsifree. +} D2GGetItemActivationResponse; + +// Summary +// The response message for Check Content Updates Request +#ifndef _WIN32 +#warning "WARNING: D2GCheckContentUpdatesResponse Not supported" +#endif +typedef struct D2GCheckContentUpdatesResponse +{ + GHTTPResult mHttpResult; // HTTP Result + D2GContentUpdateList *mContentUpdateList; // See D2GDownloadList. This must be freed using d2gFreeDownloadList. +} D2GCheckContentUpdatesResponse; + +// Public SDK Interface + +// Initialization and Termination +// d2gCreateInstance +// Summary +// Creates Direct2Game SDK instance. +// Parameters +// None. +// Return Value +// D2GInstancePtr - a void pointer to the internal D2G Instance. +// Remarks +// This function is mandatory. This API function must be called first +// before +// any other D2G API function. +// See Also +// d2gCleanup +// +D2GInstancePtr d2gCreateInstance(); + +// d2gInitialize +// Summary +// Initializes the SDK Instance created by d2gCreateInstance. +// Parameters +// theInstance : [in] This pointer is created by the d2gCreateInstance. +// theCertificate : [in] This is the certificate obtained from +// authorization +// service. +// thePrivateData : [in] This is the private data obtained from +// authorization +// service. +// Return Value +// GSResult - Enumeration indicating success (GS_SUCCESS) or a failure +// code. +// Remarks +// This function is mandatory. +// See Also +// d2gCleanup +// +GSResult d2gInitialize( D2GInstancePtr theInstance, + const GSLoginCertificate * theCertificate, + const GSLoginPrivateData * thePrivateData); + +// d2gCleanup +// Summary +// De-allocates memory allocated by d2gCreateInstance and d2gInitialize. +// Parameters +// theInstance : [in] This pointer is created by the d2gCreateInstance. +// Return Value +// None. +// Remarks +// This function is mandatory. +// See Also +// d2gCreateInstance +// +void d2gCleanup(D2GInstancePtr theInstance); + +// d2gCreateCatalog +// Summary +// Creates a local catalog d2gInstance to access the backend catalog +// services +// from within the game. +// Parameters +// theInstance : [in] This is pointer created by the d2gCreateInstance. +// gameId : [in] Unique game id which is pre-assigned by GameSpy Tech. +// version\ : [in] A version of the Catalog for the game. +// region : [in] A string that specifies the region for the catalog. +// accessToken : [in] A unique token that allows access to the backend +// catalog services. +// Return Value +// D2GCatalogPtr - Pointer to the catalog d2gInstance initialized with +// the input parameters. +// Remarks +// This API function is mandatory. After this API function is called, all +// of the +// backend catalog and purchasing services are available to the game. For +// the D2G SDK +// to terminate properly, d2gCleanupCatalog must be called before calling +// d2gCleanup. +// See Also +// d2gCleanupCatalog +// +D2GCatalogPtr d2gCreateCatalog( D2GInstancePtr theInstance, + int gameId, + gsi_u32 version, + UCS2String region, + UCS2String accessToken); +// +// d2gCleanupCatalog +// Summary +// De-allocates the memory associated with the catalog d2gInstance. +// Parameters +// theInstance : [in] This pointer is created by the d2gCreateInstance. +// theCatalog : [in] This is pointer created by the d2gCreateCatalog. +// Return Value +// None. +// Remarks +// This API function is mandatory. This must be called before calling +// d2gCleanup. +// See Also +// d2gCreateCatalog +// +void d2gCleanupCatalog(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog); + + + +/////////////////////////////////////// +/// Callbacks for asynchronous calls // +/////////////////////////////////////// + +// D2GGetStoreAvailabilityCallback +// Summary +// This callback function is invoked when the d2gGetStoreAvailability +// request completes. +// Parameters +// result\ : [in] returned result in response to backend service request. +// If successful GS_SUCCESS otherwise an error code of +// GSResult type is returned. +// response : [in] response as defined by D2GGetStoreAvailabilityResponse. +// userData : [in/out] a void pointer to user-defined data. +// Return Value +// None. +// Remarks +// The developer will receive the store availability in the response +// parameter. +// The store availability status can be one of the following: +// D2GResultCode_StoreOnline, D2GResultCode_StoreOfflineForMaintenance, +// D2GResultCode_StoreOfflineRetired, D2GResultCode_StoreNotYetLaunched. +// +typedef void(*D2GGetStoreAvailabilityCallback) (GSResult result, + D2GGetStoreAvailabilityResponse *response, + void *userData); + +// Summary +// This callback function is invoked when the d2gLoadCatalogItems request +// completes. +// When a successful result is returned the Catalog Items are ready to be +// used in the cache. +// Parameters +// result\ : [in] returned result in response to backend service +// request. +// If successful GS_SUCCESS otherwise an error code of +// GSResult type is returned. +// response : [in] response as defined by D2GLoadCatalogItemsResponse. +// userData : [in/out] a void pointer to user defined data. +// Return Value +// None. +// Remarks +// The response contains the D2GCatalogItems list. A successful result means +// the D2G cache contains +// catalog items retrieved from the backend. In the callback, the developer +// can make local copy of +// the Catalog Items from the list by calling d2gCloneCatalogItems or can add +// to an existing list +// by calling d2gAppendCatalogItems. The developer should call the +// d2gFreeCatalogItems to free +// the items copied from the cache. +// +typedef void(*D2GLoadCatalogItemsCallback) (GSResult result, + D2GLoadCatalogItemsResponse *response, + void *userData); + +// Summary +// This callback function is invoked when the +// d2gLoadCatalogItemsByCategory +// request completes. +// Parameters +// result\ : [in] returned result in response to backend service +// request. +// If successful GS_SUCCESS otherwise an error code +// of GSResult +// type is returned. +// response : [in] response as defined by +// D2GLoadCatalogItemsByCategoryResponse. +// userData : [in/out] a void pointer to user defined data. +// Return Value +// None. +// Remarks +// The response contains the D2GCatalogItems list. A successful result +// means +// the D2G cache contains the catalog items of a specific category +// retrieved +// from the backend or they already exist in the cache. In the callback, +// the +// developer can make local copy of the Catalog Items from the list by +// calling +// d2gCloneCatalogItems or can add to an existing list by calling +// d2gAppendCatalogItems. +// The developer should call the d2gFreeCatalogItems to free the items +// copied from the cache. +typedef void(*D2GLoadCatalogItemsByCategoryCallback)(GSResult result, + D2GLoadCatalogItemsByCategoryResponse *response, + void *userData); +// Summary +// This callback function is invoked when the d2gLoadExtraCatalogInfo +// request completes. +// Parameters +// result\ : [in] returned result in response to backend service +// request. If successful, +// GS_SUCCESS otherwise an error code of GSResult +// type is returned. +// response : [in] response as defined by +// D2GLoadExtraCatalogInfoResponse. +// userData : [in/out] a void pointer to user defined data. +// Return Value +// None. +// Remarks +// The response contains the D2GExtraInfoList. When the result is success, +// the D2G cache has been populated +// with the ExtraInfo pairs. Then, d2gGetExtraCatalogInfo can +// be used to lookup a key from +// the D2G cache. +// +typedef void(*D2GLoadExtraCatalogInfoCallback) (GSResult result, + D2GLoadExtraCatalogInfoResponse *response, + void *userData); + +// Summary +// This callback function is invoked when the d2gGetUserCreditCards +// request completes. +// Parameters +// result\ : [in] returned result in response to backend service +// request. +// If successful GS_SUCCESS otherwise an error code of +// GSResult type +// is returned. +// response : [in] response as defined by D2GGetUserCreditCardsResponse. +// userData : [in/out] a void pointer to user defined data. +// Return Value +// None. +// Remarks +// The response from the service contains the account information for a +// purchase. +// The developer should keep a copy of the “D2GCreditCardInfo.mAccountIdE as +// input to the +// d2gGetOrderTotal request function. +// +#ifndef _WIN32 +#warning "WARNING: D2GGetUserCreditCardsCallback Not supported" +#endif +typedef void(*D2GGetUserCreditCardsCallback) (GSResult result, + D2GGetUserCreditCardsResponse *response, + void *userData); + +// Summary +// This callback function is invoked when the d2gGetOrderTotal request +// completes. +// Parameters +// result\ : [in] returned result in response to backend service +// request. If successful +// GS_SUCCESS otherwise an error code of GSResult +// type is returned. +// response : [in] response as defined by D2GGetOrderTotalResponse. +// userData : [in/out] a void pointer to user defined data. +// Return Value +// None. +// Remarks +// This callback response returns a total quote for the items to be purchased. +// The developer should keep a copy of the order total by calling +// d2gCloneOrderTotal +// since it is required as input to the d2gStartOrder request and free it by +// calling +// d2gFreeOrderTotal after no longer needed. +// +#ifndef _WIN32 +#warning "WARNING: D2GGetOrderTotalCallback Not supported" +#endif +typedef void(*D2GGetOrderTotalCallback) (GSResult result, + D2GGetOrderTotalResponse *response, + void *userData); + +// Summary +// The D2G SDK invokes this callback when the d2gStartOrder request +// completes. +// Parameters +// result\ : [in] returned result in response to backend service +// request. +// If successful GS_SUCCESS otherwise an error code +// of GSResult type is returned. +// response : [in] response as defined by D2GStartOrderResponse. +// userData : [in/out] a void pointer to user defined data. +// Return Value +// None. +// Remarks +// This callback response returns the Order Purchase. The developer should +// keep a copy +// of the order purchase by calling d2gCloneOrderPurchase since it is +// required as input +// to the d2gIsOrderComplete request, and free it by calling +// d2gFreeOrderPurchase +// after no longer needed. +#ifndef _WIN32 +#warning "WARNING: D2GStartOrderCallback Not supported" +#endif +typedef void(*D2GStartOrderCallback) (GSResult result, + D2GStartOrderResponse *response, + void *userData); + +// Summary +// The D2G SDK invokes D2GIsOrderCompleteCallback when the +// d2gIsOrderComplete request completes. +// Parameters +// result\ : [in] returned result in response to backend service +// request. If successful, +// GS_SUCCESS otherwise an error code of GSResult +// type is returned. +// response : [in] response as defined by D2GIsOrderCompleteResponse. +// userData : [in/out] a void pointer to user defined data. +// Return Value +// None. +// Remarks +// This callback function indicates whether a purchase has completed. +// D2GIsOrderCompleteResponse +// contains a list of licenses and the associated download items for the +// purchased item(s) +// as well as the Order Information. +// +#ifndef _WIN32 +#warning "WARNING: D2GIsOrderCompleteCallback Not supported" +#endif +typedef void(*D2GIsOrderCompleteCallback)(GSResult result, + D2GIsOrderCompleteResponse *response, + void *userData); + +// Summary +// The D2G SDK invokes this callback when the d2gGetPurchaseHistory +// request completes. +// Parameters +// result\ : [in] returned result in response to backend service +// request. If successful +// GS_SUCCESS otherwise an error code of GSResult +// type is returned. +// response : [in] response as defined by +// D2GGetPurchaseHistoryResponse. +// userData : [in/out] a void pointer to user defined data. +// Return Value +// None. +// Remarks +// This callback response is contains the entire purchase history of the +// game. It is simply +// a list of purchases, which contain the same information, returned as in +// the D2GIsOrderCompleteCallback. +// +#ifndef _WIN32 +#warning "WARNING: D2GGetPurchaseHistoryCallback Not supported" +#endif +typedef void(*D2GGetPurchaseHistoryCallback)(GSResult result, + D2GGetPurchaseHistoryResponse *response, + void *userData); + +// +// Summary +// The D2G SDK invokes this callback when the d2gGetItemActivationData +// request completes. +// Parameters +// result\ : [in] returned result in response to backend service +// request. If successful +// GS_SUCCESS otherwise an error code of GSResult +// type is returned. +// response : [in] response as defined by +// D2GGetItemActivationResponse. +// userData : [in/out] a void pointer to user defined data. +// Return Value +// GSResult - Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// This is part of an upcoming DRM functionality. +#ifndef _WIN32 +#warning "WARNING: D2GGetItemActivationDataCallback Not supported" +#endif +typedef void(*D2GGetItemActivationDataCallback) (GSResult result, + D2GGetItemActivationResponse *response, + void *userData); +// Summary +// The D2G SDK invokes this callback when the d2gCheckContentUpdates +// request completes. +// Parameters +// result\ : [in] returned result in response to backend service +// request. If successful +// GS_SUCCESS otherwise an error code of GSResult +// type is returned. +// response : [in] response as defined by +// D2GCheckContentUpdatesResponse. +// userData : [in/out] a void pointer to user defined data. +// Return Value +// None. +// Remarks +// The response includes a list of updates, if any, to previously +// purchased/downloaded content. The types of +// available updates are FORCED , MANDATORY and OPTIONAL. +// +#ifndef _WIN32 +#warning "WARNING: D2GCheckContentUpdatesCallback Not supported" +#endif +typedef void(*D2GCheckContentUpdatesCallback) (GSResult result, + D2GCheckContentUpdatesResponse *response, + void *userData); + +// Summary +// D2G SDK invokes this call function periodically during the file +// download to indicate download progress +// status during file download after it receives a successful response +// for d2gStartDownloadFileById request. +// Parameters +// bytesReceived : [in] number of bytes received so far. +// bytesTotal : [in] the total size of the file being downloaded. +// userData : [in/out] a void pointer to user defined data. +// Return Value +// None. +// Remarks +// None. +#ifndef _WIN32 +#warning "WARNING: D2GDownloadProgressCallback Not supported" +#endif +typedef void(*D2GDownloadProgressCallback) (gsi_u32 bytesReceived, gsi_u32 bytesTotal, void *userData); + +// Summary +// This callback is invoked when the download operation completes whether +// successfully or due to failure. +// Parameters +// result\ : [in] returned result in response to backend service +// request. If successful, +// GS_SUCCESS otherwise an error code of GSResult type is +// returned. +// saveFile : [in] the name of the file which was downloaded. When +// there is an error during download, +// this parameter is set to NULL. +// userData : [in/out] a void pointer to user-defined data. +// Return Value +// None. +// Remarks +// If download completes successfully, EFILENAME>Eis saved in the download +// location. +// +#ifndef _WIN32 +#warning "WARNING: D2GDownloadCompleteCallback Not supported" +#endif +typedef void(*D2GDownloadCompleteCallback) (GSResult result, gsi_char *saveFile, void *userData); + +/////////////////////////////////////////////////////////////////////////////////// +// Service Request Calls +// // +// These calls are asynchronous to allow for uninterrupted application +// execution // +/////////////////////////////////////////////////////////////////////////////////// + +//d2gGetStoreAvailability +// Summary +// This API call retrieves whether a store implementation for +// a given catalog is available. +// Parameters +// theInstance : [in] This pointer is created by the d2gCreateInstance. +// theCatalog : [in] This is pointer created by the d2gCreateCatalog. +// callback : [in] This is the pointer to developer's callback function. +// userData : [in/out] a void pointer to user-defined data. +// Return Value +// GSResult - Enum indicating success (GS_SUCCESS) or a failure code. +// +GSResult d2gGetStoreAvailability(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + D2GGetStoreAvailabilityCallback callback, + void *userData); + +// +//d2gLoadExtraCatalogInfo +// Summary +// This API function retrieves a list of ExtraInfo items that is +// particular +// to the game from the backend services. +// Parameters +// theInstance : [in] This pointer is created by the d2gCreateInstance. +// theCatalog : [in] This is pointer created by the d2gCreateCatalog. +// callback : [in] This is the pointer to developer's callback function. +// userData : [in/out] a void pointer to user-defined data. +// Return Value +// GSResult - Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// This function is optional if no ExtraInfo exist for a game. If +// ExtraInfo +// exist, this function should be called during the SDK initialization. +// Game developers can specify key/value pairs, which we call Extra +// Information +// for the purposes of modifying the store experience to their users +// within +// their D2G interface. Specific to a catalog, ExtraInfo data is generic +// enough +// that the developer can determine where it is best used, e.g. indicating +// a custom account management URL or the default item category. +// The values rare cached within the D2G SDK and can be retrieved later +// one +// at a time. In order to add ExtraInfo data to your catalog, +// email devsupport@gamespy.com to set up the data on our backend. +// +GSResult d2gLoadExtraCatalogInfo(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + D2GLoadExtraCatalogInfoCallback callback, + void *userData); + +// +//d2gLoadCatalogItems +// Summary +// This API call retrieves the items of a catalog from the backend. +// Parameters +// theInstance : [in] This pointer is created by the d2gCreateInstance. +// theCatalog : [in] This is pointer created by the d2gCreateCatalog. +// callback : [in] This is the pointer to developer's callback function. +// userData : [in/out] a void pointer to user-defined data. +// Return Value +// GSResult - Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// When this request is completed successfully the D2G cache is populated +// with the items retrieved from the Catalog. +// +GSResult d2gLoadCatalogItems(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + D2GLoadCatalogItemsCallback callback, + void *userData); + +//d2gLoadCatalogItemsByCategory +// Summary +// This function retrieves a list of the items which are in a specific +// category. +// Parameters +// theInstance : [in] This pointer is created by the d2gCreateInstance. +// theCatalog : [in] This is pointer created by the d2gCreateCatalog. +// callback : [in] This is the pointer to developer's callback function. +// userData : [in/out] a void pointer to user-defined data. +// Return Value +// GSResult - Enum indicating success (GS_SUCCESS) or a failure code. +// +GSResult d2gLoadCatalogItemsByCategory(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + const UCS2String theCategory, + D2GLoadCatalogItemsByCategoryCallback callback, + void *userData); + +//d2gGetUserCreditCards +// Summary +// This API call retrieves the user's credit cards from the backend. +// D2G needs the user account information prior to user's purchases. . +// Parameters +// theInstance : [in] This pointer is created by the d2gCreateInstance. +// theCatalog : [in] This is pointer created by the d2gCreateCatalog. +// validOnly : [in] A boolean to indicate if the request is for all or +// only valid credit cards. +// callback : [in] This is the pointer to developer's callback function. +// userData : [in/out] a void pointer to user-defined data. +// Return Value +// GSResult - Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// This function should be called before calling d2gGetOrderTotal +// since it needs the account information. +// +#ifndef _WIN32 +#warning "WARNING: d2gGetUserCreditCards Not supported" +#endif +GSResult d2gGetUserCreditCards(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + gsi_bool validOnly, + D2GGetUserCreditCardsCallback callback, + void *userData); + +//d2gGetOrderTotal +// Summary +// This API function is the first step in the ordering process. +// A list of items selected from the Catalog along with their quantities +// passed +// as input. Then, this function will send an Order Request to backend to +// retrieve +// the OrderTotal. +// Parameters +// theInstance : [in] This is the pointer created by the +// d2gCreateInstance. +// theCatalog : [in] This is the pointer created by the +// d2gCreateCatalog. +// accountId : [in] accountId is retrieved with the +// d2gGetUserCreditCards +// request from the back-end. +// cultureCode : [in] The culture code in which this order will be +// processed. +// currencyCode : [in] The currency in which this order will be +// processed. +// itemIds : [in] Pointer to the list of itemIds selected from +// the catalog. +// itemCount : [in] The number items in the itemIds list. +// itemQuantities : [in] The pointer to the corresponding list of item +// quantities +// for each item in the itemIds list. +// callback : [in] This is the pointer to developer's callback +// function. +// userData : [in/out] a void pointer to user-defined data. +// Return Value +// GSResult - Enumeration indicating success (GS_SUCCESS) or a failure +// code. +// Remarks +// This function call is the first step to make purchase. It simply builds +// the shopping cart information for the game. A list of items selected +// from +// the Catalog along with their quantities passed as input. Then, this +// function +// will send an Order Request to back-end to retrieve the OrderTotal. +// +#ifndef _WIN32 +#warning "WARNING: d2gGetOrderTotal Not supported" +#endif +GSResult d2gGetOrderTotal(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + gsi_u32 accountId, + D2GItemId *itemIds, + gsi_u32 itemCount, + gsi_u32 *itemQuantities, + D2GGetOrderTotalCallback callback, + void *userData); + +//d2gStartOrder +// Summary +// This API call is for starting the purchase process. It needs the +// OrderTotal +// obtained with the d2gGetOrderTotal request. +// Parameters +// theInstance : [in] This is the pointer created by the d2gCreateInstance. +// theCatalog : [in] This is the pointer created by the d2gCreateCatalog. +// orderTotal : [in] This is received from the backend in the response +// to d2gGetOrderTotal request. +// callback : [in] This is the pointer to developer's callback function. +// userData : [in/out] a void pointer to user-defined data. +// Return Value +// GSResult - Integer built with enums indicating success (GS_SUCCESS) or +// a failure code. +// +#ifndef _WIN32 +#warning "WARNING: d2gStartOrder Not supported" +#endif +GSResult d2gStartOrder(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + D2GOrderTotal *orderTotal, + D2GStartOrderCallback callback, + void *userData); + +//d2gIsOrderComplete +// Summary +// This API call is for retrieving the order status from the backend. +// The order status is returned by the callback when the request +// completes. +// Parameters +// theInstance : [in] This is the pointer created by the +// d2gCreateInstance. +// theCatalog : [in] This is the pointer created by the +// d2gCreateCatalog. +// orderPurchase : [in] This is received from the backend in the response +// to the d2gStartOrder request. +// callback : [in] This is the pointer to developer's callback +// function. +// userData : [in/out] a void pointer to user-defined data. +// Return Value +// GSResult - Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// This function should be called by the game periodically to poll after +// d2gStartOrder request completes successfully to retrieve the order +// status. +// The recommended polling is 2 seconds after a successful start of an +// order, +// followed by every 5 seconds until 30 seconds total have passed. If +// the order +// still hasn't completed, a message to the user should be displayed explaining +// that the order is still in progress, but is taking longer than +// expected to complete. +// There can be a number of reasons the order may not complete. One +// example is the credit +// card has expired, or is invalid. The order validation result can +// indicate that. +#ifndef _WIN32 +#warning "WARNING: d2gIsOrderComplete Not supported" +#endif +GSResult d2gIsOrderComplete(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + D2GOrderPurchase *orderPurchase, + D2GIsOrderCompleteCallback callback, + void *userData); +//d2gGetPurchaseHistory +// Summary +// This API call retrieves the user's purchase history from the backend +// service for the purchases made related to the game. +// Parameters +// theInstance : [in] This is the pointer created by the d2gCreateInstance. +// theCatalog : [in] This is the pointer created by the d2gCreateCatalog. +// callback : [in] This is the pointer to developer's callback function. +// userData : [in/out] a void pointer to user-defined data. +// Return Value +// GSResult - Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// The callback returns the list of items purchased for this game. +// +#ifndef _WIN32 +#warning "WARNING: d2gGetPurchaseHistory Not supported" +#endif +GSResult d2gGetPurchaseHistory(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + D2GGetPurchaseHistoryCallback callback, + void *userData); + +//d2gStartDownloadFileById +// Summary +// This function is called to download a purchased file. The download +// information is part of the response to d2gGetPurchaseHistory and +// d2gIsOrderComplete requests. +// Parameters +// theInstance : [in] This is the pointer created by the +// d2gCreateInstance. +// theCatalog : [in] This is the pointer created by the +// d2gCreateCatalog. +// fileId : [in] The file id to be downloaded. +// downloadLocation : [in] The directory path where the downloaded file +// to be saved. The download directory must exist. +// progressCallback : [in] This is the pointer to developer's progress +// callback. +// completedCallback: [in] This is the pointer to developer's completed +// callback. +// userData : [in/out] a void pointer to user-defined data. +// Return Value +// GSResult - Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// After the purchase completed, the developer can download the files +// from +// the download list retrieved either with d2gIsOrderComplete request or +// d2gGetPurchaseHistory request. +// +#ifndef _WIN32 +#warning "WARNING: d2gStartDownloadFileById Not supported" +#endif +GSResult d2gStartDownloadFileById(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + D2GFileId fileId, + const gsi_char *downloadLocation, + D2GDownloadProgressCallback progressCallback, + D2GDownloadCompleteCallback completedCallback, + void *userData); + +// +//d2gGetItemActivationData +// Summary +// This function retrieves the licensing information from the backend. +// if any of the licenses owned by the game expires. +// Parameters +// theInstance : [in] This is the pointer created by the d2gCreateInstance. +// theCatalog : [in] This is the pointer created by the d2gCreateCatalog. +// callback : [in] This is the pointer to developer's callback function. +// thePackageId: [in] This is parameter is an input from the DRM +// functionality. +// userData : [in/out] a void pointer to user-defined data. +// Return Value +// GSResult - Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// This is part of an upcoming DRM functionality. +// +#ifndef _WIN32 +#warning "WARNING: d2gGetItemActivationData Not supported" +#endif +GSResult d2gGetItemActivationData(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + D2GGetItemActivationDataCallback callback, + D2GPackageId thePackageId, + void * userData); + +//d2gCheckContentUpdates +// Summary +// This function invokes a service call to the backend to check any +// in-game +// content updates. +// Parameters +// theInstance : [in] This is the pointer created by the d2gCreateInstance. +// theCatalog : [in] This is the pointer created by the d2gCreateCatalog. +// requiredOnly: [in] Is the update for required content only. +// callback : [in] This is the pointer to developer's callback function. +// userData : [in/out] a void pointer to user-defined data. +// Return Value +// GSResult - Integer built with enums indicating success (GS_SUCCESS) or +// a failure code. +// Remarks +// None. +// +#ifndef _WIN32 +#warning "WARNING: d2gCheckContentUpdates Not supported" +#endif +GSResult d2gCheckContentUpdates(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + gsi_bool requiredOnly, + D2GCheckContentUpdatesCallback callback, + void *userData); +////////////////////// +//Helper Functions // +////////////////////// + +// +// Data access/management helper functions +// + +//d2gFreeCatalogItems +// Summary +// This function de-allocates the memory for theItemList and assigns NULL +// to theItemList. +// Parameters +// theItemList : [in] Pointer to the list. +// Return Value +// GSResult - Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// This is the corresponding API function to d2gCloneCatalogItems +// to de-allocate and reset memory. +GSResult d2gFreeCatalogItemList(D2GCatalogItemList *theItemList); + +//d2gCloneCatalogItems +// Summary +// This function makes a shallow copy of the items from theSrcItemList +// to theDstItemList. +// Parameters +// theDstItemList : [out] Pointer to the destination list. +// It is initialized by d2gCloneCatalogItems. +// theSrcItemList : [in] Pointer to the source list. +// Return Value +// GSResult - Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// The reason why the shallow copy is used is that the actual catalog +// items +// are located in the D2G cache. If this function is called, its +// corresponding +// free function d2gFreeCatalogItems must be called to de-allocate the +// memory +// containing theDstItemList. See D2GLoadCatalogItemsCallback for when +// this is used. +// See Also +// d2gFreeCatalogItems +// +GSResult d2gCloneCatalogItemList(D2GCatalogItemList *theDstItemList, + const D2GCatalogItemList *theSrcItemList); + +//d2gAppendCatalogItems +// Brief +// This function allows the developer append the one list to another. +// The srcList is not modified. The dstList contains its original items +// as well as the all the items from the srcList. +// Parameters +// dstItemList : [out] Pointer to the destination list the source list. +// srcItemList : [in] Pointer to source list given by the caller. +// The contents of this list are not modified. +// Return Value +// GSResult - Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// See D2GLoadCatalogItemsCallback for when this may be used. +// +GSResult d2gAppendCatalogItemList(D2GCatalogItemList *dstList, + D2GCatalogItemList *srcList); + +//d2gCloneOrderTotal +// Summary +// This function makes a deep copy of the items from sourceOrderTotal to +// destinationOrderTotal. +// Parameters +// destinationOrderTotal : [out] Pointer to the destination order total. +// sourceOrderTotal : [in] Pointer to source order total. The +// contents +// of this list are not modified. +// Return Value +// GSResult - Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// This function is important, since in the D2GGetOrderTotalCallback +// function, +// the developer needs to retain the order total. The memory for the +// destination +// list is allocated by D2G. If this function is called, its +// corresponding free +// function d2gFreeOrderTotal must be called to de-allocate the memory +// containing +// destinationOrderTotal. +// +GSResult d2gCloneOrderTotal(D2GOrderTotal **destinationOrderTotal, + D2GOrderTotal *sourceOrderTotal); + +//d2gCloneOrderPurchase +// Summary +// This function makes a deep copy of the items from sourceOrderPurchase +// to +// destOrderPurchase. +// Parameters +// destOrderPurchase : [out] a list of pointers to the destination order +// purchases. +// sourceOrderTotal : [in] Pointer to source order total. +// The contents of this list are not modified. +// Return Value +// GSResult - Integer built with enums indicating success (GS_SUCCESS) or +// a failure code. +// Remarks +// This function is important, since in the D2GStartOrderCallback +// function, the +// developer needs retain the order purchase. The D2G SDK allocates the +// memory +// space for the destination list. +// If this function is called, d2gFreeOrderPurchase must be called to +// de-allocate +// the memory space. +// See Also +// D2GStartOrderCallback for when this may be used. +// +GSResult d2gCloneOrderPurchase(D2GOrderPurchase **destOrderPurchase, + D2GOrderPurchase *sourceOrderPurchase); + +// d2gFreeExtraInfoList +// Summary +// This API function de-allocates and resets the memory pointed to +// by downloadList. +// Parameters +// extraInfoList : [in] Pointer to the memory to be released. +// Return Value +// None. +// Remarks +// Download lists should be freed explicitly because callbacks give +// pointers to the objects but does not free them. The function will +// free internal data and the list. Developers can set the pointer to +// NULL afterwards. +void d2gFreeExtraInfoList(D2GExtraInfoList *extraInfoList); + +// d2gFreeCreditCardInfoList +// Summary +// This API function de-allocates and resets the memory pointed to +// by creditCardList. +// Parameters +// creditCardList : [in] Pointer to the memory to be released. +// Return Value +// None. +// Remarks +// Credit card lists should be freed explicitly because callbacks give +// pointers to the objects but does not free them. The function will +// free internal data and the list. Developers can set the pointer to +// NULL afterwards. +void d2gFreeCreditCardInfoList(D2GCreditCardInfoList *creditCardList); + +//d2gFreeOrderTotal +// Summary +// This API function de-allocates and resets the memory pointed to by +// orderTotal. +// Parameters +// orderTotal : [in] The pointer to the order total. +// Return Value +// None. +// Remarks +// This is the corresponding API function to d2gCloneOrderTotal. +// +void d2gFreeOrderTotal(D2GOrderTotal *orderTotal); + +//d2gFreeOrderPurchase +// Summary +// This API function de-allocates and resets the memory pointed to +// by orderPurchase. +// Parameters +// orderPurchase : [in] Pointer to the memory to be release. +// Return Value +// None. +// Remarks +// This is the corresponding API call to d2gCloneOrderPurchase . +// +void d2gFreeOrderPurchase(D2GOrderPurchase *orderPurchase); + +// d2gFreePuchaseHistory +// Summary +// This API function de-allocates and resets the memory pointed to +// by purchaseHistory. +// Parameters +// purchaseHistory : [in] Pointer to the memory to be released. +// Return Value +// None. +// Remarks +// Purchase histories should be freed explicitly because callbacks give +// pointers to the objects but does not free them. +// +void d2gFreePurchaseHistory(D2GPurchaseHistory *purchaseHistory); + +// d2gFreeDownloadItemList +// Summary +// This API function de-allocates and resets the memory pointed to +// by downloadList. +// Parameters +// contentUpdateList : [in] Pointer to the memory to be released. +// Return Value +// None. +// Remarks +// content update lists should be freed explicitly because callbacks give +// pointers to the objects but does not free them. The function will +// free internal data and the list. Developers can set the pointer to +// NULL afterwards. +void d2gFreeContentUpdateList(D2GContentUpdateList *contentUpdateList); + +// +// Sorting +// + +//d2gSortCatalogItemsbyPrice +// Summary +// This function sorts a list of catalog items onto itself, according to +// the price of the items either in ascending or in descending order. +// Direction of the order is specified as an input parameter. +// Parameters +// list : [in] pointer to the list of catalog items to be sorted. +// direction : [in] an enumerated type for the sort direction; ascending +// or descending. +// Return Value +// GSResult - Integer built with enums indicating success (GS_SUCCESS) or +// a failure code. +// +GSResult d2gSortCatalogItemsbyPrice(D2GCatalogItemList *list, + D2GSortDirection direction ); + +//d2gSortCatalogItemsbyName +// Summary +// This function sorts a list of catalog items onto itself, according to +// the item names alphabetically either in ascending or in descending +// order. +// Direction of the order is specified as an input parameter. +// Parameters +// list : [in] pointer to the list of catalog items to be sorted. +// direction : [in] an enumerated type for the sort direction; +// ascending or descending. +// Return Value +// GSResult - Integer built with enums indicating success (GS_SUCCESS) or +// a failure code. +// +GSResult d2gSortCatalogItemsbyName(D2GCatalogItemList *list, + D2GSortDirection direction); + +//d2gSortCatalogItemsbyReleaseDate +// Summary +// This function sorts a list of catalog items onto itself, according to +// the release date of the items either in ascending or in descending +// order. +// Direction of the order is specified as an input parameter. +// Parameters +// list : [in] pointer to the list of catalog items to be sorted. +// direction : [in] an enumerated type for the sort direction; ascending +// or descending. +// Return Value +// GSResult - Integer built with enums indicating success (GS_SUCCESS) or +// a failure code. +// +GSResult d2gSortCatalogItemsbyReleaseDate(D2GCatalogItemList *list, + D2GSortDirection direction); + +//d2gSortCatalogItemsbyItemId +// Summary +// This function sorts a list of catalog items onto itself, according to +// the release date of the items either in ascending or in descending +// order. +// Direction of the order is specified as an input parameter. +// Parameters +// list : [in] pointer to the list of catalog items to be sorted. +// direction : [in] an enumerated type for the sort direction; ascending +// or descending. +// Return Value +// GSResult - Integer built with enums indicating success (GS_SUCCESS) or +// a failure code. +// +GSResult d2gSortCatalogItemsbyItemId(D2GCatalogItemList *list, + D2GSortDirection direction ); + +//d2gSortCatalogItemsbyExternalId +// Summary +// This function sorts a list of catalog items onto itself, according to +// the release date of the items either in ascending or in descending +// order. +// Direction of the order is specified as an input parameter. +// Parameters +// list : [in] pointer to the list of catalog items to be sorted. +// direction : [in] an enumerated type for the sort direction; ascending +// or descending. +// Return Value +// GSResult - Integer built with enums indicating success (GS_SUCCESS) or +// a failure code. +// +GSResult d2gSortCatalogItemsbyExternalId(D2GCatalogItemList *list, + D2GSortDirection direction ); + +//d2gSortCatalogItemsbySize +// Summary +// This function sorts a list of catalog items onto itself, according to +// the release date of the items either in ascending or in descending +// order. +// Direction of the order is specified as an input parameter. +// Parameters +// list : [in] pointer to the list of catalog items to be sorted. +// direction : [in] an enumerated type for the sort direction; ascending +// or descending. +// Return Value +// GSResult - Integer built with enums indicating success (GS_SUCCESS) or +// a failure code. +// +GSResult d2gSortCatalogItemsbySize(D2GCatalogItemList *list, + D2GSortDirection direction ); + + +// +// Catalog Helper functions +// + +//d2gGetCategories +// Summary +// This is a helper function designed to retrieve the list of categories +// from the catalog. This function is under development. +// +// Parameters +// theInstance : [in] This is the pointer created by the d2gCreateInstance. +// theCatalog : [in] This is the pointer created by the d2gCreateCatalog. +// categoryList: [out]a pointer to the category list for the categories +// found. +// D2G SDK allocates the memory for the list. +// Return Value +// GSResult - Integer built with enums indicating success (GS_SUCCESS) or +// a failure code. +// Remarks +// This helper function helps you render your category based UI if for +// example +// each category were to be assigned to a button in your store navigation. +// +// IMPORTANT: +// This function does a shallow copy of the categoryName from the cache. +// When releasing the memory for the categoryList->categoryName, +// use gsifree(categoryList->categoryName). +// +// +GSResult d2gGetCategories(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + D2GCategoryList *categoryList); +// d2gGetExtraCatalogInfo +// Summary +// This is a lookup function for a pair which is locally +// available in the D2G cache. +// Parameters +// theInstance : [in] This is the pointer created by the +// d2gCreateInstance. +// theCatalog : [in] This is the pointer created by the +// d2gCreateCatalog. +// extraInfoKey : [in] The key for the value to be retrieved. +// extaInfoValue : [out] Pointer to the value in the D2G cache. +// Return Value +// GSResult - Integer built with enums indicating success (GS_SUCCESS) or +// a failure code. +// Remarks +// See d2gLoadExtraCatalogInfo, for more information to initialize the +// D2G cache +// with the list of ExtraInfo items. Since a pointer to the cached value +// is returned, the developer should never free extaInfoValue pointer. +// +GSResult d2gGetExtraCatalogInfo(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + const UCS2String extraInfoKey, + UCS2String *extaInfoValue); + +/// Extra Item Info Helper functions + +// d2gGetExtraItemInfoKeyValueByKeyName +// Summary +// Looks up the extra item info value for a given D2GCatalogItem and its +// extra info key +// Parameters +// theCatalogItem : [in] pointer to item being checked. +// aKey : [in] Extra Info key for the value to be retrieved. +// aValue : [out] Pointer to value that is being looked up. +// Return Value +// GSResult - Integer built with enums indicating success (GS_SUCCESS) or +// a failure code. +// Remarks +// The catalog item being passed in must be valid otherwise the lookup +// will +// not occur. If the key doesn't exist in the extra info list for the item +// passed in, extraInfoValue will be NULL and an error will be returned: +// D2GResultCode_ExtraInfo_KeyNotFound). This function will only find +// the first +// occurrence of the key. Any additional occurrences will be ignored. +GSResult d2gGetExtraItemInfoKeyValueByKeyName(const D2GCatalogItem *theCatalogItem, + const UCS2String extraInfoKey, + UCS2String *extraInfoValue); + +// d2gFilterCatalogItemListByKeyName +// Summary +// Creates a subset of a given D2GCatalogItemList based on the +// key name passed in. +// Parameters +// incomingCatalogItems : [in] Pointer to D2GCatalogItemList being +// scanned. +// outgoingCatalogItems : [out] Pointer to Pointer of the +// D2GCatalogItemList being returned. +// extraInfoKey : [in] Extra Info Key name used for scanning +// items. +// Return Value +// GSResult - Integer built with enums indicating success (GS_SUCCESS) or +// a failure code. +// Remarks +// The incomingCatalogItems and extraInfoKey being passed in must be +// valid otherwise the scan will +// not occur. If the key doesn't exist in the extra info list for the +// list of items being +// passed in, then outgoingCatalogItems is set to NULL to represent an +// empty list. Note that +// outgoingCatalogItems is allocated internally in this function. The +// outgoingCatalogItems must be +// freed using d2gFreeCatalogItemList if this function successfully +// finds items in the incomingCatalogItems +// list. It should be pretty obvious since outgoingCatalogItems will +// *not* be NULL. +GSResult d2gFilterCatalogItemListByKeyName(const D2GCatalogItemList *incomingCatalogItems, + D2GCatalogItemList **outgoingCatalogItems, + const UCS2String extraInfoKey); + +// d2gFilterCatalogItemListByKeyNameValue +// Summary +// Creates a subset of a given D2GCatalogItemList based on the +// key name and key value passed in. +// Parameters +// incomingCatalogItems : [in] Pointer to D2GCatalogItemList being +// scanned. +// outgoingCatalogItems : [out] Pointer to Pointer of the +// D2GCatalogItemList being returned. +// extraInfoKey : [in] Extra Info Key name used for scanning +// items. +// extraInfoValue : [in] Extra Info Value name used for scanning +// items. +// Return Value +// GSResult - Integer built with enums indicating success (GS_SUCCESS) or +// a failure code. +// Remarks +// The incomingCatalogItems, extraInfoKey, extraInfoValue being passed in +// must be valid +// otherwise the scan will not occur. If the combination of the input +// key and value doesn't +// exist in the extra info list for the list of items being passed in, then +// outgoingCatalogItems is set to NULL to represent an empty list. Note that +// outgoingCatalogItems is allocated internally in this function. The +// outgoingCatalogItems must +// be freed using d2gFreeCatalogItemList if this function successfully +// finds items in the +// incomingCatalogItems list. It should be pretty obvious since +// outgoingCatalogItems will *not* +// be NULL. +GSResult d2gFilterCatalogItemListByKeyNameValue(const D2GCatalogItemList *incomingCatalogItems, + D2GCatalogItemList **outgoingCatalogItems, + const UCS2String extraInfoKey, + const UCS2String extraInfoValue); + + +/// Manifest Helper Functions + + +//d2gSetManifestFilePath +// Summary +// This function is set the path for the manifest file location. +// Parameters +// manifestFilePath : [in] String which includes location for the +// manifest file. +// Return Value +// GSResult - Integer built with enums indicating success (GS_SUCCESS) or +// a failure code. +// Remarks +// This is an optional function. If Patching Features used, the manifest +// file path +// can be set here. If used, it should be called only once right after +// the d2gInitialize. The path should exist. +// If function is not called the manifest file is located in +// the current working directory by default. Since the manifest file +// keeps the installed contents this path should not change once it is set. +// +// +GSResult d2gSetManifestFilePath( D2GInstancePtr theInstance, + const char *manifestFilePath); + +//d2gUpdateManifestInstalledContent +// Summary +// This function is updates to the manifest information kept by the SDK . +// Whenever a new content is downloaded and installed, it should be called +// after installing it. +// Parameters +// theInstance : [in] This is the pointer created by the d2gCreateInstance. +// theItemId : [in] Item Id which the content belongs to. +// theDownload : [in] Download item for the particular content. +// Return Value +// GSResult - Integer built with enums indicating success (GS_SUCCESS) or +// a failure code. +// Remarks +// This is a mandatory function and should be call after each successful +// installation +// of a downloaded item. This helper function is used in conjunction with +// the Check +// for updates service call. +// +GSResult d2gUpdateManifestInstalledContent(D2GInstancePtr theInstance, + D2GItemId theItemId, + D2GDownloadItem *theDownload); + +//d2gUpdateManifestRemovedContent +// Summary +// This function is deletes the un-installed content to the manifest +// information kept by the SDK . +// Whenever a new content is deleted, it should be called. +// Parameters +// theInstance : [in] This is the pointer created by the d2gCreateInstance. +// theItemId : [in] Item Id which the content belongs to. +// theDownload : [in] Download item for the particular content. +// Return Value +// GSResult - Integer built with enums indicating success (GS_SUCCESS) or +// a failure code. +// Remarks +// This is a mandatory function and should be call after each +// uninstallation +// of an item.This helper function is used in conjunction with the Check +// for updates service call. +// +GSResult d2gUpdateManifestRemovedContent(D2GInstancePtr theInstance, + D2GItemId theItemId, + D2GDownloadItem *theDownload); + +// +//d2gSetServiceTimeout +// Summary +// This function is for setting a timeout value for the service calls +// for the back-end services. If not set, then the default timeout +// is used. +// Parameters +// theInstance : [in] This is the pointer created by the d2gCreateInstance. +// timeoutMs : [in] The timeout value in milliseconds. +// Return Value +// GSResult - Integer built with enums indicating success (GS_SUCCESS) or +// a failure code. +// Remarks +// If this function used, it should be called during initialization. +// +GSResult d2gSetServiceTimeout(D2GInstancePtr theInstance, gsi_time timeoutMs); + +// NOTE: The following functions should only be used for testing purposes! +GSResult d2gSetServiceURL(D2GInstancePtr theInstance, const char * serviceUrl); + +void d2gDisplayManifestFileContents(D2GInstancePtr theInstance); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif //__DIRECT2GAME_H__ diff --git a/Direct2Game/Direct2GameTest/CMakeLists.txt b/Direct2Game/Direct2GameTest/CMakeLists.txt new file mode 100644 index 00000000..899b0104 --- /dev/null +++ b/Direct2Game/Direct2GameTest/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(Direct2GameTest d2gtest.c) +target_link_libraries(Direct2GameTest usd2g) +set_target_properties(Direct2GameTest PROPERTIES FOLDER Tests) diff --git a/Direct2Game/Direct2GameTest/d2gtest.c b/Direct2Game/Direct2GameTest/d2gtest.c new file mode 100644 index 00000000..aefce433 --- /dev/null +++ b/Direct2Game/Direct2GameTest/d2gtest.c @@ -0,0 +1,2064 @@ +/////////////////////////////////////////////////////////////////////////////// +// This test application uses the GameSpy Direct 2 Game SDK +// Implemented by GameSpy Tech +// Copyright (c) 2008, GameSpy Technology +/////////////////////////////////////////////////////////////////////////////// +// These files need to be added to application includes +#include "../../common/gsCommon.h" +#include "../../common/gsCore.h" +#include "../../common/gsAvailable.h" +#include "../../webservices/AuthService.h" +#include "../Direct2Game.h" +#include "../../gp/gp.h" + +#include +#include +#include +#include + + +#if defined(_WIN32) && !defined(_XBOX) && defined(_DEBUG) && defined(USE_CRTDBG) +#define CRTDBG_MAP_ALLOC +#include +#endif + +#if defined(_NITRO) + #include "../../common/nitro/screen.h" + #define printf Printf + #define vprintf VPrintf +#endif + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// Globals +//STAGE +#define SAMPLE_GAMENAME "gmtest" +#define SAMPLE_GAMEID 0 +#define SAMPLE_PARTERCODE 0 +#define SAMPLE_NAMESPACEID 1 +#define LOCAL_PLAYER_UNIQUENICK "D2GTest02" +#define LOCAL_PLAYER_PASSWORD "IGcash01" +#define GAME_ID 0 +#define GAME_CATALOG_REGION L"en" +#define GAME_CATALOG_VERSION 0 // stands for the default version +//#define GAME_GATALOG_VERSION 2 // use this to view other content +#define MAX_FILEID_DOWNLOADS 10 // for example purposes +#define SAMPLE_ACCESS_TOKEN L"BA42DAE5-D8A4-4762-88C7-4F4CFEFEF34B" +#define SAMPLE_MAX_PURCHASES 3 +// This structure is used only by +// sampleDownloadFileById function + +//DEV +// #define SAMPLE_GAMENAME "gmtest" +// #define SAMPLE_PARTERCODE 0 +// #define SAMPLE_NAMESPACEID 1 +// #define LOCAL_PLAYER_UNIQUENICK "D2GTest02" +// #define LOCAL_PLAYER_PASSWORD "IGcash01" +// #define GAME_ID 1774 +// #define GAME_CATALOG_REGION L"global" +// #define GAME_CATALOG_VERSION 0 // stands for the default version +// //#define GAME_GATALOG_VERSION 2 // use this to view other content +// #define MAX_FILEID_DOWNLOADS 10 // for example purposes +// #define SAMPLE_ACCESS_TOKEN L"9FA19EAE-C9A6-4FFF-8233-30429B3BABFC" +// #define SAMPLE_MAX_PURCHASES 3 + +// This structure is used only by +// sampleDownloadFileById function +typedef struct gameDownloadItem +{ + D2GItemId itemId; + D2GUrlId urlId; + D2GFileId fileId; + float version; +} gameDownloadItem; +typedef struct gameDownloads +{ + int downloadCount; + gameDownloadItem downloads[MAX_FILEID_DOWNLOADS]; +}gameDownloads; + +typedef struct gameAppData +{ + gsi_bool gWaitingForAuth; // simple state control + gsi_bool gLoggedIn; + gsi_bool gWaiting; + gsi_bool gPurchaseComplete; // for a simple purchase example + + GSLoginCertificate gLoginCertificate; + GSLoginPrivateData gLoginPrivData; + gameDownloads gDownloads; + D2GInstancePtr gD2GInstancePtr; + +} gameAppData; + +gameAppData sampleGameAppData; + +int gLocalPlayerID = 0; +const UCS2String gFirstCategoryID = L"0"; +const UCS2String gSampleCategoryBad = L"PC Game Downloads"; +const UCS2String gSampleCategoryGood = L"D2G Levels"; +const UCS2String gSampleExtraInfoKeyName = L"RACE_LANGUAGE"; +const UCS2String gSampleExtraInfoKeyValue = L"Moktar"; +D2GItemId *gLocalItemIds = NULL; +gsi_u32 *gLocalItemQuantities = NULL; +gsi_u32 gLocalItemCount = 0; +gsi_u32 gLocalAccountId = 0; +D2GOrderTotal *gOrderTotal; +D2GOrderPurchase *gOrderPurchase; + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// Prototypes +static gsi_bool sampleBackendAvailable(); +static gsi_bool sampleAuthPlayer(); +void sampleAuthCallback(GHTTPResult result, WSLoginResponse * response, void * userData); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// Run the required availability check (all apps must do this) +static gsi_bool sampleBackendAvailable() +{ + GSIACResult result; + + GSIStartAvailableCheck(SAMPLE_GAMENAME); + do + { + result = GSIAvailableCheckThink(); + msleep(10); + } while(result == GSIACWaiting); + + // check result + if (result == GSIACUnavailable) + { + wprintf(L"Availability check returned GSIACUnavailable -- Backend services have been disabled\r\n"); + return gsi_false; + } + else if (result == GSIACTemporarilyUnavailable) + { + wprintf(L"Availability check returned GSIACTemporarilyUnavailable -- Backend services are temporarily down\r\n"); + return gsi_false; + } + + // GSIACAvailable + return gsi_true; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// Commerce requires authentication against the Gamespy AuthService +static gsi_bool sampleAuthPlayer() +{ + int result = wsLoginUnique(SAMPLE_GAMEID, SAMPLE_PARTERCODE, SAMPLE_NAMESPACEID, LOCAL_PLAYER_UNIQUENICK, LOCAL_PLAYER_PASSWORD, "", sampleAuthCallback, NULL); + if (result != WSLogin_Success) + { + wprintf(L"Failed to start wsLoginUnique: %d\r\n", result); + return gsi_false; + } + + sampleGameAppData.gWaitingForAuth = gsi_true; + while(gsi_is_true(sampleGameAppData.gWaitingForAuth)) + { + msleep(10); + gsCoreThink(0); + } + return sampleGameAppData.gLoggedIn; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleAuthCallback(GHTTPResult result, WSLoginResponse * response, void * userData) +{ + // Check for HTTP errors + if (result != GHTTPSuccess) + { + wprintf(L"HTTP error when logging in: %d\r\n", result); + //gLoggedIn = gsi_false; + sampleGameAppData.gLoggedIn = gsi_false; + } + + // Check server result....invalid password etc + else if (response->mLoginResult != WSLogin_Success) + { + wprintf(L"Login failed, server reported: %d\r\n", response->mLoginResult); + sampleGameAppData.gLoggedIn = gsi_false; + + } + else + { + wprintf(L"Logged in as %d (%S)\r\n", response->mCertificate.mProfileId, response->mCertificate.mUniqueNick); + memcpy(&sampleGameAppData.gLoginCertificate, &response->mCertificate, sizeof(GSLoginCertificate)); + memcpy(&sampleGameAppData.gLoginPrivData, &response->mPrivateData, sizeof(GSLoginPrivateData)); + sampleGameAppData.gLoggedIn = gsi_false; + } + sampleGameAppData.gWaitingForAuth = gsi_false; + GSI_UNUSED(userData); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void printItemValidationCode(GSResult itemValidationCode) +{ + if (GS_RESULT_CODE(itemValidationCode) == D2GResultCode_OrderItem_QuantityRestriction) + { + wprintf(L"Item already purchased or is over quantity limit\n"); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleCheckStoreAvailabilityCallback(GSResult result, D2GGetStoreAvailabilityResponse *response, void *userData) +{ + wprintf(L"\n=====================BEGIN GET STORE AVAILABILITY RESPONSE=====================\r\n"); + + if (GS_FAILED(result)) + { + wprintf(L"Failed on d2gGetStoreAvailability(0x%8x)\n", result); + } + else + { + D2GResultCode availableCode; + availableCode = GS_RESULT_CODE(response->mAvailabilityCode); + if (availableCode == D2GResultCode_StoreOnline) + { + wprintf(L"Store is online\n"); + } + else if (availableCode == D2GResultCode_StoreOfflineForMaintenance) + { + wprintf(L"Store is offline for maintenance\n"); + } + else if (availableCode == D2GResultCode_StoreOfflineRetired) + { + wprintf(L"Store is offline and retired\n"); + } + else if (availableCode == D2GResultCode_StoreNotYetLaunched) + { + wprintf(L"Store is coming soon!\n"); + } + } + wprintf(L"======================END GET STORE AVAILABILITY RESPONSE=======================\r\n"); + + GSI_UNUSED(userData); + sampleGameAppData.gWaiting = gsi_false; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleCheckStoreAvailability(D2GInstancePtr instance, + D2GCatalogPtr d2gCatalog) +{ + GSResult result; + wprintf(L"Getting store availability...\n"); + + result = d2gGetStoreAvailability(instance, d2gCatalog, sampleCheckStoreAvailabilityCallback, NULL); + if (GS_FAILED(result)) + { + wprintf(L"Failed calling d2gBeginGetStoreAvailability (0x%8x)\n", result); + return; + } + + sampleGameAppData.gWaiting = gsi_true; + while(sampleGameAppData.gWaiting) + { + msleep(10); + gsCoreThink(0); + } +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void sampleAppInitializeDownloadList() +{ + int i = 0; + sampleGameAppData.gDownloads.downloadCount = 0; + for (i = 0; i< MAX_FILEID_DOWNLOADS; i++ ) + { + sampleGameAppData.gDownloads.downloads[i].itemId = 0; + sampleGameAppData.gDownloads.downloads[i].urlId = 0; + sampleGameAppData.gDownloads.downloads[i].fileId = 0; + sampleGameAppData.gDownloads.downloads[i].version = 0.0; + + } +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleAppUpdateDownloadList(D2GItemId itemId, D2GUrlId urlId, D2GFileId fileId, float version) +{ + if (sampleGameAppData.gDownloads.downloadCount< MAX_FILEID_DOWNLOADS) + { + int index = sampleGameAppData.gDownloads.downloadCount++ ; + sampleGameAppData.gDownloads.downloads[index].itemId = itemId; + sampleGameAppData.gDownloads.downloads[index].urlId = urlId; + sampleGameAppData.gDownloads.downloads[index].fileId = fileId; + sampleGameAppData.gDownloads.downloads[index].version = version; + wprintf(L"sampleAppUpdateDownloadList inserted FileId %d. Download Count %d.\n", fileId, (index+1)); + } + else + { + wprintf(L"sampleAppUpdateDownloadList FAILED to insert FileId. Maximum number is reached.\n"); + } + +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void traverseOrderItemPurchases(D2GOrderItemPurchase *items) +{ + if (items != NULL) + { + gsi_u32 i,k; + D2GOrderItem anOrderItem = items->mOrderItem ; + + wprintf(L"-----------------------------------------------------------------\r\n"); + if (anOrderItem.mValidation.mIsValid) + wprintf(L"Valid\r\n"); + else + wprintf(L"Invalid, check result code in validation\r\n"); + wprintf(L"\t Item name: %s\r\n", anOrderItem.mItem.mName); + wprintf(L"\t Item Id: %d\r\n", anOrderItem.mItem.mItemId); + wprintf(L"\t Item External Code: %s\r\n", anOrderItem.mItem.mExternalItemCode); + wprintf(L"\t Item Unite Price: %s\r\n", anOrderItem.mItem.mPrice); + wprintf(L"\t Item Tax: %s\r\n", anOrderItem.mItem.mTax); + wprintf(L"\t Item Quantity: %d\r\n", anOrderItem.mItemTotal.mQuantity); + wprintf(L"\t Item Subtotal: %s\r\n", anOrderItem.mItemTotal.mSubTotal); + wprintf(L"\t Item Total: %s\r\n", anOrderItem.mItemTotal.mTotal); + + // Print licenses here + wprintf(L"\n\t Number of Licenses: %d\r\n\n", items->mLicenseList.mCount); + for (k= 0 ; k < items->mLicenseList.mCount; k++) + { + wprintf(L"\t License %i:\r\n", k); + wprintf(L"\t Licenses Name: %s\r\n", items->mLicenseList.mLicenses[k].mLicenseName); + wprintf(L"\t License Key: %s\r\n\n", items->mLicenseList.mLicenses[k].mLicenseKey); + } + // Print Downloads here + wprintf(L"\tNumber of Downloads: %d\r\n\n", items->mDownloadList.mCount); + for (k= 0 ; k < items->mDownloadList.mCount; k++) + { + wprintf(L"\t Download %i:\r\n", k); + + wprintf(L"\tDownload Asset Type: %s\r\n", items->mDownloadList.mDownloads[k].mAssetType ? items->mDownloadList.mDownloads[k].mAssetType : L""); + wprintf(L"\t Download Name: %s\r\n", items->mDownloadList.mDownloads[k].mName); + wprintf(L"\t Download URL Id: %i\r\n", items->mDownloadList.mDownloads[k].mUrlId); + wprintf(L"\t Download File Id: %i\r\n", items->mDownloadList.mDownloads[k].mFileId); + wprintf(L"\t Download Version: %f\r\n", items->mDownloadList.mDownloads[k].mVersion); + wprintf(L"\t Download Sequence: %i\r\n", items->mDownloadList.mDownloads[k].mSequence); + + // Call the sample's function to update the download list + // Note this is only to provide an example + sampleAppUpdateDownloadList(anOrderItem.mItem.mItemId, + items->mDownloadList.mDownloads[k].mUrlId, + items->mDownloadList.mDownloads[k].mFileId, + items->mDownloadList.mDownloads[k].mVersion); + } + + for (i = 0; i < items->mPurchaseList.mCount; i++) + { + traverseOrderItemPurchases(&items->mPurchaseList.mOrderItemPurchases[i]); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleDisplayCatalogItem(D2GCatalogItem *anItem) +{ + gsi_u32 j = 0; + + wprintf(L"\t Name:%s\r\n" + L"\t Item Id:%i\r\n" + L"\t Developer:%s\r\n" + L"\t Publisher:%s\r\n" + L"\t Price:%s\r\n" + L"\t Tax:%s\r\n" + L"\t Culture:%s\r\n" + L"\t Currency:%s\r\n" + L"\t Summary:%s\r\n", + anItem->mItem.mName, + anItem->mItem.mItemId, + anItem->mProductInfo.mDeveloper, + anItem->mProductInfo.mPublisher, + anItem->mItem.mPrice, + anItem->mItem.mTax, + anItem->mGeoInfo.mCultureCode, + anItem->mGeoInfo.mCurrencyCode, + anItem->mProductInfo.mSummary); + printf("\t Release Date:%s\r\n",gsiSecondsToString(&anItem->mProductInfo.mReleaseDate)); + wprintf(L"\t Total File Size:%i\r\n",anItem->mProductInfo.mFileSize); + + if (wcslen(anItem->mItem.mExternalItemCode) > 0) + { + wprintf(L"\tExternalItemCode:%s\r\n", anItem->mItem.mExternalItemCode); + } + + wprintf(L"\t Image count: %d\r\n", anItem->mImages.mCount); + for (j = 0; j < anItem->mImages.mCount; j++) + { + wprintf(L"\t ImageUrl:%s\r\n", anItem->mImages.mImageName[j]); + } + + wprintf(L"\t Category count: %d\r\n", anItem->mCategories.mCount); + for (j = 0; j < anItem->mCategories.mCount; j++) + { + wprintf(L"\t Category:%s\r\n", anItem->mCategories.mCategoryNames[j]); + } + + wprintf(L"\t Extra Info key/value pairs count: %d\r\n", anItem->mExtraItemInfoList.mCount); + for (j = 0; j < anItem->mExtraItemInfoList.mCount; j++) + { + UCS2String myValue; + GSResult result; + wprintf(L"\t ExtraInfo key %d: %s\r\n", j, anItem->mExtraItemInfoList.mExtraInfoElements[j].mKey); + result = d2gGetExtraItemInfoKeyValueByKeyName(anItem, anItem->mExtraItemInfoList.mExtraInfoElements[j].mKey, &myValue); + if (GS_SUCCEEDED(result)) + { + wprintf(L"\t ExtraInfo value %d: %s\r\n", j, myValue); + gsifree(myValue); + } + } + +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleShowAllItemsCallback(GSResult result, + D2GLoadCatalogItemsResponse * response, + void * userData) +{ + wprintf(L"\n=======================BEGIN GET ALL ITEMS RESPONSE=======================\r\n"); + + if (GS_FAILED(result)) + { + wprintf(L"d2gBeginLoadCatalogItems failed! Result = %X\r\n", result); + } + else + { + if (response->mItemList) + { + gsi_u32 i; + D2GCatalogItemList *lItemList, *lItemList2; + D2GCatalogItemList *filteredItems = NULL; + gLocalItemCount = SAMPLE_MAX_PURCHASES < response->mItemList->mCount ? + SAMPLE_MAX_PURCHASES : + response->mItemList->mCount; + gLocalItemIds = (D2GItemId *)gsimalloc(sizeof(D2GItemId) * gLocalItemCount); + gLocalItemQuantities = (gsi_u32 *)gsimalloc(sizeof(gsi_u32) * gLocalItemCount); + + + wprintf(L"Got All Items, Items shown below\r\n"); + wprintf(L"Number of items: %d\r\n", response->mItemList->mCount); + for (i = 0; i < response->mItemList->mCount; i++) + { + D2GCatalogItem *anItem = &response->mItemList->mCatalogItems[i]; + // save itemId for use later + if (gLocalItemIds && i < gLocalItemCount) + { + gLocalItemIds[i] = anItem->mItem.mItemId; + gLocalItemQuantities[i] = 1; + } + + wprintf(L"==================================================\r\n"); + wprintf(L"Item # %d\r\n", i); + sampleDisplayCatalogItem(anItem); + + } + + lItemList = response->mItemList; + lItemList2 = (D2GCatalogItemList *)gsimalloc(sizeof(D2GCatalogItemList)); + + //For testing d2gAppendCatalogItems + d2gCloneCatalogItemList(lItemList2, response->mItemList); + d2gAppendCatalogItemList(lItemList, lItemList2); + + printf("Sort by name in descending order \n"); + d2gSortCatalogItemsbyName(lItemList,D2GSort_Descending); + printf("\n"); + + for (i = 0; i < lItemList->mCount; i++) + { + D2GCatalogItem *anItem = &lItemList->mCatalogItems[i]; + + wprintf(L"==================================================\r\n"); + wprintf(L"Item # %d\r\n", i); + + sampleDisplayCatalogItem(anItem); + + } + + printf("Sort by price in ascending order\n"); + d2gSortCatalogItemsbyPrice(lItemList,D2GSort_Ascending); + + for (i = 0; i < lItemList->mCount; i++) + { + D2GCatalogItem *anItem = &lItemList->mCatalogItems[i]; + + wprintf(L"==================================================\r\n"); + wprintf(L"Item # %d\r\n", i); + + sampleDisplayCatalogItem(anItem); + + } + + printf("Sort by release date in descending order \n"); + d2gSortCatalogItemsbyReleaseDate(lItemList,D2GSort_Descending); + for (i = 0; i < lItemList->mCount; i++) + { + D2GCatalogItem *anItem = &lItemList->mCatalogItems[i]; + + wprintf(L"==================================================\r\n"); + wprintf(L"Item # %d\r\n", i); + + sampleDisplayCatalogItem(anItem); + } + printf("Sort by item id in descending order \n"); + d2gSortCatalogItemsbyItemId(lItemList,D2GSort_Descending); + for (i = 0; i < lItemList->mCount; i++) + { + D2GCatalogItem *anItem = &lItemList->mCatalogItems[i]; + + wprintf(L"==================================================\r\n"); + wprintf(L"Item # %d\r\n", i); + + sampleDisplayCatalogItem(anItem); + } + + printf("Sort by external id in descending order \n"); + d2gSortCatalogItemsbyExternalId(lItemList,D2GSort_Descending); + for (i = 0; i < lItemList->mCount; i++) + { + D2GCatalogItem *anItem = &lItemList->mCatalogItems[i]; + + wprintf(L"==================================================\r\n"); + wprintf(L"Item # %d\r\n", i); + + sampleDisplayCatalogItem(anItem); + } + + printf("Sort by file size in descending order \n"); + d2gSortCatalogItemsbySize(lItemList,D2GSort_Descending); + for (i = 0; i < lItemList->mCount; i++) + { + D2GCatalogItem *anItem = &lItemList->mCatalogItems[i]; + + wprintf(L"==================================================\r\n"); + wprintf(L"Item # %d\r\n", i); + + sampleDisplayCatalogItem(anItem); + } + + d2gFilterCatalogItemListByKeyName(response->mItemList, &filteredItems, gSampleExtraInfoKeyName); + if (filteredItems != NULL) + { + for (i = 0; i < filteredItems->mCount; i++) + { + D2GCatalogItem *anItem = &filteredItems->mCatalogItems[i]; + + wprintf(L"==================================================\r\n"); + wprintf(L"Item # %d\r\n", i); + + sampleDisplayCatalogItem(anItem); + } + d2gFreeCatalogItemList(filteredItems); + filteredItems = NULL; + } + else + { + wprintf(L"NOTE: List returned with empty results!"); + } + d2gFilterCatalogItemListByKeyNameValue(response->mItemList, &filteredItems, gSampleExtraInfoKeyName, + L"");//gSampleExtraInfoKeyValue); + if (filteredItems != NULL) + { + for (i = 0; i < filteredItems->mCount; i++) + { + D2GCatalogItem *anItem = &filteredItems->mCatalogItems[i]; + + wprintf(L"==================================================\r\n"); + wprintf(L"Item # %d\r\n", i); + + sampleDisplayCatalogItem(anItem); + } + } + else + { + wprintf(L"NOTE: List returned with empty results!"); + } + + // Clean up the list and any references to it that the callback gave us. + d2gFreeCatalogItemList(lItemList); + lItemList = NULL; + response->mItemList = NULL; + + //For testing d2gAppendCatalogItems + d2gFreeCatalogItemList(lItemList2); + lItemList2 = NULL; + + // Clean up the filtered Item list based on extra info key + d2gFreeCatalogItemList(filteredItems); + filteredItems = NULL; + } + } + + wprintf(L"=======================END GET ALL ITEMS RESPONSE=======================\r\n"); + sampleGameAppData.gWaiting = gsi_false; + GSI_UNUSED(userData); +} + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// This is sample app to retrieve categories from the catalog +// +void sampleGetCategories(D2GInstancePtr d2gInstance, + D2GCatalogPtr d2gCatalog) +{ + GSResult result = GS_SUCCESS; + D2GCategoryList categoryList; + gsi_u32 i = 0; + + result = d2gGetCategories(d2gInstance, d2gCatalog, &categoryList); + wprintf(L"=======================BEGIN CATEGORIES =======================\r\n"); + if (GS_SUCCEEDED(result)) + { + for (i= 0; i< categoryList.mCount; i++) + { + wprintf(L"%s \r\n", categoryList.mCategoryNames[i]); + } + } + else + { + wprintf(L" Failed to get Categories . Result code = 0x%x", result); + } + wprintf(L"=======================END CATEGORIES =========================\r\n"); + + //////// + // IMPORTANT: release the memory allocated for category list with + // gsifree() function + // + gsifree(categoryList.mCategoryNames); + +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleShowAllItems(D2GInstancePtr d2gInstance, + D2GCatalogPtr d2gCatalog) +{ + GSResult result; + sampleGameAppData.gWaiting = gsi_true; + result = d2gLoadCatalogItems(d2gInstance, d2gCatalog, sampleShowAllItemsCallback, NULL); + if (GS_FAILED(result)) + { + wprintf(L"Failed on d2gBeginLoadCatalogItems (0x%8x)\n", result); + return; + } + + while(sampleGameAppData.gWaiting) + { + msleep(10); + gsCoreThink(0); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleShowItemsByCategoryCallback(GSResult result, D2GLoadCatalogItemsByCategoryResponse * response, void * userData) +{ + wprintf(L"\n===================BEGIN GET ITEMS BY CATEGORY RESPONSE=======================\r\n"); + + if (GS_FAILED(result)) + { + wprintf(L"d2gBeginGetItemsBy Category failed! Result = %X\r\n", result); + } + else + { + if (response->mItemList) + { + UCS2String sampleCategory; + gsi_u32 i; + + sampleCategory = (UCS2String)userData; + wprintf(L"Got Items in category %s, Items shown below\r\n", sampleCategory); + wprintf(L"Number of items: %d\r\n", response->mItemList->mCount); + + for (i = 0; i < response->mItemList->mCount; i++) + { + D2GCatalogItem *anItem = &response->mItemList->mCatalogItems[i]; + + // save itemId for use later + wprintf(L"==================================================\r\n"); + wprintf(L"Item # %d\r\n", i); + sampleDisplayCatalogItem(anItem); + } + } + else + wprintf(L"Successfully got data, but list was empty\n"); + // Clear out catalog item list that we won't be using later explicitly + // because the callback gives us control of the list and we're not + // referencing it later. + d2gFreeCatalogItemList(response->mItemList); + response->mItemList = NULL; + } + wprintf(L"===================END GET ITEMS BY CATEGORY RESPONSE=======================\r\n"); + sampleGameAppData.gWaiting = gsi_false; + GSI_UNUSED(userData); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleShowItemsByCategory(D2GInstancePtr d2gInstance, + D2GCatalogPtr d2gCatalog, + const UCS2String aCategory) +{ + GSResult result; + sampleGameAppData.gWaiting = gsi_true; + result = d2gLoadCatalogItemsByCategory(d2gInstance, d2gCatalog, aCategory, sampleShowItemsByCategoryCallback, (void *)aCategory); + if (GS_FAILED(result)) + { + wprintf(L"Failed on d2gLoadCatalogItemsByCategory (0x%8x)\n", result); + return; + } + + while(sampleGameAppData.gWaiting) + { + msleep(10); + gsCoreThink(0); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleGetUserCreditCardsCallback(GSResult result, D2GGetUserCreditCardsResponse * response, void * userData) +{ + + wprintf(L"\n====================BEGIN GET CREDIT CARDS RESPONSE=======================\r\n"); + + if (GS_FAILED(result)) + { + wprintf(L"d2gBeginGetUserCreditCards failed! Result = %X\r\n", result); + } + else + { + wprintf(L"=======================BEGIN GET CREDIT CARDS RESPONSE=======================\r\n"); + if (response->mListOfCreditCards->mCount) + { + gsi_u32 i; + + wprintf(L"Got credit cards, number of accounts: %d\r\n", response->mListOfCreditCards->mCount); + for (i = 0; i < response->mListOfCreditCards->mCount; i++) + { + D2GCreditCardInfo *cinfo = &response->mListOfCreditCards->mCreditCardInfos[i]; + struct tm *expire = gsiSecondsToDate(&cinfo->mExpirationDate); + + wprintf(L"==================================================\r\n"); + wprintf(L"Account %d\r\n", i); + wprintf(L"\t AccountId: %d\r\n" + L"\t Credit Card Type: %s\r\n" + L"\t Expire date: %d-%d\r\n" + L"\t Default: %s\r\n" + L"\t Last four digits: %d\r\n", + cinfo->mAccountId, + cinfo->mCreditCardType, + expire->tm_year + 1900, expire->tm_mon + 1, + cinfo->mIsDefault ? L"true" : L"false", + cinfo->mLastFourDigits); + if (i == 0) + gLocalAccountId = cinfo->mAccountId; + } + + + } + + // since we got the account id, we won't need any of the + // rest of the account info. + d2gFreeCreditCardInfoList(response->mListOfCreditCards); + response->mListOfCreditCards = NULL; + } + + wprintf(L"======================END GET CREDIT CARDS RESPONSE=========================\r\n"); + + sampleGameAppData.gWaiting = gsi_false; + GSI_UNUSED(response); + GSI_UNUSED(userData); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleGetUserCreditCards(D2GInstancePtr d2gInstance, + D2GCatalogPtr d2gCatalog) +{ + GSResult result; + gsi_bool validOnly = gsi_false; + + result = d2gGetUserCreditCards(d2gInstance, d2gCatalog, validOnly, sampleGetUserCreditCardsCallback, NULL); + if (GS_FAILED(result)) + { + wprintf(L"Failed on d2gBeginGetUserCreditCards (0x%8x)\n", result); + return; + } + + sampleGameAppData.gWaiting = gsi_true; + while(sampleGameAppData.gWaiting) + { + msleep(10); + gsCoreThink(0); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleGetOrderTotalCallback(GSResult result, D2GGetOrderTotalResponse * response, void * userData) +{ + + wprintf(L"\n=====================BEGIN GET ORDER TOTAL RESPONSE=========================\r\n"); + + if (GS_FAILED(result)) + { + wprintf(L"sampleGetOrderTotal failed! Result = %X\r\n", result); + + // Error handling should be done here. + // We also need to release the memory allocated during the response. + d2gFreeOrderTotal(response->mOrderTotal); + response->mOrderTotal = NULL; + } + else + { + if (response->mOrderTotal) + { + // The order in the response is a pointer to the pending order that the SDK stores. + gsi_u32 i; + + wprintf(L"sampleGetOrderTotal successful, order listed below: \r\n"); + + wprintf(L"\t Account Id: %d\r\n", response->mOrderTotal->mOrder.mAccountId); + wprintf(L"\t Culture: %s\r\n", response->mOrderTotal->mOrder.mGeoInfo.mCultureCode); + wprintf(L"\t Currency: %s\r\n", response->mOrderTotal->mOrder.mGeoInfo.mCurrencyCode); + wprintf(L"\t Subtotal: %s\r\n", response->mOrderTotal->mOrder.mSubTotal); + wprintf(L"\t Tax: %s\r\n", response->mOrderTotal->mOrder.mTax); + wprintf(L"\t Total: %s\r\n", response->mOrderTotal->mOrder.mTotal); + wprintf(L"\t Root Order Guid: %s\r\n", response->mOrderTotal->mOrder.mRootOrderGuid); + printf("\t Date Quoted: %s\n", gsiSecondsToString(&response->mOrderTotal->mQuoteDate)); + + if (response->mOrderTotal->mOrder.mValidation.mIsValid) + wprintf(L"Order is valid\r\n"); + else + wprintf(L"Order is invalid, check result code and message\r\n"); + + + wprintf(L"Number of items in order total: %d\r\n", response->mOrderTotal->mOrderItemList.mCount); + for (i = 0; i < response->mOrderTotal->mOrderItemList.mCount; i++) + { + D2GOrderItem pOrderItem = response->mOrderTotal->mOrderItemList.mOrderItems[i]; + + wprintf(L"=====================\r\n"); + wprintf(L"Item %d, ", i); + if (pOrderItem.mValidation.mIsValid) + wprintf(L"Valid\r\n"); + else + { + wprintf(L"Invalid\r\n"); + printItemValidationCode(pOrderItem.mValidation.mResult); + } + wprintf(L"\t Item name: %s\r\n", pOrderItem.mItem.mName); + wprintf(L"\t Item Id: %d\r\n", pOrderItem.mItem.mItemId); + wprintf(L"\t Item External Code: %s\r\n", pOrderItem.mItem.mExternalItemCode); + wprintf(L"\t Item Unite Price: %s\r\n", pOrderItem.mItem.mPrice); + wprintf(L"\t Item Unite Tax: %s\r\n", pOrderItem.mItem.mTax); + wprintf(L"\t Item Quantity: %d\r\n", pOrderItem.mItemTotal.mQuantity); + wprintf(L"\t Item Subtotal: %s\r\n", pOrderItem.mItemTotal.mSubTotal); + wprintf(L"\t Item Total: %s\r\n", pOrderItem.mItemTotal.mTotal); + + } + + if (response->mOrderTotal->mOrder.mValidation.mIsValid) + { + // IMPORTANT: We need to keep track of the Order Total because it will be + // used to confirm the placement of an order. Once the order is successfully + // started, we destroy it. + gOrderTotal = response->mOrderTotal; + } + else + { + // Since the order is returned as invalid. Error handling should be done here. + // We also need to release the memory allocated during the response. + d2gFreeOrderTotal(response->mOrderTotal); + response->mOrderTotal = NULL; + } + + + } + wprintf(L"==========================END GET ORDER TOTAL RESPONSE==========================\r\n"); + } + wprintf(L"======================END GET ORDER TOTAL RESPONSE==========================\r\n"); + + GSI_UNUSED(response); + GSI_UNUSED(userData); + sampleGameAppData.gWaiting = gsi_false; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleGetOrderTotal(D2GInstancePtr d2gInstance, D2GCatalogPtr d2gCatalog) +{ + GSResult result; + + D2GItemId *itemIds = gLocalItemIds; + gsi_u32 *itemQuantities = gLocalItemQuantities; + gsi_u32 itemCount = gLocalItemCount; + + if (gLocalItemIds) + { + result = d2gGetOrderTotal(d2gInstance, + d2gCatalog, + gLocalAccountId, + itemIds, + itemCount, + itemQuantities, + sampleGetOrderTotalCallback, + NULL); + if (GS_FAILED(result)) + { + wprintf(L"Failed on sampleGetOrderTotal (0x%8x)\n", result); + return; + } + + sampleGameAppData.gWaiting = gsi_true; + while(sampleGameAppData.gWaiting) + { + msleep(10); + gsCoreThink(0); + } + } +} + + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleStartOrderCallback(GSResult result, D2GStartOrderResponse * response, void * userData) +{ + + wprintf(L"\n=====================BEGIN PURCHASE RESPONSE=========================\r\n"); + if (GS_FAILED(result)) + { + wprintf(L"sampleBeginPurchase failed! Result = %X\r\n", result); + } + else + { + if (response->mOrderPurchase) + { + // The order in the response is a pointer to the purchased order that the SDK stores. + gsi_u32 i; + wprintf(L"sampleGetOrderTotal successful, order listed below: \r\n"); + + wprintf(L"\t Account Id: %d\r\n", response->mOrderPurchase->mOrder.mAccountId); + wprintf(L"\t Currency: %s\r\n", response->mOrderPurchase->mOrder.mGeoInfo.mCurrencyCode); + wprintf(L"\t Culture: %s\r\n", response->mOrderPurchase->mOrder.mGeoInfo.mCultureCode); + wprintf(L"\t Subtotal: %s\r\n", response->mOrderPurchase->mOrder.mSubTotal); + wprintf(L"\t Tax: %s\r\n", response->mOrderPurchase->mOrder.mTax); + wprintf(L"\t Total: %s\r\n", response->mOrderPurchase->mOrder.mTotal); + wprintf(L"\t Root Order Guid: %s\r\n", response->mOrderPurchase->mOrder.mRootOrderGuid); + printf("\t Date Purchased: %s\r\n", gsiSecondsToString(&response->mOrderPurchase->mPurchaseDate)); + + if (response->mOrderPurchase->mOrder.mValidation.mIsValid) + wprintf(L"Order is valid\r\n"); + else + { + wprintf(L"Order is invalid\r\n"); + wprintf(L"Check order items for individual validations"); + } + + wprintf(L"Number of items in order total: %d\r\n", response->mOrderPurchase->mItemPurchases.mCount); + for (i = 0; i < response->mOrderPurchase->mItemPurchases.mCount; i++) + { + D2GOrderItemPurchase anItemPurchase = response->mOrderPurchase->mItemPurchases.mOrderItemPurchases[i]; + wprintf(L"=====================\r\n"); + wprintf(L"Item %d, ", i); + if (anItemPurchase.mOrderItem.mValidation.mIsValid) + wprintf(L"Valid\r\n"); + else + wprintf(L"Invalid\r\n"); + + // Initialize the sample applications download file count here + // Since the traverseOrderItemPurchases will call sampleAppUpdateDownloadList() + sampleAppInitializeDownloadList(); + traverseOrderItemPurchases(&response->mOrderPurchase->mItemPurchases.mOrderItemPurchases[i]); + } + + if (response->mOrderPurchase->mOrder.mValidation.mIsValid) + { + // IMPORTANT: We need to keep track of the Order Purchase because it will be + // used during the confirmation of an order. Once the order is successfully + // confirmed, we destroy it. + gOrderPurchase = response->mOrderPurchase; + } + else + { + // Since the order is invalid, if no longer needed, cleanup the response object here. + d2gFreeOrderPurchase(response->mOrderPurchase); + response->mOrderPurchase = NULL; + } + + } + + } + + // We're freeing this here because this is a test. + // But Developers needs to free this at their discretion + // because they may be retrying if the request fails. + d2gFreeOrderTotal(gOrderTotal); + gOrderTotal = NULL; + + wprintf(L"=======================END PURCHASE RESPONSE==========================\r\n"); + GSI_UNUSED(response); + GSI_UNUSED(userData); + sampleGameAppData.gWaiting = gsi_false; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleBeginPurchase(D2GInstancePtr d2gInstance, + D2GCatalogPtr d2gCatalog) +{ + GSResult result; + + if (gOrderTotal && gOrderTotal->mOrder.mValidation.mIsValid) + { + // Note that the current pending order is stored by the SDK + result = d2gStartOrder(d2gInstance, d2gCatalog, gOrderTotal, sampleStartOrderCallback, NULL); + + if (GS_FAILED(result)) + { + wprintf(L"Failed on sampleGetOrderTotal (0x%8x)\n", result); + return; + } + + sampleGameAppData.gWaiting = gsi_true; + while(sampleGameAppData.gWaiting) + { + msleep(10); + gsCoreThink(0); + } + } + else + { + wprintf(L"Order is invalid, please check order before proceeding\n"); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleIsOrderCompleteCallback(GSResult result, D2GIsOrderCompleteResponse * response, void * userData) +{ + + wprintf(L"\n=====================BEGIN IS ORDER COMPLETE RESPONSE=========================\r\n"); + if (GS_FAILED(result)) + { + wprintf(L"sampleIsOrderComplete failed! Result = %X\r\n", result); + } + else + { + if (response->mOrderPurchase) + { + // The order in the response is a pointer to the purchased order that the SDK stores. + gsi_u32 i; + wprintf(L"sampleIsOrderCompleteCallback successful, order listed below: \r\n"); + + wprintf(L"\t Account Id: %d\r\n", response->mOrderPurchase->mOrder.mAccountId); + wprintf(L"\t Currency: %s\r\n", response->mOrderPurchase->mOrder.mGeoInfo.mCurrencyCode); + wprintf(L"\t Culture: %s\r\n", response->mOrderPurchase->mOrder.mGeoInfo.mCultureCode); + wprintf(L"\t Subtotal: %s\r\n", response->mOrderPurchase->mOrder.mSubTotal); + wprintf(L"\t Tax: %s\r\n", response->mOrderPurchase->mOrder.mTax); + wprintf(L"\t Total: %s\r\n", response->mOrderPurchase->mOrder.mTotal); + wprintf(L"\t Root Order Guid: %s\r\n", response->mOrderPurchase->mOrder.mRootOrderGuid); + printf("\t Date Purchased: %s\r\n", gsiSecondsToString(&response->mOrderPurchase->mPurchaseDate)); + + if (response->mOrderPurchase->mOrder.mValidation.mIsValid) + wprintf(L"Order is valid\r\n"); + else + wprintf(L"Order is invalid\r\n"); + + if (GS_SUCCEEDED(response->mOrderPurchase->mOrder.mValidation.mResult)) + { + switch (GS_RESULT_CODE(response->mOrderPurchase->mOrder.mValidation.mResult)) + { + // We're handling two cases here for now. + // Other cases will be handled in this app accordingly in the future. + case D2GResultCode_Order_Ok: + // we should be able to download the file after this + printf("\t Order Status: Completed.\n"); + sampleGameAppData.gPurchaseComplete = gsi_true; + break; + case D2GResultCode_Order_BillingPending: + printf("\t Order Status: Billing Pending.\n"); + break; + default: + printf("Order succeeded with an unknown status code\n"); + } + } + + + wprintf(L"Number of items in order total: %d\r\n", response->mOrderPurchase->mItemPurchases.mCount); + for (i = 0; i < response->mOrderPurchase->mItemPurchases.mCount; i++) + { + D2GOrderItemPurchase anItemPurchase = response->mOrderPurchase->mItemPurchases.mOrderItemPurchases[i]; + + wprintf(L"=====================\r\n"); + wprintf(L"Item %d, ", i); + + if (anItemPurchase.mOrderItem.mValidation.mIsValid) + wprintf(L"Valid\r\n"); + else + wprintf(L"Invalid, check result code in validation\r\n"); + + sampleAppInitializeDownloadList(); + traverseOrderItemPurchases(&response->mOrderPurchase->mItemPurchases.mOrderItemPurchases[i]); + + } + + + // Free the confirmed order purchase also since we don't have a use for it + // after the callback. + d2gFreeOrderPurchase(response->mOrderPurchase); + response->mOrderPurchase = NULL; + } + + } + + + wprintf(L"======================END IS ORDER COMPLETE RESPONSE==========================\r\n"); + + GSI_UNUSED(userData); + sampleGameAppData.gWaiting = gsi_false; +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleIsOrderComplete(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog) +{ + GSResult result; + + sampleGameAppData.gPurchaseComplete = gsi_false; + if (gOrderPurchase && gOrderPurchase->mOrder.mValidation.mIsValid) + { + // Note that the current pending order is stored by the SDK + // Call this function 2 seconds after the callback to place order is received + // Call this function subsequently no more than once every 5 seconds + result = d2gIsOrderComplete(theInstance, + theCatalog, + gOrderPurchase, + sampleIsOrderCompleteCallback, + NULL); + if (GS_FAILED(result)) + { + wprintf(L"Failed on sampleIsOrderComplete (0x%8x)\n", result); + return; + } + + sampleGameAppData.gWaiting = gsi_true; + while(sampleGameAppData.gWaiting) + { + msleep(10); + gsCoreThink(0); + } + } + else + { + wprintf(L"sampleIsOrderComplete: Order is invalid, please check order before proceeding\n"); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleIsOrderCompleteCheck(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog) +{ + + // Note that this is just a simple example of how the IsOrderComplete could be used + if (gOrderPurchase && gOrderPurchase->mOrder.mValidation.mIsValid) + { + int retrialCount = 3; + int i = 0; + sampleGameAppData.gPurchaseComplete = gsi_false; + for (i = 0; ((i< retrialCount) && !sampleGameAppData.gPurchaseComplete); i++) + { + // Call this function 2 seconds after the callback to place order is received + // Call this function subsequently no more than once every 5 seconds + sampleIsOrderComplete(theInstance, theCatalog); + if (i==0) + { + msleep(2000); + } + else + { + msleep(5000); + } + } + if (!sampleGameAppData.gPurchaseComplete) + { + // The Order Result codes are provided in the callback. + wprintf(L"Order could not be completed\n"); + } + + // Free the order purchases since it is not needed anymore. + // But developers must free this object at some point in their app. + d2gFreeOrderPurchase(gOrderPurchase); + gOrderPurchase = NULL; + } + else + { + wprintf(L"Order is invalid, please check order before proceeding\n"); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void DownloadProgressCallback(gsi_u32 bytesReceived, gsi_u32 bytesTotal, void *userData) +{ + if ((bytesReceived * 100/ bytesTotal) %10 == 0) + wprintf(L"sampleDownloadFileById: received %d / %d \r\n", bytesReceived, bytesTotal); + + GSI_UNUSED(userData); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void DownloadCompleteCallback(GSResult result, gsi_char *saveFile, void *userData) +{ + wprintf(L"\n======================BEGIN DOWNLOAD FILE ID RESPONSE=========================\r\n"); + + if (GS_FAILED(result)) + { + wprintf(L"sampleDownloadFileById failed! Result = %X\r\n", result); + } + else + { + wprintf(L"sampleDownloadFileById complete with filename: %S\r\n", saveFile); + } + wprintf(L"======================END DOWNLOAD FILE ID RESPONSE=============================\r\n"); + + sampleGameAppData.gWaiting = gsi_false; + GSI_UNUSED(userData); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +const gsi_char *gDownloadLocation = _T("maps\\"); +void sampleDownloadFileById(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + D2GFileId fileId) +{ + GSResult result; + + if (fileId != 0) + { + // Keep in mind that this starts a download thread so thinking does not need to be done here. + // The benefits are non-blocking calls, callbacks will let developer know of status and completion. + + result = d2gStartDownloadFileById(theInstance, + theCatalog, + fileId, + gDownloadLocation, + DownloadProgressCallback, + DownloadCompleteCallback, + NULL); + + sampleGameAppData.gWaiting = gsi_true; + while(sampleGameAppData.gWaiting) + { + msleep(10); + gsCoreThink(0); + } + } + else + { + wprintf(L"sampleDownloadFileById: uninitialized fileId\n"); + } + +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleDownloadListOfFilesById(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog) +{ + int i = 0; + + wprintf(L"Number of Downloads available %d \n", sampleGameAppData.gDownloads.downloadCount); + for (i=0; i< sampleGameAppData.gDownloads.downloadCount; i++) + { + D2GFileId fileId = sampleGameAppData.gDownloads.downloads[i].fileId; + if (fileId != 0 ) + { + // we have a fileId start downloading + sampleDownloadFileById(theInstance, theCatalog, fileId); + } + else + { + wprintf(L"sampleDownloadListOfFilesById: uninitialized fileId\n"); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// This sample is for updating the manifest file after a successful installation +// of a just purchased product. +void sampleUpdateInstalledContent(D2GInstancePtr theInstance) +{ + int i = 0; + + wprintf(L"\n======================BEGIN UPDATE MANIFEST INSTALLED CONTENT ======================\r\n"); + + wprintf(L"Number of Downloads %d \n", sampleGameAppData.gDownloads.downloadCount); + for (i=0; i< sampleGameAppData.gDownloads.downloadCount; i++) + { + D2GDownloadItem downloadItem ; + D2GItemId itemId = sampleGameAppData.gDownloads.downloads[i].itemId; + + downloadItem.mUrlId = sampleGameAppData.gDownloads.downloads[i].urlId; + downloadItem.mVersion = sampleGameAppData.gDownloads.downloads[i].version; + if (downloadItem.mUrlId != 0 ) + { + GSResult result = GS_SUCCESS; + // we have a fileId start downloading + result = d2gUpdateManifestInstalledContent(theInstance, itemId, &downloadItem); + if (GS_SUCCEEDED(result)) + { + wprintf(L"Updated Installed Content \n"); + wprintf(L"\t Item Id : %d\n", itemId); + wprintf(L"\t URL Id : %d\n", downloadItem.mUrlId); + wprintf(L"\t Version : %f\n", downloadItem.mVersion); + } + } + else + { + wprintf(L"sampleUpdateInstalledContent: uninitialized download item\n"); + } + } + wprintf(L"\n======================END UPDATE MANIFEST INSTALLED CONTENT ======================\r\n"); + +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleGetPurchaseHistoryCallback(GSResult result, + D2GGetPurchaseHistoryResponse * response, + void *userData) +{ + gsi_u32 j=0; + + wprintf(L"\n======================BEGIN GET PURCHASE HISTORY RESPONSE ======================\r\n"); + if (GS_FAILED(result)) + { + wprintf(L"sampleGetPurchaseHistory failed! Result = %X\r\n", result); + } + else + { + wprintf(L"sampleGetOrderTotal successful, order listed below: \r\n"); + + sampleAppInitializeDownloadList(); + + for (j = 0; j < response->mPurchaseHistory->mCount; j++) + { + gsi_u32 i; + wprintf(L"=========================PURCHASE ORDER[%i]=========================\r\n", j); + + // The order in the response is a pointer to the purchased order that the SDK stores. + + wprintf(L"\t Account Id: %d\r\n", response->mPurchaseHistory->mPurchases[j].mOrder.mAccountId); + wprintf(L"\t Currency: %s\r\n", response->mPurchaseHistory->mPurchases[j].mOrder.mGeoInfo.mCurrencyCode); + wprintf(L"\t Culture: %s\r\n", response->mPurchaseHistory->mPurchases[j].mOrder.mGeoInfo.mCultureCode); + wprintf(L"\t Subtotal: %s\r\n", response->mPurchaseHistory->mPurchases[j].mOrder.mSubTotal); + wprintf(L"\t Tax: %s\r\n", response->mPurchaseHistory->mPurchases[j].mOrder.mTax); + wprintf(L"\t Total: %s\r\n", response->mPurchaseHistory->mPurchases[j].mOrder.mTotal); + wprintf(L"\t Root Order Guid: %s\r\n", response->mPurchaseHistory->mPurchases[j].mOrder.mRootOrderGuid); + printf("\t Date Purchased: %s\r\n", gsiSecondsToString(&response->mPurchaseHistory->mPurchases[j].mPurchaseDate)); + + if (response->mPurchaseHistory->mPurchases[j].mOrder.mValidation.mIsValid) + wprintf(L"Order is valid\r\n"); + else + wprintf(L"Order is invalid\r\n"); + + + wprintf(L"Number of items in order total: %d\r\n", response->mPurchaseHistory->mPurchases[j].mItemPurchases.mCount); + for (i = 0; i < response->mPurchaseHistory->mPurchases[j].mItemPurchases.mCount; i++) + { + D2GOrderItem anOrderItem = response->mPurchaseHistory->mPurchases[j].mItemPurchases.mOrderItemPurchases[i].mOrderItem; + + wprintf(L"=====================\r\n"); + wprintf(L"Item %d, ", i); + if (anOrderItem.mValidation.mIsValid) + wprintf(L"Valid\r\n"); + else + wprintf(L"Invalid, check result code in validation\r\n"); + + // Initialize the gameAppData download list for the sample here + // since traverseOrderItemPurchases updates this list. + traverseOrderItemPurchases(&response->mPurchaseHistory->mPurchases[j].mItemPurchases.mOrderItemPurchases[i]); + } + wprintf(L"=========================END OF PURCHASE ORDER[%i]=========================\r\n\n", j); + } + + if (response->mPurchaseHistory) + { + d2gFreePurchaseHistory(response->mPurchaseHistory); + response->mPurchaseHistory = NULL; + } + } + wprintf(L"=======================END GET PURCHASE HISTORY RESPONSE========================\r\n"); + + GSI_UNUSED(userData); + sampleGameAppData.gWaiting = gsi_false; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleGetPurchaseHistory(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog) +{ + GSResult result; + + // Keep in mind that this starts a download thread so thinking does not need to be done here. + // The benefits are non-blocking calls, callbacks will let developer know of status and completion. + result = d2gGetPurchaseHistory(theInstance, theCatalog, sampleGetPurchaseHistoryCallback, NULL); + sampleGameAppData.gWaiting = gsi_true; + while(sampleGameAppData.gWaiting) + { + msleep(10); + gsCoreThink(0); + } +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleLoadExtraInfoCallback( GSResult result, + D2GLoadExtraCatalogInfoResponse * response, + void * userData) +{ + gsi_u32 j=0; + wprintf(L"\n=========================BEGIN EXTRA INFO RESPONSE %u =========================\r\n", j); + + if (GS_FAILED(result)) + { + wprintf(L"sampleExtraInfo failed! Result = %X\r\n", result); + } + else + { + wprintf(L"sampleExtraInfo Success!\r\n"); + + if (response->mExtraCatalogInfoList) + { + wprintf(L"Number of elements: %d\r\n", response->mExtraCatalogInfoList->mCount); + + for (j=0; jmExtraCatalogInfoList->mCount; j++) + { + wprintf(L"EXTRA INFO [%i]\r\n", j); + wprintf(L"\tKey : %s\r\n", response->mExtraCatalogInfoList->mExtraInfoElements[j].mKey); + wprintf(L"\tValue: %s\r\n", response->mExtraCatalogInfoList->mExtraInfoElements[j].mValue); + wprintf(L"\r\n"); + } + + // since we are not using the extra info for other than + // printing, we will delete the data to avoid memory leaks + d2gFreeExtraInfoList(response->mExtraCatalogInfoList); + response->mExtraCatalogInfoList = NULL; + } + } + wprintf(L"==========================END EXTRA INFO RESPONSE==========================\r\n"); + + GSI_UNUSED(userData); + sampleGameAppData.gWaiting = gsi_false; +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleLoadExtraInfo(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog) +{ + GSResult result; + result = d2gLoadExtraCatalogInfo(theInstance, theCatalog, sampleLoadExtraInfoCallback, NULL); + sampleGameAppData.gWaiting = gsi_true; + while(sampleGameAppData.gWaiting) + { + msleep(10); + gsCoreThink(0); + } +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void sampleGetExtraInfo(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + UCS2String aKey) +{ + GSResult result; + UCS2String aValue = NULL; + + wprintf(L"=========================GET EXTRA INFO =========================\r\n" ); + + result = d2gGetExtraCatalogInfo(theInstance, theCatalog, aKey, &aValue); + + if (GS_FAILED(result)) + { + wprintf(L"Get Extra Info Failed for key <%s>: Key does not exist.\r\n", aKey); + } + else + { + wprintf(L"\tKey : %s\r\n", aKey); + wprintf(L"\tValue: %s\r\n", aValue); + } + wprintf(L"=================================================================\r\n"); + +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleGetItemActivationDataCallback(GSResult result, + D2GGetItemActivationResponse * response, + void * userData) +{ + gsi_u32 j=0; + wprintf(L"\n======================BEGIN ITEM ACTIVATION DATA RESPONSE %u ======================\r\n", j); + + if (GS_FAILED(result)) + { + wprintf(L"sampleGetItemActivationData failed! Result = 0x%X\r\n", result); + wprintf(L"\t HTTP result: 0x%x \r\n", response->mHttpResult); + wprintf(L"\tStatus message: %s\r\n", response->mStatusMessage); + } + else + { + wprintf(L"sampleGetItemActivationData success! Result = %X\r\n", result); + wprintf(L"=================================================================\r\n"); + wprintf(L"\t Item Id : %d\r\n", response->mItemId); + wprintf(L"\tActivation Code: %s\r\n", response->mActivationCode?response->mActivationCode:L""); + wprintf(L"=================================================================\r\n"); + + // free strings that we will no longer be using + // because the callback gives us control of these. + gsifree(response->mActivationCode); + response->mActivationCode = NULL; + + gsifree(response->mStatusMessage); + response->mStatusMessage = NULL; + + } + wprintf(L"=======================END ITEM ACTIVATION DATA RESPONSE=======================\r\n"); + + + + GSI_UNUSED(userData); + sampleGameAppData.gWaiting = gsi_false; +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleGetItemActivationData(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog) +{ + GSResult result= GS_SUCCESS; + D2GPackageId aPackageId = 11336; + result = d2gGetItemActivationData(theInstance, + theCatalog, + sampleGetItemActivationDataCallback, + aPackageId, + NULL); + sampleGameAppData.gWaiting = gsi_true; + while(sampleGameAppData.gWaiting) + { + msleep(10); + gsCoreThink(0); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// FOR TESTING ONLY ! +void sampleDisplayManifestFileContents(D2GInstancePtr theInstance) +{ + wprintf(L"\n======================BEGIN MANIFEST FILE CONTENTS ======================\r\n"); + d2gDisplayManifestFileContents(theInstance); + wprintf(L"\n========================END MANIFEST FILE CONTENTS ======================\r\n"); + +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleCheckContentUpdatesCallback( GSResult result, + D2GCheckContentUpdatesResponse *response, + void *userData) +{ + wprintf(L"\n======================BEGIN CHECK CONTENTS UPDATES RESPONSE ======================\r\n"); + + if (GS_FAILED(result)) + { + wprintf(L"sampleCheckContentUpdatesCallback failed! Result = 0x%X\r\n", result); + wprintf(L"\t HTTP result: 0x%x \r\n", response->mHttpResult); + } + else + { + gsi_u32 j=0; + + wprintf(L"sampleCheckContentUpdatesCallback success! Result = %X\r\n", result); + + wprintf(L"=================================================================\r\n"); + wprintf(L"\tNumber of available updates: %d\r\n", response->mContentUpdateList->mCount); + for (j = 0 ; jmContentUpdateList->mCount; j++) + { + wprintf(L"==================Update [%d]====================================\r\n",j); + wprintf(L"\t Item Id : %d\r\n", response->mContentUpdateList->mContentUpdates[j].mItemId); + wprintf(L"\tDownload URL Id : %d\r\n", response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mUrlId); + wprintf(L"\t Change Type : %s\r\n",(response->mContentUpdateList->mContentUpdates[j].mChangeType == D2GContentRemoved)? L"Removed" : L"New/Updated"); + wprintf(L"\t File Id : %d\r\n", response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mFileId); + wprintf(L"\t Sequence : %d\r\n", response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mSequence); + wprintf(L"\t Name : %s\r\n", response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mName); + wprintf(L"\t Asset Type : %s\r\n", response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mAssetType ? response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mAssetType : L""); + wprintf(L"\t Version : %f\r\n", response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mVersion); + switch (response->mContentUpdateList->mContentUpdates[j].mDownloadType) + { + case D2GDownloadOptional : + wprintf(L"\t Download type : Optional\r\n"); + break; + case D2GDownloadMandatory : + wprintf(L"\t Download type : Mandatory\r\n"); + break; + case D2GDownloadForced : + wprintf(L"\t Download type : Forced\r\n"); + break; + default : + wprintf(L"\t Download type : Unidentified\r\n"); + } + } + wprintf(L"=================================================================\r\n"); + + // The callback gives us control of the objects + // free the object since we don't need any more. + d2gFreeContentUpdateList(response->mContentUpdateList); + response->mContentUpdateList = NULL; + } + wprintf(L"=======================END CHECK CONTENTS UPDATES RESPONSE=======================\r\n"); + + GSI_UNUSED(userData); + sampleGameAppData.gWaiting = gsi_false; +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleCheckContentUpdates (D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog) +{ + GSResult result= GS_SUCCESS; + result = d2gCheckContentUpdates(theInstance, + theCatalog, + gsi_false, + sampleCheckContentUpdatesCallback, + NULL); + + sampleGameAppData.gWaiting = gsi_true; + while(sampleGameAppData.gWaiting) + { + msleep(10); + gsCoreThink(0); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleInstallContentFromUpdatesCallback( GSResult result, + D2GCheckContentUpdatesResponse *response, + void *userData) +{ + wprintf(L"\n======================BEGIN CHECK CONTENTS UPDATES RESPONSE ======================\r\n"); + + if (GS_FAILED(result)) + { + wprintf(L"sampleInstallContentFromUpdatesCallback failed! Result = 0x%X\r\n", result); + wprintf(L"\t HTTP result: 0x%x \r\n", response->mHttpResult); + } + else + { + gsi_u32 j=0; + + wprintf(L"sampleInstallContentFromUpdatesCallback success! Result = %X\r\n", result); + + wprintf(L"=================================================================\r\n"); + wprintf(L"\tNumber of available updates: %d\r\n", response->mContentUpdateList->mCount); + for (j = 0 ; jmContentUpdateList->mCount; j++) + { + wprintf(L"==================Update [%d]====================================\r\n",j); + wprintf(L"\t Item Id : %d\r\n", response->mContentUpdateList->mContentUpdates[j].mItemId); + wprintf(L"\tDownload URL Id : %d\r\n", response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mUrlId); + wprintf(L"\t Change Type : %s\r\n",(response->mContentUpdateList->mContentUpdates[j].mChangeType == D2GContentRemoved)? L"Removed" : L"New/Updated"); + wprintf(L"\t File Id : %d\r\n", response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mFileId); + wprintf(L"\t Sequence : %d\r\n", response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mSequence); + wprintf(L"\t Name : %s\r\n", response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mName); + wprintf(L"\t Asset Type : %s\r\n", response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mAssetType ? response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mAssetType : L""); + wprintf(L"\t Version : %f\r\n", response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mVersion); + switch (response->mContentUpdateList->mContentUpdates[j].mDownloadType) + { + case D2GDownloadOptional : + wprintf(L"\t Download type : Optional\r\n"); + break; + case D2GDownloadMandatory : + wprintf(L"\t Download type : Mandatory\r\n"); + break; + case D2GDownloadForced : + wprintf(L"\t Download type : Forced\r\n"); + break; + default : + wprintf(L"\t Download type : Unidentified\r\n"); + } + + switch (response->mContentUpdateList->mContentUpdates[j].mChangeType) + { + case D2GContentNew: + case D2GContentUpdated: + // Note for downloading, refer to sampleDownloadByFileId function and how it is used. + wprintf(L"***Assuming Developer update is downloaded****************************\r\n"); + wprintf(L"***Assuming Developer's Install function can be called here **********\r\n"); + wprintf(L"==================Installed Update [%d]================================\r\n",j); + result = d2gUpdateManifestInstalledContent(sampleGameAppData.gD2GInstancePtr, + response->mContentUpdateList->mContentUpdates[j].mItemId, + &response->mContentUpdateList->mContentUpdates[j].mDownloadItem); + if (GS_SUCCEEDED(result)) + { + wprintf(L"Manifest file updated with installed content successfully \r\n"); + wprintf(L"\t Item Id : %d \r\n", response->mContentUpdateList->mContentUpdates[j].mItemId); + wprintf(L"\t Download URL Id: %d \r\n", response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mUrlId); + wprintf(L"\t Download Version: %f \r\n", response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mVersion); + } + else + { + wprintf(L"Failed to update manifest file : 0x%x\r\n", result); + wprintf(L"\t Item Id : %d \r\n", response->mContentUpdateList->mContentUpdates[j].mItemId); + wprintf(L"\t Download URL Id: %d \r\n", response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mUrlId); + wprintf(L"\t Download Version: %f \r\n", response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mVersion); + + } + break; + + case D2GContentRemoved: + wprintf(L"***Assuming Developer's Uninstall function can be called here **********\r\n"); + wprintf(L"================= Removed Content [%d]=================================\r\n",j); + result = d2gUpdateManifestRemovedContent(sampleGameAppData.gD2GInstancePtr, + response->mContentUpdateList->mContentUpdates[j].mItemId, + &response->mContentUpdateList->mContentUpdates[j].mDownloadItem); + if (GS_SUCCEEDED(result)) + { + wprintf(L"Manifest file updated for the removed content successfully \r\n"); + wprintf(L"\t Item Id : %d \r\n", response->mContentUpdateList->mContentUpdates[j].mItemId); + wprintf(L"\t Download URL Id: %d \r\n", response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mUrlId); + wprintf(L"\t Download Version: %f \r\n", response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mVersion); + } + else + { + wprintf(L"Failed to remove content from manifest file : 0x%x\r\n", result); + wprintf(L"\t Item Id : %d \r\n", response->mContentUpdateList->mContentUpdates[j].mItemId); + wprintf(L"\t Download URL Id: %d \r\n", response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mUrlId); + wprintf(L"\t Download Version: %f \r\n", response->mContentUpdateList->mContentUpdates[j].mDownloadItem.mVersion); + + } + + break; + default: + wprintf(L"\t Unknown Action %d\r\n",response->mContentUpdateList->mContentUpdates[j].mChangeType ); + + } + } + wprintf(L"=================================================================\r\n"); + + // The callback gives us control of the objects + // free the object since we don't need any more. + d2gFreeContentUpdateList(response->mContentUpdateList); + response->mContentUpdateList = NULL; + } + wprintf(L"=======================END CHECK CONTENTS UPDATES RESPONSE=======================\r\n"); + GSI_UNUSED(userData); + sampleGameAppData.gWaiting = gsi_false; +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// The purpose of this function is to demonstrate the use of +// install/remove helper functions in the callback +void sampleInstallContentFromUpdates (D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog) +{ + GSResult result= GS_SUCCESS; + result = d2gCheckContentUpdates(theInstance, + theCatalog, + gsi_false, + sampleInstallContentFromUpdatesCallback, + NULL); + + sampleGameAppData.gWaiting = gsi_true; + while(sampleGameAppData.gWaiting) + { + msleep(10); + gsCoreThink(0); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void sampleShutdown(D2GInstancePtr d2gInstance) +{ + d2gCleanup(d2gInstance); + + gsifree(gLocalItemQuantities); + gsifree(gLocalItemIds); + // Shutdown the core + gsCoreShutdown(); + while(gsCoreIsShutdown() == GSCore_SHUTDOWN_PENDING) + { + gsCoreThink(0); + msleep(10); + } +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +int test_main(int argc, char *argv[]) +{ + gsi_bool result; + D2GInstancePtr d2gInstance = NULL; + D2GCatalogPtr d2gCatalog = NULL; +#if defined(_WIN32) && defined(USE_CRTDBG) + _CrtMemState s1, s2, s3; +#endif + + wprintf(L"=================BEGIN INITIALIZE D2G============================\r\n"); + +#if defined(_WIN32) && defined(USE_CRTDBG) + _CrtMemCheckpoint( &s1 ); +#endif + + // Set debug output options + gsSetDebugFile(stdout); + gsSetDebugLevel(GSIDebugCat_All, GSIDebugType_All, GSIDebugLevel_Debug); + + + // enable Win32 C Runtime debugging + #if defined(_WIN32) && defined(USE_CRTDBG) + { + int tempFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + _CrtSetDbgFlag(tempFlag | _CRTDBG_LEAK_CHECK_DF); + } + #endif + + gsCoreInitialize(); + + result = sampleBackendAvailable(); + if (gsi_is_false(result)) + { + sampleShutdown(d2gInstance); + } + + //strcpy(wsAuthServiceURL, "https://mwstage.gamespy.com/AuthService/AuthService.asmx"); + sampleAuthPlayer(); + + /** + * Test 1 : Create D2G Instance + */ + wprintf(L"Creating GameSpy Commerce SDK Instance\r\n"); + sampleGameAppData.gD2GInstancePtr = d2gInstance = d2gCreateInstance(); + if (d2gInstance == NULL) + { + wprintf(L"d2gCreateInstance failed!"); + sampleShutdown(d2gInstance); + } + + /** + * Test 2 : Initialize D2G Instance + */ + wprintf(L"Initializing GameSpy Commerce SDK Instance(gameid = %d)\r\n", GAME_ID); + d2gInitialize(d2gInstance, &sampleGameAppData.gLoginCertificate, &sampleGameAppData.gLoginPrivData); + + /** + * Test 3 : Initialize Manifest File Path (if needed) + */ + // If Patching Features used, the manifest file path can be set here. + // If not set, the current working directory is use. Always the same path + // should be used. The path should exist. + //d2gSetManifestFilePath( d2gInstance, "manifest\\"); + + /** + * Test 4 : Create D2G Catalog + */ + wprintf(L"Creating A GameSpy Commerce SDK Catalog for: \ + \r\n\t gameid: %d \r\n\t version: %d \r\n\t region: %s \ + \r\n\t access token: %s \r\n", + GAME_ID, GAME_CATALOG_VERSION, GAME_CATALOG_REGION, SAMPLE_ACCESS_TOKEN); + + + + d2gCatalog = d2gCreateCatalog(d2gInstance, + GAME_ID, + GAME_CATALOG_VERSION, + GAME_CATALOG_REGION, + SAMPLE_ACCESS_TOKEN); + + // Testing for different environments. + + // STAGE + //d2gSetServiceURL(d2gInstance, "mwstage.gamespy.com/commerce/1.0/"); + + /** + * Test 5: Check Store Availability + */ + wprintf(L"\nChecking store availability (gameid = %d)\r\n", GAME_ID); + sampleCheckStoreAvailability(d2gInstance, d2gCatalog); + wprintf(L"\n===================END INITIALIZE D2G============================\r\n\n"); + + /** + * Test 6: Extra Info + */ + wprintf(L"\nLoading Extra Info\r\n"); + sampleLoadExtraInfo(d2gInstance, d2gCatalog); + + /** + * Test 7: Retrieve all items of a catalog + */ + wprintf(L"\nRetrieving all items (gameid = %d)\r\n", GAME_ID); + sampleShowAllItems(d2gInstance, d2gCatalog); + + /** + * Test 8: Retrieve all the categories from a catalog + */ + wprintf(L"\nRetrieving Categories(gameid = %d)\r\n", GAME_ID); + sampleGetCategories(d2gInstance, d2gCatalog); + + /** + * Test 9: Retrieve items of a given category + */ + wprintf(L"Retrieving Items for a given Category %s\r\n", gSampleCategoryBad); + sampleShowItemsByCategory(d2gInstance, d2gCatalog, gSampleCategoryBad); + + wprintf(L"Retrieving Items for a given Category %s\r\n", gSampleCategoryGood); + sampleShowItemsByCategory(d2gInstance, d2gCatalog, gSampleCategoryGood); + + /** + * Test 10: Get Credit cards of the user + */ + wprintf(L"\nRetrieving user credit cards\r\n"); + sampleGetUserCreditCards(d2gInstance, d2gCatalog); + + /** + * Test 11: Get an order filled in + */ + wprintf(L"\nGetting order total\r\n"); + sampleGetOrderTotal(d2gInstance, d2gCatalog); + + /** + * Test 12: Purchase items + */ + // NOTE: Before running this functionality, make sure to refund previous + // purchases of the same item. + wprintf(L"\nMaking a purchase\r\n"); + sampleBeginPurchase(d2gInstance, d2gCatalog); + + /** + * Test 13: Verify purchase + */ + // NOTE: Before running this functionality, make sure to refund previous + // purchases of the same item. + wprintf(L"\nChecking placed order\r\n"); + sampleIsOrderCompleteCheck(d2gInstance, d2gCatalog); + + /** + * Test 14: Get the purchase history of the user + */ + wprintf(L"\nGetting Purchase History\n"); + sampleGetPurchaseHistory(d2gInstance, d2gCatalog); + + /** + * Test 15: Download by FileId + * For now only do it for one fileId + */ + wprintf(L"\nStarting download for a given file id: %d.\n",sampleGameAppData.gDownloads.downloads[0].fileId); + sampleDownloadFileById(d2gInstance, d2gCatalog, sampleGameAppData.gDownloads.downloads[0].fileId); + + /** + * Test 16: Download a list of files by FileId + * For each download it calls sampleDownloadFileById + */ + wprintf(L"\nStarting download for a list of file ids.\n"); + sampleDownloadListOfFilesById(d2gInstance, d2gCatalog); + + /** + * Test 17: Update Installed Content + * Assumption is the installation took place after downloading + * a purchased item successfully. Now we need to update the + * installed contents for future patches available. + * Note that the best practice is to update installed content + * after each download one at a time. The following function does + * this for a list of files for demonstration purposes. + */ + wprintf(L"\nUpdate Installed Content for a list of file ids.\n"); + sampleUpdateInstalledContent(d2gInstance); + + //sampleDisplayManifestFileContents(d2gInstance); + /** + * Test 18: Get Extra Info + */ + + wprintf(L"\nGetting an Extra Info Item\n"); + sampleGetExtraInfo(d2gInstance, d2gCatalog, L"TOS1"); + + sampleGetExtraInfo(d2gInstance, d2gCatalog, L"TOS"); + + sampleGetExtraInfo(d2gInstance, d2gCatalog, L"MOTD"); + + /** + * Test 19: Get Item Activation Data + */ + wprintf(L"\nGetting Item Activation Data\n"); + sampleGetItemActivationData(d2gInstance, d2gCatalog); + + /** + * Test 20: Installing/Removing Content + */ + wprintf(L"\nInstall/Remove Content given available Updates\n" ); + sampleInstallContentFromUpdates (d2gInstance,d2gCatalog); + + sampleDisplayManifestFileContents(d2gInstance); + + /** + * Test 21: Cleanup Catalog + */ + + d2gCleanupCatalog(d2gInstance, d2gCatalog); + + // Pause before exit + sampleShutdown(d2gInstance); + +#if defined(_WIN32) && defined(USE_CRTDBG) + _CrtMemCheckpoint( &s2 ); + if ( _CrtMemDifference( &s3, &s1, &s2 ) ) + { + printf("s3 stats\n"); + _CrtMemDumpStatistics( &s3 ); + } + printf ("All Stats \n"); + _CrtMemDumpAllObjectsSince( NULL ); + if (! _CrtDumpMemoryLeaks()) + printf("No Leaks\n"); +#endif + + GSI_UNUSED(argc); + GSI_UNUSED(argv); + GSI_UNUSED(result); + + fflush(stderr); + wprintf(L"Done - Press Enter\r\n"); + fflush(stdout); + getc(stdin); + + return 0; +} diff --git a/Direct2Game/changelog.txt b/Direct2Game/changelog.txt new file mode 100644 index 00000000..f2e33710 --- /dev/null +++ b/Direct2Game/changelog.txt @@ -0,0 +1,42 @@ +Changelog for: GameSpy Direct to Game SDK +-------------------------------------------------------- + +DATE VERSION BY TYPE DESCRIPTION +---------- ------- --- ------- --------------------------------------------------------- +06-25-2010 1.01.00 SN FEATURE Added Extensible Info for both catalogs and catalog items +02-09-2009 1.00.00 SN RELEASE Releasing to developer site +02-09-2009 0.06.10 NAB OTHER Release preparation +02-09-2009 0.06.09 SN FIX Fixed issue with temp file being renamed + SN FIX Reconfigured production environment URL + SN FIX Fixed problem with parsing activation code +02-09-2009 0.06.08 NAB FIX Fix for content updates and additional test functionality +02-06-2009 0.06.07 NAB FIX/FEATURE Submitted override manifest file path function and + bug fix for memory leak and changes to eliminate + the duplicate items in the merged lists. +02-05-2009 0.06.06 SN FIX Made some minor name changes to be consistent + SN FIX Fixed a few issues mentioned +02-03-2009 0.06.05 SN FIX Renamed the type for content updates to a more appropriate name. +02-02-2009 0.06.04 SN FIX Removed requirement of cloning parts of a response in a callback + SN FIX Made various bug fixes to cleanup functionality + SN FIX Minor fixes to the test app made +01-28-2009 0.06.03 NAB FEATURE sort by item id, externalid, file size and refactored sort algorithm. +01-09-2009 0.06.02 NAB OTHER Checking in changes in preparation of the release +01-09-2009 0.06.01 NAB FEATURE Checking content updates, manifest and sorting. +11-25-2008 0.06.00 NAB RELEASE Releasing beta version to developer site +05-19-2008 0.05.02 SN FEATURE Added some caching functionality for d2gGetAllItems + SN FEATURE Added d2gGetItemsByCategory + SN FEATURE Added d2gFilterItemsByCategory for local lists + SN OTHER Debug logging was added +05-05-2008 0.05.01 SN FIX Added another error code needed for the D2G service +04-07-2008 0.05.00 SN RELEASE Releasing to developer site +04-07-2008 0.04.02 SN FIX Updated error codes to reflect backend +03-27-2008 0.04.01 SN FEATURE Updated Place Order + SN FEATURE Added Check Placed Order + SN FEATURE Added Get Store Availiability + SN FEATURE Added Get Wallet Admin URL + SN FEATURE Updated test application to reflect changes in SDK +03-27-2008 0.04.00 SN RELEASE Alpha 2 release +03-27-2008 0.03.01 SN FEATURE Added data types for get order total, purchased items, and others + SN FEATURE Modified interface to conform to new data types +03-14-2008 0.03.00 SN CREATE Alpha SDK Implementation completed + diff --git a/Direct2Game/d2gDeserialize.c b/Direct2Game/d2gDeserialize.c new file mode 100644 index 00000000..504a8e5d --- /dev/null +++ b/Direct2Game/d2gDeserialize.c @@ -0,0 +1,2311 @@ +/////////////////////////////////////////////////////////////////////////////// +// d2gDeserialize.c +// +// GameSpy DIRECT TO GAME SDK +// This file is part of the Direct to Game SDK designed and developed by GameSpy Tech. +// Copyright (c) 2008, GameSpy Technology +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// includes +#include "../common/gsResultCodes.h" +#include "../common/gsAvailable.h" +#include "../ghttp/ghttpCommon.h" +#include "Direct2Game.h" +#include "d2gMain.h" +#include "d2gServices.h" +#include "d2gUtil.h" +#include "d2gDeserialize.h" + +/////////////////////////////////////////////////////////////////////////////// +// General Parsing functions +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseGetResponse(GSXmlStreamReader theResponseXml , + const char *theResponseTag) +{ + if (gsi_is_false(gsXmlMoveToStart(theResponseXml)) || + gsi_is_false(gsXmlMoveToNext(theResponseXml, theResponseTag))) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): could not parse xml: start; tag %s", + __FILE__, __FUNCTION__, __LINE__, theResponseTag); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + return d2giParseResponseHeader(theResponseXml); + +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseResponseHeader(GSXmlStreamReader theResponseXml) +{ + GSResult result; + int responseStatusCode; + + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, GS_D2G_RESULT_SECTION)) || + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, GS_D2G_RESULT_CODE, &responseStatusCode)) || + gsi_is_false(gsXmlMoveToParent(theResponseXml))) + { + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + result = d2giResultFromStatusCode(responseStatusCode); + return result; +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseItemInfoFromResponse(D2GIInstance *theInstance, + D2GBasicItemInfo *pItem, + GSXmlStreamReader theResponse) +{ + const char *emptyStr = ""; + const char *itemStrings[4]; + int lenItemStrings[4]; + + + if (gsi_is_false(gsXmlReadChildAsInt (theResponse, "itemid", (int *)&pItem->mItemId)) || + gsi_is_false(gsXmlReadChildAsString(theResponse, "externalid", &itemStrings[0], &lenItemStrings[0])) || + gsi_is_false(gsXmlReadChildAsString(theResponse, "name", &itemStrings[1], &lenItemStrings[1])) || + gsi_is_false(gsXmlReadChildAsString(theResponse, "price", &itemStrings[2],&lenItemStrings[2])) || + gsi_is_false(gsXmlReadChildAsString(theResponse, "tax", &itemStrings[3], &lenItemStrings[3])) + ) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: itemid\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + + } + + //externalId + if (itemStrings[0] != NULL && lenItemStrings[0] > 0) + { + pItem->mExternalItemCode = UTF8ToUCS2StringAllocLen(itemStrings[0], lenItemStrings[0]); + } + + else + { + pItem->mExternalItemCode = UTF8ToUCS2StringAllocLen(emptyStr, (int)strlen(emptyStr)); + } + + // Name: if name is empty we should still set it to the empty string so + // that we don't have NULLs + if (itemStrings[1] && lenItemStrings[1] > 0) + { + pItem->mName = UTF8ToUCS2StringAllocLen(itemStrings[1], lenItemStrings[1]); + } + else + { + pItem->mName = UTF8ToUCS2StringAllocLen(emptyStr, (int)strlen(emptyStr)); + } + + // Price: should not be null + pItem->mPrice = UTF8ToUCS2StringAllocLen(itemStrings[2], lenItemStrings[2]); + + // Tax: should not be null + pItem->mTax = UTF8ToUCS2StringAllocLen(itemStrings[3], lenItemStrings[3]); + + if (pItem->mExternalItemCode == NULL || + pItem->mName == NULL || + pItem->mPrice == NULL || + pItem->mTax == NULL) + { + gsifree(pItem->mExternalItemCode); + gsifree(pItem->mName); + gsifree(pItem->mPrice); + gsifree(pItem->mTax); + + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_WarmError, + "%s :: %s (%d): out of memory: item strings\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + GSI_UNUSED(theInstance); + return GS_SUCCESS; +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseProductInfoFromResponse( D2GIInstance *theInstance, + D2GProductInfo *pProductInfo, + GSXmlStreamReader theResponse) +{ + const char *emptyStr = ""; + const char *itemStrings[3]; + int lenItemStrings[3]; + + if (gsi_is_false(gsXmlReadChildAsDateTimeElement(theResponse, "releasedate", &pProductInfo->mReleaseDate))) + { + pProductInfo->mReleaseDate = 0; + } + + if (gsi_is_false(gsXmlReadChildAsInt(theResponse, "filesizebytessum", (int *) &pProductInfo->mFileSize))) + { + pProductInfo->mFileSize = 0; + } + + if (gsi_is_false(gsXmlReadChildAsString(theResponse, "publisher", &itemStrings[0], &lenItemStrings[0])) || + gsi_is_false(gsXmlReadChildAsString(theResponse, "developer", &itemStrings[1], &lenItemStrings[1])) || + gsi_is_false(gsXmlReadChildAsString(theResponse, "summary", &itemStrings[2], &lenItemStrings[2]))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: itemStrings[1-6]\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + + } + + // Publisher: same as Name + if (itemStrings[0] && lenItemStrings[0] > 0) + { + pProductInfo->mPublisher = UTF8ToUCS2StringAllocLen(itemStrings[0], lenItemStrings[0]); + } + else + { + pProductInfo->mPublisher = UTF8ToUCS2StringAllocLen(emptyStr, (int)strlen(emptyStr)); + } + + // Developer: same as Name + if (itemStrings[1] && lenItemStrings[1] > 0) + { + pProductInfo->mDeveloper = UTF8ToUCS2StringAllocLen(itemStrings[1], lenItemStrings[1]); + } + else + { + pProductInfo->mDeveloper = UTF8ToUCS2StringAllocLen(emptyStr, (int)strlen(emptyStr)); + } + + // Summary: same as Summary + if (itemStrings[2] && lenItemStrings[2] > 0) + { + pProductInfo->mSummary = UTF8ToUCS2StringAllocLen(itemStrings[2], lenItemStrings[2]); + } + else + { + pProductInfo->mSummary = UTF8ToUCS2StringAllocLen(emptyStr, (int)strlen(emptyStr)); + } + + if (pProductInfo->mPublisher == NULL || + pProductInfo->mDeveloper == NULL || + pProductInfo->mSummary == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_WarmError, + "%s :: %s (%d): out of memory: item strings\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + GSI_UNUSED(theInstance); + + return GS_SUCCESS; +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseGeoInfoFromResponse( D2GIInstance *theInstance, + D2GGeoInfo *pGeoInfo, + GSXmlStreamReader theResponse) +{ + const char *emptyStr = ""; + const char *itemStrings[2]; + int lenItemStrings[2]; + + if ( + gsi_is_false(gsXmlReadChildAsString(theResponse, "culturecode", &itemStrings[0], &lenItemStrings[0])) || + gsi_is_false(gsXmlReadChildAsString(theResponse, "currencycode", &itemStrings[1], &lenItemStrings[1]))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: itemStrings[1-6]\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + + } + + // Publisher: same as Name + if (itemStrings[0] && lenItemStrings[0] > 0) + { + pGeoInfo->mCultureCode = UTF8ToUCS2StringAllocLen(itemStrings[0], lenItemStrings[0]); + } + else + { + pGeoInfo->mCultureCode = UTF8ToUCS2StringAllocLen(emptyStr, (int)strlen(emptyStr)); + } + + // Developer: same as Name + if (itemStrings[1] && lenItemStrings[1] > 0) + { + pGeoInfo->mCurrencyCode = UTF8ToUCS2StringAllocLen(itemStrings[1], lenItemStrings[1]); + } + else + { + pGeoInfo->mCurrencyCode = UTF8ToUCS2StringAllocLen(emptyStr, (int)strlen(emptyStr)); + } + + if (pGeoInfo->mCultureCode == NULL || + pGeoInfo->mCurrencyCode == NULL ) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_WarmError, + "%s :: %s (%d): out of memory: item strings\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + GSI_UNUSED(theInstance); + return GS_SUCCESS; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseImageListFromResponse( D2GIInstance *theInstance, + D2GImageList *pImageList, + GSXmlStreamReader theResponseXml) +{ + gsi_u32 i = 0; + + if (gsi_is_false(gsXmlMoveToSibling(theResponseXml, "imagelist")) || + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "count", (int *)&pImageList->mCount)) || + gsi_is_false(gsXmlMoveToChild(theResponseXml, "image"))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: images\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + pImageList->mImageName = (UCS2String *)gsimalloc(sizeof(UCS2String) * pImageList->mCount); + if (pImageList->mImageName == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_WarmError, + "%s :: %s (%d): out of memory: mImageName\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + memset(pImageList->mImageName, 0, sizeof(UCS2String *) * pImageList->mCount); + + for (i = 0; i< pImageList->mCount; i++) + { + const char *tempStr; + int tempStrLen; + + if (gsi_is_false(gsXmlReadChildAsString(theResponseXml, "url", &tempStr, &tempStrLen))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: image url\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + pImageList->mImageName[i] = UTF8ToUCS2StringAllocLen(tempStr, tempStrLen); + if (pImageList->mImageName[i] == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_WarmError, + "%s :: %s (%d): out of memory: pImageList->mImageName[i]\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + if ((i < pImageList->mCount -1)) + { + if (gsi_is_false(gsXmlMoveToSibling(theResponseXml, "image"))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: images\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + } + } + if (gsi_is_false(gsXmlMoveToParent(theResponseXml))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not move to parent."); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + GSI_UNUSED(theInstance); + return GS_SUCCESS; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseCategoryListFromResponse(D2GIInstance *theInstance, + D2GCategoryList *pCategoryList, + GSXmlStreamReader theResponseXml) +{ + gsi_u32 i = 0; + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "categorylist")) || + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "count", (int*)&pCategoryList->mCount)) || + gsi_is_false(gsXmlMoveToChild(theResponseXml, "category"))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: categories\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + pCategoryList->mCategoryNames= (UCS2String *)gsimalloc(sizeof(UCS2String) * pCategoryList->mCount); + if (pCategoryList->mCategoryNames == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_WarmError, + "%s :: %s (%d): out of memory: pCategoryList->mCategoryName\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + memset(pCategoryList->mCategoryNames, 0, sizeof(UCS2String) * pCategoryList->mCount); + + for (i = 0; i < pCategoryList->mCount; i++) + { + const char *tempStr; + int tempStrLen; + if (gsi_is_false(gsXmlReadChildAsString(theResponseXml, "name", &tempStr, &tempStrLen))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d) could not parse xml stream: name\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + pCategoryList->mCategoryNames[i] = UTF8ToUCS2StringAllocLen(tempStr, tempStrLen); + if (pCategoryList->mCategoryNames[i] == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_WarmError, + "%s :: %s (%d): out of memory: mCategoryName[i]\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + if (i < pCategoryList->mCount -1) + { + if (gsi_is_false(gsXmlMoveToSibling(theResponseXml, "category"))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse XML stream: categories\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + } + } + if (gsi_is_false(gsXmlMoveToParent(theResponseXml))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not move to parent."); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + GSI_UNUSED(theInstance); + return GS_SUCCESS; +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseItemFromResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + D2GCatalogItem **item, + GSXmlStreamReader theResponse) +{ + + D2GBasicItemInfo itemInfo; + D2GCatalogItem * currentItem; + + GSResult result = GS_SUCCESS; + + if ((result= d2giParseItemInfoFromResponse(theInstance, &itemInfo, theResponse)) + != GS_SUCCESS) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not parse Item Info."); + return result; + } + + // Update the catalog with the item received in the response. + // If we do not have an entry for this item then a new item + // is added to the catalog. + currentItem = d2giGetCatalogItem(theInstance, theCatalog, itemInfo.mItemId ); + if (currentItem == NULL) + { + currentItem = d2giNewCatalogItem(theInstance, theCatalog, itemInfo.mItemId); + } + else + { + d2giResetCatalogItem(currentItem); + currentItem->mItem.mItemId = itemInfo.mItemId; + } + + if (currentItem == NULL) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError, "Out of memory: currentItem."); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + //Shallow Copy + currentItem->mItem = itemInfo; + // parse release date separately + result= d2giParseGeoInfoFromResponse(theInstance, ¤tItem->mGeoInfo, theResponse); + if (result != GS_SUCCESS) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not parse Geo Info."); + return result; + } + + result= d2giParseProductInfoFromResponse(theInstance, ¤tItem->mProductInfo, theResponse); + if (result != GS_SUCCESS) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not parse Product Info."); + return result; + } + + result = d2giParseCategoryListFromResponse(theInstance, ¤tItem->mCategories, theResponse); + if (result != GS_SUCCESS) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not parse Category List."); + return result; + } + + result = d2giParseImageListFromResponse(theInstance, ¤tItem->mImages, theResponse); + if (result!= GS_SUCCESS) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not parse Image List."); + return result; + } + + result = d2giParseExtraInfoList(theInstance, NULL, theResponse, ¤tItem->mExtraItemInfoList); + if (result != GS_SUCCESS) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not parse extra info list."); + return result; + } + + + *item = currentItem; + return GS_SUCCESS; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseItemTotalFromResponse( D2GIInstance *theInstance, + D2GOrderItemTotal *theItemTotal, + GSXmlStreamReader theResponseXml) + +{ + GSResult result = GS_SUCCESS; + + const char *tempSubTotal, *tempTotal; + int tempSubTotalLen, tempTotalLen; + + if (gsi_is_false(gsXmlReadChildAsInt (theResponseXml, "quantity", (int *)&theItemTotal->mQuantity)) || + gsi_is_false(gsXmlReadChildAsString(theResponseXml, "subtotal", &tempSubTotal, &tempSubTotalLen)) || + gsi_is_false(gsXmlReadChildAsString(theResponseXml, "total", &tempTotal, &tempTotalLen))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: total", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + theItemTotal->mSubTotal = UTF8ToUCS2StringAllocLen(tempSubTotal, tempSubTotalLen); + theItemTotal->mTotal = UTF8ToUCS2StringAllocLen(tempTotal, tempTotalLen); + + if (theItemTotal->mSubTotal == NULL || theItemTotal->mTotal == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed to convert to UCS2String, out of memory", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + GSI_UNUSED(theInstance); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseOrderItemValidationFromResponse(GSXmlStreamReader theResponseXml, + D2GOrderValidation *theValidation) + +{ + GSResult result = GS_SUCCESS; + const char *tempIsValid,*tempMsg; + int tempValidCode, tempIsValidLen, tempMsgLen; + + memset(theValidation, 0, sizeof(D2GOrderValidation)); + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "orderitemvalidation"))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: ordervalidation\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + if (gsi_is_false(gsXmlReadChildAsString(theResponseXml, "isvalid", &tempIsValid, &tempIsValidLen)) || + gsi_is_false(gsXmlReadChildAsString(theResponseXml, "message", &tempMsg, &tempMsgLen)) || + gsi_is_false(gsXmlReadChildAsInt (theResponseXml, "orderitemstatuscode", &tempValidCode)) + ) + { + gsDebugFormat(GSIDebugCat_Direct2Game,GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: orderstatuscode\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + theValidation->mIsValid = strncmp(tempIsValid, "true", tempIsValidLen) == 0 ? gsi_true : gsi_false; + theValidation->mResult = d2giOrderValidationResultFromCode(tempValidCode); + theValidation->mMessage = UTF8ToUCS2StringAllocLen(tempMsg, tempMsgLen); + + if (theValidation->mIsValid == gsi_false && + theValidation->mMessage == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed to convert to UCS2String, possibly out of memory", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_OutOfMemory); + } + + if (gsi_is_false(gsXmlMoveToParent(theResponseXml))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not move to parent."); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseOrderItemFromResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + D2GOrderItem *theOrderItem, + GSXmlStreamWriter theResponseXml) +{ + D2GCatalogItem *pCatalogItem = NULL; + + + GSResult result = GS_SUCCESS; + + //========================================= + // Parse the item (itemBase) first + //========================================= + + memset(&theOrderItem->mItem, 0, sizeof(D2GBasicItemInfo)); + + result = d2giParseItemInfoFromResponse(theInstance, &theOrderItem->mItem,theResponseXml); + if (GS_FAILED(result)) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): failed to call d2giParseItemFromResponse\n", + __FILE__, __FUNCTION__, __LINE__); + return result; + } + + // Check if have it in the catalog. + pCatalogItem = d2giGetCatalogItem(theInstance, theCatalog, theOrderItem->mItem.mItemId); + + result = d2giParseItemTotalFromResponse( theInstance, &theOrderItem->mItemTotal,theResponseXml); + if (GS_FAILED(result)) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): failed to call d2giParseItemTotalFromResponse\n", + __FILE__, __FUNCTION__, __LINE__); + return result; + } + + result = d2giParseOrderItemValidationFromResponse(theResponseXml, &theOrderItem->mValidation); + + if (GS_FAILED(result)) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): failed to call d2giParseOrderValidationFromResponse\n", + __FILE__, __FUNCTION__, __LINE__); + return result; + } + + return GS_SUCCESS; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseOrderItemLicensesFromResponse(D2GIInstance *theInstance, + D2GLicenseItemList *theLicenseList, + GSXmlStreamReader theResponseXml) +{ + GSResult result = GS_SUCCESS; + + + if (gsi_is_false(gsXmlMoveToChild (theResponseXml, "licenses")) || + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, GS_D2G_COUNT_ELEMENT, (int *)&theLicenseList->mCount))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: licenses, count\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + if (theLicenseList->mCount == 0) + { + theLicenseList->mLicenses = NULL; + } + else + { + const char *tempLicenseKey, *tempLicenseName; + int lenLicenseKey, lenLicenseName; + gsi_u32 i=0; + + theLicenseList->mLicenses = (D2GLicenseItem *)gsimalloc(sizeof(D2GLicenseItem) + * theLicenseList->mCount); + if (theLicenseList->mLicenses == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed to allocate licenses, out of memory", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "license"))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: license\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + for (i = 0; i < theLicenseList->mCount; i++) + { + D2GLicenseItem *currentLicense = &theLicenseList->mLicenses[i]; + if(gsi_is_false(gsXmlReadChildAsString(theResponseXml, "licensekey", &tempLicenseKey, &lenLicenseKey)) || + gsi_is_false(gsXmlReadChildAsString(theResponseXml, "licensename", &tempLicenseName, &lenLicenseName))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: licensename\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + currentLicense->mLicenseKey = UTF8ToUCS2StringAllocLen(tempLicenseKey, lenLicenseKey); + currentLicense->mLicenseName = UTF8ToUCS2StringAllocLen(tempLicenseName, lenLicenseName); + + if (currentLicense->mLicenseKey == NULL || currentLicense->mLicenseName == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed to convert to UCS2String, out of memory", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + if (i < theLicenseList->mCount - 1) + { + if (gsi_is_false(gsXmlMoveToSibling(theResponseXml, "license"))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream (move): license\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + } + } + + if (gsi_is_false(gsXmlMoveToParent(theResponseXml))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not move to parent."); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + } + GSI_UNUSED(theInstance); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseOrderItemDownloadsFromResponse(D2GIInstance *theInstance, + D2GDownloadItemList *theDownloadList, + GSXmlStreamReader theResponseXml) +{ + const char *emptyStr = ""; + + if (gsi_is_false(gsXmlMoveToSibling(theResponseXml, "downloads")) || + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, GS_D2G_COUNT_ELEMENT, (int *)&theDownloadList->mCount))) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream (move): downloads\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + if (theDownloadList->mCount == 0) + { + theDownloadList->mDownloads = NULL; + } + else + { + const char *tempName, *tempAssetType; + int tempNameLen, tempAssetTypeLen; + gsi_u32 i=0; + theDownloadList->mDownloads = (D2GDownloadItem *)gsimalloc(sizeof(D2GDownloadItem) * theDownloadList->mCount); + if (theDownloadList->mDownloads == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed to allocate downloads, out of memory", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "download"))) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: license\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + for (i = 0; i < theDownloadList->mCount; i++) + { + D2GDownloadItem *currentItem = &theDownloadList->mDownloads[i]; + + if (gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "downloadurlid", (int *)¤tItem->mUrlId)) || + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "fileid", (int *)¤tItem->mFileId)) || + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "sequence", (int *)¤tItem->mSequence)) || + gsi_is_false(gsXmlReadChildAsString(theResponseXml, "assettype", &tempAssetType, &tempAssetTypeLen)) || + gsi_is_false(gsXmlReadChildAsFloat(theResponseXml, "version",¤tItem->mVersion)) ) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: assettype\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + if (gsi_is_false(gsXmlReadChildAsString(theResponseXml, "name", &tempName, &tempNameLen))) + { + currentItem->mName = UTF8ToUCS2StringAllocLen(emptyStr, (int)strlen(emptyStr)); + } + else + { + currentItem->mName = UTF8ToUCS2StringAllocLen(tempName, tempNameLen); + } + // asset type is nullable. + currentItem->mAssetType = UTF8ToUCS2StringAllocLen(tempAssetType, tempAssetTypeLen); + + if (i < theDownloadList->mCount - 1) + { + if (gsi_is_false(gsXmlMoveToSibling(theResponseXml, "download"))) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream (move): download\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + } + } + if (gsi_is_false(gsXmlMoveToParent(theResponseXml))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not move to parent."); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + } + GSI_UNUSED(theInstance); + return GS_SUCCESS; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseOrderItemPurchaseFromResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + D2GOrderItemPurchase *orderItemPurchase, + GSXmlStreamWriter theResponseXml, + gsi_bool updateManifest) +{ + GSResult result; + gsi_u32 i; + + result = d2giParseOrderItemFromResponse(theInstance, + theCatalog, + &orderItemPurchase->mOrderItem, + theResponseXml); + if (GS_FAILED(result)) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): failed to call d2giParseOrderItemFromResponse\n", + __FILE__, __FUNCTION__, __LINE__); + return result; + } + + result = d2giParseOrderItemLicensesFromResponse(theInstance, + &orderItemPurchase->mLicenseList, + theResponseXml); + if (GS_FAILED(result)) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): failed parsing license items\n", + __FILE__, __FUNCTION__, __LINE__); + return result; + } + + result = d2giParseOrderItemDownloadsFromResponse(theInstance, + &orderItemPurchase->mDownloadList, + theResponseXml); + if (GS_FAILED(result)) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): failed parsing download items\n", + __FILE__, __FUNCTION__, __LINE__); + return result; + } + else if (updateManifest) + { + // Update manifest file with the purchased content + for (i= 0; imDownloadList.mCount ; i++) + { + D2GIManifestRecord manifest; + manifest.itemId = orderItemPurchase->mOrderItem.mItem.mItemId; + manifest.urlId = orderItemPurchase->mDownloadList.mDownloads[i].mUrlId; + manifest.version = 0.0; + result = d2giUpdateManifestRecord(theInstance, manifest); + if (GS_FAILED(result)) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): failed when updating Manifest Info for the purchased content.\n", + __FILE__, __FUNCTION__, __LINE__); + return result; + } + + } + } + // Now we start parsing the order item purchases. + if (gsi_is_false(gsXmlMoveToSibling(theResponseXml, "orderitempurchases")) || + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, GS_D2G_COUNT_ELEMENT, + (int *)&orderItemPurchase->mPurchaseList.mCount))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream (move): orderitempurchases\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + if (orderItemPurchase->mPurchaseList.mCount == 0) + { + orderItemPurchase->mPurchaseList.mOrderItemPurchases = NULL; + if (gsi_is_false(gsXmlMoveToParent(theResponseXml))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not move to parent."); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + } + else + { + orderItemPurchase->mPurchaseList.mOrderItemPurchases = (D2GOrderItemPurchase *)gsimalloc(sizeof(D2GOrderItemPurchase) + * orderItemPurchase->mPurchaseList.mCount); + if (orderItemPurchase->mPurchaseList.mOrderItemPurchases == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed to allocate sub item purchases,out of memory", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "orderitempurchase"))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: orderitempurchase\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + for (i = 0; i < orderItemPurchase->mPurchaseList.mCount; i++) + { + + D2GOrderItemPurchase *currentItem = &orderItemPurchase->mPurchaseList.mOrderItemPurchases[i]; + + result = d2giParseOrderItemPurchaseFromResponse(theInstance, + theCatalog, + currentItem, + theResponseXml, + updateManifest); + if (GS_FAILED(result)) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): failed in d2giParseOrderItemPurchaseFromResponse\n", + __FILE__, __FUNCTION__, __LINE__); + return result; + } + + if (i < orderItemPurchase->mPurchaseList.mCount - 1) + { + if (gsi_is_false(gsXmlMoveToSibling(theResponseXml, "orderitempurchase"))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream (move): orderitempurchase\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + } + } + if (gsi_is_false(gsXmlMoveToParent(theResponseXml))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not move to parent."); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + } + return GS_SUCCESS; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseGetStoreAvailResponse(D2GIInstance * instance, + GSXmlStreamReader theResponseXml, + D2GGetStoreAvailabilityResponse *response) +{ + GSResult result; + int storeAvailability; + + result = d2giParseGetResponse(theResponseXml, GS_D2G_GETSTOREAVAIL_RESULT); + if (GS_FAILED(result)) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_HotError, + "%s :: %s (%d): failed to parse status in d2giParseResponseHeader\n", + __FILE__, __FUNCTION__, __LINE__); + return result; + } + + if (gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "storestatusid", &storeAvailability))) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Could not parse XML: storestatusid\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + response->mAvailabilityCode = d2giResultFromStoreAvailabilityCode(storeAvailability); + + GSI_UNUSED(instance); + return result; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Credit Cards +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giParseCreditCardInfoList. +// This function parses the received SOAP message to retrieve +// user's credit cards. +// +GSResult d2giParseCreditCardInfoList(D2GIInstance *instance, + GSXmlStreamReader theResponseXml, + D2GGetUserCreditCardsResponse *responseOut) +{ + GSResult result; + int creditCardIndex = 0; + int count = 0; + + result = d2giParseGetResponse(theResponseXml, GS_D2G_GETUSERCREDITCARDS_RESULT); + + if (GS_SUCCEEDED(result)) + { + responseOut->mListOfCreditCards = (D2GCreditCardInfoList *)gsimalloc(sizeof(D2GCreditCardInfoList)); + if (responseOut->mListOfCreditCards == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_WarmError, + "%s :: %s (%d): out of memory: item strings\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + // try to parse the soap request message + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "creditcardlist")) || + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "count", &count))) + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + return result; + } + + // Initialize the mListOfCreditCards + if (count == 0) + { + responseOut->mListOfCreditCards->mCount = count; + responseOut->mListOfCreditCards->mCreditCardInfos = NULL; + return result; + } + else + { + responseOut->mListOfCreditCards->mCount = count; + responseOut->mListOfCreditCards->mCreditCardInfos = + (D2GCreditCardInfo *)gsimalloc(sizeof(D2GCreditCardInfo) *count); + memset(responseOut->mListOfCreditCards->mCreditCardInfos, 0, sizeof(D2GCreditCardInfo) * count); + } + + if (responseOut->mListOfCreditCards->mCreditCardInfos == NULL) + { + result = GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + return result; + } + + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "creditcard"))) + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + return result; + } + + for (creditCardIndex = 0; creditCardIndex < count; creditCardIndex++) + { + D2GCreditCardInfo *pCreditCard = NULL; + int nAccountId; + const char *tempCardType, *tempDefault; + int tempCardTypeLen, tempDefaultLen; + int nLastFour; + time_t tExpireDate; + + if (gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "accountid", &nAccountId)) || + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "lastfournumbers", &nLastFour)) || + gsi_is_false(gsXmlReadChildAsDateTimeElement(theResponseXml, "expirationdate", &tExpireDate)) || + gsi_is_false(gsXmlReadChildAsString(theResponseXml, "accounttype", &tempCardType, &tempCardTypeLen)) || + gsi_is_false(gsXmlReadChildAsString(theResponseXml, "isdefault", &tempDefault, &tempDefaultLen))) + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + return result; + } + + pCreditCard = &responseOut->mListOfCreditCards->mCreditCardInfos[creditCardIndex]; + if (pCreditCard == NULL) + { + // This should never happen but just in case + result = GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + return result; + } + + pCreditCard->mAccountId = nAccountId; + + pCreditCard->mCreditCardType = UTF8ToUCS2StringAllocLen(tempCardType, tempCardTypeLen); + if (pCreditCard->mCreditCardType == NULL) + { + result = GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + return result; + } + + pCreditCard->mExpirationDate = tExpireDate; + pCreditCard->mIsDefault = (strncmp(tempDefault, "true", tempDefaultLen)==0)?gsi_true:gsi_false; + pCreditCard->mLastFourDigits = nLastFour; + + if ((creditCardIndex < count - 1)) + { + if (gsi_is_false(gsXmlMoveToSibling(theResponseXml, "creditcards"))) + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + return result; + } + } + } + } + + GSI_UNUSED(instance); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +// Catalog Items +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2giParseItemsFromResponse. +// This is utility function to parse items from a response. +// For those items parsed if they do not exist in the cache already +// they are added to the cache. If they do then they updated with the +// information received with the response message. +// +// On a failure all the items parsed so far is removed from +// the cache as well. +// +GSResult d2giParseItemsFromResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GCatalogItemList *outItemList) +{ + GSResult result = GS_SUCCESS; + int count; + int i; + + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, GS_D2G_CATALOG_ITEM_LIST)) || + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, GS_D2G_COUNT_ELEMENT, &count))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError, "Could not parse xml stream: %s, %s.", + GS_D2G_CATALOG_ITEM_LIST, GS_D2G_COUNT_ELEMENT); + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + return d2giFreeCatalogItems(theCatalog, outItemList, result, gsi_true); + } + + if (count == 0) + { + outItemList->mCount = 0; + outItemList->mCatalogItems = NULL; + return result; + } + // Initialize the outItemList + outItemList->mCount = count; + outItemList->mCatalogItems = (D2GCatalogItem *)gsimalloc(sizeof(D2GCatalogItem ) * count); + if (outItemList->mCatalogItems == NULL) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError, "Out of memory: outItemList->mCatalogItems."); + result = GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + return d2giFreeCatalogItems(theCatalog, outItemList, result, gsi_true); + } + memset(outItemList->mCatalogItems, 0, sizeof(D2GCatalogItem) * count); + + // Start processing all the items from the response + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, GS_D2G_CATALOG_ITEM))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError, "Could not parse xml stream: %s.", GS_D2G_CATALOG_ITEM); + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + return d2giFreeCatalogItems(theCatalog, outItemList, result, gsi_true); + } + + for (i = 0; i < count; i++) + { + D2GCatalogItem *currentItem=NULL; + + result = d2giParseItemFromResponse(theInstance, theCatalog, ¤tItem, theResponseXml); + if (GS_FAILED(result)) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError, "Could not parse xml stream for %s.", GS_D2G_CATALOG_ITEM); + return d2giFreeCatalogItems(theCatalog, outItemList, result, gsi_true); + } + + //shallow copy + outItemList->mCatalogItems[i] = *currentItem; + + if ((i < count-1)) + { + if (gsi_is_false(gsXmlMoveToParent(theResponseXml)) || + gsi_is_false(gsXmlMoveToSibling(theResponseXml, GS_D2G_CATALOG_ITEM))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError, "Could not parse xml stream: %s.", GS_D2G_CATALOG_ITEM); + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + return d2giFreeCatalogItems(theCatalog, outItemList, result, gsi_true); + } + } + } // for loop + + return GS_SUCCESS; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseLoadCatalogItemsFromResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GLoadCatalogItemsResponse *getAllItemsResponse) +{ + + GSResult result = GS_SUCCESS; + + result = d2giParseGetResponse(theResponseXml,GS_D2G_GETALLITEMS_RESULT ); + if (GS_FAILED(result)) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): got bad status code %x\n", + __FILE__, __FUNCTION__, __LINE__ , result); + + // gsi_true - We failed remove all items permanently + return d2giFreeCatalogItems(theCatalog, getAllItemsResponse->mItemList, result, gsi_true); + } + getAllItemsResponse->mItemList = (D2GCatalogItemList *)gsimalloc(sizeof(D2GCatalogItemList)); + + if (getAllItemsResponse->mItemList == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): got bad status code %x\n", + __FILE__, __FUNCTION__, __LINE__ , result); + + // We failed; Remove all items permanently (the last parameter tells the method to do this) + return d2giFreeCatalogItems(theCatalog, getAllItemsResponse->mItemList, GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory), gsi_true); + } + memset(getAllItemsResponse->mItemList, 0, sizeof(D2GCatalogItemList *)); + return d2giParseItemsFromResponse(theInstance, theCatalog, theResponseXml, getAllItemsResponse->mItemList); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseGetItemsByCategoryFromResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GLoadCatalogItemsByCategoryResponse *response) +{ + + GSResult result = GS_SUCCESS; + + result = d2giParseGetResponse(theResponseXml,GS_D2G_GETITEMSBYCATEGORY_RESULT ); + if (GS_FAILED(result)) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): got bad status code\n", + __FILE__, __FUNCTION__, __LINE__); + return d2giFreeCatalogItems(theCatalog, response->mItemList, result, gsi_true); + } + + response->mItemList = (D2GCatalogItemList *)gsimalloc(sizeof(D2GCatalogItemList)); + if (response->mItemList == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): got bad status code %x\n", + __FILE__, __FUNCTION__, __LINE__ , result); + + // We failed; Remove all items permanently (the last parameter tells the method to do this) + return d2giFreeCatalogItems(theCatalog, response->mItemList, GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory), gsi_true); + } + + return d2giParseItemsFromResponse(theInstance, theCatalog, theResponseXml, response->mItemList); +} + +/////////////////////////////////////////////////////////////////////////////// +// Orders & Purchases +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseOrderValidationFromResponse(GSXmlStreamReader theResponseXml, + D2GOrderValidation *theValidation) + +{ + GSResult result = GS_SUCCESS; + const char *tempIsValid,*tempMsg; + int tempValidCode, tempIsValidLen, tempMsgLen; + + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "ordervalidation"))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: ordervalidation\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + if (gsi_is_false(gsXmlReadChildAsString(theResponseXml, "isvalid", &tempIsValid, &tempIsValidLen)) || + gsi_is_false(gsXmlReadChildAsString(theResponseXml, "message", &tempMsg, &tempMsgLen)) || + gsi_is_false(gsXmlReadChildAsInt (theResponseXml, "orderstatuscode", &tempValidCode)) + ) + { + gsDebugFormat( GSIDebugCat_Direct2Game,GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: orderstatuscode\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + theValidation->mIsValid = strncmp(tempIsValid, "true", tempIsValidLen) == 0 ? gsi_true : gsi_false; + theValidation->mResult = d2giOrderValidationResultFromCode(tempValidCode); + theValidation->mMessage = UTF8ToUCS2StringAllocLen(tempMsg, tempMsgLen); + + if (theValidation->mIsValid == gsi_false && + theValidation->mMessage == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed to convert to UCS2String, possibly out of memory", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_OutOfMemory); + } + + if (gsi_is_false(gsXmlMoveToParent(theResponseXml))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not move to parent."); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseOrderFromResponse(GSXmlStreamWriter theResponseXml, + D2GOrderInfo *order) +{ + + int aAccountId; + const char *tempSubTotal, *tempTax, *tempTotal, *tempGuid, *tempCultureCode, *tempCurrencyCode; + int tempSubTotalLen, tempTaxLen, tempTotalLen, tempGuidLen, tempCultureCodeLen, tempCurrencyCodeLen; + + if (gsi_is_false(gsXmlReadChildAsInt (theResponseXml, "accountid", &aAccountId)) || + gsi_is_false(gsXmlReadChildAsString(theResponseXml, "culturecode",&tempCultureCode, &tempCultureCodeLen)) || + gsi_is_false(gsXmlReadChildAsString(theResponseXml, "currencycode",&tempCurrencyCode, &tempCurrencyCodeLen)) || + gsi_is_false(gsXmlReadChildAsString(theResponseXml, "subtotal", &tempSubTotal, &tempSubTotalLen)) || + gsi_is_false(gsXmlReadChildAsString(theResponseXml, "tax", &tempTax, &tempTaxLen)) || + gsi_is_false(gsXmlReadChildAsString(theResponseXml, "total", &tempTotal, &tempTotalLen)) || + gsi_is_false(gsXmlReadChildAsString(theResponseXml, "rootorderid",&tempGuid, &tempGuidLen)) + ) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "s :: %s (%d): could not parse xml stream: rootorderid\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + order->mAccountId = aAccountId; + order->mRootOrderGuid = UTF8ToUCS2StringAllocLen(tempGuid, tempGuidLen); + order->mGeoInfo.mCultureCode = UTF8ToUCS2StringAllocLen(tempCultureCode, tempCultureCodeLen); + order->mGeoInfo.mCurrencyCode= UTF8ToUCS2StringAllocLen(tempCurrencyCode, tempCurrencyCodeLen); + order->mSubTotal = UTF8ToUCS2StringAllocLen(tempSubTotal, tempSubTotalLen); + order->mTax = UTF8ToUCS2StringAllocLen(tempTax, tempTaxLen); + order->mTotal = UTF8ToUCS2StringAllocLen(tempTotal, tempTotalLen); + + + if (order->mSubTotal == NULL || + order->mTax == NULL || + order->mTotal == NULL || + order->mGeoInfo.mCultureCode == NULL || + order->mGeoInfo.mCurrencyCode == NULL || + order->mRootOrderGuid == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "s :: %s (%d): Failed to convert to UCS2String, out of memory", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_OutOfMemory); + } + return d2giParseOrderValidationFromResponse(theResponseXml, &order->mValidation); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseOrderTotalFromResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamWriter theResponseXml, + D2GOrderTotal *orderTotal) +{ + GSResult result; + gsi_u32 i; + + result = d2giParseOrderFromResponse(theResponseXml, &orderTotal->mOrder); + if (GS_FAILED(result)) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed to parse D2GOrderInfo structure in d2giParseOrderFromResponse", + __FILE__, __FUNCTION__, __LINE__); + return result; + } + + if (gsi_is_false(gsXmlReadChildAsDateTimeElement(theResponseXml, "datequoted", &orderTotal->mQuoteDate))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_HotError, + "%s :: %s (%d): could not parse xml stream: datequoted\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "orderitemtotals")) || + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, GS_D2G_COUNT_ELEMENT, (int *)&orderTotal->mOrderItemList.mCount))) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_HotError, + "%s :: %s (%d): could not parse xml stream: count\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + if (orderTotal->mOrderItemList.mCount == 0) + { + orderTotal->mOrderItemList.mOrderItems = NULL; + return GS_SUCCESS; + } + + orderTotal->mOrderItemList.mOrderItems = (D2GOrderItem *)gsimalloc(sizeof(D2GOrderItem) * orderTotal->mOrderItemList.mCount); + if (orderTotal->mOrderItemList.mOrderItems == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed to allocate order items, out of memory", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_OutOfMemory); + } + + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "orderitemtotal"))) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_HotError, + "%s :: %s (%d): could not parse xml stream (moveto): orderitemtotal\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + for(i = 0; i < orderTotal->mOrderItemList.mCount; i++) + { + result = d2giParseOrderItemFromResponse(theInstance, + theCatalog, + &orderTotal->mOrderItemList.mOrderItems[i], + theResponseXml); + if (GS_FAILED(result)) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed to parse D2GOrderTotal structure in d2giParseOrderTotalFromResponse", + __FILE__, __FUNCTION__, __LINE__); + break; + } + + if (i < orderTotal->mOrderItemList.mCount - 1) + { + if (gsi_is_false(gsXmlMoveToSibling(theResponseXml, "orderitemtotal"))) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_HotError, + "%s :: %s (%d): could not parse xml stream (movetosibling): orderitemtotal\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + } + } + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseOrderPurchaseFromResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamWriter theResponseXml, + D2GOrderPurchase *orderPurchase, + gsi_bool updateManifest) +{ + GSResult result = GS_SUCCESS; + gsi_u32 i; + + result = d2giParseOrderFromResponse(theResponseXml, &orderPurchase->mOrder); + + if (GS_FAILED(result)) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Failed to parse D2GOrderInfo structure in d2giParseOrderFromResponse"); + return result; + } + + if (gsi_is_false(gsXmlReadChildAsDateTimeElement(theResponseXml,"datepurchased",&orderPurchase->mPurchaseDate))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_HotError,"Could not parse xml stream: datequoted."); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + //=============================================== + // Now we start parsing the order item purchases + //=============================================== + + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, GS_D2G_ORDER_ITEM_PURCHASES)) || + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, GS_D2G_COUNT_ELEMENT, + (int *)&orderPurchase->mItemPurchases.mCount))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_HotError,"Could not parse xml stream: %s, %s.", + GS_D2G_ORDER_ITEM_PURCHASES, GS_D2G_COUNT_ELEMENT); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + if (orderPurchase->mItemPurchases.mCount == 0) + { + orderPurchase->mItemPurchases.mOrderItemPurchases = NULL; + return GS_SUCCESS; + } + + orderPurchase->mItemPurchases.mOrderItemPurchases = (D2GOrderItemPurchase *) + gsimalloc(sizeof(D2GOrderItemPurchase) * orderPurchase->mItemPurchases.mCount); + + if (orderPurchase->mItemPurchases.mOrderItemPurchases == NULL) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError, "Failed to allocate order items, out of memory."); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_OutOfMemory); + } + + //================================================== + // Now we start parsing the each order item purchase + //================================================== + + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, GS_D2G_ORDER_ITEM_PURCHASE))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_HotError, "Could not parse xml stream: %s.", GS_D2G_ORDER_ITEM_PURCHASE); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + //determine if we need to update Manifest + if ((updateManifest == gsi_true) && + (orderPurchase->mOrder.mValidation.mIsValid)) + { + updateManifest =(GS_RESULT_CODE(orderPurchase->mOrder.mValidation.mResult) == D2GResultCode_Order_Ok); + } + + for(i = 0; i < orderPurchase->mItemPurchases.mCount; i++) + { + result = d2giParseOrderItemPurchaseFromResponse(theInstance, + theCatalog, + &orderPurchase->mItemPurchases.mOrderItemPurchases[i], + theResponseXml, + updateManifest); + + if (GS_FAILED(result)) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError, "Failed to parse D2GOrderItemPurchase from response."); + return result; + } + + if (i < orderPurchase->mItemPurchases.mCount - 1) + { + if (gsi_is_false(gsXmlMoveToSibling(theResponseXml, GS_D2G_ORDER_ITEM_PURCHASE))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_HotError, "Could not parse xml stream: %s.", GS_D2G_ORDER_ITEM_PURCHASE); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + } + + } + if (gsi_is_false(gsXmlMoveToParent(theResponseXml))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not move to parent."); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + if (gsi_is_false(gsXmlMoveToParent(theResponseXml))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not move to parent."); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseGetOrderTotalResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamWriter theResponseXml, + D2GGetOrderTotalResponse *theResponse) +{ + GSResult result; + result = d2giParseGetResponse(theResponseXml, GS_D2G_GETORDERTOTAL_RESULT); + + if (GS_SUCCEEDED(result)) + { + // move to response and start with the order object + GSResult orderResult; + if (gsi_is_false(gsXmlMoveToNext(theResponseXml, "ordertotal"))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: GS_D2G_GETORDERTOTAL_RESPONSE", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + theResponse->mOrderTotal = (D2GOrderTotal *)gsimalloc(sizeof(D2GOrderTotal)); + memset(theResponse->mOrderTotal, 0, sizeof(D2GOrderTotal)); + if(theResponse->mOrderTotal == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed to allocate D2GOrderTotal, out of memory", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_OutOfMemory); + } + + // go through the order total object and grab all the internal data + orderResult = d2giParseOrderTotalFromResponse(theInstance, + theCatalog, + theResponseXml, + theResponse->mOrderTotal); + if (GS_FAILED(orderResult)) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed parse in d2giParseOrderFromResponse", + __FILE__, __FUNCTION__, __LINE__); + d2giFreeOrderTotal(theResponse->mOrderTotal); + theResponse->mOrderTotal = NULL; + return result; + } + } + + GSI_UNUSED(theInstance); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseStartOrderResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GStartOrderResponse *response) +{ + GSResult result; + + result = d2giParseGetResponse(theResponseXml,GS_D2G_PURCHASEITEMS_RESULT); + + if (GS_SUCCEEDED(result)) + { + + GSResult orderResult; + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "orderpurchase"))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: orderpurchase\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + response->mOrderPurchase = (D2GOrderPurchase *)gsimalloc(sizeof(D2GOrderPurchase)); + memset(response->mOrderPurchase, 0, sizeof(D2GOrderPurchase)); + + // getting order parsed with purchase gsi_true + orderResult = d2giParseOrderPurchaseFromResponse(theInstance, + theCatalog, + theResponseXml, + response->mOrderPurchase, + gsi_false); + if (GS_FAILED(orderResult)) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed to parse order", + __FILE__, __FUNCTION__, __LINE__); + d2giFreeOrderPurchase(response->mOrderPurchase); + response->mOrderPurchase = NULL; + return orderResult; + } + } + + GSI_UNUSED(theInstance); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseIsOrderCompleteResponse( D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GIsOrderCompleteResponse *response) +{ + GSResult result; + + result = d2giParseGetResponse(theResponseXml, GS_D2G_PLACEDORDER_RESULT); + + if (GS_SUCCEEDED(result)) + { + GSResult orderResult; + gsi_bool updateManifest = gsi_true; + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "orderpurchase"))) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: GS_D2G_PLACEDORDER_RESPONSE", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + response->mOrderPurchase = (D2GOrderPurchase *)gsimalloc(sizeof(D2GOrderPurchase)); + if (response->mOrderPurchase == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Out of memory when allocating: response->mOrderPurchase", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + memset(response->mOrderPurchase, 0, sizeof(D2GOrderPurchase)); + + //this call will update the manifest file if the purchase is complated + orderResult = d2giParseOrderPurchaseFromResponse(theInstance, + theCatalog, + theResponseXml, + response->mOrderPurchase, + updateManifest); + if (GS_FAILED(orderResult)) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed to parse order", + __FILE__, __FUNCTION__, __LINE__); + d2giFreeOrderPurchase(response->mOrderPurchase); + response->mOrderPurchase = NULL; + return orderResult; + } + } + + GSI_UNUSED(theInstance); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giParseGetPurchaseHistoryResponse +// +GSResult d2giParseGetPurchaseHistoryResponse( D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GGetPurchaseHistoryResponse *response) +{ + GSResult result; + gsi_u32 i; + + result = d2giParseGetResponse(theResponseXml, GS_D2G_GETPURCHASEHISTORY_RESULT); + + if (GS_SUCCEEDED(result)) + { + + response->mPurchaseHistory = (D2GPurchaseHistory *)gsimalloc(sizeof(D2GPurchaseHistory)); + if (response->mPurchaseHistory == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Out of memory when allocating: response->mPurchaseHistory", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + // clear this out since we want to have count = 0 and purchases = NULL for cleanup. + response->mPurchaseHistory->mCount = 0; + response->mPurchaseHistory->mPurchases = NULL; + + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "orderpurchases")) || + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "count", (int *)&response->mPurchaseHistory->mCount))) + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + return result; + } + + if (response->mPurchaseHistory->mCount == 0) + { + // we've already nulled the purchases and just need to return success. + return result; + } + else + { + response->mPurchaseHistory->mPurchases = (D2GOrderPurchase *) + gsimalloc(sizeof(D2GOrderPurchase) * response->mPurchaseHistory->mCount); + memset(response->mPurchaseHistory->mPurchases, 0, sizeof(D2GOrderPurchase) * response->mPurchaseHistory->mCount); + } + + for (i=0 ; i < response->mPurchaseHistory->mCount ; i++) + { + // Now go through each purchase order + + GSResult orderResult; + if (i == 0) + { + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "orderpurchase"))) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: orderpurchase\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + } + else + { + if (gsi_is_false(gsXmlMoveToSibling(theResponseXml, "orderpurchase"))) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: orderpurchase\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + } + + orderResult = d2giParseOrderPurchaseFromResponse(theInstance, + theCatalog, + theResponseXml, + &response->mPurchaseHistory->mPurchases[i], + gsi_false); + if (GS_FAILED(orderResult)) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed to parse order\n", + __FILE__, __FUNCTION__, __LINE__); + return orderResult; + } + } + if (gsi_is_false(gsXmlMoveToParent(theResponseXml))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not move to parent."); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + } + else + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed to parse D2GOrderInfo structure in d2giParseGetPurchaseHistoryResponse\n", + __FILE__, __FUNCTION__, __LINE__); + } + + GSI_UNUSED(theInstance); + return result; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Download Info +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseDownloadHeaderFromResponse(GSXmlStreamReader theResponseXml, + gsi_char *userHeader) +{ + GSResult result = GS_SUCCESS; + + int tempKeyLen = 0, + tempValueLen = 0; + + const char *tempKey = NULL, + *tempValue = NULL; + + if (gsi_is_false(gsXmlReadChildAsString(theResponseXml, "key", &tempKey, &tempKeyLen)) || + gsi_is_false(gsXmlReadChildAsString(theResponseXml, "value",&tempValue, &tempValueLen))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: value\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + strncat(userHeader,tempKey, tempKeyLen); + strncat(userHeader,":", 1); + strncat(userHeader,tempValue, tempValueLen); + strcat(userHeader,CRLF); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseDownloadUserHeadersFromResponse(GSXmlStreamReader theResponseXml, + gsi_char **userHeader) +{ + GSResult result = GS_SUCCESS; + gsi_u32 nHeaderCount = 0; + gsi_u32 i = 0; + + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "urlrequestheaderlist")) || + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "count", (int*)&nHeaderCount))) + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + return result; + } + + if (nHeaderCount == 0) + { + *userHeader = NULL; + return result; + } + else + { + int nMaxUserHeaderLen = 4096; + + *userHeader = gsimalloc(sizeof(gsi_char) * nMaxUserHeaderLen + 1); + memset(*userHeader, '\0', (sizeof(gsi_char) * nMaxUserHeaderLen + 1)); + + for (i=0 ; i < nHeaderCount ; i++) + { + if (i == 0) + { + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "urlrequestheader"))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, + GSIDebugType_Misc, + GSIDebugLevel_WarmError, + "d2giParseDownloadUserHeadersFromResponse: could not \ + parse xml stream: urlrequestheader\n"); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + } + else + { + if (gsi_is_false(gsXmlMoveToSibling(theResponseXml, "urlrequestheader"))) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: urlrequestheader\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + } + + result = d2giParseDownloadHeaderFromResponse(theResponseXml, *userHeader); + + if (GS_FAILED(result)) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: d2giParseDownloadUserHeadersFromResponse\n", + __FILE__, __FUNCTION__, __LINE__); + return result; + } + } + } + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +// Extra Info +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseExtraInfo(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GExtraInfo *theExtraInfo) +{ + GSResult result = GS_SUCCESS; + const char *aKey, *aValue; + int aKeyLen, aValueLen; + + if (gsi_is_false(gsXmlReadChildAsString(theResponseXml, "key", &aKey, &aKeyLen)) || + gsi_is_false(gsXmlReadChildAsString(theResponseXml, "value",&aValue, &aValueLen))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError, "Could not parse xml stream: value."); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + // First Check if we have it in the cache + // If not add it. If we do then update it. + theExtraInfo->mKey = UTF8ToUCS2StringAllocLen(aKey, aKeyLen); + theExtraInfo->mValue = UTF8ToUCS2StringAllocLen(aValue, aValueLen); + + if (theExtraInfo->mKey == NULL || theExtraInfo->mValue == NULL) + { + GS_D2G_LOG(GSIDebugType_Memory, GSIDebugLevel_HotError, "Failed to allocate memory: theExtraInfo->mValue, theExtraInfo->mKey."); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + if (theCatalog) + { + D2GExtraInfo *pExtraInfo; + // Update the cache with the key,value pair. + pExtraInfo = d2giGetExtraInfo(theInstance, theCatalog, theExtraInfo->mKey); + if (pExtraInfo == NULL) + { + pExtraInfo = d2giNewExtraInfo(theInstance, theCatalog, theExtraInfo); + } + else + { + d2giResetExtraInfo(pExtraInfo); + pExtraInfo->mKey = theExtraInfo->mKey; + pExtraInfo->mValue = theExtraInfo->mValue; + } + } + + GSI_UNUSED(theInstance); + return result; +} + +GSResult d2giParseExtraInfoList(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GExtraInfoList *extraInfoList) +{ + gsi_u32 i; + GSResult result = GS_SUCCESS; + if (gsi_is_false(gsXmlMoveToNext(theResponseXml, GS_D2G_ETRA_INFO_LIST)) || + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "count", + (int *)&extraInfoList->mCount))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError, "Could not parse xml stream: extrainfolist."); + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + return result; + } + + if (extraInfoList->mCount == 0) + { + extraInfoList->mExtraInfoElements = NULL; + return result; + } + else + { + extraInfoList->mExtraInfoElements = (D2GExtraInfo *) gsimalloc(sizeof(D2GExtraInfo)* extraInfoList->mCount); + memset(extraInfoList->mExtraInfoElements, 0, sizeof(D2GExtraInfo) * extraInfoList->mCount); + } + + for (i=0 ; i < extraInfoList->mCount ; i++) + { + if (i == 0) + { + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, GS_D2G_EXTRA_INFO))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not parse xml stream: %s", GS_D2G_EXTRA_INFO); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + } + else + { + if (gsi_is_false(gsXmlMoveToSibling(theResponseXml, GS_D2G_EXTRA_INFO))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not parse xml stream: %s", GS_D2G_EXTRA_INFO); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + } + + result = d2giParseExtraInfo(theInstance, + theCatalog, + theResponseXml, + &extraInfoList->mExtraInfoElements[i]); + if (GS_FAILED(result)) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not parse xml stream: %s", GS_D2G_EXTRA_INFO); + return result; + } + } + if (gsi_is_false(gsXmlMoveToParent(theResponseXml))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not move to parent."); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + return result; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseLoadExtraCatalogInfoResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GLoadExtraCatalogInfoResponse *response) +{ + GSResult result; + + result = d2giParseGetResponse(theResponseXml, GS_D2G_GET_STORE_EXTENSION_DATA_RESULT); + if (GS_SUCCEEDED(result)) + { + response->mExtraCatalogInfoList = (D2GExtraInfoList *)gsimalloc(sizeof(D2GExtraInfoList)); + if (response->mExtraCatalogInfoList == NULL) + { + GS_D2G_LOG(GSIDebugType_Memory, GSIDebugLevel_HotError, "Unable to allocate D2GExtraInfoList."); + result = GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + return result; + } + + // clear this array otherwise the free function will fail since + // it's not clean memory + memset(response->mExtraCatalogInfoList, 0, sizeof(D2GExtraInfoList)); + + result = d2giParseExtraInfoList(theInstance, theCatalog, theResponseXml, response->mExtraCatalogInfoList); + } + else + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError, "Failed to parse D2GExtraItem structure in d2giParseGetExtraInfoResponse"); + } + + GSI_UNUSED(theInstance); + return result; +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseGetItemActivationResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GGetItemActivationResponse *response) + + +{ + GSResult result = GS_SUCCESS; + + result = d2giParseGetResponse(theResponseXml, GS_D2G_GETITEMACTIVATIONDATA_RESULT); + + if (GS_SUCCEEDED(result)) + { + const char *activationCode; + int activationCodeLen; + if (gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "itemid", (int *)&response->mItemId))) + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + return result; + } + + gsXmlReadChildAsString(theResponseXml, "activationcode",&activationCode, &activationCodeLen); + + if (activationCodeLen > 0) + { + response->mActivationCode = UTF8ToUCS2StringAllocLen(activationCode,activationCodeLen); + } + else + { + response->mActivationCode = NULL; + } + } + GSI_UNUSED(theInstance); + GSI_UNUSED(theCatalog); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseCheckContentUpdates(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GCheckContentUpdatesResponse *response) + +{ + GSResult result = GS_SUCCESS; + + result = d2giParseGetResponse(theResponseXml, GS_D2G_CHECKFORCONTENTUPDATES_RESULT); + + if (GS_SUCCEEDED(result)) + { + response->mContentUpdateList = (D2GContentUpdateList *)gsimalloc(sizeof(D2GContentUpdateList)); + if (response->mContentUpdateList == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_HotError, + "%s :: %s (%d): unable to allocate D2GDownloadList\n", + __FILE__, __FUNCTION__, __LINE__); + result = GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + return result; + } + + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "downloaditemupdates")) || + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, GS_D2G_COUNT_ELEMENT, (int *)&response->mContentUpdateList->mCount))) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream (move): downloads\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + if (response->mContentUpdateList->mCount == 0) + { + response->mContentUpdateList->mContentUpdates = NULL; + } + else + { + gsi_u32 i = 0; + + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "download"))) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: license\n", + __FILE__, __FUNCTION__, __LINE__); + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + response->mContentUpdateList->mContentUpdates = (D2GContentUpdate *)gsimalloc(sizeof(D2GContentUpdate) * response->mContentUpdateList->mCount); + if (response->mContentUpdateList->mContentUpdates == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed to allocate downloads, out of memory", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + for (i = 0; i < response->mContentUpdateList->mCount; i++) + { + const char *aName, *anAssetType, *isRequired; + int aNameLen, anAssetTypeLen, isRequiredLen; + D2GContentUpdate *currentItem = &response->mContentUpdateList->mContentUpdates[i]; + + if (gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "itemid", (int *)¤tItem->mItemId))|| + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "downloadurlid", (int *)¤tItem->mDownloadItem.mUrlId))|| + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "changetype", (int *)¤tItem->mChangeType)) || + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "fileid", (int *)¤tItem->mDownloadItem.mFileId)) || + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "sequence", (int *)¤tItem->mDownloadItem.mSequence)) || + gsi_is_false(gsXmlReadChildAsString(theResponseXml, "name", &aName, &aNameLen)) || + gsi_is_false(gsXmlReadChildAsString(theResponseXml, "assettype", &anAssetType, &anAssetTypeLen)) || + gsi_is_false(gsXmlReadChildAsFloat(theResponseXml, "version",¤tItem->mDownloadItem.mVersion)) || + gsi_is_false(gsXmlReadChildAsString(theResponseXml, "requirementlevelcode",&isRequired, &isRequiredLen))) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: assettype\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + + currentItem->mDownloadItem.mName = UTF8ToUCS2StringAllocLen(aName, aNameLen); + if (currentItem->mDownloadItem.mName == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed to convert to UCS2String, out of memory", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + currentItem->mDownloadItem.mAssetType = UTF8ToUCS2StringAllocLen(anAssetType, anAssetTypeLen); + + // check if the download is optional if not set mRequired to true + if (strncmp("MANDATORY", isRequired, isRequiredLen) ==0) + { + currentItem->mDownloadType = D2GDownloadMandatory ; + } + else if (strncmp("FORCED", isRequired, isRequiredLen) == 0) + { + currentItem->mDownloadType = D2GDownloadForced ; + } + else + { + currentItem->mDownloadType = D2GDownloadOptional ; + } + + if (i < response->mContentUpdateList->mCount - 1) + { + if (gsi_is_false(gsXmlMoveToSibling(theResponseXml, "download"))) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream (move): download\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + } + } + if (gsi_is_false(gsXmlMoveToParent(theResponseXml))) + { + GS_D2G_LOG(GSIDebugType_Misc, GSIDebugLevel_WarmError,"Could not move to parent."); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + }// else + } // if (GS_SUCCEEDED(result)) + GSI_UNUSED(theInstance); + GSI_UNUSED(theCatalog); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giParseManifestRecord +// +GSResult d2giParseManifestRecord(char *buffer, + D2GContentUpdate *theContentUpdate) +{ + GSResult result = GS_SUCCESS; + GSXmlStreamReader *reader ; + + memset(theContentUpdate, 0 , sizeof(D2GContentUpdate)); + + GS_ASSERT(buffer); + if ((buffer == NULL) || (theContentUpdate == NULL)) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_HotError, + "%s :: %s (%d): Null pointer passed\n", + __FILE__, __FUNCTION__, __LINE__); + + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_InvalidParameters); + + } + reader = gsXmlCreateStreamReader(); + + // now create parse-able xml string form a string + // convert to xml string + if(gsi_is_false(gsXmlParseBuffer(reader, buffer, (int)strlen(buffer)))) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_HotError, + "%s :: %s (%d): could parse buffer into an xml string \n", + __FILE__, __FUNCTION__, __LINE__); + + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_InvalidParameters); + + } + else + { + // now we need to parse it + if (gsi_is_false(gsXmlMoveToStart(reader)) || + gsi_is_false(gsXmlMoveToNext(reader, "download"))) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): could not parse xml: start; tag %s", + __FILE__, __FUNCTION__, __LINE__, "download"); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + result = d2giParseDownload(reader, theContentUpdate); + } + gsXmlFreeReader(reader); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giParseAndDecodeManifestRecord +// +GSResult d2giParseAndDecodeManifestRecord(char *manifestRead, + D2GContentUpdate *contentUpdate) +{ + GSResult result; + D2GIString manifestXml; + + manifestXml.str = gsimalloc((strlen(manifestRead)+2) * sizeof(char)); + + memset (manifestXml.str, '\0',((strlen(manifestRead)+2)*sizeof(char)) ); + manifestXml.len = (int)strlen(manifestXml.str); + + // Uses URL safe encoding + B64Decode(manifestRead, manifestXml.str, (int)strlen(manifestRead), &manifestXml.len, 2); + result = d2giParseManifestRecord(manifestXml.str, contentUpdate); + gsifree(manifestXml.str); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giParseDownload +// +GSResult d2giParseDownload(GSXmlStreamReader *reader, + D2GContentUpdate *contentUpdate) +{ + + if (gsi_is_false(gsXmlReadChildAsInt(reader, "itemid", (int *)&contentUpdate->mItemId)) || + gsi_is_false(gsXmlReadChildAsInt(reader, "downloadurlid", (int *)&contentUpdate->mDownloadItem.mUrlId)) || + gsi_is_false(gsXmlReadChildAsFloat(reader,"version", (float *)&contentUpdate->mDownloadItem.mVersion))) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: version\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + } + return GS_SUCCESS; +} diff --git a/Direct2Game/d2gDeserialize.h b/Direct2Game/d2gDeserialize.h new file mode 100644 index 00000000..6a5f5b3f --- /dev/null +++ b/Direct2Game/d2gDeserialize.h @@ -0,0 +1,171 @@ +/** +* d2gDeserialize.h +* +* GameSpy DIRECT TO GAME SDK +* This file is part of the Direct to Game SDK designed and developed by GameSpy Tech. +* Copyright (c) 2008, GameSpy Technology +*/ +#ifndef __D2GDESERIALIZE_H__ +#define __D2GDESERIALIZE_H__ + +#include "../ghttp/ghttpSoap.h" +#include "../common/gsCrypt.h" +#include "../common/gsLargeInt.h" +#include "d2gUtil.h" + +/////////////////////////////////////////////////////////////////////////////// +// Common +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseGetResponse(GSXmlStreamReader theResponseXml , + const char *theResponseTag); + +GSResult d2giParseResponseHeader(GSXmlStreamReader theResponseXml); + +/////////////////////////////////////////////////////////////////////////////// +// Store Availability +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseGetStoreAvailResponse(D2GIInstance * instance, + GSXmlStreamReader theResponseXml, + D2GGetStoreAvailabilityResponse *response); + +/////////////////////////////////////////////////////////////////////////////// +// Extra Info +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseExtraInfo(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GExtraInfo *theExtraInfo); + +GSResult d2giParseExtraInfoList(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GExtraInfoList *response); + +GSResult d2giParseLoadExtraCatalogInfoResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GLoadExtraCatalogInfoResponse *response); + +GSResult d2giParseGetItemActivationResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GGetItemActivationResponse *response); +/////////////////////////////////////////////////////////////////////////////// +// Parsing Catalog Items +/////////////////////////////////////////////////////////////////////////////// + +GSResult d2giParseGeoInfoFromResponse(D2GIInstance *theInstance, + D2GGeoInfo *pGeoInfo, + GSXmlStreamReader theResponse); + +GSResult d2giParseItemFromResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + D2GCatalogItem **item, + GSXmlStreamReader theResponse); + + +GSResult d2giParseItemsFromResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GCatalogItemList *outItemList); + +GSResult d2giParseLoadCatalogItemsFromResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GLoadCatalogItemsResponse *getAllItemsResponse); + +GSResult d2giParseGetItemsByCategoryFromResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GLoadCatalogItemsByCategoryResponse *response); + +/////////////////////////////////////////////////////////////////////////////// +// Credit Cards +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giParseCreditCardInfoList(D2GIInstance *instance, + GSXmlStreamReader theResponseXml, + D2GGetUserCreditCardsResponse *responseOut); + +/////////////////////////////////////////////////////////////////////////////// +// Parsing Orders and Purchases +/////////////////////////////////////////////////////////////////////////////// + +GSResult d2giParseOrderItemFromResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + D2GOrderItem *theOrderItem, + GSXmlStreamWriter theResponseXml); + +GSResult d2giParseOrderItemPurchaseFromResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + D2GOrderItemPurchase *theOrderItem, + GSXmlStreamWriter theResponseXml, + gsi_bool updateManifest); + +GSResult d2giParseOrderValidationFromResponse(GSXmlStreamReader theResponseXml, + D2GOrderValidation *theValidation); + +GSResult d2giParseOrderFromResponse(GSXmlStreamWriter theResponseXml, + D2GOrderInfo *order); + +GSResult d2giParseOrderTotalFromResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamWriter theResponseXml, + D2GOrderTotal *orderTotal); + +GSResult d2giParseOrderPurchaseFromResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamWriter theResponseXml, + D2GOrderPurchase *orderPurchase, + gsi_bool updateManifest); + +GSResult d2giParseGetOrderTotalResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamWriter theResponseXml, + D2GGetOrderTotalResponse *theResponse); + +GSResult d2giParseStartOrderResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GStartOrderResponse *response); + + +GSResult d2giParseIsOrderCompleteResponse( D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GIsOrderCompleteResponse *response); + +GSResult d2giParseGetPurchaseHistoryResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GGetPurchaseHistoryResponse *response); + +/////////////////////////////////////////////////////////////////////////////// +// Download Info +/////////////////////////////////////////////////////////////////////////////// + +GSResult d2giParseDownloadHeaderFromResponse(GSXmlStreamReader theResponseXml, + gsi_char *userHeader); + +GSResult d2giParseDownloadUserHeadersFromResponse(GSXmlStreamReader theResponseXml, + gsi_char **userHeader); + +GSResult d2giParseGetItemActivationResponse(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GGetItemActivationResponse *response); + +GSResult d2giParseCheckContentUpdates(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamReader theResponseXml, + D2GCheckContentUpdatesResponse *response); + +GSResult d2giParseManifestRecord(char *buffer, + D2GContentUpdate *theContentUpdate); + +GSResult d2giParseAndDecodeManifestRecord(char *manifestRead, + D2GContentUpdate *contentUpdate); + +GSResult d2giParseDownload(GSXmlStreamReader *reader, + D2GContentUpdate *contentUpdate); + +#endif diff --git a/Direct2Game/d2gDownloads.c b/Direct2Game/d2gDownloads.c new file mode 100644 index 00000000..18838c78 --- /dev/null +++ b/Direct2Game/d2gDownloads.c @@ -0,0 +1,225 @@ +/** +* d2gDownloads.c +* +* GameSpy DIRECT TO GAME SDK +* This file is part of the Direct to Game SDK designed and developed by GameSpy Tech. +* Copyright (c) 2008, GameSpy Technology +*/ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// includes +#include "../common/gsResultCodes.h" +#include "../common/gsAvailable.h" +#include "Direct2Game.h" +#include "d2gMain.h" +#include "d2gServices.h" +#include "d2gUtil.h" +#include "d2gDownloads.h" + +typedef struct D2GIDownloadThreadData +{ + //gsi_bool mCancelThread; + gsi_char *mUrl; + gsi_char *mFullPath; + gsi_char *mFullPathTemporary; + gsi_char *mHttpAdditionalHeaders; + D2GDownloadProgressCallback mProgressCallback; + D2GDownloadCompleteCallback mCompleteCallback; + void *mUserData; +} D2GIDownloadThreadData; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void d2giDownloadHttpProgressCallback( GHTTPRequest request, + GHTTPState state, + const char *buffer, + GHTTPByteCount bufferLen, + GHTTPByteCount bytesReceived, + GHTTPByteCount totalSize, + void *param) +{ + // Check progress and call any developer callbacks + D2GIDownloadThreadData *dLoadData; + dLoadData = (D2GIDownloadThreadData *)param; + + GS_ASSERT(dLoadData); + + if (state == GHTTPReceivingFile) + { + // At this point the callback should be set. We already did most of the error checking + // so no need to assert. + dLoadData->mProgressCallback((gsi_u32)bytesReceived, (gsi_u32)totalSize, dLoadData->mUserData); + } + + GSI_UNUSED(request); + GSI_UNUSED(bufferLen); + GSI_UNUSED(buffer); + GSI_UNUSED(buffer); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GHTTPBool d2giDownloadHttpCompletedCallback( GHTTPRequest httpRequest, + GHTTPResult httpResult, + char *buffer, + GHTTPByteCount bufferLen, + char* headers, + void *param) +{ + D2GIDownloadThreadData *dLoadData; + GSResult result; + dLoadData = (D2GIDownloadThreadData *)param; + + result = d2giResultFromHttpResult(httpResult); + + if (GS_SUCCEEDED(result)) + { + // The download succeeded + // rename the temporary download file to the actual file name + FILE *tempFileCheck; + tempFileCheck = fopen(dLoadData->mFullPath, "rb"); + if (tempFileCheck) + { + fclose(tempFileCheck); + _tremove(dLoadData->mFullPath); + } + _trename( dLoadData->mFullPathTemporary, dLoadData->mFullPath); + dLoadData->mCompleteCallback( result, dLoadData->mFullPath, dLoadData->mUserData); + } + else + { + dLoadData->mCompleteCallback( result, NULL, dLoadData->mUserData); + } + + GSI_UNUSED(bufferLen); + GSI_UNUSED(buffer); + GSI_UNUSED(httpRequest); + return GHTTPTrue; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +#ifdef _WIN32 +DWORD WINAPI d2giDownloadThreadFunc(void *arg) +{ + D2GIDownloadThreadData *dLoadData; + GHTTPRequest httpRequest; + dLoadData = (D2GIDownloadThreadData *)arg; + + httpRequest = ghttpSaveEx( dLoadData->mUrl, //URL + dLoadData->mFullPathTemporary, //Filename + dLoadData->mHttpAdditionalHeaders, //Headers + NULL, //Post + GHTTPFalse, //Throttle + GHTTPTrue, //Blocking + d2giDownloadHttpProgressCallback, //ProgressCallback + d2giDownloadHttpCompletedCallback, //CompletedCallback + (void *)dLoadData); + if (IS_GHTTP_ERROR(httpRequest)) + { + GSResult result = d2giResultFromHttpRequest(httpRequest); + dLoadData->mCompleteCallback(result, NULL, dLoadData->mUserData); + } + + gsifree(dLoadData->mFullPath); + gsifree(dLoadData->mUrl); + gsifree(dLoadData->mFullPathTemporary); + gsifree(dLoadData->mHttpAdditionalHeaders); + gsifree(dLoadData); + return 0; +} +#endif +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giStartDownloadThread( const gsi_char *downloadLocation, + gsi_char *url, + gsi_char *httpHeaders, + D2GDownloadProgressCallback progress, + D2GDownloadCompleteCallback complete, + void *userData ) +{ +#ifdef _WIN32 + GSIThreadID threadId; + gsi_char *fileName; + gsi_char *trailingJunk; + int returnVal; + int downloadLocationLength; + D2GIDownloadThreadData *dloadThreadData; + + dloadThreadData = (D2GIDownloadThreadData *)gsimalloc(sizeof(D2GIDownloadThreadData)); + if (dloadThreadData == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_HotError, + "%s :: %s (%d): unable to allocate dloadThreadData\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + memset(dloadThreadData, 0, sizeof(D2GIDownloadThreadData)); + dloadThreadData->mUrl = url; + dloadThreadData->mHttpAdditionalHeaders = httpHeaders; + dloadThreadData->mProgressCallback = progress; + dloadThreadData->mCompleteCallback = complete; + dloadThreadData->mUserData = userData; + + + fileName = _tcsrchr(dloadThreadData->mUrl, '/'); + fileName++; + trailingJunk = _tcsrchr(fileName, '?'); + downloadLocationLength = _tcslen(downloadLocation); + + if (trailingJunk == NULL) + { + trailingJunk = (gsi_char *)(downloadLocation + downloadLocationLength); + } + dloadThreadData->mFullPath = (gsi_char *)gsimalloc(downloadLocationLength + (trailingJunk - fileName) + 1); + if (dloadThreadData->mFullPath == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_HotError, + "%s :: %s (%d): unable to allocate dloadThreadData->mFullPath\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + _tsnprintf( dloadThreadData->mFullPath, + _tcslen(downloadLocation) + (trailingJunk - fileName), + "%s%s", + downloadLocation, fileName); + dloadThreadData->mFullPath[downloadLocationLength + (trailingJunk - fileName)] = '\0'; + + // Initialize the temporary file before starting the download + dloadThreadData->mFullPathTemporary = (gsi_char *)gsimalloc( _tcslen(dloadThreadData->mFullPath) + + _tcslen( GS_D2G_DOWNLOAD_FILE_TEMP_EXT ) + 1); + if (dloadThreadData->mFullPathTemporary == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_HotError, + "%s :: %s (%d): unable to allocate dloadThreadData->mFullPathTemporary\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + _stprintf(dloadThreadData->mFullPathTemporary, "%s%s", dloadThreadData->mFullPath, GS_D2G_DOWNLOAD_FILE_TEMP_EXT); + + gsDebugFormat(GSIDebugCat_Direct2Game, + GSIDebugType_Network, + GSIDebugLevel_Comment, + "Starting download service..."); + + // start the actual download thread + returnVal = gsiStartThread( d2giDownloadThreadFunc, + 0, + (void *)dloadThreadData, + &threadId); + if (returnVal == -1) + { + // There has to be some really wrong for this to happen + // Thread creation failures would probably mean resource issues. + gsifree(dloadThreadData->mUrl); + gsifree(dloadThreadData->mFullPathTemporary); + gsifree(dloadThreadData->mFullPath); + gsifree(dloadThreadData); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_ThreadCreateFailed); + } +#endif + + return GS_SUCCESS; +} diff --git a/Direct2Game/d2gDownloads.h b/Direct2Game/d2gDownloads.h new file mode 100644 index 00000000..6ebb84d1 --- /dev/null +++ b/Direct2Game/d2gDownloads.h @@ -0,0 +1,21 @@ +/** +* d2gDownloads.h +* +* GameSpy DIRECT TO GAME SDK +* This file is part of the Direct to Game SDK designed and developed by GameSpy Tech. +* Copyright (c) 2008, GameSpy Technology +*/ + +#ifndef __D2GDOWNLOADS_H__ +#define __D2GDOWNLOADS_H__ + +#define GS_D2G_DOWNLOAD_FILE_TEMP_EXT ".tmp" +#define GS_MAX_FILENAME_LEN (256) + +GSResult d2giStartDownloadThread(const gsi_char *downloadLocation, + gsi_char *url, + gsi_char *httpHeaders, + D2GDownloadProgressCallback progress, + D2GDownloadCompleteCallback complete, + void *userData); +#endif diff --git a/Direct2Game/d2gMain.c b/Direct2Game/d2gMain.c new file mode 100644 index 00000000..d07b6191 --- /dev/null +++ b/Direct2Game/d2gMain.c @@ -0,0 +1,2491 @@ +/** +* d2gMain.c +* +* GameSpy DIRECT TO GAME SDK +* This file is part of the Direct to Game SDK designed and developed by GameSpy Tech. +* Copyright (c) 2008, GameSpy Technology +*/ + +/////////////////////////////////////////////////////////////////////////////// +// Includes +/////////////////////////////////////////////////////////////////////////////// +#include "Direct2Game.h" +#include "d2gMain.h" +#include "d2gServices.h" +#include "d2gUtil.h" +#include "../common/gsAvailable.h" +#include +#include + +/////////////////////////////////////////////////////////////////////////////// +// Initialization and Termination +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2gCreateInstance +// Brief +// Creates Direct2Game SDK instance. +// Parameters +// None. +// Return Value +// D2GInstancePtr - a void pointer to the internal D2G Instance. +// Remarks +// This function is mandatory. This API function must be called first before +// any other D2G API function. +// See Also +// d2gCleanup +// +D2GInstancePtr d2gCreateInstance() +{ + D2GIInstance * instance = NULL; + + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_Debug, + "%s :: %s (%d): Creating Commerce SDK instance\n", + __FILE__, __FUNCTION__, __LINE__); + + instance = (D2GIInstance*)gsimalloc(sizeof(D2GIInstance)); + if (instance == NULL) + return NULL; + + // initialize the struct + memset(instance, 0, sizeof(D2GIInstance)); + return instance; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2gInitialize +// Brief +// Initializes the SDK Instance created by d2gCreateInstance. +// Parameters +// theInstance [in] This pointer is created by the d2gCreateInstance. +// theCertificate [in] This is the certificate obtained from authorization +// service. +// thePrivateData [in] This is the private data obtained from authorization +// service. +// Return Value +// GSResult – Enumeration indicating success (GS_SUCCESS) or a failure code. +// Remarks +// This function is mandatory. +// See Also +// d2gCleanup +// +GSResult d2gInitialize(D2GInstancePtr theInstance, + const GSLoginCertificate * theCertificate, + const GSLoginPrivateData * thePrivateData) +{ + D2GIInstance * instance = (D2GIInstance*)theInstance; + GS_ASSERT(instance); + if (!instance) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called with NULL instance\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + // verify availability check + if (GSIGetAvailable()!=GSIACAvailable) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called without doing availability check", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + if ( theCertificate == NULL || thePrivateData == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called with NULL parameter", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_NoAvailabilityCheck); + } + + memcpy(&instance->mCertificate, theCertificate, sizeof(GSLoginCertificate)); + memcpy(&instance->mPrivateData, thePrivateData, sizeof(GSLoginPrivateData)); + instance->mTimeoutMs = GS_D2G_DEFAULT_SERVICE_TIMEOUTMS; + instance->mManifestFilePath = NULL; + instance->mServiceURL = NULL; + instance->mServiceURLBase = NULL; + GSI_UNUSED(instance); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2gCleanup +// Brief +// De-allocates memory allocated by d2gCreateInstance and d2gInitialize. +// Parameters +// theInstance [in] This pointer is created by the d2gCreateInstance. +// Return Value +// None. +// Remarks +// This function is mandatory. +// See Also +// d2gCreateInstance +// +void d2gCleanup(D2GInstancePtr theInstance) +{ + D2GIInstance * instance = (D2GIInstance*)theInstance; + GS_ASSERT(instance); + if (!instance) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called with NULL instance\n", + __FILE__, __FUNCTION__, __LINE__); + return; + } + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_Debug, + "%s :: %s (%d): called, destroying Direct2Game interface\n", + __FILE__, __FUNCTION__, __LINE__); + + gsifree(instance->mManifestFilePath); + instance->mManifestFilePath = NULL; + + gsifree(instance->mManifestFilePath); + instance->mManifestFilePath = NULL; + + gsifree(instance->mServiceURL); + instance->mServiceURL = NULL; + + gsifree(instance->mServiceURLBase); + instance->mServiceURLBase = NULL; + + gsifree(instance); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// Internal function referenced by d2gCreateCatalog +static void d2giCatalogCacheFreeItem(void * elem) +{ + D2GCatalogItem * item = (D2GCatalogItem *)elem; + if (item != NULL) + d2giResetCatalogItem(item); +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// Internal function referenced by d2gCreateCatalog +static void d2giExtraInfoCacheFree(void * elem) +{ + D2GExtraInfo * item = (D2GExtraInfo *)elem; + if (item != NULL) + d2giResetExtraInfo(item); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2gCreateCatalog +// Brief +// Creates a local catalog d2gInstance to access the backend catalog services +// from within the game. +// Parameters +// theInstance [in] This is pointer created by the d2gCreateInstance. +// gameId [in] Unique game id which is pre-assigned by GameSpy Tech. +// version [in] A version of the Catalog for the game. +// region [in] An enumerated type that specifies the region for the catalog. +// accessToken [in] A unique token that allows access to the backend catalog services. +// Return Value +// D2GCatalogPtr - Pointer to the catalog d2gInstance initialized with the input parameters. +// Remarks +// This API function is mandatory. After this API function is called, all of the +// backend catalog and purchasing services are available to the game. For the D2G SDK +// to terminate properly, d2gCleanupCatalog must be called before calling d2gCleanup. +// See Also +// d2gCleanupCatalog +// +D2GCatalogPtr d2gCreateCatalog( D2GInstancePtr theInstance, + int gameId, + gsi_u32 version, + UCS2String region, + UCS2String accessToken) +{ + D2GICatalog *d2gCatalog = NULL; + D2GIInstance *d2gInstance = (D2GIInstance*)theInstance; + + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_Debug, + "Creating Commerce SDK Catalog for a given instance\n"); + + GS_ASSERT(d2gInstance); + if (!d2gInstance) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_Debug, + "%s :: %s (%d): Null d2gInstance\n", + __FILE__, __FUNCTION__, __LINE__); + } + else + { + GS_ASSERT(region); + GS_ASSERT(accessToken); + if (region == NULL || + accessToken == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_Debug, + "%s :: %s (%d): Null pointer to accessToken or region \n", + __FILE__, __FUNCTION__, __LINE__); + } + else + { + d2gCatalog = (D2GICatalog*)gsimalloc(sizeof(D2GICatalog)); + if (d2gCatalog == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_Debug, + "%s :: %s (%d): Null Catalog possibly out of memory\n", + __FILE__, __FUNCTION__, __LINE__); + return d2gCatalog; + } + else + { + // initialize the catalog + memset(d2gCatalog, 0, sizeof(D2GICatalog)); + d2gCatalog->mGameId = gameId; + d2gCatalog->mVersion = version; + d2gCatalog->mAccessToken = goawstrdup(accessToken); + d2gCatalog->mRegion = goawstrdup(region); + d2gCatalog->mItemArray = NULL; + d2gCatalog->mAllItemsReceived = gsi_false; + d2gCatalog->mExtraInfoCache = NULL; + + // Initialize the item array + d2gCatalog->mItemArray = ArrayNew(sizeof(D2GCatalogItem), + GS_D2G_ITEMARRAY_INITIAL_COUNT, + d2giCatalogCacheFreeItem); + + d2gCatalog->mExtraInfoCache = ArrayNew(sizeof(D2GExtraInfo), + GS_D2G_EXTRAINFOCACHE_INITIAL_COUNT, + d2giExtraInfoCacheFree); + } + } + } + return d2gCatalog; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2gCleanupCatalog +// Brief +// De-allocates the memory associated with the catalog d2gInstance. +// Parameters +// theInstance [in] This pointer is created by the d2gCreateInstance. +// theCatalog [in] This is pointer created by the d2gCreateCatalog. +// Return Value +// None. +// Remarks +// This API function is mandatory. This must be called before calling d2gCleanup. +// See Also +// d2gCreateCatalog +// +void d2gCleanupCatalog(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog) +{ + D2GIInstance *d2gInstance = (D2GIInstance*)theInstance; + + D2GICatalog *d2gCatalog = (D2GICatalog*)theCatalog; + + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_Debug, + "d2gCleanupCatalog called, destroying Direct2Game Cache\n"); + + GS_ASSERT(d2gInstance); + GS_ASSERT(d2gCatalog); + if (!d2gInstance || !d2gCatalog) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called with NULL instance\n", + __FILE__, __FUNCTION__, __LINE__); + return; + } + if (d2gCatalog->mItemArray != NULL) + { + ArrayFree(d2gCatalog->mItemArray); + } + if (d2gCatalog->mExtraInfoCache != NULL) + { + ArrayFree(d2gCatalog->mExtraInfoCache); + } + gsifree(d2gCatalog->mRegion); + gsifree(d2gCatalog->mAccessToken); + gsifree(d2gCatalog); + d2gCatalog = NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//d2gGetStoreAvailability +// Brief +// This API call retrieves whether a store implementation for +// a given catalog is available. +// Parameters +// theInstance [in] This pointer is created by the d2gCreateInstance. +// theCatalog [in] This is pointer created by the d2gCreateCatalog. +// callback [in] This is the pointer to developer’s callback function. +// userData [in/out] a void pointer to user-defined data. +// Return Value +// GSResult – Enum indicating success (GS_SUCCESS) or a failure code. +// +GSResult d2gGetStoreAvailability(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + D2GGetStoreAvailabilityCallback callback, + void *userData) +{ + D2GIInstance *d2gInstance = (D2GIInstance *)theInstance; + D2GICatalog *d2gCatalog = (D2GICatalog *)theCatalog; + GSResult result; + + GS_ASSERT(d2gInstance); + if (!d2gInstance) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called with NULL instance\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + // verify availability check + if (GSIGetAvailable()!=GSIACAvailable) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called without doing availability check", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_NoAvailabilityCheck); + } + + // A callback should be provided. + GS_ASSERT(callback); + if (!callback) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): developer callback is NULL!\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + result = d2giServiceGetStoreAvail(d2gInstance, d2gCatalog, callback, userData); + if (GS_SUCCEEDED(result)) + return GS_SUCCESS; // this strips extended success information + else + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//d2gLoadExtraCatalogInfo +// Brief +// This API function retrieves a list of ExtraInfo items that is particular +// to the game from the backend services. +// Parameters +// theInstance [in] This pointer is created by the d2gCreateInstance. +// theCatalog [in] This is pointer created by the d2gCreateCatalog. +// callback [in] This is the pointer to developer’s callback function. +// userData [in/out] a void pointer to user-defined data. +// Return Value +// GSResult – Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// This function is optional if no ExtraInfo exist for a game. If ExtraInfo +// exist, this function should be called during the SDK initialization. +// +GSResult d2gLoadExtraCatalogInfo(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + D2GLoadExtraCatalogInfoCallback callback, + void *userData) + +{ + D2GIInstance *d2gInstance = (D2GIInstance *)theInstance; + D2GICatalog *d2gCatalog = (D2GICatalog*)theCatalog; + GSResult result; + + GS_ASSERT(d2gInstance); + if (!d2gInstance) + { + gsDebugFormat(GSIDebugCat_Direct2Game,GSIDebugType_State,GSIDebugLevel_WarmError, + "%s :: %s (%d): called with NULL instance\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + // verify availability check + if (GSIGetAvailable()!=GSIACAvailable) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called without doing availability check", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_NoAvailabilityCheck); + } + + // A callback must be provided + GS_ASSERT(callback); + if (!callback) + { + gsDebugFormat( GSIDebugCat_Direct2Game,GSIDebugType_State,GSIDebugLevel_HotError, + "%s :: %s (%d): developer callback is NULL!\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + result = d2giServiceLoadExtraInfo(d2gInstance, d2gCatalog, callback, userData); + if (GS_SUCCEEDED(result)) + return GS_SUCCESS; // this strips extended success information + else + return result; + + GSI_UNUSED(userData); + GSI_UNUSED(callback); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2gLoadCatalogItems +// Brief +// This API call retrieves the items of a catalog from the backend. +// Parameters +// theInstance [in] This pointer is created by the d2gCreateInstance. +// theCatalog [in] This is pointer created by the d2gCreateCatalog. +// callback [in] This is the pointer to developer’s callback function. +// userData [in/out] a void pointer to user-defined data. +// Return Value +// GSResult – Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// When this request is completed successfully the D2G cache is populated +// with the items retrieved from the Catalog. +// +GSResult d2gLoadCatalogItems(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + D2GLoadCatalogItemsCallback callback, + void *userData) +{ + GSResult result = GS_SUCCESS; + D2GIInstance *d2gInstance = (D2GIInstance*)theInstance; + D2GICatalog *d2gCatalog = (D2GICatalog*) theCatalog; + + GS_ASSERT(d2gInstance); + GS_ASSERT(d2gCatalog); + if (!d2gInstance || !d2gCatalog) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called with NULL instance\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + // verify availability check + if (GSIGetAvailable()!=GSIACAvailable) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called without doing availability check", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_NoAvailabilityCheck); + } + + // A callback must be provided + GS_ASSERT(callback); + if (!callback) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): developer callback is NULL!\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + if (d2gCatalog->mAllItemsReceived) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_Debug, + "%s :: %s (%d): getting data from cache.\n", + __FILE__, __FUNCTION__, __LINE__); + result = d2giGetAllItemsFromCache(d2gCatalog, callback, userData); + } + else + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_Debug, + "%s :: %s (%d): data not in cache, getting data from service.\n", + __FILE__, __FUNCTION__, __LINE__); + // do the web service call + result = d2giServiceLoadCatalogItems(d2gInstance, d2gCatalog, callback, userData); + } + + if (GS_SUCCEEDED(result)) + return GS_SUCCESS; + else + return result; + + GSI_UNUSED(userData); + GSI_UNUSED(callback); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2gLoadCatalogItemsByCategory +// Brief +// This function retrieves a list of the items which are in a specific +// category. +// Parameters +// theInstance [in] This pointer is created by the d2gCreateInstance. +// theCatalog [in] This is pointer created by the d2gCreateCatalog. +// callback [in] This is the pointer to developer’s callback function. +// userData [in/out] a void pointer to user-defined data. +// Return Value +// GSResult – Enum indicating success (GS_SUCCESS) or a failure code. +// +GSResult d2gLoadCatalogItemsByCategory(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + const UCS2String theCategory, + D2GLoadCatalogItemsByCategoryCallback callback, + void *userData) +{ + D2GIInstance *d2gInstance = (D2GIInstance*)theInstance; + D2GICatalog *d2gCatalog = (D2GICatalog*)theCatalog; + + GSResult result; + GS_ASSERT(d2gInstance); + GS_ASSERT(d2gCatalog); + if (!d2gInstance || !d2gCatalog) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called with NULL instance or catalog\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + // verify availability check + if (GSIGetAvailable()!=GSIACAvailable) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called without doing availability check", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_NoAvailabilityCheck); + } + + // They should provide a callback from the beginning + GS_ASSERT(callback); + if (!callback) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): developer callback is NULL!\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + + if (d2gCatalog->mAllItemsReceived) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_Debug, + "%s :: %s (%d): getting data from cache.\n", + __FILE__, __FUNCTION__, __LINE__); + // Catalog is already in the cache + result = d2giGetCatalogItemsByCategoryFromCache(d2gCatalog, + theCategory, + callback, + userData); + } + else + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_Debug, + "%s :: %s (%d): data not in cache, getting data from service.\n", + __FILE__, __FUNCTION__, __LINE__); + // do the web service call + result = d2giServiceGetItemsByCategory(d2gInstance, + d2gCatalog, + theCategory, + callback, + userData); + } + + if (GS_SUCCEEDED(result)) + return GS_SUCCESS; // this strips extended success information + else + return result; + + GSI_UNUSED(userData); + GSI_UNUSED(callback); +} + +//////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2gGetUserCreditCards +// Brief +// This API call retrieves the user’s credit cards from the backend. +// D2G needs the user account information prior to user’s purchases. . +// Parameters +// theInstance [in] This pointer is created by the d2gCreateInstance. +// theCatalog [in] This is pointer created by the d2gCreateCatalog. +// validOnly [in] A boolean to indicate if the request is for all or +// only valid credit cards. +// callback [in] This is the pointer to developer’s callback function. +// userData [in/out] a void pointer to user-defined data. +// Return Value +// GSResult – Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// This function should be called before calling d2gGetOrderTotal +// since it needs the account information. +// +GSResult d2gGetUserCreditCards(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + gsi_bool validOnly, + D2GGetUserCreditCardsCallback callback, + void * userData) +{ + D2GIInstance *d2gInstance = (D2GIInstance *)theInstance; + D2GICatalog *d2gCatalog = (D2GICatalog*)theCatalog; + GSResult result; + + GS_ASSERT(d2gInstance); + if (!d2gInstance) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called with NULL instance\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + // verify availability check + if (GSIGetAvailable()!=GSIACAvailable) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called without doing availability check", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_NoAvailabilityCheck); + } + + // They should provide a callback from the beginning + GS_ASSERT(callback); + if (!callback) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): developer callback is NULL!\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + result = d2giServiceGetUserCreditCards(d2gInstance, d2gCatalog, validOnly, callback, userData); + if (GS_SUCCEEDED(result)) + return GS_SUCCESS; // this strips extended success information + else + return result; + + GSI_UNUSED(userData); + GSI_UNUSED(callback); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2gGetOrderTotal +// Brief +// This API function is the first step in the ordering process. +// Parameters +// theInstance [in] This is the pointer created by the d2gCreateInstance. +// theCatalog [in] This is the pointer created by the d2gCreateCatalog. +// accountId [in] accountId is retrieved with the d2gGetUserCreditCards +// request from the back-end. +// cultureCode [in] The culture code in which this order will be processed. +// currencyCode [in] The currency in which this order will be processed. +// itemIds [in] Pointer to the list of itemIds selected from the catalog. +// itemCount [in] The number items in the itemIds list. +// itemQuantities [in] The pointer to the corresponding list of item quantities +// for each item in the itemIds list. +// callback [in] This is the pointer to developer’s callback function. +// userData [in/out] a void pointer to user-defined data. +// Return Value +// GSResult – Enumeration indicating success (GS_SUCCESS) or a failure code. +// Remarks +// This function call is the first step to make purchase. It simply builds +// the shopping cart information for the game. A list of items selected from +// the Catalog along with their quantities passed as input. Then, this function +// will send an Order Request to back-end to retrieve the OrderTotal. +// +GSResult d2gGetOrderTotal( D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + gsi_u32 accountId, + D2GItemId *itemIds, + gsi_u32 itemCount, + gsi_u32 *itemQuantities, + D2GGetOrderTotalCallback callback, + void *userData) +{ + D2GIInstance *d2gInstance = (D2GIInstance *)theInstance; + D2GICatalog *d2gCatalog = (D2GICatalog*)theCatalog; + GSResult result; + + GS_ASSERT(d2gInstance); + if (!d2gInstance) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called with NULL instance\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + // verify availability check + if (GSIGetAvailable()!=GSIACAvailable) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called without doing availability check", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_NoAvailabilityCheck); + } + + GS_ASSERT(accountId); + if (accountId == 0) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): accountId is zero!\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + GS_ASSERT(itemIds); + if (itemIds == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): itemIds is NULL!\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + GS_ASSERT(itemCount); + if (itemCount == 0) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): itemIdsCount is 0!\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + GS_ASSERT(itemQuantities); + if (itemQuantities == 0) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): itemQuantities is 0!\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + result = d2giCheckItemsQuantities(d2gInstance, itemQuantities, itemCount); + if (GS_FAILED(result)) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): one of the item quantities is zero!\n", + __FILE__, __FUNCTION__, __LINE__); + return result; + } + + // A callback must be provided + GS_ASSERT(callback); + if (!callback) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): developer callback is NULL!\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + result = d2giServiceGetOrderTotal( d2gInstance, + d2gCatalog, + accountId, + itemIds, + itemCount, + itemQuantities, + callback, + userData); + if (GS_SUCCEEDED(result)) + return GS_SUCCESS; // this strips extended success information + else + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//d2gStartOrder +// Brief +// This API call is for starting the purchase process. It needs the OrderTotal +// obtained with the d2gGetOrderTotal request. +// Parameters +// theInstance [in] This is the pointer created by the d2gCreateInstance. +// theCatalog [in] This is the pointer created by the d2gCreateCatalog. +// orderTotal [in] This is received from the backend in the response +// to d2gGetOrderTotal request. +// callback [in] This is the pointer to developer’s callback function. +// userData [in/out] a void pointer to user-defined data. +// Return Value +// GSResult – Enumeration indicating success (GS_SUCCESS) or a failure code. +// +GSResult d2gStartOrder(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + D2GOrderTotal *orderTotal, + D2GStartOrderCallback callback, + void *userData) +{ + D2GIInstance *d2gInstance = (D2GIInstance *)theInstance; + D2GICatalog *d2gCatalog = (D2GICatalog*)theCatalog; + + GSResult result; + + GS_ASSERT(d2gInstance); + if (!d2gInstance) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called with NULL instance\n"); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + // verify availability check + if (GSIGetAvailable()!=GSIACAvailable) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called without doing availability check", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_NoAvailabilityCheck); + } + + GS_ASSERT(orderTotal->mOrder.mValidation.mIsValid); + if (!orderTotal->mOrder.mValidation.mIsValid) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): pending order was not valid, need to retry getordertotal!\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + // A callback must be provided + GS_ASSERT(callback); + if (!callback) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): developer callback is NULL!\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + result = d2giServicePurchaseItems(d2gInstance, + d2gCatalog, + orderTotal, + callback, + userData); + + if (GS_SUCCEEDED(result)) + return GS_SUCCESS; // this strips extended success information + else + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//d2gIsOrderComplete +// Brief +// This API call is for retrieving the order status from the backend. +// The order status is returned by the callback when the request completes. +// Parameters +// theInstance [in] This is the pointer created by the d2gCreateInstance. +// theCatalog [in] This is the pointer created by the d2gCreateCatalog. +// orderPurchase [in] This is received from the backend in the response +// to the d2gStartOrder request. +// callback [in] This is the pointer to developer’s callback function. +// userData [in/out] a void pointer to user-defined data. +// Return Value GSResult – Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// This function should be called by the game periodically to poll after +// d2gStartOrder request completes successfully to retrieve the order status. +// +GSResult d2gIsOrderComplete(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + D2GOrderPurchase *orderPurchase, + D2GIsOrderCompleteCallback callback, + void *userData) +{ + D2GIInstance *d2gInstance = (D2GIInstance *)theInstance; + D2GICatalog *d2gCatalog = (D2GICatalog *)theCatalog; + GSResult result; + + GS_ASSERT(d2gInstance); + if (!d2gInstance) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called with NULL instance\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + // verify availability check + if (GSIGetAvailable()!=GSIACAvailable) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called without doing availability check", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_NoAvailabilityCheck); + } + + GS_ASSERT(orderPurchase->mOrder.mValidation.mIsValid); + if (!orderPurchase->mOrder.mValidation.mIsValid) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): pending order was not valid, need to retry getordertotal!\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + // They should provide a callback from the beginning + GS_ASSERT(callback); + if (!callback) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): developer callback is NULL!\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + result = d2giServiceIsOrderComplete(d2gInstance, + d2gCatalog, + orderPurchase, + callback, + userData); + if (GS_SUCCEEDED(result)) + return GS_SUCCESS; // this strips extended success information + else + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2gGetPurchaseHistory +// Brief +// This API call retrieves the user’s purchase history from the backend +// service for the purchases made related to the game. +// Parameters +// theInstance [in] This is the pointer created by the d2gCreateInstance. +// theCatalog [in] This is the pointer created by the d2gCreateCatalog. +// callback [in] This is the pointer to developer’s callback function. +// userData [in/out] a void pointer to user-defined data. +// Return Value +// GSResult – Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// The callback returns the list of items purchased for this game. +// +GSResult d2gGetPurchaseHistory(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + D2GGetPurchaseHistoryCallback callback, + void *userData) + +{ + D2GIInstance *d2gInstance = (D2GIInstance *)theInstance; + D2GICatalog *d2gCatalog = (D2GICatalog*)theCatalog; + GSResult result; + + GS_ASSERT(d2gInstance); + if (!d2gInstance) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called with NULL instance\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + // verify availability check + if (GSIGetAvailable()!=GSIACAvailable) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called without doing availability check", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_NoAvailabilityCheck); + } + + // They should provide a callback from the beginning + GS_ASSERT(callback); + if (!callback) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): developer callback is NULL!\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + result = d2giServiceGetPurchaseHistory(d2gInstance, d2gCatalog, callback, userData); + if (GS_SUCCEEDED(result)) + return GS_SUCCESS; // this strips extended success information + else + return result; + + GSI_UNUSED(userData); + GSI_UNUSED(callback); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//d2gStartDownloadFileById +// Brief +// This function is called to download a purchased file. The download +// information is part of the response to d2gGetPurchaseHistory and +// d2gIsOrderComplete requests. +// Parameters +// theInstance [in] This is the pointer created by the d2gCreateInstance. +// theCatalog [in] This is the pointer created by the d2gCreateCatalog. +// fileId [in] The file id to be downloaded. +// downloadLocation [in] The directory path where the downloaded file +// to be saved. The download directory must exist. +// progressCallback [in] This is the pointer to developer’s progress callback. +// completedCallback[in] This is the pointer to developer’s completed callback. +// userData [in/out] a void pointer to user-defined data. +// Return Value +// GSResult – Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// After the purchase completed, the developer can download the files from +// the download list retrieved either with d2gIsOrderComplete request or +// d2gGetPurchaseHistory request. +// +GSResult d2gStartDownloadFileById( D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + D2GFileId fileId, + const gsi_char *downloadLocation, + D2GDownloadProgressCallback progressCallback, + D2GDownloadCompleteCallback completedCallback, + void *userData) +{ + D2GIInstance *d2gInstance = (D2GIInstance *)theInstance; + D2GICatalog *d2gCatalog = (D2GICatalog *)theCatalog; + GSResult result = GS_SUCCESS;; + + GS_ASSERT(d2gInstance); + if (!d2gInstance) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called with NULL instance, please create and initialize.\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + // verify availability check + if (GSIGetAvailable()!=GSIACAvailable) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called without doing availability check", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_NoAvailabilityCheck); + } + + // The callbacks must be provided + GS_ASSERT(progressCallback && completedCallback); + if (!progressCallback || !completedCallback) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): developer callbacks are NULL!\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + result = d2giServiceGetDownloadInfo(d2gInstance, + d2gCatalog, + fileId, + downloadLocation, + progressCallback, + completedCallback, + userData); + + if (GS_SUCCEEDED(result)) + return GS_SUCCESS; // this strips extended success information + else + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//NOT SUPPORTED YET! DO NOT USE! +//d2gGetItemActivationData +// Brief +// This function retrieves the licensing information from the backend. +// if any of the licenses owned by the game expires. +// Parameters +// theInstance [in] This is the pointer created by the d2gCreateInstance. +// theCatalog [in] This is the pointer created by the d2gCreateCatalog. +// callback [in] This is the pointer to developer’s callback function. +// thePackageId[in] This is parameter is an input from the DRM functionality. +// userData [in/out] a void pointer to user-defined data. +// Return Value +// GSResult – Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// This is part of an upcoming DRM functionlity. Do not use yet! +// +GSResult d2gGetItemActivationData(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + D2GGetItemActivationDataCallback callback, + D2GPackageId thePackageId, + void *userData) +{ + D2GIInstance *d2gInstance = (D2GIInstance *)theInstance; + D2GICatalog *d2gCatalog = (D2GICatalog *)theCatalog; + GSResult result = GS_SUCCESS; + + GS_ASSERT(d2gInstance); + GS_ASSERT(d2gCatalog); + if (!d2gInstance || !d2gCatalog) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called with NULL instance\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + // verify availability check + if (GSIGetAvailable()!=GSIACAvailable) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called without doing availability check\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_NoAvailabilityCheck); + } + + // They should provide a callback from the beginning + GS_ASSERT(callback); + if (!callback) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): developer callback is NULL!\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + result = d2giServiceGetItemActivationData(d2gInstance, d2gCatalog, callback, thePackageId, userData); + if (GS_SUCCEEDED(result)) + return GS_SUCCESS; // this strips extended success information + else + return result; + + GSI_UNUSED(userData); + GSI_UNUSED(callback); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//d2gCloneCatalogItems +// Brief +// This function makes a shallow copy of the items from theSrcItemList +// to theDstItemList. +// Parameters +// theDstItemList [out] Pointer to the destination list. +// It is initialized by d2gCloneCatalogItems. +// theSrcItemList [in] Pointer to the source list. +// Return Value +// GSResult – Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// The reason why the shallow copy is used is that the actual catalog items +// are located in the D2G cache. If this function is called, its corresponding +// free function d2gFreeCatalogItems must be called to de-allocate the memory +// containing theDstItemList. See D2GLoadCatalogItemsCallback for when this is used. +// See Also +// d2gFreeCatalogItems +// +GSResult d2gCloneCatalogItemList(D2GCatalogItemList *dstList, + const D2GCatalogItemList *srcList) +{ + gsi_u32 i; + + GS_ASSERT(srcList); + GS_ASSERT(dstList); + + if (dstList == NULL || srcList == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_Warning, + "%s :: %s (%d): called with null itemList", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + // initialize the destination catalog items list + dstList->mCount = srcList->mCount; + if (dstList->mCount == 0) + dstList->mCatalogItems = NULL; + else + { + dstList->mCatalogItems = (D2GCatalogItem *)gsimalloc(sizeof(D2GCatalogItem) * srcList->mCount); + if (dstList->mCatalogItems == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_HotError, + "%s :: %s (%d): failed to allocate clone structure", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + memset(dstList->mCatalogItems, 0, sizeof(D2GCatalogItem) * srcList->mCount); + for (i = 0; i < srcList->mCount; i++) + { + //shallow copy + dstList->mCatalogItems[i] = srcList->mCatalogItems[i]; + } + } + + return GS_SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//d2gFreeCatalogItems +// Brief +// This function de-allocates the memory for theItemList and assigns NULL +// to theItemList. +// Parameters +// theItemList [in] Pointer to the list. +// Return Value +// GSResult – Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// This is the corresponding API function to d2gCloneCatalogItems +// to de-allocate and reset memory. +GSResult d2gFreeCatalogItemList(D2GCatalogItemList *theItemList) +{ + if (theItemList == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_Warning, + "%s :: %s (%d): called with null ItemList", + __FILE__, __FUNCTION__, __LINE__); + return GS_SUCCESS; + } + + // gsi_false - Only free the container for the list items + return d2giFreeCatalogItems(NULL, theItemList, GS_SUCCESS, gsi_false); +} + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//d2gAppendCatalogItems +// Brief +// This function allows the developer append the one list to another. +// The srcList is not modified. The dstList contains its original items +// as well as the all the items from the srcList. +// Parameters +// dstItemList [out] Pointer to the destination list the source list. +// srcItemList [in] Pointer to source list given by the caller. +// The contents of this list are not modified. +// Return Value +// GSResult – Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// See D2GLoadCatalogItemsCallback for when this may be used. +// +GSResult d2gAppendCatalogItemList(D2GCatalogItemList *dstList, + D2GCatalogItemList *srcList) +{ + GSResult result = GS_SUCCESS; + GS_ASSERT(srcList); + GS_ASSERT(dstList); + + if (dstList == NULL || srcList == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_Warning, + "%s :: %s (%d): called with null itemList", + __FILE__, __FUNCTION__, __LINE__); + result = GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + else + { + // sort the destination list according to item id first. + result = d2giSortCatalogItemsbyID(dstList, D2GSort_Ascending); + if (GS_SUCCEEDED(result)) + { + // initialize the destination catalog items list + if (srcList->mCount != 0) + { + // sort the source list now. + result = d2giSortCatalogItemsbyID(srcList, D2GSort_Ascending); + if (GS_SUCCEEDED(result)) + { + // allocate enough space for the merged lists. + gsi_u32 maxCount = dstList->mCount+srcList->mCount; + D2GCatalogItem *pDstList = NULL; + + pDstList = (D2GCatalogItem *)realloc( dstList->mCatalogItems, + (sizeof(D2GCatalogItem)*maxCount)); + if (pDstList != NULL) + { + gsi_u32 i,j = 0; + gsi_u32 dstListCountOriginal = dstList->mCount; + + dstList->mCatalogItems = pDstList; + memset((pDstList+dstList->mCount), 0 , sizeof(D2GCatalogItem)*srcList->mCount); + for ( i = 0 ; imCount ; i++) + { + while ((jmCatalogItems[i].mItem.mItemId < dstList->mCatalogItems[j].mItem.mItemId)) + { + j++; + } + + if (j mCatalogItems[i].mItem.mItemId != dstList->mCatalogItems[j].mItem.mItemId) + { + // We do not have a duplicate of this item. + // Add this item at the end. + memcpy(&dstList->mCatalogItems[dstList->mCount], &srcList->mCatalogItems[i], sizeof(D2GCatalogItem)); + dstList->mCount++; + + } + j++; // now move to the next item in the destination list + } + + } + // So now we have a merged list + if (maxCount > dstList->mCount ) + { + pDstList = (D2GCatalogItem *)realloc( dstList->mCatalogItems, + (sizeof(D2GCatalogItem)*dstList->mCount)); + dstList->mCatalogItems = pDstList; + + } + + } + else + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_HotError, + "%s :: %s (%d): Not enough space to append lists", + __FILE__, __FUNCTION__, __LINE__); + result = GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + } + } + else + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_HotError, + "%s :: %s (%d): Not enough space to append lists", + __FILE__, __FUNCTION__, __LINE__); + result = GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + } + } + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//d2gCloneOrderTotal +// Brief +// This function makes a deep copy of the items from sourceOrderTotal to +// destinationOrderTotal. +// Parameters +// destinationOrderTotal [out] Pointer to the destination order total. +// sourceOrderTotal [in] Pointer to source order total. The contents +// of this list are not modified. +// Return Value +// GSResult – Enum indicating success (GS_SUCCESS) or a failure code. +// Remarks +// This function is important, since in the D2GGetOrderTotalCallback function, +// the developer needs to retain the order total. The memory for the destination +// list is allocated by D2G. If this function is called, its corresponding free +// function d2gFreeOrderTotal must be called to de-allocate the memory containing +// destinationOrderTotal. +// +GSResult d2gCloneOrderTotal(D2GOrderTotal **destinationOrderTotal, + D2GOrderTotal *sourceOrderTotal) +{ + D2GOrderTotal *tempOrderTotal; + gsi_u32 i; + GS_ASSERT(sourceOrderTotal); + GS_ASSERT(destinationOrderTotal); + if (sourceOrderTotal == NULL || destinationOrderTotal == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_Warning, + "%s :: %s (%d): called with null sourceOrderTotal", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + tempOrderTotal = *destinationOrderTotal = (D2GOrderTotal *)gsimalloc(sizeof(D2GOrderTotal)); + + tempOrderTotal->mQuoteDate = sourceOrderTotal->mQuoteDate; + + tempOrderTotal->mOrder.mAccountId = sourceOrderTotal->mOrder.mAccountId; + GS_ASSERT(sourceOrderTotal->mOrder.mRootOrderGuid); + GS_ASSERT(sourceOrderTotal->mOrder.mSubTotal); + GS_ASSERT(sourceOrderTotal->mOrder.mTax); + GS_ASSERT(sourceOrderTotal->mOrder.mTotal); + GS_ASSERT(sourceOrderTotal->mOrder.mValidation.mIsValid || + (sourceOrderTotal->mOrder.mValidation.mIsValid == gsi_false && + sourceOrderTotal->mOrder.mValidation.mMessage)); + + if (sourceOrderTotal->mOrder.mRootOrderGuid == NULL || + sourceOrderTotal->mOrder.mSubTotal == NULL || + sourceOrderTotal->mOrder.mTax == NULL || + sourceOrderTotal->mOrder.mTotal == NULL || + sourceOrderTotal->mOrder.mGeoInfo.mCultureCode == NULL || + sourceOrderTotal->mOrder.mGeoInfo.mCurrencyCode == NULL || + (sourceOrderTotal->mOrder.mValidation.mIsValid == gsi_false && + sourceOrderTotal->mOrder.mValidation.mMessage == NULL)) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_Warning, + "%s :: %s (%d): called with null items in sourceOrderTotal.", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + tempOrderTotal->mOrder.mRootOrderGuid = goawstrdup(sourceOrderTotal->mOrder.mRootOrderGuid); + tempOrderTotal->mOrder.mSubTotal = goawstrdup(sourceOrderTotal->mOrder.mSubTotal); + tempOrderTotal->mOrder.mTax = goawstrdup(sourceOrderTotal->mOrder.mTax); + tempOrderTotal->mOrder.mTotal = goawstrdup(sourceOrderTotal->mOrder.mTotal); + tempOrderTotal->mOrder.mValidation.mIsValid = sourceOrderTotal->mOrder.mValidation.mIsValid; + tempOrderTotal->mOrder.mValidation.mMessage = goawstrdup(sourceOrderTotal->mOrder.mValidation.mMessage); + tempOrderTotal->mOrder.mValidation.mResult = sourceOrderTotal->mOrder.mValidation.mResult; + tempOrderTotal->mOrder.mGeoInfo.mCultureCode = goawstrdup(sourceOrderTotal->mOrder.mGeoInfo.mCultureCode); + tempOrderTotal->mOrder.mGeoInfo.mCurrencyCode = goawstrdup(sourceOrderTotal->mOrder.mGeoInfo.mCurrencyCode); + + // Initialize the Order Item List + GS_ASSERT(sourceOrderTotal->mOrderItemList.mCount > 0); + tempOrderTotal->mOrderItemList.mCount = sourceOrderTotal->mOrderItemList.mCount; + tempOrderTotal->mOrderItemList.mOrderItems = (D2GOrderItem *)gsimalloc(sizeof(D2GOrderItem) * tempOrderTotal->mOrderItemList.mCount); + memset(tempOrderTotal->mOrderItemList.mOrderItems, 0, sizeof(D2GOrderItem) * tempOrderTotal->mOrderItemList.mCount); + + // Now copy the Order Item List + for (i = 0; i < tempOrderTotal->mOrderItemList.mCount; i++) + { + d2giCloneOrderItem(&tempOrderTotal->mOrderItemList.mOrderItems[i], &sourceOrderTotal->mOrderItemList.mOrderItems[i]); + } + + return GS_SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//d2gFreeOrderTotal +// Brief +// This API function de-allocates and resets the memory pointed to by +// orderTotal. +// Parameters +// orderTotal [in] The pointer to the order total. +// Return Value +// None. +// Remarks +// This is the corresponding API function to d2gCloneOrderTotal. +// +void d2gFreeOrderTotal(D2GOrderTotal *orderTotal) +{ + d2giFreeOrderTotal(orderTotal); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//d2gCloneOrderPurchase +// Brief +// This function makes a deep copy of the items from sourceOrderPurchase to +// destOrderPurchase. +// Parameters +// destOrderPurchase [out] a list of pointers to the destination order purchases. +// sourceOrderTotal [in] Pointer to source order total. +// The contents of this list are not modified. +// Return Value +// GSResult – Enumeration indicating success (GS_SUCCESS) or a failure code. +// Remarks +// This function is important, since in the D2GStartOrderCallback function, the +// developer needs retain the order purchase. The D2G SDK allocates the memory +// space for the destination list. +// If this function is called, d2gFreeOrderPurchase must be called to de-allocate +// the memory space. +// See Also +// D2GStartOrderCallback for when this may be used. +// +GSResult d2gCloneOrderPurchase(D2GOrderPurchase **destOrderPurchase, + D2GOrderPurchase *sourceOrderPurchase) +{ + D2GOrderPurchase *anOrderPurchase; + GSResult result; + + GS_ASSERT(sourceOrderPurchase); + GS_ASSERT(destOrderPurchase); + if (sourceOrderPurchase == NULL || destOrderPurchase == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): called with null sourceOrderTotal", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + anOrderPurchase = *destOrderPurchase = (D2GOrderPurchase *)gsimalloc(sizeof(D2GOrderPurchase)); + + anOrderPurchase->mPurchaseDate = sourceOrderPurchase->mPurchaseDate; + + anOrderPurchase->mOrder.mAccountId = sourceOrderPurchase->mOrder.mAccountId; + GS_ASSERT(sourceOrderPurchase->mOrder.mRootOrderGuid); + GS_ASSERT(sourceOrderPurchase->mOrder.mSubTotal); + GS_ASSERT(sourceOrderPurchase->mOrder.mTax); + GS_ASSERT(sourceOrderPurchase->mOrder.mTotal); + GS_ASSERT(sourceOrderPurchase->mOrder.mValidation.mMessage); + GS_ASSERT(sourceOrderPurchase->mOrder.mGeoInfo.mCultureCode); + GS_ASSERT(sourceOrderPurchase->mOrder.mGeoInfo.mCurrencyCode); + + if (sourceOrderPurchase->mOrder.mRootOrderGuid == NULL || + sourceOrderPurchase->mOrder.mSubTotal == NULL || + sourceOrderPurchase->mOrder.mTax == NULL || + sourceOrderPurchase->mOrder.mTotal == NULL || + sourceOrderPurchase->mOrder.mValidation.mMessage == NULL || + sourceOrderPurchase->mOrder.mGeoInfo.mCultureCode == NULL || + sourceOrderPurchase->mOrder.mGeoInfo.mCurrencyCode == NULL ) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_Warning, + "%s :: %s (%d): called with null items in sourceOrderPurchase", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + anOrderPurchase->mOrder.mRootOrderGuid = goawstrdup(sourceOrderPurchase->mOrder.mRootOrderGuid); + anOrderPurchase->mOrder.mSubTotal = goawstrdup(sourceOrderPurchase->mOrder.mSubTotal); + anOrderPurchase->mOrder.mTax = goawstrdup(sourceOrderPurchase->mOrder.mTax); + anOrderPurchase->mOrder.mTotal = goawstrdup(sourceOrderPurchase->mOrder.mTotal); + + anOrderPurchase->mOrder.mGeoInfo.mCultureCode = goawstrdup(sourceOrderPurchase->mOrder.mGeoInfo.mCultureCode); + anOrderPurchase->mOrder.mGeoInfo.mCurrencyCode = goawstrdup(sourceOrderPurchase->mOrder.mGeoInfo.mCurrencyCode); + + anOrderPurchase->mOrder.mValidation.mIsValid = sourceOrderPurchase->mOrder.mValidation.mIsValid; + anOrderPurchase->mOrder.mValidation.mMessage = goawstrdup(sourceOrderPurchase->mOrder.mValidation.mMessage); + anOrderPurchase->mOrder.mValidation.mResult = sourceOrderPurchase->mOrder.mValidation.mResult; + + GS_ASSERT(sourceOrderPurchase->mItemPurchases.mCount > 0); + anOrderPurchase->mItemPurchases.mCount = sourceOrderPurchase->mItemPurchases.mCount; + + result = d2giCloneOrderItemPurchases(&anOrderPurchase->mItemPurchases.mOrderItemPurchases, + &anOrderPurchase->mItemPurchases.mCount, + sourceOrderPurchase->mItemPurchases.mOrderItemPurchases, + sourceOrderPurchase->mItemPurchases.mCount); + if (GS_FAILED(result)) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): Failed to copy subitem list for item using d2giCloneOrderItemPurchases.\n", + __FILE__, __FUNCTION__, __LINE__); + return result; + } + + return GS_SUCCESS; +} +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//d2gFreeOrderPurchase +// Brief +// This API function de-allocates and resets the memory pointed to +// by orderPurchase. +// Parameters +// orderPurchase : [in] Pointer to the memory to be released. +// Return Value +// None. +// Remarks +// This is the corresponding API call to d2gCloneOrderPurchase . +// +void d2gFreeOrderPurchase(D2GOrderPurchase *orderPurchase) +{ + d2giFreeOrderPurchase(orderPurchase); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// d2gFreePuchaseHistory +// Brief +// This API function de-allocates and resets the memory pointed to +// by purchaseHistory. +// Parameters +// purchaseHistory : [in] Pointer to the memory to be released. +// Return Value +// None. +// Remarks +// Purchase histories should be freed explicitly because callbacks give +// pointers to the objects but does not free them. +// +void d2gFreePurchaseHistory(D2GPurchaseHistory *purchaseHistory) +{ + d2giFreePurchaseHistory(purchaseHistory); +} + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// d2gFreeDownloadItemList +// Brief +// This API function de-allocates and resets the memory pointed to +// by downloadList. +// Parameters +// contentUpdateList : [in] Pointer to the memory to be released. +// Return Value +// None. +// Remarks +// content update lists should be freed explicitly because callbacks give +// pointers to the objects but does not free them. The function will +// free internal data and the list. Developers can set the pointer to +// NULL afterwards. +void d2gFreeContentUpdateList(D2GContentUpdateList *contentUpdateList) +{ + d2giFreeContentUpdateList(contentUpdateList); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// d2gFreeExtraInfoList +// Brief +// This API function de-allocates and resets the memory pointed to +// by downloadList. +// Parameters +// extraInfoList : [in] Pointer to the memory to be released. +// Return Value +// None. +// Remarks +// Download lists should be freed explicitly because callbacks give +// pointers to the objects but does not free them. The function will +// free internal data and the list. Developers can set the pointer to +// NULL afterwards. +void d2gFreeExtraInfoList(D2GExtraInfoList *extraInfoList) +{ + d2giFreeExtraInfoList(NULL, extraInfoList, GS_SUCCESS, gsi_false); +} + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// d2gFreeCreditCardInfoList +// Brief +// This API function de-allocates and resets the memory pointed to +// by creditCardList. +// Parameters +// creditCardList : [in] Pointer to the memory to be released. +// Return Value +// None. +// Remarks +// Credit card lists should be freed explicitly because callbacks give +// pointers to the objects but does not free them. The function will +// free internal data and the list. Developers can set the pointer to +// NULL afterwards. +void d2gFreeCreditCardInfoList(D2GCreditCardInfoList *creditCardList) +{ + d2giFreeCreditCardInfoList(creditCardList); +} + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//d2gSortCatalogItemsbyPrice +// Brief +// This function sorts a list of catalog items onto itself, according to +// the price of the items either in ascending or in descending order. +// Direction of the order is specified as an input parameter. +// Parameters +// list [in] pointer to the list of catalog items to be sorted. +// direction [in] an enumerated type for the sort direction; ascending or descending. +// Return Value +// GSResult – Enumeration indicating success (GS_SUCCESS) or a failure code. +// +GSResult d2gSortCatalogItemsbyPrice(D2GCatalogItemList *list, D2GSortDirection direction ) +{ + if ((list == NULL) || (list->mCount <=0)) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_Warning, + "%s :: %s (%d): null or empty Catalog Item List", + __FILE__, __FUNCTION__, __LINE__); + return GS_SUCCESS; // Nothing to sort return success here + } + return d2giSortCatalogItemsbyPrice(list, direction) ; + +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//d2gSortCatalogItemsbyName +// Brief +// This function sorts a list of catalog items onto itself, according to +// the item names alphabetically either in ascending or in descending order. +// Direction of the order is specified as an input parameter. +// Parameters +// list [in] pointer to the list of catalog items to be sorted. +// direction [in] an enumerated type for the sort direction; +// ascending or descending. +// Return Value +// GSResult – Enumeration indicating success (GS_SUCCESS) or a failure code. +// +GSResult d2gSortCatalogItemsbyName(D2GCatalogItemList *list, D2GSortDirection direction) +{ + if ((list == NULL) || (list->mCount <=0)) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_Warning, + "%s :: %s (%d): null or empty Catalog Item List", + __FILE__, __FUNCTION__, __LINE__); + return GS_SUCCESS; // Nothing to sort return success here + } + return d2giSortCatalogItemsbyName(list, direction); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//d2gSortCatalogItemsbyReleaseDate +// Brief +// This function sorts a list of catalog items onto itself, according to +// the release date of the items either in ascending or in descending order. +// Direction of the order is specified as an input parameter. +// Parameters +// list [in] pointer to the list of catalog items to be sorted. +// direction [in] an enumerated type for the sort direction; ascending or descending. +// Return Value +// GSResult – Enumeration indicating success (GS_SUCCESS) or a failure code. +// +GSResult d2gSortCatalogItemsbyReleaseDate(D2GCatalogItemList *list, D2GSortDirection direction ) +{ + if ((list == NULL) || (list->mCount <=0)) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_Warning, + "%s :: %s (%d): null or empty Catalog Item List", + __FILE__, __FUNCTION__, __LINE__); + return GS_SUCCESS; // Nothing to sort return success here + } + return d2giSortCatalogItemsbyDate(list, direction); + +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//d2gSortCatalogItemsbyItemId +// Brief +// This function sorts a list of catalog items onto itself, according to +// the release date of the items either in ascending or in descending order. +// Direction of the order is specified as an input parameter. +// Parameters +// list [in] pointer to the list of catalog items to be sorted. +// direction [in] an enumerated type for the sort direction; ascending or descending. +// Return Value +// GSResult – Enumeration indicating success (GS_SUCCESS) or a failure code. +// +GSResult d2gSortCatalogItemsbyItemId(D2GCatalogItemList *list, D2GSortDirection direction ) +{ + if ((list == NULL) || (list->mCount <=0)) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_Warning, + "%s :: %s (%d): null or empty Catalog Item List", + __FILE__, __FUNCTION__, __LINE__); + return GS_SUCCESS; // Nothing to sort return success here + } + return d2giSortCatalogItemsbyID(list, direction); + +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//d2gSortCatalogItemsbyExternalId +// Brief +// This function sorts a list of catalog items onto itself, according to +// the release date of the items either in ascending or in descending order. +// Direction of the order is specified as an input parameter. +// Parameters +// list [in] pointer to the list of catalog items to be sorted. +// direction [in] an enumerated type for the sort direction; ascending or descending. +// Return Value +// GSResult – Enumeration indicating success (GS_SUCCESS) or a failure code. +// +GSResult d2gSortCatalogItemsbyExternalId(D2GCatalogItemList *list, D2GSortDirection direction ) +{ + if ((list == NULL) || (list->mCount <=0)) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_Warning, + "%s :: %s (%d): null or empty Catalog Item List", + __FILE__, __FUNCTION__, __LINE__); + return GS_SUCCESS; // Nothing to sort return success here + } + return d2giSortCatalogItemsbyExtId(list, direction); + +} +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +//d2gSortCatalogItemsbySize +// Brief +// This function sorts a list of catalog items onto itself, according to +// the release date of the items either in ascending or in descending order. +// Direction of the order is specified as an input parameter. +// Parameters +// list [in] pointer to the list of catalog items to be sorted. +// direction [in] an enumerated type for the sort direction; ascending or descending. +// Return Value +// GSResult – Enumeration indicating success (GS_SUCCESS) or a failure code. +// +GSResult d2gSortCatalogItemsbySize(D2GCatalogItemList *list, D2GSortDirection direction ) +{ + if ((list == NULL) || (list->mCount <=0)) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_Warning, + "%s :: %s (%d): null or empty Catalog Item List", + __FILE__, __FUNCTION__, __LINE__); + return GS_SUCCESS; // Nothing to sort return success here + } + return d2giSortCatalogItemsbySize(list, direction); + +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2gGetCategories +// Brief +// This is a helper function designed to retrieve the list of categories +// from the catalog. This function is under development. +// Parameters +// theInstance [in] This is the pointer created by the d2gCreateInstance. +// theCatalog [in] This is the pointer created by the d2gCreateCatalog. +// categoryList[out]a pointer to the category list for the categories found. +// D2G SDK allocates the memory for the list. +// Return Value +// GSResult – Enumeration indicating success (GS_SUCCESS) or a failure code. +// Remarks +// This helper function helps you render your category based UI if for example +// each category were to be assigned to a button in your store navigation. +// +// IMPORTANT: +// This function does a shallow copy of the categoryName from the cache. +// When releasing the memory for the categoryList->categoryName, +// use gsifree(categoryList->categoryName). +// +// +GSResult d2gGetCategories(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + D2GCategoryList *categoryList) +{ + D2GIInstance *d2gInstance = (D2GIInstance *)theInstance; + D2GICatalog *d2gCatalog = (D2GICatalog *) theCatalog; + GSResult result = GSResultCode_Success; + + // initialize the categoryList + categoryList->mCount = 0; + categoryList->mCategoryNames = NULL; + + GS_ASSERT(d2gInstance); + GS_ASSERT(d2gCatalog); + if (!d2gInstance || !d2gCatalog) + { + + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called with NULL instance or catalog\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + // verify availability check + if (GSIGetAvailable()!=GSIACAvailable) + { + + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called without doing availability check", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_NoAvailabilityCheck); + } + + // Catalog is already in the cache + result = d2giGetCategoriesFromCache(d2gCatalog, + categoryList); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2gGetExtraCatalogInfo +// Brief +// This is a lookup function for a pair which is locally +// available in the D2G cache. +// Parameters +// theInstance [in] This is the pointer created by the d2gCreateInstance. +// theCatalog [in] This is the pointer created by the d2gCreateCatalog. +// extraInfoKey [in] The key for the value to be retrieved. +// aValue [in] Pointer to the value in the D2G cache. +// Return Value +// GSResult – Enumeration indicating success (GS_SUCCESS) or a failure code. +// Remarks +// See d2gLoadExtraCatalogInfo, for more information to initialize the D2G cache +// with the list of ExtraInfo items. Since a pointer to the cached value +// is returned, the developer should never free aValue pointer. +// +GSResult d2gGetExtraCatalogInfo (D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + const UCS2String extraInfoKey, + UCS2String *extraInfoValue) +{ + GSResult result = GS_SUCCESS; + D2GIInstance *d2gInstance = (D2GIInstance *)theInstance; + D2GICatalog *d2gCatalog = (D2GICatalog*)theCatalog; + + GS_ASSERT(d2gInstance); + GS_ASSERT(d2gCatalog); + GS_ASSERT(extraInfoKey); + + if (!d2gInstance || + !d2gCatalog || + !extraInfoKey) + { + GS_D2G_LOG(GSIDebugType_State, GSIDebugLevel_WarmError, + "Called with NULL instance or NULL extraInfoKey."); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + // verify availability check + if (GSIGetAvailable()!=GSIACAvailable) + { + GS_D2G_LOG(GSIDebugType_State, GSIDebugLevel_WarmError, "Called without doing availability check."); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_NoAvailabilityCheck); + } + + *extraInfoValue = d2giLookUpExtraInfo(theInstance, theCatalog, extraInfoKey); + + if (!*extraInfoValue) + { + GS_D2G_LOG(GSIDebugType_State, GSIDebugLevel_WarmError, "Key does not exist in the cache."); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + return result; +}; + +// d2gGetExtraItemInfoByKey +// Summary +// Looks up the extra item info value for a given D2GCatalogItem and its +// extra info key +// Parameters +// theInstance : [in] Pointer created by the d2gCreateInstance. +// theCatalogItem : [in] pointer to item being checked. +// extraInfoKey : [in] Extra Info key for the value to be retrieved. +// aValue : [out] Pointer to value that is being looked up. +// Return Value +// GSResult – Integer built with enums indicating success (GS_SUCCESS) +// or a failure code. +// Remarks +// The catalog item being passed in must be valid otherwise the lookup will +// not occur. If the key doesn't exist in the extra info list for the item +// passed in, extraInfoValue will be NULL. Valid extraInfoValue will be a copy +// of the data value given the key. It is the developer's responsibility to +// free the memory pointed to by extraInfoValue. +GSResult d2gGetExtraItemInfoKeyValueByKeyName(const D2GCatalogItem *theCatalogItem, + const UCS2String extraInfoKey, + UCS2String *extraInfoValue) +{ + gsi_u32 i; + + GS_ASSERT(theCatalogItem); + GS_ASSERT(extraInfoKey); + + if (theCatalogItem == NULL || extraInfoKey == NULL || extraInfoValue == NULL) + { + GS_D2G_LOG(GSIDebugType_State, GSIDebugLevel_WarmError, + "Called with NULL parameter(s). Please check parameters for any NULL values."); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + for (i = 0; i < theCatalogItem->mExtraItemInfoList.mCount; i++) + { + // grab the pointer to the key for the ith element + const UCS2String keyI = theCatalogItem->mExtraItemInfoList.mExtraInfoElements[i].mKey; + const UCS2String valueI = theCatalogItem->mExtraItemInfoList.mExtraInfoElements[i].mValue; + if (wcscmp(keyI, extraInfoKey) == 0 && valueI != NULL) + { + *extraInfoValue = goawstrdup(valueI); + return GS_SUCCESS; + } + } + + *extraInfoValue = NULL; + GS_D2G_LOG(GSIDebugType_State, GSIDebugLevel_WarmError, "Key %s not found", extraInfoKey); + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_ExtraInfo_KeyNotFound); +} + +// d2gFilterCatalogItemListByKeyName +// Summary +// Creates a subset of a given D2GCatalogItemList based on the +// key name passed in. +// Parameters +// incomingCatalogItems : [in] Pointer to D2GCatalogItemList being scanned. +// outgoingCatalogItems : [out] Pointer to Pointer of the D2GCatalogItemList being returned. +// extraInfoKey : [in] Extra Info Key name used for scanning items. +// Return Value +// GSResult – Integer built with enums indicating success (GS_SUCCESS) or a failure code. +// Remarks +// The incomingCatalogItems and extraInfoKey being passed in must be valid otherwise the scan will +// not occur. outgoingCatalogItems will be allocated to store the filtered item list. If the key +// doesn't exist in the extra info list for the list of items being passed in, then +// outgoingCatalogItems is set to NULL to represent an empty list. +GSResult d2gFilterCatalogItemListByKeyName(const D2GCatalogItemList *incomingCatalogItems, + D2GCatalogItemList **outgoingCatalogItems, + const UCS2String extraInfoKey) +{ + // create a temporary list of the items to include in the output + // so that we can allocate space appropriately on the outgoing list. + D2GCatalogItem **itemsChecked; + gsi_u32 foundItemsCount; + gsi_u32 i; + gsi_u32 j; + D2GCatalogItemList *outList = NULL; + + if (incomingCatalogItems == NULL || outgoingCatalogItems == NULL || extraInfoKey == NULL) + { + // we bail because we don't have anything to work with. + GS_D2G_LOG(GSIDebugType_State, GSIDebugLevel_WarmError, + "Called with NULL parameter(s). Please check parameters for any NULL values."); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + foundItemsCount = 0; + + itemsChecked = (D2GCatalogItem **)gsimalloc(sizeof(D2GCatalogItem *) * incomingCatalogItems->mCount); + if (itemsChecked == NULL) + { + GS_D2G_LOG(GSIDebugType_Memory, GSIDebugLevel_HotError, "Failed to allocate memory for: foundItems."); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + for (i = 0; i < incomingCatalogItems->mCount; i++) + { + D2GCatalogItem *anItem = &incomingCatalogItems->mCatalogItems[i]; + itemsChecked[i] = NULL; + if (anItem->mExtraItemInfoList.mCount > 0) + { + for (j = 0; j < anItem->mExtraItemInfoList.mCount; j++) + { + if (wcscmp(anItem->mExtraItemInfoList.mExtraInfoElements[j].mKey, extraInfoKey) == 0) + { + itemsChecked[i] = anItem; + foundItemsCount++; + break; + } + } + } + } + + if (foundItemsCount == 0) + { + *outgoingCatalogItems = NULL; + gsifree(itemsChecked); + return GS_SUCCESS; + } + + outList = (D2GCatalogItemList *)gsimalloc(sizeof(D2GCatalogItemList)); + if (outList == NULL) + { + gsifree(itemsChecked); + GS_D2G_LOG(GSIDebugType_Memory, GSIDebugLevel_HotError, "Failed to allocate memory for: outList."); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + outList->mCount = foundItemsCount; + outList->mCatalogItems = (D2GCatalogItem *)gsimalloc(sizeof(D2GCatalogItem) * outList->mCount); + if (outList->mCatalogItems == NULL) + { + gsifree(outList); + gsifree(itemsChecked); + GS_D2G_LOG(GSIDebugType_Memory, GSIDebugLevel_HotError, "Failed to allocate memory for: outList->mCatalogItems."); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + for(i = 0, j = 0; i < incomingCatalogItems->mCount && j < outList->mCount; i++) + { + if (itemsChecked[i]) + { + outList->mCatalogItems[j++] = *itemsChecked[i]; + } + } + + *outgoingCatalogItems = outList; + gsifree(itemsChecked); + + return GS_SUCCESS; +} + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// d2gFilterCatalogItemListByKeyValue +// Summary +// Creates a subset of a given D2GCatalogItemList based on the +// key name and key value passed in. +// Parameters +// incomingCatalogItems : [in] Pointer to D2GCatalogItemList being scanned. +// outgoingCatalogItems : [out] Pointer to Pointer of the D2GCatalogItemList being returned. +// extraInfoKey : [in] Extra Info Key name used for scanning items. +// extraInfoValue : [in] Extra Info Value name used for scanning items. +// Return Value +// GSResult – Integer built with enums indicating success (GS_SUCCESS) or a failure code. +// Remarks +// The incomingCatalogItems, extraInfoKey, extraInfoValue being passed in must be valid +// otherwise the scan will not occur. If the combination of the input key and value doesn't +// exist in the extra info list for the list of items being passed in, then +// outgoingCatalogItems is set to NULL to represent an empty list. +GSResult d2gFilterCatalogItemListByKeyNameValue(const D2GCatalogItemList *incomingCatalogItems, + D2GCatalogItemList **outgoingCatalogItems, + const UCS2String extraInfoKey, + const UCS2String extraInfoValue) +{ + // create a temporary list of the items to include in the output + // so that we can allocate space appropriately on the outgoing list. + D2GCatalogItem **itemsChecked; + gsi_u32 foundItemsCount; + gsi_u32 i; + gsi_u32 j; + D2GCatalogItemList *outList = NULL; + + if (incomingCatalogItems == NULL || outgoingCatalogItems == NULL || extraInfoKey == NULL || + extraInfoValue == NULL) + { + // we bail because we don't have anything to work with. + GS_D2G_LOG(GSIDebugType_State, GSIDebugLevel_WarmError, + "Called with NULL parameter(s). Please check parameters for any NULL values."); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + foundItemsCount = 0; + + itemsChecked = (D2GCatalogItem **)gsimalloc(sizeof(D2GCatalogItem *) * incomingCatalogItems->mCount); + if (itemsChecked == NULL) + { + GS_D2G_LOG(GSIDebugType_Memory, GSIDebugLevel_HotError, "Failed to allocate memory for: foundItems."); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + for (i = 0; i < incomingCatalogItems->mCount; i++) + { + D2GCatalogItem *anItem = &incomingCatalogItems->mCatalogItems[i]; + itemsChecked[i] = NULL; + if (anItem->mExtraItemInfoList.mCount > 0) + { + + for (j = 0; j < anItem->mExtraItemInfoList.mCount; j++) + { + if (wcscmp(anItem->mExtraItemInfoList.mExtraInfoElements[j].mKey, extraInfoKey) == 0 && + wcscmp(anItem->mExtraItemInfoList.mExtraInfoElements[j].mValue, extraInfoValue) == 0) + { + itemsChecked[i] = anItem; + foundItemsCount++; + break; + } + } + } + } + + if (foundItemsCount == 0) + { + *outgoingCatalogItems = NULL; + gsifree(itemsChecked); + return GS_SUCCESS; + } + + outList = (D2GCatalogItemList *)gsimalloc(sizeof(D2GCatalogItemList)); + if (outList == NULL) + { + gsifree(itemsChecked); + GS_D2G_LOG(GSIDebugType_Memory, GSIDebugLevel_HotError, "Failed to allocate memory for: outList."); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + outList->mCount = foundItemsCount; + outList->mCatalogItems = (D2GCatalogItem *)gsimalloc(sizeof(D2GCatalogItem) * outList->mCount); + if (outList->mCatalogItems == NULL) + { + gsifree(outList); + gsifree(itemsChecked); + GS_D2G_LOG(GSIDebugType_Memory, GSIDebugLevel_HotError, "Failed to allocate memory for: outList->mCatalogItems."); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + for(i = 0, j = 0; i < incomingCatalogItems->mCount && j < outList->mCount; i++) + { + if (itemsChecked[i]) + { + outList->mCatalogItems[j++] = *itemsChecked[i]; + } + } + + *outgoingCatalogItems = outList; + gsifree(itemsChecked); + + return GS_SUCCESS; +} + + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2gSetManifestFilePath +// Brief +// This function is set the path for the manifest file location. +// Parameters +// theInstance [in] This is the pointer created by the d2gCreateInstance. +// manifestFilePath [in] String which includes location for the manifest file. +// Return Value +// GSResult – Enumeration indicating success (GS_SUCCESS) or a failure code. +// Remarks +// This is an optional function. If Patching Features used, the manifest file path +// can be set here. If used, it should be called only once right after +// the d2gInitialize. The path should exist. +// If function is not called the manifest file is located in +// the current working directory by default. Since the manifest file +// keeps the installed contents this path should not change once it is set. +// +GSResult d2gSetManifestFilePath( D2GInstancePtr theInstance, + const char *manifestFilePath) +{ + GSResult result = GS_SUCCESS; + + GS_ASSERT(theInstance); + GS_ASSERT(manifestFilePath); + if ( !theInstance || !manifestFilePath) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State,GSIDebugLevel_WarmError, + "%s :: %s (%d): called with NULL instance, please create and initialize.\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + else + { + int failCode; + D2GIInstance *d2giInstance = (D2GIInstance *) theInstance; + d2giInstance->mManifestFilePath = goastrdup(manifestFilePath); + failCode = _mkdir(manifestFilePath); + if (failCode != EEXIST || failCode != 0) + { + gsifree(d2giInstance->mManifestFilePath); + d2giInstance->mManifestFilePath = NULL; + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_File,GSIDebugLevel_WarmError, + "%s :: %s (%d): Could not create the specified path for manifest file, path given: %s.\n", + __FILE__, __FUNCTION__, __LINE__, manifestFilePath); + return GS_D2G_ERROR(GSResultSection_File, GSResultCode_FileCannotCreateDirectory); + } + } + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2gCheckContentUpdates +// Brief +// This function invokes a service call to the backend to check any in-game +// content update +// Parameters +// theInstance [in] This is the pointer created by the d2gCreateInstance. +// theCatalog [in] This is the pointer created by the d2gCreateCatalog. +// requiredOnly[in] Is the update for required content only. +// callback [in] This is the pointer to developer’s callback function. +// userData [in/out] a void pointer to user-defined data. +// Return Value +// GSResult – Enumeration indicating success (GS_SUCCESS) or a failure code. +// Remarks +// +// +GSResult d2gCheckContentUpdates(D2GInstancePtr theInstance, + D2GCatalogPtr theCatalog, + gsi_bool requiredOnly, + D2GCheckContentUpdatesCallback callback, + void *userData) +{ + D2GIInstance * d2gInstance = (D2GIInstance *)theInstance; + D2GICatalog * d2gCatalog = (D2GICatalog*)theCatalog; + GSResult result; + + GS_ASSERT(d2gInstance); + GS_ASSERT(d2gCatalog); + if (!d2gInstance||!d2gCatalog) + + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State,GSIDebugLevel_WarmError, + "%s :: %s (%d): called with NULL instance, please create and initialize.\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + // verify availability check + if (GSIGetAvailable()!=GSIACAvailable) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State,GSIDebugLevel_WarmError, + "%s :: %s (%d): called without doing availability check", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_NoAvailabilityCheck); + } + + // They should provide a callback from the beginning + GS_ASSERT(callback); + if (!callback) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): developer callbacks are NULL!\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + result = d2giServiceCheckContentUpdates(d2gInstance, + d2gCatalog, + requiredOnly, + callback, + userData); + + if (GS_SUCCEEDED(result)) + return GS_SUCCESS; + else + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2gUpdateManifestInstalledContent +// Brief +// This function is updates to the manifest information kept by the SDK . +// Whenever a new content is downloaded and installed, it should be called +// after installing it. +// Parameters +// theItemId [in] Item Id which the content belongs to. +// theDownload [in] Download item for the particular content. +// Return Value +// GSResult – Enumeration indicating success (GS_SUCCESS) or a failure code. +// Remarks +// This is a mandatory function and should be call after each successful installation +// of a downloaded item. This helper function is used in conjunction with the Check +// for updates service call. +// +GSResult d2gUpdateManifestInstalledContent( D2GInstancePtr theInstance, + D2GItemId theItemId, + D2GDownloadItem *theDownload) +{ + D2GIManifestRecord manifest; + GS_ASSERT(theInstance); + GS_ASSERT(theDownload); + + if(!theDownload || !theInstance) + + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State,GSIDebugLevel_WarmError, + "%s :: %s (%d): called with NULL instance, please create and initialize.\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + // verify availability check + if (GSIGetAvailable()!=GSIACAvailable) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State,GSIDebugLevel_WarmError, + "%s :: %s (%d): called without doing availability check", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_NoAvailabilityCheck); + } + + manifest.itemId = theItemId; + manifest.urlId = theDownload->mUrlId; + manifest.version = theDownload->mVersion; + + return d2giUpdateManifestRecord( (D2GIInstance *) theInstance, manifest); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2gUpdateManifestRemovedContent +// Brief +// This function is deletes the un-installed content to the manifest +// information kept by the SDK . +// Whenever a new content is deleted, it should be called. +// Parameters +// theInstance [in] This is the pointer created by the d2gCreateInstance. +// theItemId [in] Item Id which the content belongs to. +// theDownload [in] Download item for the particular content. +// Return Value +// GSResult – Enumeration indicating success (GS_SUCCESS) or a failure code. +// Remarks +// This is a mandatory function and should be call after each uninstallation +// of an item.This helper function is used in conjunction with the Check +// for updates service call. +// +GSResult d2gUpdateManifestRemovedContent(D2GInstancePtr theInstance, + D2GItemId theItemId, + D2GDownloadItem *theDownload) +{ + D2GIManifestRecord manifest; + + GS_ASSERT(theDownload); + GS_ASSERT(theInstance); + if (!theDownload || !theInstance) + + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State,GSIDebugLevel_WarmError, + "%s :: %s (%d): called with NULL instance, please create and initialize.\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + // verify availability check + if (GSIGetAvailable()!=GSIACAvailable) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State,GSIDebugLevel_WarmError, + "%s :: %s (%d): called without doing availability check", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_NoAvailabilityCheck); + } + manifest.itemId = theItemId; + manifest.urlId = theDownload->mUrlId; + manifest.version = theDownload->mVersion; + + return d2giDeleteManifestRecord((D2GIInstance *)theInstance, manifest); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// +// For testing purposes only! +// +GSResult d2gSetServiceURL(D2GInstancePtr theInstance, + const char * serviceUrl) +{ + GS_ASSERT(theInstance != NULL); + GS_ASSERT(serviceUrl != NULL); + + // Check parameters + if (theInstance == NULL || serviceUrl == NULL) + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + + if (strlen(serviceUrl) >= GS_D2G_MAX_BASE_URL_LEN) + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_SizeExceedsMaximum); + ((D2GIInstance *)theInstance)->mServiceURLBase = gsimalloc(GS_D2G_MAX_BASE_URL_LEN); + strcpy(((D2GIInstance *)theInstance)->mServiceURLBase , serviceUrl); + printf("Service URL is %s", ((D2GIInstance *)theInstance)->mServiceURLBase); + return GS_SUCCESS; +} + +GSResult d2gSetServiceTimeout(D2GInstancePtr theInstance, + gsi_time timeoutMs) +{ + D2GIInstance *d2gInstance = (D2GIInstance *)theInstance; + + GS_ASSERT(d2gInstance); + if (!d2gInstance) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called with NULL instance\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + d2gInstance->mTimeoutMs = timeoutMs; + return GS_SUCCESS; +} +////////////////////////////////////////////////////////////////// +// For Content Updates testing purposes only +void d2gDisplayManifestFileContents(D2GInstancePtr d2gInstance) +{ + D2GIInstance *theInstance = (D2GIInstance *) d2gInstance; + char manifestRead[GS_D2G_MAX_FILE_BUFFER_LEN]; + FILE *pManifest = NULL; + char *manifestFileName = NULL; + + // determine the filename & path + if (!theInstance->mManifestFilePath) + { + manifestFileName = goastrdup(GS_D2G_MANIFEST_FILENAME); + } + else + { + manifestFileName = gsimalloc(sizeof(char) * (strlen(GS_D2G_MANIFEST_FILENAME)+strlen(theInstance->mManifestFilePath)+1)); + _stprintf(manifestFileName, "%s%s", theInstance->mManifestFilePath, GS_D2G_MANIFEST_FILENAME); + } + + pManifest = fopen(manifestFileName, "r"); + gsifree(manifestFileName); + + if (pManifest == NULL) + { + // manifest file down not exist + // No downloaded content + printf (" NO MANIFEST"); + } + else + { + memset(manifestRead, '\0', sizeof(manifestRead)); + while(!feof(pManifest)&& (fgets(manifestRead, sizeof(manifestRead)-1, pManifest) != NULL)) + { + D2GIString manifestXml; + manifestXml.str = gsimalloc((strlen(manifestRead)+2) * sizeof(char)); + memset (manifestXml.str, '\0',(strlen(manifestRead)*sizeof(char)) ); + // Uses URL safe encoding + B64Decode(manifestRead, manifestXml.str, (int) strlen(manifestRead), &manifestXml.len, 2); + printf("%s\n", manifestXml.str); + gsifree(manifestXml.str); + } + fclose(pManifest); + } +} \ No newline at end of file diff --git a/Direct2Game/d2gMain.h b/Direct2Game/d2gMain.h new file mode 100644 index 00000000..afeaf202 --- /dev/null +++ b/Direct2Game/d2gMain.h @@ -0,0 +1,72 @@ +/** +* d2gMain.h +* +* GameSpy DIRECT TO GAME SDK +* This file is part of the Direct to Game SDK designed and developed by GameSpy Tech. +* Copyright (c) 2008, GameSpy Technology +*/ +#ifndef __D2GMAIN_H__ +#define __D2GMAIN_H__ +/////////////////////////////////////////////////////////////////////////////// +/** +* Internal header for GameSpy Commerce SDK. +* SEE Direct2Game.h FOR PUBLIC INTERFACE +*/ + +#if defined(__cplusplus) +extern "C" +{ +#endif + +// D2G Shortcuts for creating a GSResult +#define GS_D2G_ERROR(s,c) GS_ERROR(GSResultSDK_Direct2Game,s,c) +#define GS_D2G_SUCCESS(s,c) GS_RESULT(0,GSResultSDK_Direct2Game,s,c) + +// Constants and Settings +static const int GS_D2G_ITEMARRAY_INITIAL_COUNT = 32; +static const int GS_D2G_ITEMARRAY_GROWBY = 32; +static const int GS_D2G_EXTRAINFOCACHE_INITIAL_COUNT = 32; +static const int GS_D2G_EXTRAINFOCACHE_GROWBY = 8; +static const int GS_D2G_DEFAULT_SERVICE_TIMEOUTMS = 30000; + +#define GS_D2G_LOG(t, l, m, ... ) gsDebugFormat(GSIDebugCat_Direct2Game, t, l, "%s(%d): In %s: \n" m "\n", __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) + +////////////////////////////////////////////////////////////////////////////// +// Types +////////////////////////////////////////////////////////////////////////////// + +// +// D2G SDK Instance +// + typedef struct D2GIInstance +{ + GSLoginCertificate mCertificate; + GSLoginPrivateData mPrivateData; + gsi_time mTimeoutMs; // MS = milliseconds + char *mManifestFilePath; + char *mServiceURLBase; + char *mServiceURL; +} D2GIInstance; + + +// +// D2GCatalog Keeps the cached data +// + typedef struct D2GICatalog + { + int mGameId; + gsi_u32 mVersion; + UCS2String mRegion; + UCS2String mAccessToken; + + // Item caching data + DArray mItemArray; + gsi_bool mAllItemsReceived; + DArray mExtraInfoCache; + } D2GICatalog; + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif //__D2GMAIN_H__ diff --git a/Direct2Game/d2gServices.c b/Direct2Game/d2gServices.c new file mode 100644 index 00000000..bde83241 --- /dev/null +++ b/Direct2Game/d2gServices.c @@ -0,0 +1,1630 @@ +// d2gServices.c +// +// GameSpy DIRECT TO GAME SDK +// This file is part of the Direct to Game SDK designed and developed by GameSpy Tech. +// Copyright (c) 2008, GameSpy Technology +// +/////////////////////////////////////////////////////////////////////////////// +// The functions in this file are internal to D2G SDK and they implement the +// the messaging interface between the SDK and the D2G server. +// They are called directly from the API. +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Includes +/////////////////////////////////////////////////////////////////////////////// +#include "../common/gsResultCodes.h" +#include "../common/gsAvailable.h" +#include "../ghttp/ghttpCommon.h" + +#include "Direct2Game.h" + +#include "d2gMain.h" +#include "d2gServices.h" +#include "d2gUtil.h" +#include "d2gDownloads.h" +#include "d2gDeserialize.h" + + +// This string is used as part of the SOAP messages +const char * GS_D2G_NAMESPACES[GS_D2G_NAMESPACE_COUNT] = +{ + GS_D2G_NAMESPACE "=\"http://gamespy.net/commerce/2009/02\"" +}; + + +/////////////////////////////////////////////////////////////////////////////// +// Get Store Availability +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giServiceIsOrderCompleteCallback +// Internal function to process response for store availibility +// +static void d2giGetStoreAvailCallback(GHTTPResult httpResult, + GSXmlStreamWriter theRequestXml, + GSXmlStreamReader theResponseXml, + void * theRequestData) +{ + GSResult result = GS_SUCCESS; + D2GIRequestData * requestData = NULL; + + // leave on stack, it goes away after the developer callback is triggered + D2GGetStoreAvailabilityResponse response; + memset(&response, 0, sizeof(response)); + response.mHttpResult = httpResult; + + requestData = (D2GIRequestData*)theRequestData; + + if (httpResult == GHTTPSuccess) + { + result = d2giParseGetStoreAvailResponse(requestData->mInstance, theResponseXml, &response); + } + else if (httpResult == GHTTPRequestCancelled) + { + result = GS_D2G_ERROR(GSResultSection_Network, GSResultCode_RequestTimedOut); + } + else + { + result = GS_D2G_ERROR(GSResultSection_Network, GSResultCode_HttpError); + } + + requestData->mUserCallback.mGetStoreAvailCallback(result, &response, requestData->mUserData); + gsifree(requestData); + GSI_UNUSED(theRequestXml); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giServiceGetStoreAvail +// Internal function send a request for store availability to the backend. +// +GSResult d2giServiceGetStoreAvail(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + D2GGetStoreAvailabilityCallback callback, + void *userData) +{ + GSXmlStreamWriter writer; + D2GIRequestData * requestData = NULL; + + // no need to use secure URL + d2giSetServiceUrl(theInstance, GS_D2G_URL_SECURE, GS_D2G_CATALOG_SERVICE_URL_FORMAT); + + // make a copy of the request callback and user param + requestData = (D2GIRequestData*)gsimalloc(sizeof(D2GIRequestData)); + if (requestData == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on malloc(%d) for D2GIRequestData", + __FILE__, __FUNCTION__, __LINE__, sizeof(D2GIRequestData)); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + requestData->mInstance = theInstance; + requestData->mCatalog = theCatalog; + requestData->mUserData = userData; + requestData->mUserCallback.mGetStoreAvailCallback = callback; + + writer = gsXmlCreateStreamWriter(GS_D2G_NAMESPACES, GS_D2G_NAMESPACE_COUNT); + if (writer == NULL) + { + gsifree(requestData); + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsXmlCreateStreamWriter (assuming out of memory)", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + else + { + GSSoapTask * aTask = NULL; + + if (gsi_is_false(gsXmlWriteOpenTag (writer, GS_D2G_NAMESPACE, GS_D2G_GETSTOREAVAIL_REQUEST)) || + gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "gameid", theCatalog->mGameId)) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "version", theCatalog->mVersion)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer,GS_D2G_NAMESPACE,"region", theCatalog->mRegion)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer,GS_D2G_NAMESPACE,"accesstoken", theCatalog->mAccessToken)) || + gsi_is_false(gsXmlWriteCloseTag (writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteCloseTag (writer, GS_D2G_NAMESPACE, GS_D2G_GETSTOREAVAIL_REQUEST)) || + gsi_is_false(gsXmlCloseWriter (writer))) + { + gsifree(requestData); + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not write request data: GS_D2G_GETSTOREAVAIL_REQUEST\n", + __FILE__, __FUNCTION__, __LINE__); + // Should only fail to write if the buffer is full + gsXmlFreeWriter(writer); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + aTask = gsiExecuteSoapWithTimeout(theInstance->mServiceURL, + GS_D2G_GETSTOREAVAIL_SOAP, + writer, + d2giGetStoreAvailCallback, theInstance->mTimeoutMs, + (void*)requestData); + if (aTask == NULL) + { + gsXmlFreeWriter(writer); + gsifree(requestData); + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsiExecuteSoapWithTimeout\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + } + return GS_SUCCESS; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +static void d2giServiceLoadExtraInfoCallback(GHTTPResult httpResult, + GSXmlStreamWriter theRequestXml, + GSXmlStreamReader theResponseXml, + void * theRequestData) +{ + GSResult result = GS_SUCCESS; + D2GIRequestData *requestData = (D2GIRequestData*)theRequestData; + + // leave on stack, it goes away after the developer callback is triggered + D2GLoadExtraCatalogInfoResponse response; + memset(&response, 0, sizeof(response)); + response.mHttpResult = httpResult; + + if (httpResult == GHTTPSuccess) + { + result = d2giParseLoadExtraCatalogInfoResponse(requestData->mInstance, + requestData->mCatalog, + theResponseXml, + &response); + if (GS_FAILED(result)) + { + d2giFreeExtraInfoList(requestData->mCatalog, response.mExtraCatalogInfoList, result, gsi_true); + response.mExtraCatalogInfoList = NULL; + } + } + else if (httpResult == GHTTPRequestCancelled) + { + result = GS_D2G_ERROR(GSResultSection_Network, GSResultCode_RequestTimedOut); + } + else + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_HttpError); + } + + // don't delete the extra info object because developers will keeping track of those. + requestData->mUserCallback.mGetExtraInfoCallback(result, &response, requestData->mUserData); + + // Done with the request free it before leaving + gsifree(requestData); + + GSI_UNUSED(theRequestXml); + +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giServiceLoadExtraInfo(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + D2GLoadExtraCatalogInfoCallback callback, + void *userData) +{ + GSXmlStreamWriter writer; + D2GIRequestData * requestData = NULL; + + // no need to use secure URL + d2giSetServiceUrl(theInstance, GS_D2G_URL_SECURE, GS_D2G_CATALOG_SERVICE_URL_FORMAT); + + // make a copy of the request callback and user param + requestData = (D2GIRequestData*)gsimalloc(sizeof(D2GIRequestData)); + if (requestData == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on malloc(%d) for D2GIRequestData", + __FILE__, __FUNCTION__, __LINE__, sizeof(D2GIRequestData)); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + requestData->mInstance = theInstance; + requestData->mCatalog = theCatalog; + requestData->mUserData = userData; + requestData->mUserCallback.mGetExtraInfoCallback = callback; + + writer = gsXmlCreateStreamWriter(GS_D2G_NAMESPACES, GS_D2G_NAMESPACE_COUNT); + if (writer == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsXmlCreateStreamWriter (assuming out of memory)", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + else + { + GSSoapTask * aTask = NULL; + + if (gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, GS_D2G_GET_STORE_EXTENSION_DATA_REQUEST)) || + gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "gameid", theCatalog->mGameId)) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "version", theCatalog->mVersion)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer,GS_D2G_NAMESPACE,"region", theCatalog->mRegion)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer,GS_D2G_NAMESPACE,"accesstoken", theCatalog->mAccessToken)) || + gsi_is_false(gsXmlWriteCloseTag(writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteCloseTag(writer, GS_D2G_NAMESPACE, GS_D2G_GET_STORE_EXTENSION_DATA_REQUEST)) || + gsi_is_false(gsXmlCloseWriter(writer)) + ) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not write request data: GS_D2G_GETEXTRAINFO_REQUEST\n", + __FILE__, __FUNCTION__, __LINE__); + // Should only fail to write if the buffer is full + gsXmlFreeWriter(writer); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + aTask = gsiExecuteSoapWithTimeout(theInstance->mServiceURL, + GS_D2G_GET_STORE_EXTENSION_DATA_SOAP, + writer, + d2giServiceLoadExtraInfoCallback, + theInstance->mTimeoutMs, + (void*)requestData); + if (aTask == NULL) + { + gsXmlFreeWriter(writer); + gsifree(requestData); + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsiExecuteSoapWithTimeout\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + } + return GS_SUCCESS; + +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +static void d2giServiceLoadCatalogItemsCallback(GHTTPResult httpResult, + GSXmlStreamWriter theRequestXml, + GSXmlStreamReader theResponseXml, + void *theRequestData) +{ + GSResult result = GS_SUCCESS; + D2GIRequestData * requestData = NULL; + + // initialize the Response data. + D2GLoadCatalogItemsResponse response; + memset(&response, 0, sizeof(response)); + response.mHttpResult = httpResult; + + requestData = (D2GIRequestData*)theRequestData; + + if (httpResult == GHTTPRequestCancelled) + { + result = GS_D2G_ERROR(GSResultSection_Network, GSResultCode_RequestTimedOut); + } + else if (httpResult != GHTTPSuccess) + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_HttpError); + } + else // httpResult == GHTTPSuccess + { + result = d2giParseLoadCatalogItemsFromResponse(requestData->mInstance, + requestData->mCatalog, + theResponseXml, + &response); + } + + requestData->mUserCallback.mLoadCatalogItemsCallback(result, &response, requestData->mUserData); + + // NOTE: Developer will be responsible for calling d2gFreeCatalogItems on mItemList in response. + if (GS_SUCCEEDED(result)) + { + // we successfully received all the items + requestData->mCatalog->mAllItemsReceived = gsi_true; + } + gsifree(requestData); + GSI_UNUSED(theRequestXml); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giServiceLoadCatalogItems(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + D2GLoadCatalogItemsCallback callback, + void *userData ) +{ + GSXmlStreamWriter writer; + D2GIRequestData *requestData = NULL; + + d2giSetServiceUrl(theInstance, GS_D2G_URL_SECURE, GS_D2G_CATALOG_SERVICE_URL_FORMAT); + + writer = gsXmlCreateStreamWriter(GS_D2G_NAMESPACES, GS_D2G_NAMESPACE_COUNT); + if (writer == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc,GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsXmlCreateStreamWriter(assuming out of memory)", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + else + { + GSSoapTask * aTask = NULL; + + // make a copy of the request callback and user param + requestData = (D2GIRequestData*)gsimalloc(sizeof(D2GIRequestData)); + if (requestData == NULL) + { + gsXmlFreeWriter(writer); + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Memory,GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on malloc(%d) for D2GIRequestData", + __FILE__, __FUNCTION__, __LINE__,sizeof(D2GIRequestData)); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + requestData->mInstance = theInstance; + requestData->mCatalog = theCatalog; + requestData->mUserData = userData; + requestData->mUserCallback.mLoadCatalogItemsCallback = callback; + + if (gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, GS_D2G_GETALLITEMS_REQUEST)) || + gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "gameid", theCatalog->mGameId)) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "version", theCatalog->mVersion)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer,GS_D2G_NAMESPACE,"region", theCatalog->mRegion)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer,GS_D2G_NAMESPACE,"accesstoken", theCatalog->mAccessToken)) || + gsi_is_false(gsXmlWriteCloseTag (writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteCloseTag(writer, GS_D2G_NAMESPACE, GS_D2G_GETALLITEMS_REQUEST)) || + gsi_is_false(gsXmlCloseWriter(writer))) + { + gsXmlFreeWriter(writer); + gsifree(requestData); + // Should only fail to write if the buffer is full + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not write request data: GS_D2G_GETALLITEMS_REQUEST.", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + aTask = gsiExecuteSoapWithTimeout( theInstance->mServiceURL, + GS_D2G_GETALLITEMS_SOAP, + writer, + d2giServiceLoadCatalogItemsCallback, + theInstance->mTimeoutMs, + (void*)requestData); + if (aTask == NULL) + { + gsXmlFreeWriter(writer); + gsifree(requestData); + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsiExecuteSoapWithTimeout\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + } + return GS_SUCCESS; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +static void d2giGetItemsByCategoryCallback( GHTTPResult httpResult, + GSXmlStreamWriter theRequestXml, + GSXmlStreamReader theResponseXml, + void *theRequestData) +{ + GSResult result = GS_SUCCESS; + D2GIRequestData * requestData = NULL; + + // leave on stack, it goes away after the developer callback is triggered + D2GLoadCatalogItemsByCategoryResponse response; + memset(&response, 0, sizeof(response)); + response.mHttpResult = httpResult; + + requestData = (D2GIRequestData*)theRequestData; + + if (httpResult == GHTTPSuccess) + { + result = d2giParseGetItemsByCategoryFromResponse(requestData->mInstance, + requestData->mCatalog, + theResponseXml, + &response); + } + else if (httpResult == GHTTPRequestCancelled) + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_RequestTimedOut); + } + else + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_HttpError); + } + + // NOTE: Developer will be responsible for calling d2gFreeCatalogItems on mItemList in response. + requestData->mUserCallback.mGetItemsByCategoryCallback(result, &response, requestData->mUserData); + + gsifree(requestData); + GSI_UNUSED(theRequestXml); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giServiceGetItemsByCategory(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + const UCS2String theCategory, + D2GLoadCatalogItemsByCategoryCallback usercallback, + void *userData) +{ + GSXmlStreamWriter writer; + D2GIRequestData * requestData = NULL; + + d2giSetServiceUrl(theInstance, GS_D2G_URL_SECURE, GS_D2G_CATALOG_SERVICE_URL_FORMAT); + + requestData = (D2GIRequestData*)gsimalloc(sizeof(D2GIRequestData)); + if (requestData == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_WarmError, + "%s :: %s (%d):Failed on malloc(%d) for D2GIRequestData", + __FILE__, __FUNCTION__, __LINE__, sizeof(D2GIRequestData)); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + requestData->mInstance = theInstance; + requestData->mCatalog = theCatalog; + requestData->mUserData = userData; + requestData->mUserCallback.mGetItemsByCategoryCallback = usercallback; + + writer = gsXmlCreateStreamWriter(GS_D2G_NAMESPACES, GS_D2G_NAMESPACE_COUNT); + if (writer == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d):Failed on gsXmlCreateStreamWriter (assuming out of memory)", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + else + { + GSSoapTask * aTask = NULL; + + if (gsi_is_false(gsXmlWriteOpenTag (writer, GS_D2G_NAMESPACE, GS_D2G_GETITEMSBYCATEGORY_REQUEST)) || + gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "gameid", theCatalog->mGameId)) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "version", theCatalog->mVersion)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer,GS_D2G_NAMESPACE,"region", theCatalog->mRegion)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer,GS_D2G_NAMESPACE,"accesstoken", theCatalog->mAccessToken)) || + gsi_is_false(gsXmlWriteOpenTag (writer, GS_D2G_NAMESPACE, "category")) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer, GS_D2G_NAMESPACE, "name", theCategory)) || + gsi_is_false(gsXmlWriteCloseTag (writer, GS_D2G_NAMESPACE, "category")) || + gsi_is_false(gsXmlWriteCloseTag (writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteCloseTag (writer, GS_D2G_NAMESPACE, GS_D2G_GETITEMSBYCATEGORY_REQUEST)) || + gsi_is_false(gsXmlCloseWriter (writer))) + { + // Should only fail to write if the buffer is full + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not write request data: GS_D2G_GETITEMSBYCATEGORY_REQUEST.", + __FILE__, __FUNCTION__, __LINE__); + gsXmlFreeWriter(writer); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + aTask = gsiExecuteSoapWithTimeout(theInstance->mServiceURL, + GS_D2G_GETITEMSBYCATEGORY_SOAP, + writer, + d2giGetItemsByCategoryCallback, + theInstance->mTimeoutMs, + (void*)requestData); + if (aTask == NULL) + { + gsXmlFreeWriter(writer); + gsifree(requestData); + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsiExecuteSoapWithTimeout\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + } + return GS_SUCCESS; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +static void d2giServiceGetUserCreditCardsCallback(GHTTPResult httpResult, + GSXmlStreamWriter theRequestXml, + GSXmlStreamReader theResponseXml, + void *theRequestData) +{ + GSResult result = GS_SUCCESS; + D2GIRequestData * requestData = NULL; + + D2GGetUserCreditCardsResponse response; + memset(&response, 0, sizeof(response)); + response.mHttpResult = httpResult; + + requestData = (D2GIRequestData*)theRequestData; + + if (httpResult == GHTTPSuccess) + { + result = d2giParseCreditCardInfoList(requestData->mInstance, theResponseXml, &response); + if (GS_FAILED(result)) + { + d2giFreeCreditCardInfoList(response.mListOfCreditCards); + response.mListOfCreditCards = NULL; + } + } + else if (httpResult == GHTTPRequestCancelled) + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_RequestTimedOut); + } + else + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_HttpError); + } + + requestData->mUserCallback.mGetUserCreditCardsCallback(result, &response, requestData->mUserData); + + gsifree(requestData); + GSI_UNUSED(theRequestXml); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giServiceGetUserCreditCards(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + gsi_bool validOnly, + D2GGetUserCreditCardsCallback callback, + void *userData) +{ + GSXmlStreamWriter writer; + D2GIRequestData *requestData = NULL; + + gsDebugFormat(GSIDebugCat_Direct2Game,GSIDebugType_Memory,GSIDebugLevel_Verbose, + "%s::%s entered\r\n", __FILE__, __FUNCTION__); + + d2giSetServiceUrl(theInstance, GS_D2G_URL_SECURE, GS_D2G_ACCOUNT_SERVICE_URL_FORMAT); + + // make a copy of the request callback and user param + requestData = (D2GIRequestData*)gsimalloc(sizeof(D2GIRequestData)); + if (requestData == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game,GSIDebugType_Memory,GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on malloc(%d) for D2GIRequestData\r\n", + __FILE__, __FUNCTION__, __LINE__, sizeof(D2GIRequestData)); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + requestData->mInstance = theInstance; + requestData->mCatalog = theCatalog; + requestData->mUserData = userData; + requestData->mUserCallback.mGetUserCreditCardsCallback = callback; + + writer = gsXmlCreateStreamWriter(GS_D2G_NAMESPACES, GS_D2G_NAMESPACE_COUNT); + if (writer == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game,GSIDebugType_Misc,GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsXmlCreateStreamWriter (assuming out of memory)\r\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + else + { + GSSoapTask *aTask = NULL; + char *returnValidOnly = validOnly ? "true" : "false"; + + if (gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, GS_D2G_GETUSERCREDITCARDS_REQUEST)) || + gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteHexBinaryElement(writer, GS_D2G_NAMESPACE, "proof", + (const gsi_u8 *)theInstance->mPrivateData.mKeyHash, GS_CRYPT_MD5_HASHSIZE)) || + gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, "certificate")) || + gsi_is_false(wsLoginCertWriteXML(&theInstance->mCertificate, GS_D2G_NAMESPACE, writer)) || + gsi_is_false(gsXmlWriteCloseTag(writer, GS_D2G_NAMESPACE, "certificate")) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "gameid", theCatalog->mGameId)) || + gsi_is_false(gsXmlWriteStringElement(writer, GS_D2G_NAMESPACE, "returnvalidonly", returnValidOnly)) || + gsi_is_false(gsXmlWriteCloseTag (writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteCloseTag(writer, GS_D2G_NAMESPACE, GS_D2G_GETUSERCREDITCARDS_REQUEST)) || + gsi_is_false(gsXmlCloseWriter(writer)) + ) + { + // Should only fail to write if the buffer is full + gsXmlFreeWriter(writer); + gsDebugFormat(GSIDebugCat_Direct2Game,GSIDebugType_Misc,GSIDebugLevel_WarmError, + "%s :: %s (%d): could not write request data: GS_D2G_GETUSERCREDITCARDS_REQUEST, etc.", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + aTask = gsiExecuteSoapWithTimeout( theInstance->mServiceURL, + GS_D2G_GETUSERCREDITCARDS_SOAP, + writer, + d2giServiceGetUserCreditCardsCallback, + theInstance->mTimeoutMs, + (void*)requestData); + if (aTask == NULL) + { + gsXmlFreeWriter(writer); + gsifree(requestData); + gsDebugFormat(GSIDebugCat_Direct2Game,GSIDebugType_Misc,GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsiExecuteSoapWithTimeout\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + } + return GS_SUCCESS; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +static void d2giGetOrderTotalCallback(GHTTPResult httpResult, + GSXmlStreamWriter theRequestXml, + GSXmlStreamReader theResponseXml, + void *theRequestData) +{ + GSResult result = GS_SUCCESS; + D2GIRequestData * requestData = NULL; + + // leave on stack, it goes away after the developer callback is triggered + D2GGetOrderTotalResponse response; + memset(&response, 0, sizeof(response)); + response.mHttpResult = httpResult; + + requestData = (D2GIRequestData*)theRequestData; + + if (httpResult == GHTTPSuccess) + { + result = d2giParseGetOrderTotalResponse(requestData->mInstance, + requestData->mCatalog, + theResponseXml, + &response); + } + else if (httpResult == GHTTPRequestCancelled) + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_RequestTimedOut); + } + else + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_HttpError); + } + + requestData->mUserCallback.mGetOrderTotalCallback(result, &response, requestData->mUserData); + + // Developer is responsible for calling d2gFreeOrderTotal to clean memory. + // We just need to clear our pointer to it. + response.mOrderTotal = NULL; + + // clean up the request + gsifree(requestData); + GSI_UNUSED(theRequestXml); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giServiceGetOrderTotal(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + gsi_u32 accountId, + D2GItemId *itemIds, + gsi_u32 itemCount, + gsi_u32 *itemQuantities, + D2GGetOrderTotalCallback callback, + void *userData) +{ + GSXmlStreamWriter writer; + D2GIRequestData *requestData = NULL; + + d2giSetServiceUrl(theInstance, GS_D2G_URL_SECURE, GS_D2G_PURCHASE_SERVICE_URL_FORMAT); + + // make a copy of the request callback and user param + requestData = (D2GIRequestData*)gsimalloc(sizeof(D2GIRequestData)); + if (requestData == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on malloc(%d) for D2GIRequestData\n", + __FILE__, __FUNCTION__, __LINE__, sizeof(D2GIRequestData)); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + requestData->mInstance = theInstance; + requestData->mCatalog = theCatalog; + requestData->mUserData = userData; + requestData->mUserCallback.mGetOrderTotalCallback = callback; + + writer = gsXmlCreateStreamWriter(GS_D2G_NAMESPACES, GS_D2G_NAMESPACE_COUNT); + if (writer == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsXmlCreateStreamWriter (assuming out of memory)\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + else + { + GSSoapTask * aTask = NULL; + D2GCatalogItem *item = d2giGetCatalogItem(theInstance, theCatalog, itemIds[0]); + + if ((item == NULL) || + gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, GS_D2G_GETORDERTOTAL_REQUEST)) || + gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "gameid", theCatalog->mGameId)) || + gsi_is_false(d2giWriteCachedItemsToXml(theInstance, theCatalog, writer, accountId, + item->mGeoInfo.mCultureCode , item->mGeoInfo.mCurrencyCode, itemIds, itemCount, itemQuantities)) || + + gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, "certificate")) || + gsi_is_false(wsLoginCertWriteXML(&theInstance->mCertificate, GS_D2G_NAMESPACE, writer)) || + gsi_is_false(gsXmlWriteCloseTag(writer, GS_D2G_NAMESPACE, "certificate")) || + gsi_is_false(gsXmlWriteHexBinaryElement(writer, GS_D2G_NAMESPACE, "proof", + (const gsi_u8 *)theInstance->mPrivateData.mKeyHash, GS_CRYPT_MD5_HASHSIZE)) || + gsi_is_false(gsXmlWriteCloseTag (writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteCloseTag (writer, GS_D2G_NAMESPACE, GS_D2G_GETORDERTOTAL_REQUEST)) || + gsi_is_false(gsXmlCloseWriter (writer)) + ) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not write request data: GS_D2G_GETORDERTOTAL_REQUEST\n", + __FILE__, __FUNCTION__, __LINE__); + + gsXmlFreeWriter(writer); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + aTask = gsiExecuteSoapWithTimeout( theInstance->mServiceURL, + GS_D2G_GETORDERTOTAL_SOAP, + writer, + d2giGetOrderTotalCallback, + theInstance->mTimeoutMs, + (void*)requestData); + if (aTask == NULL) + { + gsXmlFreeWriter(writer); + gsifree(requestData); + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsiExecuteSoapWithTimeout\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + } + return GS_SUCCESS; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void d2giServicePurchaseItemsCallback(GHTTPResult httpResult, + GSXmlStreamWriter theRequestXml, + GSXmlStreamReader theResponseXml, + void * theRequestData) +{ + GSResult result = GS_SUCCESS; + D2GIRequestData * requestData = NULL; + + // leave on stack, it goes away after the developer callback is triggered + D2GStartOrderResponse response; + memset(&response, 0, sizeof(response)); + response.mHttpResult = httpResult; + + requestData = (D2GIRequestData*)theRequestData; + + if (httpResult == GHTTPSuccess) + { + result = d2giParseStartOrderResponse(requestData->mInstance, + requestData->mCatalog, + theResponseXml, &response); + } + else if (httpResult == GHTTPRequestCancelled) + { + // TODO: ask for more info + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_RequestTimedOut); + } + else + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_HttpError); + } + + requestData->mUserCallback.mBeginPurchaseCallback(result, &response, requestData->mUserData); + + // Developer is responsible for calling d2gFreeOrderPurchase to clean memory. + // We just need to clear our pointer to it. + response.mOrderPurchase = NULL; + + // cleanup the request + gsifree(requestData); + + GSI_UNUSED(theRequestXml); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giServicePurchaseItems(D2GIInstance * theInstance, + D2GICatalog * theCatalog, + const D2GOrderTotal *theOrderTotal, + D2GStartOrderCallback callback, + void * userData) +{ + GSXmlStreamWriter writer; + D2GIRequestData * requestData = NULL; + + // no need to use secure URL + d2giSetServiceUrl(theInstance, GS_D2G_URL_SECURE, GS_D2G_PURCHASE_SERVICE_URL_FORMAT); + + // make a copy of the request callback and user param + requestData = (D2GIRequestData*)gsimalloc(sizeof(D2GIRequestData)); + if (requestData == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on malloc(%d) for D2GIRequestData", + __FILE__, __FUNCTION__, __LINE__, sizeof(D2GIRequestData)); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + requestData->mInstance = theInstance; + requestData->mCatalog = theCatalog; + requestData->mUserData = userData; + requestData->mUserCallback.mBeginPurchaseCallback = callback; + + writer = gsXmlCreateStreamWriter(GS_D2G_NAMESPACES, GS_D2G_NAMESPACE_COUNT); + if (writer == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsXmlCreateStreamWriter (assuming out of memory)", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + else + { + GSSoapTask * aTask = NULL; + + if (gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, GS_D2G_PURCHASEITEMS_REQUEST)) || + gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "gameid", theCatalog->mGameId)) || + gsi_is_false(d2giWriteOrderTotalToXml(theInstance, writer, theOrderTotal)) || + gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, "certificate")) || + gsi_is_false(wsLoginCertWriteXML(&theInstance->mCertificate, GS_D2G_NAMESPACE, writer)) || + gsi_is_false(gsXmlWriteCloseTag(writer, GS_D2G_NAMESPACE, "certificate")) || + gsi_is_false(gsXmlWriteHexBinaryElement(writer, GS_D2G_NAMESPACE, "proof", + (const gsi_u8 *)theInstance->mPrivateData.mKeyHash, GS_CRYPT_MD5_HASHSIZE)) || + gsi_is_false(gsXmlWriteCloseTag (writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteCloseTag(writer, GS_D2G_NAMESPACE, GS_D2G_PURCHASEITEMS_REQUEST)) || + gsi_is_false(gsXmlCloseWriter(writer)) + ) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not write request data: GS_D2G_PURCHASEITEMS_REQUEST\n", + __FILE__, __FUNCTION__, __LINE__); + + // Should only fail to write if the buffer is full + gsXmlFreeWriter(writer); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + aTask = gsiExecuteSoapWithTimeout( theInstance->mServiceURL, + GS_D2G_PURCHASEITEMS_SOAP, + writer, + d2giServicePurchaseItemsCallback, + theInstance->mTimeoutMs, + (void*)requestData); + if (aTask == NULL) + { + gsXmlFreeWriter(writer); + gsifree(requestData); + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsiExecuteSoapWithTimeout\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + } + return GS_SUCCESS; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void d2giServiceIsOrderCompleteCallback(GHTTPResult httpResult, + GSXmlStreamWriter theRequestXml, + GSXmlStreamReader theResponseXml, + void *theRequestData) +{ + GSResult result = GS_SUCCESS; + D2GIRequestData * requestData = NULL; + + // leave on stack, it goes away after the developer callback is triggered + D2GIsOrderCompleteResponse response; + memset(&response, 0, sizeof(response)); + response.mHttpResult = httpResult; + + requestData = (D2GIRequestData*)theRequestData; + + if (httpResult == GHTTPSuccess) + { + result = d2giParseIsOrderCompleteResponse(requestData->mInstance, + requestData->mCatalog, + theResponseXml, + &response); + } + else if (httpResult == GHTTPRequestCancelled) + { + // TODO: ask for more info + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_RequestTimedOut); + } + else + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_HttpError); + } + + requestData->mUserCallback.mIsOrderCompleteCallback(result, &response, requestData->mUserData); + + // Developer is responsible for calling d2gFreeOrderPurchase to clean memory. + // We just need to clear our pointer to it. + response.mOrderPurchase = NULL; + + // cleanup the request + gsifree(requestData); + GSI_UNUSED(theRequestXml); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giServiceIsOrderComplete( D2GIInstance *theInstance, + D2GICatalog *theCatalog, + const D2GOrderPurchase *theOrderPurchase, + D2GIsOrderCompleteCallback callback, + void *userData ) +{ + GSXmlStreamWriter writer; + D2GIRequestData * requestData = NULL; + + // no need to use secure URL + d2giSetServiceUrl(theInstance, GS_D2G_URL_SECURE, GS_D2G_PURCHASE_SERVICE_URL_FORMAT); + + // make a copy of the request callback and user param + requestData = (D2GIRequestData*)gsimalloc(sizeof(D2GIRequestData)); + if (requestData == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on malloc(%d) for D2GIRequestData", + __FILE__, __FUNCTION__, __LINE__, sizeof(D2GIRequestData)); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + requestData->mInstance = theInstance; + requestData->mCatalog = theCatalog; + requestData->mUserData = userData; + requestData->mUserCallback.mIsOrderCompleteCallback = callback; + + writer = gsXmlCreateStreamWriter(GS_D2G_NAMESPACES, GS_D2G_NAMESPACE_COUNT); + if (writer == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsXmlCreateStreamWriter (assuming out of memory)", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + else + { + GSSoapTask * aTask = NULL; + + if (gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, GS_D2G_PLACEDORDER_REQUEST)) || + gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer, GS_D2G_NAMESPACE, "rootorderid", + theOrderPurchase->mOrder.mRootOrderGuid)) || + gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, "certificate")) || + gsi_is_false(wsLoginCertWriteXML(&theInstance->mCertificate, GS_D2G_NAMESPACE, writer)) || + gsi_is_false(gsXmlWriteCloseTag(writer, GS_D2G_NAMESPACE, "certificate")) || + gsi_is_false(gsXmlWriteHexBinaryElement(writer, GS_D2G_NAMESPACE, "proof", + (const gsi_u8 *)theInstance->mPrivateData.mKeyHash, GS_CRYPT_MD5_HASHSIZE)) || + gsi_is_false(gsXmlWriteCloseTag (writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteCloseTag(writer, GS_D2G_NAMESPACE, GS_D2G_PLACEDORDER_REQUEST)) || + gsi_is_false(gsXmlCloseWriter(writer)) + ) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not write request data: GS_D2G_PLACEDORDER_REQUEST\n", + __FILE__, __FUNCTION__, __LINE__); + // Should only fail to write if the buffer is full + gsXmlFreeWriter(writer); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + aTask = gsiExecuteSoapWithTimeout(theInstance->mServiceURL, + GS_D2G_PLACEDORDER_SOAP, + writer, + d2giServiceIsOrderCompleteCallback, + theInstance->mTimeoutMs, + (void*)requestData); + if (aTask == NULL) + { + gsXmlFreeWriter(writer); + gsifree(requestData); + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsiExecuteSoapWithTimeout\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + } + return GS_SUCCESS; +} + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +static void d2giServiceDownloadFileByIdCallback(GHTTPResult httpResult, + GSXmlStreamWriter theRequestXml, + GSXmlStreamReader theResponseXml, + void * theRequestData) +{ + GSResult result = GS_SUCCESS; + D2GIRequestData * requestData = NULL; + + requestData = (D2GIRequestData*)theRequestData; + + if (httpResult == GHTTPSuccess) + { + result = d2giParseGetResponse(theResponseXml, GS_D2G_GETDOWNLOADINFO_RESULT); + if (GS_SUCCEEDED(result)) + { + int downloadUrlLen = 4096; + gsi_char *downloadUrl; + int statusCode; + const char *statusMessage; + int statusMessageLen; + + if (gsi_is_false(gsXmlMoveToChild(theResponseXml, "downloadinfo")) || + gsi_is_false(gsXmlMoveToChild(theResponseXml, "status")) || + gsi_is_false(gsXmlReadChildAsInt(theResponseXml, "code", &statusCode)) || + gsi_is_false(gsXmlReadChildAsString(theResponseXml, "message", &statusMessage, &statusMessageLen))|| + gsi_is_false(gsXmlMoveToParent(theResponseXml))) + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + requestData->mCompleteCallback(result, NULL, requestData->mUserData); + } + else + { + result = d2giResultFromDownloadStatusCode(statusCode); + if (GS_SUCCEEDED(result)) + { + downloadUrl = gsimalloc(sizeof(gsi_char) * downloadUrlLen + 1); + + if (gsi_is_false(gsXmlReadChildAsTStringNT(theResponseXml, "url", downloadUrl, downloadUrlLen))) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not parse xml stream: downloadurl\n", + __FILE__, __FUNCTION__, __LINE__); + + gsifree(downloadUrl); + + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_CouldNotParseXML); + requestData->mCompleteCallback(result, NULL, requestData->mUserData); + } + else + { + gsi_char *httpUserHeaders = NULL; + + // get additional http headers, each has to be terminated by a (CRLF). + result = d2giParseDownloadUserHeadersFromResponse(theResponseXml, &httpUserHeaders); + + if (GS_SUCCEEDED(result)) + { + // Now we can start downloading + result = d2giStartDownloadThread(requestData->mDownloadLocation, + downloadUrl, + httpUserHeaders, + requestData->mProgressCallback, + requestData->mCompleteCallback, + requestData->mUserData); + + if (GS_FAILED(result)) + { + requestData->mCompleteCallback(result, NULL, requestData->mUserData); + } + } + else + { + requestData->mCompleteCallback(result, NULL, requestData->mUserData); + } + } + } + else + { + // Received Failure from the backend + requestData->mCompleteCallback(result, NULL, requestData->mUserData); + } + } + } + else + { + // Received a failure from the back end. + requestData->mCompleteCallback(result, NULL, requestData->mUserData); + } + } + else if (httpResult == GHTTPRequestCancelled) + { + result = GSResultCode_RequestTimedOut; + requestData->mCompleteCallback(result, NULL, requestData->mUserData); + } + else + { + result = GS_D2G_ERROR(GSResultSection_Network, GSResultCode_HttpError); + requestData->mCompleteCallback(result, NULL, requestData->mUserData); + } + gsifree(requestData); + GSI_UNUSED(theRequestXml); +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +GSResult d2giServiceGetDownloadInfo(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + D2GFileId fileId, + const gsi_char *downloadLocation, + D2GDownloadProgressCallback progressCallback, + D2GDownloadCompleteCallback completeCallback, + void *userData) +{ + GSXmlStreamWriter writer; + D2GIRequestData * requestData = NULL; + + d2giSetServiceUrl(theInstance, GS_D2G_URL_SECURE, GS_D2G_CATALOG_SERVICE_URL_FORMAT); + + // make a copy of the request callback and user param + requestData = (D2GIRequestData *)gsimalloc(sizeof(D2GIRequestData)); + if (requestData == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on malloc(%d) for D2GIRequestData\n", + __FILE__, __FUNCTION__, __LINE__, sizeof(D2GIRequestData)); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + memset(requestData, 0, sizeof(D2GIRequestData)); + requestData->mInstance = theInstance; + requestData->mCatalog = theCatalog; + requestData->mUserData = userData; + requestData->mDownloadLocation = downloadLocation; + requestData->mProgressCallback = progressCallback; + requestData->mCompleteCallback = completeCallback; + + writer = gsXmlCreateStreamWriter(GS_D2G_NAMESPACES, GS_D2G_NAMESPACE_COUNT); + if (writer == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsXmlCreateStreamWriter (assuming out of memory)\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + else + { + GSSoapTask * aTask = NULL; + if (gsi_is_false(gsXmlWriteOpenTag (writer, GS_D2G_NAMESPACE, GS_D2G_GETDOWNLOADINFO_REQUEST)) || + gsi_is_false(gsXmlWriteOpenTag (writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "gameid", theCatalog->mGameId)) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "fileid", fileId)) || + gsi_is_false(gsXmlWriteOpenTag (writer, GS_D2G_NAMESPACE, "certificate")) || + gsi_is_false(wsLoginCertWriteXML (&theInstance->mCertificate, GS_D2G_NAMESPACE, writer)) || + gsi_is_false(gsXmlWriteCloseTag (writer, GS_D2G_NAMESPACE, "certificate")) || + gsi_is_false(gsXmlWriteHexBinaryElement(writer, GS_D2G_NAMESPACE, "proof", + (const gsi_u8 *)theInstance->mPrivateData.mKeyHash, GS_CRYPT_MD5_HASHSIZE)) || + gsi_is_false(gsXmlWriteCloseTag (writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteCloseTag (writer, GS_D2G_NAMESPACE, GS_D2G_GETDOWNLOADINFO_REQUEST)) || + gsi_is_false(gsXmlCloseWriter (writer)) + ) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not write request data: GS_D2G_GETDOWNLOADINFO_REQUEST\n", + __FILE__, __FUNCTION__, __LINE__); + + gsXmlFreeWriter(writer); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + aTask = gsiExecuteSoapWithTimeout(theInstance->mServiceURL, + GS_D2G_GETDOWNLOADINFO_SOAP, + writer, + d2giServiceDownloadFileByIdCallback, + theInstance->mTimeoutMs, + (void*)requestData); + if (aTask == NULL) + { + gsXmlFreeWriter(writer); + gsifree(requestData); + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsiExecuteSoapWithTimeout\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + } + return GS_SUCCESS; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +static void d2giServiceGetPurchaseHistoryCallback(GHTTPResult httpResult, + GSXmlStreamWriter theRequestXml, + GSXmlStreamReader theResponseXml, + void * theRequestData) +{ + GSResult result = GS_SUCCESS; + D2GIRequestData * requestData = (D2GIRequestData*)theRequestData; + + // leave on stack, it goes away after the developer callback is triggered + D2GGetPurchaseHistoryResponse response; + memset(&response, 0, sizeof(response)); + response.mHttpResult = httpResult; + + if (httpResult == GHTTPSuccess) + { + result = d2giParseGetPurchaseHistoryResponse(requestData->mInstance, + requestData->mCatalog, + theResponseXml, + &response); + if (GS_FAILED(result)) + { + d2giFreePurchaseHistory(response.mPurchaseHistory); + response.mPurchaseHistory = NULL; + } + } + else if (httpResult == GHTTPRequestCancelled) + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_RequestTimedOut); + } + else + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_HttpError); + } + + // we don't need to clean up the purchase history object in the case of success because the developer + // will need to keep track of it using a pointer. It saves them from having to clone the history + // because that eats up twice as much memory during the callback scope. + requestData->mUserCallback.mGetPurchaseHistoryCallback(result, &response, requestData->mUserData); + + gsifree(requestData); + + GSI_UNUSED(theRequestXml); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giServiceGetPurchaseHistory( D2GIInstance *theInstance, + D2GICatalog *theCatalog, + D2GGetPurchaseHistoryCallback callback, + void *userData ) +{ + GSXmlStreamWriter writer; + D2GIRequestData * requestData = NULL; + + // no need to use secure URL + d2giSetServiceUrl(theInstance, GS_D2G_URL_SECURE, GS_D2G_PURCHASE_SERVICE_URL_FORMAT); + + // make a copy of the request callback and user param + requestData = (D2GIRequestData*)gsimalloc(sizeof(D2GIRequestData)); + if (requestData == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on malloc(%d) for D2GIRequestData", + __FILE__, __FUNCTION__, __LINE__, sizeof(D2GIRequestData)); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + requestData->mInstance = theInstance; + requestData->mCatalog = theCatalog; + requestData->mUserData = userData; + requestData->mUserCallback.mGetPurchaseHistoryCallback = callback; + + writer = gsXmlCreateStreamWriter(GS_D2G_NAMESPACES, GS_D2G_NAMESPACE_COUNT); + if (writer == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsXmlCreateStreamWriter (assuming out of memory)", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + else + { + GSSoapTask * aTask = NULL; + + if (gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, GS_D2G_GETPURCHASEHISTORY_REQUEST)) || + gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "gameid", theCatalog->mGameId)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer,GS_D2G_NAMESPACE,"accesstoken", theCatalog->mAccessToken)) || + gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, "certificate")) || + gsi_is_false(wsLoginCertWriteXML(&theInstance->mCertificate, GS_D2G_NAMESPACE, writer)) || + gsi_is_false(gsXmlWriteCloseTag(writer, GS_D2G_NAMESPACE, "certificate")) || + gsi_is_false(gsXmlWriteHexBinaryElement(writer, GS_D2G_NAMESPACE, "proof", + (const gsi_u8 *)theInstance->mPrivateData.mKeyHash, GS_CRYPT_MD5_HASHSIZE)) || + gsi_is_false(gsXmlWriteCloseTag (writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteCloseTag(writer, GS_D2G_NAMESPACE, GS_D2G_GETPURCHASEHISTORY_REQUEST)) || + gsi_is_false(gsXmlCloseWriter(writer)) + ) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not write request data: GS_D2G_GETPURCHASEHISTORY_REQUEST\n", + __FILE__, __FUNCTION__, __LINE__); + // Should only fail to write if the buffer is full + gsXmlFreeWriter(writer); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + aTask = gsiExecuteSoapWithTimeout(theInstance->mServiceURL, + GS_D2G_GETPURCHASEHISTORY_SOAP, + writer, + d2giServiceGetPurchaseHistoryCallback, + theInstance->mTimeoutMs, + (void*)requestData); + if (aTask == NULL) + { + gsXmlFreeWriter(writer); + gsifree(requestData); + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsiExecuteSoapWithTimeout\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + } + return GS_SUCCESS; +} + + +/////////////////////////////////////////////////////////////////////////////// +// GeT Item Activation Data - Upcoming feature not fully supported +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giServiceGetItemActivationDataCallback +// +static void d2giServiceGetItemActivationDataCallback(GHTTPResult httpResult, + GSXmlStreamWriter theRequestXml, + GSXmlStreamReader theResponseXml, + void * theRequestData) +{ + GSResult result = GS_SUCCESS; + D2GIRequestData *requestData = (D2GIRequestData*)theRequestData; + + D2GGetItemActivationResponse response; + memset(&response, 0, sizeof(response)); + response.mHttpResult = httpResult; + + if (httpResult == GHTTPSuccess) + { + result = d2giParseGetItemActivationResponse(requestData->mInstance, + requestData->mCatalog, + theResponseXml, + &response); + } + else if (httpResult == GHTTPRequestCancelled) + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_RequestTimedOut); + } + else + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_HttpError); + } + + requestData->mUserCallback.mGetActivationDataCallback(result, &response, requestData->mUserData); + + // Done with the request free it before leaving + gsifree(requestData); + + GSI_UNUSED(theRequestXml); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giServiceGetItemActivationData +// +GSResult d2giServiceGetItemActivationData( D2GIInstance *theInstance, + D2GICatalog *theCatalog, + D2GGetItemActivationDataCallback callback, + D2GPackageId thePackageId, + void *userData) +{ + GSResult result = GS_SUCCESS; + GSXmlStreamWriter writer; + D2GIRequestData *requestData = NULL; + + // no need to use secure URL + d2giSetServiceUrl(theInstance, GS_D2G_URL_SECURE, GS_D2G_CATALOG_SERVICE_URL_FORMAT); + + // make a copy of the request callback and user param + requestData = (D2GIRequestData*)gsimalloc(sizeof(D2GIRequestData)); + if (requestData == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on malloc(%d) for D2GIRequestData\n", + __FILE__, __FUNCTION__, __LINE__, sizeof(D2GIRequestData)); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + requestData->mInstance = theInstance; + requestData->mCatalog = theCatalog; + requestData->mUserData = userData; + requestData->mUserCallback.mGetActivationDataCallback = callback; + + writer = gsXmlCreateStreamWriter(GS_D2G_NAMESPACES, GS_D2G_NAMESPACE_COUNT); + if (writer == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsXmlCreateStreamWriter (assuming out of memory)\n", + __FILE__, __FUNCTION__, __LINE__ ); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + else + { + GSSoapTask * aTask = NULL; + + if (gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, GS_D2G_GETITEMACTIVATIONDATA_REQUEST)) || + gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "gameid", theCatalog->mGameId)) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "packageid", thePackageId)) || + gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, "certificate")) || + gsi_is_false(wsLoginCertWriteXML(&theInstance->mCertificate, GS_D2G_NAMESPACE, writer)) || + gsi_is_false(gsXmlWriteCloseTag(writer, GS_D2G_NAMESPACE, "certificate")) || + gsi_is_false(gsXmlWriteHexBinaryElement(writer, GS_D2G_NAMESPACE, "proof", + (const gsi_u8 *)theInstance->mPrivateData.mKeyHash, GS_CRYPT_MD5_HASHSIZE)) || + gsi_is_false(gsXmlWriteCloseTag(writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteCloseTag(writer, GS_D2G_NAMESPACE, GS_D2G_GETITEMACTIVATIONDATA_REQUEST)) || + gsi_is_false(gsXmlCloseWriter(writer)) + ) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not write request data.\n", + __FILE__, __FUNCTION__, __LINE__ ); + + // Should only fail to write if the buffer is full + gsXmlFreeWriter(writer); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + aTask = gsiExecuteSoapWithTimeout(theInstance->mServiceURL, + GS_D2G_GETITEMACTIVATIONDATA_SOAP, + writer, + d2giServiceGetItemActivationDataCallback, + theInstance->mTimeoutMs, + (void*)requestData); + if (aTask == NULL) + { + gsXmlFreeWriter(writer); + gsifree(requestData); + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsiExecuteSoapWithTimeout\n", + __FILE__, __FUNCTION__, __LINE__ ); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + } + + return result; +} +/////////////////////////////////////////////////////////////////////////////// +// Downloadable Content Updates +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// d2giServiceCheckContentUpdatesCallback +// Internal callback to process CheckContentUpdates Service Response. +// +static void d2giServiceCheckContentUpdatesCallback(GHTTPResult httpResult, + GSXmlStreamWriter theRequestXml, + GSXmlStreamReader theResponseXml, + void *theRequestData) + +{ + GSResult result = GS_SUCCESS; + D2GIRequestData *requestData = (D2GIRequestData*)theRequestData; + D2GCheckContentUpdatesResponse response; + memset(&response, 0, sizeof(response)); + response.mHttpResult = httpResult; + + if (httpResult == GHTTPSuccess) + { + result = d2giParseCheckContentUpdates(requestData->mInstance, + requestData->mCatalog, + theResponseXml, + &response); + if (GS_FAILED(result)) + { + d2giFreeContentUpdateList(response.mContentUpdateList); + response.mContentUpdateList = NULL; + } + } + else if (httpResult == GHTTPRequestCancelled) + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_RequestTimedOut); + } + else + { + result = GS_D2G_ERROR(GSResultSection_Soap, GSResultCode_HttpError); + } + + requestData->mUserCallback.mCheckContentUpdatesCallback(result, &response, requestData->mUserData); + + gsifree(requestData); + + GSI_UNUSED(httpResult); + GSI_UNUSED(theRequestXml); + GSI_UNUSED(theResponseXml); + GSI_UNUSED(theRequestData); +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giServiceCheckContentUpdates +// This is called when the application offers updates on a previously downloaded +// or purchased content. It makes a service call to the backend. +// +GSResult d2giServiceCheckContentUpdates(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + gsi_bool requiredOnly, + D2GCheckContentUpdatesCallback callback, + void *userData) +{ + GSResult result = GS_SUCCESS; + GSXmlStreamWriter writer; + D2GIRequestData * requestData = NULL; + + d2giSetServiceUrl(theInstance, GS_D2G_URL_SECURE, GS_D2G_CATALOG_SERVICE_URL_FORMAT); + + // make a copy of the request callback and user param + requestData = (D2GIRequestData *)gsimalloc(sizeof(D2GIRequestData)); + if (requestData == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on malloc(%d) for D2GIRequestData\n", + __FILE__, __FUNCTION__, __LINE__, sizeof(D2GIRequestData)); + + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + memset(requestData, 0, sizeof(D2GIRequestData)); + requestData->mInstance = theInstance; + requestData->mCatalog = theCatalog; + requestData->mUserData = userData; + requestData->mUserCallback.mCheckContentUpdatesCallback = callback; + + writer = gsXmlCreateStreamWriter(GS_D2G_NAMESPACES, GS_D2G_NAMESPACE_COUNT); + if (writer == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsXmlCreateStreamWriter (assuming out of memory)\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + else + { + GSSoapTask *aTask = NULL; + + if (gsi_is_false(gsXmlWriteOpenTag (writer, GS_D2G_NAMESPACE, GS_D2G_CHECKFORCONTENTUPDATES_REQUEST)) || + gsi_is_false(gsXmlWriteOpenTag (writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "gameid", theCatalog->mGameId)) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "requiredonly", requiredOnly)) || + gsi_is_false(gsXmlWriteOpenTag (writer, GS_D2G_NAMESPACE, "downloadeditems")) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "count",0 )) || + gsi_is_false(d2giWriteManifestDownloadsToXML(writer, theInstance)) || + gsi_is_false(gsXmlWriteCloseTag (writer, GS_D2G_NAMESPACE, "downloadeditems")) || + gsi_is_false(gsXmlWriteOpenTag (writer, GS_D2G_NAMESPACE, "certificate")) || + gsi_is_false(wsLoginCertWriteXML (&theInstance->mCertificate, GS_D2G_NAMESPACE, writer)) || + gsi_is_false(gsXmlWriteCloseTag (writer, GS_D2G_NAMESPACE, "certificate")) || + gsi_is_false(gsXmlWriteHexBinaryElement(writer, GS_D2G_NAMESPACE, "proof", (const gsi_u8 *)theInstance->mPrivateData.mKeyHash, GS_CRYPT_MD5_HASHSIZE)) || + + gsi_is_false(gsXmlWriteCloseTag (writer, GS_D2G_NAMESPACE, GS_D2G_REQUEST)) || + gsi_is_false(gsXmlWriteCloseTag (writer, GS_D2G_NAMESPACE, GS_D2G_CHECKFORCONTENTUPDATES_REQUEST)) || + gsi_is_false(gsXmlCloseWriter (writer)) + ) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not write request data: GS_D2G_CHECKFORCONTENTUPDATES_REQUEST\n", + __FILE__, __FUNCTION__, __LINE__); + // Should only fail to write if the buffer is full + gsXmlFreeWriter(writer); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + aTask = gsiExecuteSoapWithTimeout(theInstance->mServiceURL, + GS_D2G_CHECKFORCONTENTUPDATES_SOAP, + writer, + d2giServiceCheckContentUpdatesCallback, + theInstance->mTimeoutMs, + (void*)requestData); + if (aTask == NULL) + { + gsXmlFreeWriter(writer); + gsifree(requestData); + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsiExecuteSoapWithTimeout\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + } + return result; +}; diff --git a/Direct2Game/d2gServices.h b/Direct2Game/d2gServices.h new file mode 100644 index 00000000..0faafdbb --- /dev/null +++ b/Direct2Game/d2gServices.h @@ -0,0 +1,237 @@ +/** +* d2gServices.h +* +* GameSpy DIRECT TO GAME SDK +* This file is part of the Direct to Game SDK designed and developed by GameSpy Tech. +* Copyright (c) 2008, GameSpy Technology +*/ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +#ifndef __D2GSERVICES_H__ +#define __D2GSERVICES_H__ + + +// ***** Commerce web services. +// +// ***** PUBLIC INTERFACE AT THE BOTTOM OF THE FILE + +#include "../ghttp/ghttpSoap.h" +#include "../common/gsCrypt.h" +#include "../common/gsLargeInt.h" +#include "d2gUtil.h" +#if defined(__cplusplus) +extern "C" +{ +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// general header SOAP elements +#define GS_D2G_RESULT_SECTION "status" +#define GS_D2G_RESULT_CODE "code" +#define GS_D2G_RESULT_MSG "message" +#define GS_D2G_COUNT_ELEMENT "count" +#define GS_D2G_REQUEST "request" +#define GS_D2G_EXTRA_INFO "extensiondata" +#define GS_D2G_ETRA_INFO_LIST "extensiondatalist" +#define GS_D2G_CATALOG_ITEM_LIST "catalogitemlist" +#define GS_D2G_CATALOG_ITEM "catalogitem" +#define GS_D2G_ORDER_ITEM_PURCHASE "orderitempurchase" +#define GS_D2G_ORDER_ITEM_PURCHASES "orderitempurchases" +// item list soap elements + +// Catalog Service SOAP elements +#define GS_D2G_GETALLITEMS_REQUEST "GetAllItems" +#define GS_D2G_GETALLITEMS_RESULT "GetAllItemsResult" +#define GS_D2G_GETCATEGORIES_REQUEST "GetCategories" +#define GS_D2G_GETCATEGORIES_RESULT "GetCategoriesResult" +#define GS_D2G_GETITEMSBYCATEGORY_REQUEST "GetItemsByCategory" +#define GS_D2G_GETITEMSBYCATEGORY_RESULT "GetItemsByCategoryResult" +#define GS_D2G_GETITEMDETAILS_REQUEST "GetItemDetails" +#define GS_D2G_GETITEMDETAILS_RESULT "GetItemDetailsResult" +#define GS_D2G_GETSTOREAVAIL_REQUEST "GetStoreAvailability" +#define GS_D2G_GETSTOREAVAIL_RESULT "GetStoreAvailabilityResult" +#define GS_D2G_GETDOWNLOADINFO_REQUEST "GetDownloadInfo" +#define GS_D2G_GETDOWNLOADINFO_RESULT "GetDownloadInfoResult" +#define GS_D2G_GET_STORE_EXTENSION_DATA_REQUEST "GetStoreExtensionData" +#define GS_D2G_GET_STORE_EXTENSION_DATA_RESULT "GetStoreExtensionDataResult" +#define GS_D2G_GETITEMACTIVATIONDATA_REQUEST "GetItemActivationData" +#define GS_D2G_GETITEMACTIVATIONDATA_RESULT "GetItemActivationDataResult" +#define GS_D2G_CHECKFORCONTENTUPDATES_REQUEST "CheckForContentUpdates" +#define GS_D2G_CHECKFORCONTENTUPDATES_RESULT "CheckForContentUpdatesResult" + +// Catalog Service SOAP Actions +#define GS_D2G_GETITEMDETAILS_SOAP "SOAPAction: \"http://gamespy.net/commerce/2009/02/CatalogService/GetItemDetails\"" +#define GS_D2G_GETITEMSBYCATEGORY_SOAP "SOAPAction: \"http://gamespy.net/commerce/2009/02/CatalogService/GetItemsByCategory\"" +#define GS_D2G_GETALLITEMS_SOAP "SOAPAction: \"http://gamespy.net/commerce/2009/02/CatalogService/GetAllItems\"" +#define GS_D2G_GETCATEGORIES_SOAP "SOAPAction: \"http://gamespy.net/commerce/2009/02/CatalogService/GetCategories\"" +#define GS_D2G_GETSTOREAVAIL_SOAP "SOAPAction: \"http://gamespy.net/commerce/2009/02/CatalogService/GetStoreAvailability\"" +#define GS_D2G_GETDOWNLOADINFO_SOAP "SOAPAction: \"http://gamespy.net/commerce/2009/02/CatalogService/GetDownloadInfo\"" +#define GS_D2G_GET_STORE_EXTENSION_DATA_SOAP "SOAPAction: \"http://gamespy.net/commerce/2009/02/CatalogService/GetStoreExtensionData\"" +#define GS_D2G_GETITEMACTIVATIONDATA_SOAP "SOAPAction: \"http://gamespy.net/commerce/2009/02/CatalogService/GetItemActivationData\"" +#define GS_D2G_CHECKFORCONTENTUPDATES_SOAP "SOAPAction: \"http://gamespy.net/commerce/2009/02/CatalogService/CheckForContentUpdates\"" + +// Account Service SOAP Objects +#define GS_D2G_GETUSERCREDITCARDS_REQUEST "GetUserCreditCards" +#define GS_D2G_GETUSERCREDITCARDS_RESPONSE "GetUserCreditCardsResponse" +#define GS_D2G_GETUSERCREDITCARDS_RESULT "GetUserCreditCardsResult" + +// Account Service SOAP Actions +#define GS_D2G_GETUSERCREDITCARDS_SOAP "SOAPAction: \"http://gamespy.net/commerce/2009/02/AccountService/GetUserCreditCards\"" + +// Purchase Service SOAP Actions +#define GS_D2G_GETORDERTOTAL_REQUEST "GetOrderTotal" +#define GS_D2G_GETORDERTOTAL_RESPONSE "GetOrderTotalResponse" +#define GS_D2G_GETORDERTOTAL_RESULT "GetOrderTotalResult" +#define GS_D2G_PURCHASEITEMS_REQUEST "PlaceOrder" +#define GS_D2G_PURCHASEITEMS_RESPONSE "PlaceOrderResponse" +#define GS_D2G_PURCHASEITEMS_RESULT "PlaceOrderResult" +#define GS_D2G_PLACEDORDER_REQUEST "GetPlacedOrder" +#define GS_D2G_PLACEDORDER_RESPONSE "GetPlacedOrderResponse" +#define GS_D2G_PLACEDORDER_RESULT "GetPlacedOrderResult" +#define GS_D2G_GETPURCHASEHISTORY_REQUEST "GetPurchaseHistory" +#define GS_D2G_GETPURCHASEHISTORY_RESPONSE "GetPurchaseHistoryResponse" +#define GS_D2G_GETPURCHASEHISTORY_RESULT "GetPurchaseHistoryResult" + +#define GS_D2G_GETORDERTOTAL_SOAP "SOAPAction: \"http://gamespy.net/commerce/2009/02/PurchaseService/GetOrderTotal\"" +#define GS_D2G_PURCHASEITEMS_SOAP "SOAPAction: \"http://gamespy.net/commerce/2009/02/PurchaseService/PlaceOrder\"" +#define GS_D2G_PLACEDORDER_SOAP "SOAPAction: \"http://gamespy.net/commerce/2009/02/PurchaseService/GetPlacedOrder\"" +#define GS_D2G_GETPURCHASEHISTORY_SOAP "SOAPAction: \"http://gamespy.net/commerce/2009/02/PurchaseService/GetPurchaseHistory\"" + + +#define GS_D2G_NAMESPACE "gsc" +#define GS_D2G_AUTHSERVICE_NAMESPACE "gsa" +#define GS_D2G_NAMESPACE_COUNT 1 + +#define GS_D2G_CATALOG_SERVICE_URL_FORMAT "catalogservice.svc" +#define GS_D2G_ACCOUNT_SERVICE_URL_FORMAT "accountservice.svc" +#define GS_D2G_PURCHASE_SERVICE_URL_FORMAT "purchaseservice.svc" + +// This is used by the manifest file +#define GS_D2G_MANIFEST_MAX_XML_LEN (1024) + +// Private data to help match async requests with callbacks +typedef struct D2GIRequestData +{ + D2GIInstance* mInstance; + D2GICatalog* mCatalog; + void* mUserData; // data from the developer, to be passed through + union + { + D2GLoadCatalogItemsByCategoryCallback mGetItemsByCategoryCallback; +// D2GGetCategoriesCallback mGetCategoriesCallback; + D2GLoadCatalogItemsCallback mLoadCatalogItemsCallback; + D2GGetUserCreditCardsCallback mGetUserCreditCardsCallback; + D2GGetOrderTotalCallback mGetOrderTotalCallback; + D2GStartOrderCallback mBeginPurchaseCallback; + D2GGetStoreAvailabilityCallback mGetStoreAvailCallback; + D2GIsOrderCompleteCallback mIsOrderCompleteCallback; + D2GGetPurchaseHistoryCallback mGetPurchaseHistoryCallback; + D2GLoadExtraCatalogInfoCallback mGetExtraInfoCallback; + D2GGetItemActivationDataCallback mGetActivationDataCallback; + D2GCheckContentUpdatesCallback mCheckContentUpdatesCallback; + } mUserCallback; + + D2GDownloadProgressCallback mProgressCallback; + D2GDownloadCompleteCallback mCompleteCallback; + const gsi_char *mDownloadLocation; +} D2GIRequestData; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// Internal service functions +GSResult d2giServiceLoadCatalogItems (D2GIInstance *theInstance, + D2GICatalog *theCatalog, + D2GLoadCatalogItemsCallback callback, + void *userData); + +GSResult d2giServiceGetUserCreditCards(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + gsi_bool validOnly, + D2GGetUserCreditCardsCallback callback, + void *userData); +// +// GSResult d2giServiceGetCategories(D2GIInstance *theInstance, +// D2GICatalog *theCatalog, +// D2GGetCategoriesCallback callback, +// void *userData); + +GSResult d2giServiceGetItemsByCategory(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + const UCS2String theCategory, + D2GLoadCatalogItemsByCategoryCallback usercallback, + void *userData); + +GSResult d2giServiceGetOrderTotal(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + gsi_u32 accountId, + D2GItemId *itemIds, + gsi_u32 itemCount, + gsi_u32 *itemQuantities, + D2GGetOrderTotalCallback callback, + void *userData); + +GSResult d2giServicePurchaseItems(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + const D2GOrderTotal *theOrderTotal, + D2GStartOrderCallback callback, + void *userData); + +GSResult d2giServiceIsOrderComplete(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + const D2GOrderPurchase *theOrderPurchase, + D2GIsOrderCompleteCallback callback, + void *userData); + +GSResult d2giServiceGetStoreAvail(D2GIInstance *theinstance, + D2GICatalog *theCatalog, + D2GGetStoreAvailabilityCallback callback, + void *userData); + +GSResult d2giServiceGetDownloadInfo(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + D2GFileId fileId, + const gsi_char *downloadLocation, + D2GDownloadProgressCallback progressCallback, + D2GDownloadCompleteCallback completeCallback, + void *userData); + +GSResult d2giServiceGetPurchaseHistory(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + D2GGetPurchaseHistoryCallback callback, + void *userData); + +GSResult d2giServiceLoadExtraInfo(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + D2GLoadExtraCatalogInfoCallback callback, + void *userData ); + +GSResult d2giServiceGetItemActivationData(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + D2GGetItemActivationDataCallback callback, + D2GPackageId thePackageId, + void *userData); + +GSResult d2giServiceCheckContentUpdates(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + gsi_bool requiredOnly, + D2GCheckContentUpdatesCallback callback, + void *userData); + + +// Data functions +GSResult d2giFreeGetItemsByCategoryResponse(D2GIInstance *theInstance, + D2GLoadCatalogItemsByCategoryResponse *response, + GSResult result, + gsi_bool freeItem); + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif //__D2GSERVICES_H__ diff --git a/Direct2Game/d2gUtil.c b/Direct2Game/d2gUtil.c new file mode 100644 index 00000000..b6ce0fbe --- /dev/null +++ b/Direct2Game/d2gUtil.c @@ -0,0 +1,2444 @@ +/////////////////////////////////////////////////////////////////////////////// +// GameSpy Direct2Game SDK +// This file is part of the Direct to Game SDK designed and developed by +// GameSpy Tech. +// Copyright (c) 2008, GameSpy Technology +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// includes +#include "../common/gsResultCodes.h" +#include "../common/gsAvailable.h" +#include "Direct2Game.h" +#include "d2gMain.h" +#include "d2gServices.h" +#include "d2gUtil.h" +#include "d2gDeserialize.h" +#include "d2gDownloads.h" +#include +#include "stddef.h" + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giResultFromStoreAvailabilityCode +// +GSResult d2giResultFromStoreAvailabilityCode(int availableCode) +{ + GSResult result; + switch(availableCode) + { + case 10: + { + result = GS_D2G_SUCCESS(GSResultSection_SdkSpecifc, D2GResultCode_StoreOnline); + break; + } + case 20: + { + result = GS_D2G_SUCCESS(GSResultSection_SdkSpecifc, D2GResultCode_StoreOfflineForMaintenance); + break; + } + case 50: + { + result = GS_D2G_SUCCESS(GSResultSection_SdkSpecifc, D2GResultCode_StoreOfflineRetired); + break; + } + case 100: + { + result = GS_D2G_SUCCESS(GSResultSection_SdkSpecifc, D2GResultCode_StoreNotYetLaunched); + break; + } + default: + if (((~GS_RESULT_CODEBITS)&availableCode) == 0) + { + // report the error code as it is. + result = GS_RESULT(0, GSResultSDK_Unknown, GSResultSection_NULL, availableCode); + } + else + { + result = GS_D2G_SUCCESS(GSResultSection_NULL, GSResultCode_UnknownError); + } + } + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void d2giSetServiceUrl(D2GIInstance *theInstance, + gsi_bool secure, + const char *service) +{ + +#ifdef UNISPY_FORCE_IP + if (theInstance->mServiceURLBase == NULL) + { + // allocate memory and initialize it + theInstance->mServiceURLBase = gsimalloc(GS_D2G_MAX_BASE_URL_LEN); + snprintf(theInstance->mServiceURLBase, GS_D2G_MAX_BASE_URL_LEN, "%s.%s/commerce/1.1", __GSIACGamename, UNISPY_FORCE_IP); + } +#else + if (theInstance->mServiceURLBase == NULL) + { + // allocate memory and initialize it + theInstance->mServiceURLBase = gsimalloc(GS_D2G_MAX_BASE_URL_LEN); +#ifdef GS_D2G_STAGE + snprintf(theInstance->mServiceURLBase, GS_D2G_MAX_BASE_URL_LEN, GS_D2G_SERVICE_URL_BASE); +#else + snprintf(theInstance->mServiceURLBase, GS_D2G_MAX_BASE_URL_LEN, GS_D2G_SERVICE_URL_BASE, __GSIACGamename); +#endif + } +#endif + + if (theInstance->mServiceURL == NULL) + { + theInstance->mServiceURL = gsimalloc(GS_D2G_MAX_URL_LEN); + } + + if (secure) + snprintf(theInstance->mServiceURL, GS_D2G_MAX_URL_LEN, "%s%s%s", GS_D2G_SECURE_HTTP, theInstance->mServiceURLBase, service); + else + snprintf(theInstance->mServiceURL, GS_D2G_MAX_URL_LEN, "%s%s%s", GS_D2G_HTTP, theInstance->mServiceURLBase, service); + +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giResultFromStatusCode(int statusCode) +{ + GSResult result; + switch(statusCode) + { + case -1: + result = GS_D2G_ERROR(GSResultSection_Network, GSResultCode_ServiceUninitialized); + break; + case 0: + result = GS_D2G_SUCCESS(GSResultSection_NULL,GSResultCode_Success); + break; + case 1: + result = GS_D2G_ERROR(GSResultSection_Network, GSResultCode_ServerError); + break; + case 2: + result = GS_D2G_ERROR(GSResultSection_Network, GSResultCode_ServerError); + break; + case 3: + result = GS_D2G_ERROR(GSResultSection_Account, GSResultCode_InvalidCertificate); + break; + case 101: + result = GS_D2G_ERROR(GSResultSection_Account, GSResultCode_AccountNotFound); + break; + case 102: + result = GS_D2G_ERROR(GSResultSection_Account, GSResultCode_UnknownError); + break; + case 201: + result = GS_D2G_ERROR(GSResultSection_NULL, GSResultCode_GameNotFound); + break; + case 202: + result = GS_D2G_ERROR(GSResultSection_NULL, GSResultCode_NotFound); + break; + case 203: + result = GS_D2G_SUCCESS(GSResultSection_NULL, GSResultCode_Success); + break; + case 301: + result = GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_InsufficientFunds); + break; + case 302: + result = GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_NoPaymentMethodFound); + break; + default: + // unsupported error code + if (((~GS_RESULT_CODEBITS)&statusCode) == 0) + { + // report the error code as it is. + result = GS_ERROR(GSResultSDK_Unknown, GSResultSection_NULL, statusCode); + } + else + { + result = GS_D2G_ERROR(GSResultSection_NULL, GSResultCode_UnknownError); + } + break; + }; + + return result; +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giResultFromHttpResult(GHTTPResult result) +{ + switch(result) + { + case GHTTPSuccess: + return GS_D2G_SUCCESS(GSResultSection_NULL, GSResultCode_Success); + case GHTTPOutOfMemory: + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + case GHTTPSocketFailed: + return GS_D2G_ERROR(GSResultSection_Network, GSResutlCode_SocketError); + case GHTTPFileWriteFailed: + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_FileWriteError); + case GHTTPFileToBig: + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_FileTooLarge); + case GHTTPForbidden: + return GS_D2G_ERROR(GSResultSection_Network, GSResultCode_HttpForbidden); + default: + // unsupported error code + if (((~GS_RESULT_CODEBITS)&result) == 0) + { + // report the error code as it is. + return GS_ERROR(GSResultSDK_Unknown, GSResultSection_Network, result); + } + else + { + return GS_D2G_ERROR(GSResultSection_Network, GSResultCode_HttpError); + } + } +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giResultFromHttpRequest(GHTTPRequest request) +{ + switch(request) + { + case 0: + return GS_D2G_SUCCESS(GSResultSection_NULL, GSResultCode_Success); + + case GHTTPInvalidURL: + return GS_D2G_ERROR(GSResultSection_Network, GSResultCode_BadUrl); + + case GHTTPInvalidFileName: + return GS_D2G_ERROR(GSResultSection_File, GSResultCode_InvalidParameters); + + case GHTTPInsufficientMemory: + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + + case GHTTPInvalidPost: + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_HttpError); + + case GHTTPFailedToOpenFile: + return GS_D2G_ERROR(GSResultSection_File, GSResultCode_FileFailedToOpen); + + default: + // unsupported error code + if (((~GS_RESULT_CODEBITS)&request) == 0) + { + // report the error code as it is. + return GS_ERROR(GSResultSDK_Unknown, GSResultSection_NULL, request); + } + else + { + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, GSResultCode_UnknownError); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giOrderValidationResultFromCode(int orderCode) +{ + switch(orderCode) + { + case 1: + return GS_D2G_SUCCESS(GSResultSection_SdkSpecifc, D2GResultCode_Order_Ok); + case 20: + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_Order_OrderInitiateFailed); + case 40: + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_Order_UserNotAuthorized); + case 41: + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_Order_UserNotFound); + case 42: + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_Order_UserAccountNotFound); + case 60: + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_Order_BillingFailed); + case 61: + return GS_D2G_SUCCESS(GSResultSection_SdkSpecifc, D2GResultCode_Order_BillingPending); + case 62: + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_Order_BillingRequestNotFound); + case 63: + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_Order_InsufficientFunds); + case 80: + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_Order_OrderItemFailed); + case 100: + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_Order_RequiredFieldNotSet); + case 1000: + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_Order_UnexepectedError); + default: + // Note the change from success to error. + if (((~GS_RESULT_CODEBITS)&orderCode) == 0) + { + // report the error code as it is. + return GS_ERROR(GSResultSDK_Unknown, GSResultSection_NULL, orderCode); + } + else + { + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_Order_UnknownResultCode); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giOrderItemValidationResultFromCode(int orderItemCode) +{ + switch(orderItemCode) + { + case 1: + return GS_D2G_SUCCESS(GSResultSection_SdkSpecifc, D2GResultCode_OrderItem_Ok); + case 10: + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_OrderItem_CountryRestriction); + case 11: + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_OrderItem_AgeRestriction); + case 12: + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_OrderItem_QuantityRestriction); + case 22: + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_OrderItem_VariantDoesNotExist); + case 23: + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_OrderItem_ItemDoesNotExist); + case 29: + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_OrderItem_UnspecificedRestriction); + case 30: + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_OrderItem_ItemNotAuthorized); + case 31: + return GS_D2G_SUCCESS(GSResultSection_SdkSpecifc, D2GResultCode_OrderItem_PriceChanged); + case 32: + return GS_D2G_SUCCESS(GSResultSection_SdkSpecifc, D2GResultCode_OrderItem_ItemRefunded); + case 1000: + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_Order_UnexepectedError); + default: + if (((~GS_RESULT_CODEBITS)&orderItemCode) == 0) + { + // report the error code as it is. + return GS_ERROR(GSResultSDK_Unknown, GSResultSection_NULL, orderItemCode); + } + else + { + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_OrderItem_UnknownResultCode); + } + } +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +GSResult d2giResultFromDownloadStatusCode(int statusCode) +{ + switch(statusCode) + { + case 1: + return GS_D2G_SUCCESS(GSResultSection_SdkSpecifc, GSResultCode_Success); + case 220: + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_File_NotOwnedByUser); + default: + if (statusCode>>GS_RESULT_CODEBITS == 0) + { + // report the error code as it is. + return GS_ERROR(GSResultSDK_Unknown, GSResultSection_NULL, statusCode); + } + else + { + return GS_D2G_ERROR(GSResultSection_SdkSpecifc, D2GResultCode_OrderItem_UnknownResultCode); + } + + } + +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// Order total request will have to write the items requested into the soap request payload +gsi_bool d2giWriteCachedItemsToXml(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamWriter writer, + gsi_u32 accountId, + UCS2String cultureCode, + UCS2String currencyCode, + gsi_u32 *itemIds, + int itemCount, + gsi_u32 *itemQuantities) +{ + int i; + + GS_ASSERT(theInstance); + GS_ASSERT(itemIds); + GS_ASSERT(writer); + GS_ASSERT(itemCount); + GS_ASSERT(itemQuantities); + GS_ASSERT(currencyCode); + GS_ASSERT(currencyCode); + + if (gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, "order")) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "accountid", accountId)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer, GS_D2G_NAMESPACE, "culturecode", cultureCode)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer, GS_D2G_NAMESPACE, "currencycode", currencyCode)) || + gsi_is_false(gsXmlWriteStringElement(writer, GS_D2G_NAMESPACE, "subtotal", "0")) || + gsi_is_false(gsXmlWriteStringElement(writer, GS_D2G_NAMESPACE, "tax", "0")) || + gsi_is_false(gsXmlWriteStringElement(writer, GS_D2G_NAMESPACE, "total", "0")) || + //gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "orderitemcount", itemCount)) + gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, "orderitems")) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, GS_D2G_COUNT_ELEMENT, itemCount))) + { + return gsi_false; + } + + for (i = 0; i < itemCount; i++) + { + D2GCatalogItem *itemToSerialize; + + if (gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, "orderitem"))) + { + return gsi_false; + } + + itemToSerialize = d2giGetCatalogItem(theInstance, theCatalog, itemIds[i]); + if (itemToSerialize == NULL) + return gsi_false; + + if (gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "itemid", itemToSerialize->mItem.mItemId)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer, GS_D2G_NAMESPACE, "externalid", itemToSerialize->mItem.mExternalItemCode)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer, GS_D2G_NAMESPACE, "name", itemToSerialize->mItem.mName)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer, GS_D2G_NAMESPACE, "price", itemToSerialize->mItem.mPrice)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer, GS_D2G_NAMESPACE, "tax", itemToSerialize->mItem.mTax)) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "quantity", itemQuantities[i])) || + gsi_is_false(gsXmlWriteStringElement(writer, GS_D2G_NAMESPACE, "subtotal", "0")) || + gsi_is_false(gsXmlWriteStringElement(writer, GS_D2G_NAMESPACE, "total", "0"))) + { + return gsi_false; + } + if (gsi_is_false(gsXmlWriteCloseTag(writer, GS_D2G_NAMESPACE, "orderitem"))) + { + return gsi_false; + } + } + if (gsi_is_false(gsXmlWriteCloseTag(writer, GS_D2G_NAMESPACE, "orderitems"))) + { + return gsi_false; + } + if (gsi_is_false(gsXmlWriteCloseTag(writer, GS_D2G_NAMESPACE, "order"))) + { + return gsi_false; + } + return gsi_true; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2giWriteOrderTotalToXml +// Order total request will have to write the items requested into +// the soap request payload +// +gsi_bool d2giWriteOrderTotalToXml(D2GIInstance *theInstance, + GSXmlStreamWriter writer, + const D2GOrderTotal *theOrderTotal) +{ + gsi_u32 i; + + GS_ASSERT(theInstance); + GS_ASSERT(writer); + GS_ASSERT(theOrderTotal); + + if (gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, "order")) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "accountid", theOrderTotal->mOrder.mAccountId)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer, GS_D2G_NAMESPACE, "culturecode", theOrderTotal->mOrder.mGeoInfo.mCultureCode)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer, GS_D2G_NAMESPACE, "currencycode", theOrderTotal->mOrder.mGeoInfo.mCurrencyCode)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer, GS_D2G_NAMESPACE, "subtotal", theOrderTotal->mOrder.mSubTotal)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer, GS_D2G_NAMESPACE, "tax", theOrderTotal->mOrder.mTax)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer, GS_D2G_NAMESPACE, "total", theOrderTotal->mOrder.mTotal)) || + gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, "orderitems")) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, GS_D2G_COUNT_ELEMENT, theOrderTotal->mOrderItemList.mCount))) + { + return gsi_false; + } + + for (i = 0; i < theOrderTotal->mOrderItemList.mCount; i++) + { + D2GOrderItem *itemToSerialize = &theOrderTotal->mOrderItemList.mOrderItems[i]; + + if (gsi_is_false(gsXmlWriteOpenTag(writer, GS_D2G_NAMESPACE, "orderitem"))) + { + return gsi_false; + } + + if (itemToSerialize == NULL) + return gsi_false; + + if (gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "itemid", itemToSerialize->mItem.mItemId)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer, GS_D2G_NAMESPACE, "externalid", itemToSerialize->mItem.mExternalItemCode)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer, GS_D2G_NAMESPACE, "name", itemToSerialize->mItem.mName)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer, GS_D2G_NAMESPACE, "price", itemToSerialize->mItem.mPrice)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer, GS_D2G_NAMESPACE, "tax", itemToSerialize->mItem.mTax)) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "quantity", itemToSerialize->mItemTotal.mQuantity)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer, GS_D2G_NAMESPACE, "subtotal", itemToSerialize->mItemTotal.mSubTotal)) || + gsi_is_false(gsXmlWriteUnicodeStringElement(writer, GS_D2G_NAMESPACE, "total", itemToSerialize->mItemTotal.mTotal))) + { + return gsi_false; + } + + if (gsi_is_false(gsXmlWriteCloseTag(writer, GS_D2G_NAMESPACE, "orderitem"))) + { + return gsi_false; + } + } + + if (gsi_is_false(gsXmlWriteCloseTag(writer, GS_D2G_NAMESPACE, "orderitems")) || + gsi_is_false(gsXmlWriteCloseTag(writer, GS_D2G_NAMESPACE, "order"))) + { + return gsi_false; + } + GSI_UNUSED(theInstance); + return gsi_true; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Catalog Helper Functions +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2giNewCatalogItem +// This is a helper function, given an item id, it returns a pointer to +// the new item's location in the catalog. +// +D2GCatalogItem * d2giNewCatalogItem(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + D2GItemId itemId) +{ + D2GCatalogItem item; + + GS_ASSERT(theInstance != NULL); + GS_ASSERT(theCatalog != NULL); + if (theInstance == NULL || theCatalog == NULL) + return NULL; + + memset(&item, 0, sizeof(D2GCatalogItem)); + + item.mItem.mItemId = itemId; + ArrayAppend(theCatalog->mItemArray, &item); + + return (D2GCatalogItem *)ArrayNth(theCatalog->mItemArray, ArrayLength(theCatalog->mItemArray)-1); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2giGetCatalogItem +// This is a helper function, given an item id, +// to retrieve a catalog item from the catalog saved in the cache. +// +D2GCatalogItem * d2giGetCatalogItem(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + gsi_u32 itemid) +{ + int index=0; + for (index=0; index < ArrayLength(theCatalog->mItemArray); index++) + { + D2GCatalogItem * info = (D2GCatalogItem *)ArrayNth(theCatalog->mItemArray, index); + if (info->mItem.mItemId == itemid) + return info; + } + GSI_UNUSED(theInstance); + return NULL; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giFreeCatalogItem +// This is a helper function to remove a catalog item +// from the catalog saved in the cache. +// +GSResult d2giFreeCatalogItem(D2GICatalog *theCatalog, + D2GCatalogItem *item) +{ + int i=0; + GS_ASSERT(theCatalog != NULL); + GS_ASSERT(theCatalog->mItemArray != NULL); + if (theCatalog == NULL || theCatalog->mItemArray == NULL || ArrayLength(theCatalog->mItemArray) == 0) + { + GS_D2G_LOG(GSIDebugType_State, GSIDebugLevel_WarmError, "The ItemArray is null or is empty."); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + if (item == NULL) + { + GS_D2G_LOG(GSIDebugType_State,GSIDebugLevel_WarmError, "Called with a null item."); + return GS_SUCCESS; + } + + for (i=0; i < ArrayLength(theCatalog->mItemArray); i++) + { + D2GCatalogItem * elem = (D2GCatalogItem *) ArrayNth(theCatalog->mItemArray, i); + if (elem->mItem.mItemId == item->mItem.mItemId) + { + ArrayDeleteAt(theCatalog->mItemArray, i); + break; + } + } + + return GS_SUCCESS; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giResetCatalogItem +// Clears the contents of a given catalog item. +// +GSResult d2giResetCatalogItem(D2GCatalogItem * catalogItem) +{ + gsi_u32 i; + GS_ASSERT(catalogItem != NULL); + if (catalogItem == NULL) + { + GS_D2G_LOG(GSIDebugType_Misc,GSIDebugLevel_HotError,"Received NULL input parameter."); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_ArgumentNull); + } + + // Free D2GBasicItemInfo Contents + gsifree(catalogItem->mItem.mExternalItemCode); + gsifree(catalogItem->mItem.mName); + gsifree(catalogItem->mItem.mPrice); + gsifree(catalogItem->mItem.mTax); + catalogItem->mItem.mItemId = 0; + catalogItem->mItem.mExternalItemCode = NULL; + catalogItem->mItem.mName = NULL; + catalogItem->mItem.mPrice = NULL; + catalogItem->mItem.mTax = NULL; + + // Free D2GGeoInfo + gsifree(catalogItem->mGeoInfo.mCultureCode); + gsifree(catalogItem->mGeoInfo.mCurrencyCode); + catalogItem->mGeoInfo.mCultureCode = NULL; + catalogItem->mGeoInfo.mCurrencyCode = NULL; + + // Free D2GProductInfo Contents + catalogItem->mProductInfo.mReleaseDate = 0; + gsifree(catalogItem->mProductInfo.mPublisher); + gsifree(catalogItem->mProductInfo.mDeveloper); + gsifree(catalogItem->mProductInfo.mSummary); + + catalogItem->mProductInfo.mPublisher = NULL; + catalogItem->mProductInfo.mDeveloper = NULL; + catalogItem->mProductInfo.mSummary = NULL; + + // Free D2GImageList Contents + for(i = 0; i < catalogItem->mImages.mCount; i++) + { + gsifree(catalogItem->mImages.mImageName[i]); + catalogItem->mImages.mImageName[i] = NULL; + } + gsifree(catalogItem->mImages.mImageName); + catalogItem->mImages.mImageName = NULL; + catalogItem->mImages.mCount = 0; + + // Free D2GCategoryList Contents + for(i = 0; i < catalogItem->mCategories.mCount; i++) + { + gsifree(catalogItem->mCategories.mCategoryNames[i]); + catalogItem->mCategories.mCategoryNames[i] = NULL; + } + gsifree(catalogItem->mCategories.mCategoryNames); + catalogItem->mCategories.mCategoryNames = NULL; + catalogItem->mCategories.mCount = 0; + + for (i = 0; i < catalogItem->mExtraItemInfoList.mCount; i++) + { + gsifree(catalogItem->mExtraItemInfoList.mExtraInfoElements[i].mKey); + gsifree(catalogItem->mExtraItemInfoList.mExtraInfoElements[i].mValue); + } + + gsifree(catalogItem->mExtraItemInfoList.mExtraInfoElements); + + return GS_SUCCESS; +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2giFreeCatalogItems +// Deletes the contents of a given catalog. +// +GSResult d2giFreeCatalogItems(D2GICatalog *theCatalog, + D2GCatalogItemList *itemList, + GSResult result, + gsi_bool freeItem) +{ + gsi_u32 i; + + if (itemList == NULL) + return result; + + for (i = 0; i < itemList->mCount; i++) + { + if (freeItem) + { + d2giFreeCatalogItem(theCatalog, &itemList->mCatalogItems[i]); + } + } + + if (itemList->mCount > 0) + { + gsifree(itemList->mCatalogItems); + itemList->mCatalogItems = NULL; + itemList->mCount = 0; + } + + gsifree(itemList); + return result; +} + +void d2giSwap(void *ptr1, void *ptr2, size_t size) +{ + unsigned char *ptrTmp = gsimalloc(size); + memcpy(ptrTmp, (unsigned char*) ptr1, size); + memcpy((unsigned char*) ptr1, (unsigned char*) ptr2, size); + memcpy((unsigned char*) ptr2, ptrTmp, size); + gsifree(ptrTmp); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2giCompareU32 +// +gsi_bool d2giCompareU32(const void *ptrA, + const void *ptrB, + D2GSortDirection direction) +{ + gsi_u32 valueA = *(gsi_u32 *)ptrA; + gsi_u32 valueB = *(gsi_u32 *)ptrB; + if (direction == D2GSort_Ascending) + { + return (valueA <= valueB); + } + else + { + return (valueA >= valueB); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2giCompareInt +// +gsi_bool d2giCompareInt(const void *ptrA, + const void *ptrB, + D2GSortDirection direction) +{ + int valueA = *((int *)ptrA); + int valueB = *((int *)ptrB); + if (direction == D2GSort_Ascending) + { + return (valueA <= valueB); + } + else + { + return (valueA >= valueB); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2giCompareWNum +// +gsi_bool d2giCompareWFloat(const void *ptrA, + const void *ptrB, + D2GSortDirection direction) +{ + + if (direction == D2GSort_Ascending) + { + return (gsiWStringToDouble(*(UCS2String *) ptrA) <= gsiWStringToDouble(*(UCS2String *)ptrB)); + } + else + { + return (gsiWStringToDouble(*(UCS2String *) ptrA) >= gsiWStringToDouble(*(UCS2String *)ptrB)); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2giCompareWStr +// +gsi_bool d2giCompareWStr(const void *ptrA, + const void *ptrB, + D2GSortDirection direction) +{ + if (direction == D2GSort_Ascending) + { + return (wcscmp(*(UCS2String *)ptrA, *(UCS2String *) ptrB) <= 0); + } + else + { + return (wcscmp(*(UCS2String *)ptrA,*(UCS2String *) ptrB) >= 0); + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2giCompareTime +// +gsi_bool d2giCompareTime(const void *ptrA, + const void *ptrB, + D2GSortDirection direction) +{ + time_t valueA = *(time_t *)ptrA; + time_t valueB = *(time_t *)ptrB; + + if (direction == D2GSort_Ascending) + { + return (valueA <= valueB); + } + else + { + return (valueA >= valueB); + } +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2giQSort +// +void d2giQSort(void *list, + size_t size, + unsigned int offset, + D2GSortDirection direction, + int left, + int right, + compareFunc compareFunction) +{ + int pivotIndex, l_hold, r_hold; + + l_hold = left; + r_hold = right; + pivotIndex = left; + + while (left < right) + { + + if (direction == D2GSort_Ascending) + { + while ((left <= right) && (*compareFunction)((unsigned char*)list+left*size+offset, + (unsigned char*)list+pivotIndex*size+offset, + direction)) + left++; + while ((left <= right) && (*compareFunction)((unsigned char*)list+pivotIndex*size+offset, + (unsigned char*)list+right*size+offset, + direction )) + right--; + + } + else + { + while ((left <= right) &&(*compareFunction)((unsigned char*)list+left*size+offset, + (unsigned char*)list+pivotIndex*size+offset, + direction)) + left++; + while ((left <= right) &&(*compareFunction)((unsigned char*)list+pivotIndex*size+offset, + (unsigned char*)list+right*size+offset, + direction)) + right--; + } + + if (left < right) + { + d2giSwap(((unsigned char*)list+left*size), ((unsigned char*)list+right*size), size); + } + } + if (pivotIndex != right) + { + d2giSwap(((unsigned char*)list+pivotIndex*size), ((unsigned char*)list+right*size), size); + } + pivotIndex = right; + if (l_hold < (pivotIndex-1)) + d2giQSort(list, size, offset, direction, l_hold, pivotIndex-1, compareFunction); + if (r_hold > (pivotIndex+1)) + d2giQSort(list, size, offset, direction, pivotIndex+1, r_hold, compareFunction); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giSortCatalogItemsbyName +// +GSResult d2giSortCatalogItemsbyName(D2GCatalogItemList *list, + D2GSortDirection direction) +{ + int offset = offsetof(D2GCatalogItem, mItem.mName); + d2giQSort(&list->mCatalogItems[0], + sizeof(D2GCatalogItem), + offset, + direction, + 0, + (list->mCount-1), + &d2giCompareWStr); + + return GS_SUCCESS; +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giSortCatalogItemsbyPrice +// +GSResult d2giSortCatalogItemsbyPrice(D2GCatalogItemList *list, + D2GSortDirection direction) +{ + int offset = offsetof(D2GCatalogItem, mItem.mPrice); + d2giQSort(&list->mCatalogItems[0], + sizeof(D2GCatalogItem), + offset, + direction, + 0, + (list->mCount-1), + &d2giCompareWFloat); + + return GS_SUCCESS; +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giSortCatalogItemsbyDate +// +GSResult d2giSortCatalogItemsbyDate(D2GCatalogItemList *list, + D2GSortDirection direction) +{ + int offset = offsetof(D2GCatalogItem, mProductInfo.mReleaseDate); + d2giQSort(&list->mCatalogItems[0], + sizeof(D2GCatalogItem), + offset, + direction, + 0, + (list->mCount-1), + &d2giCompareTime); + + return GS_SUCCESS; +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giSortCatalogItemsbyID +// +GSResult d2giSortCatalogItemsbyID(D2GCatalogItemList *list, + D2GSortDirection direction) +{ + int offset = offsetof(D2GCatalogItem, mItem.mItemId); + + d2giQSort(&list->mCatalogItems[0], + sizeof(D2GCatalogItem), + offset, + direction, + 0, + (list->mCount-1), + &d2giCompareU32); + + return GS_SUCCESS; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giSortCatalogItemsbyExtId +// +GSResult d2giSortCatalogItemsbyExtId(D2GCatalogItemList *list, + D2GSortDirection direction) +{ + int offset = offsetof(D2GCatalogItem, mItem.mExternalItemCode); + d2giQSort(&list->mCatalogItems[0], + sizeof(D2GCatalogItem), + offset, + direction, + 0, + (list->mCount-1), + &d2giCompareWStr); + + return GS_SUCCESS; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giSortCatalogItemsbySize +// +GSResult d2giSortCatalogItemsbySize(D2GCatalogItemList *list, + D2GSortDirection direction) +{ + int offset = offsetof(D2GCatalogItem, mProductInfo.mFileSize); + d2giQSort(&list->mCatalogItems[0], + sizeof(D2GCatalogItem), + offset, + direction, + 0, + (list->mCount-1), + &d2giCompareU32); + + return GS_SUCCESS; +} +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// d2giFreeCreditCardInfoList +// Deletes the contents of credit card list in the response. +// +void d2giFreeCreditCardInfoList(D2GCreditCardInfoList *creditCardList) +{ + gsi_u32 i; + + if (creditCardList == NULL) + return; + + for(i = 0; i < creditCardList->mCount; i++) + { + gsifree(creditCardList->mCreditCardInfos[i].mCreditCardType); + } + + gsifree(creditCardList->mCreditCardInfos); + creditCardList->mCount = 0; + creditCardList->mCreditCardInfos=NULL; + gsifree(creditCardList); + GSI_UNUSED(creditCardList); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//d2giFreeOrder +// Frees all contents of the OrderInfo +// +void d2giFreeOrder(D2GOrderInfo *theOrder) +{ + if (theOrder) + { + gsifree(theOrder->mGeoInfo.mCultureCode); + theOrder->mGeoInfo.mCultureCode = NULL; + + gsifree(theOrder->mGeoInfo.mCurrencyCode); + theOrder->mGeoInfo.mCurrencyCode = NULL; + + gsifree(theOrder->mSubTotal); + theOrder->mSubTotal = NULL; + + gsifree(theOrder->mTax); + theOrder->mTax = NULL; + + gsifree(theOrder->mTotal); + theOrder->mTotal = NULL; + + gsifree(theOrder->mRootOrderGuid); + theOrder->mRootOrderGuid = NULL; + + gsifree(theOrder->mValidation.mMessage); + theOrder->mValidation.mMessage = NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giFreeOrderItem +// +void d2giFreeOrderItem(D2GOrderItem *orderItem) +{ + if (orderItem) + { + // Free D2GBasicItemInfo Contents. + gsifree(orderItem->mItem.mExternalItemCode); + gsifree(orderItem->mItem.mName); + gsifree(orderItem->mItem.mPrice); + gsifree(orderItem->mItem.mTax); + + orderItem->mItem.mItemId = 0; + orderItem->mItem.mExternalItemCode = NULL; + orderItem->mItem.mName = NULL; + orderItem->mItem.mPrice = NULL; + orderItem->mItem.mTax = NULL; + + // Free D2GOrderValidation Contents + gsifree(orderItem->mValidation.mMessage); + + orderItem->mValidation.mMessage = NULL; + orderItem->mValidation.mIsValid = gsi_false; + orderItem->mValidation.mResult = 0; + + // Free D2GOrderItemTotal Contents + gsifree(orderItem->mItemTotal.mTotal); + gsifree(orderItem->mItemTotal.mSubTotal); + + orderItem->mItemTotal.mQuantity = 0; + orderItem->mItemTotal.mSubTotal = NULL; + orderItem->mItemTotal.mTotal = NULL; + } +} + +void d2giFreeDownloadItemContents(D2GDownloadItem * downloadItem) +{ + if (downloadItem) + { + downloadItem->mUrlId = 0; + downloadItem->mFileId = 0; + downloadItem->mSequence = 0; + downloadItem->mVersion = 0; + + gsifree(downloadItem->mName); + downloadItem->mName = NULL; + gsifree(downloadItem->mAssetType); + downloadItem->mAssetType = NULL; + } +} + + +void d2giFreeContentUpdate(D2GContentUpdate *downloadRecord) +{ + if (downloadRecord) + { + d2giFreeDownloadItemContents(&downloadRecord->mDownloadItem); + } +} + + +void d2giFreeContentUpdateList(D2GContentUpdateList *downloadList) +{ + gsi_u32 i; + if (downloadList == NULL) + return; + + for (i = 0; i < downloadList->mCount; i++ ) + { + d2giFreeContentUpdate(&downloadList->mContentUpdates[i]); + } + gsifree(downloadList->mContentUpdates); + downloadList->mCount = 0; + downloadList->mContentUpdates = NULL; + gsifree(downloadList); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giFreeOrderItemPurchases +// recursively deletes the purchase list +// +void d2giFreeOrderItemPurchases(D2GOrderItemPurchase *orderItemPurchases, + gsi_u32 orderItemPurchaseCount) +{ + gsi_u32 i; + gsi_u32 j; + + if (orderItemPurchases != NULL) + { + for (i = 0; i < orderItemPurchaseCount; i++) + { + // Free D2GOrderItem + d2giFreeOrderItem(&orderItemPurchases[i].mOrderItem); + + // Free D2GLicenseItemList + for (j = 0; j < orderItemPurchases[i].mLicenseList.mCount; j++) + { + gsifree(orderItemPurchases[i].mLicenseList.mLicenses[j].mLicenseKey); + orderItemPurchases[i].mLicenseList.mLicenses[j].mLicenseKey = NULL; + + gsifree(orderItemPurchases[i].mLicenseList.mLicenses[j].mLicenseName); + orderItemPurchases[i].mLicenseList.mLicenses[j].mLicenseName = NULL; + } + gsifree(orderItemPurchases[i].mLicenseList.mLicenses); + orderItemPurchases[i].mLicenseList.mLicenses = NULL; + orderItemPurchases[i].mLicenseList.mCount = 0; + + // Free D2GDownloadItemList + for (j = 0; j < orderItemPurchases[i].mDownloadList.mCount; j++) + { + d2giFreeDownloadItemContents(&orderItemPurchases[i].mDownloadList.mDownloads[j]); + } + gsifree(orderItemPurchases[i].mDownloadList.mDownloads); + orderItemPurchases[i].mDownloadList.mDownloads = NULL; + orderItemPurchases[i].mDownloadList.mCount = 0; + + // Recursively free the D2GOrderItemPurchaseList + d2giFreeOrderItemPurchases(orderItemPurchases[i].mPurchaseList.mOrderItemPurchases, + orderItemPurchases[i].mPurchaseList.mCount); + } + gsifree(orderItemPurchases); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giFreeOrderTotal +// +void d2giFreeOrderTotal(D2GOrderTotal *orderTotal) +{ + gsi_u32 i; + + if (orderTotal != NULL) + { + d2giFreeOrder(&orderTotal->mOrder); + + for(i = 0; i < orderTotal->mOrderItemList.mCount; i++) + { + d2giFreeOrderItem(&orderTotal->mOrderItemList.mOrderItems[i]); + } + + gsifree(orderTotal->mOrderItemList.mOrderItems); + orderTotal->mOrderItemList.mOrderItems = NULL; + orderTotal->mOrderItemList.mCount = 0 ; + gsifree(orderTotal); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giFreeOrderPurchase +// +void d2giFreeOrderPurchase(D2GOrderPurchase *purchase) +{ + if (purchase) + { + d2giFreeOrder(&purchase->mOrder); + + d2giFreeOrderItemPurchases(purchase->mItemPurchases.mOrderItemPurchases, + purchase->mItemPurchases.mCount); + + purchase->mItemPurchases.mOrderItemPurchases = NULL; + purchase->mItemPurchases.mCount = 0; + gsifree(purchase); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giFreeOrderPurchase +// +void d2giFreeOrderPurchaseContents(D2GOrderPurchase *purchase) +{ + if (purchase) + { + d2giFreeOrder(&purchase->mOrder); + + d2giFreeOrderItemPurchases(purchase->mItemPurchases.mOrderItemPurchases, + purchase->mItemPurchases.mCount); + + purchase->mItemPurchases.mOrderItemPurchases = NULL; + purchase->mItemPurchases.mCount = 0; + } +} + + +void d2giFreePurchaseHistory(D2GPurchaseHistory *purchaseHistory) +{ + gsi_u32 i; + if (purchaseHistory == NULL) + return; + + // cleanup each purchase in the list + for (i = 0 ; i < purchaseHistory->mCount; i++) + { + d2giFreeOrderPurchaseContents(&purchaseHistory->mPurchases[i]); + } + + // cleanup the rest of the structure + gsifree(purchaseHistory->mPurchases); + purchaseHistory -> mPurchases = NULL; + purchaseHistory -> mCount = 0; + gsifree(purchaseHistory); +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giGetAllItemsFromCache +// Used to retrieve the catalog items from the cache. The result is +// returned by the callback +// +GSResult d2giGetAllItemsFromCache(D2GICatalog *theCatalog, + D2GLoadCatalogItemsCallback callback, + void *userData ) +{ + D2GLoadCatalogItemsResponse aResponse; + GSResult result = GS_SUCCESS; + + aResponse.mHttpResult = GHTTPSuccess; + aResponse.mItemList = (D2GCatalogItemList *)gsimalloc(sizeof(D2GCatalogItemList)); + if (aResponse.mItemList == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_HotError, + "%s :: %s (%d): Out of memory during allocation of aResponse.mItemList\n" + __FILE__, __FUNCTION__, __LINE__); + result = GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + else + { + aResponse.mItemList->mCount = ArrayLength(theCatalog->mItemArray); + aResponse.mItemList->mCatalogItems = NULL; + + if (aResponse.mItemList->mCount) + { + gsi_u32 i; + + aResponse.mItemList->mCatalogItems = (D2GCatalogItem *)gsimalloc(sizeof(D2GCatalogItem) * aResponse.mItemList->mCount); + if (aResponse.mItemList->mCatalogItems == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_HotError, + "%s :: %s (%d): Out of memory during allocation of aResponse.mItemList->mCatalogItems\n" + __FILE__, __FUNCTION__, __LINE__); + result = GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + for (i = 0; i < aResponse.mItemList->mCount; i++) + { + //shallow copy + aResponse.mItemList->mCatalogItems[i] = *(D2GCatalogItem *)ArrayNth(theCatalog->mItemArray, i); + } + } + } + + if (GS_FAILED(result)) + { + d2giFreeCatalogItems(theCatalog, aResponse.mItemList, result, gsi_false); + aResponse.mItemList = NULL; + } + + callback(result, &aResponse, userData); + + return result; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giFindOrAddCategory +// Internal function to add the found categories in the cache to a list. +// +static gsi_bool d2giFindOrAddCategory(UCS2String newCategory, + D2GCategoryList *categoryList) +{ + GSResult result = GS_SUCCESS; + gsi_bool found = gsi_false; + + int upperBound = (int) categoryList->mCount; + int lowerBound = 0; + + while ((lowerBound < upperBound) && !found) + { + UCS2String currentCategory = categoryList->mCategoryNames[(lowerBound + upperBound) / 2]; + + int cmpResult = wcscmp(newCategory,currentCategory); + + if (cmpResult > 0) + { + lowerBound = ((lowerBound + upperBound) / 2) + 1; + } + else if (cmpResult < 0) + { + upperBound = ((lowerBound + upperBound) / 2); + } + else + { + // we found it in the list just return true + found = gsi_true; + } + } + if (!found) + { + if (categoryList->mCountmCount>lowerBound) + { + memmove(((categoryList->mCategoryNames)+lowerBound+1), + ((categoryList->mCategoryNames)+lowerBound), + (sizeof(UCS2String *)*(categoryList->mCount-lowerBound))); + } + categoryList->mCategoryNames[lowerBound] = newCategory; + categoryList->mCount++; + } + else + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_HotError, + "%s :: %s (%d): Maximum number of categories have been reached : %d\n" + __FILE__, __FUNCTION__, __LINE__, GS_D2G_MAX_CATEGORIES); + result = GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_SizeExceedsMaximum); + + } + } + return result ; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giGetCategoriesFromCache +// Used to retrieve the categories from cache. +// +GSResult d2giGetCategoriesFromCache(D2GICatalog *theCatalog, D2GCategoryList *categoryList ) +{ + GSResult result = GS_SUCCESS; + + D2GCatalogItemList catalogItems; + + // the list is empty + categoryList->mCount = 0; + categoryList->mCategoryNames = NULL; + + catalogItems.mCount = ArrayLength(theCatalog->mItemArray); + catalogItems.mCatalogItems = NULL; + + if (catalogItems.mCount > 0) + { + gsi_u32 i, j; + + categoryList->mCategoryNames = (UCS2String *) gsimalloc(sizeof(UCS2String)*GS_D2G_MAX_CATEGORIES); + memset(categoryList->mCategoryNames, 0, sizeof(UCS2String)*GS_D2G_MAX_CATEGORIES); + + for (i = 0; i < catalogItems.mCount ; i++) + { + D2GCatalogItem *catalogItem; + catalogItem = (D2GCatalogItem *)ArrayNth(theCatalog->mItemArray, i); + for (j = 0; j < catalogItem->mCategories.mCount; j++) + { + result = d2giFindOrAddCategory(catalogItem->mCategories.mCategoryNames[j], categoryList); + if (GS_FAILED(result)) + { + return result; + } + } + } + } + else + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): data not in cache!\n", + __FILE__, __FUNCTION__, __LINE__); + result = GS_D2G_ERROR(GSResultSection_State, D2GResultCode_Catalog_Empty); + } + + return result; +} + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +GSResult d2giCheckItemsQuantities(D2GIInstance *theInstance, + gsi_u32 *itemQuantities, + gsi_u32 itemQuantitiesCount) +{ + gsi_u32 i; + GS_ASSERT(theInstance); + GS_ASSERT(itemQuantities); + GS_ASSERT(itemQuantitiesCount); + + for(i = 0; i < itemQuantitiesCount; i++) + { + if (itemQuantities[i] == 0) + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + GSI_UNUSED(theInstance); + return GS_SUCCESS; +} + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// d2giCloneOrderItem +// makes a deep copy of the Order Item function +// +GSResult d2giCloneOrderItem(D2GOrderItem *dstOrderItem, + D2GOrderItem *srcOrderItem) +{ + GS_ASSERT(srcOrderItem); + GS_ASSERT(dstOrderItem); + + if (srcOrderItem == NULL || dstOrderItem == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State,GSIDebugLevel_HotError, + "%s :: %s (%d): called with null dstOrderItem, srcOrderItem", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + dstOrderItem->mValidation.mIsValid = srcOrderItem->mValidation.mIsValid; + dstOrderItem->mValidation.mResult = srcOrderItem->mValidation.mResult; + + // copy D2GBasicItemInfo + dstOrderItem->mItem.mItemId = srcOrderItem->mItem.mItemId; + + dstOrderItem->mItemTotal.mQuantity = srcOrderItem->mItemTotal.mQuantity; + + GS_ASSERT(srcOrderItem->mValidation.mIsValid || + (!srcOrderItem->mValidation.mIsValid && srcOrderItem->mValidation.mMessage)); + GS_ASSERT(srcOrderItem->mItemTotal.mSubTotal); + GS_ASSERT(srcOrderItem->mItemTotal.mTotal); + GS_ASSERT(srcOrderItem->mItem.mExternalItemCode); + GS_ASSERT(srcOrderItem->mItem.mName); + GS_ASSERT(srcOrderItem->mItem.mPrice); + GS_ASSERT(srcOrderItem->mItem.mTax); + + if (srcOrderItem->mItem.mExternalItemCode == NULL || + srcOrderItem->mItem.mName == NULL || + srcOrderItem->mItem.mPrice == NULL || + srcOrderItem->mItem.mTax == NULL || + srcOrderItem->mItemTotal.mSubTotal == NULL || + srcOrderItem->mItemTotal.mTotal == NULL || + (srcOrderItem->mValidation.mIsValid == gsi_false && srcOrderItem->mValidation.mMessage == NULL)) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): called with null srcOrderItem->mIsValidMsg\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + // Copy D2GBasicItemInfo + dstOrderItem->mItem.mExternalItemCode = goawstrdup(srcOrderItem->mItem.mExternalItemCode); + dstOrderItem->mItem.mName = goawstrdup(srcOrderItem->mItem.mName); + dstOrderItem->mItem.mPrice = goawstrdup(srcOrderItem->mItem.mPrice); + dstOrderItem->mItem.mTax = goawstrdup(srcOrderItem->mItem.mTax); + + // Copy D2GOrderValidation + dstOrderItem->mValidation.mMessage = goawstrdup(srcOrderItem->mValidation.mMessage); + + // Copy D2GOrderItemTotal + dstOrderItem->mItemTotal.mSubTotal = goawstrdup(srcOrderItem->mItemTotal.mSubTotal); + dstOrderItem->mItemTotal.mTotal = goawstrdup(srcOrderItem->mItemTotal.mTotal); + + return GS_SUCCESS; +} + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// d2giCloneDownloadList +// a deep copy function +GSResult d2giCloneDownloadList(D2GDownloadItemList *dstDownloadItemList, + D2GDownloadItemList *srcDownloadItemList) +{ + gsi_u32 i; + + // srcDownloadItemList is checked first because it is the source + GS_ASSERT(srcDownloadItemList); + GS_ASSERT(dstDownloadItemList); + + if (srcDownloadItemList == NULL || dstDownloadItemList == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): called with null dstDownloadItemList or srcDownloadItemList.\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + dstDownloadItemList->mCount = srcDownloadItemList->mCount; + + if (dstDownloadItemList->mCount == 0) + { + dstDownloadItemList->mDownloads = NULL; + return GS_SUCCESS; + } + + dstDownloadItemList->mDownloads = (D2GDownloadItem *)gsimalloc(sizeof(D2GDownloadItem) * dstDownloadItemList->mCount); + if (dstDownloadItemList->mDownloads == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): failed to allocate memory for dstDownloadItemList->mDownloads.\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + for (i = 0; i < dstDownloadItemList->mCount; i++) + { + dstDownloadItemList->mDownloads[i].mUrlId = srcDownloadItemList->mDownloads[i].mUrlId; + dstDownloadItemList->mDownloads[i].mFileId = srcDownloadItemList->mDownloads[i].mFileId; + dstDownloadItemList->mDownloads[i].mSequence = srcDownloadItemList->mDownloads[i].mSequence; + dstDownloadItemList->mDownloads[i].mVersion = srcDownloadItemList->mDownloads[i].mVersion; + + GS_ASSERT(srcDownloadItemList->mDownloads[i].mAssetType); + if (srcDownloadItemList->mDownloads[i].mAssetType == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): called with null mAssetType in srcDownloadItemList at %d.\n", + __FILE__, __FUNCTION__, __LINE__, i); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + dstDownloadItemList->mDownloads[i].mAssetType = goawstrdup(srcDownloadItemList->mDownloads[i].mAssetType); + if (dstDownloadItemList->mDownloads[i].mAssetType == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): failed to allocate mAssetType in dstDownloadItemList at %d.\n", + __FILE__, __FUNCTION__, __LINE__, i); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + + GS_ASSERT(srcDownloadItemList->mDownloads[i].mName); + if (srcDownloadItemList->mDownloads[i].mName == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): called with null mName in srcDownloadItemList at %d.\n", + __FILE__, __FUNCTION__, __LINE__, i); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + dstDownloadItemList->mDownloads[i].mName = goawstrdup(srcDownloadItemList->mDownloads[i].mName); + if (dstDownloadItemList->mDownloads[i].mName == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): failed to allocate mName in dstDownloadItemList at %d.\n", + __FILE__, __FUNCTION__, __LINE__, i); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + } + + return GS_SUCCESS; +} + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// d2giCloneLicenseList +// a deep copy function +// +GSResult d2giCloneLicenseList(D2GLicenseItemList *dstLicenses, + D2GLicenseItemList *srcLicenses) +{ + gsi_u32 i; + GS_ASSERT(srcLicenses); + GS_ASSERT(dstLicenses); + + if (srcLicenses == NULL || dstLicenses == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): called with null dstLicenses or srcLicenses\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + dstLicenses->mCount = srcLicenses->mCount; + + if (dstLicenses->mCount == 0) + { + dstLicenses->mLicenses = NULL; + return GS_SUCCESS; + } + + dstLicenses->mLicenses = (D2GLicenseItem *)gsimalloc(sizeof(D2GLicenseItem) * dstLicenses->mCount); + if (dstLicenses->mLicenses == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): failed to allocate memory for dstLicenses->mLicenses.\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + for (i = 0; i < dstLicenses->mCount; i++) + { + GS_ASSERT(srcLicenses->mLicenses[i].mLicenseKey); + if (srcLicenses->mLicenses[i].mLicenseKey == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): called with null mLicenseKey in srcLicenses at %d.\n", + __FILE__, __FUNCTION__, __LINE__, i); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + dstLicenses->mLicenses[i].mLicenseKey = goawstrdup(srcLicenses->mLicenses[i].mLicenseKey); + if (dstLicenses->mLicenses[i].mLicenseKey == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): failed to allocate mLicenseKey in dstLicenses at %d.\n", + __FILE__, __FUNCTION__, __LINE__, i); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + GS_ASSERT(srcLicenses->mLicenses[i].mLicenseName); + if (srcLicenses->mLicenses[i].mLicenseName == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): called with null mLicenseName in srcLicenses at %d.\n", + __FILE__, __FUNCTION__, __LINE__, i); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + dstLicenses->mLicenses[i].mLicenseName = goawstrdup(srcLicenses->mLicenses[i].mLicenseName); + if (dstLicenses->mLicenses[i].mLicenseName == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): failed to allocate mLicenseName in dstLicenses at %d.\n", + __FILE__, __FUNCTION__, __LINE__, i); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + } + + return GS_SUCCESS; +} + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// d2giCloneOrderItemPurchases +// a deep copy function - recursive +// +GSResult d2giCloneOrderItemPurchases(D2GOrderItemPurchase **destPurchases, + gsi_u32 *dstCount, + D2GOrderItemPurchase *srcPurchases, + gsi_u32 srcCount) +{ + D2GOrderItemPurchase *tempPurchases; + gsi_u32 i, count; + GS_ASSERT(destPurchases); + if (destPurchases == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): called with null destPurchases\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + + count = *dstCount = srcCount; + if (count == 0) + { + *destPurchases = NULL; + return GS_SUCCESS; + } + + tempPurchases = *destPurchases = (D2GOrderItemPurchase *)gsimalloc(sizeof(D2GOrderItemPurchase) * count); + if (tempPurchases == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): failed to allocate memory for tempPurchases.\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + + for (i = 0; i < count; i++) + { + GSResult result; + result = d2giCloneOrderItem(&tempPurchases[i].mOrderItem, &srcPurchases[i].mOrderItem); + if (GS_FAILED(result)) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): Failed to copy order item %d using d2giCloneOrderItem.\n", + __FILE__, __FUNCTION__, __LINE__, i); + return result; + } + + result = d2giCloneDownloadList(&tempPurchases[i].mDownloadList, &srcPurchases[i].mDownloadList); + if (GS_FAILED(result)) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): Failed to copy download list for item %d using d2giCloneDownloadList.\n", + __FILE__, __FUNCTION__, __LINE__, i); + return result; + } + + result = d2giCloneLicenseList(&tempPurchases[i].mLicenseList, &srcPurchases[i].mLicenseList); + if (GS_FAILED(result)) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): Failed to copy license list for item %d using d2giCloneLicenseList.\n", + __FILE__, __FUNCTION__, __LINE__, i); + return result; + } + + result = d2giCloneOrderItemPurchases(&tempPurchases[i].mPurchaseList.mOrderItemPurchases, + &tempPurchases[i].mPurchaseList.mCount, + srcPurchases[i].mPurchaseList.mOrderItemPurchases, + srcPurchases[i].mPurchaseList.mCount); + if (GS_FAILED(result)) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_HotError, + "%s :: %s (%d): Failed to copy subitem list for item %d using d2giCloneOrderItemPurchases.\n", + __FILE__, __FUNCTION__, __LINE__, i); + return result; + } + } + + return GS_SUCCESS; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giGetItemsByCategoryFromCache +// Used to retrieve the catalog items for cache +// +GSResult d2giGetCatalogItemsByCategoryFromCache(D2GICatalog *theCatalog, + const UCS2String theCategory, + D2GLoadCatalogItemsByCategoryCallback callback, + void *userData ) +{ + D2GLoadCatalogItemsByCategoryResponse aResponse; + gsi_u32 itemArrayCount; + GSResult result = GS_SUCCESS; + + memset(&aResponse, 0, sizeof(aResponse)); + aResponse.mHttpResult = GHTTPSuccess; + itemArrayCount = ArrayLength(theCatalog->mItemArray); + + if (itemArrayCount > 0) + { + gsi_u32 i,j; + gsi_u32 foundItems = 0; + D2GCatalogItem **aItemArray; + + // allocate a list of pointers to the CatalogItems cached + aItemArray = (D2GCatalogItem **)gsimalloc(sizeof(D2GCatalogItem *) * itemArrayCount); + memset(aItemArray, 0, sizeof(D2GCatalogItem *)*itemArrayCount); + + // for each catalog item in the cache + for(i = 0; i < itemArrayCount; i++) + { + D2GCatalogItem *anItem = (D2GCatalogItem *)ArrayNth(theCatalog->mItemArray, i); + if (anItem->mCategories.mCount) + { + // there is category attached to the catalog item + for (j = 0; j < anItem->mCategories.mCount; j++) + { + // if it belongs to the category we are interested in + if (wcscmp((wchar_t *)anItem->mCategories.mCategoryNames[j], (wchar_t *)theCategory)==0) + { + // copy the pointer to the item into the pointer list + aItemArray[i] = anItem; + foundItems++; + break; + } + } + } + } + aResponse.mItemList = (D2GCatalogItemList *)gsimalloc(sizeof(D2GCatalogItemList)); + if (aResponse.mItemList == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Memory, GSIDebugLevel_HotError, + "%s :: %s (%d): Out of memory during allocation of aResponse.mItemList\n" + __FILE__, __FUNCTION__, __LINE__); + result = GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + else + { + memset(aResponse.mItemList, 0, sizeof(D2GCatalogItemList)); + + // Now we have list for items belongs to the same category retrieved from the catalog + aResponse.mItemList->mCount = foundItems; + if (aResponse.mItemList->mCount) + { + aResponse.mItemList->mCatalogItems = (D2GCatalogItem *)gsimalloc(sizeof(D2GCatalogItem) * aResponse.mItemList->mCount); + } + + for (j = 0, i = 0; j < foundItems && i < itemArrayCount; i++) + { + if (aItemArray[i]) + { + aResponse.mItemList->mCatalogItems[j++] = *aItemArray[i]; + } + } + } + // free the pointer array, just the container. + gsifree(aItemArray); + } + + if (GS_FAILED(result)) + { + d2giFreeCatalogItems(theCatalog, aResponse.mItemList, result, gsi_false); + aResponse.mItemList = NULL; + } + + // We don't release the mItemList. That's for the developer to choose. + // They can keep it until they need to get rid of it by calling + // the free function for the item. + callback(result, &aResponse, userData); + + // return result as always! + return result; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Extra Info Cache Helper Functions +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giLookUpExtraInfo +// +UCS2String d2giLookUpExtraInfo( D2GIInstance *theInstance, D2GICatalog *theCatalog, const UCS2String key ) +{ + int index=0; + for (index=0; index < ArrayLength(theCatalog->mExtraInfoCache); index++) + { + D2GExtraInfo * info = (D2GExtraInfo *)ArrayNth(theCatalog->mExtraInfoCache, index); + if (wcscmp(info->mKey, key) == 0) + { + return info->mValue; + } + } + GSI_UNUSED(theInstance); + return NULL; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giNewCatalogItem +// This is a helper function, given an item id,it returns a pointer to +// the new item's location in the +// +D2GExtraInfo * d2giNewExtraInfo(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + D2GExtraInfo *theExtraInfo) +{ + + GS_ASSERT(theInstance != NULL); + GS_ASSERT(theCatalog != NULL); + if (theInstance == NULL || theCatalog == NULL) + return NULL; + + ArrayAppend(theCatalog->mExtraInfoCache, theExtraInfo); + + return (D2GExtraInfo *)ArrayNth(theCatalog->mExtraInfoCache, ArrayLength(theCatalog->mExtraInfoCache)-1); +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giGetExtraInfo +// This is a helper function, given a key, to retrieve its value pair +// from the cache +// +D2GExtraInfo * d2giGetExtraInfo(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + UCS2String key) +{ + int index=0; + for (index=0; index < ArrayLength(theCatalog->mExtraInfoCache); index++) + { + D2GExtraInfo * info = (D2GExtraInfo *)ArrayNth(theCatalog->mExtraInfoCache, index); + if (wcscmp(info->mKey, key) == 0) + { + return info; + } + } + GSI_UNUSED(theInstance); + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giFreeExtraInfo +// This is a helper function to remove a Extra Info item from the cache. +// +GSResult d2giFreeExtraInfo(D2GICatalog *theCatalog, + D2GExtraInfo *extraInfo) +{ + int i=0; + + GS_ASSERT(theCatalog != NULL); + GS_ASSERT(theCatalog->mExtraInfoCache != NULL); + if (theCatalog == NULL || theCatalog->mExtraInfoCache == NULL || ArrayLength(theCatalog->mExtraInfoCache) == 0) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): the ItemArray is null or is empty\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_InvalidParameters); + } + if (extraInfo == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_State, GSIDebugLevel_WarmError, + "%s :: %s (%d): called with a null item\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_SUCCESS; + } + + for (i=0; i < ArrayLength(theCatalog->mExtraInfoCache); i++) + { + D2GExtraInfo * elem = (D2GExtraInfo *) ArrayNth(theCatalog->mExtraInfoCache, i); + if (memcmp(elem->mKey,extraInfo->mKey, sizeof(extraInfo->mKey)) == 0) + { + ArrayDeleteAt(theCatalog->mExtraInfoCache, i); + break; + } + } + return GS_SUCCESS; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giResetCatalogItem +// Clears the contents of a given catalog item. +// +GSResult d2giResetExtraInfo(D2GExtraInfo *extraInfo) +{ + GS_ASSERT(extraInfo != NULL); + if (extraInfo == NULL) + { + gsDebugFormat( GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_HotError, + "%s :: %s (%d): received NULL input parameter\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_State, GSResultCode_ArgumentNull); + } + + // Free Contents + gsifree(extraInfo->mKey); + gsifree(extraInfo->mValue); + extraInfo->mKey = NULL; + extraInfo->mValue = NULL; + return GS_SUCCESS; +} + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giFreeCatalogItems +// Deletes the contents of a given catalog. +// +GSResult d2giFreeExtraInfoList(D2GICatalog *theCatalog, + D2GExtraInfoList *extraInfoList, + GSResult result, + gsi_bool freeItem) +{ + gsi_u32 i; + if (extraInfoList != NULL) + { + for (i = 0; i < extraInfoList->mCount; i++) + { + if (freeItem) + { + d2giFreeExtraInfo(theCatalog, &extraInfoList->mExtraInfoElements[i]); + } + //d2giResetExtraInfo(&extraInfoList->mExtraInfoElements[i]); + } + } + if (extraInfoList->mCount > 0) + { + gsifree(extraInfoList->mExtraInfoElements); + extraInfoList->mExtraInfoElements = NULL; + extraInfoList->mCount = 0; + } + + gsifree(extraInfoList); + return result; +} + +////////////////////////////////////////////////////////////////////////// +// Manifest File Helper Functions +////////////////////////////////////////////////////////////////////////// + +GSResult d2giSearchandUpdateManifestFile( FILE *pManifest, + FILE *pManifestTemp, + D2GIManifestRecord manifest, + D2GContentUpdate *contentUpdate, + char *manifestRead, + gsi_bool *manifestRecordFound ) +{ + GSResult result = GS_SUCCESS ; + + memset(contentUpdate, 0, sizeof(D2GContentUpdate)); + memset(manifestRead, '\0', GS_D2G_MAX_FILE_BUFFER_LEN); + + // read a manifest record from the manifest file into the downloadRecord + while ( (*manifestRecordFound == gsi_false) && + (feof(pManifest) == 0) && + (fgets(manifestRead, (GS_D2G_MAX_FILE_BUFFER_LEN-1), pManifest) != NULL) + ) + { + result = d2giParseAndDecodeManifestRecord(manifestRead, contentUpdate); + + if (GS_SUCCEEDED(result)) + { + // assuming we either have a new file or update an existing download + // Now we have downloadRecord + // compare with the download we have. itemId+urlId combination + if (contentUpdate->mItemId < manifest.itemId) + { + // write buffer directly to the manifest file + fputs(manifestRead, pManifestTemp); + } + else + if (contentUpdate->mItemId == manifest.itemId) + { + if (contentUpdate->mDownloadItem.mUrlId < manifest.urlId) + { + // write the buffer directly to the manifest file + fputs(manifestRead, pManifestTemp); + } + else + if (contentUpdate->mDownloadItem.mUrlId == manifest.urlId) + { + // we have found a matching manifest record + *manifestRecordFound = gsi_true; + break; + } + else + { + // we do not have this record so get out of here + break; + } + } //itemid== + else // itemId > + { + // we do not have this record so get out of here + break; + } + } //GS_SUCCEEDED // add else condition here. + memset(manifestRead, '\0', GS_D2G_MAX_FILE_BUFFER_LEN); + } //while fgets + return result; +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giCreateXmlStr +// +GSResult d2giCreateAndEncodeManifestRecord(D2GIManifestRecord manifest, + char *manifestUpdate) + +{ + GSResult result = GS_SUCCESS; + D2GIString manifestXml; + + memset(&manifestXml,0, sizeof(D2GIString)); + // create the XML string for the updated item + result = d2giCreateManifestXmlStr (manifest,&manifestXml); + if (GS_SUCCEEDED(result) ) + { + // Use URL safe encoding + if (manifestXml.len >0) + { + B64Encode(manifestXml.str,manifestUpdate,(int) strlen(manifestXml.str), 2); + strcat(manifestUpdate, "\n"); + } + } + gsifree(manifestXml.str); + return result; +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giUpdateManifestRecord +// +GSResult d2giUpdateManifestRecord(D2GIInstance *theInstance, + D2GIManifestRecord manifest) + +{ + GSResult result = GS_SUCCESS; + char manifestRead [GS_D2G_MAX_FILE_BUFFER_LEN]; + char manifestUpdate[GS_D2G_MAX_FILE_BUFFER_LEN]; + FILE *pManifest = NULL; + char *manifestFileName = NULL; + + memset(manifestUpdate, '\0', sizeof(manifestUpdate)); + result = d2giCreateAndEncodeManifestRecord(manifest, manifestUpdate); + + if (GS_FAILED(result)) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_HotError, + "%s :: %s (%d): Cannot create XML string (%s)", + __FILE__, __FUNCTION__, __LINE__, manifestUpdate); + return result; + } + + // determine the filename & path + if (!theInstance->mManifestFilePath) + { + manifestFileName = goastrdup(GS_D2G_MANIFEST_FILENAME); + } + else + { + manifestFileName = gsimalloc(sizeof(char) * (strlen(GS_D2G_MANIFEST_FILENAME)+strlen(theInstance->mManifestFilePath)+1)); + _stprintf(manifestFileName, "%s%s", theInstance->mManifestFilePath, GS_D2G_MANIFEST_FILENAME); + } + + pManifest = fopen(manifestFileName, "r"); + if (pManifest == NULL) + { + // manifest file down not exist + // so just write the one and only record we have + // open it as "w+" first + + pManifest = fopen(manifestFileName, "w+" ); + if (pManifest == NULL) + { + // Cannot write to directory + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_File, GSIDebugLevel_HotError, + "%s :: %s (%d): Cannot open file %s", + __FILE__, __FUNCTION__, __LINE__, manifestFileName); + result = GS_D2G_ERROR(GSResultSection_File, GSResultCode_FileWriteError); + + } + else + { + // write the XML string into the manifest + fputs(manifestUpdate, pManifest); + fclose(pManifest); + } + } + else + { + // we have a manifest file so read a record from it and write to the manifest.temp + // create a "manifest.tmp" + char manifestFileNameTmp[GS_MAX_FILENAME_LEN]; + + FILE *pManifestTemp; + memset(manifestFileNameTmp, '\0',sizeof(manifestFileNameTmp)); + strncpy(manifestFileNameTmp, manifestFileName, strlen(manifestFileName)); + strcat(manifestFileNameTmp, GS_D2G_DOWNLOAD_FILE_TEMP_EXT); + pManifestTemp = fopen(manifestFileNameTmp, "w+"); + + while((pManifestTemp != NULL) && (!feof(pManifest))) + { + D2GContentUpdate contentUpdate; + gsi_bool manifestRecordFound = gsi_false; + + result = d2giSearchandUpdateManifestFile( pManifest, + pManifestTemp, + manifest, + &contentUpdate, + manifestRead, + &manifestRecordFound ); + + if (GS_SUCCEEDED(result)) + { + // write the XML string into the manifest.tmp + fputs(manifestUpdate, pManifestTemp); + + if ((!manifestRecordFound) && (strlen(manifestRead)>0)) + { + // we do not have this record so just write the last record read + // write the buffer directly to the manifest file + if ((contentUpdate.mItemId > manifest.itemId) || + ((contentUpdate.mItemId == manifest.itemId) && + (contentUpdate.mDownloadItem.mUrlId >= manifest.urlId)) + ) + { + fputs(manifestRead, pManifestTemp); + } + } + // Now complete copying the remaining records if any + memset(manifestRead, '\0', GS_D2G_MAX_FILE_BUFFER_LEN); + while (!feof(pManifest) && + (fgets(manifestRead, GS_D2G_MAX_FILE_BUFFER_LEN, pManifest) != NULL)) + { + fputs(manifestRead, pManifestTemp); + memset(manifestRead, '\0', GS_D2G_MAX_FILE_BUFFER_LEN); + } + } + } // while + fclose(pManifestTemp); + fclose(pManifest); + if (GS_SUCCEEDED(result)) + { + _tremove(manifestFileName); + _trename(manifestFileNameTmp, manifestFileName); + } + } + gsifree(manifestFileName); + return result; + +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giDeleteManifestRecord +// + +GSResult d2giDeleteManifestRecord(D2GIInstance *theInstance, + D2GIManifestRecord manifest) + +{ + GSResult result = GS_SUCCESS; + char *manifestRead = gsimalloc(GS_D2G_MAX_FILE_BUFFER_LEN); + FILE *pManifest = NULL; + char *manifestFileName = NULL ; + + // determine the filename & path + if (!theInstance->mManifestFilePath) + { + manifestFileName = goastrdup(GS_D2G_MANIFEST_FILENAME); + } + else + { + manifestFileName = gsimalloc(sizeof(char) * (strlen(GS_D2G_MANIFEST_FILENAME)+strlen(theInstance->mManifestFilePath)+1)); + _stprintf(manifestFileName, "%s%s", theInstance->mManifestFilePath, GS_D2G_MANIFEST_FILENAME); + } + + pManifest = fopen(manifestFileName, "r"); + if (pManifest == NULL) + { + // The manifest file either was never created or must have been deleted + // because all entries have been deleted prior to this call. + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_File, GSIDebugLevel_WarmError, + "%s :: %s (%d): Manifest File does not exist", + __FILE__, __FUNCTION__, __LINE__); + result = GS_D2G_ERROR(GSResultSection_File, GSResultCode_FileNotFound); + } + else + { + // we have a manifest file so read a record from it and write to the manifest.temp + // create a "manifest.tmp" + char manifestFileNameTmp[GS_MAX_FILENAME_LEN]; + + FILE *pManifestTemp; + memset(manifestFileNameTmp, '\0',sizeof(manifestFileNameTmp)); + strncpy(manifestFileNameTmp, manifestFileName, strlen(manifestFileName)); + strcat(manifestFileNameTmp, GS_D2G_DOWNLOAD_FILE_TEMP_EXT); + + pManifestTemp = fopen(manifestFileNameTmp, "w+"); + + + while((pManifestTemp != NULL) && (!feof(pManifest))) + { + D2GContentUpdate contentUpdate; + gsi_bool manifestRecordFound = gsi_false; + + result = d2giSearchandUpdateManifestFile( pManifest, + pManifestTemp, + manifest, + &contentUpdate, + manifestRead, + &manifestRecordFound ); + + if (GS_SUCCEEDED(result)) + { + if (!manifestRecordFound) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_File, GSIDebugLevel_Warning, + "%s :: %s (%d): Item Id %d already does not exist in the manifest file. No error generated", + __FILE__, __FUNCTION__, __LINE__, manifest.itemId); + + if(strlen(manifestRead)>0) + { + // we do not have this record so just write the last record read + // write the buffer directly to the manifest file + if ((contentUpdate.mItemId > manifest.itemId) || + ((contentUpdate.mItemId == manifest.itemId) && + (contentUpdate.mDownloadItem.mUrlId >= manifest.urlId)) + ) + { + fputs(manifestRead, pManifestTemp); + } + } + } + // Now complete copying the remaining records if any + memset(manifestRead, '\0', GS_D2G_MAX_FILE_BUFFER_LEN); + while (!feof(pManifest) && + (fgets(manifestRead, GS_D2G_MAX_FILE_BUFFER_LEN, pManifest) != NULL)) + { + fputs(manifestRead, pManifestTemp); + memset(manifestRead, '\0', GS_D2G_MAX_FILE_BUFFER_LEN); + } + } + } // while + fclose(pManifestTemp); + fclose(pManifest); + if (GS_SUCCEEDED(result)) + { + _tremove(manifestFileName); + _trename(manifestFileNameTmp, manifestFileName); + + } + } + gsifree(manifestFileName); + gsifree(manifestRead); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giWriteManifestDownloadsToXML +// +gsi_bool d2giWriteManifestDownloadsToXML(GSXmlStreamWriter writer, + D2GIInstance *theInstance) +{ + char manifestRead[GS_D2G_MAX_FILE_BUFFER_LEN]; + FILE *pManifest = NULL; + GSResult result= GS_SUCCESS; + char *manifestFileName = NULL; + + if (writer == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_HotError, + "%s :: %s (%d): NULL pointer passed.\n", + __FILE__, __FUNCTION__, __LINE__); + return gsi_false; + } + + // determine the filename & path + if (!theInstance->mManifestFilePath) + { + manifestFileName = goastrdup(GS_D2G_MANIFEST_FILENAME); + } + else + { + manifestFileName = gsimalloc(sizeof(char) * (strlen(GS_D2G_MANIFEST_FILENAME)+strlen(theInstance->mManifestFilePath)+1)); + _stprintf(manifestFileName, "%s%s", theInstance->mManifestFilePath, GS_D2G_MANIFEST_FILENAME); + } + + pManifest = fopen(manifestFileName, "r"); + gsifree(manifestFileName); + + if (pManifest == NULL) + { + // manifest file down not exist + // No downloaded content + return gsi_true; + } + else + { + memset(manifestRead, '\0', sizeof(manifestRead)); + while(!feof(pManifest)&& (fgets(manifestRead, sizeof(manifestRead)-1, pManifest) != NULL)) + { + D2GContentUpdate contentUpdate; + D2GIString manifestXml; + manifestXml.str = gsimalloc((strlen(manifestRead)+2) * sizeof(char)); + memset (manifestXml.str, '\0',(strlen(manifestRead)*sizeof(char)) ); + memset(&contentUpdate, 0 , sizeof(contentUpdate)); + + // Uses URL safe encoding + B64Decode(manifestRead, manifestXml.str, (int) strlen(manifestRead), &manifestXml.len, 2); + + result = d2giParseManifestRecord(manifestXml.str, &contentUpdate); + + gsifree(manifestXml.str); + + if (GS_SUCCEEDED(result)) + { + if (gsi_is_false(gsXmlWriteOpenTag (writer, GS_D2G_NAMESPACE, "download")) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "itemid", contentUpdate.mItemId)) || + gsi_is_false(gsXmlWriteIntElement(writer, GS_D2G_NAMESPACE, "downloadurlid", contentUpdate.mDownloadItem.mUrlId)) || + gsi_is_false(gsXmlWriteFloatElement(writer, GS_D2G_NAMESPACE, "version", contentUpdate.mDownloadItem.mVersion)) || + gsi_is_false(gsXmlWriteCloseTag (writer, GS_D2G_NAMESPACE, "download"))) + + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not write request data: GS_D2G_GETDOWNLOADINFO_REQUEST\n", + __FILE__, __FUNCTION__, __LINE__); + fclose(pManifest); + return gsi_false; + } + } + } + fclose(pManifest); + } + return gsi_true; +} +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// d2giCreateManifestXmlStr +// +// 1231754461.4 + +GSResult d2giCreateManifestXmlStr(D2GIManifestRecord manifest, + D2GIString *manifestBuffer) +{ + GSResult result = GS_SUCCESS; + GSXmlStreamWriter writer = NULL; + + writer = gsXmlCreateStreamWriterNoNamespace(); + if (writer == NULL) + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): Failed on gsXmlCreateStreamWriter (assuming out of memory)\n", + __FILE__, __FUNCTION__, __LINE__); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + else + { + + if (gsi_is_false(gsXmlWriteOpenTagNoNamespace(writer,"download")) || + gsi_is_false(gsXmlWriteIntElementNoNamespace(writer, "itemid", manifest.itemId)) || + gsi_is_false(gsXmlWriteIntElementNoNamespace(writer, "downloadurlid", manifest.urlId)) || + gsi_is_false(gsXmlWriteFloatElementNoNamespace(writer,"version", manifest.version)) || + gsi_is_false(gsXmlWriteCloseTagNoNamespace(writer, "download")) || + gsi_is_false(gsXmlCloseWriterNoNamespace(writer)) + ) + + { + gsDebugFormat(GSIDebugCat_Direct2Game, GSIDebugType_Misc, GSIDebugLevel_WarmError, + "%s :: %s (%d): could not write request data: GS_D2G_GETDOWNLOADINFO_REQUEST\n", + __FILE__, __FUNCTION__, __LINE__); + + gsXmlFreeWriter(writer); + return GS_D2G_ERROR(GSResultSection_Memory, GSResultCode_OutOfMemory); + } + manifestBuffer->len = gsXmlWriterGetDataLength(writer) + 3; + manifestBuffer->str = gsimalloc(manifestBuffer->len); + memset(manifestBuffer->str, '\0', manifestBuffer->len); + strncpy(manifestBuffer->str, gsXmlWriterGetData(writer), gsXmlWriterGetDataLength(writer)); + gsXmlFreeWriter(writer); + } + return result; +} diff --git a/Direct2Game/d2gUtil.h b/Direct2Game/d2gUtil.h new file mode 100644 index 00000000..5f3ebeaa --- /dev/null +++ b/Direct2Game/d2gUtil.h @@ -0,0 +1,218 @@ +/** +* d2gUtil.h +* +* GameSpy DIRECT TO GAME SDK +* This file is part of the Direct to Game SDK designed and developed by GameSpy Tech. +* Copyright (c) 2008, GameSpy Technology +*/ +#ifndef __D2GUTIL_H__ +#define __D2GUTIL_H__ +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +// URL for direct2game commerce services (backwards compatibility) +#define GS_D2G_HTTP GSI_HTTP_PROTOCOL_URL +#define GS_D2G_SECURE_HTTP GSI_HTTP_PROTOCOL_URL + +#ifdef GS_D2G_STAGE +#define GS_D2G_SERVICE_URL_BASE "mwstage." GSI_DOMAIN_NAME "/commerce/1.1/" +#else +#define GS_D2G_SERVICE_URL_BASE "%s.d2g.pubsvs." GSI_DOMAIN_NAME "/commerce/1.1/" +#endif + +//passed into d2giSetServiceUrl +#define GS_D2G_URL_SECURE gsi_true +#define GS_D2G_URL_NOT_SECURE gsi_false + +#define GS_D2G_MAX_BASE_URL_LEN 128 +#define GS_D2G_MAX_URL_LEN 256 + +//Manifest File Name +#define GS_D2G_MANIFEST_FILENAME "InstalledContent" + +// Maximum number of categories supported +#define GS_D2G_MAX_CATEGORIES 1000 +#define GS_D2G_MAX_FILE_BUFFER_LEN 500 +typedef struct D2GIString +{ + int len; + char *str; +} D2GIString; + +typedef gsi_bool (* compareFunc)(const void *, const void *, D2GSortDirection direction) ; +void d2giQSort(void *list, + size_t size, + unsigned int offset, + D2GSortDirection direction, + int left, + int right, + compareFunc compareFunction); + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +void d2giSetServiceUrl(D2GIInstance *theInstance, gsi_bool secure, const char *service); +GSResult d2giResultFromStatusCode(int statusCode); +GSResult d2giResultFromHttpResult(GHTTPResult result); +GSResult d2giResultFromHttpRequest(GHTTPRequest request); +GSResult d2giOrderItemValidationResultFromCode(int orderItemCode); +GSResult d2giOrderValidationResultFromCode(int orderCode); +GSResult d2giResultFromStoreAvailabilityCode(int availableCode); +GSResult d2giResultFromDownloadStatusCode(int statusCode); + +gsi_bool d2giWriteCachedItemsToXml(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + GSXmlStreamWriter writer, + gsi_u32 accountId, + UCS2String cultureCode, + UCS2String currencyCode, + gsi_u32 *itemIds, + int itemCount, + gsi_u32 *itemQuantities); + +gsi_bool d2giWriteOrderTotalToXml(D2GIInstance *theInstance, + GSXmlStreamWriter writer, + const D2GOrderTotal *theOrderTotal); + +// Item functionality +GSResult d2giResetCatalogItem(D2GCatalogItem *item); + +D2GCatalogItem * d2giGetCatalogItem(D2GIInstance *theinstance, + D2GICatalog *theCatalog, + gsi_u32 itemId); + +D2GCatalogItem * d2giNewCatalogItem(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + gsi_u32 itemId); + +/////////////////////////////////////////////////////////////////////////////// +// Extra Info Cache Helper Functions +/////////////////////////////////////////////////////////////////////////////// + +D2GExtraInfo * d2giNewExtraInfo(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + D2GExtraInfo *theExtraInfo); + +UCS2String d2giLookUpExtraInfo(D2GIInstance *theInstance, D2GICatalog *theCatalog, const UCS2String key); + +D2GExtraInfo * d2giGetExtraInfo(D2GIInstance *theInstance, + D2GICatalog *theCatalog, + UCS2String key); + + +GSResult d2giFreeExtraInfo(D2GICatalog *theCatalog, + D2GExtraInfo *extraInfo); + +GSResult d2giResetExtraInfo(D2GExtraInfo *extraInfo); + +GSResult d2giFreeExtraInfoList(D2GICatalog *theCatalog, + D2GExtraInfoList *extraInfoList, + GSResult result, + gsi_bool freeItem); +//cleanup functions +void d2giFreeCreditCardInfoList(D2GCreditCardInfoList *creditCardList); + +void d2giFreeOrder(D2GOrderInfo *theOrder); + +GSResult d2giFreeCatalogItems(D2GICatalog *theCatalog, + D2GCatalogItemList *itemList, + GSResult result, + gsi_bool freeItem); + +GSResult d2giFreeCatalogItem(D2GICatalog *theCatalog, + D2GCatalogItem * item); + +void d2giFreeOrderTotal(D2GOrderTotal *orderTotal); + +void d2giFreeOrderPurchase(D2GOrderPurchase *purchase); + +void d2giFreePurchaseHistory(D2GPurchaseHistory *purchaseHistory); + +void d2giFreeOrderPurchaseContents(D2GOrderPurchase *purchase); + +void d2giFreeDownloadItemContents(D2GDownloadItem * downloadItem); + +void d2giFreeContentUpdateList(D2GContentUpdateList *contentUpdateList); + +// Sorting +GSResult d2giSortCatalogItemsbyPrice(D2GCatalogItemList *list, + D2GSortDirection direction); + +GSResult d2giSortCatalogItemsbyName(D2GCatalogItemList *list, + D2GSortDirection direction); + +GSResult d2giSortCatalogItemsbyDate(D2GCatalogItemList *list, + D2GSortDirection direction); + +GSResult d2giSortCatalogItemsbyID(D2GCatalogItemList *list, + D2GSortDirection direction); + +GSResult d2giSortCatalogItemsbyExtId(D2GCatalogItemList *list, + D2GSortDirection direction); + +GSResult d2giSortCatalogItemsbySize(D2GCatalogItemList *list, + D2GSortDirection direction); +void d2giQSortInt(void *list, + size_t size, + unsigned int offset, + D2GSortDirection direction, + int left, + int right ); + +void d2giQSortStr(D2GCatalogItemList *list, + D2GSortDirection direction, + int left, + int right ); + +GSResult d2giGetAllItemsFromCache(D2GICatalog *theCatalog, + D2GLoadCatalogItemsCallback callback, + void *userData); + +GSResult d2giGetCategoriesFromCache(D2GICatalog *theCatalog, D2GCategoryList *categoryList); + +////////////////////////////////////////////////////////////////////////// +//Manifest File operations +////////////////////////////////////////////////////////////////////////// +typedef struct D2GIManifestRecord +{ + D2GItemId itemId; + D2GUrlId urlId; + float version; +} D2GIManifestRecord; + +GSResult d2giDeleteManifestRecord(D2GIInstance *theInstance, + D2GIManifestRecord manifest); + +GSResult d2giUpdateManifestRecord(D2GIInstance *theInstance, + D2GIManifestRecord manifest); + +gsi_bool d2giWriteManifestDownloadsToXML( GSXmlStreamWriter writer, + D2GIInstance *theInstance); + +GSResult d2giCreateManifestXmlStr (D2GIManifestRecord manifest, + D2GIString *manifestBuffer); + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +GSResult d2giCheckItemsQuantities(D2GIInstance *theInstance, + gsi_u32 *itemQuantities, + gsi_u32 itemQuantitiesCount); + +GSResult d2giCloneOrderItem(D2GOrderItem *dstOrderItem, + D2GOrderItem *srcOrderItem); + +GSResult d2giCloneDownloadList(D2GDownloadItemList *dstDownloadItemList, + D2GDownloadItemList *srcDownloadItemList); + +GSResult d2giCloneLicenseList(D2GLicenseItemList *dstLicenses, + D2GLicenseItemList *srcLicenses); + +GSResult d2giCloneOrderItemPurchases(D2GOrderItemPurchase **destPurchases, + gsi_u32 *dstCount, + D2GOrderItemPurchase *srcPurchases, + gsi_u32 srcCount); + +GSResult d2giGetCatalogItemsByCategoryFromCache(D2GICatalog *theCatalog, + const UCS2String theCategory, + D2GLoadCatalogItemsByCategoryCallback callback, + void *userData); +#endif diff --git a/ghttp/CMakeLists.txt b/ghttp/CMakeLists.txt index db998ec9..8e8ca6b1 100644 --- a/ghttp/CMakeLists.txt +++ b/ghttp/CMakeLists.txt @@ -33,10 +33,8 @@ if (UNISPY_HTTPLOG) target_compile_definitions(ushttp PUBLIC HTTP_LOG=1) endif() -# Extended log for HTTP -if (CMAKE_BUILD_TYPE EQUAL "DEBUG") - target_compile_definitions(ushttp PUBLIC GHTTP_EXTENDEDERROR=1) -endif() +# Extended log for HTTP, this avoid using http errors as -1, which was done in previous SDK versions +target_compile_definitions(ushttp PUBLIC GHTTP_EXTENDEDERROR=1) if (OPENSSL_FOUND) set(HTTP_OPENSSL_LIBS OpenSSL::SSL) diff --git a/sharedDll/CMakeLists.txt b/sharedDll/CMakeLists.txt index 9be6362d..eafb76a6 100644 --- a/sharedDll/CMakeLists.txt +++ b/sharedDll/CMakeLists.txt @@ -19,4 +19,5 @@ target_link_libraries(UniSpySDK SHARED usserverbrowsing usvoice2 uswebservices + usd2g )