nix flake for tm-ce dev on nixos
0
flake.nix
290 lines 8.7 kB view raw
1{ 2 description = "TrainingMode-CommunityEdition build"; 3 4 inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 5 6 outputs = { 7 self, 8 nixpkgs, 9 }: let 10 system = "x86_64-linux"; 11 pkgs = nixpkgs.legacyPackages.${system}; 12 13 tmce-tools = pkgs.stdenv.mkDerivation { 14 pname = "tmce-tools"; 15 version = "1.0"; 16 src = ./bin; 17 nativeBuildInputs = [pkgs.autoPatchelfHook]; 18 buildInputs = [pkgs.stdenv.cc.cc.lib]; 19 installPhase = '' 20 mkdir -p $out/bin 21 cp gc_fst hgecko hmex $out/bin/ 22 chmod +x $out/bin/* 23 ''; 24 }; 25 26 devkitPPC = let 27 image = pkgs.dockerTools.pullImage { 28 imageName = "devkitpro/devkitppc"; 29 imageDigest = "sha256:4c919aa26151dd43d88ca28c922d1fe2409579a8ba60ef56517baf1abdfb1a48"; 30 sha256 = "TjTZiI1EaqU9Hr5e5RmBL2g9gvuY4Ea225zxtkh8DU8="; 31 finalImageName = "devkitpro/devkitppc"; 32 finalImageTag = "20260503"; 33 }; 34 in 35 pkgs.stdenv.mkDerivation { 36 pname = "devkitPPC"; 37 version = "20260503"; 38 src = image; 39 nativeBuildInputs = [pkgs.autoPatchelfHook]; 40 buildInputs = [pkgs.stdenv.cc.cc.lib pkgs.ncurses]; 41 unpackPhase = '' 42 mkdir merged 43 ${pkgs.python3}/bin/python3 -c " 44 import tarfile, json, io 45 with tarfile.open('$src', 'r') as outer: 46 manifest = json.load(outer.extractfile('manifest.json')) 47 for layer_name in manifest[0]['Layers']: 48 layer_data = outer.extractfile(layer_name).read() 49 with tarfile.open(fileobj=io.BytesIO(layer_data)) as layer: 50 for member in layer: 51 if member.name.startswith('opt/devkitpro/devkitPPC') or \ 52 member.name.startswith('opt/devkitpro/libogc') or \ 53 member.name.startswith('opt/devkitpro/portlibs'): 54 layer.extract(member, 'merged', filter='data') 55 " 56 ''; 57 buildPhase = "true"; 58 installPhase = '' 59 mkdir -p $out 60 cp -r merged/opt/devkitpro/devkitPPC $out/ 61 cp -r merged/opt/devkitpro/libogc $out/ 62 cp -r merged/opt/devkitpro/portlibs $out/ 63 ''; 64 }; 65 66 hmex = "${tmce-tools}/bin/hmex"; 67 hgecko = "${tmce-tools}/bin/hgecko"; 68 DEVKITPPC = "${devkitPPC}/devkitPPC"; 69 hmexFlags = "-Wall -Wextra -Wno-char-subscripts -Wno-builtin-declaration-mismatch -Wno-unused-parameter -DTM_DEBUG"; 70 hmexLink = "MexTK/melee.link"; 71 72 compileEvent = { 73 name, 74 sym, 75 srcFiles, 76 datFile ? null, 77 }: 78 pkgs.stdenv.mkDerivation { 79 pname = name; 80 version = "1.0"; 81 src = ./.; 82 nativeBuildInputs = [tmce-tools]; 83 DEVKITPPC = "${devkitPPC}/devkitPPC"; 84 buildPhase = '' 85 mkdir -p build 86 ${hmex} -q \ 87 -l ${hmexLink} \ 88 -f "${hmexFlags}" \ 89 -s ${sym} \ 90 -t MexTK/${sym}.txt \ 91 -o "build/${name}.dat" \ 92 -i ${srcFiles} \ 93 ${pkgs.lib.optionalString (datFile != null) "-dat ${datFile}"} 94 ''; 95 installPhase = '' 96 mkdir -p $out 97 cp build/${name}.dat $out/ 98 ''; 99 }; 100 101 events = { 102 eventMenu = compileEvent { 103 name = "eventMenu"; 104 sym = "tmFunction"; 105 srcFiles = "src/events.c src/menu.c src/osds.c src/savestate_v1.c"; 106 datFile = "dats/eventMenu.dat"; 107 }; 108 labCSS = compileEvent { 109 name = "labCSS"; 110 sym = "cssFunction"; 111 srcFiles = "src/lab_css.c"; 112 datFile = "dats/labCSS.dat"; 113 }; 114 lab = compileEvent { 115 name = "lab"; 116 sym = "evFunction"; 117 srcFiles = "src/lab.c"; 118 datFile = "dats/lab.dat"; 119 }; 120 lcancel = compileEvent { 121 name = "lcancel"; 122 sym = "evFunction"; 123 srcFiles = "src/lcancel.c"; 124 datFile = "dats/lcancel.dat"; 125 }; 126 ledgedash = compileEvent { 127 name = "ledgedash"; 128 sym = "evFunction"; 129 srcFiles = "src/ledgedash.c"; 130 datFile = "dats/ledgedash.dat"; 131 }; 132 wavedash = compileEvent { 133 name = "wavedash"; 134 sym = "evFunction"; 135 srcFiles = "src/wavedash.c"; 136 datFile = "dats/wavedash.dat"; 137 }; 138 powershield = compileEvent { 139 name = "powershield"; 140 sym = "evFunction"; 141 srcFiles = "src/powershield.c"; 142 }; 143 edgeguard = compileEvent { 144 name = "edgeguard"; 145 sym = "evFunction"; 146 srcFiles = "src/edgeguard.c"; 147 }; 148 fc = compileEvent { 149 name = "fc"; 150 sym = "evFunction"; 151 srcFiles = "src/fc.c"; 152 datFile = "dats/ledgedash.dat"; 153 }; 154 sweetspot = compileEvent { 155 name = "sweetspot"; 156 sym = "evFunction"; 157 srcFiles = "src/sweetspot.c"; 158 }; 159 eggs = compileEvent { 160 name = "eggs"; 161 sym = "evFunction"; 162 srcFiles = "src/eggs.c"; 163 }; 164 techchase = compileEvent { 165 name = "techchase"; 166 sym = "evFunction"; 167 srcFiles = "src/techchase.c"; 168 }; 169 slalom = compileEvent { 170 name = "slalom"; 171 sym = "evFunction"; 172 srcFiles = "src/slalom.c"; 173 datFile = "dats/wavedash.dat"; 174 }; 175 }; 176 177 codesGct = pkgs.stdenv.mkDerivation { 178 pname = "codes"; 179 version = "1.0"; 180 src = ./ASM; 181 nativeBuildInputs = [tmce-tools]; 182 DEVKITPPC = "${devkitPPC}/devkitPPC"; 183 buildPhase = '' 184 mkdir -p $out 185 ${hgecko} -q . "$out/codes.gct" 186 ''; 187 installPhase = "true"; 188 }; 189 190 allDat = pkgs.symlinkJoin { 191 name = "tmce-all-dat"; 192 paths = pkgs.lib.attrValues events; 193 }; 194 195 mkISOScript = pkgs.writeShellApplication { 196 name = "mkTM-ISO"; 197 runtimeInputs = [tmce-tools pkgs.xdelta]; 198 text = '' 199 set -e 200 ISO="''${1:-}" 201 OUT="''${2:-TM-CE.iso}" 202 if [ -z "$ISO" ] || [ ! -f "$ISO" ]; then 203 echo "Usage: nix run .#mkISO -- /path/to/vanilla/melee.iso [output.iso]" 204 exit 1 205 fi 206 207 HEADER=$(gc_fst get-header "$ISO") 208 case "$HEADER" in 209 GALE01) PATCH="${self}/Build TM Start.dol/patch.xdelta" ;; 210 GALJ01) PATCH="${self}/Build TM Start.dol/patch_jp.xdelta" ;; 211 *) echo "Error: Unknown game ID '$HEADER' (expected GALE01 or GALJ01)"; exit 1 ;; 212 esac 213 214 TMP=$(mktemp -d) 215 trap 'rm -rf "$TMP"' EXIT 216 217 gc_fst read "$ISO" Start.dol "$TMP/ISOStart.dol" 218 xdelta3 -dfs "$TMP/ISOStart.dol" "$PATCH" "$TMP/Start.dol" 219 220 cp "$ISO" "$OUT" 221 222 gc_fst fs "$OUT" \ 223 delete MvHowto.mth \ 224 delete MvOmake15.mth \ 225 delete MvOpen.mth \ 226 insert TM/eventMenu.dat "${allDat}/eventMenu.dat" \ 227 insert TM/lab.dat "${allDat}/lab.dat" \ 228 insert TM/labCSS.dat "${allDat}/labCSS.dat" \ 229 insert TM/lcancel.dat "${allDat}/lcancel.dat" \ 230 insert TM/ledgedash.dat "${allDat}/ledgedash.dat" \ 231 insert TM/wavedash.dat "${allDat}/wavedash.dat" \ 232 insert TM/powershield.dat "${allDat}/powershield.dat" \ 233 insert TM/edgeguard.dat "${allDat}/edgeguard.dat" \ 234 insert TM/fc.dat "${allDat}/fc.dat" \ 235 insert TM/sweetspot.dat "${allDat}/sweetspot.dat" \ 236 insert TM/eggs.dat "${allDat}/eggs.dat" \ 237 insert TM/techchase.dat "${allDat}/techchase.dat" \ 238 insert TM/slalom.dat "${allDat}/slalom.dat" \ 239 insert codes.gct "${codesGct}/codes.gct" \ 240 insert Start.dol "$TMP/Start.dol" \ 241 insert opening.bnr "${self}/opening.bnr" 242 243 gc_fst set-header "$OUT" "GTME01" "Training Mode Community Edition" 244 245 echo "Built $OUT" 246 ''; 247 }; 248 in { 249 packages.${system} = 250 { 251 inherit tmce-tools devkitPPC; 252 codes = codesGct; 253 inherit allDat; 254 } 255 // events; 256 257 apps.${system} = { 258 mkISO = { 259 type = "app"; 260 program = "${mkISOScript}/bin/mkTM-ISO"; 261 }; 262 }; 263 264 checks.${system} = let 265 eventsCheck = 266 pkgs.linkFarm "check-events" 267 (pkgs.lib.mapAttrsToList (name: pkg: { 268 inherit name; 269 path = pkg; 270 }) 271 events); 272 in 273 events 274 // { 275 all-events = eventsCheck; 276 codes = codesGct; 277 }; 278 279 formatter.${system} = pkgs.alejandra; 280 281 devShells.${system}.default = pkgs.mkShell { 282 buildInputs = with pkgs; [ 283 xdelta 284 tmce-tools 285 devkitPPC 286 ]; 287 DEVKITPPC = "${devkitPPC}/devkitPPC"; 288 }; 289 }; 290}