Skip to content

Commit

Permalink
Metaprogram (#3968)
Browse files Browse the repository at this point in the history
* metaprogram.h

Created by Mr. Griffin. Removed non-relevant parts

* Added DEFAULT/DEFAULT_2 macros

Also added a demonstration in battle_main

* Removed GET_ARGS

* Expanded DEFAULT

Because why not?

* Added EXCEPT

Expands to everything but the first x arguments.

* Added BIT_INDEX (thanks to MGriffin) and COMPRESS_BIT macros

These let you compress a bit up to a word in size inside a single byte and uncompress at the same time. BIT_INDEX just tells you where the bit is.

* Updated HANDLE_EXPANDED_MOVE_NAME

---------

Co-authored-by: Martin Griffin <martinrgriffin@gmail.com>
  • Loading branch information
cfmnephrite and mrgriffin authored Feb 4, 2024
1 parent 691b187 commit 7ae50ea
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 79 deletions.
14 changes: 1 addition & 13 deletions include/global.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "config.h" // we need to define config before gba headers as print stuff needs the functions nulled before defines.
#include "gba/gba.h"
#include "fpmath.h"
#include "metaprogram.h"
#include "constants/global.h"
#include "constants/flags.h"
#include "constants/vars.h"
Expand Down Expand Up @@ -123,19 +124,6 @@
#define NUM_FLAG_BYTES ROUND_BITS_TO_BYTES(FLAGS_COUNT)
#define NUM_TRENDY_SAYING_BYTES ROUND_BITS_TO_BYTES(NUM_TRENDY_SAYINGS)

// Calls m0/m1/.../m8 depending on how many arguments are passed.
#define VARARG_8(m, ...) CAT(m, NARG_8(__VA_ARGS__))(__VA_ARGS__)

// This returns the number of arguments passed to it (up to 8).
#define NARG_8(...) NARG_8_(_, ##__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define NARG_8_(_, a, b, c, d, e, f, g, h, N, ...) N

#define CAT(a, b) CAT_(a, b)
#define CAT_(a, b) a ## b

#define STR(...) STR_(__VA_ARGS__)
#define STR_(...) #__VA_ARGS__

// Converts a string to a compound literal, essentially making it a pointer to const u8
#define COMPOUND_STRING(str) (const u8[]) _(str)

Expand Down
131 changes: 131 additions & 0 deletions include/metaprogram.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/* Macros to aid with metaprogramming. */
#ifndef METAPROGRAM_H
#define METAPROGRAM_H

/* Calls m0/m1/.../m8 depending on how many arguments are passed. */
#define VARARG_8(m, ...) CAT(m, NARG_8(__VA_ARGS__))(__VA_ARGS__)

/* Returns the number of arguments passed to it (up to 8). */
#define NARG_8(...) NARG_8_(_, ##__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define NARG_8_(_, a, b, c, d, e, f, g, h, N, ...) N

/* Expands 'a' and 'b' and then concatenates them. */
#define CAT(a, b) CAT_(a, b)
#define CAT_(a, b) a ## b

/* Expands '__VA_ARGS__' and then stringizes them. */
#define STR(...) STR_(__VA_ARGS__)
#define STR_(...) #__VA_ARGS__

/* Expands to the first/second/third/fourth argument. */
#define FIRST(a, ...) a
#define SECOND(a, ...) __VA_OPT__(FIRST(__VA_ARGS__))
#define THIRD(a, ...) __VA_OPT__(SECOND(__VA_ARGS__))
#define FOURTH(a, ...) __VA_OPT__(THIRD(__VA_ARGS__))

/* Expands to everything but the first x arguments */
#define EXCEPT_1(a, ...) __VA_OPT__(__VA_ARGS__)
#define EXCEPT_2(a, ...) __VA_OPT__(EXCEPT_1(__VA_ARGS__))
#define EXCEPT_3(a, ...) __VA_OPT__(EXCEPT_2(__VA_ARGS__))
#define EXCEPT_4(a, ...) __VA_OPT__(EXCEPT_3(__VA_ARGS__))

/* 'UNPACK (x, y, z)' expands to 'x, y, z'.
* Useful for passing arguments which may contain commas into a macro. */
#define UNPACK(...) __VA_ARGS__

/* Expands to 'macro(...args, ...)'. */
#define INVOKE_WITH(macro, args, ...) INVOKE_WITH_(macro, UNPACK args __VA_OPT__(, __VA_ARGS__))
#define INVOKE_WITH_(macro, ...) macro(__VA_ARGS__)

/* Recursive macros.
* Based on https://www.scs.stanford.edu/~dm/blog/va-opt.html
*
* Macros prefixed with R_ are recursive, to correctly expand them the
* top-level macro which references them should use 'RECURSIVELY' around
* them. 'RECURSIVELY' cannot be nested, hence the top-level macro must
* use it so that a recursive macro is able to reference another
* recursive macro. */

#define RECURSIVELY(...) RECURSIVELY_4(RECURSIVELY_4(RECURSIVELY_4(RECURSIVELY_4(__VA_ARGS__))))
#define RECURSIVELY_4(...) RECURSIVELY_3(RECURSIVELY_3(RECURSIVELY_3(RECURSIVELY_3(__VA_ARGS__))))
#define RECURSIVELY_3(...) RECURSIVELY_2(RECURSIVELY_2(RECURSIVELY_2(RECURSIVELY_2(__VA_ARGS__))))
#define RECURSIVELY_2(...) RECURSIVELY_1(RECURSIVELY_1(RECURSIVELY_1(RECURSIVELY_1(__VA_ARGS__))))
#define RECURSIVELY_1(...) __VA_ARGS__

/* Useful for deferring expansion until the second scan. See
* https://www.scs.stanford.edu/~dm/blog/va-opt.html for more info. */
#define PARENS ()

/* Expands to 'macro(a)' for each 'a' in '...' */
#define R_FOR_EACH(macro, ...) __VA_OPT__(R_FOR_EACH_(macro, __VA_ARGS__))
#define R_FOR_EACH_(macro, a, ...) macro(a) __VA_OPT__(R_FOR_EACH_P PARENS (macro, __VA_ARGS__))
#define R_FOR_EACH_P() R_FOR_EACH_

/* Expands to 'macro(...args, a)' for each 'a' in '...'. */
#define R_FOR_EACH_WITH(macro, args, ...) __VA_OPT__(R_FOR_EACH_WITH_(macro, args, __VA_ARGS__))
#define R_FOR_EACH_WITH_(macro, args, a, ...) INVOKE_WITH(macro, args, a) __VA_OPT__(R_FOR_EACH_WITH_P PARENS (macro, args, __VA_ARGS__))
#define R_FOR_EACH_WITH_P() R_FOR_EACH_WITH_

/* Picks the xth VA_ARG if it exists, otherwise returns a default value */
#define DEFAULT(_default, ...) FIRST(__VA_OPT__(__VA_ARGS__, ) _default)
#define DEFAULT_2(_default, ...) DEFAULT(_default __VA_OPT__(, SECOND(__VA_ARGS__)))
#define DEFAULT_3(_default, ...) DEFAULT(_default __VA_OPT__(, THIRD(__VA_ARGS__)))
#define DEFAULT_4(_default, ...) DEFAULT(_default __VA_OPT__(, FOURTH(__VA_ARGS__)))

/* (Credit to MGriffin) A rather monstrous way of finding the set bit in a word.
Invalid input causes a compiler error. Sample: https://cexplore.karathan.at/z/x1hm7B */
#define BIT_INDEX(n) \
(n) == (1 << 0) ? 0 : \
(n) == (1 << 1) ? 1 : \
(n) == (1 << 2) ? 2 : \
(n) == (1 << 3) ? 3 : \
(n) == (1 << 4) ? 4 : \
(n) == (1 << 5) ? 5 : \
(n) == (1 << 6) ? 6 : \
(n) == (1 << 7) ? 7 : \
(n) == (1 << 8) ? 8 : \
(n) == (1 << 9) ? 9 : \
(n) == (1 << 10) ? 10 : \
(n) == (1 << 11) ? 11 : \
(n) == (1 << 12) ? 12 : \
(n) == (1 << 13) ? 13 : \
(n) == (1 << 14) ? 14 : \
(n) == (1 << 15) ? 15 : \
(n) == (1 << 16) ? 16 : \
(n) == (1 << 17) ? 17 : \
(n) == (1 << 18) ? 18 : \
(n) == (1 << 19) ? 19 : \
(n) == (1 << 20) ? 20 : \
(n) == (1 << 21) ? 21 : \
(n) == (1 << 22) ? 22 : \
(n) == (1 << 23) ? 23 : \
(n) == (1 << 24) ? 24 : \
(n) == (1 << 25) ? 25 : \
(n) == (1 << 26) ? 26 : \
(n) == (1 << 27) ? 27 : \
(n) == (1 << 28) ? 28 : \
(n) == (1 << 29) ? 29 : \
(n) == (1 << 30) ? 30 : \
(n) == (1 << 31) ? 31 : \
*(u32 *)NULL

#define COMPRESS_BITS_0 0, 1
#define COMPRESS_BITS_1 1, 1
#define COMPRESS_BITS_2 2, 1
#define COMPRESS_BITS_3 3, 1
#define COMPRESS_BITS_4 4, 1
#define COMPRESS_BITS_5 5, 1
#define COMPRESS_BITS_6 6, 1
#define COMPRESS_BITS_7 7, 1

/* Will try and compress a set bit (or up to three sequential bits) into a single byte
Input must be of the form (upper << lower) where upper can be up to 3, lower up to 31 */
#define COMPRESS_BITS(_val) COMPRESS_BITS_STEP_2 _val
#define COMPRESS_BITS_STEP_2(_unpacked) COMPRESS_BITS_STEP_3(COMPRESS_BITS_## _unpacked)
#define COMPRESS_BITS_STEP_3(...) COMPRESS_BITS_STEP_4(__VA_ARGS__)
#define COMPRESS_BITS_STEP_4(upper, lower) (((upper % 8) << 5) + (BIT_INDEX(lower)))

/* Will read a compressed bit stored by COMPRESS_BIT into a single byte */
#define UNCOMPRESS_BITS(compressed) ((compressed >> 5) << (compressed & 0x1F))

#endif
126 changes: 62 additions & 64 deletions src/battle_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -322,85 +322,83 @@ const u8 gTypeNames[NUMBER_OF_MON_TYPES][TYPE_NAME_LENGTH + 1] =
[TYPE_FAIRY] = _("Fairy"),
};

#define DEFAULT_MONEY 5
#define DEFAULT_BALL ITEM_POKE_BALL

#define TRAINER_CLASS(trainerClass, trainerName, trainerMoney, trainerBall) \
[TRAINER_CLASS_##trainerClass] = \
{ \
.name = _(trainerName), \
.money = trainerMoney, \
.ball = trainerBall, \
// extra args are money and ball
#define TRAINER_CLASS(trainerClass, trainerName, ...) \
[TRAINER_CLASS_##trainerClass] = \
{ \
.name = _(trainerName), \
.money = DEFAULT(5, __VA_ARGS__), \
.ball = DEFAULT_2(ITEM_POKE_BALL, __VA_ARGS__), \
}

const struct TrainerClass gTrainerClasses[TRAINER_CLASS_COUNT] =
{
TRAINER_CLASS(PKMN_TRAINER_1, "{PKMN} TRAINER", DEFAULT_MONEY, DEFAULT_BALL),
TRAINER_CLASS(PKMN_TRAINER_2, "{PKMN} TRAINER", DEFAULT_MONEY, DEFAULT_BALL),
TRAINER_CLASS(HIKER, "HIKER", 10, DEFAULT_BALL),
TRAINER_CLASS(TEAM_AQUA, "TEAM AQUA", 5, DEFAULT_BALL),
TRAINER_CLASS(PKMN_TRAINER_1, "{PKMN} TRAINER"),
TRAINER_CLASS(PKMN_TRAINER_2, "{PKMN} TRAINER"),
TRAINER_CLASS(HIKER, "HIKER", 10),
TRAINER_CLASS(TEAM_AQUA, "TEAM AQUA"),
TRAINER_CLASS(PKMN_BREEDER, "{PKMN} BREEDER", 10, B_TRAINER_CLASS_POKE_BALLS >= GEN_8 ? ITEM_HEAL_BALL : ITEM_FRIEND_BALL),
TRAINER_CLASS(COOLTRAINER, "COOLTRAINER", 12, ITEM_ULTRA_BALL),
TRAINER_CLASS(BIRD_KEEPER, "BIRD KEEPER", 8, DEFAULT_BALL),
TRAINER_CLASS(BIRD_KEEPER, "BIRD KEEPER", 8),
TRAINER_CLASS(COLLECTOR, "COLLECTOR", 15, ITEM_PREMIER_BALL),
TRAINER_CLASS(SWIMMER_M, "SWIMMER♂", 2, ITEM_DIVE_BALL),
TRAINER_CLASS(TEAM_MAGMA, "TEAM MAGMA", 5, DEFAULT_BALL),
TRAINER_CLASS(EXPERT, "EXPERT", 10, DEFAULT_BALL),
TRAINER_CLASS(AQUA_ADMIN, "AQUA ADMIN", 10, DEFAULT_BALL),
TRAINER_CLASS(TEAM_MAGMA, "TEAM MAGMA"),
TRAINER_CLASS(EXPERT, "EXPERT", 10),
TRAINER_CLASS(AQUA_ADMIN, "AQUA ADMIN", 10),
TRAINER_CLASS(BLACK_BELT, "BLACK BELT", 8, ITEM_ULTRA_BALL),
TRAINER_CLASS(AQUA_LEADER, "AQUA LEADER", 20, ITEM_MASTER_BALL),
TRAINER_CLASS(HEX_MANIAC, "HEX MANIAC", 6, DEFAULT_BALL),
TRAINER_CLASS(AROMA_LADY, "AROMA LADY", 10, DEFAULT_BALL),
TRAINER_CLASS(RUIN_MANIAC, "RUIN MANIAC", 15, DEFAULT_BALL),
TRAINER_CLASS(INTERVIEWER, "INTERVIEWER", 12, DEFAULT_BALL),
TRAINER_CLASS(TUBER_F, "TUBER", 1, DEFAULT_BALL),
TRAINER_CLASS(TUBER_M, "TUBER", 1, DEFAULT_BALL),
TRAINER_CLASS(LADY, "LADY", 50, DEFAULT_BALL),
TRAINER_CLASS(BEAUTY, "BEAUTY", 20, DEFAULT_BALL),
TRAINER_CLASS(RICH_BOY, "RICH BOY", 50, DEFAULT_BALL),
TRAINER_CLASS(POKEMANIAC, "POKéMANIAC", 15, DEFAULT_BALL),
TRAINER_CLASS(GUITARIST, "GUITARIST", 8, DEFAULT_BALL),
TRAINER_CLASS(KINDLER, "KINDLER", 8, DEFAULT_BALL),
TRAINER_CLASS(CAMPER, "CAMPER", 4, DEFAULT_BALL),
TRAINER_CLASS(PICNICKER, "PICNICKER", 4, DEFAULT_BALL),
TRAINER_CLASS(BUG_MANIAC, "BUG MANIAC", 15, DEFAULT_BALL),
TRAINER_CLASS(PSYCHIC, "PSYCHIC", 6, DEFAULT_BALL),
TRAINER_CLASS(HEX_MANIAC, "HEX MANIAC", 6),
TRAINER_CLASS(AROMA_LADY, "AROMA LADY", 10),
TRAINER_CLASS(RUIN_MANIAC, "RUIN MANIAC", 15),
TRAINER_CLASS(INTERVIEWER, "INTERVIEWER", 12),
TRAINER_CLASS(TUBER_F, "TUBER", 1),
TRAINER_CLASS(TUBER_M, "TUBER", 1),
TRAINER_CLASS(LADY, "LADY", 50),
TRAINER_CLASS(BEAUTY, "BEAUTY", 20),
TRAINER_CLASS(RICH_BOY, "RICH BOY", 50),
TRAINER_CLASS(POKEMANIAC, "POKéMANIAC", 15),
TRAINER_CLASS(GUITARIST, "GUITARIST", 8),
TRAINER_CLASS(KINDLER, "KINDLER", 8),
TRAINER_CLASS(CAMPER, "CAMPER", 4),
TRAINER_CLASS(PICNICKER, "PICNICKER", 4),
TRAINER_CLASS(BUG_MANIAC, "BUG MANIAC", 15),
TRAINER_CLASS(PSYCHIC, "PSYCHIC", 6),
TRAINER_CLASS(GENTLEMAN, "GENTLEMAN", 20, ITEM_LUXURY_BALL),
TRAINER_CLASS(ELITE_FOUR, "ELITE FOUR", 25, ITEM_ULTRA_BALL),
TRAINER_CLASS(LEADER, "LEADER", 25, DEFAULT_BALL),
TRAINER_CLASS(SCHOOL_KID, "SCHOOL KID", 5, DEFAULT_BALL),
TRAINER_CLASS(SR_AND_JR, "SR. AND JR.", 4, DEFAULT_BALL),
TRAINER_CLASS(WINSTRATE, "WINSTRATE", 10, DEFAULT_BALL),
TRAINER_CLASS(POKEFAN, "POKéFAN", 20, DEFAULT_BALL),
TRAINER_CLASS(YOUNGSTER, "YOUNGSTER", 4, DEFAULT_BALL),
TRAINER_CLASS(CHAMPION, "CHAMPION", 50, DEFAULT_BALL),
TRAINER_CLASS(LEADER, "LEADER", 25),
TRAINER_CLASS(SCHOOL_KID, "SCHOOL KID"),
TRAINER_CLASS(SR_AND_JR, "SR. AND JR.", 4),
TRAINER_CLASS(WINSTRATE, "WINSTRATE", 10),
TRAINER_CLASS(POKEFAN, "POKéFAN", 20),
TRAINER_CLASS(YOUNGSTER, "YOUNGSTER", 4),
TRAINER_CLASS(CHAMPION, "CHAMPION", 50),
TRAINER_CLASS(FISHERMAN, "FISHERMAN", 10, B_TRAINER_CLASS_POKE_BALLS >= GEN_8 ? ITEM_DIVE_BALL : ITEM_LURE_BALL),
TRAINER_CLASS(TRIATHLETE, "TRIATHLETE", 10, DEFAULT_BALL),
TRAINER_CLASS(DRAGON_TAMER, "DRAGON TAMER", 12, DEFAULT_BALL),
TRAINER_CLASS(NINJA_BOY, "NINJA BOY", 3, DEFAULT_BALL),
TRAINER_CLASS(BATTLE_GIRL, "BATTLE GIRL", 6, DEFAULT_BALL),
TRAINER_CLASS(PARASOL_LADY, "PARASOL LADY", 10, DEFAULT_BALL),
TRAINER_CLASS(TRIATHLETE, "TRIATHLETE", 10),
TRAINER_CLASS(DRAGON_TAMER, "DRAGON TAMER", 12),
TRAINER_CLASS(NINJA_BOY, "NINJA BOY", 3),
TRAINER_CLASS(BATTLE_GIRL, "BATTLE GIRL", 6),
TRAINER_CLASS(PARASOL_LADY, "PARASOL LADY", 10),
TRAINER_CLASS(SWIMMER_F, "SWIMMER♀", 2, ITEM_DIVE_BALL),
TRAINER_CLASS(TWINS, "TWINS", 3, DEFAULT_BALL),
TRAINER_CLASS(SAILOR, "SAILOR", 8, DEFAULT_BALL),
TRAINER_CLASS(COOLTRAINER_2, "COOLTRAINER", DEFAULT_MONEY, ITEM_ULTRA_BALL),
TRAINER_CLASS(MAGMA_ADMIN, "MAGMA ADMIN", 10, DEFAULT_BALL),
TRAINER_CLASS(RIVAL, "{PKMN} TRAINER", 15, DEFAULT_BALL),
TRAINER_CLASS(BUG_CATCHER, "BUG CATCHER", 4, DEFAULT_BALL),
TRAINER_CLASS(PKMN_RANGER, "{PKMN} RANGER", 12, DEFAULT_BALL),
TRAINER_CLASS(TWINS, "TWINS", 3),
TRAINER_CLASS(SAILOR, "SAILOR", 8),
TRAINER_CLASS(COOLTRAINER_2, "COOLTRAINER", 5, ITEM_ULTRA_BALL),
TRAINER_CLASS(MAGMA_ADMIN, "MAGMA ADMIN", 10),
TRAINER_CLASS(RIVAL, "{PKMN} TRAINER", 15),
TRAINER_CLASS(BUG_CATCHER, "BUG CATCHER", 4),
TRAINER_CLASS(PKMN_RANGER, "{PKMN} RANGER", 12),
TRAINER_CLASS(MAGMA_LEADER, "MAGMA LEADER", 20, ITEM_MASTER_BALL),
TRAINER_CLASS(LASS, "LASS", 4, DEFAULT_BALL),
TRAINER_CLASS(YOUNG_COUPLE, "YOUNG COUPLE", 8, DEFAULT_BALL),
TRAINER_CLASS(OLD_COUPLE, "OLD COUPLE", 10, DEFAULT_BALL),
TRAINER_CLASS(SIS_AND_BRO, "SIS AND BRO", 3, DEFAULT_BALL),
TRAINER_CLASS(SALON_MAIDEN, "SALON MAIDEN", DEFAULT_MONEY, DEFAULT_BALL),
TRAINER_CLASS(DOME_ACE, "DOME ACE", DEFAULT_MONEY, DEFAULT_BALL),
TRAINER_CLASS(PALACE_MAVEN, "PALACE MAVEN", DEFAULT_MONEY, DEFAULT_BALL),
TRAINER_CLASS(ARENA_TYCOON, "ARENA TYCOON", DEFAULT_MONEY, DEFAULT_BALL),
TRAINER_CLASS(FACTORY_HEAD, "FACTORY HEAD", DEFAULT_MONEY, DEFAULT_BALL),
TRAINER_CLASS(PIKE_QUEEN, "PIKE QUEEN", DEFAULT_MONEY, DEFAULT_BALL),
TRAINER_CLASS(PYRAMID_KING, "PYRAMID KING", DEFAULT_MONEY, DEFAULT_BALL),
TRAINER_CLASS(RS_PROTAG, "{PKMN} TRAINER", DEFAULT_MONEY, DEFAULT_BALL),
TRAINER_CLASS(LASS, "LASS", 4),
TRAINER_CLASS(YOUNG_COUPLE, "YOUNG COUPLE", 8),
TRAINER_CLASS(OLD_COUPLE, "OLD COUPLE", 10),
TRAINER_CLASS(SIS_AND_BRO, "SIS AND BRO", 3),
TRAINER_CLASS(SALON_MAIDEN, "SALON MAIDEN"),
TRAINER_CLASS(DOME_ACE, "DOME ACE"),
TRAINER_CLASS(PALACE_MAVEN, "PALACE MAVEN"),
TRAINER_CLASS(ARENA_TYCOON, "ARENA TYCOON"),
TRAINER_CLASS(FACTORY_HEAD, "FACTORY HEAD"),
TRAINER_CLASS(PIKE_QUEEN, "PIKE QUEEN"),
TRAINER_CLASS(PYRAMID_KING, "PYRAMID KING"),
TRAINER_CLASS(RS_PROTAG, "{PKMN} TRAINER"),
};

static void (* const sTurnActionsFuncsTable[])(void) =
Expand Down
3 changes: 1 addition & 2 deletions src/data/moves_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@

// The Gen. 4+ contest data comes from urpg's contest movedex.

#define FIRST(a, ...) a
#if B_EXPANDED_MOVE_NAMES == TRUE
#define HANDLE_EXPANDED_MOVE_NAME(_name, ...) COMPOUND_STRING(FIRST(__VA_OPT__(__VA_ARGS__, ) _name))
#define HANDLE_EXPANDED_MOVE_NAME(_name, ...) COMPOUND_STRING(DEFAULT(_name, __VA_ARGS__))
#else
#define HANDLE_EXPANDED_MOVE_NAME(_name, ...) COMPOUND_STRING(_name)
#endif
Expand Down

0 comments on commit 7ae50ea

Please sign in to comment.