diff --git a/cas.go b/cas.go index c086388..c2e9ac5 100644 --- a/cas.go +++ b/cas.go @@ -1,5 +1,9 @@ package main +import "log" + +const QueryTitlesTableByPriceCode = `SELECT item_id, price FROM titles WHERE price_code = $1` + func listItems(e *Envelope) { titleId, err := e.getKey("TitleId") if err != nil { @@ -12,10 +16,13 @@ func listItems(e *Envelope) { } var licenceStr string + var pricingCode string for _, attr := range attrs { name, value := parseNameValue(attr.InnerText()) if name == "TitleKind" { licenceStr = value + } else if name == "PricingCode" { + pricingCode = value } } @@ -25,7 +32,18 @@ func listItems(e *Envelope) { e.Error(5, "Invalid TitleKind was passed by SOAP", err) } - // TODO(SketchMaster2001): Query database for items + // Query the titles table to get our title + row := pool.QueryRow(ctx, QueryTitlesTableByPriceCode, pricingCode) + + var itemId int + var price int + err = row.Scan(&itemId, &price) + if err != nil { + log.Printf("error while querying titles table: %v", err) + e.Error(2, "error retrieving title", nil) + return + } + e.AddKVNode("ListResultTotalSize", "1") e.AddCustomType(Items{ TitleId: titleId, @@ -49,10 +67,9 @@ func listItems(e *Envelope) { Age: 9, }, Prices: Prices{ - ItemId: 0, + ItemId: itemId, Price: Price{ - // Not sure about WSC, but must match the price for the title you are purchasing in Wii no Ma. - Amount: 0, + Amount: price, Currency: "POINTS", }, Limits: LimitStruct(PR), diff --git a/database.sql b/database.sql index 4b0d127..9c7d679 100644 --- a/database.sql +++ b/database.sql @@ -2,8 +2,8 @@ -- PostgreSQL database dump -- --- Dumped from database version 14.1 --- Dumped by pg_dump version 14.1 +-- Dumped from database version 14.5 (Homebrew) +-- Dumped by pg_dump version 14.5 (Homebrew) SET statement_timeout = 0; SET lock_timeout = 0; @@ -27,7 +27,9 @@ SET default_table_access_method = heap; CREATE TABLE public.owned_titles ( account_id integer NOT NULL, title_id character varying(16) NOT NULL, - version integer + version integer, + item_id integer, + date_purchased timestamp without time zone DEFAULT now() NOT NULL ); @@ -46,6 +48,21 @@ CREATE TABLE public.tickets ( ALTER TABLE public.tickets OWNER TO wiisoap; +-- +-- Name: titles; Type: TABLE; Schema: public; Owner: wiisoap +-- + +CREATE TABLE public.titles ( + item_id integer NOT NULL, + price_code integer NOT NULL, + price integer NOT NULL, + title_id character varying(16) NOT NULL, + reference_id character varying(32) +); + + +ALTER TABLE public.titles OWNER TO wiisoap; + -- -- Name: userbase; Type: TABLE; Schema: public; Owner: wiisoap -- @@ -63,11 +80,43 @@ CREATE TABLE public.userbase ( ALTER TABLE public.userbase OWNER TO wiisoap; -- --- Name: owned_titles owned_titles_pk; Type: CONSTRAINT; Schema: public; Owner: wiisoap +-- Data for Name: owned_titles; Type: TABLE DATA; Schema: public; Owner: wiisoap -- -ALTER TABLE ONLY public.owned_titles - ADD CONSTRAINT owned_titles_pk PRIMARY KEY (account_id); +COPY public.owned_titles (account_id, title_id, version, item_id, date_purchased) FROM stdin; +\. + + +-- +-- Data for Name: tickets; Type: TABLE DATA; Schema: public; Owner: wiisoap +-- + +COPY public.tickets (title_id, ticket, version) FROM stdin; +\. + + +-- +-- Data for Name: titles; Type: TABLE DATA; Schema: public; Owner: wiisoap +-- + +COPY public.titles (item_id, price_code, price, title_id, reference_id) FROM stdin; +\. + + +-- +-- Data for Name: userbase; Type: TABLE DATA; Schema: public; Owner: wiisoap +-- + +COPY public.userbase (device_id, device_token, device_token_hashed, account_id, region, serial_number) FROM stdin; +\. + + +-- +-- Name: titles item_id; Type: CONSTRAINT; Schema: public; Owner: wiisoap +-- + +ALTER TABLE ONLY public.titles + ADD CONSTRAINT item_id PRIMARY KEY (item_id); -- @@ -78,6 +127,13 @@ ALTER TABLE ONLY public.tickets ADD CONSTRAINT tickets_pk PRIMARY KEY (title_id); +-- +-- Name: titles titles_reference_id_key; Type: CONSTRAINT; Schema: public; Owner: wiisoap +-- + +ALTER TABLE ONLY public.titles + ADD CONSTRAINT titles_reference_id_key UNIQUE (reference_id); + -- -- Name: userbase userbase_pk; Type: CONSTRAINT; Schema: public; Owner: wiisoap -- @@ -90,7 +146,7 @@ ALTER TABLE ONLY public.userbase -- Name: owned_titles_account_id_uindex; Type: INDEX; Schema: public; Owner: wiisoap -- -CREATE UNIQUE INDEX owned_titles_account_id_uindex ON public.owned_titles USING btree (account_id); +CREATE INDEX owned_titles_account_id_uindex ON public.owned_titles USING btree (account_id); -- diff --git a/ecs.go b/ecs.go index 3ad07ae..e4442e2 100644 --- a/ecs.go +++ b/ecs.go @@ -18,11 +18,16 @@ package main import ( + "encoding/hex" "errors" "fmt" + v1Ticket "github.com/OpenShopChannel/V1TicketGenerator" "github.com/wii-tools/wadlib" "log" + "math" + "strconv" "strings" + "time" ) const ( @@ -31,14 +36,24 @@ const ( WHERE owned_titles.title_id = tickets.title_id AND owned_titles.account_id = $1` + QueryOwnedServiceTitles = `SELECT titles.reference_id, owned_titles.date_purchased, titles.item_id, titles.price + FROM titles, owned_titles + WHERE titles.item_id = owned_titles.item_id + AND owned_titles.account_id = $1` + QueryTicketStatement = `SELECT ticket, version FROM tickets WHERE title_id = $1` - AssociateTicketStatement = `INSERT INTO owned_titles (account_id, title_id, version) - VALUES ($1, $2, $3)` + AssociateTicketStatement = `INSERT INTO owned_titles (account_id, title_id, version, item_id) + VALUES ($1, $2, $3, $4)` // SharedBalanceAmount describes the maximum signed 32-bit integer value. // It is not an actual tracked points value, but exists to permit reuse. - SharedBalanceAmount = 2147483647 + SharedBalanceAmount = math.MaxInt32 + + // WiinoMaApplicationID is the title ID for the Japanese channel Wii no Ma. + WiinoMaApplicationID = "000100014843494A" + // WiinoMaServiceTitleID is the service ID used by Wii no Ma's theatre. + WiinoMaServiceTitleID = "000101006843494a" ) func getBalance() Balance { @@ -109,11 +124,20 @@ func getETickets(e *Envelope) { } func purchaseTitle(e *Envelope) { - //accountId, err := e.AccountId() - //if err != nil { - // e.Error(2, "missing account ID", err) - // return - //} + accountId, err := e.AccountId() + if err != nil { + e.Error(2, "missing account ID", err) + return + } + + tempItemId, err := e.getKey("ItemId") + if err != nil { + e.Error(2, "missing item ID", err) + return + } + + // Our struct takes an integer rather than a string. + itemId, _ := strconv.Atoi(tempItemId) // Determine the title ID we're going to purchase. titleId, err := e.getKey("TitleId") @@ -138,12 +162,88 @@ func purchaseTitle(e *Envelope) { return } + price := 0 + // Wii no Ma needs the ticket to be in the v1 ticket format. + if titleId == WiinoMaServiceTitleID { + // Firstly we need to change our title ID to the Wii no Ma application ID. + titleId, err = e.getKey("ApplicationId") + if err != nil { + e.Error(2, "missing application ID", err) + return + } + + refId, err := e.getKey("ReferenceId") + if err != nil { + e.Error(2, "missing reference ID", err) + return + } + + // Convert reference ID to bytes + refIdBytes, err := hex.DecodeString(refId) + if err != nil { + log.Printf("unexpected error converting reference id to bytes: %v", err) + e.Error(2, "error purchasing", nil) + return + } + + var referenceId [16]byte + copy(referenceId[:], refIdBytes) + + subscriptions := []v1Ticket.V1SubscriptionRecord{ + { + ExpirationTime: uint32(time.Now().AddDate(0, 1, 0).Unix()), + ReferenceID: referenceId, + }, + } + + // Query the database for other purchased items + rows, err := pool.Query(ctx, QueryOwnedServiceTitles, accountId) + if err != nil { + log.Printf("unexpected error purchasing: %v", err) + e.Error(2, "error purchasing", nil) + return + } + + defer rows.Close() + for rows.Next() { + var currentRefIdString string + var purchasedTime time.Time + err = rows.Scan(¤tRefIdString, &purchasedTime, nil, &price) + if err != nil { + log.Printf("unexpected error purchasing: %v", err) + e.Error(2, "error purchasing", nil) + return + } + + refIdBytes, err = hex.DecodeString(currentRefIdString) + if err != nil { + log.Printf("unexpected error converting reference id to bytes: %v", err) + e.Error(2, "error purchasing", nil) + return + } + + var currentReferenceId [16]byte + copy(currentReferenceId[:], refIdBytes) + subscriptions = append(subscriptions, v1Ticket.V1SubscriptionRecord{ + ExpirationTime: uint32(purchasedTime.AddDate(0, 0, 30).Unix()), + ReferenceID: currentReferenceId, + }) + } + + ticket, err = v1Ticket.CreateV1Ticket(ticket, subscriptions) + if err != nil { + log.Printf("unexpected error creating v1Ticket: %v", err) + e.Error(2, "error creating ticket", nil) + return + } + } + // Associate the given title ID with the user. - //_, err = pool.Exec(ctx, AssociateTicketStatement, accountId, titleId, version) - //if err != nil { - // log.Printf("unexpected error purchasing: %v", err) - // e.Error(2, "error purchasing", nil) - //} + _, err = pool.Exec(ctx, AssociateTicketStatement, accountId, titleId, version, itemId) + if err != nil { + log.Printf("unexpected error purchasing: %v", err) + e.Error(2, "error purchasing", nil) + } // The returned ticket is expected to have two other certificates associated. ticketString := b64(append(ticket, wadlib.CertChainTemplate...)) @@ -153,9 +253,9 @@ func purchaseTitle(e *Envelope) { TransactionId: "00000000", Date: e.Timestamp(), Type: "PURCHGAME", - TotalPaid: 0, + TotalPaid: price, Currency: "POINTS", - ItemId: 0, + ItemId: itemId, }) e.AddKVNode("SyncTime", e.Timestamp()) @@ -168,8 +268,68 @@ func purchaseTitle(e *Envelope) { func listPurchaseHistory(e *Envelope) { // TODO(SketchMaster2001) Query database for transactions - e.AddCustomType([]Transactions{ - { + accountId, err := e.AccountId() + if err != nil { + e.Error(2, "missing account ID", err) + return + } + + titleId, err := e.getKey("ApplicationId") + if err != nil { + e.Error(2, "missing application ID", err) + return + } + + var transactions []Transactions + + // We will query the database differently for Wii no Ma. + if titleId == WiinoMaApplicationID { + // Query the database for other purchased items + rows, err := pool.Query(ctx, QueryOwnedServiceTitles, accountId) + if err != nil { + log.Printf("unexpected error purchasing: %v", err) + e.Error(2, "error purchasing", nil) + return + } + + defer rows.Close() + for rows.Next() { + var refId string + var purchasedTime time.Time + var itemId int + var price int + err = rows.Scan(&refId, &purchasedTime, &itemId, &price) + if err != nil { + log.Printf("unexpected error purchasing: %v", err) + e.Error(2, "error purchasing", nil) + return + } + + transaction := Transactions{ + TransactionId: "00000000", + Date: strconv.Itoa(int(purchasedTime.UnixMilli())), + Type: "PURCHGAME", + TotalPaid: price, + Currency: "POINTS", + ItemId: itemId, + ItemPricing: Prices{ + ItemId: itemId, + Price: Price{ + Amount: price, + Currency: "POINTS", + }, + Limits: LimitStruct(PR), + LicenseKind: SERVICE, + }, + TitleId: "000101006843494A", + ItemCode: itemId, + ReferenceId: refId, + } + + transactions = append(transactions, transaction) + } + } else { + transactions = append(transactions, Transactions{ TransactionId: "00000000", // Is timestamp in milliseconds, placeholder one is Wed Oct 19 2022 18:02:46 Date: "1666202566218", @@ -188,10 +348,11 @@ func listPurchaseHistory(e *Envelope) { }, TitleId: "000101006843494A", ReferenceId: "01234567890123456789012345678912", - }, - }) + }) + } - e.AddKVNode("ListResultTotalSize", "1") + e.AddCustomType(transactions) + e.AddKVNode("ListResultTotalSize", strconv.Itoa(len(transactions))) } // genServiceUrl returns a URL with the given service against a configured URL. diff --git a/go.mod b/go.mod index 7b25847..aae3952 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/OpenShopChannel/WiiSOAP go 1.18 require ( + github.com/OpenShopChannel/V1TicketGenerator v0.0.0-20221103013129-b7ba13a5f57a github.com/RiiConnect24/wiino v0.0.0-20210419165641-a2614cecbcca github.com/antchfx/xmlquery v1.3.11 github.com/jackc/pgconn v1.12.1 diff --git a/go.sum b/go.sum index d74879f..d80eca1 100644 --- a/go.sum +++ b/go.sum @@ -1,20 +1,25 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/OpenShopChannel/V1TicketGenerator v0.0.0-20221103013129-b7ba13a5f57a h1:KttQrTtbH+yCkrPI1TAY1lkqiDzEKBMg3h6BheB2ILg= +github.com/OpenShopChannel/V1TicketGenerator v0.0.0-20221103013129-b7ba13a5f57a/go.mod h1:sLOBm3XlBjIdRCsbV0o/bi2b7TpxsA5+qDJafuESHsw= github.com/RiiConnect24/wiino v0.0.0-20210419165641-a2614cecbcca h1:bqvl4vwPEGdpC//xahkhDpVTwe1ym6wTWv6EdiRxxXY= github.com/RiiConnect24/wiino v0.0.0-20210419165641-a2614cecbcca/go.mod h1:BmIQ5QOpoum6rxYqqAjdpwwdfoQeKVi1xnxeEU+56ro= github.com/antchfx/xmlquery v1.3.11 h1:8aRK7l3+dJjL8ZmwgVzG5AXysrP7Mss2424tfntKWKY= github.com/antchfx/xmlquery v1.3.11/go.mod h1:ywPcYkN0GvURUxXpUujaMVvuLSOYQBzoSfHKfAYezCE= github.com/antchfx/xpath v1.2.1 h1:qhp4EW6aCOVr5XIkT+l6LJ9ck/JsUH/yyauNgTQkBF8= github.com/antchfx/xpath v1.2.1/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= +github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -35,6 +40,7 @@ github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= @@ -76,6 +82,7 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/logrusorgru/aurora/v3 v3.0.0 h1:R6zcoZZbvVcGMvDCKo45A9U/lzYyzl5NfYIvznmDfE4= github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc= @@ -84,7 +91,9 @@ github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= @@ -92,6 +101,7 @@ github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OK github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -102,6 +112,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/wii-tools/wadlib v0.3.1 h1:g0Szzof/YsBLghP+JpoVzT/6M6jpl+AH9PHiUuG3cd8= github.com/wii-tools/wadlib v0.3.1/go.mod h1:GK+f2POk+rVu1p4xqLSb4ll1SKKbfOO6ZAB+oPLV3uQ= @@ -178,5 +189,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=