#!/usr/bin/perl -w # # Copyright 2025 Henri Verbeet # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA use strict; use warnings; use JSON; use open ':utf8'; binmode STDOUT, ':utf8'; sub method_name($) { shift->{selector} =~ s/(:.*)//r; } sub method_parameters($$) { my ($method, $method_type) = @_; my $parameters = join ", ", $method_type eq "class" ? () : "id self", map {"$_->{type} $_->{name}"} @{$method->{parameters}}; length $parameters ? $parameters : "void"; } sub send_function($) { shift->{'return-float'} ? "vkd3d_objc_msgSend_fpret" : "objc_msgSend"; } sub invocation_type($$) { my ($method, $method_type) = @_; "$method->{'return-type'} (*)(" . join(", ", $method_type eq "class" ? "Class" : "id", "SEL", map {$_->{type}} @{$method->{parameters}}) . ")"; } sub invocation_parameters($$$) { my ($method, $interface_name, $method_type) = @_; join ", ", $method_type eq "class" ? "objc_getClass(\"$interface_name\")" : "self", "sel_registerName(\"$method->{selector}\")", map {$_->{name}} @{$method->{parameters}}; } sub invocation($$$) { my ($method, $interface_name, $method_type) = @_; ($method->{'return-type'} eq "void" ? "" : "return ") . "((${\invocation_type $method, $method_type})f)" . "(${\invocation_parameters $method, $interface_name, $method_type});"; } sub print_method($$$) { my ($method, $interface_name, $method_type) = @_; print "static inline $method->{'return-type'} " . "${interface_name}_${\method_name $method}(${\method_parameters $method, $method_type})\n"; print "{\n"; print " void *f = ${\send_function $method};\n"; print " ${\invocation $method, $interface_name, $method_type}\n"; print "}\n\n"; } sub print_property($$) { my ($property, $interface_name) = @_; my $method = { 'return-type' => $property->{type}, 'return-float' => $property->{float}, selector => $property->{getter} // $property->{name}, }; my $method_type = $property->{class} ? "class" : "instance"; print_method $method, $interface_name, $method_type; if (!$property->{readonly}) { $method->{'return-type'} = "void"; $method->{'return-float'} = 0; $method->{selector} = "set${\ucfirst $property->{name}}:"; $method->{parameters} = [$property]; print_method $method, $interface_name, $method_type; } } sub print_interface(_) { my ($interface) = @_; print_method $_, $interface->{name}, "class" foreach (@{$interface->{'class-methods'}}); print_method $_, $interface->{name}, "instance" foreach (@{$interface->{'instance-methods'}}); print_property $_, $interface->{name} foreach (@{$interface->{properties}}); } sub print_header($) { my ($grammar) = @_; my $guard = "__VKD3D_${\uc $grammar->{name}}_H__"; print "/*\n"; print " * This file is automatically generated.\n"; print " * The original source is covered by the following license:\n"; print " *\n"; print map {" * $_" =~ s/ +$//r . "\n"} @{$grammar->{copyright}}; print " */\n\n"; print "#ifndef $guard\n"; print "#define $guard\n\n"; print "#include \n\n"; print "#ifdef __arm64__\n"; print "# define vkd3d_objc_msgSend_fpret objc_msgSend\n"; print "#else\n"; print "# define vkd3d_objc_msgSend_fpret objc_msgSend_fpret\n"; print "#endif /* __arm64__ */\n\n"; print_interface foreach (@{$grammar->{interfaces}}); print "#undef vkd3d_objc_msgSend_fpret\n\n"; print "#endif /* $guard */\n"; } die "No input file specified.\n" unless @ARGV; print_header do { local $/; open my $fh, '<', $ARGV[0] or die $!; decode_json <$fh>; };