GCC Code Coverage Report


Directory: avs_core/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 1.0% 14 / 0 / 1391
Functions: 1.3% 3 / 0 / 238
Branches: 0.5% 14 / 0 / 2681

core/parser/script.cpp
Line Branch Exec Source
1 // Avisynth v2.5. Copyright 2002 Ben Rudiak-Gould et al.
2 // http://avisynth.nl
3
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit
17 // http://www.gnu.org/copyleft/gpl.html .
18 //
19 // Linking Avisynth statically or dynamically with other modules is making a
20 // combined work based on Avisynth. Thus, the terms and conditions of the GNU
21 // General Public License cover the whole combination.
22 //
23 // As a special exception, the copyright holders of Avisynth give you
24 // permission to link Avisynth with independent modules that communicate with
25 // Avisynth solely through the interfaces defined in avisynth.h, regardless of the license
26 // terms of these independent modules, and to copy and distribute the
27 // resulting combined work under terms of your choice, provided that
28 // every copy of the combined work is accompanied by a complete copy of
29 // the source code of Avisynth (the version of Avisynth used to produce the
30 // combined work), being distributed under the terms of the GNU General
31 // Public License plus this exception. An independent module is a module
32 // which is not derived from or based on Avisynth, such as 3rd-party filters,
33 // import and export plugins, or graphical user interfaces.
34
35
36 #include "script.h"
37 #include <time.h>
38 #include <cstdio>
39 #include <cstdlib>
40 #include <cmath>
41 #include <vector>
42 #include <fstream>
43 #include <memory>
44 #include <limits>
45 #include <bitset>
46
47 #ifdef AVS_WINDOWS
48 #include <io.h>
49 #include <avs/win.h>
50 #else
51 #include <avs/posix.h>
52 #include "os/win32_string_compat.h"
53 #include <dirent.h>
54 #endif
55
56 #include <avs/filesystem.h>
57 #include <avs/minmax.h>
58 #include <new>
59 #include "../internal.h"
60 #include "../Prefetcher.h"
61 #include "../InternalEnvironment.h"
62 #include "../strings.h"
63 #include <map>
64 #include <string>
65 #include <utility>
66 #define __STDC_FORMAT_MACROS
67 #include <inttypes.h>
68 #include <algorithm>
69 #include <cstring>
70 #include <cctype>
71
72 #ifndef MINGW_HAS_SECURE_API
73 #define sprintf_s sprintf
74 #endif
75
76
77
78 /********************************************************************
79 ***** Declare index of new filters for Avisynth's filter engine *****
80 ********************************************************************/
81
82
83 extern const AVSFunction Script_functions[] = {
84 { "muldiv", BUILTIN_FUNC_PREFIX, "iii", Muldiv },
85
86 { "floor", BUILTIN_FUNC_PREFIX, "f", Floor },
87 { "ceil", BUILTIN_FUNC_PREFIX, "f", Ceil },
88 { "round", BUILTIN_FUNC_PREFIX, "f", Round },
89
90 { "acos", BUILTIN_FUNC_PREFIX, "f", Acos },
91 { "asin", BUILTIN_FUNC_PREFIX, "f", Asin },
92 { "atan", BUILTIN_FUNC_PREFIX, "f", Atan },
93 { "atan2", BUILTIN_FUNC_PREFIX, "ff", Atan2 },
94 { "cos", BUILTIN_FUNC_PREFIX, "f", Cos },
95 { "cosh", BUILTIN_FUNC_PREFIX, "f", Cosh },
96 { "exp", BUILTIN_FUNC_PREFIX, "f", Exp },
97 { "fmod", BUILTIN_FUNC_PREFIX, "ff", Fmod },
98 { "log", BUILTIN_FUNC_PREFIX, "f", Log },
99 { "log10", BUILTIN_FUNC_PREFIX, "f", Log10 },
100 { "pow", BUILTIN_FUNC_PREFIX, "ff", Pow },
101 { "sin", BUILTIN_FUNC_PREFIX, "f", Sin },
102 { "sinh", BUILTIN_FUNC_PREFIX, "f", Sinh },
103 { "tan", BUILTIN_FUNC_PREFIX, "f", Tan },
104 { "tanh", BUILTIN_FUNC_PREFIX, "f", Tanh },
105 { "sqrt", BUILTIN_FUNC_PREFIX, "f", Sqrt },
106
107
108 { "abs", BUILTIN_FUNC_PREFIX, "i", Abs },
109 { "abs", BUILTIN_FUNC_PREFIX, "f", FAbs },
110 { "pi", BUILTIN_FUNC_PREFIX, "", Pi },
111 #ifdef OPT_ScriptFunctionTau
112 { "tau", BUILTIN_FUNC_PREFIX, "", Tau },
113 #endif
114 { "sign", BUILTIN_FUNC_PREFIX, "f",Sign},
115
116 { "bitand", BUILTIN_FUNC_PREFIX, "ii",BitAnd},
117 { "bitnot", BUILTIN_FUNC_PREFIX, "i",BitNot},
118 { "bitor", BUILTIN_FUNC_PREFIX, "ii",BitOr},
119 { "bitxor", BUILTIN_FUNC_PREFIX, "ii",BitXor},
120 // v11
121 { "bitand64", BUILTIN_FUNC_PREFIX, "ii",BitAnd64},
122 { "bitnot64", BUILTIN_FUNC_PREFIX, "i",BitNot64},
123 { "bitor64", BUILTIN_FUNC_PREFIX, "ii",BitOr64},
124 { "bitxor64", BUILTIN_FUNC_PREFIX, "ii",BitXor64},
125
126 { "bitlshift", BUILTIN_FUNC_PREFIX, "ii",BitLShift},
127 { "bitlshiftl", BUILTIN_FUNC_PREFIX, "ii",BitLShift},
128 { "bitlshifta", BUILTIN_FUNC_PREFIX, "ii",BitLShift},
129 { "bitlshiftu", BUILTIN_FUNC_PREFIX, "ii",BitLShift},
130 { "bitlshifts", BUILTIN_FUNC_PREFIX, "ii",BitLShift},
131 { "bitshl", BUILTIN_FUNC_PREFIX, "ii",BitLShift},
132 { "bitsal", BUILTIN_FUNC_PREFIX, "ii",BitLShift},
133 // v11 omg under how many names do the same?! keep only two
134 { "bitshl64", BUILTIN_FUNC_PREFIX, "ii",BitLShift64},
135 { "bitsal64", BUILTIN_FUNC_PREFIX, "ii",BitLShift64},
136
137 { "bitrshiftl", BUILTIN_FUNC_PREFIX, "ii",BitRShiftL},
138 { "bitrshifta", BUILTIN_FUNC_PREFIX, "ii",BitRShiftA},
139 { "bitrshiftu", BUILTIN_FUNC_PREFIX, "ii",BitRShiftL},
140 { "bitrshifts", BUILTIN_FUNC_PREFIX, "ii",BitRShiftA},
141 { "bitshr", BUILTIN_FUNC_PREFIX, "ii",BitRShiftL},
142 { "bitsar", BUILTIN_FUNC_PREFIX, "ii",BitRShiftA},
143 // v11
144 { "bitshr64", BUILTIN_FUNC_PREFIX, "ii",BitRShift64L},
145 { "bitsar64", BUILTIN_FUNC_PREFIX, "ii",BitRShift64A},
146
147 { "bitlrotate", BUILTIN_FUNC_PREFIX, "ii",BitRotateL},
148 { "bitrrotate", BUILTIN_FUNC_PREFIX, "ii",BitRotateR},
149 { "bitrol", BUILTIN_FUNC_PREFIX, "ii",BitRotateL},
150 { "bitror", BUILTIN_FUNC_PREFIX, "ii",BitRotateR},
151 // v11
152 { "bitrol64", BUILTIN_FUNC_PREFIX, "ii",BitRotate64L},
153 { "bitror64", BUILTIN_FUNC_PREFIX, "ii",BitRotate64R},
154
155 { "bitchg", BUILTIN_FUNC_PREFIX, "ii",BitChg},
156 { "bitchange", BUILTIN_FUNC_PREFIX, "ii",BitChg},
157 { "bitclr", BUILTIN_FUNC_PREFIX, "ii",BitClr},
158 { "bitclear", BUILTIN_FUNC_PREFIX, "ii",BitClr},
159 { "bitset", BUILTIN_FUNC_PREFIX, "ii",BitSet},
160 { "bittst", BUILTIN_FUNC_PREFIX, "ii",BitTst},
161 { "bittest", BUILTIN_FUNC_PREFIX, "ii",BitTst},
162 { "bitsetcount", BUILTIN_FUNC_PREFIX, "i+",BitSetCount }, // avs+ 180221
163 // v11
164 { "bitchg64", BUILTIN_FUNC_PREFIX, "ii",BitChg64},
165 { "bitclr64", BUILTIN_FUNC_PREFIX, "ii",BitClr64},
166 { "bitset64", BUILTIN_FUNC_PREFIX, "ii",BitSet64},
167 { "bittst64", BUILTIN_FUNC_PREFIX, "ii",BitTst64},
168 { "bitsetcount64", BUILTIN_FUNC_PREFIX, "i+",BitSetCount64 },
169
170 { "lcase", BUILTIN_FUNC_PREFIX, "s",LCase},
171 { "ucase", BUILTIN_FUNC_PREFIX, "s",UCase},
172 { "strlen", BUILTIN_FUNC_PREFIX, "s",StrLen},
173 { "revstr", BUILTIN_FUNC_PREFIX, "s",RevStr},
174 { "leftstr", BUILTIN_FUNC_PREFIX, "si",LeftStr},
175 { "midstr", BUILTIN_FUNC_PREFIX, "si[length]i",MidStr},
176 { "rightstr", BUILTIN_FUNC_PREFIX, "si",RightStr},
177 { "findstr", BUILTIN_FUNC_PREFIX, "ss",FindStr},
178 { "fillstr", BUILTIN_FUNC_PREFIX, "i[]s",FillStr},
179 { "replacestr", BUILTIN_FUNC_PREFIX, "sss[sig]b",ReplaceStr}, // avs+ 161230, case 180222
180 { "trimall", BUILTIN_FUNC_PREFIX, "s",TrimAll }, // avs+ 180225 diff name of clip-function Trim
181 { "trimleft", BUILTIN_FUNC_PREFIX, "s",TrimLeft }, // avs+ 180225
182 { "trimright", BUILTIN_FUNC_PREFIX, "s",TrimRight }, // avs+ 180225
183
184 { "strcmp", BUILTIN_FUNC_PREFIX, "ss",StrCmp},
185 { "strcmpi", BUILTIN_FUNC_PREFIX, "ss",StrCmpi},
186
187 { "rand", BUILTIN_FUNC_PREFIX, "[max]i[scale]b[seed]b", Rand },
188
189 { "Select", BUILTIN_FUNC_PREFIX, "i.+", Select },
190
191 { "nop", BUILTIN_FUNC_PREFIX, "", NOP },
192 { "undefined",BUILTIN_FUNC_PREFIX, "", Undefined },
193
194 { "width", BUILTIN_FUNC_PREFIX, "c", Width },
195 { "height", BUILTIN_FUNC_PREFIX, "c", Height },
196 { "framecount", BUILTIN_FUNC_PREFIX, "c", FrameCount },
197 { "framerate", BUILTIN_FUNC_PREFIX, "c", FrameRate },
198 { "frameratenumerator", BUILTIN_FUNC_PREFIX, "c", FrameRateNumerator },
199 { "frameratedenominator", BUILTIN_FUNC_PREFIX, "c", FrameRateDenominator },
200 { "audiorate", BUILTIN_FUNC_PREFIX, "c", AudioRate },
201 { "audiolength", BUILTIN_FUNC_PREFIX, "c", AudioLength }, // v11: returns real int64
202 { "audiolengthlo", BUILTIN_FUNC_PREFIX, "c[]i", AudioLengthLo }, // audiolength%i
203 { "audiolengthhi", BUILTIN_FUNC_PREFIX, "c[]i", AudioLengthHi }, // audiolength/i
204 { "audiolengths", BUILTIN_FUNC_PREFIX, "c", AudioLengthS }, // as a string
205 { "audiolengthf", BUILTIN_FUNC_PREFIX, "c", AudioLengthF }, // at least this will give an order of the size
206 { "audioduration", BUILTIN_FUNC_PREFIX, "c", AudioDuration }, // In seconds
207 { "audiochannels", BUILTIN_FUNC_PREFIX, "c", AudioChannels },
208 { "audiobits", BUILTIN_FUNC_PREFIX, "c", AudioBits },
209 { "IsAudioFloat", BUILTIN_FUNC_PREFIX, "c", IsAudioFloat },
210 { "IsAudioInt", BUILTIN_FUNC_PREFIX, "c", IsAudioInt },
211
212 { "IsChannelMaskKnown", BUILTIN_FUNC_PREFIX, "c", IsChannelMaskKnown },
213 { "GetChannelMask", BUILTIN_FUNC_PREFIX, "c", GetChannelMask }, // SetChannelMask: see in audio.cpp
214
215 { "IsRGB", BUILTIN_FUNC_PREFIX, "c", IsRGB },
216 { "IsYUY2", BUILTIN_FUNC_PREFIX, "c", IsYUY2 },
217 { "IsYUV", BUILTIN_FUNC_PREFIX, "c", IsYUV },
218 { "IsY8", BUILTIN_FUNC_PREFIX, "c", IsY8 },
219 { "IsYV12", BUILTIN_FUNC_PREFIX, "c", IsYV12 },
220 { "IsYV16", BUILTIN_FUNC_PREFIX, "c", IsYV16 },
221 { "IsYV24", BUILTIN_FUNC_PREFIX, "c", IsYV24 },
222 { "IsYV411", BUILTIN_FUNC_PREFIX, "c", IsYV411 },
223 { "IsPlanar", BUILTIN_FUNC_PREFIX, "c", IsPlanar },
224 { "IsInterleaved", BUILTIN_FUNC_PREFIX, "c", IsInterleaved },
225 { "IsRGB24", BUILTIN_FUNC_PREFIX, "c", IsRGB24 },
226 { "IsRGB32", BUILTIN_FUNC_PREFIX, "c", IsRGB32 },
227 { "IsFieldBased", BUILTIN_FUNC_PREFIX, "c", IsFieldBased },
228 { "IsFrameBased", BUILTIN_FUNC_PREFIX, "c", IsFrameBased },
229 { "GetParity", BUILTIN_FUNC_PREFIX, "c[n]i", GetParity },
230 { "String", BUILTIN_FUNC_PREFIX, ".[]s", String },
231 { "Hex", BUILTIN_FUNC_PREFIX, "i[width]i", Hex }, // avs+ 20180222 new width parameter
232 { "Func", BUILTIN_FUNC_PREFIX, "n", Func },
233 { "Format", BUILTIN_FUNC_PREFIX, "s.*", FormatString },
234
235 { "IsBool", BUILTIN_FUNC_PREFIX, ".", IsBool },
236 { "IsInt", BUILTIN_FUNC_PREFIX, ".", IsInt },
237 { "IsLongStrict", BUILTIN_FUNC_PREFIX, ".", IsLongStrict }, // v11
238 { "IsFloat", BUILTIN_FUNC_PREFIX, ".", IsFloat },
239 { "IsFloatFStrict", BUILTIN_FUNC_PREFIX, ".", IsFloatfStrict }, // v11
240 { "IsString", BUILTIN_FUNC_PREFIX, ".", IsString },
241 { "IsClip", BUILTIN_FUNC_PREFIX, ".", IsClip },
242 { "IsFunction", BUILTIN_FUNC_PREFIX, ".", IsFunction },
243 { "Defined", BUILTIN_FUNC_PREFIX, ".", Defined },
244 { "TypeName", BUILTIN_FUNC_PREFIX, ".", TypeName },
245
246 { "Default", BUILTIN_FUNC_PREFIX, "..", Default },
247
248 { "Eval", BUILTIN_FUNC_PREFIX, "s[name]s", Eval },
249 { "Eval", BUILTIN_FUNC_PREFIX, "cs[name]s", EvalOop },
250 { "Apply", BUILTIN_FUNC_PREFIX, "s.*", Apply },
251 { "Import", BUILTIN_FUNC_PREFIX, "s+[utf8]b", Import },
252
253 { "Assert", BUILTIN_FUNC_PREFIX, "b[message]s", Assert },
254 { "Assert", BUILTIN_FUNC_PREFIX, "s", AssertEval },
255
256 { "SetMemoryMax", BUILTIN_FUNC_PREFIX, "[]i[type]i[index]i", SetMemoryMax }, // Neo
257 { "SetWorkingDir", BUILTIN_FUNC_PREFIX, "s", SetWorkingDir },
258 { "Exist", BUILTIN_FUNC_PREFIX, "s[utf8]b", Exist },
259
260 { "Chr", BUILTIN_FUNC_PREFIX, "i", AVSChr },
261 { "Ord", BUILTIN_FUNC_PREFIX, "s", AVSOrd },
262 { "Time", BUILTIN_FUNC_PREFIX, "s", AVSTime },
263 { "Spline", BUILTIN_FUNC_PREFIX, "[x]ff+[cubic]b", Spline },
264
265 // parameter is 'f' which cover any integer or float numbers
266 { "int", BUILTIN_FUNC_PREFIX, "f", Int },
267 { "frac", BUILTIN_FUNC_PREFIX, "f", Frac },
268 { "float", BUILTIN_FUNC_PREFIX, "f", Float },
269 { "inti", BUILTIN_FUNC_PREFIX, "f", IntI }, // v11
270 { "long", BUILTIN_FUNC_PREFIX, "f", Long }, // v11
271 { "floatf", BUILTIN_FUNC_PREFIX, "f", Floatf }, // v11
272 { "double", BUILTIN_FUNC_PREFIX, "f", Double }, // v11
273
274 { "value", BUILTIN_FUNC_PREFIX, "s",Value},
275 { "hexvalue", BUILTIN_FUNC_PREFIX, "s[pos]i",HexValue}, // avs+ 20180222 new pos parameter
276 { "hexvalue64", BUILTIN_FUNC_PREFIX, "s[pos]i",HexValue64 }, // v11
277
278 { "VersionNumber", BUILTIN_FUNC_PREFIX, "", VersionNumber },
279 { "VersionString", BUILTIN_FUNC_PREFIX, "", VersionString },
280 { "IsVersionOrGreater", BUILTIN_FUNC_PREFIX, "[majorversion]i[minorVersion]i[bugfixVersion]i", IsVersionOrGreater },
281
282 { "HasVideo", BUILTIN_FUNC_PREFIX, "c", HasVideo },
283 { "HasAudio", BUILTIN_FUNC_PREFIX, "c", HasAudio },
284
285 { "Min", BUILTIN_FUNC_PREFIX, "f+", AvsMin },
286 { "Max", BUILTIN_FUNC_PREFIX, "f+", AvsMax },
287
288 { "ScriptName", BUILTIN_FUNC_PREFIX, "", ScriptName },
289 { "ScriptFile", BUILTIN_FUNC_PREFIX, "", ScriptFile },
290 { "ScriptDir", BUILTIN_FUNC_PREFIX, "", ScriptDir },
291 { "ScriptNameUtf8", BUILTIN_FUNC_PREFIX, "", ScriptNameUtf8 },
292 { "ScriptFileUtf8", BUILTIN_FUNC_PREFIX, "", ScriptFileUtf8 },
293 { "ScriptDirUtf8", BUILTIN_FUNC_PREFIX, "", ScriptDirUtf8 },
294
295 { "PixelType", BUILTIN_FUNC_PREFIX, "c", PixelType },
296
297 { "AddAutoloadDir", BUILTIN_FUNC_PREFIX, "s[toFront]b[utf8]b", AddAutoloadDir },
298 { "ClearAutoloadDirs", BUILTIN_FUNC_PREFIX, "", ClearAutoloadDirs },
299 { "ListAutoloadDirs", BUILTIN_FUNC_PREFIX, "[utf8]b", ListAutoloadDirs },
300 { "AutoloadPlugins", BUILTIN_FUNC_PREFIX, "", AutoloadPlugins },
301 { "FunctionExists", BUILTIN_FUNC_PREFIX, "s", FunctionExists },
302 { "InternalFunctionExists", BUILTIN_FUNC_PREFIX, "s", InternalFunctionExists },
303
304 { "SetFilterMTMode", BUILTIN_FUNC_PREFIX, "si[force]b", SetFilterMTMode },
305 { "Prefetch", BUILTIN_FUNC_PREFIX, "c[threads]i[frames]i", Prefetcher::Create },
306 { "SetLogParams", BUILTIN_FUNC_PREFIX, "[target]s[level]i", SetLogParams },
307 { "LogMsg", BUILTIN_FUNC_PREFIX, "si", LogMsg },
308 { "SetCacheMode", BUILTIN_FUNC_PREFIX, "[mode]i", SetCacheMode }, // Neo
309 { "SetDeviceOpt", BUILTIN_FUNC_PREFIX, "[opt]i[val]i", SetDeviceOpt }, // Neo
310 { "SetMaxCPU", BUILTIN_FUNC_PREFIX, "s", SetMaxCPU }, // 20200331
311 { "SetFilterProp", BUILTIN_FUNC_PREFIX, "ss.[mode]i", SetFilterProp }, // any type (int/float/bool/string/fn/undef); clip rejected in body
312 { "SetFilterProp", BUILTIN_FUNC_PREFIX, "ss.s.[mode]i", SetFilterProp }, // conditional: when param==match, inject key=value
313 { "GetFilterProps", BUILTIN_FUNC_PREFIX, "", GetFilterProps },
314 { "SetFilterPropPassthrough", BUILTIN_FUNC_PREFIX, "s", SetFilterPropPassthrough },
315
316 { "IsY", BUILTIN_FUNC_PREFIX, "c", IsY },
317 { "Is420", BUILTIN_FUNC_PREFIX, "c", Is420 },
318 { "Is422", BUILTIN_FUNC_PREFIX, "c", Is422 },
319 { "Is444", BUILTIN_FUNC_PREFIX, "c", Is444 },
320 { "IsRGB48", BUILTIN_FUNC_PREFIX, "c", IsRGB48 },
321 { "IsRGB64", BUILTIN_FUNC_PREFIX, "c", IsRGB64 },
322 { "ComponentSize", BUILTIN_FUNC_PREFIX, "c", ComponentSize },
323 { "BitsPerComponent", BUILTIN_FUNC_PREFIX, "c", BitsPerComponent },
324 { "IsYUVA", BUILTIN_FUNC_PREFIX, "c", IsYUVA },
325 { "IsPlanarRGB", BUILTIN_FUNC_PREFIX, "c", IsPlanarRGB },
326 { "IsPlanarRGBA", BUILTIN_FUNC_PREFIX, "c", IsPlanarRGBA },
327 { "ColorSpaceNameToPixelType", BUILTIN_FUNC_PREFIX, "s", ColorSpaceNameToPixelType },
328 { "NumComponents", BUILTIN_FUNC_PREFIX, "c", NumComponents }, // r2348+
329 { "HasAlpha", BUILTIN_FUNC_PREFIX, "c", HasAlpha }, // r2348+
330 { "IsPackedRGB", BUILTIN_FUNC_PREFIX, "c", IsPackedRGB }, // r2348+
331 { "IsVideoFloat", BUILTIN_FUNC_PREFIX, "c", IsVideoFloat }, // r2435+
332
333 { "GetProcessInfo", BUILTIN_FUNC_PREFIX, "[type]i", GetProcessInfo }, // 170526-
334 #ifdef AVS_WINDOWS
335 { "StrToUtf8", BUILTIN_FUNC_PREFIX, "s", StrToUtf8 }, // 170601-
336 { "StrFromUtf8", BUILTIN_FUNC_PREFIX, "s", StrFromUtf8 }, // 170601-
337 #endif
338
339 { "IsFloatUvZeroBased", BUILTIN_FUNC_PREFIX, "", IsFloatUvZeroBased }, // 180516-
340 { "BuildPixelType", BUILTIN_FUNC_PREFIX, "[family]s[bits]i[chroma]i[compat]b[oldnames]b[sample_clip]c", BuildPixelType }, // 180517-
341 { "VarExist", BUILTIN_FUNC_PREFIX, "s", VarExist }, // 180606-
342
343
344 // Creates script array from zero or more anything.
345 // Direct array constant syntax e.g. x = [arg1,arg2,...] is translated to x = Array(arg1,arg2,...)
346 { "Array", BUILTIN_FUNC_PREFIX, ".*", ArrayCreate },
347 { "IsArray", BUILTIN_FUNC_PREFIX, ".", IsArray },
348 // dictionary type array indexing
349 { "ArrayGet", BUILTIN_FUNC_PREFIX, ".s", ArrayGet },
350 // classic array indexing background helper: e.g. a[3,4] -> ArrayGet(a, [2,3])
351 { "ArrayGet", BUILTIN_FUNC_PREFIX, ".i+", ArrayGet }, // .+i+ syntax is not possible.
352 // dictionary type array key -> index lookup, -1 if not found
353 { "ArrayIndexOf", BUILTIN_FUNC_PREFIX, ".s", ArrayIndexOf },
354 // length can be zero
355 { "ArraySize", BUILTIN_FUNC_PREFIX, ".", ArraySize },
356 // Insert/Add/Replace can share the same logic
357 { "ArrayIns", BUILTIN_FUNC_PREFIX, "..i+", ArrayIns, (void*)0 },
358 { "ArrayAdd", BUILTIN_FUNC_PREFIX, "..i*", ArrayIns, (void*)1 },
359 { "ArraySet", BUILTIN_FUNC_PREFIX, "..i+", ArrayIns, (void*)2 },
360 // dictionary type array key set: smart replace-on-exist / append-on-missing [key, value]
361 { "ArraySet", BUILTIN_FUNC_PREFIX, "..s", ArraySetByKey, (void*)0 },
362 { "ArrayDel", BUILTIN_FUNC_PREFIX, ".i+", ArrayIns, (void*)3 },
363 // dictionary type array key delete
364 { "ArrayDel", BUILTIN_FUNC_PREFIX, ".s", ArraySetByKey, (void*)1 },
365 { "ArraySort", BUILTIN_FUNC_PREFIX, ".", ArraySort, (void*)0 },
366
367 /*
368 { "IsArrayOf", BUILTIN_FUNC_PREFIX, ".s", IsArrayOf },
369 */
370 { 0 }
371 };
372
373
374 /**********************************
375 ******* Script Function ******
376 *********************************/
377
378 ScriptFunction::ScriptFunction( const PExpression& _body, const bool* _param_floats,
379 const char** _param_names, int param_count )
380 : body(_body)
381 {
382 param_floats = new bool[param_count];
383 memcpy(param_floats, _param_floats, param_count * sizeof(const bool));
384
385 param_names = new const char* [param_count];
386 memcpy(param_names, _param_names, param_count * sizeof(const char*));
387 }
388
389 static bool is_within_int_in_float32_range(int64_t value) {
390 return value >= -16777216 && value <= 16777216;
391 }
392
393 AVSValue ScriptFunction::Execute(AVSValue args, void* user_data, IScriptEnvironment* env)
394 {
395 ScriptFunction* self = (ScriptFunction*)user_data;
396 env->PushContext();
397 for (int i=0; i<args.ArraySize(); ++i)
398 env->SetVar(self->param_names[i],
399 // Same as in ScriptFunction::Execute and AVSValue FunctionInstance::Execute
400
401 // force float args that are actually long/int (int64) to be float/double (depending on the range)
402 // opportunity to fit into the smaller float size
403 (self->param_floats[i] && args[i].IsInt()) ?
404 is_within_int_in_float32_range(args[i].AsLong()) ? (float)args[i].AsLong() : (double)args[i].AsLong() :
405 args[i]
406 );
407
408 AVSValue result;
409 try {
410 result = self->body->Evaluate(env);
411 }
412 catch (...) {
413 env->PopContext();
414 throw;
415 }
416
417 env->PopContext();
418 return result;
419 }
420
421 void ScriptFunction::Delete(void* self, IScriptEnvironment*)
422 {
423 delete (ScriptFunction*)self;
424 }
425
426 /***********************************
427 ******* Helper Functions ******
428 **********************************/
429
430 #ifdef AVS_WINDOWS
431
432 std::wstring CWDChanger::GetCurrentWorkingDirectory() {
433 DWORD length = GetCurrentDirectoryW(0, nullptr);
434 if (length == 0) return {};
435
436 std::wstring buffer(length, L'\0');
437 if (GetCurrentDirectoryW(length, &buffer[0]) == 0) return {};
438
439 // Remove trailing null character if present
440 if (!buffer.empty() && buffer.back() == L'\0') {
441 buffer.pop_back();
442 }
443
444 return buffer;
445 }
446
447 #else
448
449 8 std::string CWDChanger::GetCurrentWorkingDirectory() {
450 char buffer[FILENAME_MAX];
451
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 8 times.
8 if (getcwd(buffer, sizeof(buffer)) == nullptr) return {};
452
453
1/2
✓ Branch 7 → 8 taken 8 times.
✗ Branch 7 → 12 not taken.
16 return std::string(buffer);
454 }
455
456 #endif
457
458
459 #ifdef AVS_WINDOWS
460 void CWDChanger::Init(const wchar_t* new_cwd)
461 {
462 // works in unicode internally
463 uint32_t cwdLen = GetCurrentDirectoryW(0, NULL);
464 old_working_directory = std::make_unique<wchar_t[]>(cwdLen); // instead of new wchar_t[cwdLen];
465 uint32_t save_cwd_success = GetCurrentDirectoryW(cwdLen, old_working_directory.get());
466 bool set_cwd_success = SetCurrentDirectoryW(new_cwd);
467 restore = (save_cwd_success && set_cwd_success);
468 }
469
470 CWDChanger::CWDChanger(const wchar_t* new_cwd)
471 {
472 Init(new_cwd);
473 }
474
475 // utf8 on Windows as well
476 CWDChanger::CWDChanger(const char* new_cwd_utf8)
477 {
478 auto new_cwd_w = Utf8ToWideChar(new_cwd_utf8);
479 Init(new_cwd_w.get());
480 }
481
482 CWDChanger::~CWDChanger(void)
483 {
484 if (restore)
485 SetCurrentDirectoryW(old_working_directory.get());
486 }
487
488 DllDirChanger::DllDirChanger(const char* new_dir)
489 {
490 uint32_t len = GetDllDirectory (0, NULL);
491 old_directory = std::make_unique<char[]>(len + 1); // instead of new char[len+1]
492 uint32_t save_success = GetDllDirectory (len, old_directory.get());
493 bool set_success = SetDllDirectory(new_dir);
494 restore = (save_success && set_success);
495 }
496
497 DllDirChanger::~DllDirChanger(void)
498 {
499 if (restore)
500 SetDllDirectory(old_directory.get());
501 }
502 #else // copied from AvxSynth
503 CWDChanger::CWDChanger(const char* new_cwd)
504 {
505
506 char* path = getcwd(old_working_directory, FILENAME_MAX);
507 bool save_cwd_success = (NULL != path);
508 bool set_cwd_success = (0 == chdir(new_cwd));
509 restore = (save_cwd_success && set_cwd_success);
510 }
511
512 CWDChanger::~CWDChanger(void)
513 {
514 if (restore)
515 chdir(old_working_directory);
516 }
517 #endif
518
519
520 AVSValue Assert(AVSValue args, void*, IScriptEnvironment* env)
521 {
522 if (!args[0].AsBool())
523 env->ThrowError("%s", args[1].Defined() ? args[1].AsString() : "Assert: assertion failed");
524 return AVSValue();
525 }
526
527 AVSValue AssertEval(AVSValue args, void*, IScriptEnvironment* env)
528 {
529 const char* pred = args[0].AsString();
530 AVSValue eval_args[] = { args[0].AsString(), "asserted expression" };
531 AVSValue val = env->Invoke("Eval", AVSValue(eval_args, 2));
532 if (!val.IsBool())
533 env->ThrowError("Assert: expression did not evaluate to true or false: \"%s\"", pred);
534 if (!val.AsBool())
535 env->ThrowError("Assert: assertion failed: \"%s\"", pred);
536 return AVSValue();
537 }
538
539 AVSValue Eval(AVSValue args, void*, IScriptEnvironment* env)
540 {
541 const char *filename = args[1].AsString(0);
542 if (filename) filename = env->SaveString(filename);
543 ScriptParser parser(env, args[0].AsString(), filename);
544 PExpression exp = parser.Parse();
545 return exp->Evaluate(env);
546 }
547
548 AVSValue Apply(AVSValue args, void*, IScriptEnvironment* env)
549 {
550 return env->Invoke(args[0].AsString(), args[1]);
551 }
552
553 AVSValue EvalOop(AVSValue args, void*, IScriptEnvironment* env)
554 {
555 AVSValue prev_last = env->GetVarDef("last"); // Store previous last
556 env->SetVar("last", args[0]); // Set implicit last
557
558 AVSValue result;
559 try {
560 result = Eval(AVSValue(&args[1], 2), 0, env);
561 }
562 catch(...) {
563 env->SetVar("last", prev_last); // Restore implicit last
564 throw;
565 }
566 env->SetVar("last", prev_last); // Restore implicit last
567 return result;
568 }
569
570 AVSValue Import(AVSValue args, void*, IScriptEnvironment* env)
571 {
572 // called as s+ or s+[Utf8]b
573 const bool bHasUTF8param = args.IsArray() && args.ArraySize() == 2 && args[1].IsBool();
574 const bool bUtf8 = bHasUTF8param ? args[1].AsBool(false) : false;
575
576 args = args[0];
577 AVSValue result;
578
579 InternalEnvironment *envi = static_cast<InternalEnvironment*>(env);
580 const bool MainScript = (envi->IncrImportDepth() == 1);
581
582 AVSValue lastScriptName = env->GetVarDef("$ScriptName$");
583 AVSValue lastScriptFile = env->GetVarDef("$ScriptFile$");
584 AVSValue lastScriptDir = env->GetVarDef("$ScriptDir$");
585
586 AVSValue lastScriptNameUtf8 = env->GetVarDef("$ScriptNameUtf8$");
587 AVSValue lastScriptFileUtf8 = env->GetVarDef("$ScriptFileUtf8$");
588 AVSValue lastScriptDirUtf8 = env->GetVarDef("$ScriptDirUtf8$");
589
590 for (int i = 0; i < args.ArraySize(); ++i) {
591 const char* script_name = args[i].AsString();
592
593 #ifdef AVS_WINDOWS
594 /* Linux, macOS, pretty much every OS aside from Windows uses
595 UTF-8 pervasively and by default, making all the Ansi<->Unicode
596 stuff we have to specially handle on Windows (which uses UTF-16
597 when it does 'Unicode', further complicating things if you don't
598 force UTF-8) irrelevant. */
599
600 // Handling utf8 and ansi, working in wchar_t internally
601 // filename and path can be full unicode
602 // unicode input can come from CAVIFileSynth
603
604 std::unique_ptr<wchar_t[]> full_path_w;
605 wchar_t *file_part_w;
606
607 // make wchar_t full path strnig from either ansi or utf8
608 auto script_name_w = !bUtf8 ? AnsiToWideChar(script_name) : Utf8ToWideChar(script_name);
609
610 // Long (>MAX_PATH) path support starting in Windows 10, version 1607.
611 if (wcschr(script_name_w.get(), '\\') || wcschr(script_name_w.get(), '/')) {
612 DWORD len = GetFullPathNameW(script_name_w.get(), 0, NULL, NULL); // buffer size for path + terminating zero
613 full_path_w = std::make_unique<wchar_t[]>(len);
614 len = GetFullPathNameW(script_name_w.get(), len, full_path_w.get(), &file_part_w);
615 if (len == 0) {
616 auto script_name_utf8 = WideCharToUtf8(script_name_w.get());
617 env->ThrowError("Import: unable to open \"%s\" (path invalid?), error=0x%x", script_name_utf8.get(), GetLastError());
618 }
619 }
620 else {
621 DWORD len = SearchPathW(NULL, script_name_w.get(), NULL, 0, NULL, NULL); // buffer size for path + terminating zero
622 full_path_w = std::make_unique<wchar_t[]>(len);
623 len = SearchPathW(NULL, script_name_w.get(), NULL, len, full_path_w.get(), &file_part_w);
624 if (len == 0) {
625 auto script_name_utf8 = WideCharToUtf8(script_name_w.get());
626 env->ThrowError("Import: unable to locate \"%s\" (try specifying a path), error=0x%x", script_name_utf8.get(), GetLastError());
627 }
628 }
629
630 // back to 8 bit Ansi and Utf8
631 auto full_path = WideCharToAnsi(full_path_w.get());
632 auto full_path_utf8 = WideCharToUtf8(full_path_w.get());
633 auto file_part = WideCharToAnsi(file_part_w);
634 auto file_part_utf8 = WideCharToUtf8(file_part_w);
635 size_t dir_part_len = wcslen(full_path_w.get()) - wcslen(file_part_w);
636 auto dir_part = WideCharToAnsi_maxn(full_path_w.get(), dir_part_len);
637 auto dir_part_utf8 = WideCharToUtf8_maxn(full_path_w.get(), dir_part_len);
638
639 // supply L"\\\\?\\" if necessary for long file path support
640 std::wstring full_path_ex = std::wstring(full_path_w.get());
641 if (full_path_ex.length() > FILENAME_MAX && full_path_ex.substr(0, 4) != L"\\\\?\\")
642 full_path_ex = L"\\\\?\\" + full_path_ex;
643
644 HANDLE h = ::CreateFileW(full_path_ex.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
645 if (h == INVALID_HANDLE_VALUE)
646 env->ThrowError("Import: couldn't open \"%s\"", full_path.get());
647
648 env->SetGlobalVar("$ScriptName$", env->SaveString(full_path.get()));
649 env->SetGlobalVar("$ScriptFile$", env->SaveString(file_part.get()));
650 env->SetGlobalVar("$ScriptDir$", env->SaveString(dir_part.get()));
651 env->SetGlobalVar("$ScriptNameUtf8$", env->SaveString(full_path_utf8.get()));
652 env->SetGlobalVar("$ScriptFileUtf8$", env->SaveString(file_part_utf8.get()));
653 env->SetGlobalVar("$ScriptDirUtf8$", env->SaveString(dir_part_utf8.get()));
654 if (MainScript)
655 {
656 env->SetGlobalVar("$MainScriptName$", env->SaveString(full_path.get()));
657 env->SetGlobalVar("$MainScriptFile$", env->SaveString(file_part.get()));
658 env->SetGlobalVar("$MainScriptDir$", env->SaveString(dir_part.get()));
659 env->SetGlobalVar("$MainScriptNameUtf8$", env->SaveString(full_path_utf8.get()));
660 env->SetGlobalVar("$MainScriptFileUtf8$", env->SaveString(file_part_utf8.get()));
661 env->SetGlobalVar("$MainScriptDirUtf8$", env->SaveString(dir_part_utf8.get()));
662 }
663
664 *file_part_w = 0; // trunc full_path_w to dir-only
665 CWDChanger change_cwd(full_path_w.get());
666 // end of filename parsing / file open things
667
668 DWORD size = GetFileSize(h, NULL);
669 std::vector<char> buf(size + 1, 0);
670 bool status = ReadFile(h, buf.data(), size, &size, NULL);
671 CloseHandle(h);
672 if (!status)
673 env->ThrowError("Import: unable to read \"%s\"", script_name);
674
675 // Give poor Unicode users a hint they need to use ANSI encoding import"
676 if (size >= 2) {
677 unsigned char* q = reinterpret_cast<unsigned char*>(buf.data());
678
679 if ((q[0] == 0xFF && q[1] == 0xFE) || (q[0] == 0xFE && q[1] == 0xFF))
680 env->ThrowError("Import: Unicode source files are not supported, "
681 "re-save script with ANSI or UTF8 w/o BOM encoding! : \"%s\"", script_name);
682
683 if (q[0] == 0xEF && q[1] == 0xBB && q[2] == 0xBF)
684 env->ThrowError("Import: UTF-8 source files with BOM are not supported, "
685 "re-save script with ANSI or UTF8 w/o BOM encoding! : \"%s\"", script_name);
686 }
687
688 #else // adapted from AvxSynth
689 std::string file_part = fs::path(script_name).filename().string();
690 std::string full_path = fs::path(script_name).remove_filename();
691 std::string dir_part = fs::path(script_name).parent_path();
692
693 FILE* h = fopen(script_name, "r");
694 if(NULL == h)
695 env->ThrowError("Import: couldn't open \"%s\"", script_name );
696
697 env->SetGlobalVar("$ScriptName$", env->SaveString(script_name));
698 env->SetGlobalVar("$ScriptFile$", env->SaveString(file_part.c_str()));
699 env->SetGlobalVar("$ScriptDir$", env->SaveString(full_path.c_str()));
700 env->SetGlobalVar("$ScriptNameUtf8$", env->SaveString(script_name));
701 env->SetGlobalVar("$ScriptFileUtf8$", env->SaveString(file_part.c_str()));
702 env->SetGlobalVar("$ScriptDirUtf8$", env->SaveString(full_path.c_str()));
703 if (MainScript)
704 {
705 env->SetGlobalVar("$MainScriptName$", env->SaveString(script_name));
706 env->SetGlobalVar("$MainScriptFile$", env->SaveString(file_part.c_str()));
707 env->SetGlobalVar("$MainScriptDir$", env->SaveString(full_path.c_str()));
708 env->SetGlobalVar("$MainScriptNameUtf8$", env->SaveString(script_name));
709 env->SetGlobalVar("$MainScriptFileUtf8$", env->SaveString(file_part.c_str()));
710 env->SetGlobalVar("$MainScriptDirUtf8$", env->SaveString(full_path.c_str()));
711 }
712
713 //*file_part = 0; // trunc full_path to dir-only
714 CWDChanger change_cwd(full_path.c_str());
715 // end of filename parsing / file open things
716
717 fseek(h, 0, SEEK_END);
718 size_t size = ftell(h);
719 fseek(h, 0, SEEK_SET);
720
721 std::vector<char> buf(size + 1, 0);
722 if(size != fread(buf.data(), 1, size, h))
723 env->ThrowError("Import: unable to read \"%s\"", script_name);
724
725 fclose(h);
726 #endif
727
728 buf[size] = 0;
729 AVSValue eval_args[] = { buf.data(), script_name };
730 result = env->Invoke("Eval", AVSValue(eval_args, 2));
731 //env->ThrowError("Import: test %s size %d\n", buf.data(), (int)size);
732 }
733
734 env->SetGlobalVar("$ScriptName$", lastScriptName);
735 env->SetGlobalVar("$ScriptFile$", lastScriptFile);
736 env->SetGlobalVar("$ScriptDir$", lastScriptDir);
737 env->SetGlobalVar("$ScriptNameUtf8$", lastScriptNameUtf8);
738 env->SetGlobalVar("$ScriptFileUtf8$", lastScriptFileUtf8);
739 env->SetGlobalVar("$ScriptDirUtf8$", lastScriptDirUtf8);
740 envi->DecrImportDepth();
741
742 return result;
743 }
744
745
746 AVSValue ScriptName(AVSValue args, void*, IScriptEnvironment* env) { return env->GetVarDef("$ScriptName$"); }
747 AVSValue ScriptFile(AVSValue args, void*, IScriptEnvironment* env) { return env->GetVarDef("$ScriptFile$"); }
748 AVSValue ScriptDir (AVSValue args, void*, IScriptEnvironment* env) { return env->GetVarDef("$ScriptDir$" ); }
749 AVSValue ScriptNameUtf8(AVSValue args, void*, IScriptEnvironment* env) { return env->GetVarDef("$ScriptNameUtf8$"); }
750 AVSValue ScriptFileUtf8(AVSValue args, void*, IScriptEnvironment* env) { return env->GetVarDef("$ScriptFileUtf8$"); }
751 AVSValue ScriptDirUtf8(AVSValue args, void*, IScriptEnvironment* env) { return env->GetVarDef("$ScriptDirUtf8$"); }
752 AVSValue SetWorkingDir(AVSValue args, void*, IScriptEnvironment* env) { return env->SetWorkingDir(args[0].AsString()); }
753
754 AVSValue Muldiv(AVSValue args, void*, IScriptEnvironment* ) {
755 // designed for 32 bits, no change other than read int64 parameters,
756 // though they are caster back immediately to int
757 auto result = MulDiv((int)args[0].AsLong(), (int)args[1].AsLong(), (int)args[2].AsLong());
758 return (int)result;
759 }
760
761 // v11: up to int64 range
762 AVSValue Floor(AVSValue args, void*, IScriptEnvironment* ) {
763 int64_t result = static_cast<int64_t>(floor(args[0].AsFloat()));
764 if (result >= INT_MIN && result <= INT_MAX)
765 return (int)result;
766 return result;
767 }
768 // v11: up to int64 range
769 AVSValue Ceil(AVSValue args, void*, IScriptEnvironment* ) {
770 int64_t result = static_cast<int64_t>(ceil(args[0].AsFloat()));
771 if (result >= INT_MIN && result <= INT_MAX)
772 return (int)result;
773 return result;
774 }
775 // v11: up to int64 range
776 AVSValue Round(AVSValue args, void*, IScriptEnvironment* ) {
777 int64_t result = args[0].AsFloat() < 0 ? -static_cast<int64_t>(-args[0].AsFloat() + .5) : static_cast<int64_t>(args[0].AsFloat() + .5);
778 if (result >= INT_MIN && result <= INT_MAX)
779 return (int)result;
780 return result;
781 }
782
783 AVSValue Acos(AVSValue args, void* , IScriptEnvironment* ) { return acos(args[0].AsFloat()); }
784 AVSValue Asin(AVSValue args, void* , IScriptEnvironment* ) { return asin(args[0].AsFloat()); }
785 AVSValue Atan(AVSValue args, void* , IScriptEnvironment* ) { return atan(args[0].AsFloat()); }
786 AVSValue Atan2(AVSValue args, void* , IScriptEnvironment* ) { return atan2(args[0].AsFloat(), args[1].AsFloat()); }
787 AVSValue Cos(AVSValue args, void* , IScriptEnvironment* ) { return cos(args[0].AsFloat()); }
788 AVSValue Cosh(AVSValue args, void* , IScriptEnvironment* ) { return cosh(args[0].AsFloat()); }
789 AVSValue Exp(AVSValue args, void* , IScriptEnvironment* ) { return exp(args[0].AsFloat()); }
790 AVSValue Fmod(AVSValue args, void* , IScriptEnvironment* ) { return fmod(args[0].AsFloat(), args[1].AsFloat()); }
791 AVSValue Log(AVSValue args, void* , IScriptEnvironment* ) { return log(args[0].AsFloat()); }
792 AVSValue Log10(AVSValue args, void* , IScriptEnvironment* ) { return log10(args[0].AsFloat()); }
793 AVSValue Pow(AVSValue args, void* , IScriptEnvironment* ) { return pow(args[0].AsFloat(),args[1].AsFloat()); }
794 AVSValue Sin(AVSValue args, void* , IScriptEnvironment* ) { return sin(args[0].AsFloat()); }
795 AVSValue Sinh(AVSValue args, void* , IScriptEnvironment* ) { return sinh(args[0].AsFloat()); }
796 AVSValue Tan(AVSValue args, void* , IScriptEnvironment* ) { return tan(args[0].AsFloat()); }
797 AVSValue Tanh(AVSValue args, void* , IScriptEnvironment* ) { return tanh(args[0].AsFloat()); }
798 14 AVSValue Sqrt(AVSValue args, void* , IScriptEnvironment* ) { return sqrt(args[0].AsFloat()); }
799
800 // v11: up to int64 range
801 AVSValue Abs(AVSValue args, void* , IScriptEnvironment* ) {
802 int64_t result = std::abs(args[0].AsLong());
803 if (result >= INT_MIN && result <= INT_MAX)
804 return (int)result;
805 return result;
806 }
807 AVSValue FAbs(AVSValue args, void* , IScriptEnvironment* ) { return fabs(args[0].AsFloat()); }
808 AVSValue Pi(AVSValue args, void* , IScriptEnvironment* ) { return 3.14159265358979324; }
809 #ifdef OPT_ScriptFunctionTau
810 AVSValue Tau(AVSValue args, void* , IScriptEnvironment* ) { return 6.28318530717958648; }
811 #endif
812 AVSValue Sign(AVSValue args, void*, IScriptEnvironment* ) { return args[0].AsFloat()==0 ? 0 : args[0].AsFloat() > 0 ? 1 : -1; }
813
814 // v11: These bitwise functions are strictly for 32 bit, if 64 bit versions are implemented they will have different names
815
816 AVSValue BitAnd(AVSValue args, void*, IScriptEnvironment* ) { return args[0].AsInt() & args[1].AsInt(); }
817 AVSValue BitNot(AVSValue args, void*, IScriptEnvironment* ) { return ~args[0].AsInt(); }
818 AVSValue BitOr(AVSValue args, void*, IScriptEnvironment* ) { return args[0].AsInt() | args[1].AsInt(); }
819 AVSValue BitXor(AVSValue args, void*, IScriptEnvironment* ) { return args[0].AsInt() ^ args[1].AsInt(); }
820
821 AVSValue BitAnd64(AVSValue args, void*, IScriptEnvironment*) { return args[0].AsLong() & args[1].AsLong(); }
822 AVSValue BitNot64(AVSValue args, void*, IScriptEnvironment*) { return ~args[0].AsLong(); }
823 AVSValue BitOr64(AVSValue args, void*, IScriptEnvironment*) { return args[0].AsLong() | args[1].AsLong(); }
824 AVSValue BitXor64(AVSValue args, void*, IScriptEnvironment*) { return args[0].AsLong() ^ args[1].AsLong(); }
825
826 AVSValue BitLShift(AVSValue args, void*, IScriptEnvironment* ) { return args[0].AsInt() << args[1].AsInt(); }
827 AVSValue BitRShiftL(AVSValue args, void*, IScriptEnvironment* ) { return int(unsigned(args[0].AsInt()) >> unsigned(args[1].AsInt())); }
828 AVSValue BitRShiftA(AVSValue args, void*, IScriptEnvironment* ) { return args[0].AsInt() >> args[1].AsInt(); }
829
830 AVSValue BitLShift64(AVSValue args, void*, IScriptEnvironment*) { return args[0].AsLong() << args[1].AsInt(); }
831 AVSValue BitRShift64L(AVSValue args, void*, IScriptEnvironment*) { return int64_t(uint64_t(args[0].AsLong()) >> unsigned(args[1].AsInt())); }
832 AVSValue BitRShift64A(AVSValue args, void*, IScriptEnvironment*) { return args[0].AsLong() >> args[1].AsInt(); }
833
834 static unsigned int a_rol(unsigned int value, int shift) {
835 if ((shift &= sizeof(value)*8 - 1) == 0)
836 return value;
837 return (value << shift) | (value >> (sizeof(value)*8 - shift));
838 }
839 static uint64_t a_rol(uint64_t value, int shift) {
840 if ((shift &= sizeof(value) * 8 - 1) == 0)
841 return value;
842 return (value << shift) | (value >> (sizeof(value) * 8 - shift));
843 }
844
845 static unsigned int a_ror(unsigned int value, int shift) {
846 if ((shift &= sizeof(value)*8 - 1) == 0)
847 return value;
848 return (value >> shift) | (value << (sizeof(value)*8 - shift));
849 }
850 static uint64_t a_ror(uint64_t value, int shift) {
851 if ((shift &= sizeof(value) * 8 - 1) == 0)
852 return value;
853 return (value >> shift) | (value << (sizeof(value) * 8 - shift));
854 }
855
856 static int a_btc(int value, int bit) {
857 value ^= 1 << bit;
858 return value;
859 }
860 static int64_t a_btc(int64_t value, int bit) {
861 value ^= static_cast<int64_t>(1) << bit;
862 return value;
863 }
864
865 static int a_btr(int value, int bit) {
866 value &= ~(1 << bit);
867 return value;
868 }
869 static int64_t a_btr(int64_t value, int bit) {
870 value &= ~(static_cast<int64_t>(1) << bit);
871 return value;
872 }
873
874 static int a_bts(int value, int bit) {
875 value |= (1 << bit);
876 return value;
877 }
878 static int64_t a_bts(int64_t value, int bit) {
879 value |= (static_cast<int64_t>(1) << bit);
880 return value;
881 }
882
883 static bool a_bt(int value, int bit) {
884 return (value & (1 << bit)) ? true : false;
885 }
886 static bool a_bt(int64_t value, int bit) {
887 return (value & (static_cast<int64_t>(1)<< bit)) ? true : false;
888 }
889
890 AVSValue BitRotateL(AVSValue args, void*, IScriptEnvironment* ) { return (int)a_rol((unsigned int)args[0].AsInt(), args[1].AsInt()); }
891 AVSValue BitRotateR(AVSValue args, void*, IScriptEnvironment* ) { return (int)a_ror((unsigned int)args[0].AsInt(), args[1].AsInt()); }
892 AVSValue BitRotate64L(AVSValue args, void*, IScriptEnvironment*) { return (int64_t)a_rol((uint64_t)args[0].AsLong(), args[1].AsInt()); }
893 AVSValue BitRotate64R(AVSValue args, void*, IScriptEnvironment*) { return (int64_t)a_ror((uint64_t)args[0].AsLong(), args[1].AsInt()); }
894
895 AVSValue BitChg(AVSValue args, void*, IScriptEnvironment* ) { return a_btc(args[0].AsInt(), args[1].AsInt()); }
896 AVSValue BitClr(AVSValue args, void*, IScriptEnvironment* ) { return a_btr(args[0].AsInt(), args[1].AsInt()); }
897 AVSValue BitSet(AVSValue args, void*, IScriptEnvironment* ) { return a_bts(args[0].AsInt(), args[1].AsInt()); }
898 AVSValue BitTst(AVSValue args, void*, IScriptEnvironment* ) { return a_bt (args[0].AsInt(), args[1].AsInt()); }
899 AVSValue BitChg64(AVSValue args, void*, IScriptEnvironment*) { return a_btc(args[0].AsLong(), args[1].AsInt()); }
900 AVSValue BitClr64(AVSValue args, void*, IScriptEnvironment*) { return a_btr(args[0].AsLong(), args[1].AsInt()); }
901 AVSValue BitSet64(AVSValue args, void*, IScriptEnvironment*) { return a_bts(args[0].AsLong(), args[1].AsInt()); }
902 AVSValue BitTst64(AVSValue args, void*, IScriptEnvironment*) { return a_bt(args[0].AsLong(), args[1].AsInt()); }
903
904 static int numberOfSetBits(uint32_t i)
905 {
906 i = i - ((i >> 1) & 0x55555555);
907 i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
908 return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
909 }
910
911 static int numberOfSetBits64(uint64_t i) {
912 return static_cast<int>(std::bitset<64>(i).count());
913 }
914
915 AVSValue BitSetCount(AVSValue args, void*, IScriptEnvironment*) {
916 if (args[0].IsInt())
917 return numberOfSetBits(static_cast<uint32_t>(args[0].AsInt()));
918 // multiple integer parameters
919 int count = 0;
920 for (int i = 0; i < args[0].ArraySize(); i++)
921 count += numberOfSetBits(static_cast<uint32_t>(args[0][i].AsInt()));
922 return count;
923 }
924
925 AVSValue BitSetCount64(AVSValue args, void*, IScriptEnvironment*) {
926 if (args[0].IsInt())
927 return numberOfSetBits64(static_cast<uint64_t>(args[0].AsLong()));
928 // multiple integer parameters
929 int count = 0;
930 for (int i = 0; i < args[0].ArraySize(); i++)
931 count += numberOfSetBits64(static_cast<uint64_t>(args[0][i].AsLong()));
932 return count;
933 }
934
935 static const char* toUpperCase(const char* string) {
936 // Make a temporary copy of the string
937 char* tmp = _strdup(string);
938 if (tmp == nullptr) {
939 return nullptr;
940 }
941 // Convert the copy to uppercase
942 _strupr(tmp);
943 return tmp;
944 }
945 AVSValue UCase(AVSValue args, void*, IScriptEnvironment* env) {
946 const char *res = toUpperCase(args[0].AsString());
947 if(res == nullptr)
948 env->ThrowError("UCase: memory allocation error");
949 AVSValue result = env->SaveString(res);
950 free((void*)res);
951 return result;
952 }
953 static const char* toLowerCase(const char* string) {
954 // Make a temporary copy of the string
955 char* tmp = _strdup(string);
956 if (tmp == nullptr) {
957 return nullptr;
958 }
959 // Convert the copy to lowercase
960 _strlwr(tmp);
961 return tmp;
962 }
963 AVSValue LCase(AVSValue args, void*, IScriptEnvironment* env) {
964 const char* res = toLowerCase(args[0].AsString());
965 if (res == nullptr)
966 env->ThrowError("LCase: memory allocation error");
967 AVSValue result = env->SaveString(res);
968 free((void*)res);
969 return result;
970 }
971
972 AVSValue StrLen(AVSValue args, void*, IScriptEnvironment* ) {
973 size_t len = strlen(args[0].AsString());
974 if (len > static_cast<size_t>(std::numeric_limits<int>::max()))
975 return static_cast<int64_t>(len);
976 else
977 return static_cast<int>(len);
978 }
979 static const char* toReversed(const char* string) {
980 // Make a temporary copy of the string
981 char* tmp = _strdup(string);
982 if (tmp == nullptr) {
983 return nullptr;
984 }
985 // reverse the copy
986 _strrev(tmp);
987 return tmp;
988 }
989
990 AVSValue RevStr(AVSValue args, void*, IScriptEnvironment* env) {
991 const char* res = toReversed(args[0].AsString());
992 if (res == nullptr)
993 env->ThrowError("RevStr: memory allocation error");
994 AVSValue result = env->SaveString(res);
995 free((void*)res);
996 return result;
997 }
998
999 AVSValue LeftStr(AVSValue args, void*, IScriptEnvironment* env)
1000 {
1001 const int64_t _count = args[1].AsLong();
1002 if (_count < 0) {
1003 env->ThrowError("LeftStr: Negative character count not allowed");
1004 }
1005 if (static_cast<uint64_t>(_count) > std::numeric_limits<size_t>::max() - 1) {
1006 env->ThrowError("LeftStr: Character count exceeds maximum allowed value");
1007 }
1008 const size_t count = static_cast<size_t>(_count);
1009
1010 char* result = new(std::nothrow) char[count + 1];
1011 if (!result) env->ThrowError("LeftStr: malloc failure (%zu bytes)!", count + 1);
1012 strncpy(result, args[0].AsString(), count);
1013 result[count] = '\0'; // Ensure null termination
1014 AVSValue ret = env->SaveString(result);
1015 delete[] result;
1016 return ret;
1017 }
1018
1019 AVSValue MidStr(AVSValue args, void*, IScriptEnvironment* env)
1020 {
1021 const size_t maxlen = strlen(args[0].AsString());
1022
1023 if (args[1].AsLong() < 1)
1024 env->ThrowError("MidStr: Illegal character location. Positions start with 1.");
1025
1026 if (static_cast<uint64_t>(args[1].AsLong() - 1) > std::numeric_limits<size_t>::max() - 1)
1027 env->ThrowError("MidStr: Offset exceeds maximum allowed value");
1028
1029 size_t offset = static_cast<size_t>(args[1].AsLong() - 1); // pos=1 specifies start.
1030
1031 int64_t _len = args[2].AsLong(maxlen);
1032 if (_len < 0)
1033 env->ThrowError("MidStr: Character count cannot be negative");
1034 if (maxlen <= offset) { offset = 0; _len = 0;}
1035
1036 if (static_cast<uint64_t>(_len) > std::numeric_limits<size_t>::max() - 1)
1037 env->ThrowError("MidStr: Character count exceeds maximum allowed value");
1038 size_t len = static_cast<size_t>(_len);
1039
1040 if (offset + len > maxlen)
1041 len = maxlen - offset; // though strncpy handles premature string end
1042
1043 char *result = new(std::nothrow) char[len + 1];
1044 if (!result) env->ThrowError("MidStr: malloc failure (%zu bytes)!", len + 1);
1045 strncpy(result, args[0].AsString() + offset, len);
1046 result[len] = '\0';
1047
1048 AVSValue ret = env->SaveString(result);
1049 delete[] result;
1050 return ret;
1051 }
1052
1053 AVSValue RightStr(AVSValue args, void*, IScriptEnvironment* env)
1054 {
1055 const int64_t _count = args[1].AsLong();
1056 if (_count < 0)
1057 env->ThrowError("RightStr: Negative character count not allowed");
1058
1059 if (static_cast<uint64_t>(_count) > std::numeric_limits<size_t>::max() - 1)
1060 env->ThrowError("RightStr: Character count exceeds maximum allowed value");
1061
1062 size_t count = static_cast<size_t>(_count);
1063 const size_t len = strlen(args[0].AsString());
1064 if (count > len)
1065 count = len;
1066 // no error given, limit to string length
1067 // env->ThrowError("RightStr: Character count (%zu) exceeds string length (%zu)", count, len);
1068
1069 const size_t offset = len - count;
1070
1071 char* result = new(std::nothrow) char[count + 1];
1072 if (!result) env->ThrowError("RightStr: memory allocation failure (%zu bytes)!", count + 1);
1073 strncpy(result, args[0].AsString() + offset, count);
1074 result[count] = '\0';
1075
1076 AVSValue ret = env->SaveString(result);
1077 delete[] result;
1078 return ret;
1079 }
1080
1081 AVSValue ReplaceStr(AVSValue args, void*, IScriptEnvironment* env) {
1082 char const * const original = args[0].AsString();
1083 char const * const pattern = args[1].AsString();
1084 char const * const replacement = args[2].AsString();
1085 const bool case_insensitive = args[3].AsBool(false);
1086
1087 const size_t replace_len = strlen(replacement);
1088 const size_t pattern_len = strlen(pattern);
1089 const size_t orig_len = strlen(original);
1090
1091 size_t pattern_count = 0;
1092 const char * orig_ptr;
1093 const char * pattern_location;
1094
1095 if (0 == pattern_len)
1096 return original;
1097
1098 if (case_insensitive) {
1099 char *original_lower = new(std::nothrow) char[sizeof(char) * (orig_len + 1)];
1100 if (!original_lower) env->ThrowError("ReplaceStr: malloc failure!");
1101 char *pattern_lower = new(std::nothrow) char[sizeof(char) * (pattern_len + 1)];
1102 if (!pattern_lower) env->ThrowError("ReplaceStr: malloc failure!");
1103
1104 // make them lowercase for comparison
1105 strcpy(original_lower, original);
1106 strcpy(pattern_lower, pattern);
1107 #ifdef MSVC
1108 // works fine also for accented ANSI characters
1109 _locale_t locale = _create_locale(LC_ALL, ".ACP"); // Sets the locale to the ANSI code page obtained from the operating system.
1110 _strlwr_l(original_lower, locale);
1111 _strlwr_l(pattern_lower, locale);
1112 _free_locale(locale);
1113 #else
1114 _strlwr(original_lower);
1115 _strlwr(pattern_lower);
1116 #endif
1117
1118 // find how many times the _lowercased_ pattern occurs in the _lowercased_ original string
1119 for (orig_ptr = original_lower; (pattern_location = strstr(orig_ptr, pattern_lower)); orig_ptr = pattern_location + pattern_len)
1120 {
1121 pattern_count++;
1122 }
1123
1124 // allocate memory for the new string
1125 size_t const retlen = orig_len + pattern_count * (replace_len - pattern_len);
1126 char *result = new(std::nothrow) char[sizeof(char) * (retlen + 1)];
1127 if (!result) env->ThrowError("ReplaceStr: malloc failure!");
1128 *result = 0;
1129
1130 // copy the original string,
1131 // replacing all the instances of the pattern
1132 const char * orig_upper_ptr;
1133 char * result_ptr = result;
1134 // handling dual pointer set: orig, uppercase
1135 for (orig_ptr = original, orig_upper_ptr = original_lower;
1136 (pattern_location = strstr(orig_upper_ptr, pattern_lower));
1137 orig_upper_ptr = pattern_location + pattern_len, orig_ptr = original + (orig_upper_ptr - original_lower))
1138 {
1139 const size_t skiplen = pattern_location - orig_upper_ptr;
1140 // copy the section until the occurence of the pattern
1141 strncpy(result_ptr, orig_ptr, skiplen);
1142 result_ptr += skiplen;
1143 // copy the replacement
1144 strncpy(result_ptr, replacement, replace_len);
1145 result_ptr += replace_len;
1146 }
1147 // copy rest
1148 strcpy(result_ptr, orig_ptr);
1149 AVSValue ret = env->SaveString(result);
1150 delete[] result;
1151 delete[] original_lower;
1152 delete[] pattern_lower;
1153 return ret;
1154 }
1155
1156 // old case sensitive version
1157
1158 // find how many times the pattern occurs in the original string
1159 for (orig_ptr = original; (pattern_location = strstr(orig_ptr, pattern)); orig_ptr = pattern_location + pattern_len)
1160 {
1161 pattern_count++;
1162 }
1163
1164 // allocate memory for the new string
1165 size_t const retlen = orig_len + pattern_count * (replace_len - pattern_len);
1166 char *result = new(std::nothrow) char[sizeof(char) * (retlen + 1)];
1167 if (!result) env->ThrowError("ReplaceStr: malloc failure!");
1168 *result = 0;
1169
1170 // copy the original string,
1171 // replacing all the instances of the pattern
1172 char * result_ptr = result;
1173 for (orig_ptr = original; (pattern_location = strstr(orig_ptr, pattern)); orig_ptr = pattern_location + pattern_len)
1174 {
1175 const size_t skiplen = pattern_location - orig_ptr;
1176 // copy the section until the occurence of the pattern
1177 strncpy(result_ptr, orig_ptr, skiplen);
1178 result_ptr += skiplen;
1179 // copy the replacement
1180 strncpy(result_ptr, replacement, replace_len);
1181 result_ptr += replace_len;
1182 }
1183 // copy rest
1184 strcpy(result_ptr, orig_ptr);
1185 AVSValue ret = env->SaveString(result);
1186 delete[] result;
1187 return ret;
1188 }
1189
1190 AVSValue TrimLeft(AVSValue args, void*, IScriptEnvironment* env)
1191 {
1192 char const *original = args[0].AsString();
1193 char const *s = original;
1194 char ch;
1195 // space, npsp, tab
1196 while ((ch = *s) == (char)32 || ch == (char)160 || ch == (char)9)
1197 s++;
1198
1199 if (original == s)
1200 return args[0]; // avoid SaveString if no change
1201
1202 return env->SaveString(s);
1203 }
1204
1205 AVSValue TrimRight(AVSValue args, void*, IScriptEnvironment* env)
1206 {
1207 char const *original = args[0].AsString();
1208 size_t len = strlen(original);
1209 if (len == 0)
1210 return args[0]; // avoid SaveString if no change
1211
1212 size_t orig_len = len;
1213 char const *s = original + len;
1214
1215 char ch;
1216 // space, npsp, tab
1217 while ((len > 0) && ((ch = *--s) == (char)32 || ch == (char)160 || ch == (char)9)) {
1218 len--;
1219 }
1220
1221 if(orig_len == len)
1222 return args[0]; // avoid SaveString if no change
1223
1224 if (len == 0)
1225 return env->SaveString("");
1226
1227 size_t retlen = s - original + 1;
1228
1229 char *result = new(std::nothrow) char[sizeof(char) * (retlen + 1)];
1230 if (!result) env->ThrowError("TrimRight: malloc failure!");
1231 strncpy(result, original, retlen);
1232 result[retlen] = 0;
1233
1234 AVSValue ret = env->SaveString(result);
1235 delete[] result;
1236 return ret;
1237 }
1238
1239 AVSValue TrimAll(AVSValue args, void*, IScriptEnvironment* env)
1240 {
1241 // not simplify with calling Left/Right, avoid double SaveStrings
1242
1243 // like TrimLeft
1244 char const *original = args[0].AsString();
1245 if (!*original)
1246 return args[0]; // avoid SaveString if no change
1247
1248 char ch;
1249 // space, npsp, tab
1250 while ((ch = *original) == (char)32 || ch == (char)160 || ch == (char)9)
1251 original++;
1252
1253 // almost like TrimRight
1254 size_t len = strlen(original);
1255 if (len == 0)
1256 return env->SaveString("");
1257
1258 size_t orig_len = len;
1259 char const *s = original + len;
1260
1261 // space, npsp, tab
1262 while ((len > 0) && ((ch = *--s) == (char)32 || ch == (char)160 || ch == (char)9))
1263 len--;
1264
1265 if (orig_len == len)
1266 return env->SaveString(original); // nothing to cut from right
1267
1268 if (len == 0)
1269 return env->SaveString(""); // full cut
1270
1271 size_t retlen = s - original + 1;
1272
1273 char *result = new(std::nothrow) char[sizeof(char) * (retlen + 1)];
1274 if (!result) env->ThrowError("TrimAll: malloc failure!");
1275 strncpy(result, original, retlen);
1276 result[retlen] = 0;
1277
1278 AVSValue ret = env->SaveString(result);
1279 delete[] result;
1280 return ret;
1281 }
1282
1283
1284 AVSValue StrCmp(AVSValue args, void*, IScriptEnvironment*)
1285 {
1286 return lstrcmp( args[0].AsString(), args[1].AsString() );
1287 }
1288
1289 AVSValue StrCmpi(AVSValue args, void*, IScriptEnvironment*)
1290 {
1291 return lstrcmpi( args[0].AsString(), args[1].AsString() );
1292 }
1293
1294 AVSValue FindStr(AVSValue args, void*, IScriptEnvironment*)
1295 {
1296 const char *pdest = strstr( args[0].AsString(),args[1].AsString() );
1297 int result = (int)(pdest - args[0].AsString() + 1);
1298 if (pdest == NULL) result = 0;
1299 return result;
1300 }
1301
1302 // FIXME: to v11 64 bit support
1303 AVSValue Rand(AVSValue args, void*, IScriptEnvironment*)
1304 { int limit = args[0].AsInt(RAND_MAX);
1305 bool scale_mode = args[1].AsBool((abs(limit) > RAND_MAX));
1306
1307 if (args[2].AsBool(false)) srand( (unsigned) time(NULL) ); //seed
1308
1309 if (scale_mode) {
1310 double f = 1.0 / (RAND_MAX + 1.0);
1311 return int(f * rand() * limit);
1312 }
1313 else { //modulus mode
1314 int s = (limit < 0 ? -1 : 1);
1315 if (limit==0) return 0;
1316 else return s * rand() % limit;
1317 }
1318 }
1319
1320 AVSValue Select(AVSValue args, void*, IScriptEnvironment* env)
1321 {
1322 // arraysize is still int
1323 int64_t i = args[0].AsLong();
1324 if ((args[1].ArraySize() <= i) || (i < 0) || (i > INT_MAX))
1325 env->ThrowError("Select: Index value out of range");
1326 return args[1][static_cast<int>(i)];
1327 }
1328
1329 AVSValue NOP(AVSValue args, void*, IScriptEnvironment*) { return 0;}
1330
1331 AVSValue Undefined(AVSValue args, void*, IScriptEnvironment*) { return AVSValue();}
1332
1333 AVSValue Exist(AVSValue args, void*, IScriptEnvironment*nv) {
1334 const char *filename = args[0].AsString();
1335 #ifdef AVS_POSIX
1336 constexpr bool utf8default = true;
1337 #else
1338 constexpr bool utf8default = false;
1339 #endif
1340 const bool utf8 = args[1].AsBool(utf8default);
1341
1342 if (strchr(filename, '*') || strchr(filename, '?')) // wildcard
1343 return false;
1344
1345 #ifdef AVS_WINDOWS
1346 if (utf8) {
1347 // fixme/enhance me: check win codepage 65001 UTF8 and do like posix native utf8
1348 // (remark applies to all utf8 in avs+)
1349 auto wsource = Utf8ToWideChar(filename);
1350 std::wstring filename_w = wsource.get();
1351 return fs::exists(filename_w);
1352 }
1353 #endif
1354 return fs::exists(filename);
1355 }
1356
1357
1358 //WE ->
1359
1360 // Spline functions to generate and evaluate a natural bicubic spline
1361 void spline(float x[], float y[], int n, float y2[])
1362 {
1363 int i, k;
1364 float p, qn, sig, un, * u;
1365
1366 u = new float[n];
1367
1368 y2[1] = u[1] = 0.0f;
1369
1370 for (i = 2; i <= n - 1; i++) {
1371 sig = (x[i] - x[i - 1]) / (x[i + 1] - x[i - 1]);
1372 p = sig * y2[i - 1] + 2.0f;
1373 y2[i] = (sig - 1.0f) / p;
1374 u[i] = (y[i + 1] - y[i]) / (x[i + 1] - x[i]) - (y[i] - y[i - 1]) / (x[i] - x[i - 1]);
1375 u[i] = (6.0f * u[i] / (x[i + 1] - x[i - 1]) - sig * u[i - 1]) / p;
1376 }
1377 qn = un = 0.0f;
1378 y2[n] = (un - qn * u[n - 1]) / (qn * y2[n - 1] + 1.0f);
1379 for (k = n - 1; k >= 1; k--) {
1380 y2[k] = y2[k] * y2[k + 1] + u[k];
1381 }
1382
1383 delete[] u;
1384 }
1385
1386 int splint(float xa[], float ya[], float y2a[], int n, float x, float& y, bool cubic)
1387 {
1388 int klo, khi, k;
1389 float h, b, a;
1390
1391 klo = 1;
1392 khi = n;
1393 while (khi - klo > 1) {
1394 k = (khi + klo) >> 1;
1395 if (xa[k] > x) khi = k;
1396 else klo = k;
1397 }
1398 h = xa[khi] - xa[klo];
1399 if (h == 0.0f) {
1400 y = 0.0f;
1401 return -1; // all x's have to be different
1402 }
1403 a = (xa[khi] - x) / h;
1404 b = (x - xa[klo]) / h;
1405
1406 if (cubic) {
1407 y = a * ya[klo] + b * ya[khi] + ((a * a * a - a) * y2a[klo] + (b * b * b - b) * y2a[khi]) * (h * h) / 6.0f;
1408 }
1409 else {
1410 y = a * ya[klo] + b * ya[khi];
1411 }
1412 return 0;
1413 }
1414
1415 // the script functions
1416 AVSValue AVSChr(AVSValue args, void*, IScriptEnvironment* env)
1417 {
1418 char s[2];
1419
1420 s[0] = (char)(args[0].AsInt());
1421 s[1] = 0;
1422 return env->SaveString(s);
1423 }
1424
1425 AVSValue AVSOrd(AVSValue args, void*, IScriptEnvironment*)
1426 {
1427 return (int)args[0].AsString()[0] & 0xFF;
1428 }
1429
1430 AVSValue FillStr(AVSValue args, void*, IScriptEnvironment* env )
1431 {
1432 const int64_t _count = args[0].AsLong();
1433 if (_count <= 0)
1434 env->ThrowError("FillStr: Repeat count must be greater than zero!");
1435
1436 const char *str = args[1].AsString(" ");
1437 const size_t len_to_repeat = strlen(str);
1438 if (len_to_repeat == 0)
1439 return str;
1440
1441 constexpr size_t max_size_t = std::numeric_limits<size_t>::max();
1442 size_t max_repeats = (max_size_t - 1) / len_to_repeat;
1443
1444 size_t count = static_cast<size_t>(_count);
1445 if (count > max_repeats)
1446 env->ThrowError("FillStr: too many repeats, resulting string exceeds the maximum allowed length!");
1447
1448 const size_t total = count * len_to_repeat;
1449
1450 char *buff = new(std::nothrow) char[total+1];
1451 if (!buff)
1452 env->ThrowError("FillStr: memory allocation failure (%zu bytes)!", total + 1);
1453
1454 if (len_to_repeat == 1)
1455 std::fill_n(buff, total, str[0]);
1456 else {
1457 for (size_t i = 0; i < count; i++)
1458 memcpy(buff + i * len_to_repeat, str, len_to_repeat);
1459 }
1460 buff[total] = '\0';
1461
1462 AVSValue ret = env->SaveString(buff);
1463 delete[] buff;
1464 return ret;
1465 }
1466
1467 AVSValue AVSTime(AVSValue args, void*, IScriptEnvironment* env)
1468 {
1469 time_t lt_t;
1470 struct tm* lt;
1471 time(&lt_t);
1472 lt = localtime(&lt_t);
1473 char s[1024];
1474 strftime(s, 1024, args[0].AsString(""), lt);
1475 s[1023] = 0;
1476 return env->SaveString(s);
1477 }
1478
1479 // FIXME: to v11 64 bit support
1480 AVSValue Spline(AVSValue args, void*, IScriptEnvironment* env)
1481 {
1482 int n;
1483 float x, y;
1484 int i;
1485 bool cubic;
1486
1487 AVSValue coordinates;
1488
1489 x = args[0].AsFloatf(0);
1490 coordinates = args[1];
1491 cubic = args[2].AsBool(true);
1492
1493 n = coordinates.ArraySize();
1494
1495 if (n < 4 || n & 1) env->ThrowError("To few arguments for Spline");
1496
1497 n = n / 2;
1498
1499 const size_t work_array_stride = static_cast<size_t>(n) + 1;
1500 std::vector<float> buf(work_array_stride * 3);
1501 float* xa = buf.data();
1502 float* ya = xa + work_array_stride;
1503 float* y2a = ya + work_array_stride;
1504
1505 for (i = 1; i <= n; i++) {
1506 xa[i] = coordinates[(i - 1) * 2 + 0].AsFloatf(0);
1507 ya[i] = coordinates[(i - 1) * 2 + 1].AsFloatf(0);
1508 }
1509
1510 for (i = 1; i < n; i++) {
1511 if (xa[i] >= xa[i + 1]) env->ThrowError("Spline: all x values have to be different and in ascending order!");
1512 }
1513
1514 spline(xa, ya, n, y2a);
1515 splint(xa, ya, y2a, n, x, y, cubic);
1516
1517 return y;
1518 }
1519
1520 // WE <-
1521
1522 static inline const VideoInfo& VI(const AVSValue& arg) { return arg.AsClip()->GetVideoInfo(); }
1523
1524 static const std::map<int, std::string> pixel_format_table =
1525 { // names for lookup by pixel_type or name
1526 {VideoInfo::CS_BGR24, "RGB24"},
1527 {VideoInfo::CS_BGR32, "RGB32"},
1528 {VideoInfo::CS_YUY2 , "YUY2"},
1529 {VideoInfo::CS_YV24 , "YV24"},
1530 {VideoInfo::CS_YV16 , "YV16"},
1531 {VideoInfo::CS_YV12 , "YV12"},
1532 {VideoInfo::CS_I420 , "YV12"},
1533 {VideoInfo::CS_YUV9 , "YUV9"},
1534 {VideoInfo::CS_YV411, "YV411"},
1535 {VideoInfo::CS_Y8 , "Y8"},
1536
1537 {VideoInfo::CS_YUV420P10, "YUV420P10"},
1538 {VideoInfo::CS_YUV422P10, "YUV422P10"},
1539 {VideoInfo::CS_YUV444P10, "YUV444P10"},
1540 {VideoInfo::CS_Y10 , "Y10"},
1541 {VideoInfo::CS_YUV420P12, "YUV420P12"},
1542 {VideoInfo::CS_YUV422P12, "YUV422P12"},
1543 {VideoInfo::CS_YUV444P12, "YUV444P12"},
1544 {VideoInfo::CS_Y12 , "Y12"},
1545 {VideoInfo::CS_YUV420P14, "YUV420P14"},
1546 {VideoInfo::CS_YUV422P14, "YUV422P14"},
1547 {VideoInfo::CS_YUV444P14, "YUV444P14"},
1548 {VideoInfo::CS_Y14 , "Y14"},
1549 {VideoInfo::CS_YUV420P16, "YUV420P16"},
1550 {VideoInfo::CS_YUV422P16, "YUV422P16"},
1551 {VideoInfo::CS_YUV444P16, "YUV444P16"},
1552 {VideoInfo::CS_Y16 , "Y16"},
1553 {VideoInfo::CS_YUV420PS , "YUV420PS"},
1554 {VideoInfo::CS_YUV422PS , "YUV422PS"},
1555 {VideoInfo::CS_YUV444PS , "YUV444PS"},
1556 {VideoInfo::CS_Y32 , "Y32"},
1557
1558 {VideoInfo::CS_BGR48 , "RGB48"},
1559 {VideoInfo::CS_BGR64 , "RGB64"},
1560
1561 {VideoInfo::CS_RGBP , "RGBP"},
1562 {VideoInfo::CS_RGBP10 , "RGBP10"},
1563 {VideoInfo::CS_RGBP12 , "RGBP12"},
1564 {VideoInfo::CS_RGBP14 , "RGBP14"},
1565 {VideoInfo::CS_RGBP16 , "RGBP16"},
1566 {VideoInfo::CS_RGBPS , "RGBPS"},
1567
1568 {VideoInfo::CS_YUVA420, "YUVA420"},
1569 {VideoInfo::CS_YUVA422, "YUVA422"},
1570 {VideoInfo::CS_YUVA444, "YUVA444"},
1571 {VideoInfo::CS_YUVA420P10, "YUVA420P10"},
1572 {VideoInfo::CS_YUVA422P10, "YUVA422P10"},
1573 {VideoInfo::CS_YUVA444P10, "YUVA444P10"},
1574 {VideoInfo::CS_YUVA420P12, "YUVA420P12"},
1575 {VideoInfo::CS_YUVA422P12, "YUVA422P12"},
1576 {VideoInfo::CS_YUVA444P12, "YUVA444P12"},
1577 {VideoInfo::CS_YUVA420P14, "YUVA420P14"},
1578 {VideoInfo::CS_YUVA422P14, "YUVA422P14"},
1579 {VideoInfo::CS_YUVA444P14, "YUVA444P14"},
1580 {VideoInfo::CS_YUVA420P16, "YUVA420P16"},
1581 {VideoInfo::CS_YUVA422P16, "YUVA422P16"},
1582 {VideoInfo::CS_YUVA444P16, "YUVA444P16"},
1583 {VideoInfo::CS_YUVA420PS , "YUVA420PS"},
1584 {VideoInfo::CS_YUVA422PS , "YUVA422PS"},
1585 {VideoInfo::CS_YUVA444PS , "YUVA444PS"},
1586
1587 {VideoInfo::CS_RGBAP , "RGBAP"},
1588 {VideoInfo::CS_RGBAP10 , "RGBAP10"},
1589 {VideoInfo::CS_RGBAP12 , "RGBAP12"},
1590 {VideoInfo::CS_RGBAP14 , "RGBAP14"},
1591 {VideoInfo::CS_RGBAP16 , "RGBAP16"},
1592 {VideoInfo::CS_RGBAPS , "RGBAPS"},
1593 };
1594
1595 static const std::multimap<int, std::string> pixel_format_table_ex =
1596 { // alternative names for lookup by name (multimap!)
1597 {VideoInfo::CS_YV24 , "YUV444"},
1598 {VideoInfo::CS_YV16 , "YUV422"},
1599 {VideoInfo::CS_YV12 , "YUV420"},
1600 {VideoInfo::CS_YV411, "YUV411"},
1601 {VideoInfo::CS_RGBP , "RGBP8"},
1602 {VideoInfo::CS_RGBAP, "RGBAP8"},
1603 {VideoInfo::CS_YV24 , "YUV444P8"},
1604 {VideoInfo::CS_YV16 , "YUV422P8"},
1605 {VideoInfo::CS_YV12 , "YUV420P8"},
1606 {VideoInfo::CS_YV411, "YUV411P8"},
1607 {VideoInfo::CS_YUVA420, "YUVA420P8"},
1608 {VideoInfo::CS_YUVA422, "YUVA422P8"},
1609 {VideoInfo::CS_YUVA444, "YUVA444P8"},
1610 };
1611
1612 const char *GetPixelTypeName(const int pixel_type)
1613 {
1614 const std::string name = "";
1615 auto it = pixel_format_table.find(pixel_type);
1616 if (it == pixel_format_table.end())
1617 return "";
1618 return (it->second).c_str();
1619 }
1620
1621 7 int GetPixelTypeFromName(const char *pixeltypename)
1622 {
1623
1/2
✓ Branch 4 → 5 taken 7 times.
✗ Branch 4 → 46 not taken.
7 std::string name_to_find = pixeltypename;
1624
2/2
✓ Branch 19 → 8 taken 51 times.
✓ Branch 19 → 20 taken 7 times.
116 for (auto & c: name_to_find) c = toupper(c); // uppercase input string
1625
2/2
✓ Branch 29 → 21 taken 408 times.
✓ Branch 29 → 30 taken 6 times.
414 for (auto it = pixel_format_table.begin(); it != pixel_format_table.end(); it++)
1626 {
1627
3/4
✓ Branch 22 → 23 taken 408 times.
✗ Branch 22 → 49 not taken.
✓ Branch 23 → 24 taken 1 time.
✓ Branch 23 → 26 taken 407 times.
408 if ((it->second).compare(name_to_find) == 0)
1628 1 return it->first;
1629 }
1630 // find by alternative names e.g. YUV420 or YUV420P8 instead of YV12
1631
1/2
✓ Branch 40 → 32 taken 26 times.
✗ Branch 40 → 41 not taken.
26 for (auto it = pixel_format_table_ex.begin(); it != pixel_format_table_ex.end(); it++)
1632 {
1633
3/4
✓ Branch 33 → 34 taken 26 times.
✗ Branch 33 → 50 not taken.
✓ Branch 34 → 35 taken 6 times.
✓ Branch 34 → 37 taken 20 times.
26 if ((it->second).compare(name_to_find) == 0)
1634 6 return it->first;
1635 }
1636 return VideoInfo::CS_UNKNOWN;
1637 7 }
1638
1639
1640 AVSValue PixelType (AVSValue args, void*, IScriptEnvironment*) {
1641 return GetPixelTypeName(VI(args[0]).pixel_type);
1642 }
1643
1644 // AVS+
1645 AVSValue ColorSpaceNameToPixelType (AVSValue args, void*, IScriptEnvironment*) {
1646 return GetPixelTypeFromName(args[0].AsString());
1647 }
1648
1649 AVSValue Width(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).width; }
1650 AVSValue Height(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).height; }
1651 AVSValue FrameCount(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).num_frames; }
1652 AVSValue FrameRate(AVSValue args, void*, IScriptEnvironment*) { const VideoInfo& vi = VI(args[0]); return (double)vi.fps_numerator / vi.fps_denominator; } // maximise available precision
1653 AVSValue FrameRateNumerator(AVSValue args, void*, IScriptEnvironment*) { return (int)VI(args[0]).fps_numerator; } // unsigned int truncated to int
1654 AVSValue FrameRateDenominator(AVSValue args, void*, IScriptEnvironment*) { return (int)VI(args[0]).fps_denominator; } // unsigned int truncated to int
1655 AVSValue AudioRate(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).audio_samples_per_second; }
1656 AVSValue AudioLength(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).num_audio_samples; } // since v11 not truncated to int
1657 AVSValue AudioLengthLo(AVSValue args, void*, IScriptEnvironment*) { return (int)(VI(args[0]).num_audio_samples % (unsigned)args[1].AsInt(1000000000)); }
1658 AVSValue AudioLengthHi(AVSValue args, void*, IScriptEnvironment*) { return (int)(VI(args[0]).num_audio_samples / (unsigned)args[1].AsInt(1000000000)); }
1659 AVSValue AudioLengthS(AVSValue args, void*, IScriptEnvironment* env) {
1660 char s[32];
1661 #ifdef AVS_WINDOWS
1662 return env->SaveString(_i64toa(VI(args[0]).num_audio_samples, s, 10));
1663 #else
1664 sprintf(s, "%" PRId64, VI(args[0]).num_audio_samples);
1665 return env->SaveString(s);
1666 #endif
1667 }
1668 AVSValue AudioLengthF(AVSValue args, void*, IScriptEnvironment*) { return static_cast<double>(VI(args[0]).num_audio_samples); } // at least this will give an order of the size.
1669 // Since v11 it has of little use: AudioLength now can return int64,
1670 // anyway, cast to double instead of float
1671
1672 AVSValue AudioDuration(AVSValue args, void*, IScriptEnvironment*) {
1673 const VideoInfo& vi = VI(args[0]);
1674 return (double)vi.num_audio_samples / vi.audio_samples_per_second;
1675 }
1676
1677 AVSValue AudioChannels(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).HasAudio() ? VI(args[0]).nchannels : 0; }
1678 AVSValue AudioBits(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).BytesPerChannelSample()*8; }
1679 AVSValue IsAudioFloat(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsSampleType(SAMPLE_FLOAT); }
1680 AVSValue IsAudioInt(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsSampleType(SAMPLE_INT8 | SAMPLE_INT16 | SAMPLE_INT24 | SAMPLE_INT32 ); }
1681 AVSValue IsChannelMaskKnown(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsChannelMaskKnown(); }
1682 AVSValue GetChannelMask(AVSValue args, void*, IScriptEnvironment*) { return (int)VI(args[0]).GetChannelMask(); }
1683
1684 AVSValue IsRGB(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsRGB(); }
1685 AVSValue IsRGB24(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsRGB24(); }
1686 AVSValue IsRGB32(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsRGB32(); }
1687 AVSValue IsYUV(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsYUV(); }
1688 AVSValue IsYUY2(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsYUY2(); }
1689 AVSValue IsY8(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsY8(); }
1690 AVSValue IsYV12(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsYV12(); }
1691 AVSValue IsYV16(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsYV16(); }
1692 AVSValue IsYV24(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsYV24(); }
1693 AVSValue IsYV411(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsYV411(); }
1694 AVSValue IsPlanar(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsPlanar(); }
1695 AVSValue IsInterleaved(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsColorSpace(VideoInfo::CS_INTERLEAVED); }
1696 AVSValue IsFieldBased(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsFieldBased(); }
1697 AVSValue IsFrameBased(AVSValue args, void*, IScriptEnvironment*) { return !VI(args[0]).IsFieldBased(); }
1698 AVSValue GetParity(AVSValue args, void*, IScriptEnvironment*) { return args[0].AsClip()->GetParity(args[1].AsInt(0)); }
1699
1700 AVSValue HasVideo(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).HasVideo(); }
1701 AVSValue HasAudio(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).HasAudio(); }
1702
1703 AVSValue String(AVSValue args, void*, IScriptEnvironment* env)
1704 {
1705 if (args[0].IsString()) return args[0];
1706 if (args[0].IsBool()) return (args[0].AsBool() ? "true" : "false");
1707 if (args[0].IsFunction()) return args[0].AsFunction()->ToString(env);
1708 if (args[1].Defined()) {
1709 // WE --> when a format parameter is present
1710 // order! if it is an Int: IsFloat gives True
1711 // If parameter exists, always convert to float
1712 if (args[0].GetType() == AvsValueType::VALUE_TYPE_FLOAT) // real 32 bit float
1713 {
1714 return env->Sprintf(args[1].AsString("%f"), args[0].AsFloatf());
1715 // AsFloatf returns 32 bit float
1716 }
1717 else if (args[0].IsFloat()) // 32 or 64 bit integer or double
1718 {
1719 return env->Sprintf(args[1].AsString("%lf"), args[0].AsFloat());
1720 // AsFloat returns double
1721 }
1722 return "";
1723 }
1724 else {
1725 // standard behaviour
1726 if (args[0].IsLongStrict()) {
1727 char s[21];
1728 sprintf(s, "%" PRId64, args[0].AsLong());
1729 return env->SaveString(s);
1730 }
1731 if (args[0].IsInt()) {
1732 char s[12]; // with sign: worst case 11
1733 sprintf(s, "%d", args[0].AsInt());
1734 return env->SaveString(s);
1735 }
1736 if (args[0].IsFloat()) { // for double as well.
1737 char s[50]; // safe size for double
1738 #ifdef MSVC
1739 _locale_t locale = _create_locale(LC_NUMERIC, "C"); // decimal point: dot
1740 _sprintf_l(s, "%lf", locale, args[0].AsFloat());
1741 _free_locale(locale);
1742 #else
1743 sprintf(s, "%lf", args[0].AsFloat());
1744 #endif
1745 return env->SaveString(s);
1746 }
1747 }
1748 return "";
1749 }
1750
1751 AVSValue Hex(AVSValue args, void*, IScriptEnvironment* env)
1752 {
1753 int n = args[0].AsInt();
1754 int wid = args[1].AsInt(0); // 0..8 is the minimum width of the returned string
1755 wid = (wid<0) ? 0 : (wid > 8) ? 8 : wid;
1756 char buf[8 + 1];
1757 sprintf_s(buf, "%0*X", wid, n); // uppercase, unlike <=r2580
1758 return env->SaveString(buf);
1759 }
1760
1761 static std::string AVSValue_to_string(AVSValue v, IScriptEnvironment* env) {
1762 if (v.IsString()) return v.AsString();
1763 if (v.IsBool()) return v.AsBool() ? "true" : "false";
1764 if (v.IsFunction()) return v.AsFunction()->ToString(env);
1765 if (v.IsInt()) return std::to_string(v.AsLong()); // AsLong handles both 32 and 64-bit int
1766 if (v.IsFloat()) return double_to_string(v.AsFloat());
1767 return "";
1768 }
1769
1770
1771 // Formatting function with parameter list with ordered, indexed or named replacements
1772 AVSValue FormatString(AVSValue args, void*, IScriptEnvironment* env)
1773 {
1774 // Format("{} {}!", "Hello", "world")
1775 // max_pixel_value = 255
1776 // Format("max pixel value = {max_pixel_value}!")
1777 // Format("Pi={1} x={0} y={0}!", 12, Pi())
1778 std::string format = args[0].AsString();
1779 int numargs = args[1].ArraySize();
1780
1781 // (name), value pairs, name can be empty
1782 std::vector<std::pair<std::string, std::string>> sv;
1783 for (int i = 0; i < numargs; i++)
1784 {
1785 std::string name; // can be empty
1786 std::string val_as_s;
1787
1788 AVSValue v = args[1][i];
1789 // ["name", value] support
1790 if (v.IsArray()) {
1791 if (v.ArraySize() != 2 || !v[0].IsString())
1792 env->ThrowError("Format: for key-value lookup parameter must be in [\"name\", value] array format");
1793 name = v[0].AsString();
1794 v = v[1];
1795 }
1796
1797 val_as_s = AVSValue_to_string(v, env);
1798
1799 sv.push_back(std::make_pair(name, val_as_s));
1800 }
1801
1802 size_t supplied_params_count = sv.size();
1803
1804 size_t len = format.size();
1805 size_t i = 0;
1806 bool in_parenthesis = false;
1807 std::string ss;
1808 size_t last_pos = 0;
1809 std::string last_param_section;
1810
1811 size_t param_counter = 0;
1812
1813 while (i < len) {
1814 if (!in_parenthesis) {
1815 size_t x = format.find_first_of('{', last_pos);
1816 // }} can appear only when escaped
1817 size_t cx = format.find_first_of('}', last_pos);
1818 if (cx != std::string::npos && cx < x)
1819 {
1820 if (cx + 1 < len && format[cx + 1] == '}') // }} escaped
1821 {
1822 ss += format.substr(last_pos, cx - last_pos + 1);
1823 last_pos = cx + 2;
1824 i = last_pos;
1825 continue;
1826 }
1827 env->ThrowError("Format: unbalanced curly bracket at position %zu", cx);
1828 }
1829
1830 if (x == std::string::npos) // { not found
1831 {
1832 ss += format.substr(last_pos); // copy rest
1833 break;
1834 }
1835 else if (x + 1 < len && format[x + 1] == '{') // {{ escaped
1836 {
1837 ss += format.substr(last_pos, x - last_pos + 1);
1838 last_pos = x + 2;
1839 i = last_pos;
1840 }
1841 else {
1842 // found {, not escaped
1843 ss += format.substr(last_pos, x - last_pos);
1844 last_pos = x + 1; // points to after the {
1845 i = last_pos;
1846 in_parenthesis = true;
1847 }
1848 continue;
1849 } // end of not-in-bracket-mode
1850
1851 // in-curly-bracket: search for the closing bracket
1852
1853 size_t x = format.find_first_of('}', last_pos);
1854 if (x == std::string::npos) // not found, will throw error outside
1855 break;
1856
1857 last_param_section = format.substr(last_pos, x - last_pos); // name, order number or empty
1858
1859 if (last_param_section.empty()) {
1860 // simple {}, insert next parameter, consume one from the list
1861 // in c++20 you cannot mix
1862 if (param_counter >= supplied_params_count)
1863 env->ThrowError("Format: more parameter sections than parameters supplied");
1864 ss += sv[param_counter++].second;
1865 }
1866 else {
1867 // name or number
1868
1869 bool validName = true;
1870 // check for a valid identifier name
1871 auto ch = last_param_section[0];
1872 if (ch != '_' && !isalpha(ch))
1873 validName = false;
1874 else {
1875 for (size_t i = 1; i < last_param_section.length(); i++) {
1876 const char ch = last_param_section[i];
1877 if (!(ch == '_' || isalnum(ch))) {
1878 validName = false;
1879 break;
1880 }
1881 }
1882 }
1883
1884 if (!validName) {
1885 // valid number like {2} to index parameters
1886 int index;
1887 try {
1888 // string -> integer
1889 index = std::stoi(last_param_section);
1890 }
1891 catch (...) {
1892 env->ThrowError("Format: invalid parameter specifier: \"%s\".", last_param_section.c_str());
1893 }
1894
1895 if (index < 0 || index >= (int)supplied_params_count)
1896 env->ThrowError("Format: parameter index is out of range: %d", index);
1897
1898 ss += sv[index].second;
1899 }
1900 else {
1901 // find among the named parameters
1902 auto it = std::find_if(sv.begin(), sv.end(),
1903 [&last_param_section](const std::pair<std::string, std::string>& element) { return element.first == last_param_section; });
1904 if (it != sv.end())
1905 ss += it->second; // name was found
1906 else {
1907 // last resort: variable with the given name
1908 AVSValue v;
1909 if (!env->GetVarTry(last_param_section.c_str(), &v))
1910 env->ThrowError("Format: no parameter or variable found with name \"%s\".", last_param_section.c_str());
1911
1912 std::string val_as_s = AVSValue_to_string(v, env);
1913
1914 ss += val_as_s;
1915 }
1916 }
1917 }
1918
1919 last_pos = x + 1;
1920 i = last_pos;
1921 in_parenthesis = false;
1922 }
1923
1924 if(in_parenthesis)
1925 env->ThrowError("Format: unclosed curly bracket");
1926
1927 return env->SaveString(ss.c_str());
1928 }
1929
1930 AVSValue Func(AVSValue args, void*, IScriptEnvironment*) { return args[0]; }
1931
1932 AVSValue IsBool(AVSValue args, void*, IScriptEnvironment*) { return args[0].IsBool(); }
1933 AVSValue IsInt(AVSValue args, void*, IScriptEnvironment*) { return args[0].IsInt(); }
1934 AVSValue IsLongStrict(AVSValue args, void*, IScriptEnvironment*) { return args[0].IsLongStrict(); }
1935 AVSValue IsFloat(AVSValue args, void*, IScriptEnvironment*) { return args[0].IsFloat(); }
1936 AVSValue IsFloatfStrict(AVSValue args, void*, IScriptEnvironment*) { return args[0].IsFloatfStrict(); }
1937 AVSValue IsString(AVSValue args, void*, IScriptEnvironment*) { return args[0].IsString(); }
1938 AVSValue IsClip(AVSValue args, void*, IScriptEnvironment*) { return args[0].IsClip(); }
1939 AVSValue IsFunction(AVSValue args, void*, IScriptEnvironment*) { return args[0].IsFunction(); }
1940 AVSValue Defined(AVSValue args, void*, IScriptEnvironment*) { return args[0].Defined(); }
1941
1942 const char* GetAVSTypeName(AVSValue value) {
1943 if (value.IsClip())
1944 return "clip";
1945 else if (value.IsBool())
1946 return "bool";
1947 else if (value.IsLongStrict()) // must be before IsInt
1948 return "long";
1949 else if (value.IsInt())
1950 return "int";
1951 else if (value.IsFloatfStrict()) // before IsFloat
1952 return "float";
1953 else if (value.IsFloat())
1954 return "double";
1955 else if (value.IsString())
1956 return "string";
1957 else if (value.IsArray())
1958 return "array";
1959 else if (value.IsFunction())
1960 return "function";
1961 else if (!value.Defined())
1962 return "undefined value";
1963 else
1964 return "unknown type";
1965 }
1966
1967 AVSValue TypeName(AVSValue args, void*, IScriptEnvironment*) { return GetAVSTypeName(args[0]); }
1968
1969 AVSValue Default(AVSValue args, void*, IScriptEnvironment*) { return args[0].Defined() ? args[0] : args[1]; }
1970
1971 static float find_next_valid_float(const double version) {
1972 float version_f = static_cast<float>(version);
1973 const float initial_value = version_f;
1974 // float epsilon = std::nextafterf(version_f, INFINITY) - version_f;
1975 int steps = 0;
1976
1977 while (version_f < version) {
1978 version_f = std::nextafterf(version_f, INFINITY);
1979 steps++;
1980
1981 // Safety check to prevent infinite loops
1982 if (steps > 1000000) {
1983 // std::cout << "Too many steps required, possible overflow\n";
1984 version_f = initial_value;
1985 break;
1986 }
1987 }
1988 return version_f;
1989 }
1990
1991 AVSValue VersionNumber(AVSValue args, void*, IScriptEnvironment*) {
1992 const double VersionToReturn = AVS_VERSION; // consider upgrading
1993 float VersionToReturnf = find_next_valid_float(VersionToReturn);
1994 return VersionToReturnf;
1995 // A typical transition, when - even in Avisynth+ - we return 2.6f here.
1996 // From 3.7.4 script constants are of 64-bit double precision, and
1997 // the very popular IsAvs26 = VersionNumber() >= 2.6 will fail, since
1998 // 2.6f < 2.6, (double)(float)2.6 is 2.5999999046. Arrrgh.
1999 // 2.6 cannot be exactly specified as a floating point number and has
2000 // differently rounded values in float and in double.
2001 // Thus we start increasing the float value with the smallest available steps,
2002 // until the comparison will be fine.
2003 // Prior to interface version 11, Avisynth supported only 32-bit floating-point data (float), not 64-bit (double).
2004 // To maintain compatibility (old plugins get this value as 32 bit float), this return
2005 // value must remain a 32-bit float.
2006 }
2007
2008 AVSValue VersionString(AVSValue args, void*, IScriptEnvironment*) { return AVS_FULLVERSION; }
2009 AVSValue IsVersionOrGreater(AVSValue args, void*, IScriptEnvironment* env)
2010 {
2011 if (!args[0].Defined() || !args[1].Defined()) {
2012 env->ThrowError("IsVersionOrGreater error: at least two parameters (majorVersion, minorVersion) required!");
2013 }
2014
2015 // true when current version is at least the same or greater than the given three-part version
2016 const int majorVersion = args[0].AsInt(0);
2017 const int minorVersion = args[1].AsInt(0);
2018 const int bugfixVersion = args[2].AsInt(0);
2019 if (majorVersion != AVS_MAJOR_VER) return majorVersion < AVS_MAJOR_VER;
2020 if (minorVersion != AVS_MINOR_VER) return minorVersion < AVS_MINOR_VER;
2021 return bugfixVersion <= AVS_BUGFIX_VER;
2022 }
2023
2024 AVSValue Frac(AVSValue args, void*, IScriptEnvironment*) {
2025 if (args[0].IsInt()) return 0.f;
2026 double result = args[0].AsFloat() - int64_t(args[0].AsFloat());
2027 if (args[0].IsFloat())
2028 return (float)result;
2029 return result;
2030 }
2031
2032 AVSValue Int(AVSValue args, void*, IScriptEnvironment*) {
2033 if (args[0].IsLongStrict()) return args[0].AsLong();
2034 if (args[0].IsInt()) return args[0].AsInt();
2035
2036 int64_t result = int64_t(args[0].AsFloat());
2037 if (result >= INT_MIN && result <= INT_MAX)
2038 return (int)result;
2039 return result;
2040 }
2041
2042 AVSValue IntI(AVSValue args, void*, IScriptEnvironment*) {
2043 if (args[0].IsInt()) return static_cast<int>(args[0].AsLong());
2044
2045 int result = static_cast<int>(args[0].AsFloat());
2046 return result;
2047 }
2048
2049 AVSValue Long(AVSValue args, void*, IScriptEnvironment*) {
2050 if (args[0].IsInt()) return args[0].AsLong();
2051
2052 int64_t result = static_cast<int64_t>(args[0].AsFloat());
2053 return result;
2054 }
2055
2056 AVSValue Float(AVSValue args, void*, IScriptEnvironment*) {
2057 if (args[0].IsInt())
2058 return (double)args[0].AsLong();
2059 if(args[0].IsFloatfStrict())
2060 return args[0].AsFloatf();
2061 return args[0].AsFloat();
2062 }
2063
2064 // Always to 64 bit double
2065 AVSValue Double(AVSValue args, void*, IScriptEnvironment*) {
2066 return args[0].AsFloat();
2067 }
2068
2069 // Always to 32 bit float
2070 AVSValue Floatf(AVSValue args, void*, IScriptEnvironment*) {
2071 return args[0].AsFloatf();
2072 }
2073
2074 AVSValue Value(AVSValue args, void*, IScriptEnvironment*) { char *stopstring; return strtod(args[0].AsString(),&stopstring); }
2075
2076 AVSValue HexValue(AVSValue args, void*, IScriptEnvironment*)
2077 {
2078 // Added optional pos arg default = 1, start position in string of the HexString, 1 denotes the string beginning.
2079 // Will return 0 if error in 'pos' ie if pos is less than 1 or greater than string length.
2080 const char* str = args[0].AsString();
2081 int64_t pos = args[1].AsLong(1) - 1; // start is pos 1
2082 size_t sz = strlen(str);
2083 if (pos < 0 || static_cast<size_t>(pos) >= sz)
2084 return 0;
2085 str += pos;
2086 char* stopstring;
2087 unsigned long result = strtoul(str, &stopstring, 16);
2088 // keep int range, FFFFFFFF is negative - compatibility, see HexValue64
2089 return (int)(result);
2090 }
2091
2092 // new in v11
2093 AVSValue HexValue64(AVSValue args, void*, IScriptEnvironment*)
2094 {
2095 // Added optional pos arg default = 1, start position in string of the HexString, 1 denotes the string beginning.
2096 // Will return 0 if error in 'pos' ie if pos is less than 1 or greater than string length.
2097 const char* str = args[0].AsString();
2098 int64_t pos = args[1].AsLong(1) - 1; // start is pos 1
2099 size_t sz = strlen(str);
2100 if (pos < 0 || static_cast<size_t>(pos) >= sz)
2101 return 0;
2102 str += pos;
2103 char* stopstring;
2104 // v11: strtoul --> strtoull to 64 bit results long long
2105 unsigned long long result = strtoull(str, &stopstring, 16);
2106 // FFFFFFFF is positive
2107 return static_cast<int64_t>(result);
2108 }
2109
2110 AVSValue AvsMin(AVSValue args, void*, IScriptEnvironment* env)
2111 {
2112 bool isInt = true;
2113 bool isFloat32 = true;
2114
2115 const int n = args[0].ArraySize();
2116 if (n < 2) env->ThrowError("Too few arguments for Min");
2117
2118 // If all numbers are Ints return an Int
2119 for (int i = 0; i < n; i++)
2120 if (!args[0][i].IsInt()) {
2121 isInt = false;
2122 break;
2123 }
2124
2125 // v11: If all numbers are 32 bit floats return real float instead of double
2126 for (int i = 0; i < n; i++)
2127 if (!args[0][i].IsFloatfStrict()) {
2128 isFloat32 = false;
2129 break;
2130 }
2131
2132 if (isInt) {
2133 int64_t V = args[0][0].AsLong();
2134 for (int i = 1; i < n; i++)
2135 V = min(V, args[0][i].AsLong());
2136 // keep the smaller type
2137 if (V >= INT_MIN && V <= INT_MAX)
2138 return (int)V;
2139 return V;
2140 }
2141 else {
2142 double V = args[0][0].AsFloat();
2143 for (int i = 1; i < n; i++)
2144 V = min(V, args[0][i].AsFloat());
2145 if (isFloat32)
2146 return (float)V;
2147 return V;
2148 }
2149 }
2150
2151 AVSValue AvsMax(AVSValue args, void*, IScriptEnvironment* env)
2152 {
2153 bool isInt = true;
2154 bool isFloat32 = true;
2155
2156 const int n = args[0].ArraySize();
2157 if (n < 2) env->ThrowError("Too few arguments for Max");
2158
2159 // If all numbers are Ints return an Int
2160 for (int i = 0; i < n; i++)
2161 if (!args[0][i].IsInt()) {
2162 isInt = false;
2163 break;
2164 }
2165
2166 // v11: If all numbers are 32 bit floats return real float instead of double
2167 for (int i = 0; i < n; i++)
2168 if (!args[0][i].IsFloatfStrict()) {
2169 isFloat32 = false;
2170 break;
2171 }
2172
2173 if (isInt) {
2174 int64_t V = args[0][0].AsLong();
2175 for (int i = 1; i < n; i++)
2176 V = max(V, args[0][i].AsLong());
2177 // keep the smaller type
2178 if (V >= INT_MIN && V <= INT_MAX)
2179 return (int)V;
2180 return V;
2181 }
2182 else {
2183 double V = args[0][0].AsFloat();
2184 for (int i = 1; i < n; i++)
2185 V = max(V, args[0][i].AsFloat());
2186 if (isFloat32)
2187 return (float)V;
2188 return V;
2189 }
2190 }
2191
2192 // The "AddAutoloadDir" script function allows supplying a UTF-8 directory path
2193 // even when the system code page is ANSI.
2194 // On Windows the Avisynth script typically treats string parameters as ANSI
2195 // by default, unless we force the interpretation with an optional bool utf8 = true.
2196 // The directory parameter is converted from ANSI to UTF-8 internally before calling
2197 // the interface API. This behavior depends on the IScriptEnvironment2 (development)
2198 // interface, which provides an AddAutoloadDir method that requires now UTF-8 paths.
2199 AVSValue AddAutoloadDir (AVSValue args, void*, IScriptEnvironment* env)
2200 {
2201 IScriptEnvironment2 *env2 = static_cast<IScriptEnvironment2*>(env);
2202 const bool utf8 = args[2].AsBool(false); // default ANSI on Windows, n/a (see charToUtf8) on other OSes
2203 auto dir_utf8 = charToUtf8(args[0].AsString(), utf8); // takes care of ANSI to UTF8 conversion on Windows if needed
2204 env2->AddAutoloadDir(dir_utf8.c_str(), args[1].AsBool(true));
2205
2206 return AVSValue();
2207 }
2208
2209 AVSValue ClearAutoloadDirs (AVSValue args, void*, IScriptEnvironment* env)
2210 {
2211 IScriptEnvironment2 *env2 = static_cast<IScriptEnvironment2*>(env);
2212 env2->ClearAutoloadDirs();
2213 return AVSValue();
2214 }
2215
2216 AVSValue ListAutoloadDirs(AVSValue args, void*, IScriptEnvironment* env)
2217 {
2218 InternalEnvironment* envi = static_cast<InternalEnvironment*>(env);
2219 const char* AutoLoadDirs = envi->ListAutoloadDirs(); // internally uses SaveString
2220 #if defined(AVS_WINDOWS)
2221 const bool utf8 = args[0].AsBool(false); // default ANSI on Windows, n/a on other OSes
2222 if (!utf8) {
2223 // On Windows the environment uses ANSI by default. When the caller requests ANSI
2224 // (utf8 == false), convert the internally stored UTF-8 string to ANSI before returning.
2225 // Characters that cannot be represented in ANSI will be replaced with '?'.
2226 return AVSValue(env->SaveString(Utf8ToAnsi(AutoLoadDirs).c_str())); // New SaveString needed.
2227 }
2228 #endif
2229 return AVSValue(AutoLoadDirs);
2230 }
2231
2232
2233 AVSValue AutoloadPlugins (AVSValue args, void*, IScriptEnvironment* env)
2234 {
2235 IScriptEnvironment2 *env2 = static_cast<IScriptEnvironment2*>(env);
2236 env2->AutoloadPlugins();
2237 return AVSValue();
2238 }
2239
2240 AVSValue FunctionExists (AVSValue args, void*, IScriptEnvironment* env)
2241 {
2242 return env->FunctionExists(args[0].AsString());
2243 }
2244
2245 AVSValue InternalFunctionExists (AVSValue args, void*, IScriptEnvironment* env)
2246 {
2247 IScriptEnvironment2 *env2 = static_cast<IScriptEnvironment2*>(env);
2248 return env2->InternalFunctionExists(args[0].AsString());
2249 }
2250
2251 AVSValue SetFilterMTMode (AVSValue args, void*, IScriptEnvironment* env)
2252 {
2253 IScriptEnvironment2 *env2 = static_cast<IScriptEnvironment2*>(env);
2254 env2->SetFilterMTMode(args[0].AsString(), (MtMode)args[1].AsInt(), args[2].AsBool(false));
2255 return AVSValue();
2256 }
2257
2258 AVSValue SetLogParams(AVSValue args, void*, IScriptEnvironment* env)
2259 {
2260 const char* target = args[0].AsString("stderr");
2261 const int level = args[1].AsInt(LOGLEVEL_INFO);
2262
2263 InternalEnvironment *envi = static_cast<InternalEnvironment*>(env);
2264 envi->SetLogParams(target, level);
2265 return AVSValue();
2266 }
2267
2268 AVSValue LogMsg(AVSValue args, void*, IScriptEnvironment* env)
2269 {
2270 if ((args.ArraySize() != 2) || !args[0].IsString() || !args[1].IsInt())
2271 {
2272 env->ThrowError("Invalid parameters to Log() function.");
2273 }
2274 else
2275 {
2276 InternalEnvironment *envi = static_cast<InternalEnvironment*>(env);
2277 envi->LogMsg(args[1].AsInt(), args[0].AsString());
2278 }
2279 return AVSValue();
2280 }
2281
2282 AVSValue SetCacheMode(AVSValue args, void*, IScriptEnvironment* env)
2283 {
2284 InternalEnvironment *envI = static_cast<InternalEnvironment*>(env);
2285 envI->SetCacheMode((CacheMode)args[0].AsInt());
2286 return AVSValue();
2287 }
2288
2289 AVSValue SetDeviceOpt(AVSValue args, void*, IScriptEnvironment* env)
2290 {
2291 InternalEnvironment *envI = static_cast<InternalEnvironment*>(env);
2292 envI->SetDeviceOpt((DeviceOpt)args[0].AsInt(), args[1].AsInt(0));
2293 return AVSValue();
2294 }
2295
2296 AVSValue SetFilterProp(AVSValue args, void*, IScriptEnvironment* env)
2297 {
2298 InternalEnvironment* envi = static_cast<InternalEnvironment*>(env);
2299
2300 // 3+1-arg: "ss.[mode]i"
2301 // 5+1-arg: "ss.s.[mode]i":
2302 // arg3 exists and is string or integer/undefined
2303 if (args[3].IsString()) {
2304 // 5+1-arg conditional form matched by "ss.s.[mode]i":
2305 // SetFilterProp(filter, param_name, param_match, prop_key, prop_value [, mode])
2306 // Inject prop_key=prop_value only when the named call arg 'param_name' equals param_match.
2307 const AVSValue& param_match = args[2];
2308 // param_match may be a scalar (int/float/bool/string) or an array of scalars (aliases)
2309 if (param_match.IsArray()) {
2310 for (int j = 0; j < param_match.ArraySize(); ++j) {
2311 const AVSValue& elem = param_match[j];
2312 if (!elem.IsInt() && !elem.IsFloat() && !elem.IsString() && !elem.IsBool())
2313 env->ThrowError("SetFilterProp: alias array element %d must be int, float, bool, or string", j);
2314 }
2315 } else if (!param_match.IsInt() && !param_match.IsFloat() && !param_match.IsString() && !param_match.IsBool()) {
2316 env->ThrowError("SetFilterProp: condition value must be int, float, bool, string, "
2317 "or an array of those");
2318 }
2319 const AVSValue& prop_value = args[4];
2320 if (prop_value.Defined() &&
2321 !prop_value.IsInt() && !prop_value.IsFloat() && !prop_value.IsString() &&
2322 !prop_value.IsBool() && !prop_value.IsFunction())
2323 env->ThrowError("SetFilterProp: property value (arg 5) must be int, float, bool, string, or function");
2324 // Frame properties have no bool type: convert bool value to int 0/1
2325 AVSValue stored_value = prop_value;
2326 if (stored_value.IsBool())
2327 stored_value = AVSValue(stored_value.AsBool() ? 1 : 0);
2328 const int mode = args[5].AsInt(AVSPropAppendMode::PROPAPPENDMODE_REPLACE);
2329 envi->SetFilterPropConditional(args[0].AsString(), args[1].AsString(), param_match,
2330 args[3].AsString(), stored_value, mode);
2331 } else {
2332 // 3-arg simple form matched by "ss.[mode]i":
2333 // SetFilterProp(filter, prop_key, value [, mode])
2334 const int mode = args[3].AsInt(AVSPropAppendMode::PROPAPPENDMODE_REPLACE);
2335 AVSValue val = args[2];
2336 if (val.Defined() && !val.IsInt() && !val.IsFloat() && !val.IsString() && !val.IsBool() && !val.IsFunction())
2337 env->ThrowError("SetFilterProp: property value must be int, float, bool, string, function, or undefined()");
2338 // Frame properties have no bool type: convert bool to int 0/1
2339 if (val.IsBool())
2340 val = AVSValue(val.AsBool() ? 1 : 0);
2341 envi->SetFilterProp(args[0].AsString(), args[1].AsString(), val, mode);
2342 }
2343 return AVSValue();
2344 }
2345
2346 AVSValue GetFilterProps(AVSValue args, void*, IScriptEnvironment* env)
2347 {
2348 InternalEnvironment* envi = static_cast<InternalEnvironment*>(env);
2349 return AVSValue(envi->GetFilterProps());
2350 }
2351
2352 AVSValue SetFilterPropPassthrough(AVSValue args, void*, IScriptEnvironment* env)
2353 {
2354 InternalEnvironment* envi = static_cast<InternalEnvironment*>(env);
2355 envi->SetFilterPropPassthrough(args[0].AsString());
2356 return AVSValue();
2357 }
2358
2359 // Neo style
2360 AVSValue SetMemoryMax(AVSValue args, void*, IScriptEnvironment* env)
2361 {
2362 int memMax = args[0].AsInt(0);
2363 int deviceType = args[1].AsInt(0);
2364 int deviceIndex = args[2].AsInt(0);
2365
2366 if (deviceType == 0 || deviceType == DEV_TYPE_CPU) {
2367 return env->SetMemoryMax(memMax);
2368 }
2369
2370 InternalEnvironment *envI = static_cast<InternalEnvironment*>(env);
2371 return envI->SetMemoryMax((AvsDeviceType)deviceType, deviceIndex, memMax);
2372 }
2373
2374 AVSValue SetMaxCPU(AVSValue args, void*, IScriptEnvironment* env)
2375 {
2376 InternalEnvironment* envI = static_cast<InternalEnvironment*>(env);
2377 envI->SetMaxCPU(args[0].AsString());
2378 return AVSValue();
2379 }
2380
2381 AVSValue IsY(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsY(); }
2382 AVSValue Is420(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).Is420(); }
2383 AVSValue Is422(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).Is422(); }
2384 AVSValue Is444(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).Is444(); }
2385 AVSValue IsRGB48(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsRGB48(); }
2386 AVSValue IsRGB64(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsRGB64(); }
2387 AVSValue ComponentSize(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).ComponentSize(); }
2388 AVSValue BitsPerComponent(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).BitsPerComponent(); }
2389 AVSValue IsYUVA(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsYUVA(); }
2390 AVSValue IsPlanarRGB(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsPlanarRGB(); }
2391 AVSValue IsPlanarRGBA(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsPlanarRGBA(); }
2392 AVSValue NumComponents(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).NumComponents(); }
2393 AVSValue HasAlpha(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsPlanarRGBA() || VI(args[0]).IsYUVA() || VI(args[0]).IsRGB32() || VI(args[0]).IsRGB64(); }
2394 AVSValue IsPackedRGB(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).IsRGB24() || VI(args[0]).IsRGB32() || VI(args[0]).IsRGB48() || VI(args[0]).IsRGB64(); }
2395 AVSValue IsVideoFloat(AVSValue args, void*, IScriptEnvironment*) { return VI(args[0]).BitsPerComponent() == 32; }
2396
2397 // helper for GetProcessInfo
2398 static int ProcessType() {
2399 #define PROCESS_UNKNOWN -1
2400 #define PROCESS_32_ON_32 0
2401 #define PROCESS_32_ON_64 1
2402 #define PROCESS_64_ON_64 2
2403
2404 if constexpr(sizeof(void*) == 8)
2405 return PROCESS_64_ON_64;
2406 #ifdef AVS_WINDOWS
2407 else {
2408 // IsWow64Process is not available on all supported versions of Windows.
2409 // Use GetModuleHandle to get a handle to the DLL that contains the function
2410 // and GetProcAddress to get a pointer to the function if available.
2411
2412 BOOL bWoW64Process = FALSE;
2413 typedef bool(WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
2414 LPFN_ISWOW64PROCESS fnIsWow64Process;
2415 HMODULE hKernel32 = GetModuleHandle("kernel32.dll");
2416 if (hKernel32 == NULL)
2417 return PROCESS_UNKNOWN;
2418
2419 fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(hKernel32, "IsWow64Process");
2420 if (fnIsWow64Process != NULL)
2421 fnIsWow64Process(GetCurrentProcess(), &bWoW64Process);
2422 else
2423 return PROCESS_UNKNOWN;
2424
2425 if (bWoW64Process)
2426 return PROCESS_32_ON_64; //WoW64
2427
2428 return PROCESS_32_ON_32;
2429 }
2430 #endif
2431 }
2432
2433 AVSValue GetProcessInfo(AVSValue args, void*, IScriptEnvironment* env)
2434 {
2435 int infoType = args[0].AsInt(0);
2436 if (infoType < 0 || infoType > 1)
2437 env->ThrowError("GetProcessInfo: type must be 0 or 1");
2438 if (infoType == 0) {
2439 return sizeof(void *) == 8 ? 64 : 32;
2440 }
2441 // infoType == 1
2442 return ProcessType();
2443 }
2444
2445 #ifdef AVS_WINDOWS
2446 AVSValue StrToUtf8(AVSValue args, void*, IScriptEnvironment* env) {
2447 const char *source = args[0].AsString();
2448 // in two steps: Ansi -> WideChar -> Utf8
2449 auto wsource = AnsiToWideCharACP(source);
2450 // wide -> utf8
2451 auto source_utf8 = WideCharToUtf8(wsource.get());
2452 AVSValue ret = env->SaveString(source_utf8.get());
2453 return ret;
2454 }
2455
2456 AVSValue StrFromUtf8(AVSValue args, void*, IScriptEnvironment* env) {
2457 const char *source_utf8 = args[0].AsString();
2458 // in two steps: Utf8 -> WideChar -> Ansi
2459 auto wsource = Utf8ToWideChar(source_utf8);
2460 // wide -> ansi
2461 auto source_ansi = WideCharToAnsiACP(wsource.get());
2462 AVSValue ret = env->SaveString(source_ansi.get());
2463 return ret;
2464 }
2465 #endif
2466
2467
2468 AVSValue IsFloatUvZeroBased(AVSValue args, void*, IScriptEnvironment*)
2469 {
2470 #ifdef FLOAT_CHROMA_IS_HALF_CENTERED
2471 return false;
2472 #else
2473 return true;
2474 #endif
2475 }
2476
2477 AVSValue BuildPixelType(AVSValue args, void*, IScriptEnvironment* env)
2478 {
2479 // { "BuildPixelType", BUILTIN_FUNC_PREFIX, "[family]s[bits]i[chroma]i[compat]b[oldnames]b[sample_clip]c", BuildPixelType }, // 180517-
2480 // family: YUV, YUVA, RGB, RGBA, Y
2481 // bits: 8, 10, 12, 14, 16, 32
2482 // chroma: for YUV(A) 420,422,444,411. Ignored for RGB(A) and Y
2483 // compat (default false): returns packed rgb formats for 8/16 bits (RGB default: planar RGB)
2484 // oldnames (default false): returns YV12/YV16/YV24 instead of YUV420P8/YUV422P8/YUV444P8
2485 // sample_clip: when supported, its format is overridden by specified parameters (e.g. only change bits=10)
2486
2487 const bool hasTemplate = args[5].Defined();
2488
2489 if (!args[0].Defined() && !hasTemplate)
2490 env->ThrowError("BuildPixelType error: no color space 'family' or template 'sample_clip' specified");
2491 if (!args[1].Defined() && !hasTemplate)
2492 env->ThrowError("BuildPixelType error: no 'bits' or template 'sample_clip' specified");
2493
2494 std::string family;
2495 if (!args[0].Defined() && hasTemplate) {
2496 // no family parameter: use template
2497 VideoInfo const &vi = args[5].AsClip()->GetVideoInfo();
2498 if (vi.IsY())
2499 family = "Y";
2500 else if (vi.IsPlanar()) {
2501 if (vi.IsYUV())
2502 family = "YUV";
2503 else if (vi.IsYUVA())
2504 family = "YUVA";
2505 else if (vi.IsPlanarRGB())
2506 family = "RGB";
2507 else if (vi.IsPlanarRGBA())
2508 family = "RGBA";
2509 else
2510 env->ThrowError("BuildPixelType error: invalid sample_clip format");
2511 }
2512 else if (vi.IsRGB24() || vi.IsRGB48())
2513 family = "RGB";
2514 else if (vi.IsRGB32() || vi.IsRGB64())
2515 family = "RGBA";
2516 else
2517 env->ThrowError("BuildPixelType error: invalid sample_clip format");
2518 }
2519 else {
2520 family = args[0].AsString();
2521 for (auto & c : family) c = toupper(c); // uppercase input string
2522 }
2523
2524 const bool isYUV = family == "YUV";
2525 const bool isYUVA = family == "YUVA";
2526 const bool isRGB = family == "RGB";
2527 const bool isRGBA = family == "RGBA";
2528 const bool isY = family == "Y";
2529
2530 if(!isYUV && !isYUVA && !isRGB && !isRGBA && !isY)
2531 env->ThrowError("BuildPixelType error: wrong 'family'.", family.c_str());
2532
2533 int bits;
2534 if (!args[1].Defined() && hasTemplate) {
2535 // no bits parameter: get it from template sample_clip
2536 bits = args[5].AsClip()->GetVideoInfo().BitsPerComponent();
2537 } else {
2538 bits = args[1].AsInt();
2539 }
2540
2541 if (bits != 8 && bits != 10 && bits != 12 && bits != 14 && bits != 16 && bits != 32)
2542 env->ThrowError("BuildPixelType error: 'bits'=%d is not valid.", bits);
2543
2544 int chroma;
2545
2546 if (isYUV || isYUVA) {
2547 if (!args[2].Defined() && hasTemplate) {
2548 // no chroma parameter: subsampling from template clip
2549 VideoInfo const &vi = args[5].AsClip()->GetVideoInfo();
2550 const int hs = vi.GetPlaneWidthSubsampling(PLANAR_U);
2551 const int vs = vi.GetPlaneHeightSubsampling(PLANAR_U);
2552 if (hs == 0 && vs == 0) chroma = 444;
2553 else if (hs == 1 && vs == 0) chroma = 422;
2554 else if (hs == 1 && vs == 1) chroma = 420;
2555 else if (hs == 2 && vs == 0) chroma = 411;
2556 else
2557 env->ThrowError("BuildPixelType error: sample_clip has invalid chroma subsampling.");
2558 }
2559 else {
2560 chroma = args[2].AsInt(444);
2561 }
2562 }
2563 else {
2564 chroma = 444; // n/a
2565 }
2566
2567 if(chroma != 444 && chroma != 422 && chroma != 420 && chroma != 411)
2568 env->ThrowError("BuildPixelType error: 'chroma' must be 444, 422, 420 or 411.");
2569
2570 // packed RGB compatibility formats only for RGB(A)
2571 const bool compat = isRGB || isRGBA ? args[3].AsBool(false) : false;
2572
2573 // e.g. return YV12 instead of YUV420P8
2574 const bool oldNames = args[4].AsBool(false);
2575
2576 if(compat && bits != 8 && bits != 16)
2577 env->ThrowError("BuildPixelType error: 'compat'=true requires bits=8 or 16 for RGB(A).");
2578
2579 if(chroma == 411 && bits != 8)
2580 env->ThrowError("BuildPixelType error: 411 is supported only for 8 bits.");
2581
2582 if (compat) {
2583 if (isRGB && bits == 8)
2584 return "RGB24";
2585 if (isRGB && bits == 16)
2586 return "RGB48";
2587 if (isRGBA && bits == 8)
2588 return "RGB32";
2589 return "RGB64"; // RGBA, bits==16
2590 }
2591
2592 std::string format;
2593
2594 if (isYUV || isYUVA || isY)
2595 format = family;
2596 else if (isRGB)
2597 format = "RGBP";
2598 else if (isRGBA)
2599 format = "RGBAP";
2600
2601 if (isYUV || isYUVA) {
2602 if (chroma == 444)
2603 format += "444";
2604 else if(chroma == 422)
2605 format += "422";
2606 else if (chroma == 420)
2607 format += "420";
2608 else if (chroma == 411)
2609 format += "411";
2610
2611 format = format + "P";
2612 }
2613
2614 if (bits == 32)
2615 format += (isY ? "32" : "S"); // no "YS", only "Y32"
2616 else
2617 format = format + std::to_string(bits);
2618
2619 if (oldNames) {
2620 if (format == "YUV420" || format == "YUV420P8") format = "YV12";
2621 else if (format == "YUV422" || format == "YUV422P8") format = "YV16";
2622 else if (format == "YUV444" || format == "YUV444P8") format = "YV24";
2623 }
2624
2625 // 411 has no alternative naming
2626 if (format == "YUV411") format = "YV411";
2627
2628 return env->SaveString(format.c_str());
2629 }
2630
2631 AVSValue VarExist(AVSValue args, void*, IScriptEnvironment* env)
2632 {
2633 const char *name = args[0].AsString();
2634 int len = (int)strlen(name);
2635
2636 bool validName = true;
2637 // check for a valid identifier name
2638 if (*name != '_' && !isalpha(*name))
2639 validName = false;
2640 else {
2641 for (int i = 1; i < len; i++) {
2642 const char ch = name[i];
2643 if (!(ch == '_' || isalnum(ch))) {
2644 validName = false;
2645 break;
2646 }
2647 }
2648 }
2649
2650 if (!validName)
2651 env->ThrowError("VarExist: invalid variable name");
2652
2653 AVSValue result;
2654 return (env->GetVarTry(name, &result)); // true if exists
2655 }
2656
2657
2658 AVSValue ArrayCreate(AVSValue args, void*, IScriptEnvironment* env)
2659 {
2660 return args[0];
2661 }
2662
2663 AVSValue IsArray(AVSValue args, void*, IScriptEnvironment* env) { return args[0].IsArray(); }
2664
2665 AVSValue ArrayGet(AVSValue args, void*, IScriptEnvironment* env)
2666 {
2667 // signature .i+
2668 // parameters: [0] array to index; [1] one or more integer indexes or a string
2669 if (!args[0].IsArray())
2670 env->ThrowError("ArrayGet: array type required.");
2671 const int size = args[0].ArraySize();
2672 if (args[1].IsString()) {
2673 // associative search
2674 // linear search and case insensitive key match
2675 // { {"a", element1}, { "b", element2 }, etc..}
2676 const char* tag = args[1].AsString();
2677 for (int i = 0; i < size; i++)
2678 {
2679 AVSValue currentTagValue = args[0][i]; // two elements e.g. { "b", element2 }
2680 if (!currentTagValue.IsArray())
2681 env->ThrowError("ArrayGet: Array must contain array[string, any] elements for dictionary lookup");
2682 if (currentTagValue.ArraySize() < 2)
2683 env->ThrowError("ArrayGet: Internal array must have at least two elements (tag, value)");
2684 AVSValue currentTag = currentTagValue[0];
2685 if (currentTag.IsString() && !lstrcmpi(currentTag.AsString(), tag))
2686 {
2687 return currentTagValue[1];
2688 }
2689 }
2690 return AVSValue(); // undefined if not found
2691 }
2692 else if (args[1].IsArray()) {
2693 // array, even is only a single index is used
2694 AVSValue indexes = args[1];
2695 AVSValue currentValue = args[0];
2696 int index_count = indexes.ArraySize(); // array of parameters. a[1,2] -> [1,2]
2697 if (index_count == 0)
2698 env->ThrowError("ArrayGet: no index specified");
2699 for (int i = 0; i < index_count; i++)
2700 {
2701 if (!currentValue.IsArray())
2702 env->ThrowError("ArrayGet: not an array. Index=%d", i);
2703 int currentIndex = indexes[i].AsInt();
2704 if (currentIndex < 0 || currentIndex >= currentValue.ArraySize())
2705 env->ThrowError("ArrayGet: Array index out of range. Problematic index count: %d", i + 1);
2706 currentValue = currentValue[currentIndex];
2707 }
2708 return currentValue;
2709 }
2710 env->ThrowError("ArrayGet: Invalid array index, must be integer or string, or comma separated integers");
2711 // unreachable, but to avoid compiler warning
2712 return AVSValue(); // undefined
2713 }
2714
2715 AVSValue ArrayIndexOf(AVSValue args, void*, IScriptEnvironment* env)
2716 {
2717 // signature .s
2718 // parameters: [0] dictionary style array to search; [1] key (case insensitive)
2719 // { {"a", element1}, { "b", element2 }, etc..}
2720 if (!args[0].IsArray())
2721 env->ThrowError("ArrayIndexOf: array type required.");
2722 const int size = args[0].ArraySize();
2723 const char* tag = args[1].AsString();
2724 for (int i = 0; i < size; i++)
2725 {
2726 AVSValue currentTagValue = args[0][i]; // must be a key-value pair with two elements e.g. { "b", element2 }
2727 if (!currentTagValue.IsArray())
2728 env->ThrowError("ArrayIndexOf: Array must contain array[string, any] elements for dictionary lookup");
2729 if (currentTagValue.ArraySize() < 2)
2730 env->ThrowError("ArrayIndexOf: Internal array must have at least two elements (tag, value)");
2731 AVSValue currentTag = currentTagValue[0];
2732 if (currentTag.IsString() && !lstrcmpi(currentTag.AsString(), tag))
2733 return i;
2734 }
2735 return -1; // not found
2736 }
2737
2738 AVSValue ArraySize(AVSValue args, void*, IScriptEnvironment* env)
2739 {
2740 // func signature: "."
2741 if (!args[0].IsArray())
2742 env->ThrowError("ArraySize: parameter must be an array");
2743 return args[0].ArraySize();
2744 }
2745
2746 AVSValue ArrayIns(AVSValue args, void* user_data, IScriptEnvironment* env)
2747 {
2748 int mode = (int)(intptr_t)user_data;
2749 enum ArrayMode {
2750 INSERT = 0,
2751 APPEND = 1,
2752 REPLACE = 2,
2753 DEL = 3
2754 };
2755 // signature .. and ..i
2756 // parameters:
2757 // [0] array to modify;
2758 // [1] element to insert (ArrayAdd, ArrayIns and ArraySet) [2] inserting index(es) (ArrayIns, ArraySet)
2759 // or [1] delete index(es) ArrayDel
2760
2761 const char* funcname = mode == DEL ? "ArrayDel" : mode == REPLACE ? "ArraySet" : mode == APPEND ? "ArrayAdd" : "ArrayIns";
2762
2763 if (!args[0].IsArray())
2764 env->ThrowError("%s error: array type required.", funcname);
2765
2766 const auto orig_size = args[0].ArraySize();
2767
2768 const int index_param_pos = mode == DEL ? 1 : 2;
2769 AVSValue indexes = args[index_param_pos];
2770 int index_count = indexes.ArraySize(); // array of parameters. a[1,2] -> [1,2]
2771
2772 if (mode == INSERT || mode == REPLACE || mode == DEL) {
2773 if (index_count == 0)
2774 env->ThrowError("%s: no index specified", funcname);
2775 }
2776
2777 const int new_size =
2778 mode == DEL && index_count == 1 ? orig_size - 1 :
2779 mode == APPEND && index_count == 0 ? orig_size + 1 :
2780 mode == INSERT && index_count == 1 ? orig_size + 1 :
2781 orig_size; // replace and recursive other cases
2782
2783 std::vector<AVSValue> new_val(new_size);
2784
2785 int action_pos;
2786 if (mode == APPEND)
2787 action_pos = orig_size; // at the end
2788 else {
2789 action_pos = indexes[0].AsInt();
2790 const int max_valid_pos = (mode == INSERT && index_count == 1) ? orig_size : orig_size - 1;
2791 if (action_pos < 0 || action_pos > max_valid_pos)
2792 env->ThrowError("%s: index %d out of range (array size is %d)", funcname, action_pos, orig_size);
2793 }
2794
2795 // copy before insertion/replace point
2796 for (int i = 0; i < action_pos; i++)
2797 new_val[i] = args[0][i]; // avs+: automatic deep copy
2798
2799 if (
2800 ((mode == REPLACE || mode == INSERT || mode == DEL) && index_count > 1) ||
2801 ((mode == APPEND) && index_count >= 1))
2802 {
2803 int current_index = indexes[0].AsInt();
2804 if (current_index < 0 || current_index >= orig_size)
2805 env->ThrowError("%s: index %d out of range (array size is %d)", funcname, current_index, orig_size);
2806 // for multi-level array recursion is needed because there is no exact reference to an inner element
2807 if (mode == DEL) {
2808 AVSValue params[2] = { args[0][current_index], index_count <= 1 ? AVSValue(nullptr, 0) : AVSValue(&indexes[1], index_count - 1) };
2809 new_val[current_index] = env->Invoke(funcname, AVSValue(params, 2)); // recursively
2810 }
2811 else {
2812 AVSValue params[3] = { args[0][current_index], args[1], index_count <= 1 ? AVSValue(nullptr, 0) : AVSValue(&indexes[1], index_count - 1) };
2813 new_val[current_index] = env->Invoke(funcname, AVSValue(params, 3)); // recursively
2814 }
2815 mode = REPLACE;
2816 }
2817 else if (mode != DEL) {
2818 new_val[action_pos] = args[1];
2819 }
2820
2821 // copy from after insertion/replace/delete point
2822 if (mode == DEL) {
2823 for (int i = action_pos + 1; i < orig_size; i++)
2824 new_val[i - 1] = args[0][i]; // avs+: automatic deep copy
2825 }
2826 else if (mode == REPLACE) {
2827 for (int i = action_pos+1; i < orig_size; i++)
2828 new_val[i] = args[0][i]; // avs+: automatic deep copy
2829 }
2830 else {
2831 for (int i = action_pos; i < orig_size; i++)
2832 new_val[i + 1] = args[0][i]; // avs+: automatic deep copy
2833 }
2834
2835 if(new_size == 0)
2836 return AVSValue(nullptr, 0); // zero array
2837
2838 return AVSValue(new_val.data(), new_size);
2839 }
2840
2841 AVSValue ArraySetByKey(AVSValue args, void* user_data, IScriptEnvironment* env)
2842 {
2843 int mode = (int)(intptr_t)user_data;
2844 enum ArrayMode {
2845 INSERT_OR_APPEND = 0,
2846 DEL = 1
2847 };
2848 // INSERT_OR_APPEND:
2849 // signature ..s
2850 // parameters: [0] dictionary style array to modify; [1] value; [2] key
2851 // Replaces the value at the matching key, or appends a new [key, value] pair if the key is not found.
2852
2853 // DEL:
2854 // signature .s
2855 // parameters: [0] dictionary style array to delete from; [1] key
2856 // Deletes the key-value pair if the key is found. No-op otherwise.
2857
2858 const char* funcname = mode == DEL ? "ArrayDel" : "ArraySet";
2859
2860 // Array of key-value pairs, e.g.
2861 // { {"a", element1}, { "b", element2 }, etc..}
2862 if (!args[0].IsArray())
2863 env->ThrowError("%s: array type required.", funcname);
2864 const int size = args[0].ArraySize();
2865 const char* tag = mode == DEL ? args[1].AsString() : args[2].AsString();
2866 int found = -1;
2867 // first a linear search for the key (case insensitive) to find the index of the matching pair
2868 for (int i = 0; i < size; i++)
2869 {
2870 AVSValue currentTagValue = args[0][i]; // two elements e.g. { "b", element2 }
2871 if (!currentTagValue.IsArray())
2872 env->ThrowError("%s: Array must contain array[string, any] elements for dictionary lookup", funcname);
2873 if (currentTagValue.ArraySize() < 2)
2874 env->ThrowError("%s: Internal array must have at least two elements (tag, value)", funcname);
2875 AVSValue currentTag = currentTagValue[0];
2876 if (currentTag.IsString() && !lstrcmpi(currentTag.AsString(), tag))
2877 {
2878 found = i;
2879 break;
2880 }
2881 }
2882
2883 // DEL
2884 if (mode == DEL) {
2885 if (found < 0)
2886 return args[0]; // key not found: no-op, return the original array untouched
2887
2888 const int new_size = size - 1;
2889 if (new_size == 0)
2890 return AVSValue(nullptr, 0); // delete the only element, resulting in a zero array
2891
2892 std::vector<AVSValue> new_val(new_size);
2893 // copy before the deleted pair
2894 for (int i = 0; i < found; i++)
2895 new_val[i] = args[0][i]; // avs+: automatic deep copy
2896 // copy after the deleted pair
2897 for (int i = found + 1; i < size; i++)
2898 new_val[i - 1] = args[0][i];
2899
2900 return AVSValue(new_val.data(), new_size); // create AVSValue from array and return
2901 }
2902
2903 // INSERT_OR_APPEND
2904 // Have to copy the original array even when replacing an existing value,
2905 // we cannot modify the original array in place.
2906 const int new_size = (found >= 0) ? size : size + 1;
2907 std::vector<AVSValue> new_val(new_size);
2908 for (int i = 0; i < size; i++)
2909 new_val[i] = args[0][i]; // avs+: automatic deep copy
2910
2911 AVSValue pair_elems[2] = { AVSValue(tag), args[1] };
2912
2913 const int action_pos = found >= 0 ? found : size; // replace or append
2914
2915 new_val[action_pos] = AVSValue(pair_elems, 2);
2916
2917 return AVSValue(new_val.data(), new_size); // create AVSValue from array and return
2918 }
2919
2920 // Custom comparator functions for sorting
2921 bool customCompareBool(const std::pair<const AVSValue*, int>& a, const std::pair<const AVSValue*, int>& b) {
2922 return (a.first)->AsBool() < (b.first)->AsBool();
2923 }
2924
2925 // v11: 64 bit content as well
2926 bool customCompareInt(const std::pair<const AVSValue *, int>& a, const std::pair<const AVSValue *, int>& b) {
2927 return (a.first)->AsLong() < (b.first)->AsLong(); // v11: AsLong instead of AsInt
2928 }
2929
2930 // v11: 64 bit content as well
2931 bool customCompareFloat(const std::pair<const AVSValue*, int>& a, const std::pair<const AVSValue*, int>& b) {
2932 return (a.first)->AsFloat() < (b.first)->AsFloat(); // v11: AsFloat instead of AsFloatf
2933 }
2934
2935 bool customCompareString(const std::pair<const AVSValue*, int>& a, const std::pair<const AVSValue*, int>& b) {
2936 return std::strcmp((a.first)->AsString(), (b.first)->AsString()) < 0;
2937 }
2938
2939 AVSValue ArraySort(AVSValue args, void* user_data, IScriptEnvironment* env)
2940 {
2941 // [0] array to sort;
2942
2943 if (!args[0].IsArray())
2944 env->ThrowError("ArraySort error: array type required.");
2945
2946 const auto size = args[0].ArraySize();
2947
2948 if (size == 0)
2949 return AVSValue(nullptr, 0); // zero array
2950
2951 std::vector<std::pair<const AVSValue*, int>> indexedArr(size);
2952
2953 // Create a pair of (element reference, index) with type checks
2954 // Note: integers and float can be mixed, sort by the broadest type
2955 AvsValueType finalType = (args[0][0]).GetType();
2956 for (int i = 0; i < size; ++i) {
2957 indexedArr[i] = { &args[0][i], i };
2958 AvsValueType currentType = indexedArr[i].first->GetType();
2959 if (finalType == AvsValueType::VALUE_TYPE_INT && currentType == AvsValueType::VALUE_TYPE_LONG)
2960 {
2961 // promote int to long; note: since v11: long (int64) exists
2962 finalType = currentType;
2963 }
2964 else if (finalType == AvsValueType::VALUE_TYPE_FLOAT && currentType == AvsValueType::VALUE_TYPE_DOUBLE)
2965 {
2966 // promote float to double; note: since v11: double exists
2967 finalType = currentType;
2968 }
2969 else if ((finalType == AvsValueType::VALUE_TYPE_INT || finalType == AvsValueType::VALUE_TYPE_LONG) &&
2970 (currentType == AvsValueType::VALUE_TYPE_FLOAT || currentType == AvsValueType::VALUE_TYPE_DOUBLE)) {
2971 // promote int-like to float-like; note: since v11: int64/double exists
2972 finalType = currentType;
2973 }
2974 // cannot mix bools, ints and strings
2975 if (finalType == AvsValueType::VALUE_TYPE_STRING) {
2976 if (currentType != AvsValueType::VALUE_TYPE_STRING)
2977 env->ThrowError("ArraySort: array contains different basic types, string expected.");
2978 }
2979 else if (finalType == AvsValueType::VALUE_TYPE_BOOL) {
2980 if (currentType != AvsValueType::VALUE_TYPE_BOOL)
2981 env->ThrowError("ArraySort: array contains different basic types, bool expected.");
2982 }
2983 else {
2984 // int-like or float-like
2985 if (currentType != AvsValueType::VALUE_TYPE_INT &&
2986 currentType != AvsValueType::VALUE_TYPE_LONG &&
2987 currentType != AvsValueType::VALUE_TYPE_FLOAT &&
2988 currentType != AvsValueType::VALUE_TYPE_DOUBLE)
2989 env->ThrowError("ArraySort: array contains different basic types, number expected.");
2990 }
2991 }
2992
2993 switch(finalType){
2994 case AvsValueType::VALUE_TYPE_BOOL: std::sort(indexedArr.begin(), indexedArr.end(), customCompareBool); break;
2995 case AvsValueType::VALUE_TYPE_INT:
2996 case AvsValueType::VALUE_TYPE_LONG:
2997 std::sort(indexedArr.begin(), indexedArr.end(), customCompareInt); break;
2998 case AvsValueType::VALUE_TYPE_FLOAT:
2999 case AvsValueType::VALUE_TYPE_DOUBLE:
3000 std::sort(indexedArr.begin(), indexedArr.end(), customCompareFloat); break;
3001 case AvsValueType::VALUE_TYPE_STRING: std::sort(indexedArr.begin(), indexedArr.end(), customCompareString); break;
3002 default:
3003 env->ThrowError("ArraySort: unsupported data type");
3004 }
3005
3006 // copy the results once
3007 std::vector<AVSValue> new_val(size);
3008 for (int i = 0; i < size; ++i) {
3009 new_val[i] = *indexedArr[i].first;
3010 }
3011
3012 return AVSValue(new_val.data(), size);
3013 }
3014
3015