Add support for purchasing and purchase history in Wii Room Theatre

This commit is contained in:
Sketch 2022-11-04 18:47:35 -04:00
parent 58f9612429
commit 46015a563b
5 changed files with 278 additions and 31 deletions

25
cas.go
View File

@ -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),

View File

@ -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);
--

201
ecs.go
View File

@ -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(&currentRefIdString, &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.

1
go.mod
View File

@ -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

12
go.sum
View File

@ -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=