Ejemplo n.º 1
0
/*
 * ident input: identify the game state described in a given image
 */
static int
cmd_ident(int argc, char *argv[])
{
	img_t *image;
	kv_screen_t info;

	if (argc < 1)
		return (EXIT_USAGE);

	if (kv_init(dirname((char *)kv_arg0)) != 0) {
		warnx("failed to initialize masks");
		return (EXIT_FAILURE);
	}

	image = img_read(argv[0]);
	if (image == NULL) {
		warnx("failed to read %s", argv[0]);
		return (EXIT_FAILURE);
	}

	kv_ident(image, &info, KV_IDENT_ALL);
	kv_screen_json(argv[0], 0, 0, &info, NULL, stdout);

	return (EXIT_SUCCESS);
}
Ejemplo n.º 2
0
static int
check_start_frame(video_frame_t *vp, void *rawarg)
{
	kv_screen_t ks;
	int *lastp = rawarg;

	if (*lastp > 0 && vp->vf_frametime - *lastp < 3000)
		return (0);

	kv_ident(&vp->vf_image, &ks, KV_IDENT_START);
	if (ks.ks_events & KVE_RACE_START) {
		*lastp = vp->vf_frametime;
		(void) printf("%d\n", (int) (*lastp / 1000));
		(void) fflush(stdout);
	}

	return (0);
}
Ejemplo n.º 3
0
void
kv_vidctx_frame(const char *framename, int i, int timems,
    img_t *image, kv_vidctx_t *kvp)
{
	int j;
	kv_screen_t *ksp, *pksp, *raceksp;
	kv_screen_t ipks;
	boolean_t itemsdiff, invalid;

	ksp = &kvp->kv_frame;
	pksp = &kvp->kv_pframe;
	raceksp = &kvp->kv_raceframe;

	/*
	 * As we process video frames, we go through a simple state machine:
	 *
	 * (1) We start out waiting for the first RACE_START frame.  We're in
	 *     this state while last_start == -1.  When we see RACE_START, we
	 *     set last_frame to this frame number.
	 *
	 * (2) We ignore the first KV_MIN_RACE_FRAMES after a RACE_START frame
	 *     to avoid catching what may look like multiple start frames right
	 *     next to each other.  This also avoids pointless changes in player
	 *     position in the first few seconds.
	 *
	 * (3) While the race is ongoing, we track player positions until we see
	 *     a RACE_DONE frame (indicating the race was completed) or another
	 *     RACE_START frame (indicating that the race was aborted and
	 *     another race was started).  If we see a normal RACE_DONE frame,
	 *     we go back to the first state, waiting for another RACE_START
	 *     frame.
	 */
	if (kvp->kv_last_start != -1 &&
	    i - kvp->kv_last_start < KV_MIN_RACE_FRAMES)
		/* Skip the first frames after a start. See above. */
		return;

	bcopy(ksp, &ipks, sizeof (ipks));
	if (kv_debug > 0)
		(void) printf("%s\n", framename);
	/* XXX why would this include characters? */
	kv_ident(image, ksp, KV_IDENT_NOTRACK);

	if (ksp->ks_events & KVE_RACE_START) {
		if (kvp->kv_last_start != -1) {
			(void) fprintf(stderr, "%s (time %dm:%02ds): "
			    "new race begun (previous one aborted)",
			    framename, (int)((double)timems / MILLISEC) / 60,
			    timems % 60);
		}

		kv_ident(image, ksp, KV_IDENT_ALL);
		bcopy(ksp, &kvp->kv_startbuffer[i % KV_STARTFRAMES],
		    sizeof (ksp));
		kv_vidctx_chars(kvp, ksp, i);
		kvp->kv_last_start = i;
		*pksp = *ksp;
		*raceksp = *ksp;
		kv_vidctx_frame_emit(kvp, framename, i, timems, image,
		    ksp, NULL, stdout);
		bzero(&kvp->kv_startbuffer[0], sizeof (kvp->kv_startbuffer));
		return;
	}

	/*
	 * Skip frames if we're not currently inside a race.
	 */
	if (kvp->kv_last_start == -1) {
		bcopy(ksp, &kvp->kv_startbuffer[i % KV_STARTFRAMES],
		    sizeof (*ksp));
		return;
	}

	/*
	 * kv_screen_invalid() ignores screens that have a different number of
	 * players than the initial race screen.  This is rare, since on most
	 * tracks we use the rank numerals in each square to reliably report the
	 * number of players.  On such tracks, a wrong number of players
	 * indicates a numeral in transition, in which case the frame can just
	 * be ignored.  However, on Yoshi Valley, we only have numerals for
	 * players who have finished the race, so the number of players can
	 * easily be wrong until the race is over (even after some players have
	 * finished).  In order to get the correct race times, we must not
	 * ignore such frames.  We fix this by simply bumping up the number of
	 * players on that track.  That's sufficient, since the later player
	 * fields will be initialized to the "unknown" values.  Of course,
	 * consumers need to be able to handle them.
	 */
	if (ksp->ks_nplayers > 1 &&
	    ksp->ks_nplayers < raceksp->ks_nplayers &&
	    raceksp->ks_track[0] == 'y')
		ksp->ks_nplayers = raceksp->ks_nplayers;

	/*
	 * Update item box state.  This always operates on the immediately
	 * previous frame (saved at the start of this function), rather than the
	 * representative frame for the current game state, because there are
	 * item changes with each frame that don't represent logically different
	 * game states.  That's also why we do this every frame, not just those
	 * that we save.
	 */
	for (j = 0; j < ksp->ks_nplayers; j++)
		kv_vidctx_items(ksp, &ipks, j);

	itemsdiff = kv_screen_compare_items(ksp, pksp, kvp->kv_flags) != 0;
	invalid = kv_screen_invalid(ksp, pksp, raceksp) != 0;

	/*
	 * Normally we would omit invalid frames, but item detection is
	 * sensitive to dropping individual frames, and we want to emit state
	 * changes as soon as they happen, even if the rest of the frame would
	 * have been invalid.  But we still don't want to emit an invalid frame,
	 * so for this very specific case, we fake up the state based on the
	 * last one we saw, but ignoring any events (which are generally
	 * one-frame-only).
	 */
	if (itemsdiff && invalid) {
		ksp->ks_events = 0;
		ksp->ks_nplayers = pksp->ks_nplayers;
		for (j = 0; j < ksp->ks_nplayers; j++) {
			ksp->ks_players[j].kp_place =
			    pksp->ks_players[j].kp_place;
			ksp->ks_players[j].kp_lapnum =
			    pksp->ks_players[j].kp_lapnum;
		}

		invalid = B_FALSE;
	}

	if (invalid)
		return;

	if (itemsdiff == 0 &&
	    kv_screen_compare(ksp, pksp, raceksp, kvp->kv_flags) == 0)
		return;

	/*
	 * In Yoshi Valley (and only this rare case), we must explicitly fill in
	 * the last place finisher, since we usually won't have detected it by
	 * itself.
	 */
	if (raceksp->ks_track[0] == 'y' && ksp->ks_events & KVE_RACE_DONE) {
		for (j = 0; j < ksp->ks_nplayers; j++) {
			if (ksp->ks_players[j].kp_place == 0) {
				ksp->ks_players[j].kp_place =
				    ksp->ks_nplayers;
				ksp->ks_players[j].kp_placescore = 0.0001;
				break;
			}
		}
	}

	kv_vidctx_frame_emit(kvp, framename, i, timems, image, ksp,
	    raceksp, stdout);
	*pksp = *ksp;

	if (ksp->ks_events & KVE_RACE_DONE)
		kvp->kv_last_start = -1;
}