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
)