Skip to content

Instantly share code, notes, and snippets.

@TheGreatRambler
Last active April 2, 2023 04:11
Show Gist options
  • Save TheGreatRambler/899ff02155d7c2ea9f8a8e5ad7442a51 to your computer and use it in GitHub Desktop.
Save TheGreatRambler/899ff02155d7c2ea9f8a8e5ad7442a51 to your computer and use it in GitHub Desktop.
Super Mario Maker 2 Data ID to Course ID
package id
import (
"fmt"
"strings"
)
func CourseIdToNum(id string) (int, error) {
id = strings.ToUpper(strings.ReplaceAll(id, "-", ""))
// https://github.com/kinnay/NintendoClients/wiki/Data-Store-Codes#super-mario-maker-2
if len(id) != 9 {
return 0, fmt.Errorf("invalid length for course id %d", len(id))
}
charset := map[string]int{
"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9, "B": 10, "C": 11, "D": 12, "F": 13, "G": 14, "H": 15, "J": 16, "K": 17, "L": 18, "M": 19, "N": 20, "P": 21, "Q": 22, "R": 23, "S": 24, "T": 25, "V": 26, "W": 27, "X": 28, "Y": 29,
}
num := 0
for i := len(id) - 1; i >= 0; i-- {
c := string(id[i])
index, ok := charset[c]
if !ok {
return 0, fmt.Errorf("invalid char for course id %s", c)
}
num = num*30 + index
}
return num, nil
}
func CourseIdToDataId(id string) (uint64, error) {
num, err := CourseIdToNum(id)
if err != nil {
return 0, err
}
leftSide := num
leftSide = leftSide << 34
const leftSideReplaceMask = 0b1111111111110000000000000000000000000000000000
num = num ^ ((num ^ leftSide) & leftSideReplaceMask)
num = num >> 14
num = num ^ 0b00010110100000001110000001111100
return uint64(num), nil
}
func DataIdToCourseId(id uint64, isCourse bool) (string, error) {
charset := "0123456789BCDFGHJKLMNPQRSTVWXY"
if id > 4294967295 {
return "", fmt.Errorf("dataID > 4294967295")
}
var fieldA, fieldB, exed, fieldC, fieldE, fieldF, fieldD, intermediate uint64
fieldA = 0b1000
fieldB = (id - 31) % 64
exed = id ^ 0b00010110100000001110000001111100
fieldC = exed & 0b00000000000011111111111111111111
fieldE = 0b1
fieldF = exed >> 20
if isCourse {
fieldD = 0b0
} else {
fieldD = 0b1
}
intermediate = (fieldA << 40) + (fieldB << 34) + (fieldC << 14) + (fieldD << 13) + (fieldE << 12) + fieldF
converted_id := []byte{}
for intermediate > 0 {
charIndex := intermediate % 30
converted_id = append(converted_id, charset[charIndex])
intermediate = intermediate / 30
}
return string(converted_id), nil
}
func AddDashes(id string) string {
if len(id) != 9 {
return id
}
return fmt.Sprintf("%s-%s-%s", id[0:3], id[3:6], id[6:9])
}
#include <cstdint>
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
std::string dataIdToCourseId(uint64_t id, bool isCourse) {
static char charset[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K',
'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y' };
if(id > 4294967295) {
return "";
}
uint64_t fieldA = 0b1000;
uint64_t fieldB = (id - 31) % 64;
uint64_t exed = id ^ 0b00010110100000001110000001111100;
uint64_t fieldC = exed & 0b00000000000011111111111111111111;
uint64_t fieldE = 0b1;
uint64_t fieldF = exed >> 20;
uint64_t fieldD;
if(isCourse) {
fieldD = 0b0;
} else {
fieldD = 0b1;
}
uint64_t intermediate = (fieldA << 40) + (fieldB << 34) + (fieldC << 14) + (fieldD << 13) + (fieldE << 12) + fieldF;
std::string converted_id = "";
while(intermediate > 0) {
uint64_t charIndex = intermediate % 30;
converted_id += charset[charIndex];
intermediate /= 30;
}
return converted_id;
}
// static const std::vector<std::string> arrayOfTargets
// = { "DR4G0N", "G1BL3V", "D14LG4", "DR34M", "W4RSPY", "M4K3R", "T3ND0G", "4SP3N", "0TT3R", "P41K14", "P1GUY",
// "V3L3S", "3NTHR0", "CYNR4", "K1NGB00", "CH3RRY", "D3RG", "C0R4", "S4K1" };
static const std::vector<std::string> arrayOfTargets = { "D3RG", "C0R4", "S4K1" };
uint64_t dataId = 0;
bool isCourse = true;
uint64_t counter = 0;
int main() {
while(counter < 50000000) {
std::string id = dataIdToCourseId(dataId + counter, isCourse);
for(auto& x : arrayOfTargets) {
if(id.find(x) != std::string::npos) {
std::cout << "Found data_id " << (dataId + counter) << " with id " << id << std::endl;
}
}
if(counter % 1000000 == 0) {
std::cout << "Checked " << counter << " ids" << std::endl;
}
counter += 1;
}
}
import sys
# written by mainline2
# free to use in all ways as long as you credit mainline2 as the original author
# based on the algorithm at https://github.com/kinnay/NintendoClients/wiki/Data-Store-Codes#super-mario-maker-2
fieldA = 0b1000
fieldDCourse = 0b0
fieldDMaker = 0b1
fieldE = 0b1
fieldCMask = 0b00000000000011111111111111111111
fieldCShiftRight = 14
fieldFMask = 0b00000000000000000000111111111111
fieldFShiftRight = 20
theXOR = 0b00010110100000001110000001111100
fieldAShiftLeft = 40
fieldBShiftLeft = 34
fieldCShiftLeft = 14
fieldDShiftLeft = 13
fieldEShiftLeft = 12
charset = "0123456789BCDFGHJKLMNPQRSTVWXY"
def usage():
print("Converts SMM2 course or maker IDs to data IDs and vice-versa")
print("Usage: smm2id.py [c2d|d2c|m2d|d2m] <id>")
exit()
def d2x(ids, isCourse):
id = int(ids)
if id > 4294967295:
printf("Id appears too large!")
exit()
fieldB = (id - 31) % 64
exed = id ^ theXOR
fieldC = exed & fieldCMask
fieldF = exed >> fieldFShiftRight
# print("id is %s, fieldA %s fieldB %s fieldC %s fieldD %s fieldE %s fieldF %s" % (id, '{0:b}'.format(fieldA), '{0:b}'.format(fieldB), '{0:b}'.format(fieldC), '{0:b}'.format(fieldD), '{0:b}'.format(fieldE), '{0:b}'.format(fieldF)))
if isCourse:
fieldD = fieldDCourse
else:
fieldD = fieldDMaker
intermediate = (fieldA << fieldAShiftLeft) + (fieldB << fieldBShiftLeft) + (fieldC << fieldCShiftLeft) + (fieldD << fieldDShiftLeft) + (fieldE << fieldEShiftLeft) + fieldF
# print("intermediate is %s" % '{0:b}'.format(intermediate))
converted_id = ""
index = 0
while intermediate > 0:
charIndex = intermediate % 30
converted_id += charset[charIndex]
intermediate //= 30
index += 1
if (index % 3 == 0) and (intermediate > 0):
converted_id += "-"
if isCourse:
print("course id is %s" % converted_id)
else:
print("maker id is %s" % converted_id)
def x2d(ids, isCourse):
uppered = ids.upper()
reverseNoHashes = uppered.replace("-", "")[::-1]
intermediate = 0
for c in reverseNoHashes:
intermediate = (intermediate * 30) + charset.index(c)
# print('{0:b}'.format(intermediate))
fieldF = intermediate & fieldFMask
fieldC = (intermediate >> fieldCShiftRight) & fieldCMask
# print("fieldC %s fieldF %s" % ('{0:b}'.format(fieldC), '{0:b}'.format(fieldF)))
converted_id = ((fieldF << fieldFShiftRight) + fieldC) ^ theXOR
if isCourse:
print("course data id is %s" % converted_id)
else:
print("maker data id is %s" % converted_id)
def main():
if len(sys.argv) != 3:
usage()
cmd = sys.argv[1]
id = sys.argv[2]
if cmd == "c2d":
x2d(id, True)
elif cmd == "d2c":
d2x(id, True)
elif cmd == "m2d":
x2d(id, False)
elif cmd == "d2m":
d2x(id, False)
else:
usage()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment