# Copyright (c) 2022-2026 Thomas Frohwein
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

package IndieRunner::Engine::Godot;

=head1 NAME

IndieRunner::Engine::Godot - Godot engine module

=cut

use v5.36;
use version 0.77; our $VERSION = version->declare('v0.0.1');
use English;

use parent 'IndieRunner::Engine';

use Carp;
use Cwd;
use Readonly;

use IndieRunner::Helpers qw( match_bin_file match_bin_file_head );

=head1 DESCRIPTION

Module to set up and launch games made with the Godot game engine.
Currently supports games made with Godot version 3.x or 4.x.

=head2 Status

Workarounds:

=over 8

=item Symlink hack to simulate the use of bundled Godot binary (ARGV0_SYMLINK)

=back

Limitations on OpenBSD include:

=over 8

=item Missing support for some GDNative/GDExtension modules

=item No support for encrypted Godot games

=back

=head1 METHODS

=over 8

=cut

use constant PACK_HEADER_MAGIC =>	'GDPC';
use constant ARGV0_SYMLINK =>		'.indierunner-godot-helper';

my $game_file;

# keys: OS
# values: array of runtimes for each pack version. Position in array == pack format version
#         for example: [ 'godot2', 'godot3', 'godot44', 'godot45' ]
Readonly my %os_pack_version_runtimes => {
	'openbsd'	=> [	'',
				'/usr/local/bin/godot3',
				'/usr/local/bin/godot44',
				'/usr/local/bin/godot',
			   ],
};

=item get_pack_format_version()

Helper function to determine the Godot pack format version.

=cut

sub get_pack_format_version() {
	my @files = glob( "*.pck *.x86_64 *.x86 *.exe Melt_Them_All" );
	for my $f ( @files ) {
		my $pack_header_bytes;

		# [\x00-\x03] - marker for pack version for Godot 2 (\x00) to
		# Godot 4.5+ (\x03)
		next unless -f $f;
		if ( $f =~ /\.pck$/ ) {
			# .pck files have the signature at the beginning, so don't load the whole file
			$pack_header_bytes = match_bin_file_head( 'GDPC[\x00-\x09]', $f, 8 );
		}
		else {
			$pack_header_bytes = match_bin_file( 'GDPC[\x00-\x09]', $f );
		}
		next unless $pack_header_bytes;
		$game_file = $f;
		my $pack_format_version = hex unpack( 'H2', substr($pack_header_bytes, -1));
		return $pack_format_version if $pack_format_version;
	}
	die "Failed to find pack format version";
}

=item detect_game()

Heuristic to return the name of the game (if possible).

=cut

sub detect_game( $self ) {
	my @pck_files =	glob '*.pck';
	return undef unless @pck_files;
	return $pck_files[0] =~ s/\.pck$//r;
}

=item get_bin()

Create a local symlink to the appropriate binary, to work around issues with games that expect to be called from a binary in the same location as the other files. The binary is determined by the pack format version that is found.

=cut

sub get_bin( $self ) {
	my $pack_format_version = get_pack_format_version();
	my $bin = ${ $os_pack_version_runtimes{ $OSNAME } }[ $pack_format_version ];

	croak "No binary for Godot pack format version $pack_format_version on $OSNAME"
		unless $bin;

	# XXX: need to launch with full path to ARGV0_SYMLINK, otherwise Brotato
	#      and The Case of the Golden Idol can't detect their DLC if present.
	$$self{ mode_obj }->insert( $bin, ARGV0_SYMLINK );
	return cwd() . '/' . ARGV0_SYMLINK;
}

=item get_args_ref()

Return arguments required for execution (main pack file and verbosity arguments).

=cut

sub get_args_ref( $self ) {
	my @args = (
		'--quiet',
		'--main-pack',
		$game_file,
		);
	return \@args;
}

1;

__END__

=back

=head1 SEE ALSO

L<IndieRunner::Engine>

=head1 AUTHOR

Thomas Frohwein E<lt>thfr@cpan.orgE<gt>.

=head1 COPYRIGHT

Copyright 2022-2025 by Thomas Frohwein E<lt>thfr@cpan.orgE<gt>.

This program is free software; you can redistribute it and/or modify it under the ISC license.
