1 /*
2 * Copyright (c) 2012-2017 Fredrik Mellbin
3 *
4 * This file is part of VapourSynth.
5 *
6 * VapourSynth is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * VapourSynth is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with VapourSynth; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 
21 #include "../core/x86utils.h"
22 #include "../core/cpufeatures.h"
23 #include "avisynth_compat.h"
24 #include <algorithm>
25 #include <cstdarg>
26 #include <limits>
27 #include "../common/vsutf16.h"
28 
29 #define NOMINMAX
30 #include <Windows.h>
31 
32 extern const AVS_Linkage* const AVS_linkage;
33 
34 namespace {
operator ""_s(const char * str,size_t len)35 std::string operator""_s(const char *str, size_t len) { return{ str, len }; }
36 } // namespace
37 
38 
39 namespace AvisynthCompat {
40 
VSFormatToAVSPixelType(const VSFormat * fi)41 static int VSFormatToAVSPixelType(const VSFormat *fi) {
42     if (fi->id == pfCompatBGR32)
43         return VideoInfo::CS_BGR32;
44     else if (fi->id == pfCompatYUY2)
45         return VideoInfo::CS_YUY2;
46     else if (fi->id == pfYUV444P8)
47         return VideoInfo::CS_YV24;
48     else if (fi->id == pfYUV422P8)
49         return VideoInfo::CS_YV16;
50     else if (fi->id == pfYUV420P8)
51         return VideoInfo::CS_YV12;
52     else if (fi->id == pfYUV410P8)
53         return VideoInfo::CS_YUV9;
54     else if (fi->id == pfYUV411P8)
55         return VideoInfo::CS_YV411;
56     else if (fi->id == pfGray8)
57         return VideoInfo::CS_Y8;
58     else if (fi->id == pfYUV444P10)
59         return VideoInfo::CS_YUV444P10;
60     else if (fi->id == pfYUV422P10)
61         return VideoInfo::CS_YUV422P10;
62     else if (fi->id == pfYUV420P10)
63         return VideoInfo::CS_YUV420P10;
64     else if (fi->bitsPerSample == 10 && fi->colorFamily == cmGray && fi->sampleType == stInteger)
65         return VideoInfo::CS_Y10;
66     else if (fi->id == pfYUV444P12)
67         return VideoInfo::CS_YUV444P12;
68     else if (fi->id == pfYUV422P12)
69         return VideoInfo::CS_YUV422P12;
70     else if (fi->id == pfYUV420P12)
71         return VideoInfo::CS_YUV420P12;
72     else if (fi->bitsPerSample == 12 && fi->colorFamily == cmGray && fi->sampleType == stInteger)
73         return VideoInfo::CS_Y12;
74     else if (fi->id == pfYUV444P14)
75         return VideoInfo::CS_YUV444P14;
76     else if (fi->id == pfYUV422P14)
77         return VideoInfo::CS_YUV422P14;
78     else if (fi->id == pfYUV420P14)
79         return VideoInfo::CS_YUV420P14;
80     else if (fi->bitsPerSample == 14 && fi->colorFamily == cmGray && fi->sampleType == stInteger)
81         return VideoInfo::CS_Y14;
82     else if (fi->id == pfYUV444P16)
83         return VideoInfo::CS_YUV444P16;
84     else if (fi->id == pfYUV422P16)
85         return VideoInfo::CS_YUV422P16;
86     else if (fi->id == pfYUV420P16)
87         return VideoInfo::CS_YUV420P16;
88     else if (fi->id == pfGray16)
89         return VideoInfo::CS_Y16;
90     else if (fi->id == pfYUV444PS)
91         return VideoInfo::CS_YUV444PS;
92     else if (fi->bitsPerSample == 32 && fi->colorFamily == cmYUV && fi->sampleType == stFloat && fi->subSamplingH == 0 && fi->subSamplingW == 1)
93         return VideoInfo::CS_YUV422PS;
94     else if (fi->bitsPerSample == 32 && fi->colorFamily == cmYUV && fi->sampleType == stFloat && fi->subSamplingH == 1 && fi->subSamplingW == 1)
95         return VideoInfo::CS_YUV420PS;
96     else if (fi->id == pfGrayS)
97         return VideoInfo::CS_Y32;
98     else if (fi->id == pfRGB24)
99         return VideoInfo::CS_RGBP;
100     else if (fi->id == pfRGB30)
101         return VideoInfo::CS_RGBP10;
102     else if (fi->bitsPerSample == 12 && fi->colorFamily == cmRGB)
103         return VideoInfo::CS_RGBP12;
104     else if (fi->bitsPerSample == 14 && fi->colorFamily == cmRGB)
105         return VideoInfo::CS_RGBP14;
106     else if (fi->id == pfRGB48)
107         return VideoInfo::CS_RGBP16;
108     else if (fi->id == pfGrayS)
109         return VideoInfo::CS_Y32;
110     else
111         return 0;
112 }
113 
AVSPixelTypeToVSFormat(const VideoInfo & vi,VSCore * core,const VSAPI * vsapi)114 static const VSFormat *AVSPixelTypeToVSFormat(const VideoInfo &vi, VSCore *core, const VSAPI *vsapi) {
115     if (vi.IsYUY2())
116         return vsapi->getFormatPreset(pfCompatYUY2, core);
117     else if (vi.IsRGB32())
118         return vsapi->getFormatPreset(pfCompatBGR32, core);
119 
120     if (vi.IsPlanar()) {
121         bool hasSubSampling = vi.IsYUV();
122         int colorspace = vi.IsYUV() ? cmYUV : (vi.IsRGB() ? cmRGB : (vi.IsY() ? cmGray : 0));
123         if (colorspace)
124             return vsapi->registerFormat(colorspace, vi.BitsPerComponent() == 32 ? stFloat : stInteger, vi.BitsPerComponent(), hasSubSampling ? vi.GetPlaneWidthSubsampling(PLANAR_U) : 0, hasSubSampling ? vi.GetPlaneHeightSubsampling(PLANAR_U) : 0, core);
125     }
126 
127     return nullptr;
128 }
129 
avsToVSFrame(VideoFrame * frame)130 const VSFrameRef *FakeAvisynth::avsToVSFrame(VideoFrame *frame) {
131     const VSFrameRef *ref = nullptr;
132     std::map<VideoFrame *, const VSFrameRef *>::iterator it = ownedFrames.find(frame);
133 
134     if (it != ownedFrames.end()) {
135         ref = vsapi->cloneFrameRef(it->second);
136     } else {
137         vsapi->logMessage(mtFatal, "unreachable condition");
138         assert(false);
139     }
140 
141     it = ownedFrames.begin();
142 
143     while (it != ownedFrames.end()) {
144         if (it->first->refcount == 0 || it->first->refcount == 9000) {
145             delete it->first;
146             vsapi->freeFrame(it->second);
147             it = ownedFrames.erase(it);
148         } else {
149             ++it;
150         }
151     }
152 
153     assert(ref);
154     return ref;
155 }
156 
~FakeAvisynth()157 FakeAvisynth::~FakeAvisynth() {
158     std::map<VideoFrame *, const VSFrameRef *>::iterator it = ownedFrames.begin();
159 
160     while (it != ownedFrames.end()) {
161         delete it->first;
162         vsapi->freeFrame(it->second);
163         it = ownedFrames.erase(it);
164     }
165 
166     ownedFrames.clear();
167 }
168 
GetCPUFlags()169 int FakeAvisynth::GetCPUFlags() {
170     const CPUFeatures *cpuf = getCPUFeatures();
171     int flags = CPUF_FPU | CPUF_MMX | CPUF_INTEGER_SSE | CPUF_SSE | CPUF_SSE2; // minimum to run VS
172     if (cpuf->sse3)      flags |= CPUF_SSE3;
173     if (cpuf->ssse3)     flags |= CPUF_SSSE3;
174     if (cpuf->sse4_1)    flags |= CPUF_SSE4_1;
175     if (cpuf->sse4_2)    flags |= CPUF_SSE4_2;
176     if (cpuf->avx)       flags |= CPUF_AVX;
177     if (cpuf->avx2)      flags |= CPUF_AVX2;
178     if (cpuf->fma3)      flags |= CPUF_FMA3;
179     if (cpuf->f16c)      flags |= CPUF_F16C;
180     if (cpuf->avx512_f)  flags |= CPUF_AVX512F;
181     if (cpuf->avx512_bw) flags |= CPUF_AVX512BW;
182     if (cpuf->avx512_dq) flags |= CPUF_AVX512DQ;
183     if (cpuf->avx512_cd) flags |= CPUF_AVX512CD;
184     if (cpuf->avx512_vl) flags |= CPUF_AVX512VL;
185     return flags;
186 }
187 
SaveString(const char * s,int length)188 char *FakeAvisynth::SaveString(const char *s, int length) {
189     auto strIter = (length >= 0) ? savedStrings.emplace(s, length) : savedStrings.emplace(s);
190     // UGLY
191     // Cast away the const because nobody would actually try to write to a saved string (except possibly an avisynth plugin developer)
192     return const_cast<char *>(strIter.first->c_str());
193 }
194 
Sprintf(const char * fmt,...)195 char *FakeAvisynth::Sprintf(const char *fmt, ...) {
196     va_list val;
197     va_start(val, fmt);
198     char *result = VSprintf(fmt, val);
199     va_end(val);
200     return result;
201 }
202 
VSprintf(const char * fmt,void * val)203 char *FakeAvisynth::VSprintf(const char *fmt, void *val) {
204     std::vector<char> buf;
205     int size = 0, count = -1;
206 
207     while (count == -1) {
208         buf.resize(buf.size() + 4096);
209         count = vsnprintf(buf.data(), size - 1, fmt, (va_list)val);
210     }
211 
212     char *i = SaveString(buf.data());
213     return i;
214 }
215 
ThrowError(const char * fmt,...)216 void FakeAvisynth::ThrowError(const char *fmt, ...) {
217     va_list val;
218     va_start(val, fmt);
219     char buf[8192];
220     _vsnprintf(buf, sizeof(buf) - 1, fmt, val);
221     va_end(val);
222     buf[sizeof(buf)-1] = '\0';
223     throw AvisynthError(SaveString(buf));
224 }
225 
charToFilterArgumentString(char c)226 std::string FakeAvisynth::charToFilterArgumentString(char c) {
227     switch (c) {
228     case 'i':
229     case 'b':
230         return "int";
231     case 'f':
232         return "float";
233     case 's':
234         return "data";
235     case 'c':
236         return "clip";
237     default:
238         vsapi->logMessage(mtFatal, "Avisynth Compat: invalid argument type character, I quit");
239         return "";
240     }
241 }
242 
VSClip(VSNodeRef * clip,FakeAvisynth * fakeEnv,const VSAPI * vsapi)243 VSClip::VSClip(VSNodeRef *clip, FakeAvisynth *fakeEnv, const VSAPI *vsapi)
244     : clip(clip), fakeEnv(fakeEnv), vsapi(vsapi), numSlowWarnings(0) {
245     const ::VSVideoInfo *srcVi = vsapi->getVideoInfo(clip);
246     vi = {};
247     vi.width = srcVi->width;
248     vi.height = srcVi->height;
249 
250     vi.pixel_type = VSFormatToAVSPixelType(srcVi->format);
251     if (!vi.pixel_type)
252         vsapi->logMessage(mtFatal, "Bad colorspace");
253 
254     vi.image_type = VideoInfo::IT_BFF;
255     vi.fps_numerator = int64ToIntS(srcVi->fpsNum);
256     vi.fps_denominator = int64ToIntS(srcVi->fpsDen);
257     vi.num_frames = srcVi->numFrames;
258     vi.sample_type = SAMPLE_INT16;
259 }
260 
GetFrame(int n,IScriptEnvironment * env)261 PVideoFrame VSClip::GetFrame(int n, IScriptEnvironment *env) {
262     const VSFrameRef *ref;
263     n = std::min(std::max(0, n), vi.num_frames - 1);
264 
265     if (fakeEnv->initializing)
266         ref = vsapi->getFrame(n, clip, nullptr, 0);
267     else
268         ref = vsapi->getFrameFilter(n, clip, fakeEnv->uglyCtx);
269 
270     std::vector<char> buf(1024);
271 
272     if (!ref) {
273         if (numSlowWarnings < 200) {
274             numSlowWarnings++;
275             std::string s = "Avisynth Compat: requested frame "_s + std::to_string(n) + " not prefetched, using slow method";
276             vsapi->logMessage(mtWarning, s.c_str());
277         }
278         ref = vsapi->getFrame(n, clip, buf.data(), static_cast<int>(buf.size()));
279     }
280 
281     if (!ref)
282         vsapi->logMessage(mtFatal, ("Avisynth Compat: error while getting input frame synchronously: "_s + buf.data()).c_str());
283 
284     bool isMultiplePlanes = (vi.pixel_type & VideoInfo::CS_PLANAR) && !(vi.pixel_type & VideoInfo::CS_INTERLEAVED);
285 
286     const uint8_t *firstPlanePtr = vsapi->getReadPtr(ref, 0);
287 
288     VideoFrame *vfb = new VideoFrame(
289         // the data will never be modified due to the writable protections embedded in this mess
290         (BYTE *)firstPlanePtr,
291         false,
292         0,
293         vsapi->getStride(ref, 0),
294         vsapi->getFrameWidth(ref, 0) * vsapi->getFrameFormat(ref)->bytesPerSample,
295         vsapi->getFrameHeight(ref, 0),
296         isMultiplePlanes ? vsapi->getReadPtr(ref, 1) - firstPlanePtr : 0,
297         isMultiplePlanes ? vsapi->getReadPtr(ref, 2) - firstPlanePtr : 0,
298         isMultiplePlanes ? vsapi->getStride(ref, 1) : 0,
299         vsapi->getFrameWidth(ref, 1) * vsapi->getFrameFormat(ref)->bytesPerSample,
300         vsapi->getFrameHeight(ref, 1));
301     PVideoFrame pvf(vfb);
302     fakeEnv->ownedFrames.insert(std::make_pair(vfb, ref));
303     return pvf;
304 }
305 
WrappedClip(const std::string & filterName,const PClip & clip,const std::vector<VSNodeRef * > & preFetchClips,const PrefetchInfo & prefetchInfo,FakeAvisynth * fakeEnv)306 WrappedClip::WrappedClip(const std::string &filterName, const PClip &clip, const std::vector<VSNodeRef *> &preFetchClips, const PrefetchInfo &prefetchInfo, FakeAvisynth *fakeEnv)
307     : filterName(filterName), prefetchInfo(prefetchInfo), preFetchClips(preFetchClips), clip(clip), fakeEnv(fakeEnv) {
308 }
309 
prefetchHelper(int n,VSNodeRef * node,const PrefetchInfo & p,VSFrameContext * frameCtx,const VSAPI * vsapi)310 static void prefetchHelper(int n, VSNodeRef *node, const PrefetchInfo &p, VSFrameContext *frameCtx, const VSAPI *vsapi) {
311     n /= p.div;
312     n *= p.mul;
313 
314     for (int i = n + p.from; i <= n + p.to; i++) {
315         if (i < 0)
316             continue;
317 
318         vsapi->requestFrameFilter(i, node, frameCtx);
319     }
320 }
321 
322 #define WARNING(fname, warning) if (name == #fname) vsapi->logMessage(mtWarning, "Avisynth Compat: "_s + #fname + " - " + #warning).c_str());
323 #define BROKEN(fname) if (name == #fname) vsapi->logMessage(mtWarning, ("Avisynth Compat: Invoking known broken function "_s + name).c_str());
324 #define OTHER(fname) if (name == #fname) return PrefetchInfo(1, 1, 0, 0);
325 #define SOURCE(fname) if (name == #fname) return PrefetchInfo(1, 1, 0, 0);
326 #define PREFETCHR0(fname) if (name == #fname) return PrefetchInfo(1, 1, 0, 0);
327 #define PREFETCHR1(fname) if (name == #fname) return PrefetchInfo(1, 1, -1, 1);
328 #define PREFETCHR2(fname) if (name == #fname) return PrefetchInfo(1, 1, -2, 2);
329 #define PREFETCHR3(fname) if (name == #fname) return PrefetchInfo(1, 1, -3, 3);
330 #define PREFETCH(fname, div, mul, from, to) if (name == #fname) return PrefetchInfo(div, mul, from, to);
331 
getPrefetchInfo(const std::string & name,const VSMap * in,const VSAPI * vsapi)332 static PrefetchInfo getPrefetchInfo(const std::string &name, const VSMap *in, const VSAPI *vsapi) {
333     int err;
334     int temp;
335     // FFMS2
336     OTHER(FFIndex)
337     SOURCE(FFVideoSource)
338     PREFETCHR0(SWScale)
339     OTHER(FFSetLogLevel)
340     OTHER(FFGetLogLevel)
341     OTHER(FFGetVersion)
342     // TNLMeans
343     temp = int64ToIntS(vsapi->propGetInt(in, "Az", 0, &err));
344     PREFETCH(TNLMeans, 1, 1, -temp, temp)
345     // yadif*
346     PREFETCHR1(Yadif)
347     PREFETCHR0(yadifmod)
348     // *EDI*
349     PREFETCHR0(eedi3)
350     PREFETCHR0(eedi3_rpow2)
351     PREFETCHR0(nnedi2)
352     PREFETCHR0(nnedi2_rpow2)
353     PREFETCHR0(nnedi3)
354     PREFETCHR0(nnedi3_rpow2)
355     // mixed Tritical
356     temp = int64ToIntS(vsapi->propGetInt(in, "mode", 0, &err));
357     switch(temp) {
358     case 0:
359     case -1:
360     case -2:
361         PREFETCHR2(TDeint); break;
362     case 2:
363         PREFETCHR3(TDeint); break;
364     case 1:
365         PREFETCH(TDeint, 2, 1, -2, 2); break;
366     }
367     BROKEN(ColorMatrix)
368     PREFETCHR1(Cnr2)
369     temp = int64ToIntS(vsapi->propGetInt(in, "tbsize", 0, &err));
370     PREFETCH(dfttest, 1, 1, -(temp / 2), temp / 2)
371 
372     // MPEG2DEC
373     SOURCE(MPEG2Source)
374     PREFETCHR0(LumaYV12)
375     PREFETCHR0(BlindPP)
376     PREFETCHR0(Deblock)
377 
378     // Meow
379     SOURCE(DGSource)
380     PREFETCHR0(DGDenoise)
381     PREFETCHR0(DGSharpen)
382     temp = int64ToIntS(vsapi->propGetInt(in, "mode", 0, &err));
383     PREFETCH(DGBob, (temp > 0) ? 2 : 1, 1, -2, 2) // close enough?
384     BROKEN(IsCombed)
385     PREFETCHR0(FieldDeinterlace)
386     PREFETCH(Telecide, 1, 1, -2, 10) // not good
387     PREFETCH(DGTelecide, 1, 1, -2, 10) // also not good
388     temp = int64ToIntS(vsapi->propGetInt(in, "cycle", 0, &err));
389     PREFETCH(DGDecimate, temp - 1, temp, -(temp + 3), temp + 3) // probably suboptimal
390     PREFETCH(Decimate, temp - 1, temp, -(temp + 3), temp + 3) // probably suboptimal too
391 
392     // Masktools2
393     PREFETCHR0(mt_edge)
394     PREFETCHR1(mt_motion)
395     PREFETCHR0(mt_expand)
396     PREFETCHR0(mt_inpand)
397     PREFETCHR0(mt_inflate)
398     PREFETCHR0(mt_lut)
399     PREFETCHR0(mt_lutxy)
400     PREFETCHR0(mt_lutf)
401     PREFETCHR0(mt_luts)
402     PREFETCHR0(mt_makediff)
403     PREFETCHR0(mt_adddiff)
404     PREFETCHR0(mt_average)
405     PREFETCHR0(mt_clamp)
406     PREFETCHR0(mt_merge)
407     PREFETCHR0(mt_logic)
408     PREFETCHR0(mt_hysteresis)
409     PREFETCHR0(mt_invert)
410     PREFETCHR0(mt_binarize)
411     PREFETCHR0(mt_convolution)
412     PREFETCHR0(mt_mappedblur)
413     OTHER(mt_circle)
414     OTHER(mt_square)
415     OTHER(mt_diamond)
416     OTHER(mt_losange)
417     OTHER(mt_rectangle)
418     OTHER(mt_ellipse)
419     OTHER(mt_polish)
420     // Mixed
421     BROKEN(RemoveGrain)
422     BROKEN(Repair)
423     PREFETCHR0(VagueDenoiser)
424     PREFETCHR0(UnDot)
425     PREFETCHR0(SangNom)
426     PREFETCHR0(gradfun2db)
427     PREFETCHR0(debilinear)
428     PREFETCHR0(debilinearY)
429     SOURCE(dss2)
430     SOURCE(DirectShowSource)
431     PREFETCHR0(SCXvid)
432     PREFETCHR0(ResampleHQ)
433     PREFETCHR0(AssRender)
434     PREFETCHR0(TextSub)
435     PREFETCHR0(VobSub)
436     PREFETCHR2(fft3dGPU)
437     PREFETCHR2(FFT3DFilter)
438     PREFETCHR1(Convolution3D)
439     PREFETCHR1(deen)
440     PREFETCHR0(eDeen)
441     // Mvtools
442     PREFETCHR0(MSuper)
443     temp = int64ToIntS(vsapi->propGetInt(in, "delta", 0, &err));
444     if (temp < 1)
445         temp = 1;
446     PREFETCH(MAnalyse, 1, 1, -temp, temp)
447     PREFETCHR3(MDegrain1)
448     PREFETCHR3(MDegrain2)
449     PREFETCHR3(MDegrain3)
450     PREFETCHR3(MCompensate)
451     PREFETCHR0(MMask)
452     PREFETCHR0(MSCDetection)
453     PREFETCHR0(MShow)
454     PREFETCHR0(MDepan)
455     PREFETCHR0(MFlow)
456     PREFETCHR1(MFlowInter)
457     PREFETCHR1(MFlowFps)
458     PREFETCHR1(MBlockFps)
459     PREFETCHR1(MFlowBlur)
460     PREFETCHR1(MRecalculate)
461 
462     // aWarpShit
463     PREFETCHR0(aBlur)
464     PREFETCHR0(aSobel)
465     PREFETCHR0(aWarp)
466     PREFETCHR0(aWarp4)
467     PREFETCHR0(aWarpSharp)
468     PREFETCHR0(aWarpSharp2)
469 
470     // CullResize
471     PREFETCHR0(CullBilinearResize)
472     PREFETCHR0(CullBicubicResize)
473     PREFETCHR0(CullLanczosResize)
474     PREFETCHR0(CullLanczos4Resize)
475     PREFETCHR0(CullBlackmanResize)
476     PREFETCHR0(CullSpline16Resize)
477     PREFETCHR0(CullSpline36Resize)
478     PREFETCHR0(CullSpline64Resize)
479     PREFETCHR0(CullGaussResize)
480 
481     // Spline resize
482     PREFETCHR0(Spline100Resize)
483     PREFETCHR0(Spline144Resize)
484 
485     // PVBob
486     temp = int64ToIntS(vsapi->propGetInt(in, "mode", 0, &err));
487     PREFETCH(DGBob, (temp > 0) ? 2 : 1, 1, -2, 2)
488     PREFETCH(PVBob, (temp > 0) ? 2 : 1, 1, -2, 2)
489 
490     // Avisynth internal
491     PREFETCH(Bob, 2, 1, 0, 0)
492     PREFETCH(TemporalSoften, 1, 1, -5, 5)
493 
494     // AutoAdjust
495     temp = int64ToIntS(vsapi->propGetInt(in, "temporal_radius", 0, &err));
496     if (err || temp < 0)
497         temp = 20;
498     PREFETCH(AutoAdjust, 1, 1, -temp, temp)
499 
500     // prefetch nothing by default
501     return PrefetchInfo(1, 1, 0, -1);
502 }
503 
avisynthFilterInit(VSMap * in,VSMap * out,void ** instanceData,VSNode * node,VSCore * core,const VSAPI * vsapi)504 static void VS_CC avisynthFilterInit(VSMap *in, VSMap *out, void **instanceData, VSNode *node, VSCore *core, const VSAPI *vsapi) {
505     WrappedClip *clip = (WrappedClip *) * instanceData;
506 
507     if (!clip->preFetchClips.empty())
508         clip->fakeEnv->uglyNode = clip->preFetchClips.front();
509 
510     const VideoInfo &viAvs = clip->clip->GetVideoInfo();
511     ::VSVideoInfo vi;
512     vi.height = viAvs.height;
513     vi.width = viAvs.width;
514     vi.numFrames = viAvs.num_frames;
515     vi.fpsNum = viAvs.fps_numerator;
516     vi.fpsDen = viAvs.fps_denominator;
517     vs_normalizeRational(&vi.fpsNum, &vi.fpsDen);
518 
519     vi.format = AVSPixelTypeToVSFormat(viAvs, core, vsapi);
520 
521     if (!vi.format)
522         vsapi->setError(out, "Avisynth Compat: bad format!");
523 
524     vi.flags = 0;
525     vsapi->setVideoInfo(&vi, 1, node);
526 }
527 
avisynthFilterGetFrame(int n,int activationReason,void ** instanceData,void ** frameData,VSFrameContext * frameCtx,VSCore * core,const VSAPI * vsapi)528 static const VSFrameRef *VS_CC avisynthFilterGetFrame(int n, int activationReason, void **instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) {
529     WrappedClip *clip = (WrappedClip *) * instanceData;
530     PVideoFrame frame;
531     n = std::min(n, clip->clip->GetVideoInfo().num_frames - 1);
532 
533     if (activationReason == arAllFramesReady || (activationReason == arInitial && (clip->preFetchClips.empty() || clip->prefetchInfo.from > clip->prefetchInfo.to))) {
534         // Ready the global stuff needed to make things work behind the scenes, the locking model makes this technically safe but quite ugly.
535         // The frame number is needed to pass through frame attributes for filters that create a new frame to return, the context is for GetFrame().
536         if (!clip->preFetchClips.empty()) {
537             clip->fakeEnv->uglyN = n;
538             clip->fakeEnv->uglyCtx = frameCtx;
539         }
540 
541         try {
542             frame = clip->clip->GetFrame(n, clip->fakeEnv);
543 
544             if (!frame)
545                 vsapi->logMessage(mtFatal, "Avisynth Error: no frame returned");
546         } catch (const AvisynthError &e) {
547             vsapi->logMessage(mtFatal, ("Avisynth Error: avisynth errors in GetFrame() are unrecoverable, crashing... "_s + e.msg).c_str());
548         } catch (const IScriptEnvironment::NotFound &) {
549             vsapi->logMessage(mtFatal, "Avisynth Error: escaped IScriptEnvironment::NotFound exceptions are non-recoverable, crashing... ");
550         } catch (...) {
551             vsapi->logMessage(mtFatal, "Avisynth Error: avisynth errors are unrecoverable, crashing...");
552         }
553 
554         clip->fakeEnv->uglyCtx = nullptr;
555     } else if (activationReason == arInitial) {
556         for (VSNodeRef *c : clip->preFetchClips)
557             prefetchHelper(n, c, clip->prefetchInfo, frameCtx, vsapi);
558     } else if (activationReason == arError) {
559         return nullptr;
560     }
561 
562     // Enjoy the casting to trigger the void * operator. Please contact me if you can make it pretty.
563 
564     const VSFrameRef *ref = nullptr;
565 
566     if (frame)
567         ref = clip->fakeEnv->avsToVSFrame((VideoFrame *)((void *)frame));
568 
569     return ref;
570 }
571 
avisynthFilterFree(void * instanceData,VSCore * core,const VSAPI * vsapi)572 static void VS_CC avisynthFilterFree(void *instanceData, VSCore *core, const VSAPI *vsapi) {
573     WrappedClip *clip = (WrappedClip *)instanceData;
574     delete clip;
575 }
576 
isSupportedPF(const VSFormat * f,int interfaceVersion)577 static bool isSupportedPF(const VSFormat *f, int interfaceVersion) {
578     if (interfaceVersion == 2)
579         return (f->id == pfYUV420P8) || (f->id == pfCompatYUY2) || (f->id == pfCompatBGR32);
580     else
581         return !!VSFormatToAVSPixelType(f);
582 }
583 
fakeAvisynthFunctionWrapper(const VSMap * in,VSMap * out,void * userData,VSCore * core,const VSAPI * vsapi)584 static void VS_CC fakeAvisynthFunctionWrapper(const VSMap *in, VSMap *out, void *userData,
585         VSCore *core, const VSAPI *vsapi) {
586     WrappedFunction *wf = (WrappedFunction *)userData;
587     FakeAvisynth *fakeEnv = new FakeAvisynth(wf->interfaceVersion, core, vsapi);
588     std::vector<AVSValue> inArgs(wf->parsedArgs.size());
589     std::vector<VSNodeRef *> preFetchClips;
590 
591     for (size_t i = 0; i < inArgs.size(); i++) {
592         const AvisynthArgs &parsedArg = wf->parsedArgs.at(i);
593 
594         if (vsapi->propNumElements(in, parsedArg.name.data()) > 0) {
595             switch (parsedArg.type) {
596             case 'i':
597                 inArgs[i] = int64ToIntS(vsapi->propGetInt(in, parsedArg.name.c_str(), 0, nullptr));
598                 break;
599             case 'f':
600                 inArgs[i] = vsapi->propGetFloat(in, parsedArg.name.c_str(), 0, nullptr);
601                 break;
602             case 'b':
603                 inArgs[i] = !!vsapi->propGetInt(in, parsedArg.name.c_str(), 0, nullptr);
604                 break;
605             case 's':
606                 inArgs[i] = vsapi->propGetData(in, parsedArg.name.c_str(), 0, nullptr);
607                 break;
608             case 'c':
609                 VSNodeRef *cr = vsapi->propGetNode(in, parsedArg.name.c_str(), 0, nullptr);
610                 const VSVideoInfo *vi = vsapi->getVideoInfo(cr);
611                 if (!isConstantFormat(vi) || !isSupportedPF(vi->format, wf->interfaceVersion)) {
612                     vsapi->setError(out, "Invalid avisynth colorspace in one of the input clips");
613                     vsapi->freeNode(cr);
614                     delete fakeEnv;
615                     return;
616                 }
617 
618                 preFetchClips.push_back(cr);
619                 inArgs[i] = new VSClip(cr, fakeEnv, vsapi);
620                 break;
621             }
622         }
623     }
624 
625     AVSValue inArgAVSValue(inArgs.data(), static_cast<int>(wf->parsedArgs.size()));
626     AVSValue ret;
627 
628     try {
629         ret = wf->apply(inArgAVSValue, wf->avsUserData, fakeEnv);
630     } catch (const AvisynthError &e) {
631         vsapi->setError(out, e.msg);
632         return;
633     } catch (const IScriptEnvironment::NotFound &) {
634         vsapi->logMessage(mtFatal, "Avisynth Error: escaped IScriptEnvironment::NotFound exceptions are non-recoverable, crashing... ");
635     }
636 
637     fakeEnv->initializing = false;
638 
639     if (ret.IsClip()) {
640         PrefetchInfo prefetchInfo = getPrefetchInfo(wf->name, in, vsapi);
641         WrappedClip *filterData = new WrappedClip(wf->name, ret.AsClip(), preFetchClips, prefetchInfo, fakeEnv);
642         vsapi->createFilter(
643                                     in,
644                                     out,
645                                     wf->name.c_str(),
646                                     avisynthFilterInit,
647                                     avisynthFilterGetFrame,
648                                     avisynthFilterFree,
649                                     (preFetchClips.empty() || prefetchInfo.from > prefetchInfo.to) ? fmSerial : fmParallelRequests,
650                                     0,
651                                     filterData,
652                                     core);
653     } else if (ret.IsBool()) {
654         vsapi->propSetInt(out, "val", ret.AsBool() ? 1 : 0, paReplace);
655     } else if (ret.IsInt()) {
656         vsapi->propSetInt(out, "val", ret.AsInt(), paReplace);
657     } else if (ret.IsFloat()) {
658         vsapi->propSetFloat(out, "val", ret.AsFloat(), paReplace);
659     } else if (ret.IsString()) {
660         vsapi->propSetData(out, "val", ret.AsString(), -1, paReplace);
661     }
662 }
663 
AddFunction(const char * name,const char * params,ApplyFunc apply,void * user_data)664 void FakeAvisynth::AddFunction(const char *name, const char *params, ApplyFunc apply, void *user_data) {
665     size_t paramLength = strlen(params);
666     size_t paramPos = 0;
667     int argNum = 1;
668     int numArgs = 0;
669     std::vector<AvisynthArgs> parsedArgs;
670     std::string newArgs;
671     std::string fname(name);
672 
673     if (fname == "RemoveGrain" || fname == "Repair" || fname == "ColorMatrix" || fname == "IsCombed") {
674         vsapi->logMessage(mtWarning, ("Avisynth Compat: rejected adding Avisynth function " + fname + "because it is too broken").c_str());
675         return;
676     }
677 
678     if (fname == "FFMS2" || fname == "FFCopyrightInfringement") {
679         vsapi->logMessage(mtWarning, ("Avisynth Compat: rejected adding Avisynth function " + fname + "because it calls invoke").c_str());
680         return;
681     }
682 
683     while (paramPos < paramLength) {
684         if (params[paramPos] == '*' || params[paramPos] == '+' || params[paramPos] == '.') {
685             vsapi->logMessage(mtWarning, ("Avisynth Compat: varargs not implemented so I'm just gonna skip importing " + fname).c_str());
686             return;
687         }
688 
689         if (params[paramPos] == '[') { // named argument start
690             std::string argName(params);
691             size_t nameStart = ++paramPos;
692 
693             while (paramPos < paramLength) {
694                 if (params[paramPos++] == ']') {
695                     argName = argName.substr(nameStart, paramPos - nameStart - 1);
696                     break;
697                 }
698             }
699 
700             newArgs += argName + ":" + charToFilterArgumentString(params[paramPos]) + ":opt;";
701             parsedArgs.push_back(AvisynthArgs(argName, params[paramPos++], false));
702         } else {
703             newArgs += params[paramPos] + std::to_string(argNum) + ":" + charToFilterArgumentString(params[paramPos]) + ";";
704             parsedArgs.push_back(AvisynthArgs((params[paramPos] + std::to_string(argNum)), params[paramPos], true));
705             paramPos++;
706             argNum++;
707         }
708 
709         numArgs++;
710     }
711 
712     std::lock_guard<std::mutex> lock(registerFunctionLock);
713 
714     if (registeredFunctions.count(fname)) {
715         for (size_t i = 2; i < SIZE_MAX; i++) {
716             std::string numberedName = fname + "_" + std::to_string(i);
717             if (!registeredFunctions.count(numberedName)) {
718                 fname = numberedName;
719                 break;
720             }
721         }
722     }
723 
724     registeredFunctions.insert(fname);
725     vsapi->registerFunction(fname.c_str(), newArgs.c_str(), fakeAvisynthFunctionWrapper, new WrappedFunction(fname, apply, parsedArgs, user_data, interfaceVersion), vsapi->getPluginById("com.vapoursynth.avisynth", core));
726 }
727 
FunctionExists(const char * name)728 bool FakeAvisynth::FunctionExists(const char *name) {
729     vsapi->logMessage(mtWarning, "FunctionExists not implemented");
730     return false;
731 }
732 
Invoke(const char * name,const AVSValue args,const char * const * arg_names)733 AVSValue FakeAvisynth::Invoke(const char *name, const AVSValue args, const char* const* arg_names) {
734     if (!_stricmp(name, "Cache") || !_stricmp(name, "InternalCache")) {
735         return args;
736     }
737 
738     if (!_stricmp(name, "Crop")) {
739         vsapi->logMessage(mtWarning, "Invoke not fully implemented, tried to call Crop() but I will do nothing");
740         return args[0];
741     }
742 
743     if (!_stricmp(name, "AudioDub")) {
744         vsapi->logMessage(mtWarning, "Invoke not fully implemented, tried to call AudioDub() but I will do nothing");
745         return args[0];
746     }
747 
748     vsapi->logMessage(mtWarning, ("Invoke not fully implemented, tried to call: "_s + name + " but I will pretend it doesn't exist").c_str());
749     throw IScriptEnvironment::NotFound();
750     return AVSValue();
751 }
752 
GetVar(const char * name)753 AVSValue FakeAvisynth::GetVar(const char *name) {
754     return AVSValue();
755 }
756 
SetVar(const char * name,const AVSValue & val)757 bool FakeAvisynth::SetVar(const char *name, const AVSValue &val) {
758     return true;
759 }
760 
SetGlobalVar(const char * name,const AVSValue & val)761 bool FakeAvisynth::SetGlobalVar(const char *name, const AVSValue &val) {
762     return true;
763 }
764 
PushContext(int level)765 void FakeAvisynth::PushContext(int level) {
766     vsapi->logMessage(mtFatal, "PushContext not implemented");
767 }
768 
PopContext()769 void FakeAvisynth::PopContext() {
770     vsapi->logMessage(mtFatal, "PopContext not implemented");
771 }
772 
NewVideoFrame(const VideoInfo & vi,int align)773 PVideoFrame FakeAvisynth::NewVideoFrame(const VideoInfo &vi, int align) {
774     VSFrameRef *ref = nullptr;
775     assert(vi.width > 0);
776     assert(vi.height > 0);
777 
778     // attempt to copy over the right set of properties, assuming that frame n in is also n out
779     const VSFrameRef *propSrc = nullptr;
780 
781     if (uglyNode && uglyCtx)
782         propSrc = vsapi->getFrameFilter(uglyN, uglyNode, uglyCtx);
783 
784     bool isMultiplePlanes = (vi.pixel_type & VideoInfo::CS_PLANAR) && !(vi.pixel_type & VideoInfo::CS_INTERLEAVED);
785 
786     const VSFormat *f = AVSPixelTypeToVSFormat(vi, core, vsapi);
787 
788     if (!f)
789         vsapi->logMessage(mtFatal, "Unsupported frame format in newvideoframe (alpha and/or packed RGB not supported)");
790 
791     ref = vsapi->newVideoFrame(f, vi.width, vi.height, propSrc, core);
792 
793     if (propSrc)
794         vsapi->freeFrame(propSrc);
795 
796     uint8_t *firstPlanePtr = vsapi->getWritePtr(ref, 0);
797     VideoFrame *vfb = new VideoFrame(
798         (BYTE *)firstPlanePtr,
799         true,
800         0,
801         vsapi->getStride(ref, 0),
802         vi.width * vsapi->getFrameFormat(ref)->bytesPerSample,
803         vi.height,
804         isMultiplePlanes ? vsapi->getWritePtr(ref, 1) - firstPlanePtr : 0,
805         isMultiplePlanes ? vsapi->getWritePtr(ref, 2) - firstPlanePtr : 0,
806         isMultiplePlanes ? vsapi->getStride(ref, 1) : 0,
807         vsapi->getFrameWidth(ref, 1) * vsapi->getFrameFormat(ref)->bytesPerSample,
808         vsapi->getFrameHeight(ref, 1));
809 
810     PVideoFrame pvf(vfb);
811     ownedFrames.insert(std::make_pair(vfb, ref));
812     return pvf;
813 }
814 
MakeWritable(PVideoFrame * pvf)815 bool FakeAvisynth::MakeWritable(PVideoFrame *pvf) {
816     // Find the backing frame, copy it, wrap the new frame into a avisynth PVideoFrame
817     VideoFrame *vfb = (VideoFrame *)(void *)(*pvf);
818     auto it = ownedFrames.find(vfb);
819     assert(it != ownedFrames.end());
820     VSFrameRef *ref = vsapi->copyFrame(it->second, core);
821     uint8_t *firstPlanePtr = vsapi->getWritePtr(ref, 0);
822     VideoFrame *newVfb = new VideoFrame(
823         // the data will never be modified due to the writable protections embedded in this mess
824         (BYTE *)firstPlanePtr,
825         true,
826         0,
827         vsapi->getStride(ref, 0),
828         (*pvf)->row_size,
829         (*pvf)->height,
830         vsapi->getWritePtr(ref, 1) - firstPlanePtr,
831         vsapi->getWritePtr(ref, 2) - firstPlanePtr,
832         vsapi->getStride(ref, 1),
833         (*pvf)->row_sizeUV,
834         (*pvf)->heightUV);
835     *pvf = PVideoFrame(newVfb);
836     ownedFrames.insert(std::make_pair(newVfb, ref));
837     return true;
838 }
839 
BitBlt(BYTE * dstp,int dst_pitch,const BYTE * srcp,int src_pitch,int row_size,int height)840 void FakeAvisynth::BitBlt(BYTE* dstp, int dst_pitch, const BYTE* srcp, int src_pitch, int row_size, int height) {
841     if (src_pitch == dst_pitch && dst_pitch == row_size) {
842         memcpy(dstp, srcp, row_size * height);
843     } else {
844         for (int i = 0; i < height; i++) {
845             memcpy(dstp, srcp, row_size);
846             dstp += dst_pitch;
847             srcp += src_pitch;
848         }
849     }
850 }
851 
AtExit(ShutdownFunc function,void * user_data)852 void FakeAvisynth::AtExit(ShutdownFunc function, void *user_data) {
853     // intentionally ignored to prevent issues when multiple cores load the sample plugin in the same process
854 }
855 
CheckVersion(int version)856 void FakeAvisynth::CheckVersion(int version) {
857     if (version > AVISYNTH_INTERFACE_VERSION)
858         ThrowError("Plugin was designed for a later version of Avisynth (%d)", version);
859 }
860 
Subframe(PVideoFrame src,int rel_offset,int new_pitch,int new_row_size,int new_height)861 PVideoFrame FakeAvisynth::Subframe(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height) {
862     if (src->row_size != new_row_size)
863         vsapi->logMessage(mtFatal, "Subframe only partially implemented (row_size != new_row_size)");
864     // not pretty at all, but the underlying frame has to be fished out to have any idea what the input really is
865     const VSFrameRef *f = avsToVSFrame((VideoFrame *)(void *)src);
866     const VSFormat *fi = vsapi->getFrameFormat(f);
867     VideoInfo vi;
868     vi.height = new_height;
869     vi.width = vsapi->getFrameWidth(f, 0);
870 
871     if (fi->id == pfCompatYUY2)
872         vi.pixel_type = VideoInfo::CS_YUY2;
873     else if (fi->id == pfCompatBGR32)
874         vi.pixel_type = VideoInfo::CS_BGR32;
875     else
876         vsapi->logMessage(mtFatal, "Bad colorspace");
877 
878     PVideoFrame dst = NewVideoFrame(vi);
879     BitBlt(dst->GetWritePtr(), dst->GetPitch(), src->GetReadPtr() + rel_offset, new_pitch, new_row_size, new_height);
880 
881     return dst;
882 }
883 
SetMemoryMax(int mem)884 int FakeAvisynth::SetMemoryMax(int mem) {
885     // ignore
886     return 0;
887 }
888 
SetWorkingDir(const char * newdir)889 int FakeAvisynth::SetWorkingDir(const char *newdir) {
890     vsapi->logMessage(mtFatal, "SetWorkingDir not implemented");
891     return 1;
892 }
893 
ManageCache(int key,void * data)894 void *FakeAvisynth::ManageCache(int key, void *data) {
895     vsapi->logMessage(mtFatal, "ManageCache not implemented");
896     return nullptr;
897 }
898 
PlanarChromaAlignment(PlanarChromaAlignmentMode key)899 bool FakeAvisynth::PlanarChromaAlignment(PlanarChromaAlignmentMode key) {
900     vsapi->logMessage(mtFatal, "PlanarChromaAlignment not implemented");
901     return true;
902 }
903 
SubframePlanar(PVideoFrame src,int rel_offset,int new_pitch,int new_row_size,int new_height,int rel_offsetU,int rel_offsetV,int new_pitchUV)904 PVideoFrame FakeAvisynth::SubframePlanar(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV) {
905     vsapi->logMessage(mtFatal, "SubframePlanar not implemented");
906     if (src->row_size != new_row_size)
907         vsapi->logMessage(mtFatal, "SubframePlanar only partially implemented");
908     // not pretty at all, but the underlying frame has to be fished out to have any idea what the input really is
909     const VSFrameRef *f = avsToVSFrame((VideoFrame *)(void *)src);
910     const VSFormat *fi = vsapi->getFrameFormat(f);
911     VideoInfo vi;
912     vi.height = new_height;
913     vi.width = vsapi->getFrameWidth(f, 0);
914 
915     vi.pixel_type = VSFormatToAVSPixelType(fi);
916 
917     if (!vi.pixel_type)
918         vsapi->logMessage(mtFatal, "Bad colorspace, bad!");
919 
920     PVideoFrame dst = NewVideoFrame(vi);
921 
922     vsapi->logMessage(mtWarning, "Subframeplanar only partially implemented, report if it crashed or not (especially if not using YV12)");
923     BitBlt(dst->GetWritePtr(PLANAR_Y), dst->GetPitch(PLANAR_Y), src->GetReadPtr(PLANAR_Y) + rel_offset, new_pitch, new_row_size, new_height);
924     BitBlt(dst->GetWritePtr(PLANAR_U), dst->GetPitch(PLANAR_U), src->GetReadPtr(PLANAR_U) + rel_offsetU, new_pitchUV, new_row_size/2, new_height/2);
925     BitBlt(dst->GetWritePtr(PLANAR_V), dst->GetPitch(PLANAR_V), src->GetReadPtr(PLANAR_V) + rel_offsetV, new_pitchUV, new_row_size/2, new_height/2);
926     return dst;
927 }
928 
DeleteScriptEnvironment()929 void FakeAvisynth::DeleteScriptEnvironment() {
930     vsapi->logMessage(mtFatal, "DeleteScriptEnvironment not implemented");
931 }
932 
ApplyMessage(PVideoFrame * frame,const VideoInfo & vi,const char * message,int size,int textcolor,int halocolor,int bgcolor)933 void FakeAvisynth::ApplyMessage(PVideoFrame* frame, const VideoInfo& vi, const char* message, int size,
934     int textcolor, int halocolor, int bgcolor) {
935     vsapi->logMessage(mtFatal, "ApplyMessage not implemented");
936 }
937 
GetAVSLinkage()938 const AVS_Linkage* const FakeAvisynth::GetAVSLinkage() {
939     return AVS_linkage;
940 }
941 
GetVarDef(const char * name,const AVSValue & def)942 AVSValue FakeAvisynth::GetVarDef(const char* name, const AVSValue& def) {
943     return def;
944 }
945 
GetProperty(AvsEnvProperty prop)946 size_t  FakeAvisynth::GetProperty(AvsEnvProperty prop) {
947     switch (prop) {
948     case AEP_FILTERCHAIN_THREADS:
949         return 1;
950     case AEP_PHYSICAL_CPUS:
951         return 1;
952     case AEP_LOGICAL_CPUS:
953         return 1;
954     case AEP_THREAD_ID:
955         return 0;
956     case AEP_THREADPOOL_THREADS:
957         return 1;
958     case AEP_VERSION:
959         return 0;
960     default:
961         this->ThrowError("Invalid property request.");
962         return std::numeric_limits<size_t>::max();
963     }
964 
965     assert(0);
966 }
967 
GetVar(const char * name,AVSValue * val) const968 bool FakeAvisynth::GetVar(const char* name, AVSValue *val) const {
969     return val;
970 }
971 
GetVar(const char * name,bool def) const972 bool FakeAvisynth::GetVar(const char* name, bool def) const {
973     return def;
974 }
975 
GetVar(const char * name,int def) const976 int FakeAvisynth::GetVar(const char* name, int def) const {
977     return def;
978 }
979 
GetVar(const char * name,double def) const980 double FakeAvisynth::GetVar(const char* name, double def) const {
981     return def;
982 }
983 
GetVar(const char * name,const char * def) const984 const char* FakeAvisynth::GetVar(const char* name, const char* def) const {
985     return def;
986 }
987 
LoadPlugin(const char * filePath,bool throwOnError,AVSValue * result)988 bool FakeAvisynth::LoadPlugin(const char* filePath, bool throwOnError, AVSValue *result) {
989     vsapi->logMessage(mtFatal, "Plugin loading not implemented");
990     return false;
991 }
992 
AddAutoloadDir(const char * dirPath,bool toFront)993 void FakeAvisynth::AddAutoloadDir(const char* dirPath, bool toFront) {
994     vsapi->logMessage(mtFatal, "Autoloading dirs not implemented");
995 }
996 
ClearAutoloadDirs()997 void FakeAvisynth::ClearAutoloadDirs() {
998     vsapi->logMessage(mtFatal, "Clearing autoload dirs not implemented");
999 }
1000 
AutoloadPlugins()1001 void FakeAvisynth::AutoloadPlugins() {
1002     vsapi->logMessage(mtFatal, "Autoloading not implemented");
1003 }
1004 
AddFunction(const char * name,const char * params,ApplyFunc apply,void * user_data,const char * exportVar)1005 void FakeAvisynth::AddFunction(const char* name, const char* params, ApplyFunc apply, void* user_data, const char *exportVar) {
1006     AddFunction(name, params, apply, user_data);
1007 }
1008 
InternalFunctionExists(const char * name)1009 bool FakeAvisynth::InternalFunctionExists(const char* name) {
1010     return false;
1011 }
1012 
SetFilterMTMode(const char * filter,MtMode mode,bool force)1013 void FakeAvisynth::SetFilterMTMode(const char* filter, MtMode mode, bool force) {
1014     // do nothing
1015 }
1016 
NewCompletion(size_t capacity)1017 IJobCompletion* FakeAvisynth::NewCompletion(size_t capacity) {
1018     vsapi->logMessage(mtFatal, "Completions not implemented");
1019     return nullptr;
1020 }
1021 
ParallelJob(ThreadWorkerFuncPtr jobFunc,void * jobData,IJobCompletion * completion)1022 void FakeAvisynth::ParallelJob(ThreadWorkerFuncPtr jobFunc, void* jobData, IJobCompletion* completion) {
1023     vsapi->logMessage(mtFatal, "Threadpool not implemented");
1024 }
1025 
1026 // This version of Invoke will return false instead of throwing NotFound().
Invoke(AVSValue * result,const char * name,const AVSValue & args,const char * const * arg_names)1027 bool FakeAvisynth::Invoke(AVSValue *result, const char* name, const AVSValue& args, const char* const* arg_names) {
1028     try {
1029         *result = Invoke(name, args, arg_names);
1030         return true;
1031     } catch (NotFound &) {
1032         return false;
1033     }
1034 }
1035 
1036 // Support functions
Allocate(size_t nBytes,size_t alignment,AvsAllocType type)1037 void* FakeAvisynth::Allocate(size_t nBytes, size_t alignment, AvsAllocType type) {
1038     return vs_aligned_malloc(nBytes, alignment);
1039 }
1040 
Free(void * ptr)1041 void FakeAvisynth::Free(void* ptr) {
1042     vs_aligned_free(ptr);
1043 }
1044 
SubframePlanarA(PVideoFrame src,int rel_offset,int new_pitch,int new_row_size,int new_height,int rel_offsetU,int rel_offsetV,int new_pitchUV,int rel_offsetA)1045 PVideoFrame FakeAvisynth::SubframePlanarA(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size,
1046     int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV, int rel_offsetA) {
1047     vsapi->logMessage(mtFatal, "SubframePlanarA not implemented");
1048     return PVideoFrame();
1049 }
1050 
avsLoadPlugin(const VSMap * in,VSMap * out,void * userData,VSCore * core,const VSAPI * vsapi)1051 static void VS_CC avsLoadPlugin(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
1052     const char *rawPath = vsapi->propGetData(in, "path", 0, nullptr);
1053     std::wstring wPath = utf16_from_utf8(rawPath);
1054 
1055     HMODULE plugin = LoadLibraryW(wPath.c_str());
1056 
1057     typedef const char *(__stdcall *AvisynthPluginInit2Func)(IScriptEnvironment *env);
1058     typedef const char *(__stdcall *AvisynthPluginInit3Func)(IScriptEnvironment *env, const AVS_Linkage *const vectors);
1059 
1060     if (!plugin) {
1061         vsapi->setError(out, "Avisynth Loader: failed to load module");
1062         return;
1063     }
1064 
1065     AvisynthPluginInit2Func avisynthPluginInit2 = nullptr;
1066     AvisynthPluginInit3Func avisynthPluginInit3 = (AvisynthPluginInit3Func)GetProcAddress(plugin, "AvisynthPluginInit3");
1067 
1068     if (!avisynthPluginInit3)
1069         avisynthPluginInit3 = (AvisynthPluginInit3Func)GetProcAddress(plugin, "_AvisynthPluginInit3@8");
1070 
1071     if (!avisynthPluginInit3) {
1072         avisynthPluginInit2 = (AvisynthPluginInit2Func)GetProcAddress(plugin, "AvisynthPluginInit2");
1073 
1074         if (!avisynthPluginInit2)
1075             avisynthPluginInit2 = (AvisynthPluginInit2Func)GetProcAddress(plugin, "_AvisynthPluginInit2@4");
1076     }
1077 
1078     if (!avisynthPluginInit3 && !avisynthPluginInit2) {
1079         vsapi->setError(out, "Avisynth Loader: no entry point found");
1080         FreeLibrary(plugin);
1081         return;
1082     }
1083 
1084     if (avisynthPluginInit3) {
1085         FakeAvisynth *avs = new FakeAvisynth(3, core, vsapi);
1086         avisynthPluginInit3(avs, AVS_linkage);
1087         delete avs;
1088     } else {
1089 #ifdef _WIN64
1090         vsapi->setError(out, "Avisynth Loader: 2.5 plugins can't be loaded on x64");
1091         return;
1092 #else
1093         FakeAvisynth *avs = new FakeAvisynth(2, core, vsapi);
1094         avisynthPluginInit2(avs);
1095         delete avs;
1096 #endif
1097     }
1098 
1099 #ifdef VS_TARGET_OS_WINDOWS
1100     if (!vs_isSSEStateOk())
1101         vsapi->logMessage(mtFatal, ("Bad SSE state detected after loading "_s + rawPath).c_str());
1102 #endif
1103 }
1104 
WrappedFunction(const std::string & name,FakeAvisynth::ApplyFunc apply,const std::vector<AvisynthArgs> & parsedArgs,void * avsUserData,int interfaceVersion)1105 WrappedFunction::WrappedFunction(const std::string &name, FakeAvisynth::ApplyFunc apply, const std::vector<AvisynthArgs> &parsedArgs, void *avsUserData, int interfaceVersion) :
1106     name(name), apply(apply), parsedArgs(parsedArgs), avsUserData(avsUserData), interfaceVersion(interfaceVersion) {
1107 }
1108 
VapourSynthPluginInit(VSConfigPlugin configFunc,VSRegisterFunction registerFunc,VSPlugin * plugin)1109 VS_EXTERNAL_API(void) VapourSynthPluginInit(VSConfigPlugin configFunc, VSRegisterFunction registerFunc, VSPlugin *plugin) {
1110     configFunc("com.vapoursynth.avisynth", "avs", "VapourSynth Avisynth Compatibility", VAPOURSYNTH_API_VERSION, 0, plugin);
1111     registerFunc("LoadPlugin", "path:data;", &avsLoadPlugin, plugin, plugin);
1112 }
1113 
1114 }
1115